BS = chr(92)

# ---------------------------------------------------------------- the capture helper
cap = [
"# ---- run-bound process identity, captured WHILE THE PROCESSES ARE LIVE ----------",
"# Cleanup can only re-query an identity somebody wrote down. This is where it is written",
"# down, immediately after the elevated leg that starts the daemon, and it is the ONLY basis",
"# on which cleanup later says a process of this run is gone.",
"#",
"# WHY NOT A COMMAND-LINE MATCH (doyle, 2026-09-13): r10's actual survivors ran",
"# `daemon run --detached` and `daemon brain --generation 0 --start-reason cold` -- neither",
"# carries the isolated home, so a home-string predicate reports a confident CLEAR over exactly",
"# the processes that were left behind. The pid FILE names the supervisor, the process TABLE",
"# gives it a creation time, an executable path and a command line, and the parent relationship",
"# gives the brain. Those are what get re-queried.",
"#",
"# It records, and records nothing it could not read. A missing pid file, a non-numeric pid, an",
"# unreadable table: each is written as IDENT_PROBE=UNREADABLE with its reason, and cleanup reads",
"# that as UNREADABLE rather than as an empty list.",
"capture_run_identities() { # WHEN",
'  local when="$1"',
'  SPT_D2_HOME="$H" pwsh -NoProfile -Command \'',
"    $h = $env:SPT_D2_HOME",
'    $pidFile = Join-Path $h "daemon.pid"',
'    if (-not (Test-Path $pidFile)) { "IDENT_PROBE=UNREADABLE reason=no-pid-file"; exit 0 }',
"    $sp = (Get-Content -Raw $pidFile).Trim()",
'    if ($sp -notmatch "^[0-9]+$") { "IDENT_PROBE=UNREADABLE reason=pid-file-not-numeric"; exit 0 }',
"    try { $all = @(Get-CimInstance Win32_Process -ErrorAction Stop) }",
'    catch { "IDENT_PROBE=UNREADABLE reason=enumeration-failed"; exit 0 }',
'    if ($all.Count -eq 0) { "IDENT_PROBE=UNREADABLE reason=empty-enumeration-is-not-an-empty-host"; exit 0 }',
'    "IDENT_ENUM total=$($all.Count) supervisor_pid_from_file=$sp"',
"    $target = [int]$sp",
"    $sup = @($all | Where-Object { $_.ProcessId -eq $target })",
'    if ($sup.Count -eq 0) { "IDENT_PROBE=UNREADABLE reason=supervisor-pid-$sp-not-in-process-table"; exit 0 }',
"    $rows = @($sup[0]) + @($all | Where-Object { $_.ParentProcessId -eq $target })",
"    $i = 0",
"    foreach ($p in $rows) {",
'      $role = if ($i -eq 0) { "supervisor" } else { "brain" }',
'      $c = try { $p.CreationDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") } catch { "UNREADABLE" }',
'      "IDENT role=$role pid=$($p.ProcessId) created=$c exe=[$($p.ExecutablePath)] cmd=[$($p.CommandLine)]"',
"      $i++",
"    }",
'    "IDENT_PROBE=OK tracked=$($rows.Count)"',
"  ' > \"$R/run-identities.txt\" 2>&1",
"  local rc=$?",
'  exits "identity_capture_${when}_EXIT=$rc"',
'  while IFS= read -r line; do record "  IDENTITY($when) $line"; done < "$R/run-identities.txt"',
"  local n",
"  n=$(grep -c '^IDENT role=' \"$R/run-identities.txt\" 2>/dev/null)",
"  case \"$n\" in (''|*[!0-9]*) n=0 ;; esac",
'  if [ "$n" -eq 0 ]; then',
'    record "  IDENTITY($when) NO PROCESS TRACKED. Cleanup will read this as UNREADABLE, never as clean: an identity nobody captured cannot later be shown to be gone."',
"  else",
'    record "  IDENTITY($when) $n process identities recorded (supervisor from daemon.pid plus its children). These, re-queried fresh, are what decide the cleanup verdict."',
"  fi",
"  return 0",
"}",
"",
]

p = 'd2/_part_cleanup.sh'
s = open(p, encoding='utf-8', newline='').read()
anchor = "# ---- state-aware cleanup, entered EXACTLY ONCE"
i = s.index(anchor)
s = s[:i] + "\n".join(cap) + s[i:]

# ledger: report the three residual facts separately
old = '    echo "  rig_daemon_stop_exit=${DAEMON_STOP_RC:-NOT_RUN}  listener_after_cleanup=${LISTENER_STATE:-UNMEASURED}"'
new = ('    echo "  tracked_process_identities: live=${RESIDUE_LIVE:-0} gone=${RESIDUE_GONE:-0} '
       'unreadable=${RESIDUE_UNREADABLE:-0} (re-queried by pid + creation time + executable + command line)"\n'
       + old)
assert s.count(old) == 1
s = s.replace(old, new)
open(p, 'w', encoding='utf-8', newline='').write(s)

# ---------------------------------------------------------------- call it after the setup
p = 'd2/_part_main.sh'
s = open(p, encoding='utf-8', newline='').read()
anchor2 = "# ===========================================================================\n# S10  t2"
add = [
"# ===========================================================================",
"# S9a  RUN-BOUND PROCESS IDENTITY — captured NOW, while the leg's processes are live",
"# ===========================================================================",
"# The elevated setup dispatches through the daemon (ensure_running, serveverb.rs:191), so if a",
"# supervisor and a brain exist at all, they exist now. Cleanup cannot re-query what was never",
"# recorded, and it must never fall back to guessing by command-line text: the r10 survivors'",
"# command lines contain nothing that identifies this run.",
"capture_run_identities post-setup",
"",
]
assert s.count(anchor2) == 1
s = s.replace(anchor2, "\n".join(add) + anchor2)
open(p, 'w', encoding='utf-8', newline='').write(s)

# ---------------------------------------------------------------- globals
p = 'd2/_part1_header.sh'
s = open(p, encoding='utf-8', newline='').read()
a = "RESIDUE_COUNT=UNMEASURED"
assert s.count(a) == 1
s = s.replace(a, "RESIDUE_COUNT=UNMEASURED\n"
                 "RESIDUE_LIVE=0                   # tracked identities re-queried and still running\n"
                 "RESIDUE_GONE=0                   # tracked identities measured absent (or pid-reused)\n"
                 "RESIDUE_UNREADABLE=0             # attribution missing or unreadable: NEVER read as clean")
open(p, 'w', encoding='utf-8', newline='').write(s)
print('capture step, ledger fields and globals added')
