# LIFECYCLE-TRUTH W4 — dispatch + design ruling (doyle, 2026-07-07)

**GO todlando.** W4 = spawn/wake + listener lifecycle. Spec = `docs/NEXT-MILESTONE-LIFECYCLETRUTH-TRIAGE.md` §W4 (roots fully pinned) + backlog seeds #5 (listen-orphan) / #7 (dup-resume). Two REQs, both this wave:

- **REQ-SPAWN-COLLISION-GUARD-LIVE-DUP** — required_stages `[impl, unit, int]`
- **REQ-HAZARD-LISTEN-ORPHAN** — required_stages `[impl, unit]`

Activate both in the work-start commit (rule 5). RED-first ints. Signal wave head; I gate both legs (Win worktree + kitsubito Linux) + the spawn/listener seam sweep.

## Ruling 1 — single-flight wake: claim BROKER-SIDE, not on the perch record

The REQ text offers "claim on the perch record OR a broker-side in-flight set." **Ruling: broker-side in-flight set, keyed by endpoint id.** Reasoning:

- Both observed dup spawns were **direct broker children** (pid 306368) — the broker IS the single spawner and the single-flight authority. An in-process set is the natural choke point.
- A **perch-record claim would add another info.json writer** — the exact contended-record class this REQ exists to kill (the duplicate's compact stomping a fresh-/clear sid). Do not fix a write-stomp by adding a writer.
- The RC WMI auto-launch path (the other historical spawner) is **already removed** by W3 REQ-RC-RECONNECT-TRUTH — so post-W3 the broker is the sole wake/respawn spawner. No cross-process claim needed.

Shape: broker holds an in-flight-wake set keyed by id; a wake claims the id (atomic under the broker's existing sessions lock or a dedicated mutex — follow the established sessions→log lock order, no I/O under lock); the spawn path **re-checks liveness UNDER the claim** (close the check-then-spawn TOCTOU); a second wake for a claimed/live id is a **no-op ack** (not an error). Release the claim when the spawn tree is established or fails. **Int (RED-first):** two concurrent wake requests for one id → exactly one launch tree (pre-fix: two survive).

## Ruling 2 — listen-orphan watchdog: POLL baseline, OS-native optional

The REQ offers "job object / PDEATHSIG or poll." **Ruling: bounded POLL of `--parent-pid` liveness as the portable baseline** (matches the unit "parent-death → listener exits within one poll window"). Reasoning: one code path both platforms, no OS-handle plumbing, and the latency floor (one poll window, seconds) is fine for a headless-gateway orphan — the failure is "held ONLINE for minutes," so seconds-to-exit is a full fix. OS-native tightening (Win job object / Linux PDEATHSIG, immediate exit) is an **optional** follow-up, not required for the wave. The listener exits **loud** on parent death (names itself + the dead parent), so a false-ONLINE perch flips OFFLINE within one window and dead-owner rebind unblocks.

Note: perri's spt-mobile-side job-object guard (SPT-CORE-NEEDS §5) stays regardless — **independent defense-in-depth**, her side; the core watchdog is the authoritative fix. No conflict, both proceed.

## Not in this dispatch (separate tracks)
- **perri's REQ-SUPERVISOR-RELAY-RESPAWN** (adapter relay-respawn belt, minted inactive awaiting my ruling) — I rule that to perri separately; it composes with, not blocks, the core LISTEN-ORPHAN watchdog.
- **W1 field-accept swap window** — operator-coordinated, orthogonal to building W4.
