{
  "REQ-TEARDOWN-UNCOOPERATIVE-HOST": {
    "title": "TEARDOWN-AUTHORITY W1 (ADR-0045 decision 4): the teardown kill NEVER depends on harness cooperation — no graceful-input path, no waiting on PTY EOF, no ask-it-to-exit-first step. A WEDGED host is the design case, not the edge case: it is the situation that produced the ADR (doyle's own endpoint — read-only rc replayed the retained PTY and `rc --take` acquired control, but a direct prompt produced no output for 30+s because the retained harness subtree itself was nonresponsive while the broker correctly preserved and replayed its last PTY state). Any cooperation-dependent step re-imports the exact hang the verb exists to break. Gate: impl — the kill path proves no dependency on session responsiveness; int — a deliberately nonresponsive/wedged hosted harness is still reaped within the bound (broker row gone + subtree gone), asserted against a real broker-hosted session.",
    "doc": "Decisions: <!-- --> **4. The kill never depends on harness cooperation** — no graceful-input path, no waiting on PTY EOF, no \"ask it to exit first\". A wedged host is the design case, not the edge case: it is the situation that produced this ADR."
  },
  "REQ-PICKER-OFFLINE-NO-VIEW": {
    "title": "A3 (F028, operator #3): 'View now (read-only)' is offered for OFFLINE endpoints. model.rs:1030 offline branch of confirm_options is vec![Start, View] — View is meaningless with no live PTY. FIX: offline set = [Start] (+ the existing Resume/ChangeAdapter/Instantiate/Fork/Shortcut tail). Update the view.rs options tests. See triage A3.",
    "doc": ""
  },
  "REQ-GOSSIP-CONTROLLED-ANY": {
    "title": "Bug #3: a locally-controlled endpoint gossips controller_node = None so remote viewers show it free to control. Root: driven_by is stamped Some(node) only for a REMOTE WAN attach (attach.rs:337); a local controller is by=None by design (broker.rs:1750, KH 7.15 — a local-only controller must not latch driven_by). Fix: broker stamps a SEPARATE any-controller datum (true/Some(host) for a local OR remote controller) alongside stamp_driven_by, and advertise_local gossips Instance controller_node from it, leaving the remote-only driven_by untouched (do not trip REQ-HAZARD-DRIVEN-BY-SELFHEAL). node-refresh is NOT the fix (data is absent at source). See docs/NEXT-MILESTONE-BUG-TRIAGE.md #3.",
    "doc": ""
  },
  "REQ-MSG-3": {
    "title": "Ready-agent lifecycle: register perch (info.json + listener + registry address) on ready, drain spooled backlog on startup, clean teardown",
    "doc": ""
  },
  "REQ-HAZARD-BRAIN-RESTART-PSYCHE-DUP": {
    "title": "A bare brain restart leaves EXACTLY ONE `{id}-psyche` process per endpoint — no duplicate. On an abrupt brain death stop_host never runs (the LiveSet + owned child handles die with the brain) and Breap's job/group only reaps at DAEMON stop, so the PRIOR brain's Psyche stays ALIVE; the respawned brain's reconcile re-hosts a SECOND Psyche and overwrites the `{id}-psyche` perch pid, leaving the old one untracked + alive = a duplicate that lingers until daemon-stop (the operator's 'brain kill+restart wedged everything'). FIX: at brain start, BEFORE the first reconcile re-hosts, reap any pre-existing `{id}-psyche` orphan — ID-SPECIFICALLY (recycle-safe on the shared box, where sibling agents share the `claude` basename): scoped-kill the recorded pid ONLY IF it is alive AND its exe basename == the adapter's psyche program (normalize_basename) AND its COMMAND LINE contains the full psyche id `<id>-psyche` (baked via {id}); a sibling never carries THIS id, and any unreadable signal FAILS SAFE (decline to reap — a missed dup is bounded by Breap, a wrong-kill is catastrophic). CAVEAT: the cmdline carries `<id>-psyche` only when the adapter's psyche_init.command uses {id} (the norm); a non-{i",
    "doc": ""
  },
  "REQ-VIEWER-SKIP-TO-LIVE-ON-EVICT": {
    "title": "A `rc --view` VIEWER that overflows its broker subscription queue and is EVICTED (OutputLog::append try_send Full → viewers.remove, REQ-HAZARD-VIEWER-ISOLATION session-protection) must SKIP TO LIVE, not die silently. ROOT (v0.13.0, b4 JIT item 2 = p0_paste + post-b4 a_journaled-Linux, ONE root): serve_attach forwards each frame (read_event→b64decode→re-encode AttachRecord→net_stream_send) SLOWER than the drain fans out under flood → its VIEWER_CHANNEL_DEPTH(256) channel overflows → the drain evicts (viewers.remove drops the ViewerSink → drops tx → viewer_writer's rx.recv() Err → the writer returns WRITING NOTHING) → serve_attach's brain.read_event() just STOPS getting Output (no EOF, no error) → serve_attach blocks forever → the operator receives nothing (attach_received_output=FALSE). Eviction-of-a-hopelessly-behind-viewer is CORRECT session-protection (keep it); SILENT+PERMANENT eviction is the bug. VIEWER-only → B2-SAFE (a viewer never advances delivered_through / is not authoritative / exposes no resume cursor). FIX (doyle-gated, skip-to-live = tail -f reconnect): (1) explicit broker→viewer EVICTION SIGNAL (KIND_VIEWER_EVICTED, written in the viewer_writer thread OFF the log lo",
    "doc": "Shell sleep/wake (offline ↔ online): <!-- --> **BUILT (M12 W2.5).** The controller/viewer model is implemented end-to-end. Attach intent is **three-valued** (`AttachIntent = Viewer | Control | Take`, wire-default `Control`): `Control` to a FREE endpoint becomes controller; `Control` to a CONTROLLED endpoint is **refused with guidance** (`--view` to watch, `--take` to control) — never auto-viewer, never silent-displace; `Take` (`spt rc --take` / picker \"Kick\") kicks the incumbent with a **loud `Displaced{by}` notice** and full detach (not demote). The broker's per-session `OutputLog` is the fan"
  },
  "REQ-PSYCHE-SPAWN-ENV-PARITY": {
    "title": "P-2 (WORKER-TRUTH triage addendum, perri-filed field finding 2026-07-06): the per-event psyche_resume spawn threads the perch record's CAPTURED read_env stamps into the spawn ENVIRONMENT — the F-027 Half-B env-parity contract (BINDING, design-frozen: read_env captured at creation + stamped on the record + threaded IDENTICALLY to every session spawn; the spawn never reads its own process env for a stamped var) extended to the psyche role the design predates. Field driver: flynn (claude-spt:ccs) — the ccs wrapper relocates the account root via CLAUDE_CONFIG_DIR at PARENT launch and the perch record correctly captured it, but the daemon spawns psyche_resume with bare env → default ~/.claude root → headless 'Not logged in' exit-1 → strike loop; psyche + parent land in DIFFERENT account roots (auth AND root-scoped continuity both break). Core stays harness-agnostic (threads whatever [env] direction=read captured — knows nothing of CLAUDE_CONFIG_DIR). Scope note: this is the URGENT psyche leg of F-027 Half B; the full pre_spawn seam + endpoint-session env threading stays design-parked (F-027-ENDPOINT-SPAWN-FAIL-DESIGN.md) unless operator pulls it forward.",
    "doc": ""
  },
  "REQ-HAZARD-PARENT-PID-PREFER": {
    "title": "Prefer stable parent PID / broker handle over ephemeral PID (2.1)",
    "doc": ""
  },
  "REQ-SELF-ID-TRUST-INJECTED-ENV": {
    "title": "DEFERRED to a followup vX.X.n sprint (post-LIFECYCLE-TRUTH, operator-ruled 2026-07-07): self-identity resolution must trust the harness-injected authoritative id and detect a stomped perch instead of silently mis-attributing. ROOT (doyle /diagnose 2026-07-07, field: agent sends stamped `cli@HFENDULEAM` / mis-attributed): `resolve_from` (cli.rs:5480) stamps `cli@<node>` when `detect_self_id` (roster.rs:103) returns None; detect_self_id resolves self ONLY by reverse-lookup — matching `$OWL_SESSION_ID` against a perch's info.json.session_id (then SPT_AGENT_ID, then parent_pid) — and IGNORES `SPT_ENDPOINT_ID`, the authoritative self-id the adapter injects (present in-env as SPT_ENDPOINT_ID=<id>). When a perch record is STOMPED (a cross-id info.json overwrite — the REQ-SPAWN-COLLISION-GUARD-LIVE-DUP damage class; field case: doyle's live session_id written into the deployah perch), the reverse-lookup mis-resolves (doyle session -> `deployah`) or fails (real deployah -> None -> `cli@node`), and the CLI silently believes the stomped store. FIX: detect_self_id PREFERS `SPT_ENDPOINT_ID` when set+non-empty (the harness-authoritative id, immune to a stompable perch), AND cross-checks it again",
    "doc": ""
  },
  "REQ-RESUME-ADAPTER-FOLLOWS-SESSION": {
    "title": "D-2 (REMOTE-TRUTH triage §D-2 + operator Q5 @c248afc): a resume-from-history restores the RECORDED session adapter (REQ-SESSION-ADAPTER-RECORDED) — the resumed harness is the one the session ran under, re-stamped onto the endpoint PRE-SPAWN, and an unregistered recorded adapter refuses LOUDLY before launching anything. ROOT: the picker's resume_outcome (model.rs:1285) bakes adapter=ep.adapter_profile from the selected ENDPOINT, ignoring the ledger row — so a resume always uses the endpoint's CURRENT adapter even when the session ran under a different one; and the endpoint's info.adapter is never re-stamped to the row's on the resume path (cli.rs:1962 skeleton writer early-returns for an existing perch — adapter immutable, carried by bind's stamp_creation_fields). FIX (doyle fork ruling): ResumeRow (model.rs:199) gains adapter: Option<String> threaded from SessionEntry.adapter in picker/data.rs; the row title (model.rs:228) renders [{adapter}] when Some ({head} [{adapter}] - {time} (…{id5})); resume_outcome bakes the ROW's adapter with an endpoint fallback (row.adapter.unwrap_or(ep.adapter_profile)) — None → the endpoint's current stamp (benign degrade). The pre-spawn RE-STAMP + ref",
    "doc": ""
  },
  "REQ-DAEMON-1": {
    "title": "One per-machine spt-daemon owning all per-machine state",
    "doc": ""
  },
  "REQ-SUBNET-1": {
    "title": "spt subnet noun namespace: status view (bare + status [NAME] [--nodes]), create (QR/otpauth), show-code; spt pair deleted",
    "doc": ""
  },
  "REQ-INSTALL-12": {
    "title": "Durable active-profile pointer for bind-time profile selection (ADR-0021): adapters/active-profiles.toml at the registry ROOT (sibling to the per-adapter <name>/ dirs, so adapter add/update/remove — which only rewrite a <name>/ subdir — can never clobber it), a flat host_binary → \"adapter[:profile]\" map. Read at bind as the PRIMARY profile selector; unset → the registered_at_ms fallback (REQ-START-5). Written ONLY by `spt adapter use <adapter>[:profile]` (resolves the adapter's host_binaries → sets each binary→adapter[:profile]); `spt adapter use --clear <adapter|binary>` drops. NEVER auto-written by install/update/adapter add (that is precisely what would let an update silently flip the active profile). A stale pointer (uninstalled adapter / deleted profile) self-heals: ignored, fall back, warn once. Pruned on adapter remove. Atomic write (spt_store atomic). (v0.9.0)",
    "doc": "Startup flows (the two topologies): **Bind-time adapter/profile resolution (ADR-0021).** Because the seed is adapter-agnostic, `listen`/`poll` resolve the owning adapter/profile when they bind, as a pure read — never a seed-time snapshot that could drift. `--adapter <name[:profile]>` is an **optional override** on the `api` group (an explicit choice for adapter dev/iteration); omitted, resolution runs: 1. the seed's `parent_pid` → that process's **executable basename** (case-insensitive, `.exe`-stripped); 2. **candidate adapters** = registered `kind=\"harness\"` adapters whose **`host_binaries`*"
  },
  "REQ-BRAIN-RESUME-NO-CONTROL-STEAL": {
    "title": "UPDATE-WEDGE round 2 (v0.30.4, doyle-ruled 2026-07-09 — field incident on the counter-54 fetch--apply): a brain-respawn must NEVER steal, then stall-evict, the controller of a broker PTY session the daemon brain does not DRIVE. ROOT (field-pinned + SME, docs/UPDATE-WEDGE-2-RCA.md + docs/UPDATE-WEDGE-2-SME-todlando.md): `resume_sessions` (brain.rs:985) re-attaches EVERY session `KIND_SESSIONS` returns via `subscribe` = `subscribe_with(AttachIntent::Control, by:None)` (brain.rs:1450-1455). The docstring's 'a None identity never displaces (falls back to viewer)' is a MYTH for FREE / SAME-LOCAL-IDENTITY slots: `resolve_subscribe` (broker.rs:1134-1153) stall-evicts FIRST, then `become_controller` if the slot is now free OR the incumbent is also local (None==None) — viewer-fallback fires ONLY when a DIFFERENT REMOTE controls. So on a box with N spt-hosted broker PTYs, a brain-respawn STEALS the by:None controller of every free/local-controlled session (incl. the operator's LOCAL `spt rc`), which the daemon brain never drains (it hosts no PTY sessions — brainproc.rs:184) → 15s later `stall_evict_controller` (broker.rs:1039) releases driven_by → the session is UNCONTROLLABLE (Failure A). I",
    "doc": "Self-update: **resume re-attach is view-only for non-driven sessions** — on respawn the new brain queries the broker for every hosted session and re-attaches to rebuild output-continuity cursors, but it re-attaches as a **viewer**, never a controller, for any session it does not itself drive (which is *all* of them today — the supervised daemon brain hosts no PTY sessions; spt-hosted PTYs are driven by the operator's attach or the endpoint's own loop). Re-attaching as a controller would seize the controller slot of every free/local-controlled session — including the operator's local `spt rc` —"
  },
  "REQ-RC-DISPLAY-SOLE-WRITER": {
    "title": "TEARDOWN-AUTHORITY W5 (hertz RCA 3 bug 1, 2026-07-19 — REPRODUCED on 0.38.1 CLI + 0.38.1 broker; doyle ruled, and it CORRECTS an incomplete v0.38.1 call of doyle's own): while `spt rc` owns the terminal (raw / alternate screen), rc is the SOLE writer to that display — no background thread may write to the inherited stderr. TODAY: BrainConn::split_with_reader (spt-daemon/src/brain.rs:253-270) unconditionally eprintln!s `PUMP_IPC_READER: spawned` / `exited` from its reader thread. Every reconnect attempt calls establish_attach -> Brain::cold_start_pump (rc.rs:1700-1712), and reconnect_banner_bytes (rc.rs:2040-2054) deliberately clears/homes and leaves the cursor immediately after the countdown with NO trailing newline — so the marker lands exactly at that cursor and the operator sees `Reconnecting to local daemon... 9sPUMP_IPC_READER: spawned`. Field screenshot confirms it also reaches the harness alt screen on a real `endpoint run` + `rc --take`. WHY THIS IS A CORRECTION, ON THE RECORD: v0.38.1's REQ-RC-SINGLE-PUMP-BRAIN removed the DOUBLE construction and doyle ruled the surviving single banner 'truthful, keep it' and classified the change Internal (diagnostic hygiene, not user-fac",
    "doc": ""
  },
  "REQ-HAZARD-BROKER-QUIC-DEADLINE": {
    "title": "The broker bounds every brain-waiting QUIC op (dial / open_stream / send_stream) so a black-holed or dead peer fails PROMPTLY with an ORDINARY error the broker REPLIES, never an unbounded await. The bound (< the brain's 30s PUMP_PEER_IO_TIMEOUT so the BROKER fires first) surfaces to the pump as a normal broker error reply → peer_outcome's non-TimedOut arm → drop conn + redial next tick, the round CONTINUES and the heartbeat keeps advancing — it must NEVER manifest as the brain's own read-deadline (the A-half poison → supervised-restart path REQ-HAZARD-PUMP-IPC-DEADLINE guards). Exactly-once is preserved: a timed-out journaled op fails INSIDE its apply_once closure so no phantom conn_id/stream_id is recorded and a fresh tick re-dials cleanly. The happy path is unchanged (a live peer completes with zero added latency; the bound only bites a non-responsive peer). This is the ROOT-cause cure for the 2.2h hfenduleam pump wedge — a dead roster peer whose QUIC path the broker awaited unbounded — recurring on hfenduleam 2026-06-16.",
    "doc": "7.7 A slow/dead/hostile remote VIEWER must never stall the controller, child, or drain `[REQ-HAZARD-VIEWER-ISOLATION]`: ### 7.8 The broker must never make a brain wait UNBOUNDED on a QUIC op (the pump-IPC-deadline B-half) `[REQ-HAZARD-BROKER-QUIC-DEADLINE]` <!-- --> - **Failure:** the broker's brain-facing QUIC handlers (`dispatch_net_dial` / `dispatch_net_stream_open` / `dispatch_net_stream_send`) call into `NetHost::dial` / `open_stream` / `send_stream`, whose iroh awaits (`endpoint.connect` + `prove_membership`; `open_bi`; `write_all`/`finish`) had NO bound of their own. A dead/black-holed"
  },
  "REQ-CLI-4": {
    "title": "User-facing CLI output is human-readable: DIRECT-USER commands (e.g. adapter update/list/use) render friendly prose instead of raw CODE:RESULT markers — \"claude-spt is up to date (0.2.0).\" not \"ADAPTER_UPDATE_UPTODATE:claude-spt: installed 0.2.0, latest 0.2.0\". Strictly bounded to the direct-user surface: the adapter-PARSED bringup tokens (SEEDED/BOUND/READY/NO_SEED on seed/listen, which adapters grep) stay machine-parseable — humanization is additive (a human line beside the marker, or a --porcelain/--quiet split), never a silent rename of a dual-contract marker. The user-facing bringup composition belongs to the adapter (perri); this REQ owns only the direct-user CLI surface. (v0.9.0)",
    "doc": ""
  },
  "REQ-PSYCHE-EPHEMERAL-DRIVER": {
    "title": "W1 (F030, design §3): each psyche-relevant event runs exactly ONE bounded per-event turn through the existing driver stack (psyche_turn_and_relay for outbound-intent events / resume_psyche for session-custody transitions / run_psyche_turn for pure merges) — no resident psyche process exists between events. host_one (livehost.rs:518) STOPS spawning spawn_psyche_owned; the pulse loop stays as the daemon-side scheduler (thread + stop-flag + drop-dir watch correct) but a fire now invokes one bounded turn, daemon-driving every substitution key from daemon-known context (child never self-resolves home/subnet/perch — direction-(a) multi-subnet churn impossible by construction). Turn failures consume a bounded failure budget (C3(b) shape): N consecutive failures → psyche_host_error stamp + cooldown, reset on success; no respawn storm (nothing resident to respawn). Red-first: fire an event on a hosted live endpoint → assert one turn ran (SIDE-EFFECT PROOF FILE — transcript-jsonl asserts are structurally blind, 2026-07-04 rig lesson) and no {id}-psyche process survives the turn.",
    "doc": "Endpoint types: **Psyche**: The Psyche companion's own perch, distinct from its paired LiveAgent's perch. First-class endpoint type so messages addressed to a LiveAgent's Psyche route directly without ambiguity. **A Psyche is a bounded per-event turn, not a resident process (since v0.25.0).** Each psyche-relevant event (a pulse fire, a commune/signoff drop, a session-custody transition) runs **exactly one** bounded turn through the psyche role template, spawned by the daemon, which exits at turn end — there is no long-lived psyche loop or psyche pid between events. <!-- --> **Liveness = turns"
  },
  "REQ-SUBNET-5": {
    "title": "Per-subnet serve-state: spt subnet detach <NAME> [--save] / attach <NAME> [--save] — daemon keeps running, stops/starts advertising + connecting for that subnet (peer pump + responder selective); --save persists the startup default in daemon config; the all-attached banner gains per-subnet states (M8 decision 6, --save renamed from --auto per decision 25 session)",
    "doc": ""
  },
  "REQ-HAZARD-CASCADE-WIPE-GUARD": {
    "title": "No hard-delete of a parent hosting non-empty children (6.3)",
    "doc": ""
  },
  "REQ-HAZARD-RENDER-LIFECYCLE": {
    "title": "RC-RENDER-TRUTH W3 (KNOWN-HAZARDS 7.47 — umbrella conformance seam for ADR-0043): the physical terminal is never mutated or terminated outside its renderer's ordered state model. Regression matrix from the hertz RCA: output-before-exit through the production path; unconditional display teardown across every rc exit class; TUI baseline reconstructs whole after out-of-band mutation (recording backend); repaint replays tracked modes. Deferred P2 seeds recorded, NOT this milestone: Exit{after_seq} watermark defense; per-client semantic baseline (only if spt ever transforms live frames). Gate: int — the matrix; doc — KNOWN-HAZARDS 7.47.",
    "doc": "7.46 An rc surface answers from live session authority, never a stale persisted projection — and a resuming perch is UNBOUND, not offline `[REQ-HAZARD-RC-ATTACH-TRUTH]`: ### 7.47 The physical terminal is never mutated or terminated outside its renderer's ordered state model — output before exit, owned baselines, unconditional teardown `[REQ-HAZARD-RENDER-LIFECYCLE]` <!-- --> - **Failure (paid-for, hertz stale-glyphs RCA 2026-07-18, all legs doyle seam-verified):** four render-lifecycle defects presenting as \"missing whitespace\"/stale glyphs. (a) The broker exit waiter direct-writes `KIND_EXIT`"
  },
  "REQ-MSG-INJECT-LEG-DROP-VISIBLE": {
    "title": "SEED (inactive — observability): a silently-dropped delivery leg must be DISTINGUISHABLE from an honestly-offline endpoint on a status surface. The spt-hosted inject leg (spt-daemon inject.rs `try_spt_hosted_inject` — the ONE shared implementation behind local cmd_send, the WAN ingress, the idle-edge drain, and the parked-idle/pulse re-offer belts) gates on `is_spt_hosted_no_relay` → `deliver::is_online` → `liveness::is_perch_alive`: a perch PINNED TO A DEAD SESSION (the KH 7.25 wedge class — dead owner, record not yet healed by the next auth touch) makes EVERY belt on that leg silently return None → messages spool as if the endpoint were ordinarily offline, while the operator-facing view can keep reading the recorded state. Nothing anywhere surfaces 'the inject leg stopped firing for this endpoint' — the idle-window injections just stop, which from the outside is indistinguishable from 'no messages arrived' (field shape: IDLE-EDGE W1 field-verify, perri's killed-resident rig — their 'so it did not repeat' conclusion was exactly this invisibility, self-corrected only by re-rigging with a live resident). Shape at activation: a status/list surface DERIVES and reports the inject-leg v",
    "doc": ""
  },
  "REQ-FRONT-1": {
    "title": "Day-one launcher/manager frontend (list/launch/attach/init)",
    "doc": ""
  },
  "REQ-CLI-3": {
    "title": "Agent hot path stays flat across the M8 reorg: send/ring/ready/whoami/how-to unchanged; notify moves to subnet notify while notif stays top-level; breaking renames land clean with no deprecation shims (zero external CLI consumers pre-spt-claude-code) (M8 decisions 3-4, 9)",
    "doc": ""
  },
  "REQ-UPDATE-ONE-SHOT-FINISH": {
    "title": "W3 (LIFECYCLE-TRUTH): update apply works daemonless and one command finishes the cycle. ROOT (operator wart): update fetch/apply run ensure_daemon_announced (cli.rs:4386) -> on a stopped box they BOOT THE OLD broker pre-swap, guaranteeing the mixed old-broker/new-brain pair + a manual bounce. FIX: apply works daemonless (swap + record, next start runs new bytes); `update apply --finish` (name subject to docs-token gate) completes the cycle: swap -> brain cycle -> broker restart onto new bytes (rides REQ-UPDATE-FINISH-ENDPOINT-SURVIVAL so the restart is not a massacre). CLI change -> xtask docs gen, no internal codes in clap ///.",
    "doc": ""
  },
  "REQ-RUN-PICKER-HOME": {
    "title": "Home-subnet selection LAYER in the `spt endpoint run` ratatui Create-new picker (v0.14.1; the deferred half of REQ-RUN-MULTISUBNET-HOME's interactive path — ADR-0026 §3 'the interactive picker lists subnets MRU-ordered'). On a MULTI-SUBNET node the Create-new flow gains a `CreateHome` screen (CreateAdapter → CreateId → CreateHome → Confirm) that lists the node's MEMBER subnets MRU-ordered (reusing recent_home::mru_preference + order_by_mru), default cursor = MRU head; the chosen subnet rides Outcome::Run{subnet} into cmd_endpoint_run's --subnet, so decide_run_home resolves Home directly and the post-TUI `Ok to proceed? Y/n` confirm NEVER fires for the picker path. Single-subnet / local-only nodes SKIP the layer (assign_home auto-homes; CreateId → Confirm unchanged). The CLI / flagged `endpoint run` path KEEPS the decide_run_home Y/n confirm + the non-interactive MULTI_SUBNET_HOME refuse (operator: the confirm stays useful for CLI-only bringup, just not in the TUI). Esc backs CreateHome → CreateId; Enter selects → Confirm. Pure front-end invariant preserved: the layer only collects --subnet, routes through the one bringup core.",
    "doc": "Shell sleep/wake (offline ↔ online): **spt-hosted bringup picker (`spt endpoint run`)** (M12-W2): <!-- --> The user-facing bringup flow for spt-hosted endpoints. **Bare `spt endpoint run`** (no `--adapter`/`--id`) opens an in-process **ratatui picker**; the **flagged** form is the non-interactive bringup path (`--adapter <a[:profile]> --id <id> --create|--resume <session> --start|--attach|--view`), untouched — a picker selection bakes exactly that path. **Layer 1** picks the kind (*Create new* | *Pick existing*). **Create-new** chooses a registered `kind=\"harness\"` adapter with its shipped+loc"
  },
  "REQ-INSTALL-BOOTSTRAP-VERB": {
    "title": "THE-FORKENING W1 (ADR-0036 §3, bootstrap shape b operator-ruled): virgin-box install = `gh release download` the platform binary + ONE self-install verb in the binary itself — the verb places the binary at the canonical install path (the path self-update already respawns from, v0.4.2 lesson), registers user PATH, and leaves first-run identity/daemon-start to the existing idempotent first-run; hosted one-liner install scripts (curl|sh / irm|iex at the dead Pages URL) are RETIRED. Non-interactive (CONTEXT.md Installation: the install path doubles as every adapter's pack-in on-demand install). Windows UAC-740 gotcha binding: the downloaded exe keeps the `spt-*` asset name and the verb lives INSIDE spt — no installer-detection trigger words in exe names. README (bs-core) documents: install gh -> gh auth login -> gh release download -> the verb. Gate: unit — verb places/registers idempotently, refuses cross-platform binaries (platform-stamp check exists, v0.3.2); int — from a clean SPT_HOME+PATH sim, downloaded-binary self-install yields a working `spt` on PATH whose `spt update fetch` then speaks the gh channel; doc — README install section rewritten. Kin REQ-UPDATE-GH-TRANSPORT, REQ-I",
    "doc": "Installation: **Installer form (gh bootstrap, ADR-0036):** install gh → `gh auth login` (org membership) → `gh release download` the platform binary from the private channel → one **self-install verb** in the binary places it at the canonical install path and registers the *user* PATH (so adapters call `spt api …` cross-OS); first-run identity gen + daemon start stay the existing idempotent unattended first-run. Hosted one-liner scripts are retired with the public channel; first-fetch trust = gh's authenticated TLS + org membership (full ed25519 verification is `spt update`'s job thereafter)."
  },
  "REQ-CONN-POISON-ATTRIBUTION": {
    "title": "MSG-IDENTITY W6 / F-039 legs b-d (doyle W6 LOCK 2026-07-10, minted per amendment 3): every broker-conn lifecycle record is ATTRIBUTABLE — the W6 RCA's terminal undecidability (per-line 1:1 CONN_WRITE_POISONED churn = fresh-carrier churn OR stderr interleave artifact) exists because records carry no stable conn identity, no role/endpoint/session context, and no timestamps, and the once-per-conn poison latch hides multiplicity. THREE LEGS. (b) IDENTITY: mint a stable per-physical-conn id (monotonic u64 at conn construction — Arc::ptr_eq is the only identity today and it does not survive a log line) plus subscriber role and endpoint/session where known, stamped on CONN_WRITE_POISONED, CONN_WRITE_RETIRED, logical stall-evict, attach/resume/detach, and write-retirement records (RCA attach sites: presence nethost.rs:379, stream nethost.rs:258, controller broker.rs:891, viewer broker.rs:1073). (c) TIME: daemon stderr correlation records carry wall-clock AND monotonic timestamps (stderrlog has neither; broker+brain share one file — interleave is unresolvable without them). (d) LIFECYCLE (doyle-confirmed UNCONDITIONAL, not debug-gated): one BOUNDED set of per-conn lifecycle events — write s",
    "doc": ""
  },
  "REQ-HAZARD-DIRECT-WRITE-PRECEDENCE": {
    "title": "Direct-write precedence marker (with node id) guards stale overwrite (6.5)",
    "doc": ""
  },
  "REQ-HAZARD-REGISTRY-EPOCH-LEASE": {
    "title": "Registry merge ordered by per-node monotonic epoch, never wall-clock — a stale Active can't clobber a newer Offline (4.8, red-team #8)",
    "doc": "4.7 Concurrent SQLite openers must not fail with \"database is locked\": ### 4.8 Registry merge ordered by epoch, never wall-clock (red-team #8) <!-- --> - **Failure:** the per-subnet registry replicates `endpoint_id → [instances]` eventually-consistently across nodes. Under a partition or clock skew, a lagging node re-announces a stale `Active` for an endpoint that has actually gone `Offline`. If the merge ordered updates by wall-clock (or \"last write wins\"), the stale `Active` overwrites the newer `Offline` and resolution routes a message to a dead/wrong instance. - **Invariant:** the merge pr"
  },
  "REQ-HAZARD-STOP-RESPAWN-CONVOY": {
    "title": "KNOWN-HAZARDS 7.52: an operator stop outranks every implicit ensure — no convenience path resurrects what the operator just killed. The hazard-conformance twin of REQ-ENSURE-DAEMON-STOP-INHIBIT: its int-stage convoy rig IS this hazard's required test (stop under api-call storm -> down + zero respawns + honest refusal; start clears; one-daemon race). Registered separately per CLAUDE.md rule 4 — evidence may tag the same rig.",
    "doc": "7.52 An operator stop outranks every implicit ensure — no convenience path resurrects what the operator just killed `[REQ-HAZARD-STOP-RESPAWN-CONVOY]`: <!-- --> - **Failure (paid-for, hertz v0.39.4 field RCA 2026-07-22, doyle code-verified same day):** `spt daemon stop --force` is non-terminal on a box hosting live adapter sessions: every `spt api` invocation runs an unconditional `ensure_daemon()` (REQ-DAEMON-3's anchor), hook-driven api calls arrive continuously, so the stop is a race the operator loses — 5–10 ephemeral spawner windows flash and the daemon is back; several force-stops to sta"
  },
  "REQ-PICKER-1": {
    "title": "The picker renders a FOUR-state endpoint status (extending the W2 online/offline duality): the list-item square AND a color-coded STATUS line at the top of the pick-existing right-side details both show — gray OFFLINE; green ONLINE (online + PTY-controllable spt-hosted, not controlled); amber 'ONLINE - HARNESS ONLY' (online but NOT broker-PTY-controllable = harness-hosted, no broker PTY seat — today mis-shows green); blue 'ONLINE + CONTROLLED' (online + driven_by.is_some()). Derived on EndpointRow from {offline | controllable | driven_by} with precedence offline→gray, else driven_by→blue, else !controllable→amber, else green (driven_by outranks harness-only; mutually exclusive in practice — a harness-only endpoint has no broker PTY to control). The controllable discriminator is a NEW InfoJson.controllable: Option<bool> (serde-default, N-1-safe), stamped at the establish seam — cmd_listen (harness-hosted relay, no broker PTY) → Some(false); cmd_bind live_agent (spt-hosted broker PTY) → Some(true); absent → not-controllable (amber) default (harness-hosted is the common mis-reported case; one bind self-corrects). Store-projection-only (no live daemon query — doyle ruling). (v0.10.0)",
    "doc": ""
  },
  "REQ-UPDATE-RESTART-SAFE-SWAP": {
    "title": "THE-FORKENING W4 (operator-grilled 2026-07-14; RETIRES findings-backlog seed #12 REQ-UPDATE-ONE-STEP-SAFE-SWAP): `spt update --restart` = the one-step ergonomic path to the SAFE full-cycle swap — fetch -> `update adapters` -> `apply --finish` LAST (lethal-leg-last, ruled: apply --finish restarts the whole daemon incl. broker/PTYs, so it must be the final act — everything completes from ANY invoking context including an spt-hosted session whose PTY dies at that step; accepted cost: a finish FAILURE leaves updated adapters on old-activated core briefly — loud + operator-attended by nature of the flag). Composes with `-c/--core-only` (skip adapters leg). The 0.28.0 wedge lesson closes: the ergonomic one-step no longer picks the riskier path by default for operators who want the full cycle. Gate: unit — flag sequencing incl. lethal-leg-last ordering + -c compose; doc — self-update docs present --restart as the full-cycle form and name the finish-restart consequence. Kin REQ-UPDATE-DEFAULT-COMPOSITE, REQ-UPDATE-FINISH-COMMUNE-FLUSH (deferred commune-flush rides the same finish path when built), seed #12 (retired by this).",
    "doc": "Self-update: **update composite (`spt update`)** — the plain verb is the primary form: `update fetch --apply` then `update adapters` (core-first order); with core already current, only adapters update. `--core-only`/`-c` skips adapters; `spt update adapters [<a>[,<b>…]]` is the adapters leg alone (alias over `spt adapter update`). The composite's invoker always survives, because a routine apply cycles only the **brain** — the *restart-required* message on broker-side releases is a notice, not a restart. `spt update --restart` is the one-step **full cycle**: fetch → adapters → `apply --finish`"
  },
  "REQ-HAZARD-ECHO-BEFORE-SIGNOFF": {
    "title": "Echo-commune fires before INIT_SIGNOFF on orphan teardown (3.3)",
    "doc": ""
  },
  "REQ-RESUME-HARNESS-SESSION-ID": {
    "title": "B2 (F028, hall-b diagnosis, verified 0.22.0): respawn/`--resume` feeds the SPT session id to `claude -r`. After the 02:05 daemon bounce respawn built `claude.exe -r 70b5bfa40901b7d4` — an spt session id in claude's OWN session-id namespace -> claude hangs forever at a 'No sessions match' resume-picker while the endpoint reads online. Hits after EVERY daemon bounce + every picker Resume. The HARNESS session id (claude UUID, stamped in sessions.log/info.json by the hooks) is what {session_id} must mean in the adapter's [session.resume] command; the spt sid must not leak. FIX: substitute the HARNESS session id in the resume template (spt-core substitution-key semantics + LIKELY claude-spt manifest coordination — FLAG perri BEFORE touching the manifest, adapter-boundary rule). Int: resume template receives the ledger UUID, not the spt sid. See triage B2.",
    "doc": ""
  },
  "REQ-RESUME-CONTEXT-PULL": {
    "title": "Adapter-callable resume-context pull verb + not-yet-synthesized commune/signoff drop append (legacy-SPT parity, operator-directed 2026-06-24). GAP: spt-core exposes NO verb for a harness adapter's SessionStart hook to pull an agent's resume context — `resume::download_psyche_context` (spt-live/src/resume.rs:88, composes <live-role>+<live-context>+<project-context> from the durable two-tier store) is INTERNAL with ZERO spt callers and no ApiCmd verb (api/mod.rs ApiCmd enum has none); resume.rs:9 documents the intended 'adapter pulls it in its SessionStart hook' path but it was NEVER wired. Result: a harness adapter cannot inject the agent's durable mind on resume at all (claude-spt today runs only `api boundary` session-rotation + an identity brief — the agent resumes WITHOUT its mind). TIER-1 SCOPE (operator-approved; Tier-2 = drift-stamp/<current>/drift-directive + <memformat> + Pulse-Log DEFERRED to a separate parity item, NOT v0.15.0 — the legacy download_payload [claude_skill_owl context.rs:344] is richer but memformat is roadmap-deferred + drift-stamp is an orthogonal cross-machine-drift feature). TWO PARTS: (1) EXPOSE `spt api psyche-download <id> [--session-id <sid>]` -> std",
    "doc": "Manifest seams (outbound contract, detailed): **resume-session seam** — two distinct forms: - **fresh-with-preload:** resume with *cleared* context (a fresh session) + psyche-download. Accepts a `$psyche-context` key to launch the fresh session with the psyche-download preloaded — or the adapter instead pulls it via an spt-core command in its SessionStart hook. <!-- --> That command is **`spt api psyche-download <id> [--session-id <sid>]`**: it emits the durable resume brief (role → live-context → project-context, project resolved from the perch's bound cwd) to stdout for the adapter's Session"
  },
  "REQ-RC-WIN-VT-OUTPUT": {
    "title": "Bug #12: `spt rc` to an endpoint renders ANSI escapes LITERALLY (raw ←[K / color codes) on a Win10 conhost console, garbling the viewport — while `endpoint run --attach` in the SAME env renders fine and Win11 Windows Terminal is unaffected. Root (code-grounded, doyle): rc.rs RawGuard::enable calls only crossterm enable_raw_mode (INPUT raw mode) and NEVER enables ENABLE_VIRTUAL_TERMINAL_PROCESSING on the OUTPUT handle; Win10 conhost defaults VT-output OFF so escapes print literally, whereas the picker/alt-screen setup on the endpoint-run path enters crossterm's VT-enabling console setup first (leaving VT-output on) — so it is the rc-attach CLIENT PATH specifically, and --attach-works-same-env confirms (not refutes) the VT-out theory. Fix: in the rc attach path (RawGuard), on cfg(windows) + interactive console (mirror the windows_mouse_wanted guard so piped stdin/stdout keeps clean bytes for the e2e byte tests), SetConsoleMode STD_OUTPUT_HANDLE |= ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT, capture the prior mode, restore on Drop. cfg(windows)-only, client-side, independent of #4/#6. See docs/NEXT-MILESTONE-BUG-TRIAGE.md #12.",
    "doc": ""
  },
  "REQ-ENDPOINT-LIST-PALETTE": {
    "title": "Bugs #11 + #15 (display): spt endpoint list renders status as plain text while the picker turns the same ResourceRow into the W5 colored EpDisplay palette. Fix: extract one shared ResourceRow-to-EpDisplay builder + make the picker display enums/helpers public, and have endpoint list render the same colored status squares (via helpfmt stdout_color, not ratatui Span). This also fixes #15 — a lone warm detached instance renders as its online flavor (Dormant maps to online) instead of leaking the bare word Dormant through the text-only list (no resting.rs/CONTEXT model change; operator ruling display-only). Couples REQ-PICKER-NODE-GROUPING (both edit subnet_rows — sequence the shared-builder extraction first). See docs/NEXT-MILESTONE-BUG-TRIAGE.md #11/#15.",
    "doc": ""
  },
  "REQ-HAZARD-REGISTRY-CONCURRENT": {
    "title": "Concurrent SQLite openers (registry/spool) must not fail with 'database is locked' (4.7)",
    "doc": "4.6 Addressable-id charset reserves the address delimiters: ### 4.7 Concurrent SQLite openers must not fail with \"database is locked\" <!-- --> - **Failure:** two endpoints on one machine open the same SQLite store at once (e.g. two `ReadyAgent::start` calls registering simultaneously) and one fails outright with `SQLITE_BUSY` / \"database is locked\" → spurious registration/spool failure. Surfaced as a parallel-test flake in `two_agents_exchange_message_tcp_and_spool`, but the bug is real concurrency, not test-only. - **Invariant:** `busy_timeout` is set **before** any lock-taking statement on e"
  },
  "REQ-SEAM-HISTORY": {
    "title": "History subsystem (fetcher / locate-normalize / native store)",
    "doc": ""
  },
  "REQ-INST-12": {
    "title": "Endpoint visibility per-(endpoint,subnet): excluded semantics, OR-of-defaults + override, gates sync",
    "doc": ""
  },
  "REQ-NET-3": {
    "title": "Cross-node Psyche sync over P2P replaces gh-repo-sync",
    "doc": ""
  },
  "REQ-RC-NEWLINE-PRESENTATION-TRUTH": {
    "title": "W3 (v0.39.4 field bug 3 ROOT, pinned 2026-07-22): the rc client presents relayed bytes exactly as the emitter addressed them — KNOWN-HAZARDS 7.56. rc's with_vt_output enables ENABLE_PROCESSED_OUTPUT without DISABLE_NEWLINE_AUTO_RETURN (DNAR: zero grep hits tree-wide), so the operator's console translates relayed bare LF into CR+LF — a column reset the emitter never asked for; the /c/config field scraps are capture record t=1784714152501's bare-LF index presented through that mode. Relay byte-clean end to end (why both isolated rigs read NOT-REPRODUCED and the fresh viewer read clean — the synthesized repaint is CUP-absolute). FIX: DNAR joins the with_vt_output composition whenever processed/VT output is enabled; prior-mode capture/restore unchanged; Unix needs nothing (raw mode already clears OPOST/ONLCR). Regression: the W3 captured-bytes rig PAIR (vehicle console without DNAR = field-client model = RED pre-fix; with DNAR = GREEN post-fix) + the minimal LF column-preservation probe through an rc-mode console. Amends REQ-RC-WIN-VT-OUTPUT (#12); kin KH 7.55 + the W3 vehicle-fidelity clause (three console-mode-seam manifestations, one day). Attribution CLOSED when hertz confirms fixe",
    "doc": "7.56 The rc client presents relayed bytes exactly as the emitter addressed them — console output modes are part of presentation truth `[REQ-RC-NEWLINE-PRESENTATION-TRUTH]`: <!-- --> - **Failure (paid-for — v0.39.4 field bug 3, the `/c/config` scraps, pinned 2026-07-22 via the W3 bisect):** rc's Windows viewport enables VT output as `prior | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT` (`rc.rs` `with_vt_output`) and `DISABLE_NEWLINE_AUTO_RETURN` appears NOWHERE in the tree — so with processed output on, the operator's console translates every relayed bare LF into CR+LF. A hoste"
  },
  "REQ-HAZARD-UPDATE-ROLLBACK": {
    "title": "Self-update rejects version rollback; metadata expiry + adapter content signing (codex #5)",
    "doc": ""
  },
  "REQ-ADAPTER-TRANSLATE-PROOF": {
    "title": "`spt adapter translate-proof <adapter> --event <envelope> [--session <id>]` — the author-time EMIT-half proof tool for `[message-idle-translation-binary]` (ADR-0022), symmetric to `spt adapter digest-proof` (REQ-TERM-5). It spawns and feeds the adapter's declared translation binary EXACTLY as the daemon does at idle-delivery — running the REAL `spt_daemon::translation` driver VERBATIM (no protocol reimplementation): `TranslationChild::spawn` the binary, send the `{type:\"init\",endpoint_id,node}` line then the `{type:\"event\",envelope}` line, and read back the emitted `{key}`/`{text}`/`{delay_ms}`/`{commit}` keystroke-command stream — then prints it author-readable (each Key with its `key_to_bytes` rendering, Text quoted, Delay in ms, Commit marker) with counts. It fills the SAME `{id}`→option and `{session_id}`→(--session, else a placeholder) keys into the `--event` envelope the daemon fills at runtime, so an envelope that proofs here feeds faithfully live. EMIT-half ONLY: it proves the binary's spawn+feed+emit contract; it does NOT exercise the daemon's atomic PTY apply / controller-buffering (that stays covered by the W2 inject_control_wedge int gate) — `--help` says so. Exit codes",
    "doc": "Author-time proof: `spt adapter translate-proof`: <!-- -->"
  },
  "REQ-PICKER-UX-V013": {
    "title": "`spt endpoint run` picker UX (v0.13.0 operator dogfooding): (1) SKIP the first screen — open directly on 'Pick existing'; `n` jumps to 'Create new'. (2) AUTO-ATTACH after both Start-new AND Resume-from-history (both currently don't attach and show no stdout); add an `h` shortcut to run headless (no attach). (3) 'controlled by' shows the node NAME (node_label_display), not the raw hex. (4) Clean up Start-new output — drop the Rust `pid=Some(142748)` leak and the 'harness binds its perch on startup' internals; user-friendly, not a process log. (v0.13.0)",
    "doc": ""
  },
  "REQ-DAEMON-STATUS-JSON-TRUTH": {
    "title": "D2 (F028, perri F-d): `daemon status --json` truth drift. managed_by/managed_active read null in JSON while the HUMAN view says 'managed-by: manual — at-logon task registered' (the two surfaces disagree); and pump staleness (the STALLED diagnosis, B5) is NOT computable from JSON — only a raw pump_heartbeat_ms is emitted, no derived staleness/stalled field. FIX: JSON managed_by/active match the human render, and add a derived pump-staleness/stalled field so B5's condition is machine-observable. See triage D2.",
    "doc": ""
  },
  "REQ-HAZARD-STALE-INDEX-LOCK": {
    "title": "Sweep stale lockfiles on daemon boot (1.3)",
    "doc": ""
  },
  "REQ-HAZARD-TEARDOWN-DEADEND": {
    "title": "TEARDOWN-AUTHORITY W1 HAZARD (ADR-0045 decision 8; hertz RCA 2 — hit doyle's OWN live production endpoint, recoverable only by out-of-band scoped kill): `endpoint stop` followed by `endpoint run --id <same>` MUST succeed (spawn or resume) and must NEVER answer ENDPOINT_CREATE_CONFLICT about a session that stop claimed to end. TRAP SHAPE (two individually-CORRECT behaviors composing into a lifecycle DEAD END with no in-band exit): (1) stop leaves the broker session alive (REQ-ENDPOINT-TEARDOWN-AUTHORITY), and (2) `endpoint run` correctly REFUSES ENDPOINT_CREATE_CONFLICT rather than silently reattaching (v0.37.0 no-silent-reattach rule, deliberately chosen). The survivor is therefore simultaneously what stop claims to have killed AND what run refuses to work around; with a wedged harness every in-band verb is exhausted (stop lies, run refuses, rc replays a dead PTY, rc --take controls a process that never answers). Neither behavior is individually wrong — the COMPOSITION is the hazard, so the regression must assert the composition, not either verb alone. This single assertion is the whole user-visible point of W1. Gate: impl — covered by the shared primitive; int — start a real broke",
    "doc": "7.48 At most one input-capable controller lease per PTY session — takeover revokes atomically and loudly, input is fenced to the active lease, node identity is never a lease `[REQ-HAZARD-CONTROLLER-LEASE]`: ### 7.49 A teardown verb never stamps a terminal or resting state it has not caused — and two individually-correct verbs must not compose into a lifecycle dead end `[REQ-HAZARD-TEARDOWN-DEADEND]` <!-- --> <!-- --> - **Failure (paid-for, two hertz field RCAs 2026-07-19, both doyle code-verified the same day; the second hit doyle's OWN live production endpoint):** `endpoint shutdown` reported"
  },
  "REQ-SUBNET-2": {
    "title": "Guided join e2e: spt subnet join CLI initiator + always-on daemon pairing responder",
    "doc": "Product-surface amendment (2026-06-05 — M7 D3): <!-- -->"
  },
  "REQ-TRANSLATE-COMMAND": {
    "title": "`[message-idle-translation-binary]` accepts a `command` (opaque; args + ADAPTER-STATIC {adapter_dir}/{adapter_name} substitution ONLY — ratified v0.16.0 W1, NOT session {key}: the translation binary is a persistent process serving all sessions on the endpoint (session/event ctx arrives per-message via the stdin Init/Event protocol, never the spawn argv) and the live-update respawn site has no session ctx (a {id}-bearing command would MissingKey→spool); program token resolved against install_dir like [digest].extractor/[session.psyche_init]) in addition to the bare `path`. `path` is DEPRECATED — keeps parsing (manifest forward/back-compat) but emits a registration warning steering to command. Exactly one of {path, command} (both-set refused at registration; neither = no translation binary). The spawn lifecycle + stdin/stdout JSON-lines protocol (Init/Event/Input → key/text/delay_ms/commit) are UNCHANGED — command alters only how the executable+args are located/launched (read_translation_path → read_translation_command). Unblocks folding `claude-spt translate` into the one consolidated binary (downstream ADR-0006). (v0.16.0)",
    "doc": "path = \"cc-spt-idle-translate\" # DEPRECATED bare-program form (still parses; warns at registration): <!-- --> - **`command` (preferred, since v0.16.0)** — an **opaque** command string (a program token plus args), exactly like the other command seams. Its program token resolves against the adapter **install dir** (REQ-INSTALL-11), like `[digest].extractor` / `[session.psyche_resume]`: a bare/relative program (e.g. `claude-spt`) resolves to `<install_dir>/<program>(.exe)` before PATH. Args support **adapter-static `{adapter_dir}` / `{adapter_name}` substitution only** — **not** session keys. The"
  },
  "REQ-HAZARD-INJECT-WORKER-POISON": {
    "title": "The per-session inject-worker floor Mutex is SHARED by the inject worker (open/flush) and the controller-input path (dispatch_input Layer C buffer_if_held); a panic under the lock on EITHER poisons it, and a bare .lock().unwrap() at the next site then panics too — a panic in the inject WORKER kills its thread WITHOUT reaping the translation child, orphaning a live binary while event_tx.send fails, so force-native reports 'worker-gone' delivered=false FOREVER (the F-e generic-miss shape). HARDENING, NOT the F-e incident root (perri's matrix exonerated poison under thrash/dormancy/churn): (i) poison-tolerant floor lock (unwrap_or_else into_inner) at all 3 sites so one panic can't cascade the session's delivery dead; (ii) a panic-resilient inject worker (catch_unwind -> fault+terminate the child on a worker panic) so a dead worker never orphans a live binary + strands delivery. Poison-tolerance class of REQ-HAZARD-EFFECT-JOURNAL-PTY-WEDGE / bug #16.",
    "doc": ""
  },
  "REQ-ARCH-3": {
    "title": "Wire-protocol version independent of crate semver, N-1 compat window",
    "doc": ""
  },
  "REQ-RC-QUALIFIED-TARGET-CANONICAL": {
    "title": "RC-RENDER-TRUTH W1 (ADR-0042 decision 4, hertz elevated-endpoints RCA core leg 2, doyle seam-confirmed rc.rs establish_attach): the resolver's canonical BARE endpoint id is carried separately from the user-facing qualified target — AttachRequest.endpoint_id is always the bare id (today rc passes the ORIGINAL qualified string; the target's resolve_local_session compares verbatim vs the bare HostedSession.endpoint, so `spt rc id@node`/`subnet:id` dials the RIGHT node then gets a false no-live-session refusal). N-1-additive: bare-form callers are unchanged. Gate: impl — canonical-id carry through establish_attach; unit — Address::parse qualified forms yield bare wire id, user-facing copy keeps the qualified spelling; int — bare + id@node + subnet:id ALL attach against a remote broker-hosted target, wire always carries the canonical bare id; doc — ADR-0042.",
    "doc": "Decisions: <!-- --> 1. **`spt rc` consults the honest-session authority before the offline fast-fail.** Normal `spt rc <id>` runs the same bounded `SessionProbe::has_live_session_honest` gate `endpoint run` uses (ADR-0041 single liveness authority). An honest session exists → attach via the session-confirmed path regardless of persisted status. No honest session → the existing offline refusal stands. A claimed session with a dead client tree → refusal/reap, never attach. Reuse `SessionProbe`; no new liveness heuristic. <!-- --> 2. **Resume stamps UNBOUND.** A resume launch transitions an exist"
  },
  "REQ-REL-2": {
    "title": "Release asset set consumable by the self-updater: platform binaries, SHA256SUMS, SignedRelease metadata, manifest schema, mock-adapter zip; tag-triggered cross-repo pipeline",
    "doc": "Release runbook: <!-- -->"
  },
  "REQ-EP-2": {
    "title": "Agent endpoints vs Shells distinction in the type model",
    "doc": ""
  },
  "REQ-INST-9": {
    "title": "Multi-subnet membership (same-user N subnets; cross-user seam)",
    "doc": ""
  },
  "REQ-PICKER-CURRENT-DIR-LABEL": {
    "title": "A-2/A-3 (F029, operator, semantic pair): the Choose-project rows must self-identify the CURRENT DIR. build_project_choices (picker/model.rs:322-352). A-2: when the run cwd IS already a history dir the `Here:` row is (correctly) suppressed by the dedup (REQ-PICKER-CHOOSE-DEDUP-ALL), but the matching history row rendered bare `r.display` with no cwd affordance — mark it `<display> (CURRENT DIR)`. A-3: the not-in-history current-dir row changes from `Here: <run_cwd>` to `CURRENT DIR --> <project>`, deriving the display the SAME way the history refs do (folder tail; honest fallback to the raw path when underivable). `cwd` payload unchanged. Grep-tests rule: 3 `starts_with(\"Here: \")` asserts (model.rs) + a `Here: /here` render assert (view.rs) are behavior assertions on the OLD label. See triage A-2/A-3.",
    "doc": ""
  },
  "REQ-PAIR-1": {
    "title": "TOTP-seeded SPAKE2 pairing",
    "doc": ""
  },
  "REQ-MSG-IDLE-TRANSLATION-BINARY": {
    "title": "spt-hosted idle message delivery via an adapter TRANSLATION BINARY (ADR-0022). New opt-in manifest section `[message-idle-translation-binary]` = a TABLE carrying a `path` scalar (doyle OPT-B ruling: modeled as a table, not a bare top-level scalar, so a preceding section cannot silently absorb it + N+1 extensible; spt-core does NOT deny_unknown_fields, so a future key degrades gracefully); spt-core LIFECYCLE-manages it (spawn when the endpoint comes up, terminate when it goes down). The binary is a PURE stdin→stdout filter; spt-core owns EVERY PTY write. stdin (JSON-lines): `{type:\"init\",endpoint_id,node}` first · `{type:\"event\",envelope:\"<EVENT…>\"}` per inbound message (ADR-0020 envelope) · `{type:\"input\"}` content-free ping on each operator keystroke (binary tracks user-idle for its own idle-gated buffering; PTY input content NOT duplicated). stdout (JSON-lines): keystroke-commands `{key:…}`/`{delay_ms:…}`/`{text:…}` (extensible). spt-core applies the emitted sequence to the broker PTY ATOMICALLY (the W1 coordination — REQ-HAZARD-INJECT-CONTROL-COEXIST). The daemon poll feed is the ONE idle substrate for both topologies (Q1=A): harness-hosted consumer = the Monitor child, spt-host",
    "doc": "`[message-idle-translation-binary]` — spt-hosted idle delivery: <!-- --> // `[inject]` — inject-input methods: <!-- --> ### `[message-idle-translation-binary]` — spt-hosted idle delivery (ADR-0022) Opt-in. The adapter's **idle-delivery translation binary**: a pure stdin→stdout JSON-lines filter spt-core lifecycle-manages (spawned when the spt-hosted endpoint comes up, terminated when it goes down). spt-core feeds it the inbound `<EVENT>` feed and reads back keystroke-commands, which spt-core applies to the broker-held PTY **atomically** — controller input is buffered during the emitted sequenc"
  },
  "REQ-UPDATE-APPLY-ALREADY-APPLIED": {
    "title": "`spt update apply` classifies an already-staged / already-applied state as a friendly exit-0 no-op instead of dying at the binary-aside rename with 'Access is denied (os error 5)'. ROOT (F-025): a second apply on an already-applied staged version reaches the two-phase binary-aside rename and fails os-error-5, reading as a hard failure when the machine is simply up to date. FIX: apply gains the same pure classifier `fetch` got in v0.18.0 (REQ-UPDATE-FETCH-CURRENT-UX) — already-applied → clear message + exit 0, and the flow MUST short-circuit BEFORE the binary-aside rename in that state; mirror the classifier at ALL apply reject/entry sites the way the fetch fix covered its three. Genuine errors (bad signature, wrong platform, true downgrade, network) still propagate nonzero. (F-025)",
    "doc": ""
  },
  "REQ-REST-TERMINAL-NORMALIZE": {
    "title": "REGISTRY-LIFECYCLE W2 (ADR-0041 decision 3, rest-normalize P0): definitive hosted-session loss normalizes TERMINALLY and ATOMICALLY — one store-level mutation writes status=offline + rest_state=suspended + clears dormant_since_ms in the SAME info.json write, invoked at authoritative broker-session-loss/liveness-reap + cmd_stop. NOT daemon_rest_event(Suspend) (no edge when effective already Suspended — raw Active intent survives); NOT reader-side blanket offline-implies-suspended (destroys explicit-Wake semantics: wake writes intent first, reconcile consumes). Graceful shutdown keeps echo-before-teardown. Kills the zombie WAKE_RESUME loop (perri field: repeated cross-generation resumes of a dead session). Gate: impl — atomic terminal-normalize mutation + call sites; unit — store-level atomic pair never mixed + endpoint_stop covers already-offline/raw-Active input; int — session vanish means offline+suspended and the NEXT reconcile emits NO WAKE_RESUME, explicit suspended/offline->Wake->Active launches EXACTLY once, RefuseLivePid-then-valid-bind custody race leaves the revived seat unsuspended; doc — ADR-0041.",
    "doc": "Decision: <!-- --> 1. **Online is earned, not declared.** A creator may stamp `status=online` only from actual persisted state + hosting authority — never from manifest capability alone. Legacy hybrid rows self-heal at reconcile, but only after a SUCCESSFUL broker query: a broker failure is never interpreted as an empty session set (no mass-offline on a hiccup). 2. **Control cleanup splits from offline classification.** Reconcile clears `controlled`/`driven_by`/`viewer_count` for EVERY endpoint absent from session truth — regardless of state or controllability — while offline classification ke"
  },
  "REQ-SPAWN-FRESH-TRUTHFUL": {
    "title": "REGISTRY-LIFECYCLE W3 (ADR-0041 decision 5, spawn-fresh): endpoint create is TRUTHFUL at the broker, atomically — spawn policies split Fresh/RejectExisting vs ResumeOrReuse; new KIND_SPAWN_FRESH wire verb with TYPED conflict (distinct kind fails LOUD on an N-1 broker vs a silently-ignored serde-defaulted field), sharing the single-flight/PTY spawn body; Fresh at AlreadyLive/lost-race returns the typed conflict NEVER Spawned(existing); CLI contract exit 1 + stable ENDPOINT_CREATE_CONFLICT:<id> token + no attach + no state mutation + never kills the live session; omitted --resume keeps the documented create-default (fresh-and-conflict, same as explicit --create); preflight stays UX-only (cannot close TOCTOU). Gate: impl — policy split + verb + CLI contract; unit — intent-aware decision matrix; int — create-over-live E2E (exit 1, exact token, no attach, same broker PID) + concurrent-fresh race (one Created one Conflict, never two children, never Reused-as-success) + mixed fresh/resume race + offline/raw-Active+stale-ledger create + N-1 IPC loud-fail never-Created; doc — ADR-0041 + CLI reference regen (docs-drift gate: xtask gen, no internal codes in clap help).",
    "doc": "Decision: <!-- --> 1. **Online is earned, not declared.** A creator may stamp `status=online` only from actual persisted state + hosting authority — never from manifest capability alone. Legacy hybrid rows self-heal at reconcile, but only after a SUCCESSFUL broker query: a broker failure is never interpreted as an empty session set (no mass-offline on a hiccup). 2. **Control cleanup splits from offline classification.** Reconcile clears `controlled`/`driven_by`/`viewer_count` for EVERY endpoint absent from session truth — regardless of state or controllability — while offline classification ke"
  },
  "REQ-HAZARD-DAEMON-IDENTITY-ENV-SANITIZE": {
    "title": "MSG-IDENTITY W1 / F-036 leg a (perri field RCA 2026-07-09/10, psyche seat-theft — doyle ACCEPTED primary fix): the daemon MUST sanitize inherited per-session identity env (SPT_ENDPOINT_ID / OWL_SESSION_ID / SPT_AGENT_ID) at startup AND before EVERY role spawn — these are per-session identity and are NEVER correct inherited state for a daemon or its role children. ROOT: a daemon restarted from inside an agent session (routine during core dev / `spt update apply`) carries the session's SPT_ENDPOINT_ID and passes it verbatim to every [session.psyche_resume] spawn; core only strips each role's DECLARED env_remove list (runtime.rs:728), so ONE adapter env_remove miss infects the whole node — every psyche claude turn fires SessionStart, the adapter hook sees the endpoint id, takes the bind path, and ROTATES the victim's perch to the psyche's own sid with a valid prior-sid proof, every pulse (field: lia/deployah/doyle psyches ALL briefed as <sptc-active-perch id=doyle>; 37 peer msgs drained into lia's psyche transcript; victim deliveries eaten, communes dark, sends downgraded from:cli@node). Adapter half FIXED v0.18.8 (env_remove += SPT_ENDPOINT_ID + shim scrub + SPT_PSYCHE_TURN hook-bail",
    "doc": "7.38 EVERY write on a physical broker connection is bounded + cancelable + poison-on-failure — no writer holds the send gate across an UNBOUNDED OS write `[REQ-HAZARD-SHAREDSEND-NO-BLOCKING-WRITE-UNDER-LOCK]`: ### 7.39 Per-session identity env (`SPT_ENDPOINT_ID`/`OWL_SESSION_ID`/`SPT_AGENT_ID`) is NEVER inherited — the daemon scrubs it at startup AND on every role spawn, regardless of any role's declared `env_remove` `[REQ-HAZARD-DAEMON-IDENTITY-ENV-SANITIZE]` <!-- --> - **Failure (paid-for, perri field RCA 2026-07-09/10 — F-036 psyche seat-theft):** a daemon restarted from inside an agent ses"
  },
  "REQ-HAZARD-LIVEHOST-BOOT-RACE": {
    "title": "The brain's daemon-hosted Psyche lifecycle surfaces a host-FAILURE on the live perch (harness-diagnosable) and runs net-INDEPENDENTLY. When reconcile_once→host_one→spawn_psyche fails for a state=live_agent+status=online endpoint (e.g. the adapter's psyche binary absent from its install dir, REQ-INSTALL-11), the failure MUST be written to the perch info.json as a CURRENT-STATE field (reason + ts + attempt count; overwritten each 5s retry, CLEARED on successful host) and surfaced by `spt endpoint list`/status — never left as an eprintln on the brain's invisible stderr where a harness reading only perch state is blind. status=online stays authoritative (agent reachable; only the Psyche is missing — brain-restart rehydrate legitimately has online-without-Psyche windows), so this is a SEPARATE psyche-host-health field, never a status de-stamp. Net-independence is a locked-in invariant: spawn_live_host (brainproc.rs:230) reaches the reconcile and hosts the Psyche on a net-less/unpaired/peer-pump-STALLED node, proven by a REAL detached-daemon E2E (real broker→brain-child, real api seed+listen, real install-dir psyche binary). spt-core SURFACES the failure; the adapter owns fixing its pack",
    "doc": ""
  },
  "REQ-HAZARD-HOSTED-LIVENESS-RECONCILE": {
    "title": "B2 KEYSTONE: a daemon-hosted (spt-hosted) endpoint's info.json status is RECONCILED to real liveness, not left latched online. The broker exit-waiter (broker.rs:889-910) reaps its in-mem session table + emits ExitEvent but NEVER touches info.json; lifecycle::mark_offline only fires on Psyche teardown — so a dead/exited harness (operator closed the tab) stays status=online forever (is_perch_alive returns ONLINE for daemon-hosted, liveness.rs:80-93). FIX (doyle ruled PULL-PRIMARY — the live-status analog of REQ-HAZARD-ROSTER-GHOST): the livehost reconcile loop (reconcile_once livehost.rs:226-313) queries the broker's live session set (KIND_SESSIONS) each tick and, for any status=online live_agent perch PAST the boot grace whose endpoint has NO live broker session, marks it offline (lifecycle::mark_offline → status=offline → is_perch_alive=false). GATED on spt-hosted (controllable==Some(true)) so a HARNESS-HOSTED relay live agent (api listen, legitimately online with no broker session) is NEVER mis-marked. Crash-robust + self-healing on the next tick (clear-on-event is not crash-robust alone). PUSH (brain ExitEvent→mark_offline) is an OPTIONAL fast-path only if the daemon brain is rel",
    "doc": ""
  },
  "REQ-RC-RECONNECT-TRUTH": {
    "title": "W3 (LIFECYCLE-TRUTH): rc reconnect never auto-starts a daemon and never hangs forever. ROOTS rig-proven (the operator's long-standing 'stop 2-4 times' bug): (a) an rc client's reconnect loop AUTO-LAUNCHES a daemon via WMI (rig: DAEMON_LAUNCH_VIA_WMI from the rc) — resurrection fights the operator's stops; (b) rc freezes at 'Reconnecting to local daemon…' forever when its session died with the broker. FIX: rc NEVER auto-starts a daemon (reconnect only to an already-up broker; loud 'session lost — daemon down' exit otherwise), bounded reconnect with visible countdown.",
    "doc": ""
  },
  "REQ-CONTROLLER-LIVENESS-REAP": {
    "title": "B-2 (REMOTE-TRUTH triage §B-2, REDUCED @bdc1242): a stale ONLINE+CONTROLLED stamp on a live-session perch self-heals — the info.json driven_by/controlled RECORD is made to match the broker's SINK-TABLE TRUTH. ROOT (persisted-stale-stamp class, doyle Q1): the livehost control reap gates on !has_session (reconcile_hosted_liveness), and a brain-only update KEEPS the session (REQ-UPD-3), so a controller stamp that went stale WHILE the session lived was never re-derived from broker truth. NOT a transport bug: (a) a persisted conn is the REQ-UPD-3 feature; (b) an idle-severed conn eventually EOFs via QUIC keepalive → handle_conn detach (path 1) — and the reason paths 1-3 previously failed to clean up was the B-1 broker floor-lock POISON WEDGE (cleanup panicked under the poisoned lock), now fixed. REDUCTION (doyle, my ground-truth): the prescription was 80% pre-built — converge_perch_stamps (broker.rs, REQ-HAZARD-CONTROL-STAMP-CONVERGENCE) ALREADY converges info.json driven_by/controlled to the broker's controller_by/has_controller on EVERY KIND_SESSIONS poll, and the livehost reconcile already TRIGGERS that poll per tick (query_live_session_endpoints). So NO new IPC query, NO new livehos",
    "doc": ""
  },
  "REQ-PICKER-4": {
    "title": "The picker's Subnet category renders the canonical node LABEL, not bare key-hex: a subnet row's node renders as 'LABEL (keyprefix…)' (e.g. 'HFENDULEAM (bcead52b…)') per CONTEXT.md:650 + Instance.node_label, NOT the raw node key-hex (SPT_DEV:14efb80cb… — a picker-only regression because resource_projection→ResourceRow drops node_label, so data.rs subnet_rows uses the raw row.node). Thread node_label into the picker subnet path (ResourceRow gains node_label, or subnet_rows looks it up via the registry's node_labels) and REUSE the one canonical render (format!(\"{l} ({}…)\", key_prefix) — cli.rs / wansend.rs), never a re-implementation. (v0.10.0)",
    "doc": ""
  },
  "REQ-HAZARD-TEMPLATE-ARGV-FILL": {
    "title": "Command-template substitution fills argv ELEMENTS, not a re-tokenized string: spt-core currently `fill_template`s {key} values INTO the command STRING and THEN `tokenize`s the filled string (runtime.rs:94/122), so a multi-word {key} value whitespace-SPLITS into multiple argv tokens unless the adapter hand-quotes the placeholder, and a value containing a `\"` (or `;`) injects/breaks tokenization (shell-injection-adjacent). A filled value MUST become exactly ONE argv element regardless of spaces/quotes in the value. Fix: tokenize the TEMPLATE into argv FIRST, then `fill_template` EACH token, so a `{key}` slot resolves to a single element and the value never participates in tokenization (no whitespace-split, no quote/semicolon injection); preserve the missing-key / empty-command errors and `{{`/`}}` non-interpretation. perri's F-009 (v0.8.1 dogfood, argv-capture-confirmed): a multi-word `{psyche_prompt}` = \"PSYCHE REVIVAL time: epoch-ms:… incoming event: (none)\" arrived as argv[6..12] (7 stray tokens), the harness runner strict-parsed `--prompt` against the 2nd word, exited 2 within ~1s → phantom hosted perch. Applies to EVERY [session.<role>] template (psyche_init, extractor, notif, …",
    "doc": ""
  },
  "REQ-HAZARD-GRACE-BEFORE-SIGNOFF": {
    "title": "Grace-period wait completes before composing INIT_SIGNOFF (1.1)",
    "doc": ""
  },
  "REQ-HAZARD-RC-ATTACH-TRUTH": {
    "title": "RC-RENDER-TRUTH W1 (KNOWN-HAZARDS 7.46 — umbrella conformance seam for ADR-0042): an rc surface answers from live session authority, never a stale persisted projection; a resuming perch is UNBOUND, not offline. Regression matrix from the hertz RCAs + operator field recovery: offline-row-over-honest-session attaches; offline-no-session refuses; zombie refuses/reaps never attaches; resume-never-bound reads UNBOUND and is attachable; harness-only refuses truthfully pre-stream; qualified targets attach with bare wire id. HEAVY nextest group at birth for any leg spawning a daemon tree (standing CI lint). Gate: int — the matrix; doc — KNOWN-HAZARDS 7.46.",
    "doc": "7.45 Endpoint lifecycle state converges to truth from every death path — no optimistic online without authority, no surviving control stamps, no immortal wake intent, no untruthful create `[REQ-HAZARD-ENDPOINT-LIFECYCLE]`: ### 7.46 An rc surface answers from live session authority, never a stale persisted projection — and a resuming perch is UNBOUND, not offline `[REQ-HAZARD-RC-ATTACH-TRUTH]` <!-- --> - **Failure (paid-for, hertz perri contradiction RCA 2026-07-17/18 + operator field recovery):** the broker hosted an honest live session (client tree alive, `SessionProbe::has_live_session_hones"
  },
  "REQ-HAZARD-UNHOST-PSYCHE-REAP": {
    "title": "On un-host, the detached `{id}-psyche` HARNESS PROCESS is reaped — not just its in-brain pulse-driver thread. Today stop_host (livehost.rs:203) trips the HostedLife stop flag + JOINS the driver thread, but the Psyche is a detached harness process (spawn_psyche → ManifestRuntime detached spawn, runtime.rs:341-356; its pid is untracked in HostedLife though stamped on the `{id}-psyche` perch, where residency-confirm already reads it). So endpoint-stop / mid-life agent-death / a B2/B5 offline-then-unhost leaves the psyche process ORPHANED, alive until the next daemon-stop (where Breap's job/group reaps the whole brain subtree). The Psyche STAYS a harness process by design (CONTEXT.md 97/203/251 — headless harness session, its own perch) — the fix does NOT move it in-brain; it SCOPED-kills the `{id}-psyche` pid on un-host (never machine-wide — shared box). Track the pid in HostedLife at host_one (cleanest) or read the `{id}-psyche` perch pid at stop_host. Composes with H3 (endpoint stop → offline → reconcile un-host → reap) and B2/B5 (the offline arms that trigger un-host). (v0.12.0)",
    "doc": ""
  },
  "REQ-START-5": {
    "title": "Adapter-agnostic harness-hosted seed + bind-time adapter/profile resolution (ADR-0021): `api seed` carries only parent_pid + session_id (+ optional cwd), no --adapter — a pure \"a harness session exists at this pid\" record; --adapter becomes an OPTIONAL override across the whole api group (an explicit name[:profile] for adapter dev, never required). Omitted, listen/poll resolve the owning adapter/profile AT BIND as a pure read against the live registry — never a seed-time snapshot that can drift: seed parent_pid → exe basename → host_binaries candidate set (REQ-MANIFEST-8) → active-profile pointer (REQ-INSTALL-12) primary, else greatest-registered_at_ms candidate base profile (name-asc tie) → friendly zero-match error. Covers BOTH LiveAgent (listen) and ReadyAgent (poll) bringup. Restores legacy parity: `$LIVE start <id>` → `$SPT listen <id>` with no mandatory --adapter, one generic SessionStart hook per harness binary. (v0.9.0)",
    "doc": "Startup flows (the two topologies): **Harness-hosted (e.g. spt-plugin; the harness binary is user-launched, harness is the parent).** Key constraint: the SPT *live agent* does not exist until the agent invokes start — the `live_id` isn't chosen at session boot, and `$LIVE start` is itself invoked *behind the Monitor tool*, so it becomes the long-running relay. So binding cannot happen at SessionStart directly. A **seed record** (daemon-held, in-memory — not a file) bridges the gap: 1. The harness's SessionStart hook calls **`spt api seed --pid <parent_pid> --session-id <sid> [cwd]`**. The daem"
  },
  "REQ-INSTALL-6": {
    "title": "Linux elevation install leg: install.sh symlinks the binary into a sudo-reachable path (/usr/local/bin; graceful print-the-one-liner when unelevated) so sudo spt resolves; first sudo spt detects elevation and prompts ONCE for the default user account — thereafter any elevated daemon launch runs daemon + state under that account, never root (KH 5.7 interplay verified) (M8 decision 8)",
    "doc": ""
  },
  "REQ-HAZARD-BOUNDARY-READY-STRAND": {
    "title": "C-2 (F029, SEAM-2 pinned — B6's SECOND HALF, the live-wake blocker; perri wakep9 vs wakep4 gate-state dump + doyle code trace): at a /clear, CC fires SessionEnd(reason=clear) for the DEPARTING session BEFORE SessionStart; the departing sid STILL matches the perch pin at that instant, so the adapter's [hooks.SessionEnd] → `api session-end` AUTHENTICATES and the soft handler REMOVES the ready marker (+ unregister_address, reporting.rs cmd_session_end:206-207). The subsequent `api boundary` rotates the sid but NOTHING re-writes ready → is_online false → try_spt_hosted_inject Nones on the CLI gate BEFORE any broker RPC → every post-clear force-native (incl. the checkpoint FIRE) reports the generic UNDELIVERED, persistent by construction (no path re-stamps ready outside a real bind). The single differing gate field at every UNDELIVERED instant is ready-absent (info online/controllable/rotated-sid all healthy, translate alive). Paid-for hazard. FIX: cmd_boundary re-stamps the ready marker (+ status online, idempotent) ATOMICALLY with the sid rotation — a boundary PROVES a live successor session on the same harness process; a REAL end has no subsequent boundary so genuine teardown is unto",
    "doc": ""
  },
  "REQ-PICKER-CHANGE-ADAPTER-FLOW": {
    "title": "B-2 (F029, operator; LARGEST item): `ConfirmOption::ChangeAdapter` wrongly routes into the CREATE flow (Screen::CreateAdapter → CreateId → CreateHome → START, reenter_create(true)) — it re-prompts id + home and then STARTS the endpoint. Required: prompt ONLY the harness-adapter pick, apply the change to the perch record (update info.json.adapter via the existing write seam; an `<adapter>:<profile>` pick stamps the full option, composing with A-4), then RETURN to the endpoint's Confirm menu — NO id prompt, NO home prompt, NO start. FIX: a return-to-Confirm mode on the adapter-pick screen (flag or dedicated Screen::ChangeAdapterPick) skipping CreateId/CreateHome + the launch outcome. Unit the flow-state transitions + the record write. Shared-seam: touches picker flow state — run the full picker cluster. See triage B-2.",
    "doc": ""
  },
  "REQ-HOST-RUN-2": {
    "title": "Project-scoped working directory for spt-hosted bringup: `spt endpoint run` lands the broker-spawned harness PTY in the user's PROJECT cwd, not the daemon's, via an additive `SpawnReq.cwd` field carried through the broker PTY spawn (portable-pty CommandBuilder cwd). N-1-safe wire change (additive, defaulted). Required because the consumer (Claude Code) is project-scoped: broker-inherited cwd = the daemon's cwd = the wrong `.claude`, wrong session history, wrong digest source; `cc <id>` at a project root MUST land the harness in that project. W1 ships broker-inherited cwd as a bringup-proof shortcut only; this REQ must land before the M12 gate (doyle, 2026-06-14).",
    "doc": ""
  },
  "REQ-REL-3": {
    "title": "Two-key release-signing trust anchor: primary + offline never-used recovery, both pubkeys embedded in the binary's trusted set, manual local signing (ADR-0015)",
    "doc": ""
  },
  "REQ-PICKER-5": {
    "title": "`spt endpoint list` (bare/subnet view) renders an ALIGNED table with canonical node labels: cmd_endpoint_list prints subnet rows with `\\t` TAB separators (cli.rs:~1651-1662) so variable-width endpoint_ids snap fields to different tab-stops → a RAGGED status column (operator screenshot: X/help statuses misaligned vs rt-*/sptc-*/wall-a); and it calls the node renderer with no label → bare key-hex for every row (SAME ResourceRow-drops-node_label root as REQ-PICKER-4). FIX: max-width per-column padding (mirror render_node_rows' pad, pad by char count not byte len — '…' is multibyte) replacing the tabs, and render the node via the shared node_label_display now that ResourceRow carries node_label (REQ-PICKER-4). Extract a pure row-formatter seam so the alignment+label is unit-testable. ALSO: the bare list is the SUBNET view (a just-run LOCAL perch is invisible cross-subnet until the next advertise tick), so emit a `--local` hint line so a freshly-run endpoint isn't perceived as lost. (v0.10.0; operator-flagged + doyle dispatch 2026-06-17)",
    "doc": ""
  },
  "REQ-CONTROLLER-LEASE-IDENTITY": {
    "title": "RC-RENDER-TRUTH W2 (ADR-0044 decisions 1+2, hertz same-machine --take split-brain RCA P0-A/B, doyle seam-verified broker.rs resolve_subscribe 1296-1318 + 1334-1347): each rc invocation/attach stream mints a UNIQUE controller lease id carried through SubscribeReq, the controller slot, and Input/Resize; node identity stays separate as attribution/access policy only. The ONLY silent successor/replay case is same-lease + equal-or-newer generation (ADR-0038 fix-6 dispatcher-recovery contract preserved exactly); same node + different lease = DISTINCT controller. Explicit Take on a distinct incumbent lease ALWAYS revokes loudly AND authoritatively — atomically revoke/fence the old lease and FORCE its attach stream closed, then install the taker, regardless of whether by-node strings match; the Displaced notice is best-effort (today old.tx.try_send at broker.rs:1342-1345 can DROP the notice on a Full queue while become_controller still replaces — the revoke/close must land even when notice enqueue fails; the closed stream is itself the terminal signal rc's PumpEnd::Displaced/EOF path handles). Today same_identity keys on controller_by()==by alone, intent never consulted; two same-machine w",
    "doc": "Decisions: <!-- --> 1. **Distinct viewport/lease identity.** Each rc invocation/attach stream mints a unique controller lease id, carried through SubscribeReq, the controller slot, and Input/Resize. Node identity is kept separately for display/access policy. The ONLY silent successor/replay case is same-lease + equal-or-newer generation (the ADR-0038 fix-6 dispatcher-recovery contract, preserved exactly). Same node but different lease is a DISTINCT controller. 2. **Explicit Take always revokes a distinct incumbent loudly — and revocation is authoritative, notification is not.** If `intent == T"
  },
  "REQ-HAZARD-ELEVATED-DAEMON-SPAWN": {
    "title": "The daemon always runs unelevated in the invoking user's universe, regardless of which command spawns it: an elevated spawner de-elevates (Windows: UAC linked token via CreateProcessWithTokenW; Linux: drop to SUDO_UID/SUDO_GID + the invoker's HOME) — an elevated daemon's pipes deny unelevated clients (every later spt reads not-running→spawn→bind Access-denied) and a sudo'd daemon roots the user's state universe (5.7)",
    "doc": "5.6 Windows detached children inherit a captured caller's pipe `[REQ-HAZARD-DETACHED-PIPE-INHERIT]`: ### 5.7 Elevated commands spawn the daemon with the wrong token `[REQ-HAZARD-ELEVATED-DAEMON-SPAWN]` <!-- --> - **Failure:** membership-implies-reachability made *every* `spt` invocation a potential daemon spawner (`ensure_running`), including the elevation-gated ones (`subnet create`/`join`, REQ-SUBNET-4). The spawned daemon inherits the spawner's token. **Windows:** an elevated `subnet create` auto-starts an ELEVATED daemon whose named pipes deny unelevated clients — every subsequent unelevat"
  },
  "REQ-HAZARD-SESSION-PIN-WEDGE": {
    "title": "A perch PINNED to a DEAD session-id self-heals instead of wedging forever. authenticate() (spt/src/api/auth.rs:78) gates api poll/state/boundary on proof-sid == info.json.session_id; if ONE boundary rotation is lost (transient env corruption kills the /clear-era hook), the perch stays pinned to the dead sid and EVERY id-scoped hook call refuses — INCLUDING boundary itself (it presents the new sid), a permanent strand (ready:false, stale .idle, drain no-ops, WAN spool sleeps forever; AUTH_REFUSED is stderr-only = invisible inside a hook). FIX: authenticate() gains a DEAD-OWNER fallback — when the sid MISMATCHES AND the perch's recorded pid is dead (proc::is_process_alive==false), ACCEPT the caller's sid and RE-PIN (rotate session_id + log SESSION_REPIN loud). Same trust model as establish_perch's conflict gate (api/startup.rs:207-210), which already allows rebind exactly when owner_alive==false (an orphaned perch accepts a new LOCAL owner). A LIVE-owner mismatch STILL refuses (squat protection UNCHANGED). ADDITIVE to token auth — the existing token-auth recovery path is UNTOUCHED; the new branch fires only on (no token) AND (sid mismatch) AND (owner dead). COVERAGE SPLIT (explicit, ",
    "doc": "F-019 diagnosis lesson — confirm an adapter binary actually SPAWNED before behavioral diagnosis `[REQ-INSTALL-11]`: ### 7.25 A perch PINNED to a DEAD session self-heals (dead-owner re-pin) instead of wedging forever; a LIVE-owner rotation still refuses `[REQ-HAZARD-SESSION-PIN-WEDGE]` - **Failure (F-024C/F-024D, ENLYZEAM field + clean-room repro 2026-07-02):** `authenticate()` (auth.rs) gates `api poll`/`state`/`boundary` on `proof.session_id == info.json.session_id`. If ONE boundary rotation is LOST — the departing session dies (crash / tab-close) or its `/clear`-era `boundary` call never lan"
  },
  "REQ-OPID-TRACING-RETRY": {
    "title": "A-4b (REMOTE-TRUTH triage §A + ADR-0034 Decision 2): the tracing-only op families auto-retry ONCE with a fresh op on the typed no-longer-held error, so a broker-restart-dropped conn/stream self-heals instead of surfacing core lingo to the user. ROOT: net/rest.rs declares rest op-ids 'tracing/correlation only … redelivery needs reporting, not dedup', yet the journal enforced exactly-once on them — a rest/attach op whose conn the broker no longer holds (post-restart) returned the typed 'already applied … no longer held … retry with a fresh op_id' (broker.rs:2796 net-dial / 3047 stream-open) straight to the user (WOKE_FAIL / rc attach fail). FIX (Decision 2, scoped): the rest family (request_rest) + rc attach stream-open (request_attach_endpoint) — and ONLY those — catch the typed no-longer-held error INTERNALLY and re-issue ONCE with a FRESH op minted from the SAME minter (A-4a MintedOp; same producer, new seq). sync/update pull families (request_sync/request_update, durable open_op) are NOT wrapped — their exactly-once dedup is load-bearing (negative control). The typed op error becomes internal-only; if the retry ALSO fails, the user-facing line names the observable situation + nex",
    "doc": "no-longer-held error by a SHARED CONST MARKER, not a typed wire variant): <!-- --> Decision 3's \"typed op error\" is realized as a single `pub const` marker phrase, not a new typed variant on the KIND_ERROR wire path"
  },
  "REQ-HAZARD-ATTACH-WEDGE": {
    "title": "A legitimately dead PTY child (real crash/kill) + an undrained operator pump must NOT wedge the broker for all other clients. ROOT (v0.12.0 real-harness defect): loopback attach output is a blocking write_all into a bounded 64KB tokio duplex (nethost.rs:1040,1090); when the operator's rc pump stops draining (tab closed) the buffer fills and write_all blocks forever (the 'loopback never hangs' assumption at nethost.rs:1103 is false), parking a worker in the 2-worker net runtime (nethost.rs:640); a couple of these saturate BOTH workers → every new attach / `endpoint run` stalls right after 'PUMP_IPC_READER: spawned' → 30s FIRST_EVENT_GRACE → 'no output / dead or wedged'; `daemon stop` cannot join the stuck workers. DISTINCT from the removed B1 path-(c) mutex deadlock. DISPOSITION = PROVE-DON'T-CHANGE (doyle GATE-PASS @e883f45, 2026-06-18): this ROOT is the SUPERSEDED v0.12.0 hypothesis — the post-L0 code ALREADY prevents the wedge, so NO fail-fast / worker-count code was added. serve_attach forwards fire-and-forget (net_stream_send op_id=None) and the broker-side send_stream is already BROKER-QUIC-DEADLINE-bounded (bounded_block_on, 10s); the loopback duplex is drained broker-INTERNA",
    "doc": ""
  },
  "REQ-LIVENESS-ORACLE-SOUND": {
    "title": "TEARDOWN-AUTHORITY W2 (todlando W1 gate-round-0 finding, doyle-scoped from the LANDED W1 code 2026-07-19): 'does this pid still exist' has ONE answer in spt-core and it is derived from the OS process table. TODAY spt-daemon/src/broker.rs session_is_zombie computes wrapper_alive from spt_store::proc::is_process_alive, which probes OpenProcess on Windows — and OpenProcess keeps SUCCEEDING for a TERMINATED process while any parent holds an open handle, which the broker ALWAYS does (Arc<PtySession>) for every PTY child it spawned. A correctly-reaped harness therefore reads ALIVE, flipping zombie_verdict off its PRIMARY class (Some(false) = dead root + surviving record = always a zombie) onto the conditional arm, which additionally demands adapter_labeled && past_grace && !has_live_descendants. CONSEQUENCE, live today: a dead-root session that is NOT adapter-labeled is claimed LIVE indefinitely — `endpoint run`'s dup-guard refuses ENDPOINT_ALREADY_LIVE over an already-dead tree and cmd_rest's Suspend alive_hint forces from=alive on the same false claim (both via has_live_session_honest, cli.rs:2014 and :3805). REQ-ENDPOINT-CYCLE-HONEST exists to give the cycle verbs ONE liveness authori",
    "doc": "7.50 The liveness oracle answers from the process table, never from a handle a caller still holds `[REQ-LIVENESS-ORACLE-SOUND]`: <!-- --> - **Failure (paid-for, found by todlando during TEARDOWN-AUTHORITY W1 gate round 0, 2026-07-19; latent in `session_is_zombie` since the cycle verbs were built):** `spt_store::proc::is_process_alive` probes `OpenProcess` on Windows, which keeps SUCCEEDING for a TERMINATED process while any parent still holds an open handle to it — and the broker holds `Arc<PtySession>`, hence such a handle, for every PTY child it spawned. So a correctly-reaped harness reads A"
  },
  "REQ-ENDPOINT-LIST-NODE-IDENT": {
    "title": "Bug #5: spt endpoint list local section header is the hardcoded literal LOCAL (this node) (render_local_section cli.rs:4359). Change to 'This node: <node-id>' using the existing node-ident idiom (os_hostname + nodeid public-key prefix, cli.rs:5531 — factor a node_ident_display helper); compute in the impure print_local_section, pass into the pure renderer. Update the two test assertions (cli.rs:10711/10716). See docs/NEXT-MILESTONE-BUG-TRIAGE.md #5.",
    "doc": ""
  },
  "REQ-HAZARD-LIVEHOST-NONRESIDENT": {
    "title": "A daemon-hosted Psyche that spawns then EXITS IMMEDIATELY is a host failure, surfaced like a spawn failure (closes the v0.8.1 residual masking): the REQ-HAZARD-LIVEHOST-BOOT-RACE signal stamps `psyche_host_error` only when `spawn_psyche` returns Err, NOT when the detached spawn() returns Ok but the child dies within moments (e.g. a bad-argv child exiting 2 — the F-009 case). That leaves the residual 'online + no Psyche + no cause' gap: the nested `{id}-psyche` info.json is written status=online with a real-but-DEAD pid and the PARENT perch carries NO psyche_host_error (perri's F-010: tasklist showed 0 host procs across the window while info.json read online). The host MUST confirm RESIDENCY — a hosted child not alive (or whose `{id}-psyche` perch never re-registers / has a dead pid) within N seconds of spawn is treated as a host failure: stamp the parent perch `psyche_host_error{reason:\"host not resident within <n>s (psyche perch missing/dead pid)\"}` (and do not leave a phantom online nested perch). Closes the last masking gap the v0.8.1 fix left open. perri's F-010 (v0.8.1 dogfood). Sibling of REQ-HAZARD-LIVEHOST-BOOT-RACE.",
    "doc": ""
  },
  "REQ-PICKER-RESUME-CONTEXT-PANEL": {
    "title": "#6: the 'Resume from history' view keeps the endpoint's 'Confirm selection' top panel and swaps ONLY the bottom panel to 'Resume from a prior session' — the user stays contextually informed about what they're picking (today the resume view replaces the whole screen). crates/spt/src/picker/view.rs (resume screen) + model screen state. See docs/NEXT-MILESTONE-PICKER-TRIAGE.md #6.",
    "doc": ""
  },
  "REQ-PICKER-PROJECT-DISPLAY-NAME": {
    "title": "A1 (F028, operator #1/#4/#6-display): `github-com-*` 'ghost' project entries are NOT phantoms — project_id_for_dir (spt-store/src/project.rs:64) derives ids from the git remote slug BY DESIGN (REQ-STORE-1 cross-machine sync): `github.com/SaberMage/spt-core` -> `github-com-sabermage-spt-core`. The ref is truthful; the BUG is presentation — the raw slug renders as the DISPLAY NAME everywhere (confirm-panel history view.rs:415-419, choose-project labels model.rs:314-337, resume-row titles, endpoint-list project column via latest_project_ref data.rs:417), which no operator recognizes as 'spt-core'. FIX: keep the slug as the KEY, render a friendly display name — the repo tail (spt-core) reusing the disambiguate_project_ids (model.rs:346) suffix mechanism for collisions. One shared display-name seam across all four surfaces. See triage A1.",
    "doc": ""
  },
  "REQ-SEAM-INJECT": {
    "title": "inject-input methods configurable per activity-state",
    "doc": ""
  },
  "REQ-ENDPOINT-LIST-MERGE-LOCAL": {
    "title": "`spt endpoint list` always merges this node's LOCAL (unadvertised) perches into the view; the `--local` flag is REMOVED (operator decision 2026-06-17). Rationale: `spt whoami` is a thin alias of `endpoint list` — a just-online agent running `whoami` must see its OWN perch, or it gets an omitted-self view ('chaos'). FIX: drop the `--local` flag + its `--detail` conflict test + the v0.10.0 REQ-PICKER-5 hint line (cli.rs:1678) + cmd_list_local; the bare list merges local perches into the subnet view; fix the whoami alias path accordingly. Run `cargo run -p xtask -- gen` (docs-drift, DEFAULT target). (v0.12.1)",
    "doc": "**endpoint list always merges local perches**: <!-- --> `spt endpoint list` (and therefore `whoami`) **always** appends this node's **LOCAL perch roster** as a trailing section, in addition to the SELF pin and the subnet groups. The subnet groups are the WAN registry snapshot, which lags a just-bound perch by a pump cadence — so without the merge a freshly-online endpoint (or the caller's own, under `whoami`) could be **absent** from its own listing, which reads as lost. The earlier `--local` flag (a separate this-node-only view) is **removed**: the local view is no longer a mode, it is uncond"
  },
  "REQ-INSTALL-8": {
    "title": "OS-service registration (REQ-INSTALL-1's deferred third leg): Linux systemd USER service + loginctl enable-linger (linger rides the elevated install leg; daemon starts at boot pre-login, user universe per KH 5.7, systemctl --user managed); Windows scheduled task at-logon (interactive session, no stored credentials); a node is reachable after reboot without any manual spt invocation (M8 decision 17)",
    "doc": ""
  },
  "REQ-PICKER-FORK-LABEL-CWD": {
    "title": "B-3 (F029, operator): the confirm-panel `Fork endpoint` option label is static and says nothing about WHERE the fork lands. A fork runs in the picker's launch cwd (run_cwd); the label must state that dir honestly: `Fork endpoint here --> <current dir>`. Anchor picker/view.rs confirm_option_label (was `fn(opt)->&'static str`). Make the label model-aware for the dir-relative options. See triage B-3.",
    "doc": ""
  },
  "REQ-DSR-SINGLE-CPR": {
    "title": "TEARDOWN-AUTHORITY W3 rider (hertz RCA 2026-07-19): a Device Status Report query yields EXACTLY ONE Cursor Position Report. Rides W3 because it is the same emulator-conformance surface as the width work and the same field capture surfaced it; kept a separate REQ so its evidence is not buried inside the width tags. Gate: impl — one CPR per DSR on the emulator reply path; unit — a DSR in the input stream produces a single well-formed CPR carrying the DISPLAY-column cursor position (i.e. consistent with REQ-SCREENGRID-WIDTH reckoning, not the raw char count).",
    "doc": ""
  },
  "REQ-HAZARD-REDISPATCH-CONTROL-STEAL": {
    "title": "REDISPATCH-TRUTH W1 (KNOWN-HAZARDS 7.41, hertz field RCA 2026-07-16 — 4/5 endpoints frozen per brain cycle): a fresh dispatcher must NEVER re-serve a terminal stream — a replayed historical Attach must not steal (same-identity silent become_controller, no Displaced) or clear (replayed-EOF detach_session) a LIVE controller. The legitimate same-by successor re-take after a brain restart still silently re-takes: the discriminator is stream LIFECYCLE, never origin identity. Gate: int — production-path regression D1: finished historical Attach + current active Attach, same endpoint/origin; restart target brain only (real run_dispatch_loop rediscovery, NO manual re-serve — the pre-fix e2e bypass is the lesson); prove the historical stream neither takes nor clears the current controller and current input/output stays exactly-once without detach; doc — KNOWN-HAZARDS 7.41. HEAVY nextest group at birth (FLAKE-LEDGER #15). Kin REQ-REDISPATCH-FINISHED-RETIRE (the mechanism), REQ-BRAIN-RESUME-NO-CONTROL-STEAL (the CLOSED session-cursor sibling — different leg), ADR-0038.",
    "doc": "7.40 A commune/signoff drop is deleted ONLY after every applicable tier is durably committed — an un-committable slice preserves the drop, never delete-then-lose `[REQ-HAZARD-COMMUNE-INGEST-BLACKHOLE]`: ### 7.41 A fresh dispatcher must NEVER re-serve a terminal stream — historical replay must not steal or clear a live controller `[REQ-HAZARD-REDISPATCH-CONTROL-STEAL]` <!-- --> - **Failure (paid-for, hertz field RCA 2026-07-16 — 4/5 endpoints frozen, twice in one day):** every brain cycle (`spt daemon refresh`, `spt update` apply — same `applyhost.rs` path) launches a fresh `dispatch::run_dispa"
  },
  "REQ-ENDPOINT-STOP-RESOLVES": {
    "title": "`spt endpoint stop <id>` REFUSES an id that nothing on the node knows, instead of stamping success on a no-op — an unconditional-success verb is a lying instrument (find: liam via flynn's discriminating repro, mechanism corrected by flynn 2026-07-26 superseding the original shell-half-action framing; shells aren't endpoints and the verb correctly never tried to resolve one — it then answered incorrectly). TODAY (cli.rs `stop_endpoint_core`, read at mint): ready-marker removal is `.is_ok()`-best-effort, `teardown_hosted_session` topology-gates on a `controllable` flag a nonexistent perch cannot have and falls through, `unregister_address` is `let _`, `terminal_normalize` silently skips a recordless perch — so EVERY string returns `Stopped{removed:false}` → `STOPPED:<id> (no ready marker; address unregistered)` exit 0, and the 'address unregistered' clause prints whether or not any address existed to unregister. FIX SHAPE: resolve FIRST — an id with ZERO evidence on this node (no ready marker, no perch record, no registered address, no broker session row) is REFUSED with a non-zero exit and a line naming that nothing by that id exists here; ANY evidence → proceed EXACTLY as today (st",
    "doc": ""
  },
  "REQ-EP-8": {
    "title": "AlwaysOnEndpoint: a resident, addressable, mindless endpoint whose adapter binary the daemon supervises continuously — register-triggered by an adapter-option's `[always-on]` manifest section, one supervised binary per `<adapter>[:profile]`, running independent of agent liveness. It self-manages its `#`-addressed channel endpoints via the existing `api bind` (one connection fronts many). The SECOND class of spt-core-boot-launched third-party binary (after the shell wake-watcher); supervision reuses the wake-watcher scaffolding (backoff / give-up latch / one-per-instance lock / orphan-kill / brain-side reconcile) MINUS the offline-only flip — always online, never resting (no dormant/suspended states). Two-way: agents message it; it may call `endpoint wake <id>`, target-side authorized (REQ-INST-3/6 wake resolution + access whitelist + shell_wake_spawn_anywhere — no caller-ownership gate). First consumer downstream: spt-discord.",
    "doc": "Always-on endpoints: resident supervised adapter binaries, `#`-sigil addressed: <!-- -->"
  },
  "REQ-HAZARD-CONTROLLER-GAP-RESUME": {
    "title": "A serving CONTROLLER whose serve-brain hits a b4 drop-don't-block FORWARD output gap must RESUME-FROM-FLOOR (re-subscribe from delivered_through and re-fetch the dropped frames from the ring), NOT snap-above and NOT fatal. ROOT (v0.13.0 forkpty re-run, post-keystone): b4 made the controller a non-blocking try_send that DROPS frames when its bounded channel fills (a controller that falls behind its OWN echo under a hard flood), so the next read is a forward gap the strict reject-gap (brain.rs:624/628, B2 exactly-once) FATALS — wedged_viewer_does_not_stall_controller (attach.rs:1048) drove ctrl.read_event() raw and fataled on `output gap got 6134 want 4643`. Pre-b4 the inline sleep-poll BLOCKED the drain to the controller's rate (no drops, no gaps); this is a b4 SIDE-EFFECT, not a new class. A controller CANNOT snap (it is authoritative — advances delivered_through; skipping rolled frames = not-exactly-once = B2 violation), so REQ-HAZARD-VIEWER-RING-ROLL-SNAP does NOT apply. B2 INVARIANT (doyle, broker.rs:327-330): the ring trim is delivered_through-BLIND (`while ring.len() > cap_chunks { pop_front() }`), so re-fetch is exactly-once IFF tail - delivered_through <= cap_chunks (4096) —",
    "doc": ""
  },
  "REQ-SHELL-3": {
    "title": "Drive channel (owner->shell, REST-only, never-spooled, latest-wins): the owner->shell mirror of sensory for continuous real-time control (scroll/crank/stick/avatar) — a [shell.drive] manifest vocab + EVENT_TYPE_DRIVE frame, delivered to the ONLINE binary only via a single live slot (a new frame supersedes an undelivered one — no spool, no queue, no replay on relink), dropped-with-diagnostic if the shell is offline; cross-node rides the ephemeral link (REST class), never the durable shell spool. Commands = discrete+durable; drive = continuous+ephemeral (CONTEXT:260, minted 2026-06-11 Gateway grill).",
    "doc": ""
  },
  "REQ-MSG-IDLE-EDGE-DRAIN": {
    "title": "On an endpoint's ACTIVE→IDLE transition the daemon DRAINS its pending spool (deferred AND non-deferred) through the same shared spt-hosted inject leg — closing the SECOND F-023 gap: no idle-edge drain exists anywhere, so an spt-hosted endpoint (which has no api-listen relay to wake it) strands BOTH message classes ('ACTIVE → spool deferred for hook-poll' and 'IDLE+no-binary → non-deferred for a relay that does not exist'). FIX: on the state ACTIVE→IDLE edge, offer the pending spool through the shared inject leg; REUSE the hook-poll drain's take/ack machinery so a concurrent `api poll` cannot double-deliver — ONE drain path, TWO triggers (hook-poll + idle-edge). v0.14.3 LAW holds on BOTH triggers: translation-binary-ONLY, a no-binary idle drain SPOOLS LOUD, never writes the PTY. (F-023, BUILD-F023-WANIDLE)",
    "doc": ""
  },
  "REQ-DAEMON-RESTART-SELF-INTERRUPTION": {
    "title": "SEED (inactive — operator-workflow trap, field-paid 2026-07-25): an spt-hosted context CANNOT restart the daemon, because the restart's STOP phase tears down the very context running the restart command — the START half never executes and the daemon stays down until an operator starts it by hand. Field case (this node, 17:47–17:50): an agent-kicked restart's stop-begin swept the initiating context and everything sharing its Job Object (flynn's shell + the relay-hopped alchemy Hub, cross-context job members — see REQ-SHELL-CLI-SPAWN-JOB-EXPOSURE's field-firing record); the broker drained ~2m10s to final exit (stderr ends abruptly at 17:50:05.8 amid routine traffic, no shutdown markers); the START was already dead; the operator manually started whatever binary was installed (v0.42.0), with no channel conveying which bits the restarting agent had intended — binary choice silently transferred to whoever brings the daemon up (kin: REQ-DAEMON-BITS-AMBIGUITY, the same which-bits-serve blindness). CONSEQUENCES: (a) every RESTART-REQUIRED apply (broker update, seed-skew restart per hazard 7.9) is operator-only today, and nothing documents that; (b) stop-begin kills bound shells with `close_",
    "doc": ""
  },
  "REQ-RUN-ID-REUSES-ADAPTER": {
    "title": "D-1 (REMOTE-TRUTH triage §D-1): `spt endpoint run --id <id>` with NO --adapter, when <id> names an EXISTING perch, REUSES that perch's recorded info.adapter and runs NON-INTERACTIVELY — instead of always falling to the picker as a create-new prefill (an existing endpoint retyping its own adapter, or being sent to a create-new flow, is the operator wart). ROOT (certain, no design tension): the cli `match (adapter,id)` special-cased only (Some,Some)→cmd_endpoint_run; the catch-all routed EVERY lone --id to crate::picker::run as a create-new prefill, never considering an existing endpoint (cli.rs ~1290). FIX: a PURE resolve_run_target(adapter, id, recorded) over the 4 (adapter?,id?) quadrants — (Some,Some)→Direct{a,id}; (None,Some(id))→ recorded adapter present (info.adapter = adapter-chosen-at-creation, spt-store info.rs:167) → Direct{recorded,id}, absent/no-perch → Picker{None,Some(id)} (today's create-new prefill UNCHANGED); (Some,None)/(None,None)→Picker unchanged. The perch lookup (read_info(resolve_perch_path(id,Infer)).adapter) is INJECTED as a closure so the router is pure + testable without a perch on disk; resume threads into BOTH Direct paths. Red-first: (None,Some(id),reco",
    "doc": ""
  },
  "REQ-HAZARD-SHELL-STALE-ONLINE": {
    "title": "A shell instance's ONLINE-ness is DERIVED (recorded status AND its recorded `shell.pid` not provably dead), never the recorded `status` field alone — an abruptly-dead binary (force-kill, crash, OOM: no link-break, so `close_shell`'s offline flip never runs) must not read online forever. The shell-side twin of REQ-HAZARD-DAEMON-HOSTED-LIVENESS, which gave AGENT perches exactly this resolver and which shells never got. PROVABLY DEAD is the narrow discriminant: `shell.pid` present AND parses non-zero AND `!is_process_alive` — pid absent, unparseable, or 0 (a broker-hosted spawn whose backend exposed no pid records 0) reads ALIVE, the same interim-parity/fail-toward-alive stance `liveness.rs` already holds, so a pid-less backend is NEVER falsely declared dead. Recycled-pid caveat, accepted at mint: a reused pid reads alive, so the heal is missed, never mis-fired — the failure direction is 'stays stale', never 'kills a live instance'. SITE CLASSIFICATION IS PART OF THE REQUIREMENT (authoritative cfg(test)-excluded census at mint = 8 status reads, 3 classes — do NOT blanket-swap the predicate): (a) DERIVED — relink's already-online refusal (the gate that made recovery impossible), the `s",
    "doc": "Conformance checklist (condensed): | # | Invariant | spt-core surface | |---|---|---| | 1.1 | Grace wait precedes INIT_SIGNOFF | daemon teardown | | 1.4/4.4 | Deferred rows excluded from event-stream drain | daemon spool drain | | 2.1/5.1 | Stable PID/broker-handle over ephemeral PID | liveness detection | | 2.3 | Handoff argv/IPC version-tolerant (newer brain ↔ older broker) | broker↔brain IPC, self-update | | 2.4 | gen_start = now() on cold-start + handoff | per-instance generation | | 2.6 | A shell's ONLINE-ness is DERIVED (recorded status AND a not-provably-dead `shell.pid`) — an abruptly-"
  },
  "REQ-RC-IDENTITY": {
    "title": "`spt rc` overlays a persistent endpoint-identity marker so the operator always groks which endpoint they control: a reserved TOP status row via a DECSTBM scroll-region (shrink the PTY's reported rows by 1, own the row), right-aligned `SUBNET : ENDPOINT_ID @ NODE`, CYAN text. Re-assert the margin + repaint on alt-screen enter / DECSTBM reset / resize (output-scanning, like the existing mouse_scanner). NO window title (the harness, e.g. CC, owns it for busyness — OSC dropped). Resolve subnet/node/id once at attach (perch/registry read) and thread into the pump; subnet = the endpoint's home/primary (\"local\" if none). The literal floating rounded-rectangle corner box is DEFERRED to the future web-based GUI (not a grid-model rc — that lift is better spent on the GUI). (v0.16.0)",
    "doc": ""
  },
  "REQ-HAZARD-DAEMON-HOSTED-LIVENESS": {
    "title": "Daemon-hosted perches (Psyche, spt-hosted Self) derive liveness from the daemon endpoint table + info.json status, never is_process_alive(info.pid) (2.5)",
    "doc": ""
  },
  "REQ-START-3": {
    "title": "spt-hosted startup: spawn-session then api bind (no file)",
    "doc": ""
  },
  "REQ-HAZARD-LIVEHOST-BOOT-LIVENESS-GATE": {
    "title": "B5: `spt daemon start` does NOT revive phantom Psyches for dead-but-online-latched perches. Today reconcile_once (livehost.rs:285) spawns a Psyche per status=online live_agent perch at boot WITHOUT verifying the harness child / {id}-psyche is actually alive — so a Cold start after an unclean stop revives N psyches for N dead-but-latched perches (3 psyches for 3 dead perches). FIX: gate the boot psyche-spawn on real child-liveness — a perch with NO live broker session (the B2 reconcile signal) is marked OFFLINE at boot instead of hosted, so a dead-harness perch is never revived. Shares the B2 reconcile loop (this is its boot-gate arm); composes with B2's honest latch. Also closes wall-a's psyche_host_error gap (residency-confirm does not run at boot tick-1, livehost.rs:395-441 / 257-263). (v0.12.0)",
    "doc": ""
  },
  "REQ-SUBNET-COUNT-ROUTABLE": {
    "title": "Bug #2: a remote node endpoint count drifts (0/2, 1/3) because node_status_rows (cli.rs:5314) increments the per-node total unconditionally, counting non-routable Offline ghost rows; purge is not a registry eviction (it gossips a one-shot Offline row that is immortal on remote viewers — eviction is per whole-node only). Fix: routable-only denominator (total += status.routable()) keeping a separate raw count for the all-Offline liveness branch; plus per-row Offline-TTL eviction so purged endpoints stop accumulating on remote snapshots. See docs/NEXT-MILESTONE-BUG-TRIAGE.md #2.",
    "doc": ""
  },
  "REQ-ATTACH-SEED-REQUEST-DOUBLE-SERVE": {
    "title": "A dispatcher-SEEDED attach Request is honored ONCE — the wire replay's copy of that same Request must not re-establish it. (Seed: DAEMON-LIFECYCLE W1 build, answering ADR-0047 decision 3's banked open question; origin CODE-PROVEN, not inferred.) THE ORIGIN: serve_attach (spt-daemon/src/attach.rs) processes the dispatcher-pinned `seed_request` through handle_attach_request BEFORE its event loop (ADR-0038 B, the durable-opener seed — it exists because a ROLLED ring can no longer replay the Request). It then subscribes the stream from the floor and enters the loop, whose `AttachRecord::Request` arm calls handle_attach_request UNCONDITIONALLY — there is NO `!attached` guard, and NO record that the seed already established this exact Request. When the ring has NOT rolled, the wire replay still carries that Request, so the SAME record is handled TWICE on the SAME conn, milliseconds apart, with identical (session_id, from_seq, intent, gen, identity) — exactly hertz's v0.39.4 bug-4 capture (conn1011, 15ms apart, post-refresh replay). The two arms are the same function on the same seed record; the seed is pinned by the dispatcher on the post-cycle replay, which is the field trigger. SIBLING",
    "doc": ""
  },
  "REQ-HAZARD-BRAIN-RESTART-LIFECYCLE-REHYDRATE": {
    "title": "B4 (deepest): a bare brain restart (broker survives) REHYDRATES the live-agent lifecycle so post-restart endpoints are hosted + attachable. Today resume_sessions (brainproc.rs:186, brain.rs:797-809) re-subscribes to the broker's PTY sessions but ALL BrainLifecycle instances (lifecycle.rs:58-130; the ephemeral brain.rs:254-275) are LOST on restart → a post-restart live endpoint gets no livehost → its Psyche is never (re)hosted and new spawns die / can't attach until a FULL daemon reset (operator: perri's brain kill+restart wedged everything until a full daemon kill). FIX: on brain startup, rebuild a BrainLifecycle per resumed live-capable session — load the manifest from the adapter registry → instantiate → start the pulse — the rehydrate the resume no-op cannot do. Composes with B2 (the reconcile re-hosts from the honest on-disk status after rehydrate). (v0.12.0)",
    "doc": ""
  },
  "REQ-HAZARD-DROP-FILE-SINGLE-WRITER": {
    "title": "Drop files are daemon-owned single-writer (6.4)",
    "doc": ""
  },
  "REQ-HAZARD-RC-EOF": {
    "title": "A severed broker stream during a live rc session surfaces GRACEFULLY, never as a raw io error that crashes the PTY. The rc read-loop (rc.rs:352-362) continues only on WouldBlock/TimedOut; ANY other read_event_until error — including UnexpectedEof 'failed to fill whole buffer' — returns Err → RC_FAIL → the PTY 'crashes' from the user's view. Confirmed trigger: a deliberate `spt daemon stop` (broker bounce) severs an active rc (perri stopped the daemon to release owlery watch handles). Same severed-broker-stream EOF class as the v0.9.1 seed fix (seed_fail_message) and the listener-death case — spt-core must classify a broker-gone EOF and (a) surface a CLEAR actionable message ('daemon stopped/restarted — re-run / reconnect'), never the raw buffer error, and ideally (b) AUTO-REATTACH to the same session on the fresh broker (the broker is the daemon-lifetime anchor; it returns on the next `spt api` call). FOLD two side-observations: (1) `spt daemon stop` SILENTLY drops active rc/live sessions — warn ('N active session(s) will drop') or graceful-detach on stop; (2) the daemon holds owlery WATCH HANDLES on perch dirs so a torn-down perch dir stays 'Device busy' until a full daemon stop r",
    "doc": ""
  },
  "REQ-HAZARD-PAIR-SEED-ROTATION": {
    "title": "Removing a node rotates the subnet seed (epoch bump) so an old node/old seed cannot rejoin; trust-store delete alone is NOT revocation because the seed is replicated to every trusted node (ADR-0005 #10)",
    "doc": ""
  },
  "REQ-INSTALL-13": {
    "title": "Adapter add is non-destructive & idempotent-safe (F-018): `spt adapter add --github|--release` REFUSES when the target `_github/<safe>` home already backs an ACTIVE registered record — emitting an actionable code (ADAPTER_ADD_ALREADY_REGISTERED) that routes to `spt adapter update <name>` (refresh in place) or `spt adapter remove <name>` then re-add (replace) — instead of clobbering the live install (the perri footgun: `add --github` over a `--release` pointer git-cloned a source tree over the extracted built binaries → registered pointer dangled → cryptic `os error 2`). And when it DOES (re)populate the home it STAGES-THEN-SWAPS (clone/extract to a sibling staging dir, swap into place only on success) so a failed fetch/clone never strands the previously-extracted manifest+binaries as a dangling pointer (the os-2 / DeferredManifest class). Mirrors the safe stage-then-swap `adapter update` already uses (REQ-UPD-9, apply_release_crc_swap). (v0.14.1)",
    "doc": "Installation: **adapter registration (`spt adapter add`)**: How a node comes to *know* an adapter — harness or shell. An explicit **`spt adapter add <path>`** (or **`--github <user/repo>`**) validates the manifest against the published JSON Schema and writes a registration record under `{SPT_HOME}/…/adapters/` — a **copy** of the files for `file_pull`-update adapters (spt-core owns what it later swaps) or a **pointer** for `delegated`-update adapters (the plugin owns + updates its own files). One command + one dir for both `kind=\"harness\"` and `kind=\"shell\"`; the `kind` field differentiates. T"
  },
  "REQ-HAZARD-HANDOFF-ARGV-COMPAT": {
    "title": "Broker/brain IPC + handoff argv version-tolerant (2.3)",
    "doc": ""
  },
  "REQ-SEAT-LIFETIME-BOUNDED": {
    "title": "REGISTRY-LIFECYCLE W1 (ADR-0040 decision 5, hertz defect B leg 5 CROSS-FAMILY per the final attribution correction — my_stream_subs releases only at conn-loop exit, NO unsubscribe verb existed, ~546/589 broker threads were unnamed parked SubscriberSeat writers on completed Registry/sync/update seats): new KIND_NET_STREAM_UNSUBSCRIBE broker verb removes the SubscriberSeat, stops AND JOINS its writer thread, drops its cursor; every dispatch worker calls it on serve completion (ALL families, success or failure); the retire sweep calls it for retired rows. N-1: unknown-kind on an older broker = tolerated send_error path, caller best-effort. Rider: spawned daemon threads NAMED (SubscriberSeat writer sub-writer-s<id> minimum, conn accept handlers) — field censuses attribute, not infer. Gate: impl — verb + worker/sweep call sites + thread naming; unit — unsubscribe removes seat/joins writer/drops cursor + idempotent + unknown-stream tolerated; int — rides REQ-HAZARD-REGISTRY-STALL seam (physical seat/thread counts plateau CROSS-FAMILY after completion); doc — ADR-0040.",
    "doc": "Decision: <!-- --> 1. **One-way (fire-and-forget) stream families are terminal at FIN, sender-side.** The registry feed pump retires its own row after successful write+FIN via the existing `net-stream-retire` verb (best-effort on N-1 brokers, per ADR-0038 A). A one-way family's exchange is definitionally over at FIN; keeping the row eligible reproduces the O(history) defect forever. 2. **Eligibility filtering is server-side.** `stream_infos` excludes `initiated_locally` rows (alongside `retired`) before serializing. Consumers keep their client-side guards (double-filter harmless; N-1 compatibl"
  },
  "REQ-HAZARD-INBOX-NO-DOUBLE": {
    "title": "No double-delivery via legacy inbox (4.5)",
    "doc": ""
  },
  "REQ-RESIDENT-SERVICE": {
    "title": "ResidentService substrate (ADR-0049, design ratified 2026-07-26): a daemon-supervised binary an adapter declares via a `[service]` manifest section — core-owned from birth, NO perch/identity/address. SPAWN: the daemon launches it job-neutrally (detached_no_inherit + the cold-start ladder posture), so it is never a shell's child (`/T` tree-kill cannot reach it; the shell-descendant hazard class of REQ-SHELL-ADAPTER-OWNED-DETACHED-SERVICE never arises) and never inside a launching terminal's Job Object (the REQ-SHELL-CLI-SPAWN-JOB-EXPOSURE service half closes by construction). START TRIGGER declared in the manifest: start = 'boot' or start = 'bind'; supervised identically once running, with the wake-watcher scaffolding (backoff, give-up latch, one-per-instance lock, orphan-kill, brain-side reconcile) minus the offline-only flip. 'boot' is DESIRED-STATE-RUNNING, not an event: the supervisor reconciles a boot service toward running at EVERY opportunity — daemon boot, ADAPTER REGISTRATION while the daemon is live (operator addition 2026-07-26: installing or registering an adapter whose manifest declares a boot service starts it THEN — spt itself is never restarted to bring a new adapter",
    "doc": "`[service]` — a daemon-supervised resident service (ADR-0049): <!-- --> - **`command`** — an **opaque** command string (program token plus args), like every other command seam. Its program token resolves against the adapter **install dir** before PATH (REQ-INSTALL-11), and args support adapter-static `{adapter_dir}` / `{adapter_name}` substitution only. Must be non-empty: a declared service means spt-core owns and supervises a process. - **`start`** — **required**, no default. `\"boot\"` is **desired-state-running, not an event**: the supervisor reconciles the service toward running at daemon bo"
  },
  "REQ-CLI-2": {
    "title": "spt daemon noun: run|stop|status (hidden daemon verb becomes daemon run; agent-endpoint shutdown keeps its name under endpoint); daemon status renders the pump heartbeat (last-tick recency) so a half-dead daemon is never rendered implied-healthy (M8 decisions 5, 23)",
    "doc": ""
  },
  "REQ-SEC-1": {
    "title": "Per-endpoint access whitelist: origin-node gate, stateful-firewall (reply/outbound exempt), node-now/user-later, outer gate before grants",
    "doc": ""
  },
  "REQ-DOC-ECHO-COMMUNE-CONTRACT": {
    "title": "W6 (LIFECYCLE-TRUTH, docs — this gap cost a full outage night, priority slot): publish the [session.echo_commune] I/O contract on the docs-site: key catalog core fills; core does NOT stdin-feed [history] (field-proven); self-locate guidance incl. CLAUDE_CONFIG_DIR / read_env; drop-file protocol (single-writer, ingest-deletes, resolver semantics from W1); stdout ingestion expectations. Public docs use VERSION numbers, never wave codes; docs-publish drift gate applies.",
    "doc": "Echo-commune — the I/O contract: <!-- the full echo-commune I/O contract: the role + fields, the key catalog spt-core fills, the no-history-on-stdin rule, read-env self-locate, the single-writer/per-endpoint-resolver/ingest-deletes drop-file protocol, and stdout ingestion -->"
  },
  "REQ-ENDPOINT-LIST-RENDER-POLISH": {
    "title": "A6 (F028, operator, 4 asks): `spt endpoint list` render polish. (a) the 'Shared subnets' line is NOT dim — LIGHT_GRAY = \"37\" (cli.rs:3019) is standard-palette WHITE, indistinguishable from row text; use SGR 90 (bright-black/gray) for the dim intent. (b) the `Total:` line takes the same dim color. (c) move the status glyph ADJACENT to the endpoint name (operator: 'right behind the endpoint name'), mirroring the picker's glyph-beside-name presentation (today the glyph sits at the end next to the status word). (d) color the status WORD like the picker TUI (green ONLINE / gray OFFLINE / blue when driven, matching picker glyph semantics). All in render_node_grouped/render_instance_row (cli.rs ~3000s); pure render with an injected color decision — unit-testable off a tty. See triage A6.",
    "doc": ""
  },
  "REQ-HAZARD-EBUSY-RENAME": {
    "title": "tmp-write + atomic-rename + retry on Windows EBUSY (5.2)",
    "doc": ""
  },
  "REQ-LIVE-AGENT-NO-INJECT-DELIVERY": {
    "title": "F-033 RE-SCOPED (doyle re-ruling 2026-07-10 after the #82 gate falsified the original premise — the hosting-mode class split is BINDING context): 'state:live_agent has a self-delivery reader' CONFLATED two hosting modes. (A) HARNESS-hosted live agents (api listen path) DO deliver via adapter channels only — and are ALREADY structurally excluded from inject: bind_from_seed stamps controllable=Some(false), no broker PTY exists. (B) SPT-HOSTED/CONTROLLED live agents' inject leg IS their delivery reader (broker PTY + translation binary — doyle's own endpoint is field proof: ENDPOINT_INJECT + IDLE_PARKED_DRAIN alongside hook-poll); the original blanket state:live_agent exclusion broke REQ-MSG-IDLE-EDGE-DRAIN + the v0.14.3 LAW on both CI platforms and was REVERTED (predicate stays controllable-gated). perri's adapter-channels-only model (F-dupmsg-adapter-confirm.md) holds for class (A) only — do not re-seed the conflation. The F-033 DUPLICATE mechanism (hook-poll + idle-inject both delivering one row, operator spool row 156) is closed structurally by REQ-CARRIER-CLAIM-EXCLUSIVE's atomic cross-carrier take. REMAINING LEGS OF THIS REQ: (a) unit — a harness-hosted live agent (controllable S",
    "doc": ""
  },
  "REQ-SUBNET-8": {
    "title": "Status render honesty: zero-subnet text is daemon-aware ('No subnets registered — this node is standalone.' + daemon-running-dependent blurb, never implying messaging works while the daemon is down); hint footer prints on bare spt subnet only (status drops it); a stalled pump is surfaced in subnet status, never rendered implied-healthy (M8 decisions 11-12, 23)",
    "doc": ""
  },
  "REQ-HAZARD-ENDPOINT-RUN-ATTACH-OUTPUT": {
    "title": "A clean `spt rc` attach to a LIVE spt-hosted (`endpoint run`) harness must DELIVER the harness's PTY output. KEYSTONE — the operator's central 'attach shows no output' symptom, reproduced on the real dummy-harness fixture (v0.12.1 Wave 1) with NO death and NO wedge: bringup succeeds (online, harness pid alive + heartbeating, psyche hosted), the attach CONNECTS (PUMP_IPC_READER spawned, no RC_FAIL, holds the full window) — but receives EXACTLY 0 bytes over 10s of the harness's flushed [session.self] stdout. DISTINCT from REQ-HAZARD-VIEWER-CLOSE-DETACH (death) and REQ-HAZARD-ATTACH-WEDGE (dead-child backpressure): here the harness is ALIVE and the attach is a clean first subscribe. This BLOCKS the 'view is independent' invariant — re-attach is meaningless if a live endpoint-run harness shows nothing. KNOWN-GOOD (rules out 'no drain'): attach.rs `local_attach_via_loopback_conn_rides_the_same_pump` + `broker_spawns_the_pty_child_in_the_requested_cwd` prove the broker DOES drain+fan a `spawn_session` PTY child to a loopback attach over the SAME transport rc uses. Both spawn_session and endpoint-run's spawn_session_pid send KIND_SPAWN → the same dispatch_spawn (broker.rs:706/835) which s",
    "doc": ""
  },
  "REQ-HAZARD-REGISTRY-STALE-CLEAN": {
    "title": "Stale registry entries degrade to fallback, never hard-fail (4.3)",
    "doc": ""
  },
  "REQ-ARCH-2": {
    "title": "Public SDK surface is spt-proto, spt-runtime, spt-msg",
    "doc": ""
  },
  "REQ-RC-HONEST-SESSION-AUTHORITY": {
    "title": "RC-RENDER-TRUTH W1 (ADR-0042 decision 1, hertz perri-contradiction RCA): normal `spt rc` consults the ADR-0041 single honest-session authority BEFORE the persisted-offline fast-fail — run the bounded SessionProbe::has_live_session_honest gate; honest session exists means attach via run_attach_session_confirmed regardless of persisted status; no honest session means the existing offline refusal stands; claimed session with dead client tree means refusal/reap, NEVER attach. Reuse SessionProbe — no new liveness heuristic. Kills the authority split where rc refused ('offline — nothing to attach to') while endpoint run --resume reattached to the same live session. Gate: impl — the pre-fast-fail probe + session-confirmed routing; unit — probe-true routes to session-confirmed attach, probe-false keeps the refusal, dead-tree claim refuses; int — the 3-row regression matrix: offline persisted row + honest live broker session => rc attaches; offline + no session => existing refusal; zombie/dead client tree => refusal, never attach; doc — ADR-0042.",
    "doc": "Decisions: <!-- --> 1. **`spt rc` consults the honest-session authority before the offline fast-fail.** Normal `spt rc <id>` runs the same bounded `SessionProbe::has_live_session_honest` gate `endpoint run` uses (ADR-0041 single liveness authority). An honest session exists → attach via the session-confirmed path regardless of persisted status. No honest session → the existing offline refusal stands. A claimed session with a dead client tree → refusal/reap, never attach. Reuse `SessionProbe`; no new liveness heuristic. <!-- --> 2. **Resume stamps UNBOUND.** A resume launch transitions an exist"
  },
  "REQ-HAZARD-ADAPTER-APPLY-SILENT-NOOP": {
    "title": "A DELEGATED live adapter apply MUST NEVER report success without performing the swap, and the live-update seam MUST use ONE parent-aware adapter matcher across all its comparators. TWO defects made the field repro (BUILD-F015B-APPLYMATCH: `--adapter cc:ccs` live update silently no-ops): (D1, matcher skew) the broker's dispatch_adapter_apply filtered sessions by EXACT `s.adapter == req.adapter`, but a `--adapter <adapter>:<profile>` endpoint stores the COMPOSITE `cc:ccs` while the apply carries the PARENT record name `cc` — so every :profile endpoint fell out to affected=[]; select_endpoints_running_adapter had the same `adp == adapter` skew, while the CLI live-gate (adapter_has_live_endpoint) already parent-matched — divergent rules on ONE seam. (D2, silent success) the affected.is_empty() branch replied KIND_APPLIED and RETURNED WITHOUT SWAPPING; once the CLI delegates the apply there is no CLI-side fallback swap, so success-without-swap = the update never lands (re-register re-reads the OLD manifest, version-of-truth honestly says old). FIX: (1) ONE shared spt_runtime::profile::adapter_parent_matches(session_adapter, parent) used by the live-gate + broker apply-filter + select_en",
    "doc": "7.23 A message that has REACHED a node's spool must NEVER depend on an adapter hook-poll cadence to reach an spt-hosted (relay-less) endpoint — the daemon drives delivery on the events it owns `[REQ-HAZARD-DELIVERY-STARVATION]`: ### 7.24 A delegated live adapter apply must NEVER report success without swapping, and the live-update seam must use ONE parent-aware adapter matcher `[REQ-HAZARD-ADAPTER-APPLY-SILENT-NOOP]` - **Failure (F015B / BUILD-F015B-APPLYMATCH):** a live adapter update to a PROFILE-COMPOSITE endpoint (`--adapter cc:ccs`) silently no-oped — `LIVE` then `DONE` printed, but the v"
  },
  "REQ-HAZARD-EPHEMERAL-CLEANUP": {
    "title": "Ephemeral perch cleanup on every ring exit path (3.1)",
    "doc": ""
  },
  "REQ-ADAPTER-MULTIPLATFORM-SPT": {
    "title": "A `.spt` adapter archive may pack multiple platforms in one signed asset: shared `manifest.toml` + `strings/` at the root, role binaries under per-Rust-target-triple subdirectories (ADR-0016 triple vocabulary, e.g. `x86_64-pc-windows-msvc/`); install/update extracts the shared root plus ONLY `current_platform()`'s triple subdir, flattened into `install_dir` so flat `<install_dir>/<program>` resolution (REQ-INSTALL-11) is unchanged. Name stays `adapter.spt` (plain-tar or gzip, `--asset` optional default); one whole-archive Ed25519 signature over the fat archive (REQ-UPD-9 single-artifact verify). A legacy flat archive (no triple subdirs) extracts as today (free back-compat); a multi-platform archive sets `min_spt_core_version >= 0.13.2` (forward-compat gate, readable before extract); a multi-platform archive missing the recipient's triple -> typed `NoArtifactForPlatform`, never a silent no-op. Large adapters may still split per-platform (single-triple archives via `--asset`, or ADR-0016 update-set machinery). (ADR-0024, v0.13.2)",
    "doc": "Multi-platform adapter `.spt` packaging: <!-- -->"
  },
  "REQ-HAZARD-REGISTRY-GHOST-ROWS": {
    "title": "Registry rows must decay (no immortal rows) via TWO triggers: (a) NODE-SILENCE — evict rows whose author node has not been heard (admitted inbound feed) within the eviction window, so a vanished node's rows stop poisoning bare-id resolution with phantom AcrossNodes ambiguity; AND (b) per-row OFFLINE-TTL — evict rows that have been non-routable (Offline) beyond the per-row grace even while the author node is alive, because purge/erase leaves an immortal Offline row otherwise (ghost-heal re-advertises Offline ONCE with a fresh epoch, and whole-node eviction never fires for a still-alive author) so Offline ghost rows accumulate unbounded on a remote viewer under purge/erase churn (#2-secondary). Both keyed on RECEIVER-observed state (heard-map recency / a sticky receiver-observed offline_since, NOT the gossip epoch — an epoch-keyed clock would be reset by ghost-heal's fresh-epoch re-advertise); own rows never decay; a revived/re-flapped row re-inserts (or clears its offline_since) from its durable epoch within one pump cadence (4.10)",
    "doc": "4.9 SQLite stores must create their parent dir — SQLite won't: ### 4.10 Dead node identities leave immortal registry rows `[REQ-HAZARD-REGISTRY-GHOST-ROWS]` <!-- --> - **Failure:** the registry's only superseding mechanism is the per-`(endpoint_id, node)` epoch lease (4.8) — a row is replaced only by a newer row *from the same node*. When a node identity dies permanently (machine retired, or `node.key` regenerated so the \"node\" never speaks again), its rows are never superseded and never expire: they sit in the in-memory registries and the `identity/registry/<subnet>.json` snapshots forever. A"
  },
  "REQ-DOCS-NO-INTERNAL-CODES": {
    "title": "Public CLI --help (the clap `///` doc-comments) and the generated `docs-site/src/cli/reference.md` MUST NOT contain internal tracker/decision codes — `REQ-*`, `F-###`, `M#-W#`, `ADR-####`. They are meaningless to an end user reading --help and ship to GH-Pages. A CI-gated scan (the `xtask check` docs gate) fails on any such token in the GENERATED reference.md (which by construction contains only clap help, so rustdoc `///` on fns/structs is OUT of scope and keeps its REQ/ADR cross-refs). Substance is kept; codes are rewritten to plain language. (v0.13.2 W6)",
    "doc": "CI commitments: <!-- --> **Public help carries no internal codes.** The public CLI `--help` (the clap `///` doc-comments) and the generated `docs-site/src/cli/reference.md` MUST NOT contain internal tracker/decision codes — `REQ-*`, `F-###`, `M#-W#`, `ADR-####`. They are meaningless to an end user reading `--help` and ship to GitHub Pages. The `xtask check` docs gate scans the **generated** reference for these tokens and fails on any hit (regeneration alone keeps drift at zero but would faithfully republish a leak — the scan is what gives the gate teeth). The scan is scoped to the generated re"
  },
  "REQ-EP-1": {
    "title": "Day-one endpoint types; open type system",
    "doc": ""
  },
  "REQ-PICKER-PURGE-STRUCTURED": {
    "title": "RC-RENDER-TRUTH W3 (ADR-0043 decision 3, hertz stale-glyphs RCA leg 3 P0): core verbs invoked from inside an active TUI return STRUCTURED outcomes and write NOTHING to the terminal — the picker purge path calls a structured-outcome purge core (no stdout/stderr under the live alternate screen) and remains the sole renderer via model.flash (today cmd_endpoint_purge writes diagnostics/PURGED to stderr while ratatui owns the alt screen, mutating the physical screen behind the previous-Buffer diff baseline => later draws skip 'already blank' cells and stderr glyph fragments persist — the x-purge symptom). Baseline-desync regression REQUIRES a stateful/recording backend (pure TestBackend view snapshots cannot catch it). Gate: impl — structured purge outcome + silent-under-TUI routing; unit — purge core emits no terminal bytes in structured mode, picker converts outcomes to flash; int — recording backend: draw ConfirmPurge, inject an external display mutation, transition back => next frame reconstructs the COMPLETE target screen; doc — ADR-0043.",
    "doc": "Decisions: <!-- --> 1. **One FIFO sequencer per attach sink.** The PTY drain/output writer is the sole sequencer for terminal Output and Exit: Exit is enqueued behind all prior output for each sink (drain EOF/completion first, then Exit). A mutex alone is insufficient — producer order is the contract. Output-before-Exit is a production-path invariant, regression-proven end-to-end (broker → attach → rc). <!-- --> 2. **rc display teardown is unconditional, idempotent, and separate from input teardown.** A display RAII guard (distinct from the OS input/raw-mode guard) runs on every exit path incl"
  },
  "REQ-HAZARD-RC-ATTACH-ONLINE-RACE": {
    "title": "`spt endpoint run` in an ATTACH/VIEW terminal action attaches BEFORE the freshly-spawned endpoint is online, so the attach races (or outright loses to) the harness bind. ROOT (doyle /diagnose, code-grounded): cmd_endpoint_run (cli.rs) does launch_harness_brokered_in -> (if start: return) -> run_attach with NO await-online between them. launch_harness_brokered_in returns once the harness PROCESS is spawned, but the broker-PTY bind (info status -> STATUS_ONLINE + the live session) lands ASYNC. Both picker attach paths route here with start=false (RunMode::Attach -> cmd_endpoint_run start=false,view=false): Start-now catches the endpoint mid-bringup -> run_attach attempts + loses the handshake race; Resume-from-history catches it still fully OFFLINE -> run_attach's status-gate (REQ-HAZARD-RC-ATTACH-FAILFAST) short-circuits 'offline - nothing to attach' and NEVER attempts. SAME root, two faces (the W4 attach-by-default surfaced both; an online endpoint is unaffected - the picker returns Outcome::Attach, not Run). FIX: in cmd_endpoint_run, when the terminal action is attach/view (NOT start), AWAIT the endpoint online between launch_harness_brokered_in success and run_attach - poll spt_s",
    "doc": ""
  },
  "REQ-ADAPTER-LIVE-UPDATE": {
    "title": "An adapter update is live and daemon-coordinated (the adapter analog of brain self-update, ADR-0004): for an endpoint with a running RESIDENT adapter binary (today the `[message-idle-translation-binary]`), the CLI keeps fetch+verify and hands the APPLY to the daemon over IPC, which per affected endpoint (1) STOPS the resident binary -> releases the OS file lock (fixes the Windows 'Access denied (os error 5)' overwrite failure), (2) swaps on disk ONLY files whose CRC differs from the staged archive (unchanged files + their still-running binaries untouched), (3) RE-CLONES the new on-disk manifest into the running `BrainLifecycle` (the in-memory manifest is cached at bringup and otherwise goes stale -> binaries+manifest back on the same page), (4) RESTARTS the resident binary from the new files. An endpoint NOT running -> CLI swaps directly (no lock, no cache). Only the resident class is cycled; ephemeral adapter binaries (Psyche loop, `[digest]` extractor, `[session.*]` runners, hooks) self-heal on next spawn and are excluded. The daemon keeps a per-endpoint registry of resident adapter children. (ADR-0025, v0.13.2)",
    "doc": "Live, daemon-coordinated adapter update: <!-- --> // Amendment (W3 build, 2026-06-22): <!-- --> <!-- -->"
  },
  "REQ-HAZARD-CONTROLLER-LEASE": {
    "title": "RC-RENDER-TRUTH W2 (KNOWN-HAZARDS 7.48 — umbrella conformance seam for ADR-0044): at most one input-capable controller lease per PTY session; takeover revokes atomically and loudly; input is fenced to the active lease; node identity is never a lease. The full hertz 8-step deterministic two-loopback-client broker regression rides verbatim: A subscribes Control from node N and controls; B subscribes Take from the SAME node with a different lease; A receives Displaced{by:N} then terminal stream completion (rc exits via existing PumpEnd::Displaced); output post-takeover reaches B not A; A's Input+Resize post-takeover mutate nothing; B's both apply; controlled/driven_by metadata identifies B with exactly one controller slot; a separate equal-lease/equal-generation replay test proves genuine dispatcher recovery remains silent and never self-displaces. Gate: int — the matrix; doc — KNOWN-HAZARDS 7.48.",
    "doc": "7.47 The physical terminal is never mutated or terminated outside its renderer's ordered state model — output before exit, owned baselines, unconditional teardown `[REQ-HAZARD-RENDER-LIFECYCLE]`: ### 7.48 At most one input-capable controller lease per PTY session — takeover revokes atomically and loudly, input is fenced to the active lease, node identity is never a lease `[REQ-HAZARD-CONTROLLER-LEASE]` <!-- --> - **Failure (paid-for, hertz same-machine `--take` RCA, field repro 2026-07-16):** terminal A controlled an endpoint; terminal B on the SAME machine ran `spt rc --take`. Local loopback"
  },
  "REQ-MANIFEST-1": {
    "title": "Per-adapter manifest with adapter_name and min_spt_core_version",
    "doc": "What is NOT in the manifest (spt-core-owned): <!-- The sections below are the authoritative schema; the typed form in `crates/spt-runtime/src/manifest.rs` is kept in lockstep. -->"
  },
  "REQ-RC-RESIZE-GEOMETRY-EPOCH": {
    "title": "A resize transition is ATOMIC from ScreenGrid's perspective: every byte is parsed at the geometry it was EMITTED under. (hertz v0.39.0 field RCA 2026-07-21, doyle re-grounded at source the same hour; P0 — this falsifies a SHIPPED v0.39.0 release claim, so it is a regression, not new scope.) SYMPTOM: right-margin fragments and left-shifted rows on a node where CLI and broker are BOTH 0.39.0 — i.e. exactly the defect the v0.39.0 notes claim fixed. SOURCE (statically provable, no repro box required): broker.rs dispatch_resize calls session.resize(SurfaceSize::new(rows, cols)) (~4557-4559) and only THEN recover_log(&log).set_size_and_notify(rows, cols) (~4560), which reaches self.grid.resize(rows, cols) (~1492) — TWO SEPARATE recover_log acquisitions, so the reader thread's append/parse interleaves between them BY CONSTRUCTION. On ConPTY the resize ITSELF emits a full repaint asynchronously; the reader parses that repaint into the grid at the OLD geometry; the subsequent top-left-preserving grid.resize then faithfully preserves an ALREADY-MISWRAPPED model; the cold attach repaint emits the shift. WHY THE EXISTING TESTS PASS: the spt-term width oracle (REQ-SCREENGRID-WIDTH's int stage) ",
    "doc": "Decision: <!-- the resize barrier + geometry epoch on the dispatch_resize / set_size_and_notify path: hold the grid parse across the ordered surface+grid transition, tag held output with the geometry it was emitted under, replay it at that geometry, and roll back on a failed surface resize. Includes the SHRINK counter-example that rejects the reorder. -->"
  },
  "REQ-NOTIF-DRAIN-ROW-VALIDITY": {
    "title": "A spooled notify envelope is validated against its notif row at DELIVERY time — a copy outliving its row must not deliver. (ADR-0046 Amendment 1 + KNOWN-HAZARDS 7.53; operator field regression from perri's node 2026-07-22, doyle root-caused same day — DAEMON-LIFECYCLE W1 RIDER.) TODAY: quiet delivery (REQ-NOTIF-QUIET-DELIVERY) makes every surface an active_only SPOOL write per endpoint, and every row-lifecycle mechanism (apply-seam dismissal REQ-NOTIF-SEAM-DISMISS, coalesce supersession, TTL, the one-shot migration) touches ROWS only — so a copy spooled while the endpoint was busy/offline is a detached snapshot no dismissal can recall, delivering 'update available' on an already-updated node at the next drain, once per qualifying surface event (perri: twice; doyle's own session: four stale 0.39.x drains post-upgrade). FIX: at the safe-point drain choke point (api poll deferred presentation, spt/src/api/delivery.rs cmd_poll), a notify-kind envelope delivers ONLY if its notif_id resolves to a live UNDISMISSED row in the local notif store; dismissed/superseded/TTL-expired/unknown -> dropped silently; N copies of one notif_id in a drain dedupe to ONE delivery. Non-notify spool content ",
    "doc": "7.53 A durable copy of revocable content is validated against its source of truth at DELIVERY time — a spooled notice outliving its row must not deliver `[REQ-NOTIF-DRAIN-ROW-VALIDITY]`: <!-- --> - **Failure (paid-for, operator field report from perri's node 2026-07-22 — the day after the v0.40.0 notif redesign shipped; doyle root-caused same day, own session corroborating with stale v0.39.x drains):** a node already ON v0.40.0 received the v0.40.0 \"update available\" notice twice. ADR-0046's quiet delivery makes every notif surface an `active_only` SPOOL write per endpoint; every lifecycle mec"
  },
  "REQ-HAZARD-INJECT-SETTLE-REARM": {
    "title": "post-0.29.0 (KNOWN-HAZARDS 7.37): the Layer-1 settle-gate must RE-ARM before every delivery on an OBSERVABLE (echoing/interactive) PTY — a mid-session reader reattach re-creates the head-swallow window. ROOT (field-confirmed on 0.29.0, doyle diagnosis + perri screenshot): the shipped W5-A settle-gate (REQ-INJECT-MULTILINE-INTEGRITY) gated Layer 1 behind a worker-local ONE-SHOT (`settled_once`) on the false premise that the head-swallow race is STARTUP-only (reader not attached after spawn). A mid-session `/clear` re-enters the harness's raw-mode input reader, re-creating the pre-settle window — but the one-shot already fired at spawn, so `settle_before_inject` is SKIPPED and the head is eaten again (a checkpoint-wake payload injected right after `/clear` lost its head, mid-path `spt/Cargo.toml)`); echo-verify (Layer 2) is default-OFF for that session, so it is silent + unrecoverable. FIX (doyle ruling): re-settle before EVERY delivery on an observable PTY; latch-skip the steady-state settle ONLY where the probe is UNOBSERVABLE (non-echoing ConPTY — no reader-reattach race to guard, and each settle burns the full deadline). The settle's own bool return (observed vs timed-out) discri",
    "doc": "7.36 The broker control plane and PTY fan-out must NEVER block on a single subscriber connection — a suspended brain conn must not wedge control `[REQ-HAZARD-BROKER-VIEWER-BRAIN-DECOUPLE]`: ### 7.37 The Layer-1 settle-gate must RE-ARM per delivery on an observable PTY — a mid-session reader reattach (`/clear`) re-creates the head-swallow race `[REQ-HAZARD-INJECT-SETTLE-REARM]` <!-- --> - **Failure (paid-for, field-CONFIRMED on 0.29.0 — doyle diagnosis + perri screenshot `WindowsTerminal_6iSjya8pMt.png`):** the shipped W5-A settle-gate (7-of-W5, `REQ-INJECT-MULTILINE-INTEGRITY`) gated Layer 1 b"
  },
  "REQ-INST-4": {
    "title": "active to dormant/suspended fires a transition echo commune",
    "doc": ""
  },
  "REQ-HAZARD-INFO-JSON-TORN-READ": {
    "title": "State-file reads tolerate concurrent writes (1.2)",
    "doc": ""
  },
  "REQ-UPDATE-FETCH-APPLY-FLAG": {
    "title": "`spt update fetch --apply` is the one-shot get-to-latest: fetch, then INSTALL the staged update REGARDLESS of whether the fetch itself staged anything new — so the brittle `fetch && apply` chain (which broke when fetch no-oped / exited nonzero on an already-staged latest, skipping the chained apply) is unnecessary. Composes with REQ-UPDATE-FETCH-CURRENT-UX: the end state is 'installed latest', reached idempotently from new-staged -> apply / already-staged (applied<candidate) -> STILL apply / already-applied -> noop+exit0 / genuine error (bad signature, no artifact for platform, true downgrade, network) -> do NOT apply, propagate the error + nonzero. Reuses the existing cmd_update_apply core (its own verify + two-phase + auto-rollback own correctness; no duplicated swap/respawn). Additive clap flag (plain doc-comment, no internal codes); reference.md regenerated. (v0.18.0)",
    "doc": ""
  },
  "REQ-SUBNET-DISPLAY-PARITY": {
    "title": "The `spt endpoint run` picker renders endpoint state IDENTICALLY for local and remote (subnet) rows — bound/unbound, controlled (+driver node), and harness-only are all visible across the subnet, not local-only. Today data.rs:293-294 reduces a remote row to plain green-filled/gray-hollow because those facts aren't propagated. (1) GOSSIP additive per-Instance fields: bound/unbound, controlled + driver-node, harness_only (Offline is never gossiped — node-down is remote-inferred). (2) Derive the full EpDisplay for subnet rows from the gossiped fields, same path as local (remove the remote-reduction). (3) PALETTE rework (fill = ACTIONABLE: filled=can act now (rc-control if online, WAKE if suspended-on-live-node) / hollow=cannot (no control seat / node gone)): green-filled=online+bound+free; blue-filled=online+controlled (desc shows `controlled by <node>`); RED-filled=online+UNBOUND (controlled-or-not; controlled-ness shown via available options not glyph) — replaces green-hollow Unbound + absorbs the dropped UnboundControlled; AMBER-HOLLOW=online+harness-only (no broker seat → can't rc) — was amber-FILLED; GRAY-FILLED=Suspended (cold, node up — wakeable) NEW; gray-hollow=Offline (node ",
    "doc": ""
  },
  "REQ-HAZARD-DEFERRED-DRAIN": {
    "title": "Deferred spool rows excluded from the event-stream drain (1.4)",
    "doc": ""
  },
  "REQ-ENDPOINT-CYCLE-HONEST": {
    "title": "REGISTRY-LIFECYCLE W3 (ADR-0041 decision 6, operator deployah stop/run wedge): cycle verbs share ONE liveness authority — the ALREADY_LIVE dup-guard liveness-probes the claimed session client tree before refusing (dead tree means reap + respawn honestly, never a refusal citing a zombie); the shutdown state machine consults the same source so is-it-live has one answer (no ALREADY_LIVE / list-OFFLINE / shutdown-NO_EDGE three-way contradiction on the same endpoint). Gate: impl — probing dup-guard + unified authority; unit — dead-tree claim probes and reaps, live claim still refuses; int — controlled zombie (killed client tree, surviving hosted record) leads to endpoint run succeeding honestly end-to-end; doc — ADR-0041.",
    "doc": "Decision: <!-- --> 1. **Online is earned, not declared.** A creator may stamp `status=online` only from actual persisted state + hosting authority — never from manifest capability alone. Legacy hybrid rows self-heal at reconcile, but only after a SUCCESSFUL broker query: a broker failure is never interpreted as an empty session set (no mass-offline on a hiccup). 2. **Control cleanup splits from offline classification.** Reconcile clears `controlled`/`driven_by`/`viewer_count` for EVERY endpoint absent from session truth — regardless of state or controllability — while offline classification ke"
  },
  "REQ-ENDPOINT-AUTOSTART": {
    "title": "MSG-IDENTITY W5 / F-038 (flynn operator-directed ask 2026-07-10, SPT-CORE-NEEDS #7 + deployah field-confirm same day: mobile-gw alive=false after the v0.30.6 full daemon restart = this feature's absence, live): an endpoint can be marked a STARTUP DEFAULT so the daemon brings it back up at daemon start — Gateway-class endpoints are infra (the phone treats mobile-gw as always-there; box reboot / daemon cold start currently leaves it down until hands-on). SHAPE RULED (doyle, dispatch): (a) `spt endpoint run --save` persists the run (id + adapter/profile + args) as a startup default REPLAYED at daemon start, symmetric with the shipped `subnet attach/detach --save` precedent — smallest orthogonal cut, explicit operator intent, no interaction with effective_rest_state/F-035 reader-parity semantics (shape (c) restore-what-was-up REJECTED for now: principled but couples to the rest_state neighborhood that just churned; revisit if --save proves insufficient in the field). A saved endpoint that fails to come up logs loud + does not block daemon start or other replays. flynn docs sweep confirmed missing-feature not docs-gap (rest/wake manual-only; no endpoint analog of subnet --save; no manif",
    "doc": "Startup defaults (`endpoint run --save`): <!-- --> Infrastructure endpoints (a gateway the phone treats as always-there) should not need hands-on bringup after a box reboot or daemon restart. `spt endpoint run … --save` persists the run — endpoint id, adapter option, and working directory — as a **startup default** in `daemon.json`; the daemon **replays** every saved default when it starts, as a fresh session with the adapter re-resolved at replay time. One entry per endpoint id (a re-save replaces the prior one); remove the entry from `daemon.json`'s `startup_endpoints` to stop auto-starting"
  },
  "REQ-PSYCHE-SID-CUSTODY": {
    "title": "W2 (F030, design §3): the psyche mints and keeps its OWN session id, stored in the nested {id}-psyche perch record — {session_id} in psyche role templates becomes the psyche's sid, never the parent's (today's fill at livehost.rs:518 is the PARENT's — the custody bug). Parent boundary (/clear, /compact) does NOT rotate the psyche sid (the psyche's conversational thread survives parent resets — its job). resume_psyche validates the custody key before spawn (resume.rs:183). Reseed path: psyche session lost/invalid → ResumeMode::FreshWithPreload (download_psyche_context composes role/live/project into {psyche_context}, resume.rs:100) + LOUD PSYCHE_RESEED:{id} marker (custody-loss loop visible; W1 budget bounds it). If the parent sid is still needed by a template it gets its OWN explicit key {parent_session_id} — never aliased. Red-first: parent `api boundary clear` → nested perch sid UNCHANGED (today it is the parent's — guard-revert reproduces).",
    "doc": "The role spt-core actually drives — one bounded turn per Psyche event.: <!-- --> <!-- --> // then it exits — no resident process, no detach.: <!-- --> **Custody sid — `{session_id}` is the Psyche's OWN id.** In a psyche role template `{session_id}` is the **Psyche's own minted session id**, kept in its nested `<parent>-psyche` perch record — **not** the parent's. A parent boundary (`/clear`, `/compact`) rotates the *parent's* sid but does **NOT** rotate the psyche sid: the Psyche's conversational thread survives parent resets (that is its job). When a template still needs the parent's sid it t"
  },
  "REQ-ACTIVITY-LIST-JSON": {
    "title": "`spt endpoint list --json` carries a per-endpoint `activity` (busy|idle) key, for consumers surveying the idle/busy state of many endpoints at once (ADR-0048 decision 1, roster pull avenue; operator addition 2026-07-24). Additive key, N-1-safe; kin the flynn 2026-07-06 last-active/description/adapter list-enrichment seed (same additive posture, may ride together).",
    "doc": "`--json` catalog: | Command | Top-level shape | |---|---| | `endpoint list` | `{ self, subnets[], local[] }` — `self`: `{id, status, ready, alive, unbound, description, psyche_host_error, translation_fault?}`; `subnets[]`: `{name, endpoints[]}` where each endpoint is `{id, node, node_label, status, resources, endpoint_type?, project?}`; `local[]`: `{id, state, address, ready, alive, unbound, project?, activity?}`. *(Since v0.33.0 the local `project` field reads the daemon-maintained project index — answers are immediate and may lag a just-changed project by moments; absent while the index has"
  },
  "REQ-PSYCHE-TURN-STREAM-EVIDENCE": {
    "title": "A failed psyche turn preserves BOTH captured streams as evidence: TurnError::Failed carries the child's stdout alongside stderr, and the failure display appends a bounded single-line stdout TAIL (last ~500 bytes, UTF-8-boundary-safe cut, newlines collapsed, the literal <EMPTY> when the stream said nothing — absence stated, never implied by a missing field). WHY (2026-07-26 spend-limit RCA): a failed turn's stdout is not psyche output, so BOTH core (turn.rs kept {status_code, stderr} only) and the adapter (guarding its outbound channel) independently discarded it — the same blind spot implemented twice — and an account-level outage surfaced as a bare 'claude exited exit code: 1' with the decisive refusal text thrown away at two layers; three agents then chain-hypothesized on an error string the real failing path never emitted. The tail is cause-agnostic instrumentation: it does not care what the failure is, which is why it survives being wrong about it. Adapter twin: claude-spt v0.25.14 dual-stream tail (shipped 2026-07-26).",
    "doc": ""
  },
  "REQ-HAZARD-ENDPOINT-LIFECYCLE": {
    "title": "REGISTRY-LIFECYCLE W2 (KNOWN-HAZARDS 7.45 — the umbrella conformance seam for ADR-0041): endpoint lifecycle state converges to truth from EVERY death path. Regression matrix from the three hertz reports + operator field: dead-PID hybrid row does not survive reconcile; raw viewport close frees the controller (full chain, broker restart included — shared with REQ-STREAM-LEASE-CLASSES int); definitive death means offline+suspended atomically and the next reconcile emits no WAKE_RESUME; explicit Wake still launches exactly once; poll-vs-reap interleave converges to cleared stamps. HEAVY nextest group at birth for any leg spawning a daemon tree. Gate: int — the matrix; doc — KNOWN-HAZARDS 7.45.",
    "doc": "7.44 Streams and seats on a long-lived connection must have bounded lifetime — one-way rows terminal at FIN, seats released at serve completion, no per-chunk full-state rewrites in a drain loop `[REQ-HAZARD-REGISTRY-STALL]`: ### 7.45 Endpoint lifecycle state converges to truth from every death path — no optimistic online without authority, no surviving control stamps, no immortal wake intent, no untruthful create `[REQ-HAZARD-ENDPOINT-LIFECYCLE]` <!-- --> - **Failure (paid-for, three hertz reports + operator field 2026-07-16):** four families, one root shape — lifecycle state written by multip"
  },
  "REQ-ENDPOINT-UNBOUND-ATTACH": {
    "title": "An spt-hosted endpoint is ATTACHABLE between spawn and bind: gate the attach on the broker SESSION being attachable (session+PTY+OutputLog exist at spawn, before bind), not on perch STATUS_ONLINE (bind). cmd_endpoint_run + `spt rc <id>` attach to a live broker session regardless of perch status (headless bringups too; lets an operator clear a bind-gating prompt) -- replaces await_endpoint_online; preserves REQ-HAZARD-RC-ATTACH-ONLINE-RACE's 'no attach before a session' intent at the earlier session-exists point; source = the broker sessions map (ADR-0025 W3a); local-only. New on-disk status STATUS_UNBOUND (spawn->unbound, bind->online, death->offline); lifecycle reuses the existing exit-waiter/reconcile (session death->offline); unbound is attachable but NOT message-addressable (messaging stays online/bound-gated). EpDisplay gains Unbound = HOLLOW (+ hollow-controlled variant) -- amber=HarnessOnly is taken + means not-controllable (the opposite of attachable). (ADR-0027)",
    "doc": "Pieces the Instances model requires: <!-- --> **Unbound endpoint**: The lifecycle point between *spawn* and *bind*: an spt-hosted endpoint whose broker **session + PTY are live** but whose harness has **not yet bound** its perch (the *post-spawn seam* hasn't fired — e.g. the harness is waiting on a startup prompt). On-disk status `unbound` (spawn → `unbound`; bind → `online`; session death → `offline`). An Unbound endpoint is **attachable** (a live PTY — `spt rc` and the `endpoint run` attach reach it, so an operator can see and drive the harness, including clearing a bind-gating prompt) but *"
  },
  "REQ-SEND-STAMP-AGENT-ID": {
    "title": "MSG-IDENTITY W4 / endpoint-identity (operator-flagged 2026-07-09 + live-confirmed on flynn's F-038 ask arriving 'cli@HFENDULEAM'): a live agent's own CLI `spt send <target>` MUST stamp from_id with the AGENT id (e.g. 'doyle'), not the node fallback 'cli@<node>' — today the agent-id stamp rides ONLY the adapter/perch shortform path, so any agent shelling out `spt send` (the DOCUMENTED reach-another-agent form) presents to recipients as an anonymous node CLI: replies mis-route (recipients answer cli@node — no perch — instead of the sender), and the F-036 victim-effect ('sends downgraded to from:cli@node') is indistinguishable from normal CLI traffic. FIX: send-time self-resolve — when the calling process/session maps to a bound live perch on this node (the session-pin/seed machinery already resolves this for bind), stamp that endpoint id as from_id; a genuinely perchless CLI keeps 'cli@<node>'. Gate: unit — a send from a session bound to a live perch stamps the endpoint id; a perchless shell keeps the node stamp; int — recipient's EVENT from= carries the agent id for a shelled-out send from a live session. Kin F-036 victim effects, EVENT envelope (ADR-0020), [[owl-send-not-legacy-spt",
    "doc": ""
  },
  "REQ-ACTIVITY-INFO-PULL": {
    "title": "The endpoint's current activity state (busy|idle) is readable via `spt api endpoint-info` — a point-in-time read of the perch idle sentinel, for consumers that need a check rather than a stream (ADR-0048 decision 1, pull avenue; operator-ruled 2026-07-24). Additive key, N-1-safe per the additive-evolution posture.",
    "doc": "1. Activity observation is a first-class surface with two avenues — and the digest is not one of them: <!-- --> <!-- -->"
  },
  "REQ-HAZARD-WAN-ORIGIN-AUTH": {
    "title": "WAN-inbound origin is transport truth, never payload: the access gate's subject (ADR-0009 origin-node whitelist) is the QUIC handshake-proven remote node id from the broker's conn/stream table — a forged origin/node field inside record bytes is inert (7.5)",
    "doc": "7.4 Per-agent pulse/psyche/echo scheduling must not serialize across agents `[REQ-HAZARD-DAEMON-SCHED-NONBLOCKING]`: ### 7.5 WAN-inbound origin is transport truth, never payload `[REQ-HAZARD-WAN-ORIGIN-AUTH]` <!-- --> - **Failure:** the ADR-0009 access whitelist gates **unsolicited wire inbound by origin node**. If the gate's subject is read from record bytes (an `origin_node`/`from`/`node` field a sender wrote), any sender forges any origin and the whitelist is decoration — same spoof class as 7.3's Psyche-supplied `from=`, now on the cross-node surface. - **Invariant:** the origin the gate ("
  },
  "REQ-HAZARD-ENVELOPE-PARSER-SAFE": {
    "title": "Two-slice envelope parser is panic-free and tolerant (4.2)",
    "doc": ""
  },
  "REQ-HAZARD-ENV-SUBST": {
    "title": "`spt endpoint run` HONORS manifest [env.<VAR>] direction=inject values (with {key} substitution) on the spt-hosted spawn. Today only the [session.self] command ARGV is {id}-substituted; the [env] inject value is NEITHER substituted NOR applied — manifest.schema.json promises EnvVar.value = 'Value to inject (with substitution)' but prepare_harness_spawn fills only argv and SpawnReq carries no env, so a [env.SPT_ENDPOINT_ID].value='{id}' arrives EMPTY. A FLAGLESS harness (bare `claude`, no argv slot for {id}) then routes the id via [env] → empty → SessionStart sees empty $SPT_ENDPOINT_ID → seeds-by-PPID instead of binding → ZERO perch → NO_PERCH (the actual wall-b bind blocker; perri hard-repro'd). SILENT failure (empty inject, no error). FIX (doyle ruled a): fill every [env] inject value from the SAME {key} catalog as argv/role (mirror F-009 TEMPLATE fill, whole-string fill_template for an env value), thread it through SpawnReq.env → the broker sets it on the spawned PTY child. Correctness fix — schema already promises it, NO manifest change, NO new binary. PAIRS with REQ-SEND-SPT-HOSTED to make endpoint run fully work. doyle F-013. (post-v0.10.0)",
    "doc": ""
  },
  "REQ-HAZARD-CONPTY-DSR": {
    "title": "ConPTY reader must auto-answer DSR (ESC[6n) or all child output stalls (5.5)",
    "doc": ""
  },
  "REQ-SPAWN-COLLISION-GUARD-LIVE-DUP": {
    "title": "W4 (LIFECYCLE-TRUTH): single-flight wake per endpoint — the WAKE/RESUME respawn seam must not launch twice for one wake. ROOT (perri parentage + recovered filing): one wake processed TWICE within 1s — broker (306368) spawned two identical `launch --cli ccs --id flynn --resume <sid>` 1s apart, both survived; check-then-spawn TOCTOU in the spawn-side guard. DAMAGE: duplicate-perch writers STOMP info.json (the duplicate's compact re-stamped an OLD sid over a fresh /clear rotation -> injects routed to the contended record and lost). FIX: single-flight wake per endpoint (claim on the perch record or broker-side in-flight set keyed by id; second wake within the window = no-op ack), and the spawn path re-checks liveness UNDER the claim. Int: two concurrent wake requests -> exactly one launch tree.",
    "doc": ""
  },
  "REQ-INST-2": {
    "title": "Per-node files, synced Psyche mind",
    "doc": ""
  },
  "REQ-REDISPATCH-FINISHED-RETIRE": {
    "title": "REDISPATCH-TRUTH W1 (ADR-0038, hertz fix A): finished/terminal stream rows are RETIRED from redispatch eligibility — NetShared.streams today has NO removal path (single insert nethost.rs ~649; StreamLog::finish only marks) so every dispatcher generation re-enumerates every historical stream forever. Retire terminal rows from the enumeration the dispatcher claims from (remove, or lifecycle-exclude), preserving only the post-EOF state genuinely needed by other readers (presence/log reads); bounded growth replaces forever-discoverable rows. Clearing the whole table on brain restart is REJECTED (destroys live streams' reconstruction facts). Gate: impl — the retirement path; unit — a finished stream is invisible to the dispatch enumeration while an active one stays claimable + post-EOF reader state survives retirement; doc — rides ADR-0038 + the triage doc. Kin REQ-HAZARD-REDISPATCH-CONTROL-STEAL (the invariant it satisfies), REQ-STREAM-OPENER-DURABLE.",
    "doc": "Context: ## Decision <!-- -->"
  },
  "REQ-ARCH-1": {
    "title": "Many small acyclically-layered crates",
    "doc": ""
  },
  "REQ-SEND-SPT-HOSTED": {
    "title": "An inbound `spt send` is DELIVERED to an spt-hosted endpoint (brought up via `spt endpoint run` → `api bind`, broker holds its PTY, NO `api listen` relay). Today cmd_bind→establish_perch (api/startup.rs ~441) writes info.json + ready marker + controllable=Some(true) but registers NO message-listener / NO address, so deliver.rs resolve_address→None→spool (deliver.rs:132-140) and the message NEVER reaches the live PTY — the endpoint reads 'online' (ready marker) yet `spt send` silently SPOOLS ('online but not deliverable' lie). Per CONTEXT:187-188 the daemon owns the PTY and delivers, manifest-configurable per activity-state (direct PTY injection / relay / HTTP). FIX: route an inbound send for an spt-hosted target through the daemon → broker InputReq → session.write_input PTY-inject (broker.rs dispatch_input/write_input ~988-1022), the same path the brain uses; the live-delivery handshake must report Sent (not Queued) and stop the spool-only fallback for a broker-hosted, PTY-resident endpoint. Detection is local: controllable==Some(true) + spt-hosted state + resolve_address==None. = the spt-core HALF of the wall-b finding (perri owns the adapter half: bind-hook fired-zero-perch + the",
    "doc": ""
  },
  "REQ-HAZARD-ROSTER-GHOST": {
    "title": "A LOCAL subnet roster entry whose backing perch is erased does NOT keep advertising Active (no phantom perch-less endpoint). `api session-end <id> --erase` removes the perch (owlery dir gone) but the subnet roster (identity/registry/<subnet>.json) keeps the endpoint's instance row ACTIVE with no backing perch; `endpoint stop` says 'address unregistered' yet the line persists; no CLI verb forgets a roster entry, and a hand-edit is re-added by the single-writer daemon advertiser. FIX: daemon-side self-heal — the advertiser DROPS/forgets a LOCAL roster entry whose backing perch no longer exists (stops advertising it Active), and/or a `forget`/evict verb; verify whether the epoch lease eventually evicts it (slow-self-heal) vs a real leak and scope accordingly. doyle secondary finding (perri). (post-v0.10.0)",
    "doc": ""
  },
  "REQ-ENDPOINT-LIST-PROJECT-COL": {
    "title": "#8: spt endpoint list gains a second column <project>/ (the endpoint's LATEST project) -> 4 columns total: id / <project>/ / type / status. Local rows: head of REQ-PICKER-PROJECT-HISTORY-TRUTH (sessions.log-derived, owlery-excluded). Remote rows: head of REQ-GOSSIP-ADAPTER-PROJECTS recent_projects. Project IDs only + #4 disambiguation; '-' when unknown (pre-field remote rows). Extends the v0.21.0 node-grouped renderer (format_instance_rows — additive column, alignment char-width-safe). --json: additive project field on the row DTO (skip-if-none, N-1 safe). Depends on #1 + #4. See docs/NEXT-MILESTONE-PICKER-TRIAGE.md #8.",
    "doc": ""
  },
  "REQ-SEND-WINDOW-DRAIN-HONOR": {
    "title": "F-035 (field finding 2026-07-09): active_only = POLL-ONLY for a relay-bearing live agent -- it must NEVER be RELAY-delivered (its contract is 'active hook window only, never wakes' per spool.rs WINDOW_ACTIVE_ONLY doc + cli.rs:85; `spt send --active-only` / the hidden `--deferred` alias and `send_deferred` shell-context mint it). FIELD SYMPTOM: lia (a full live agent -- relay-for-idle, poll-for-busy) surfaced an --active-only msg on her IDLE RELAY. RCA JOURNEY: v1 RCA (docs/F-035-RCA.md) analyzed the WRONG class (spt-hosted-relay-LESS, the idle-edge inject leg) and proposed a COLLAPSE that would have broken the shipped F-023 anti-starvation gate (docs/F-035-CONFLICT.md); operator reclassified to a relay-bearing live agent; the relay-class re-RCA (docs/F-035-RELAY-RCA.md) traced EVERY active_only->relay carrier and found them ALL ALREADY GUARDED on main@2c05dc9 -- so spt-core has NO code bug. doyle FINAL RULING: the real leak is the ADAPTER's busy->idle poll->idle-representation handoff (spt-claude-code -- a legitimate `api poll` on going idle drains active_only, then the adapter renders it into the idle/relay surface), OUTSIDE spt-core; perri's lane. spt-core DELIVERABLE = a REGRESS",
    "doc": "<!-- F-035 RCA — report-before-fix. --> # F-035 RCA — idle-edge parked-drain ignores the delivery-window tag"
  },
  "REQ-MSG-5": {
    "title": "user-msg envelope kind + daemon identity gate: a Gateway endpoint / the local user's CLI author user-msg (the user's authority); agent-family senders re-stamped to plain msg; identity-gated never payload-trusted (KH 7.3/7.5); wire-additive (N-1 receivers tolerate the new type)",
    "doc": "Endpoint types: <!-- --> A message sent from a Gateway carries **the user's authority** — it *is* the user speaking through a device — and is delivered typed **`user-msg`** (ratified 2026-06-12) so receiving agents weight it as user instruction, not peer-agent chatter. The type is **identity-gated, never payload-trusted** (the KH 7.3/7.5 posture): the daemon permits `user-msg` only from user-backed origins (a Gateway endpoint, the local user's own CLI) and re-stamps an agent-family sender's `user-msg` down to plain `msg` — authority comes from who you are, not what you wrote."
  },
  "REQ-HAZARD-RESTART-IDEMPOTENT": {
    "title": "Idempotent/exactly-once delivery across brain restart at every broker boundary (codex #14)",
    "doc": ""
  },
  "REQ-RESUME-ROW-PER-PROJECT": {
    "title": "A5 (F028, operator #6): resume-from-history labels EVERY session with the endpoint's newest project. data.rs:480-496 resume_rows_for clones project_history.first() onto every ResumeRow (line 481/488), so all sessions read as the head project (the ghost). The per-row e.cwd is already carried for launch-into-dir. FIX: derive per-row project_id_for_dir(e.cwd) (owlery-excluded -> fall back to trigger token), rendered through A1's display-name path. See triage A5.",
    "doc": ""
  },
  "REQ-WAKE-RESUME-LEG": {
    "title": "A-2 (REMOTE-TRUTH triage §A-2 + ADR-0033): the daemon reconcile gains a WAKE-RESUME LEG — an endpoint whose rest INTENT is Active but whose harness session is COLD (status != online) is resumed by the daemon via the adapter's [session.resume] template using the LAST LEDGER session id, so a bare `spt wake <id>` on a suspended live agent actually brings it back (today: reconcile_once start-arm hosts ONLY status==online (livehost.rs:199), so a woken-but-unbound endpoint is skipped forever — neither status reaches online nor does reconcile re-host). This is the ADR-0033 LIFT: the thin `spt wake` edge writes rest intent, the DAEMON does the work. Mirrors shellwake::resolve_wake (read rest state, live-pid double-launch guard, launch, NEVER flip status — the harness self-binds → online). The leg reads the recorded adapter (D-2, REQ-SESSION-ADAPTER-RECORDED); an UNREGISTERED recorded adapter is the Q5 daemon-variant refuse: do NOT spawn, record a LOUD host_error report (F-1 naming the adapter + `spt adapter add`), never silent, never fallback-spawn on a different adapter. BINDS: (1) status=online is set ONLY by a real bind — the resume leg NEVER stamps it (CONTEXT liveness truth; the A-1 e",
    "doc": ""
  },
  "REQ-PROJECT-INDEX-READER-CUTOVER": {
    "title": "PROJECT-INDEX W3 (ADR-0037): endpoint list, the picker, and endpoint-info consume the materialized index — NO git work in any user-facing read path (the O(PxB+C) fanout at cli.rs ~2954 / picker/data.rs ~456-518 dies). BEHAVIORAL PARITY is binding: precedence session-cwd -> origin-cwd -> context-recency and rendered project IDs/display names unchanged (parity suite vs the old derivation on a fixture); bare/partial run shares the indexed projection; fully-qualified --adapter+--id direct run stays picker-free; the direct-run 25s broker-session gate stays separately tested/observable. Degradation legs (git unavailable, branch malformed/locked, cwd deleted) keep fast reads. Manual latency acceptance on the 13-perch/7-branch fixture (~30s -> sub-second) + hertz field-verify on HFENDULEAM — NOT a CI wall-clock gate. Gate: impl — reader cutover; unit — parity + degradation; int — list/picker against a daemon-maintained index incl. counters proving zero reader git spawns; doc — reference regen + CONTEXT avoid-list. Kin REQ-WHOAMI-IDENTITY-ONLY, REQ-PROJECT-INDEX-STORE/WRITER/INVALIDATION.",
    "doc": "Self-update: **project index** — a node's endpoint→project attribution is DERIVED state, held as a **persistent materialized index** (ADR-0037): `spt-store` owns the versioned format + read path (daemon-offline reads = last persisted snapshot); the **daemon is the sole single-flight writer** (load-at-start, ready-without-warm, background batched reconcile, atomic replace, coalesced event-driven invalidation keyed on branch-tip fingerprints, last-known-good on failure). Readers — list, picker, endpoint-info, hooks — join index × perch roster and **never run git**; stale renders last-known or `-"
  },
  "REQ-HAZARD-BROKER-PROCESS-ISOLATION": {
    "title": "Broker and brain are separate processes: the broker runs as its own long-lived per-machine process that survives every brain restart, so a routine (brain-only) self-update restarts the brain onto the swapped binary while every hosted endpoint (PTY child, live QUIC conn, listening socket) stays untouched at the PROCESS level. The in-process-thread broker (daemon.rs:165-170) is a regression that silently unrealizes REQ-UPD-3 — apply degrades to an in-process Brain::handoff no-op and new code does not run until an unrelated restart (KNOWN-HAZARDS 6.7). Evidence must prove process-level survival (SPIKE-01/03 productionized as int: PTY child + live QUIC survive a brain-PROCESS restart onto a swapped binary), re-pointing the regression-masked in-process int tags currently on REQ-DAEMON-2 / REQ-UPD-3 (ADR-0018).",
    "doc": "6.6 Surfaced context conflicts preserve both versions until dominated: ### 6.7 Broker and brain MUST be separate processes (in-process collapse silently breaks no-endpoint-drop update) `[REQ-HAZARD-BROKER-PROCESS-ISOLATION]` - **Failure:** the daemon hosts the broker as a background *thread* in the single `spt daemon` process (`daemon.rs:165-170`, `Arc<Broker>` + `thread::spawn(serve)`) instead of a separate process. A brain restart onto a swapped binary then cannot happen without killing the broker thread — closing every PTY, orphaning every harness child, dropping every socket. So `spt updat"
  },
  "REQ-DRIVEN-BY-OWN-NODE-NORMALIZE": {
    "title": "RC-RENDER-TRUTH v0.38.1 fast-follow leg 1 — RESHAPED by operator/hertz correction + CONTEXT.md:382-389 grounding (doyle ruling v3, 2026-07-19; the original own-node NORMALIZATION is WITHDRAWN): plain `spt rc` on a CONTROLLED endpoint is refused-with-guidance BY DESIGN (CONTEXT:386 — never silent-displace; --take is the opt-in kick), and `driven_by` = the CONTROLLING NODE including the own node (CONTEXT:386 single-writer datum — the stamp comment's remote-only claim was a legacy-path artifact, NOT the model). v0.38.0 shipped behavior is CORRECT: the own-hex latch + client guidance blocking a second same-node plain rc IS the documented refusal, and the W2 generation ladder stays reachable exactly where it belongs — the recovery seams (rc reconnect re-drive + dispatcher re-serve bypass the client gate; the hertz field FAIL leg is reclassified NOT-A-DEFECT). Remaining work = truth/cosmetics across ALL own-node display surfaces (doyle gate finding 2026-07-19 — the own-hex latch is truthful but only rc.rs first learned to HUMANIZE it; the picker pin + endpoint-info attached_node still read the own-hex stamp as a foreign remote driver and print raw hex): (a) rc.rs guidance copy names the ",
    "doc": "Consequences: <!-- --> ### v0.38.1 consequence note — `driven_by` own-node truth (ruling v3)"
  },
  "REQ-HAZARD-SINGLE-PATH-SOURCE": {
    "title": "Single path/registry source of truth; no layout ambiguity (6.1)",
    "doc": ""
  },
  "REQ-DAEMON-6": {
    "title": "Service-aware `daemon start`/`stop`: when an OS service manager has a registered spt-daemon for this user, `spt daemon start` and `spt daemon stop` drive THAT service (so stop doesn't IPC-kill a unit that auto-restart-fights for the broker socket — the kitsubito 2026-06-08 loop). `start` graduates from a `run` alias to a first-class background verb (ensure-up, idempotent, non-blocking); stop routes managed→manager, manual→IPC. Linux=systemd user unit (`systemctl --user start|stop|is-active spt-daemon`, detected by unit-file presence); Windows=no controllable manager (the logon task is boot-only), so start=detached spawn / stop=IPC.",
    "doc": ""
  },
  "REQ-HAZARD-RC-ATTACH-FAILFAST": {
    "title": "B1: `spt rc <id>` to a DEAD or non-streaming session fails fast with a clear message, never an INFINITE blank screen. Today rc.rs run_attach (209-231) + pump spawns PUMP_IPC_READER and blocks: the poll times out each slice but the stream never produces output, so the operator sees a permanent blank (operator: fresh wall-f attached, closed tab, then `spt rc wall-f` HUNG — the broker still resolved a session for it). FIX: (a) once B2 lands, gate attach on is_online/status — an offline endpoint yields a clean 'endpoint offline, start it' not an attach; (b) fail-fast — if the attach-open ack / first output does not arrive within a bound, surface a clear message, never an infinite blank; (c) the broker EOFs the attach stream when the session's child is dead, so rc's existing PumpEnd::BrokerGone graceful path (REQ-HAZARD-RC-EOF) catches it. PIN the exact sub-mechanism with a repro test FIRST (dead-session-lingers-in-broker vs reaped-but-rc-waits vs alive-resting-no-wake — the wall-f Windows tab-close: child alive-silent vs dead-not-reaped). (v0.12.0)",
    "doc": ""
  },
  "REQ-RUN-NO-DUP-SESSION": {
    "title": "B1 (F028, hall-b diagnosis, verified 0.22.0): `endpoint run --id X --create` on an endpoint with a LIVE session mints a silent DUPLICATE session — and attach output can CROSS sessions (second create for diag-hallc minted a new session while the old ran; the new run's attach viewport rendered the OLD session's screen — claude resume-picker UI of pid 84512 while new claude 356020 had no -r). ROOT CLASS of the 0.21.0 attach-stall (zero events in FIRST_EVENT_GRACE rc.rs:1402 = attach bound to dead/wrong same-id slot); also the triplicate `launch --id ball-b` on ENLYZEAM. FIX: (i) run-on-live-session must REFUSE or REATTACH, never silently duplicate; (ii) RCA the attach/output routing that let frames cross same-id sessions (broker session-slot keying, dispatch_adapter vs serve_attach resolution). Int: two sessions one endpoint id -> each attach sees only its own frames. See triage B1.",
    "doc": ""
  },
  "REQ-BROKER-SCREEN-GRID": {
    "title": "Bugs #6 + #12 + #7/#8-artifacts: the broker is a raw-byte pump with no screen model — OutputLog replays the raw ring from seq 0 into a fresh terminal on attach, so an alt-screen TUI (Claude Code) corrupts scrollback (#6) and rc-to-a-pre-running-endpoint garbles (#12 — rc and endpoint run --attach are the SAME client fn, so it is replay content not a client-VT bug). Fix: a server-side VT/grid/screen model (tmux/mosh-style) that maintains authoritative screen + alt/main + cursor and synthesizes a CLEAN current-screen repaint on attach instead of replaying mid-stream ring bytes. Also eliminates residual-cell artifacts on animate/scroll/resize (#7/#8). Operator NON-NEGOTIABLE: accurate PTY representation with zero artifacts. (win32 vterm in the report means this server-side emulator, not ConPTY which is already the backend.) See docs/NEXT-MILESTONE-BUG-TRIAGE.md #6/#12.",
    "doc": "Decision: <!-- the server-side render grid + clean repaint on attach: a clean-room `ScreenGrid` (vte::Perform) interprets the byte stream into an authoritative current screen; `become_controller`/`add_viewer` emit one synthesized repaint instead of the raw ring. Companion design: V0.19.0-P6-SCREEN-GRID-DESIGN.md. -->"
  },
  "REQ-API-3": {
    "title": "commune/signoff are file-drops, not commands",
    "doc": ""
  },
  "REQ-HAZARD-SOFT-CLEANUP": {
    "title": "Soft-cleanup preserves state, removes only the ready marker (6.2)",
    "doc": ""
  },
  "REQ-INSTALL-11": {
    "title": "Adapter command templates resolve their program against the adapter's install dir BEFORE PATH: a `.spt`-shipped binary (dropped to adapters/_github/<safe>/ by --release/--github acquisition, or kept in the source_dir under copy-mode where only manifest+strings/ are copied to adapters/<name>) runs without any PATH placement — a bare-name template token (e.g. `claude-spt-digest ...`) is rewritten to <install_dir>/<program>(.exe on Windows) when that file exists, else left bare for the PATH fallback. Makes a `.spt` self-contained (closes the --release bundled-binary gap perri confirmed) (v0.7.4)",
    "doc": "Manifest seams (outbound contract, detailed): - **Command templates are opaque.** spt-core never parses out a model/tool/flag — the adapter writes the whole command line; spt-core fills substitution keys and runs it. - **A command template's program token resolves against the adapter install dir before PATH (since v0.8.0).** A `.spt` adapter ships its built binaries to the adapter's install dir (`adapters/_github/<safe>/` via `--release`/`--github`, or the record's `source_dir` under copy-mode), so a bare program name (e.g. `claude-spt-digest …`) binds to the shipped binary first and falls bac"
  },
  "REQ-PICKER-3": {
    "title": "A self-owned subnet row reconciles its status to the LIVE roster: a Subnet-category row whose endpoint_id overlaps a local (is_local) roster id is self-owned (this node hosts it), so its status square is OVERRIDDEN with the live roster status — the WAN registry snapshot (wansend::load_snapshots) is a periodically-advertised, independently-stale projection, while the local roster (p.alive) is ground truth for an endpoint this node hosts. One status square per endpoint (CONTEXT.md:348-350 — nothing licenses opposite squares for one endpoint across its Local vs Subnet listings). A reconcile pass in data.rs after the local_rows + subnet_rows gather; BOTH category listings are preserved (Local + Subnet are legitimately distinct views — you are in your own subnet), only the STATUS is unified. (v0.10.0)",
    "doc": ""
  },
  "REQ-HAZARD-DEFERRED-SURVIVE-DRAIN": {
    "title": "Deferred rows survive poll drain (4.4)",
    "doc": ""
  },
  "REQ-PICKER-CONTROL-LINE-STATUS-GATE": {
    "title": "A2 (F028, operator #2): the picker confirm-panel 'controlled locally' line renders for OFFLINE endpoints. view.rs:425-436 builds control_line from ep.controlled with NO status gate; an offline endpoint with a stale controlled stamp shows 'controlled locally' (operator screenshot: hall-a offline + controlled locally). RENDER HALF (this REQ): control_line MUST be empty when status != Online. The upstream STICKY-stamp half (stamp survives client SIGKILL >=5min) is B3/REQ-PRESENCE-CONTROL-REAP-ON-EXIT. FIX: gate the render. See triage A2(a).",
    "doc": ""
  },
  "REQ-JOIN-VERBOSE-CLOCK": {
    "title": "W2/D4 (JOIN-TRUTH): the JOINER side is no longer blind to its own ceremony clock — `spt subnet join --verbose` prints the joiner's derived TOTP step, the applied offset seconds, and the NTP correction state (corrected / uncorrected) per meet sweep; the same triple folds into `meet_failure_detail` so the NO_SEED_HOLDER verbose block carries it. ROOT: diagnosing enlyzeam required shipping a compiled probe over ssh because the member logs PAIR_MEET_UP step=N but the joiner surfaces nothing about its OWN step/offset — the exact asymmetry that hid D1-D3. Extends REQ-JOIN-DIAGNOSTICS's --verbose without a new knob. CLI help changes → xtask docs gen, no internal REQ codes in clap /// (docs-token gate).",
    "doc": ""
  },
  "REQ-ENDPOINT-TEARDOWN-AUTHORITY": {
    "title": "TEARDOWN-AUTHORITY W1 (ADR-0045; two hertz field RCAs 2026-07-19, doyle code-verified + ruled): ONE shared topology-aware broker-teardown primitive behind BOTH `endpoint shutdown` and `endpoint stop`. Today both verbs stamp state they never cause: cmd_shutdown (cli.rs:3979) = remove ready marker + cmd_rest(Suspend) — the Suspend edge (resting.rs daemon_rest_event_with_liveness -> apply_event -> cascade_shells_on_edge -> advertise) fires echo + shell cascade + advertise and NEVER touches a session, even though the verb already probed broker-session truth (cli.rs:3797-3804 has_live_session_honest) to force from=alive; cmd_stop (cli.rs:7071) = marker + unregister_address + terminal_normalize + advertise, whose own comment calls it a DEFINITIVE death observation it fabricates. Field: broker retains the whole subtree (adapter -> node -> harness -> nested resumed harness + MCP children; the surviving `spt api listen` is a DESCENDANT, so a direct-child kill misses it). PRIMITIVE (ordered, ADR-0045 decisions 1/6/7/9): resolve broker SessionInfo -> dedicated sid/endpoint-keyed broker kill claiming NO controller (NOT Brain::attach()+kill_session(), brain.rs:622 require_session() = controller",
    "doc": "7.48 At most one input-capable controller lease per PTY session — takeover revokes atomically and loudly, input is fenced to the active lease, node identity is never a lease `[REQ-HAZARD-CONTROLLER-LEASE]`: ### 7.49 A teardown verb never stamps a terminal or resting state it has not caused — and two individually-correct verbs must not compose into a lifecycle dead end `[REQ-HAZARD-TEARDOWN-DEADEND]` <!-- --> <!-- --> - **Failure (paid-for, two hertz field RCAs 2026-07-19, both doyle code-verified the same day; the second hit doyle's OWN live production endpoint):** `endpoint shutdown` reported"
  },
  "REQ-JOIN-DIAGNOSTICS": {
    "title": "`spt subnet join` never fails SILENTLY (ADR-0030; the field incident showed no output at all). (a) LIVE progress during the meet (replace the one-shot \"Searching…\" cli.rs:6268 with periodic elapsed/deadline) so silence ≠ hang; (b) DETAILED failure on meet-exhaustion — rendezvous candidates + families attempted (IPv4/IPv6) + relay-vs-direct + the last concrete error — surfaced BEFORE any code prompt (a dead subnet must not make the user fetch a code); connect_seed_holder (pairhost.rs:437) and dial_via_rendezvous (meet.rs:281) currently swallow per-attempt errors — thread the last error up with attempt context; (c) PROPAGATE the terminal event — brain.rs:1024 `_ => continue` must deliver a daemon NoSeedHolder/PairFail to the CLI as a printed error (this is WHY the user saw nothing); (d) `--verbose`/`SPT_LOG` discovery TRACE (per-probe derived id, discovery path mDNS/n0-DNS/relay, per-family timeouts), opt-in — no such knob exists today. (next milestone)",
    "doc": "Robust WAN subnet join: meet-before-code + per-family bind gate: <!-- -->"
  },
  "REQ-HAZARD-DEFERRED-MANIFEST": {
    "title": "A pointer-mode (delegated / GhReleaseManaged) adapter whose binary/manifest is not yet extracted is reported with a CLEAR diagnostic, never silently dropped. Today such an adapter reads its manifest LIVE from source_dir (registry.rs manifest_dir ~146/149); a deferred / un-extracted install makes load_manifest fail → registered() (~410, filter_map(.ok())) SILENTLY DROPS the row → downstream ADAPTER_UNRESOLVED + a cryptic os-error-2 on `spt adapter use`. FIX: surface a clear diagnostic at the resolver + at `adapter use` (name the adapter + the deferred/missing-manifest cause + the fix), not a silent filter-drop and not a bare os-error-2; consider an eager manifest copy at register time so host_binaries survive before the binary download completes. doyle Finding A. (post-v0.10.0)",
    "doc": ""
  },
  "REQ-SUBNET-7": {
    "title": "Per-machine re-pair trust overwrite: registry rows carry a hashed stable machine identifier (OS machine id /etc/machine-id|MachineGuid, domain-separated SHA-256 before gossip, spt-minted persisted UUID fallback; additive serde-default field — old rows parse clean); a COMPLETED pairing ceremony presenting the same node label AND machine id as an existing trusted row evicts the superseded identity's trust + registry rows on the seed-holder and replicates the eviction; a gossiped claim alone never evicts trust (M8 decisions 13, 22)",
    "doc": ""
  },
  "REQ-HAZARD-BIND-REST-STATE-CARRY": {
    "title": "GATEWAY-LIVENESS DEFECT A (flynn field bug 2026-07-09, confirmed independent): a re-bind MUST preserve the daemon-owned resting intent (rest_state, D9-2/REQ-INST-3) — the same carry-forward discipline establish_perch already applies to cwd/controllable/adapter/read_env. ROOT: establish_perch's record build (crates/spt/src/api/startup.rs, the build closure) constructs a fresh InfoJson via InfoJson::new (defaults rest_state None) and carries cwd/controllable/read_env forward from prior but NOT rest_state -> a re-bind WIPES the wake intent (flynn tick11 rest_state:active vanish). FIX: carry prior.rest_state (and its paired dormant_since_ms anchor, present iff dormant) forward on re-bind, like the sibling fields. Gate: a re-bind over a prior record with rest_state set preserves it (unit — the build closure carries rest_state + dormant_since_ms). KNOWN-HAZARDS entry on landing. Kin REQ-HAZARD-BIND-CWD-UNSET / REQ-PICKER-1 + REQ-INST-3.",
    "doc": ""
  },
  "REQ-HAZARD-CONTROLLER-IRRECOVERABLE-BEHIND": {
    "title": "DEFERRED EDGE of REQ-HAZARD-CONTROLLER-GAP-RESUME: when a serving controller falls behind the live ring FURTHER than the ring holds (tail - delivered_through > cap_chunks, the ring trim being delivered_through-blind, broker.rs:327-330), the dropped frames have rolled OUT of the ring and an exactly-once re-fetch is IMPOSSIBLE. v0.13.0 DETECTS this (resume floor unchanged across two consecutive resumes) and surfaces a MARKED truncation notice to the operator then ends the attach cleanly — it never silent-skips (a B2 lie) and never spins. FULL graceful handling (a clearly-marked snap-with-data-loss that keeps the operator on the live tail, or a structured truncation record the rc renders distinctly, plus the controller-too-slow + ring-too-small backpressure/sizing policy) is DEFERRED — staging it needs a netsplit / deep-behind harness (the in-process loopback rig keeps up; wedged_viewer's gap is recoverable at ~1492 < 4096). (v0.13.0+)",
    "doc": ""
  },
  "REQ-DOCS-1": {
    "title": "Dual-audience docs (human + AI dev-agent), markdown once / two depths",
    "doc": "How these docs are organized: <!-- the dual-audience contract surfaced to the second audience: agent exports, .md negotiation, schema, CLI-help-as-docs --> ## For AI agents reading this"
  },
  "REQ-INST-7": {
    "title": "Subnet registry + bare-id resolution policy",
    "doc": ""
  },
  "REQ-CRC-SWAP-OLD-DISPLACE": {
    "title": "C1 (F028, infra; memory crc-swap-old-litter-brick, RCA'd ENLYZEAM + confirmed systemic): crc_swap `.old`-litter bricks every subsequent adapter update. apply_crc_swap Phase-3 `let _ = remove_file(.old)` (spt-daemon/src/crc_swap.rs:129-133) silently fails whenever ANY pre-update process still image-maps the old binary (NORMAL on a live box — endpoint launch children survive updates). The NEXT update's first commit-op rename(exe->exe.old) = MoveFileExW(REPLACE_EXISTING) must delete the mapped .old -> win32 err 5 -> whole apply fails + rolls back FOREVER, context-free. FIX: (i) DISPLACE not replace — when <target>.old exists, rename it aside to a unique suffix (rename succeeds on mapped files; spt's own updater already does spt.exe.old-<counter>); GC stale .old.* opportunistically. (ii) Wrap swap io errors with op + path (`rename claude-spt.exe -> claude-spt.exe.old: …`). Unit seam exists (crc_swap tests). See triage C1.",
    "doc": ""
  },
  "REQ-PICKER-KEY-GATE-LAUNCH-CAPABLE": {
    "title": "B-1 (F029, operator): the `h` (headless start) / `s` (shortcut) keybinds fire from broad picker contexts (mod.rs handle_confirm_key / ChooseProject / Resume) regardless of whether the highlighted row would LAUNCH the endpoint. Restrict both to launch-capable highlights: (a) `Start now` in the immediate-start case (should_offer_project_choice == false), (b) a Choose-project row, (c) a Resume-from-history row. The footer hint line must render `h`/`s` ONLY when actually live (hint truth = availability truth). FIX: gate the key handlers on (screen, highlighted-option), unit the gate as a pure matrix. See triage B-1.",
    "doc": ""
  },
  "REQ-HAZARD-LISTEN-ORPHAN": {
    "title": "W4 (LIFECYCLE-TRUTH, KNOWN-HAZARDS): `api listen --parent-pid N` watches parent liveness and exits loud on parent death. ROOT (mobile-gw RCA): --parent-pid is auth-anchor ONLY — no liveness watch; host death orphans the listener forever -> perch held alive (false ONLINE), EVENTs stream to a dead stdout, dead-owner rebind BLOCKED (recorded pid = the live orphan). FIX: listener watches --parent-pid liveness (Windows: job object or poll; Unix: PDEATHSIG or poll) and exits loud on parent death. flynn's job-object guard (spt-mobile side) stays regardless; filed SPT-CORE-NEEDS §5. Unit: parent-death -> listener exits within one poll window.",
    "doc": ""
  },
  "REQ-SESSION-RESUME-TEMPLATE": {
    "title": "Resuming an endpoint session that HAS conversation history brings up a BLANK session. ROOT (doyle, code-grounded + CONTEXT — case-3 spt-core MISSING feature, NOT a perri docs-miss): CONTEXT L127-129 already defines the resume-session seam ('continue-existing: resume an existing harness session under the adapter — its NATIVE resume'), and the manifest already has the resume-variant pattern (Session has BOTH psyche_init AND psyche_resume, manifest.rs:217-219) — but the agent's own session has ONLY self_ (`[session.self]`, no resume sibling). cmd_endpoint_run (cli.rs:1304) re-passes the session_id through `[session.self]` on resume (resume.unwrap_or_else(mint_session_id)), so the adapter's FRESH command (e.g. `claude --session-id ..`) runs again instead of the harness NATIVE resume (`claude -r ..`) -> CC starts a fresh transcript -> blank. spt-core forwards session_id + cwd faithfully; it just has no way to express the native-resume invocation. SECOND GAP: CC resolves a transcript by session_id + cwd, but the session ledger records only {ts, session_id, trigger} (no cwd), so picker Resume-from-history (cross-project rows) can't restore the right cwd. FIX (doyle design, V0.13.0-P2-SESS",
    "doc": "`[session.<role>]` — outbound templates: <!-- --> **Resuming an existing harness session (since v0.13.0).** `[session.self]` is the *fresh* bringup; `[session.resume]` is the **native-resume** sibling. spt-core selects `[session.resume]` over `[session.self]` only when a bringup carries a prior session (`spt endpoint run --resume <session>`, or the picker's *Resume from history*) **and** your manifest declares the role. Declare it with your harness's native-resume verb — if your harness resumes a transcript by id, use that form (Claude Code: `claude -r {session_id} …`), **not** the fresh creat"
  },
  "REQ-MSG-CLI-ORIGIN": {
    "title": "A bare non-perch CLI `spt send` (no owning perch to name as origin) stamps from = `cli@<node-label>` at compose time (bare `cli` when no node label is known — never a dangling `cli@`), and WAN ingress renders an EMPTY from as the origin node DISPLAY (`node_label_display(origin_node, None)` = the QUIC-proven origin node's key-prefix; never blank) — a delivered message NEVER shows a blank sender. Scoped to `spt send`: a from-less send is LEGAL (stamped, never refused), while `spt ring` keeps its NO_SELF refusal (a ring needs a routable self for the reply leg; `cli@<node>` is a display origin, not a perch address). (F-024C item 3, doyle ruled)",
    "doc": ""
  },
  "REQ-MANIFEST-8": {
    "title": "[adapter] host_binaries declares the harness executable basenames a kind=\"harness\" adapter hosts agents inside (e.g. host_binaries = [\"claude\"]); bind-time pid→exe-basename match (case-insensitive, .exe-stripped) over the seed's parent_pid selects the candidate adapter set; zero matches → a friendly error naming the binary + the --adapter escape hatch. Additive + N-1-safe: optional Vec<String>, #[serde(default, skip_serializing_if = \"Vec::is_empty\")] (omitted-serialized like shortcut_basename, old manifests parse clean); manifest.schema.json regenerated from the derive (ADR-0001, CI drift-gated). The match-key for ADR-0021 adapter-agnostic bind-time resolution. (v0.9.0)",
    "doc": "Startup flows (the two topologies): **Bind-time adapter/profile resolution (ADR-0021).** Because the seed is adapter-agnostic, `listen`/`poll` resolve the owning adapter/profile when they bind, as a pure read — never a seed-time snapshot that could drift. `--adapter <name[:profile]>` is an **optional override** on the `api` group (an explicit choice for adapter dev/iteration); omitted, resolution runs: 1. the seed's `parent_pid` → that process's **executable basename** (case-insensitive, `.exe`-stripped); 2. **candidate adapters** = registered `kind=\"harness\"` adapters whose **`host_binaries`*"
  },
  "REQ-PRES-1": {
    "title": "Presence resolution: the presence datum (last_active_node, last_active_endpoint, ts) gossiped subnet-wide via the agent-interaction heartbeat (rides registry distribution, visibility-gated) + one first-class most-recently-active resolution API consumed by notif first-fire, update-consent delivery, consent escalation, and shell wake resolution (M5 scope decision 1: resolution only — the PresenceChannel endpoint stays deferred)",
    "doc": "Deferred Features: | Feature | Cut from | Why deferred | Trigger to revisit | |---|---|---|---| | Scrollback on-disk spillover | terminal wrapper v1 | In-memory ring covers the common case; spillover adds a persistence/rotation story | First long-running session that overflows the ring usefully, or any \"scroll back further than the buffer\" user need | | Sidecar adapter process (long-running, wire-protocol) | harness contract v1 | Manifest + `spt.exe` subcommand surface covers v1 harnesses; sidecar only earns its keep for streaming / in-memory cross-event state | A harness outgrows manifest+hoo"
  },
  "REQ-HAZARD-REGISTRY-STALL": {
    "title": "REGISTRY-LIFECYCLE W1 (KNOWN-HAZARDS 7.44, hertz post-close v0.36 RCA — the umbrella conformance seam): streams and seats on a long-lived connection have BOUNDED lifetime. The hertz regression seam verbatim: real long-lived pump + dispatcher integration, N registry-only rounds over ONE persistent connection, asserting (a) dispatcher-eligible Registry rows plateau O(active) not O(N); (b) physical stream/subscriber/seat counts plateau after completion CROSS-FAMILY (sync/update seats included, not just Registry); (c) snapshot writes O(feeds) not O(chunks x record-kinds); (d) brain refresh produces ZERO historical Registry replay subscriptions; (e) zero CONN_WRITE_POISONED / replay-write-failed events; (f) broker thread count returns to a bounded baseline. HEAVY nextest group at birth (spawns a real daemon tree). Binding: redispatch D1/D1b + REDISPATCH-STALL T1-T7 + mesh-recovery legs green every leg (retire machinery + registry gate = substrate). Gate: int — the seam above; doc — KNOWN-HAZARDS 7.44.",
    "doc": "7.43 One wedged stream subscriber must NEVER stall stream serving — replay halts at the first failed write, a poisoned subscriber is removed, and recovery machinery must not manufacture new replay victims `[REQ-HAZARD-REDISPATCH-STALL]`: ### 7.44 Streams and seats on a long-lived connection must have bounded lifetime — one-way rows terminal at FIN, seats released at serve completion, no per-chunk full-state rewrites in a drain loop `[REQ-HAZARD-REGISTRY-STALL]` <!-- --> - **Failure (paid-for, hertz post-close v0.36 field RCA 2026-07-17 — live prod box):** FOUR compounding consequences of \"phys"
  }
}
