# W3 slice 1 — BUILT FOR GATE (todlando → doyle, 2026-07-07)

Two W3 REQs complete + green on branch `lifecycle-truth` (pushed). Gate when ready.

## REQ-UPDATE-FINISH-ENDPOINT-SURVIVAL — the field-accept unblocker (seed #6) @049b2be

Daemon restart no longer massacres hosted endpoints. New one-shot
`livehost::resume_restart_orphaned_endpoints` runs at the fresh broker's live-host
start, BEFORE the phantom-clear reconcile offlines the stale-online records (the "was
online at restart" signal), and re-runs each orphaned harness from its last ledger
session via the shared `launch_ledger_resume` (extracted from `resume_woken_endpoint`'s
Resume arm, tagged `DAEMON_RESTART`; mind rides psyche re-host as today).

Gate + **both your binding belts** (pure `restart_resume_gate`, unit-matrixed):
- `status==online` (was online at the fresh-broker restart — a signed-off/offline
  record is never resurrected)
- `controllable==Some(true)` (Q1 source-definitive spt-hosted discriminator — excludes
  user-terminal `Some(false)` + legacy `None`, the safe direction)
- NOT in the live broker session set (orphaned, not a surviving brain-restart session)
- **BELT 1** no relay address (a relay = harness-hosted, `resolve_address` None)
- **BELT 2** custody-pid-NOT-alive (dup guard — `RefuseLivePid` loudly if alive, the
  breadcrumb feeds the W4 dup-wake rig; never re-run over a live process)
- broker unreachable ⇒ skip entirely (never mass-respawn on a hiccup)

Int `endpoint_survival.rs`: a fresh broker (empty session table = post-`daemon stop`
state) + a seeded orphaned online/controllable/relay-less/dead-custody endpoint with a
ledger row → the pass re-spawns its harness into a live broker PTY session. **RED-first
proven** (neuter the pass → no session). Belts unit-proven over the full matrix.

## REQ-DAEMON-STDERR-PERSIST — the RCA-blind fix @32c789e

New `spt-daemon::stderrlog`: at startup each process redirects its OWN stderr FD to
`SPT_HOME/logs/daemon.stderr.log`, opened IN-PROCESS (never inherited/piped — KH 5.6:
a file never blocks, which is why the null-guard already keeps a file stderr).
Windows `SetStdHandle`, unix `dup2`. Wired: broker at `cmd_daemon_run` BEFORE the
null-handles guard (so the pipe-defense then sees a safe file), brain at `run_brain`
first line (captures `BRAIN_UP` + connect failures). Rotation at install (5 MB cap →
`.1`, keep 2 = 2×5 MB), role+generation stamped. Within-generation the file appends
(bounded by generation turnover); a hard live cap is a documented follow-up — the
contract is capture-not-null. 4 units (rotation kernel + path/stamp).

## Local gates (post-crash-recovery clean, `CARGO_INCREMENTAL=0`)
- clippy `-p spt-daemon --all-targets` clean.
- livehost units + `endpoint_survival` 21/21; `stderrlog` 4/4.
- `traceable-reqs check` exit 0 — ENDPOINT-SURVIVAL `+impl +unit +int`, STDERR-PERSIST
  `+impl +unit`.

Not yet re-run: full-workspace nextest (broker.rs untouched this slice; livehost is the
seam — I'll run the full sweep before declaring the whole wave done, or at your gate).

## Remaining W3 (building next, in order)
REQ-DAEMON-STOP-LIVE-SESSION-WARN → REQ-RC-RECONNECT-TRUTH → REQ-UPDATE-ONE-SHOT-FINISH
(composes on ENDPOINT-SURVIVAL) → REQ-UPDATE-PROMOTE-DRAINED (mechanic-d + false-promote
rig, the binding W3 activation condition from your W2 verdict).
