# SSH डिस्कनेक्ट के बाद अज्ञात रिमोट स्थिति का सत्यापन

SSH डिस्कनेक्ट होने का मतलब यह नहीं है कि रिमोट कमांड विफल हो गई। इसका मतलब है कि जो हुआ, उसे वर्गीकृत करने के लिए ज़रूरी प्रमाण क्लाइंट खो बैठा। आपने `uname` चलाया हो तो यह बस परेशान करने वाली बात है। लेकिन डेटाबेस माइग्रेशन, रिलीज़, क्रेडेंशियल रोटेशन या किसी बाहरी API को कॉल करने वाली कमांड के मामले में इसकी कीमत बहुत बड़ी हो सकती है।

इसका समाधान बड़ा टाइमआउट नहीं है। समाधान है एक रिमोट सत्यापन क्रम, जिसमें स्थायी रन पहचान, स्पष्ट स्थिति परिवर्तन और पूरा होने का प्रभाव-विशिष्ट प्रमाण हो। यह व्यवस्था बनने के बाद दोबारा कनेक्ट करने पर सवाल «क्या इसे फिर चलाना चाहिए?» नहीं रहता, बल्कि «इस रन रिकॉर्ड में क्या लिखा है और इसने क्या बदला?» बन जाता है।

## कनेक्शन टूटने पर तीन ईमानदार स्थितियाँ बचती हैं

जब SSH क्लाइंट reset, timeout, broken pipe या अचानक EOF की सूचना देता है, तब कमांड तीन में से किसी एक स्थिति में होती है: वह कभी शुरू नहीं हुई, शुरू होकर अभी चल रही है, या पूरी हो चुकी है। क्लाइंट का exit code इन स्थितियों को भरोसेमंद ढंग से अलग नहीं करता।

आपके शेल और रिमोट प्रोग्राम के बीच कई सीमाएँ होती हैं:

- आपका स्थानीय शेल `ssh` शुरू करता है।
- क्लाइंट SSH चैनल अनुरोध और कमांड बाइट्स भेजता है।
- सर्वर अनुरोध स्वीकार करके रिमोट शेल या प्रोग्राम शुरू करता है।
- वह प्रोग्राम अपना वास्तविक काम करता है।
- प्रोग्राम समाप्त होता है और sshd आउटपुट तथा exit status वापस भेजता है।

इनमें से किसी भी सीमा के बाद नेटवर्क टूटने पर स्थानीय स्तर पर त्रुटि आ सकती है। अगर टूटना रिमोट प्रोग्राम शुरू होने से पहले हुआ, तो कुछ नहीं हुआ। अगर यह प्रोग्राम के बदलाव commit करने के बाद, लेकिन exit status लौटने से पहले हुआ, तो बदलाव हो चुका है और आपका क्लाइंट फिर भी विफलता दिखाता है।

इसीलिए स्थानीय संदेश, जैसे `Connection reset by peer`, transport का प्रमाण है, business परिणाम का नहीं। यह केवल बताता है कि क्लाइंट SSH बातचीत पूरी नहीं कर सका। यह नहीं बताता कि रिमोट ऑपरेशन चला या नहीं।

OpenSSH का अपना configuration manual inactive channels के बारे में इससे जुड़ी बात कहता है: session बंद होने से यह सुनिश्चित नहीं होता कि उससे जुड़े shell processes भी रुक गए हैं। इसलिए channel timeout job-control mechanism नहीं है।

नुकसान पहुँचाने वाली आदत है अस्पष्ट परिणाम को विफल ऑपरेशन मान लेना। यह समझ में आता है, क्योंकि ज़्यादातर command-line tools हमें nonzero exit status को «फिर से चलाएँ» के रूप में पढ़ना सिखाते हैं। SSH transport failure इस आसान नियम को तोड़ देती है।

## कमांड पहुँचना और कमांड पूरी होना अलग दावे हैं

किसी रिमोट कमांड के कम से कम चार दावे होते हैं जिन्हें साबित करना उपयोगी है: submission, start, completion और effect। टीमें अक्सर इनमें से एक को लॉग करती हैं और मान लेती हैं कि चारों सिद्ध हो गए।

Submission का मतलब है कि क्लाइंट ने कमांड भेजने की कोशिश की। आपका स्थानीय टर्मिनल यह जानता है, लेकिन यह सबसे कमजोर दावा है। Start का मतलब है कि रिमोट wrapper ने काम शुरू करने से पहले स्थायी प्रमाण बनाया। Completion का मतलब है कि wrapper ने अंतिम परिणाम दर्ज किया। Effect का मतलब है कि इच्छित रिमोट या बाहरी स्थिति अब माँगे गए परिणाम से मेल खाती है।

प्रोसेस लिस्टिंग जितना लोग सोचते हैं, उससे कम साबित करती है। PID दिखने से केवल यह पता चल सकता है कि अभी उसी नाम जैसी कोई प्रोसेस मौजूद है। इससे यह साबित नहीं होता कि वह आपके अनुरोध से जुड़ी है, उसने महत्वपूर्ण हिस्सा पहले ही पूरा नहीं कर लिया है, या बाद में retry करना सुरक्षित होगा। प्रोसेस समाप्त होने के बाद PID reuse पुराने रिकॉर्ड को और कम उपयोगी बना देता है।

Exit codes की भी यही सीमा है। POSIX में `wait` वह तरीका है जिससे शेल उस child process की स्थिति प्राप्त करता है जिसे वह जानता है। यह संबंध रिमोट शेल के भीतर मौजूद है। SSH कनेक्शन समाप्त होने पर स्थानीय शेल उस status तक पहुँच खो देता है। बाद का SSH session `wait` से उसे फिर नहीं बना सकता। उसे उस रिकॉर्ड को पढ़ना होगा जिसे पहले रन ने स्थायी रूप से सुरक्षित किया था।

Runbook और automation output में इन कथनों को अलग रखें:

1. «क्लाइंट पूर्णता की पुष्टि नहीं कर सका।»
2. «Run `r-20260722-1842-a91f` रिमोट होस्ट पर शुरू हुआ।»
3. «उस रन ने exit status 0 दर्ज किया।»
4. «Deployment marker release `2026.07.22.3` दिखाता है।»

चौथा कथन ही वास्तविक operational सवाल का उत्तर दे सकता है। फ़ाइल कॉपी कमांड के लिए checksum या अपेक्षित content वाला अंतिम pathname चाहिए। माइग्रेशन के लिए schema version या migration ledger entry चाहिए। Payment या ticketing API के अनुरोध के लिए उस API पर idempotency record चाहिए, केवल स्थानीय log line नहीं।

## काम शुरू होने से पहले रिमोट होस्ट पर रन पहचान रखें

स्थायी run ID दोबारा कनेक्ट करने के अस्पष्ट काम को lookup में बदल देती है। SSH चलाने से पहले इसे बनाएँ, रिमोट wrapper को दें और हर artifact को इससे बने path के नीचे रखें।

केवल timestamp का उपयोग न करें। दो agents एक ही सेकंड में शुरू हो सकते हैं, clocks में अंतर हो सकता है और timestamps अपारदर्शी identifiers के लिए अच्छे नहीं होते। Timestamp को random data के साथ मिलाएँ या अपने environment में उपलब्ध UUID generator का उपयोग करें। Verification के समय ID फिर से देनी होगी और हर महत्वपूर्ण log entry में यह ID होनी चाहिए।

यह shell fragment run directory बनाता है, माँगा गया operation लिखता है, start marker दर्ज करता है और दोनों output streams सुरक्षित रखता है। यह अपेक्षा करता है कि `--` के बाद कमांड दी जाएगी। Wrapper को `/usr/local/sbin/run-recorded` जैसी नियंत्रित जगह पर रखें, हर command string में मनमाने ढंग से कॉपी न करें।

```sh
#!/bin/sh
set -eu

run_id=$1
shift
[ "$1" = "--" ]
shift

base=/var/lib/recorded-runs
run_dir="$base/$run_id"

case "$run_id" in
  *[!A-Za-z0-9._-]*|'')
    printf '%s\n' "invalid run id" >&2
    exit 64
    ;;
esac

if ! mkdir "$run_dir" 2>/dev/null; then
  printf '%s\n' "run already exists: $run_id" >&2
  exit 75
fi

umask 077
printf '%s\n' "$*" > "$run_dir/request"
date -u +%Y-%m-%dT%H:%M:%SZ > "$run_dir/started_at"
printf '%s\n' "started" > "$run_dir/state"
printf '%s\n' "$$" > "$run_dir/pid"

set +e
"$@" >"$run_dir/stdout" 2>"$run_dir/stderr"
status=$?
set -e

printf '%s\n' "$status" > "$run_dir/exit_status"
date -u +%Y-%m-%dT%H:%M:%SZ > "$run_dir/finished_at"
printf '%s\n' "finished" > "$run_dir/state"
exit "$status"
```

`mkdir` कॉल केवल housekeeping नहीं कर रही। अगर run ID पहले से मौजूद हो तो directory creation विफल हो जाती है, इसलिए यह एक सरल create-once claim की तरह काम करती है। इससे एक ही ID वाली दो invocations काम को चुपचाप दो बार नहीं करतीं। अलग IDs के साथ होने वाले concurrent work की समस्या इससे हल नहीं होती। उसके लिए अलग lock या application-level constraint चाहिए।

क्रम महत्वपूर्ण है। Wrapper payload चलाने से पहले `started_at`, `state` और `pid` लिखता है। `state` को `finished` करने से पहले `exit_status` दर्ज करता है। अगर verifier को `exit_status` के बिना `finished` दिखे, तो रिकॉर्ड को damaged मानें, सफल नहीं। अगर run directory दिखे लेकिन `started_at` न हो, तो इसे अधूरी setup failure मानें।

Shell trap के ज़रिए `finished` लिखकर काम पूरा मान लेने की गलती न करें। अचानक host failure, storage failure, forced kill या filesystem समस्या traps को चलने से रोक सकती है। अंतिम marker मौजूद हो तो वह प्रमाण है, लेकिन उसके न होने से यह साबित नहीं होता कि payload पूरा नहीं हुआ।

## रन को ऐसे क्रम में सत्यापित करें जो आपको गुमराह न करे

सबसे पहले read-only status command के साथ दोबारा कनेक्ट करें। उसी arguments के साथ payload फिर से चलाकर यह उम्मीद न करें कि उत्तर अपने-आप स्पष्ट हो जाएगा।

एक उपयोगी verifier को रिकॉर्ड को `absent`, `running`, `finished` या `damaged` में वर्गीकृत करना चाहिए। यह उदाहरण ऊपर दिए directory format का उपयोग करता है और ऐसे facts दिखाता है जिनका मूल्यांकन इंसान या agent कर सकता है।

```sh
#!/bin/sh
set -eu

run_id=$1
run_dir="/var/lib/recorded-runs/$run_id"

if [ ! -d "$run_dir" ]; then
  printf '%s\n' 'state=absent'
  exit 0
fi

if [ ! -f "$run_dir/started_at" ]; then
  printf '%s\n' 'state=damaged reason=missing-start-marker'
  exit 2
fi

if [ -f "$run_dir/finished_at" ] && [ -f "$run_dir/exit_status" ]; then
  printf '%s\n' 'state=finished'
  printf 'exit_status=%s\n' "$(cat "$run_dir/exit_status")"
  printf 'started_at=%s\n' "$(cat "$run_dir/started_at")"
  printf 'finished_at=%s\n' "$(cat "$run_dir/finished_at")"
  exit 0
fi

if [ -f "$run_dir/pid" ]; then
  pid=$(cat "$run_dir/pid")
  if kill -0 "$pid" 2>/dev/null; then
    printf 'state=running pid=%s\n' "$pid"
    exit 0
  fi
fi

printf '%s\n' 'state=damaged reason=no-finish-record-and-pid-not-live'
exit 2
```

इसे नई SSH command के रूप में चलाएँ:

```sh
ssh ops@host /usr/local/sbin/check-recorded-run r-20260722-1842-a91f
```

इसका output इनमें से किसी एक जैसा होना चाहिए:

```text
state=absent
```

```text
state=running pid=48192
```

```text
state=finished
exit_status=0
started_at=2026-07-22T18:42:19Z
finished_at=2026-07-22T18:47:03Z
```

असुविधाजनक `damaged` स्थिति को protocol में शामिल करें। इसे छोड़ देने पर verifier को missing evidence को खुशमिज़ाज अनुमान में बदलना पड़ेगा। अगर command चलने के दौरान host reboot हो गया, तो `kill -0` विफल होगा और अंतिम marker नहीं होगा। सही प्रतिक्रिया है कि इच्छित effect और application logs देखें, फिर तय करें कि reconciliation action चाहिए या नहीं।

Verifier में `ps | grep` का उपयोग न करें। इससे असंबंधित processes भी मिलेंगी, command names बदल सकते हैं और output formats अलग-अलग होते हैं। `kill -0` केवल दर्ज किए गए PID के जीवित होने का संकेत है। यह completion proof नहीं है, इसलिए verifier PID देखने से पहले final artifacts जाँचता है।

## Finished exit status भी इच्छित प्रभाव का पूरा प्रमाण नहीं हो सकता

Wrapper का अंतिम रिकॉर्ड यह साबित करता है कि wrapper ने क्या देखा, यह ज़रूरी नहीं कि बाहरी दुनिया ने क्या स्वीकार किया। Requests भेजने वाली commands में यह बात साफ़ दिखती है।

मान लीजिए कोई रिमोट script API के ज़रिए DNS record बनाती है और फिर `exit_status=0` लिखती है। API से success मिलने और resolver को नया record दिखने के बीच समय लग सकता है। Deployment script rollout submit करने के बाद सफलतापूर्वक exit कर सकती है, जबकि बाद में health checks विफल हो जाएँ। Database tool सफल connection दिखा सकता है, जबकि multi-step procedure के भीतर एक statement commit हो और बाद वाली statement विफल हो जाए।

हर operation के लिए उसके effect के अनुरूप postcondition चाहिए। Postcondition को बार-बार पढ़ना सुरक्षित होना चाहिए और इतना विशिष्ट होना चाहिए कि पुराने या असंबंधित परिणाम को अस्वीकार कर सके।

Release के लिए run ID को release manifest में लिखें और service से active version पूछें। Database change के लिए migration table में migration identifier और checksum दोनों जाँचें। Generated artifact के लिए file को अंतिम pathname पर रखने के बाद पहले से गणना किए गए SHA-256 digest से तुलना करें। API request के लिए, जहाँ provider सुविधा देता हो, उसका idempotency token भेजें और फिर उस token या आपके सुरक्षित रखे request ID से बना resource पूछें।

सबसे खराब design वह script है जो request भेजने के बाद «done» लिखती है और फिर उस शब्द को प्रमाण मान लेती है। stdout file बताती है कि एक process ने क्या छापा। Postcondition बताती है कि system में अब क्या मौजूद है।

यह अंतर यह भी बताता है कि कोई operation केवल SSH के ज़रिए सुरक्षित रूप से automate नहीं किया जा सकता। अगर रिमोट command किसी ऐसे third-party service को call करती है जिसमें idempotency controls नहीं हैं और जो पिछले request को खोज नहीं सकता, तो बाधित call को वर्गीकृत करना असंभव हो सकता है। उसके चारों ओर human approval या compensating process रखें। अधिक retries missing evidence पैदा नहीं कर सकतीं।

## Recovery का दिखावा करने से बेहतर है idempotency

Verification protocol अनिश्चितता कम करता है। Idempotent command design अनिश्चितता की कीमत कम करता है। दोनों ज़रूरी हैं।

Idempotent operation समान request के साथ दोबारा लागू होने पर उसी इच्छित स्थिति तक पहुँचती है। `mkdir -p /srv/app/cache` इस मॉडल के करीब है। `useradd deploy` ऐसा नहीं है, जब तक script पहले यह न जाँच ले कि मौजूदा account में अपेक्षित properties हैं। `curl -X POST /orders` भी idempotent नहीं है, जब तक service idempotency token समझकर दोहराए गए token को उसी request के रूप में न माने।

«दूसरी बार command शायद कुछ नहीं करेगी» को idempotency न समझें। Deploy command एक ही file को दो बार overwrite कर सकती है, लेकिन दोनों बार restart trigger कर सकती है। Migration tool अपना history पहचान सकता है, फिर भी check करने से पहले खतरनाक initialization चला सकता है। Command का व्यवहार पढ़ें और interruption case का परीक्षण करें।

Requests को stable operation identifier के आधार पर बनाएँ। वही ID remote wrapper को दें और जहाँ संभव हो, target system को भी दें। Remote release wrapper `/var/lib/recorded-runs/$run_id/effect` केवल तब बना सकता है जब active release endpoint माँगा गया version दिखाए। Provisioning API call `run_id` को idempotency value के रूप में इस्तेमाल कर सकती है। तब SSH retry दोनों systems से उसी काम की स्थिति पूछ सकती है।

व्यावहारिक नियम यह है: read को स्वतंत्र रूप से retry करें, create को केवल durable uniqueness constraint के साथ retry करें और multi-stage change को तभी retry करें जब postcondition पहले run को वर्गीकृत कर चुकी हो। यह blind resubmission से धीमा है, लेकिन duplicate infrastructure साफ़ करने से बहुत तेज़ है।

## Command को background में भेजने से समस्या कहीं और चली जाती है

`nohup`, `&`, `disown`, `tmux`, `screen` और service managers समस्या के अलग-अलग हिस्से हल करते हैं। इनमें से कोई भी अनिश्चित remote request को verified result में नहीं बदलता।

सामान्य shell setup में `nohup` process को hangup signal से बचने में मदद करता है। साधारण `nohup task &` के बाद भी आपके पास output files, PID और कोई structured completion record नहीं होता, जब तक आप वह खुद न जोड़ें। Caller के सामने एक नई अस्पष्टता भी आ जाती है: क्या remote shell ने `nohup` शुरू किया था, या उससे पहले ही connection टूट गया?

`tmux` और `screen` interactive environment को जीवित रखते हैं। जब operator को लंबी command से दोबारा जुड़कर उसे हाथ से देखना हो, तब वे अच्छे हैं। Automation contract के रूप में वे कमजोर हैं, क्योंकि session names टकरा सकते हैं, scrollback result schema नहीं है और detached terminal यह नहीं बताता कि इच्छित effect हुआ या नहीं।

जब काम सचमुच service या queued job हो, तब service manager बेहतर विकल्प है। उदाहरण के लिए remote command किसी named unit को submit कर सकती है और बाद की query उस unit का lifecycle तथा logs देख सकती है। जब host पर jobs का operational owner पहले से मौजूद हो, तब यह मॉडल अपनाएँ। केवल पाँच सेकंड की administrative command के लिए service manager जोड़ने की ज़रूरत नहीं, एक छोटा run record पर्याप्त है।

व्यावहारिक विभाजन सरल है। Interactive repair work को terminal multiplexer में रखें। Scheduled या long-lived workload को service manager के अधीन रखें। ऐसे imperative commands के लिए recorded wrapper इस्तेमाल करें जिनका उत्तर SSH caller को दोबारा कनेक्ट करने के बाद भरोसेमंद ढंग से चाहिए।

## Keepalive इंतज़ार घटाते हैं, अस्पष्टता की खिड़की बंद नहीं करते

OpenSSH client keepalives dead connection को जल्दी दिखाते हैं। वे यह गारंटी नहीं देते कि network path विफल होने से पहले command स्वीकार नहीं हुई थी।

जहाँ अटका हुआ client समय बर्बाद करता हो, वहाँ ऐसी client configuration उचित है:

```text
Host production-*
    ServerAliveInterval 20
    ServerAliveCountMax 3
    TCPKeepAlive yes
```

`ServerAliveInterval` तब encrypted SSH channel के ज़रिए application-level messages भेजता है जब कोई data नहीं आ रहा हो। अगर client को पर्याप्त responses नहीं मिलते, तो वह अनिश्चितकाल तक इंतज़ार करने के बजाय exit कर जाता है। OpenSSH इसे TCP keepalives से अलग बताता है, जो transport layer पर काम करते हैं।

इस setting का उपयोग यह तय करने के लिए करें कि caller verification शुरू करने से पहले कितनी देर इंतज़ार करेगा। इसे command delivery assurance न कहें। Remote host command स्वीकार करने के बाद और client को outcome मिलने से पहले भी disconnect हो सकता है।

Multiplexing के साथ भी यही सावधानी रखें। `ControlMaster` और `ControlPersist` कई SSH commands के लिए मौजूदा network connection दोबारा इस्तेमाल कर सकते हैं। इससे setup cost घटती है, लेकिन broken master connection कई calls को एक साथ प्रभावित कर सकता है। OpenSSH manual के अनुसार persisted master मूल client के exit होने के बाद background में रहता है। यह operational रूप से उपयोगी है, लेकिन इससे उस connection पर भेजी गई command के लिए completion evidence नहीं मिलता।

Automation के लिए स्पष्ट connect timeout रखें, environment के अनुरूप liveness limits तय करें और verification path को मूल SSH session से स्वतंत्र बनाएँ। तेज़ failure तभी उपयोगी है जब अगली कार्रवाई status lookup हो, blind retry नहीं।

## रात दो बजे ज़रूरत पड़ने से पहले failure test बनाएँ

जिस protocol को आपने कभी बाधित करके नहीं देखा, वह अभी केवल design sketch है। किसी सुरक्षित लेकिन पर्याप्त धीमे operation से इसका परीक्षण करें, ताकि connection को अलग-अलग चरणों में काट सकें।

ऐसे payload से शुरू करें जो numbered progress file लिखे, stages के बीच sleep करे और अंतिम effect marker लिखे। इसे wrapper के ज़रिए चलाएँ। Remote `started_at` marker दिखने के बाद स्थानीय SSH client को बंद करें, फिर दोबारा कनेक्ट करके verifier चलाएँ। इसके बाद remote payload को `finished_at` लिखने से पहले समाप्त करके दोहराएँ। अंत में, यदि environment अनुमति दे, तो host restart का simulation करें।

आपकी अपेक्षित classifications स्पष्ट होनी चाहिए:

- Wrapper के run ID claim करने से पहले verification `absent` लौटाती है।
- Payload execution के दौरान verification `running` लौटाती है।
- सामान्य completion के बाद यह recorded status के साथ `finished` लौटाती है।
- Forced interruption या host loss के बाद यह `damaged` लौटाती है, जिसके बाद postcondition check होती है।

Duplicate submission का भी परीक्षण करें। लगभग एक ही समय पर वही run ID दो बार invoke करें। एक call को directory claim मिलना चाहिए। दूसरी को payload चलाए बिना स्पष्ट duplicate result लौटाना चाहिए। इसके बाद अलग-अलग IDs आज़माएँ जो एक ही resource को target करती हों। अगर इससे race बनती है, तो wrapper को resource-specific lock चाहिए या target system को uniqueness rule।

Run records को अपने operational retry window तक सुरक्षित रखें। अगर job को एक दिन बाद retry किया जा सकता है, लेकिन host records एक घंटे बाद मिटा देता है, तो आपने अपनी uncertainty में एक timer जोड़ दिया है। Records को casual modification से भी बचाएँ। Run verify करने वाले account को `exit_status` edit करने या payload log बदलने की अनुमति नहीं होनी चाहिए। Shared systems पर operating model अनुमति दे तो submitter, runner और reader roles अलग रखें।

जब AI agents SSH चलाते हैं, तो credential isolation और command verification को साथ काम करना चाहिए। Sallyport SSH credentials को अपने vault में रखता है और agent द्वारा माँगी गई action दर्ज कर सकता है, जबकि remote wrapper job के बारे में स्थायी उत्तर देता है। Action record यह बता सकता है कि command किस process ने माँगी थी। Remote run ID बताती है कि connection अनिश्चित होने के बाद क्या हुआ।

## सुरक्षित retry निर्णय के चार परिणाम हैं

Disconnect के बाद पहले classification करें, फिर कार्रवाई। चार उपयोगी परिणाम हैं और इनमें से केवल एक automatic retry है।

अगर run `absent` है, तो remote wrapper ने उसका record कभी बनाया ही नहीं। Wrapper के create-once व्यवहार पर भरोसा हो तो वही run ID देकर operation submit कर सकते हैं। अगर command wrapper के बाहर चल सकती थी, तो पहले target की जाँच करें, क्योंकि wrapper bypass हुई command के बारे में प्रमाण नहीं दे सकता।

अगर run `running` है, तो operation-specific control path से इंतज़ार करें या cancel करें। दूसरी copy शुरू न करें। Timeout policy payload या job manager में होनी चाहिए, ऐसी दूसरी SSH invocation में नहीं जो पहली के साथ race करे।

अगर run `finished` है, तो उसका exit status देखें और फिर meaningful external effects होने पर postcondition जाँचें। Zero exit status के साथ failed postcondition का मतलब failed operation है। Effect check को authoritative मानें।

अगर run `damaged` है, तो इसे retry problem कहना बंद करें। यह reconciliation work है। Logs, journal entries, target state और idempotency record देखें। तय करें कि partial effect को repair करना है, operation को complete mark करना है या ऐसा नया run submit करना है जो देखी गई स्थिति को स्पष्ट रूप से संभाले। Missing record ने automation का आधार बनने वाला प्रमाण हटा दिया है, इसलिए इस निर्णय में इंसान की ज़रूरत पड़ सकती है।

बदलाव छोटा है, लेकिन महत्वपूर्ण: जो command दोबारा चलने पर नुकसान पहुँचा सकती है, उसे run ID, remote start record, remote final record और target-state check दें। SSH फिर भी disconnect होगा। आपकी automation को अब यह दिखावा नहीं करना पड़ेगा कि उसे पता है क्या हुआ।
