# MSG-IDENTITY milestone — dispatch (doyle, 2026-07-10)

Operator GO (2026-07-10): *"next milestone: include those two triages plus the most prominent
backlog seeds, especially bugs that affect messaging and endpoint identity … once sufficient data
is gathered, find any missing root causes, write the milestone JIT plan, then coordinate with
todlando and deployah to build → drive to release publish."* **F-037 (short msg ids) EXCLUDED by
operator — covered later.** Test-hardening cluster stays queued (not messaging/identity).

Registry: all wave REQs registered on this branch (`triage/msg-identity-milestone`, off
main@d73e19e = v0.30.6). Stages are QUEUED — **todlando flips each wave's `required_stages` IN
the build commit with its evidence tags** ([[traceable-per-wave-activation]]; the triage PR keeps
`traceable-reqs check` green).

Lanes ([[dont-solo-across-role-lines]]): doyle triage/gate · todlando build · deployah publish.
One release at the end (target v0.31.0 — new verb = minor, not patch); doyle may split an early
cut if W4 RCA drags. Field-verify partners noted per wave.

## Waves (build order)

### W1 — IDENTITY-ENV (F-036 core legs; stops the seat-theft class)
- `REQ-HAZARD-DAEMON-IDENTITY-ENV-SANITIZE` [doc,impl,unit] — daemon scrubs inherited
  `SPT_ENDPOINT_ID`/`OWL_SESSION_ID`/`SPT_AGENT_ID` at startup + before EVERY role spawn
  (core defense; today only the role's declared `env_remove` is stripped, runtime.rs:728).
  Root: perri F-036 RCA — leaked `SPT_ENDPOINT_ID=doyle` through psyche_resume spawns rotated
  doyle's perch to psyche sids every pulse. Adapter half shipped v0.18.8.
  **Fold:** the leg-b docs fix — `recursion_guard_env` schema description is role-agnostic
  (manifest.rs:314 + crates/spt-runtime/manifest.schema.json:306, drop "summarizer children").
- `REQ-BIND-PSYCHE-CUSTODY-SQUAT-GUARD` [impl,unit] — refuse a bind whose `--set-session-id`
  equals a psyche's OWN custody sid (core owns psyche-custody.json; makes the class unreachable).
- Roots pinned; no RCA. Field-verify: perri (psyches stay dark-clean post-fix).

### W2 — BRINGUP-BIND (F-034 legs a/b/c; hertz /sptc:live cascade)
- `REQ-ADAPTER-UNRESOLVED-HINT-FORM` [impl,unit] — hint prints the clap-valid group-level
  `--adapter` form. Trivial.
- `REQ-LISTEN-SEED-CONSUME-AFTER-BIND` [doc,impl,unit,int] — validate + BIND first, consume the
  seed only on success (same effect-before-irreversible-consume class as F-032). Doc stage pins
  the refusal-consumption contract in harness-contract/api.md.
- `REQ-LISTEN-SESSION-ID-FALLBACK` [doc,impl,unit,int] — `listen --session-id <sid>` binds when
  the ephemeral seed is gone (late go-live / post-daemon-restart). Doc stage pins seed lifetime.
- Roots pinned (scope F-034-sptc-live-core-legs.md). Field-verify: hertz + perri.

### W3 — INGEST-DURABILITY (F-032; data-loss)
- `REQ-HAZARD-COMMUNE-INGEST-BLACKHOLE` [impl,unit,int] — never delete a drop whose slice
  wasn't durably committed (ingest.rs:156 empty-project_id gate vs :200 unconditional delete).
  Distinguish precedence-SUPPRESSED (safe delete) from un-committable-now (keep/preserve).
  Repro fixture banked (F-032-commune-2026-07-08T222721Z.md). Field-verify: perri.

### W4 — DELIVERY-INTEGRITY (dup-delivery cluster; **RCA-first**)
Instrument exists: REQ-SPOOL-TAKE-AUDIT shipped (spool `taken_leg/taken_sid/taken_at`,
hidden `spt spool-audit --json`) — use it; do not rebuild it.
- `REQ-LIVE-AGENT-NO-INJECT-DELIVERY` [impl,unit] (F-033) — root ALREADY code-grounded:
  `is_spt_hosted_no_relay` (inject.rs:37-49) treats a controllable live_agent with no relay
  address as inject-target → duplicate + stuck-unsubmitted input garbage. Fix = exclude
  state:live_agent from the inject predicate (perri confirmed core-only). Buildable immediately.
- `REQ-CARRIER-CLAIM-EXCLUSIVE` [impl,unit,int] — **RCA-first**: operator-observed default-window
  send to BUSY doyle delivered via BOTH poll AND relay (spool row 156 `taken_leg='idle-inject'`).
  Pin with spool-audit: poll-drain vs relay delivered=1 ordering; busy→idle edge re-offer;
  take-mark atomicity. Then: one atomic cross-carrier claim per row.
- `REQ-RELAY-NO-BUSY-DELIVER` [impl,unit] — companion: idle-edge drain re-verifies activity /
  defers to the claim. May REDUCE to an ordering assertion under the exclusive claim — doyle
  rules at RCA lock; don't build blind. **Do not break** F-023 anti-starvation +
  REQ-IDLE-PARKED-DELIVERY (already-idle delivery stays green).
- `REQ-SEND-STAMP-AGENT-ID` [impl,unit,int] — a live session's shelled `spt send` stamps the
  agent id, not `cli@<node>` (send-time self-resolve via the existing session-pin machinery).
  Identity leg; independent of the RCA — can build parallel.

### W5 — AUTOSTART (F-038; flynn/mobile-gw, operator-directed)
- `REQ-ENDPOINT-AUTOSTART` [doc,impl,unit,int] — **shape (a) RULED**: `spt endpoint run --save`
  persists a startup default replayed at daemon start (symmetric with shipped `subnet attach
  --save`). Shape (c) restore-what-was-up REJECTED for now (couples to the effective_rest_state /
  F-035 reader-parity neighborhood that just churned). Loud-skip on failed replay; never blocks
  daemon start. Deployah field-confirmed the gap live (mobile-gw alive=false after the v0.30.6
  restart). Field-verify: flynn (their logon-task interim dissolves).
- `REQ-DAEMON-SERVICE-INSTALL` stays QUEUED (rider, infra-provisioning milestone).

### W6 — POISON-LOG-SCOPE (F-039; deployah v0.30.6 PASS handoff, **RCA-first**)
- `REQ-CONN-POISON-DIAL-SCOPE` [impl,unit] — CONN_WRITE_POISONED churn 1:1 with PUMP_PEER_FAIL
  submit-dials to offline peers; no wedge, pre-existed the rig. Code context: poison_and_cancel
  (conn.rs:181) logs the SAME loud token for "exceeded bound **(or failed)**" — the fast-FAIL
  branch shares the wedge observable's tag. **RCA-first: pin WHICH broker-conn write fails per
  pump dial cycle** (fan-out to a departed subscriber? brain-side notification?) — the 1:1
  correlation mechanism is unpinned. Fix: loud token reserved for DEADLINE-EXCEEDED; fast-fail
  retires quietly. **Poison/retire behavior unchanged** (REQ-HAZARD-SHAREDSEND invariant holds).

## Sequencing + gates

- W1 first (identity infection class), then W2/W3 (roots pinned, parallelizable), W4 RCA starts
  alongside (instrument is shipped; F-033 leg buildable day one), W5/W6 tail.
- Branch discipline: build branches off THIS branch (or main once the triage PR merges).
  Worktrees under `.worktrees/` only. Gates in isolated worktree + throwaway CARGO_TARGET_DIR,
  nextest, clippy --workspace preflight, `unset SPT_INJECT_VERIFY_ECHO`.
- doyle gates each wave against this doc + CONTEXT.md/ADRs; RCA locks (W4, W6) come back to
  doyle for fix-shape ruling BEFORE build.
- Release: deployah, counter from published metadata, bump rides IN the release PR. CHANGELOG
  census = field symptoms across all six waves (dup deliveries, seat-theft, lost communes,
  bringup dead-ends, gateway-down-after-restart, poison log floods).

<!-- Co-authored by: doyle -->
