### IR-82 — a daemon death leaves NO record of who died: autostart decides on a socket ping, says "no daemon", and the successor overwrites the only handle on the corpse

- **Status:** OPEN, drafted by hertz 2026-09-07 11:0xZ on doyle's dispatch (11:03Z: *"the autostart
  death-cause item is an IR, not a board item — mint it IR-82 in your thin PR"*). Register on main
  ends at IR-78; 79/80/81 are drafts in the same PR. · **Origin:** the fleet-daemon death on
  hfenduleam at 08:03:14.87Z ([[RCA-FLEET-DAEMON-14444]]) whose cause is **still UNNAMED after a day
  of measurement** — and the reason it is unnamed is this entry. Filed as a defect in
  OBSERVABILITY, not in the death.
- **⚠ THIS IS NOT "find the killer."** The killer hunt is the RCA and stays open there. This entry
  is about the fact that a killer hunt had to be run at all from a cold start, with a watcher script
  written by hand after the event, because the product recorded nothing about the transition at the
  moment it noticed it.

- **THE MEASUREMENT — all four facts read at source, no build (gate was live on both boxes).**

  1. **The daemon writes its pid.** `crates/spt-daemon/src/daemon.rs:471` —
     `let _ = std::fs::write(daemon_pid_path(), std::process::id().to_string());`, into
     `<spt_home>/daemon.pid` (`crates/spt-daemon/src/endpoint.rs:81`). Unconditional overwrite, no
     read-before-write, no rotation, no history.
  2. **Autostart never looks at it.** `ensure_running_outcome`
     (`crates/spt-daemon/src/daemon.rs:686-724`) decides on `is_running()`, which is
     `seedmap::ping(&seed_socket_name()).is_ok()` (`daemon.rs:598-600`) — **a socket ping and
     nothing else.** The decision consults no pid, no mtime, no prior state.
  3. **The breadcrumb it emits carries no identity.** `daemon.rs:721` —
     `DAEMON_AUTOSTART: no daemon and no standing operator stop — starting one`. Deliberately
     "always-on, ids-free" per its own comment, because its job is to be COUNTED (the convoy
     serialization observable that `daemon_stop_convoy_e2e` asserts at :178/:237). That design is
     correct for what it was built for and is exactly why it answers nothing here.
  4. **Then the successor destroys the evidence.** The new daemon reaches :471 and overwrites
     `daemon.pid` with its own pid. The dead pid, and the file mtime that bracketed its life, are
     gone — overwritten by the very event that should have recorded them.

- **THE SHARP FORM — the file is read ONLY in the state where it can tell you nothing.** The only
  two readers of `daemon_pid_path()` in the tree are `crates/spt/src/cli.rs:8581` and `:8754` (both inside `cmd_daemon_status`, :8573), and
  **both are gated on `running`** (`if running { let pid = read(...) }`). So `daemon.pid` is consulted
  exclusively when a daemon is alive — when its contents are guaranteed fresh and merely restate
  what the ping already proved — and is ignored in the one state where its staleness is the only
  evidence anyone has. Its doc comment (`endpoint.rs:82-85`) says *"a stale pid here is harmless
  because callers probe the socket, never this file."* **That is true for LIVENESS and false for
  FORENSICS**, and the entry is that the codebase has only ever considered the first reading.

- **WHAT IT COST, concretely, on 2026-09-07.** Daemon pid 14444 died at 08:03:14.87Z inside the ci
  Windows unit leg's nextest LIST phase; successor 48232 cold-started at 08:03:20Z and
  `DAEMON_RESTART_RESUME`d every session, orphaning live agents' monitors and readers and handing at
  least one agent a stale brief. Neither peer ran a kill. To get ANY signal at all I had to hand-write
  an external watcher (`C:\Users\decid\.spt-watch\watch-daemon.ps1`, 1s cadence, identity re-verified
  by image path AND creation time every poll) **after** the death, which by construction cannot
  observe the event it was written for. 6600+ polls later the successor is alive and the original
  death is still uncharacterized. A product-side record of the transition would have cost bytes.

- **WHY THIS IS NOT THE CONVOY BREADCRUMB'S JOB, and must not be bolted onto it.**
  `DAEMON_AUTOSTART` is load-bearing as a COUNTABLE line: `daemon_stop_convoy_e2e` asserts *exactly
  one* appears under a storm (:178) and counts N under a deliberate race (:237). Adding identity
  fields to that line risks the count and buries a forensic record inside a line whose contract is
  its cardinality. **The record belongs in its own line and its own artifact.**

- **REMEDY — proposed, for doyle to rule; deliberately small, product-side, no wire change.**
  1. **Read before you clobber.** At `daemon.rs:471`, read the existing `daemon.pid` and its mtime
     BEFORE the write; if it holds a pid that is not ours, emit a distinct, greppable line naming
     the predecessor pid and how long ago the file was last touched — e.g.
     `DAEMON_SUCCEEDS_PID: prior=<pid> prior_pidfile_age_ms=<n> reason=<socket-ping-failed>`. The
     value is that the successor is the only process in the system that is guaranteed to run at
     exactly the moment the question becomes askable.
  2. **Keep one generation.** Rename the existing `daemon.pid` to `daemon.pid.prev` instead of
     overwriting it (best-effort, same directory, same failure posture as today's `let _ =`). One
     generation is enough to answer "which pid did I replace, and when did it last write?" and costs
     no rotation policy.
  3. **Say what the ping saw.** `ensure_running_outcome` may record, on the same line, that its
     decision came from a FAILED socket ping rather than from a never-existing daemon — the two are
     indistinguishable in today's output and they are different incidents.
  4. **NOT proposed:** a liveness change. `is_running()` must stay the socket ping — this entry does
     not argue the pidfile should become a liveness signal, which is the exact mistake
     `endpoint.rs:82-85` was written to prevent. The pidfile stays advisory; it just stops being
     silently destroyed.

- **Kin:** [[RCA-FLEET-DAEMON-14444]] (the open incident this serves — IR-82 does not close it and
  must not be read as closing it), [[IR-81]] (a pid is not an identity — the same lesson from the
  KILL side; here it is the RECORD side), [[IR-80]]/[[IR-79]] (processes outliving what should have
  accounted for them). The through-line doyle named at 10:22Z holds: *a pid is not an identity and a
  pid is not a tree* — and IR-82 adds that **a pid nobody wrote down is not evidence.**

- **Falsifiable claim, so it can be checked rather than believed:** at `f3c8495b` the tree contains
  exactly ONE writer of `daemon_pid_path()` (`daemon.rs:471`) and exactly TWO readers
  (`cli.rs:8581`, `cli.rs:8754`), both inside a `running` gate. If a future reader appears in an
  unguarded path, this entry's premise weakens and it should be re-measured, not re-asserted.
