       Cross-platform self-elevating re-launch for privilege-gated commands: a pure decision seam `decide_elevation_path(os, elevation, interactive_tty, has_display, has_pkexec, has_term_emulator) -> ElevatePath{AlreadyElevated, InlineSudo, UacWindow, Pkexec, TerminalEmulator, PrintHint}` selecting how to re-acquire privilege, and the per-OS impure launchers it dispatches — Windows UAC console (ShellExecuteW `runas` on the abs-exe + verbatim argv; the elevated child does the work, prints 'You can close this window', and pauses for a keypress; the original prints 'Elevated terminal launched…' and exits 0; NEVER pipes the child's stdout back across the privilege boundary), Linux desktop pkexec (preferred, native polkit GUI auth) else x-terminal-emulator -e sudo (fallback list x-terminal-emulator→gnome-terminal→konsole→xterm), the existing interactive-TTY inline sudo, and the headless/no-path floor that prints the absolute-path command. Reused by every gated command (not subnet-specific). Generalizes should_auto_elevate.
  [OK] REQ-ENDPOINT-LIST-MERGE-LOCAL  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       `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)
  [OK] REQ-ENDPOINT-PURGE  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
--
       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-INTERNALLY by the operator row's own read pump (RecvHalf::Loopback, retentive_cap==0 → evict-not-park) so a dead rc (a dropped IPC subscriber) never backs peer_w up; bounded_block_on parks the BROKER DISPATCH thread, not a net worker → no worker-pool exhaustion (full mechanism in the required_stages comment). Folds the status=online sub-check: a dead spt-hosted endpoint is marked OFFLINE within one reconcile tick on abrupt child death (broker exit-waiter reaps the session → B2 sees it absent) — PROVEN, no change. (v0.12.1)
  [OK] REQ-HAZARD-BIND-CWD-UNSET  required: [impl, unit, int]  stages: -doc +impl +unit +int
       A bound endpoint's `info.cwd` is SET at bind so a freshly-created perch appears under its own project tab. ROOT (found, v0.13.0): `info.cwd` is NEVER set on bind — `cmd_bind` (spt-hosted) and `bind_from_seed` (harness-hosted) never thread cwd into `establish_perch`/`rec.cwd`. FIX: `cmd_bind` reads its own `current_dir` (the broker spawned it in `project_cwd`); `bind_from_seed` passes `seed.cwd` (already captured at seed time, currently DISCARDED). DISTINCT from REQ-PICKER-HISTORY-FRESH (v0.12.1) — that unioned cwd-origin into picker MEMBERSHIP but tested merge_origin_project with a PROVIDED origin; it never asserted `info.cwd` is actually set on bind, so a real `endpoint run` perch still had an empty cwd and the union had nothing to union. This is the v0.12.1 P1 'appears under its own project right away' claim that was REFUTED in the changelog — delivered for real here. (v0.13.0)
  [OK] REQ-HAZARD-BRAIN-RESPAWN-PATH  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
--
       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.
  [OK] REQ-HAZARD-BROKER-SEED-WIRE-SKEW  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       A daemon-state wire-format change (e.g. the v0.9.0 adapter-agnostic Seed) does NOT take effect until a DELIBERATE full broker restart: the broker serves the seed-control channel and is RESIDENT across a brain-only self-update (ADR-0004 no-terminate-during-update forbids auto-killing it), so a NEW-version CLI talking to a still-resident OLD broker fails the seed handshake — the old broker cannot deserialize the new Seed (its formerly-required `adapter` field is gone) and drops the conn without an ack, which surfaces to the CLI as a raw UnexpectedEof 'failed to fill whole buffer'. spt-core must (a) surface an ACTIONABLE diagnostic on that seed-ack EOF (name the stale-broker cause + the `spt daemon stop` fix — the broker restarts on the next api call), never the cryptic io error; and (b) document the operational rule (a deliberate broker restart is required on any daemon-state wire change — NOT automatic) + the FORWARD discipline (daemon-state/Seed schema changes stay additive + serde-default so a resident OLD broker tolerates a NEW CLI across a brain-only update; note this would NOT have rescued 0.9.0 itself, since the old broker's `adapter` was a required field). perri PREP-4 FINDING 1 (v0.9.0 CLI vs stale 0.8.x broker).
  [OK] REQ-HAZARD-CASCADE-WIPE-GUARD  required: [impl, unit]  stages: -doc +impl +unit -int
       No hard-delete of a parent hosting non-empty children (6.3)
  [OK] REQ-HAZARD-CHILD-CONSOLE-FLASH  required: [impl, unit]  stages: +doc +impl +unit -int
--
  [--] REQ-HAZARD-CONTROLLER-WRITER-REORDER  required: [doc, impl, unit, int]  stages: +doc +impl -unit -int
       Two `controller_writer` threads must never race ONE brain↔broker connection's socket. ROOT (doyle, instrumented RACEDIAG repro on kitsubito): on a brain-restart re-serve the handoff brain registers as controller on the SAME session TWICE over the SAME `Brain::conn` socket — (1) `Brain::handoff` eagerly `subscribe(prior.session_id, prior.next_seq=1)` → `become_controller(from_seq=1)`, initial=[1], spawns writer-A (writes seq 1); (2) `serve_attach` re-handles the replayed `Request{from_seq:0}` → `attach_as(sid,0)` → `become_controller(from_seq=0)`, initial=[0,1], spawns writer-B (writes 0 then 1). `become_controller` (broker.rs) drops the prior `ControllerSink` (its `tx`) but does NOT stop the prior writer thread — writer-A keeps flushing its owned `initial` batch, and both writers hold clones of the same `SharedSend` (`Arc<Mutex<socket>>`) with NO inter-thread ordering. When writer-A's seq 1 wins the socket before writer-B's seq 0, the strict legacy consumer (brain.rs read_event reject-gap path) sees `output gap: got seq 1 want 0` → the test `attach_survives_target_brain_restart_exactly_once` panics at `.expect("re-serve")` OR HANGS in `render_until` (serve thread died on the gap → MARKER_TWO never reaches the wire). `prior.next_seq` is life1's CONSUMPTION cursor, NOT life2's connection state — life2's socket has been sent NOTHING, so a `from_seq=0` full replay on a connection that already streamed seq 1 is contradictory. Snap-above tolerance ALONE can't fix it (it would dedup-drop the late seq 0 → byte loss → the exactly-once byte-identity assert fails). PRE-EXISTING, surfaced by the v0.13.0 green-both-runners release gate; P1b is INNOCENT (its diff touches only input-ack machinery, proven mechanically + the test passes post-P1b in isolation). Sibling flaky cluster: `inject_control_wedge::g2`, `broker::spawn_env_reaches_child`. INVARIANT: on a single brain↔broker connection the controller output-frame stream is monotonic non-decreasing in seq (modulo dedup re-sends); exactly ONE `controller_writer` is ever live per connection; a SUPERSEDED writer writes NO further frames; a re-serve never replays a seq below what the connection already received. FIX (doyle design, V0.13.0-P1C-CONTROLLER-WRITER-REORDER-PLAN.md, three parts): (1) PRIMARY — `Brain::handoff` no longer eagerly `subscribe`s-as-controller; `serve_attach`'s `attach_as(sid,0)` is the SOLE controller registration + replay for that connection (handoff is test-only per the brain.rs comment; no production caller depends on the eager subscribe — production resumes via `cold_start`+`resume_sessions`). (2) BROKER HARDENING — `controller_writer` is epoch-gated: `controller_epoch` becomes a shared `Arc<AtomicU64>`, the writer snapshots its own epoch and checks the shared one before each frame (initial-batch AND live loop), returning the moment it is superseded — so a double-registration can never silently keep a superseded writer flushing (W1-safe: never blocks the drain under `Mutex<OutputLog>`). (3) DEFENSE-IN-DEPTH — `Brain::handoff` seeds `session_cursors` at `prior.next_seq` so the consumer uses the production dedup-below+snap-above path (matching `resume_sessions`), not the strict reject-gap legacy trap. (v0.13.0)
  [OK] REQ-HAZARD-DAEMON-HOSTED-LIVENESS  required: [impl, unit, int]  stages: -doc +impl +unit +int
       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)
  [OK] REQ-HAZARD-DAEMON-SCHED-NONBLOCKING  required: [impl, unit]  stages: -doc +impl +unit -int
--
       B3: `spt daemon stop` then an immediate `spt daemon start` does NOT race — stop fully completes before it returns. Today request_stop (seedmap.rs:240-255) returns on the KIND_STOPPING ack (sent seedmap.rs:174-176) BEFORE the seed socket unbinds, so a following is_running ping (daemon.rs:375) wins the exit window and start reports ALREADY_RUNNING (operator: daemon stop → STOPPED then start → ALREADY_RUNNING). FIX: unbind/stop-gate the seed socket BEFORE acking KIND_STOPPING, OR request_stop waits for a ping-to-fail before returning. Unit: stop then immediate is_running()==false. (v0.12.0)
  [OK] REQ-HAZARD-DAEMON-STOP-REAP  required: [impl, unit]  stages: -doc +impl +unit -int
       Breap: `spt daemon stop` REAPS the spt-hosted children it spawned — no orphaned psyche/harness processes. Today a stop leaves ~8 orphaned claude-spt-psyche.exe + spt.exe: Psyches are spawned DETACHED (runtime.rs:342-356, the Child is dropped — 'Detached' ~349) and the livehost stop flag Arc<AtomicBool> is NEVER raised (brainproc.rs:227-230 holds it 'for symmetry'). FIX: on stop, raise the livehost stop flag AND kill the spawned psyche/spt-hosted children — via a Windows job object / Unix process-group so the children die with the daemon (not detached-immortal). Folds with B3 (both the stop path). (v0.12.0)
  [OK] REQ-HAZARD-DEFERRED-DRAIN  required: [impl, unit]  stages: -doc +impl +unit -int
--
       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)
  [OK] REQ-HAZARD-DEFERRED-SURVIVE-DRAIN  required: [impl, unit]  stages: -doc +impl +unit -int
       Deferred rows survive poll drain (4.4)
  [OK] REQ-HAZARD-DETACHED-PIPE-INHERIT  required: [impl, unit]  stages: +doc +impl +unit -int
--
       `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)
  [OK] REQ-HAZARD-ENVELOPE-CR-LINESAFE  required: [impl, unit]  stages: -doc +impl +unit -int
       Envelope CR-linesafety (4.1): the line-framed EVENT codec must neutralize raw carriage returns — `event_body_escape` folds CRLF/lone-CR to the codec's representable linebreak (`\n`→`<br>`) BEFORE framing, so a body carrying `\r` (Windows `echo`/CRLF text crossing nodes) cannot survive into the single-line envelope and trigger a receiver terminal CR→col0 overwrite that corrupts the frame. Robustness on unrepresentable input, NOT a wire-format change (decoder untouched, amp-last invariant held). Belt-and-suspenders: `spt send`/`ring` also trim stdin (parity with `notify`).
  [OK] REQ-HAZARD-ENVELOPE-DECODE-ORDER  required: [impl, unit]  stages: -doc +impl +unit -int
--
       Ephemeral perch cleanup on every ring exit path (3.1)
  [OK] REQ-HAZARD-EPOCH-RESET  required: []  stages: +doc -impl -unit -int


check your ongoing Monitor tool call's output


Doc+impl satisfied (`+doc +impl -unit -int`). Now the tests. Per project convention I delegate to `spt-test-engineer`. Spawning with full context — running in background to stay reachable:


Agent

Write P1c controller-writer reorder tests

Prompt:
Write the unit + int tests for v0.13.0 P1c: REQ-HAZARD-CONTROLLER-WRITER-REORDER. The implementation is DONE and compiles; you write ONLY tests + tag them. Do NOT change the fix code. Do NOT weaken any assertion.

## Context — the bug + the fix (already landed)
Design doc: `V0.13.0-P1C-CONTROLLER-WRITER-REORDER-PLAN.md` (repo root). Hazard: `docs/KNOWN-HAZARDS.md` §7.21. REQ: `traceable-reqs.toml` id `REQ-HAZARD-CONTROLLER-WRITER-REORDER`, required_stages `["doc","impl","unit","int"]` (doc+impl already satisfied; you must satisfy **unit** + **int**).

ROOT: on a brain-restart re-serve, the handoff brain registered as controller on the SAME session TWICE over the SAME `Brain::conn` socket → two `controller_writer` threads raced one socket → non-monotonic on-wire seq (`got seq 1 want 0`) → `crates/spt-daemon/tests/attach.rs::attach_survives_target_brain_restart_exactly_once` flaky (panic at `.expect("re-serve")` or HANG in `render_until`).

THE FIX (3 parts, all in place — read them):
1. `crates/spt-daemon/src/brain.rs` `Brain::handoff` (~line 330): dropped the eager `brain.subscribe(prior.session_id, prior.next_seq)` call; NOW seeds `session_cursors.insert(prior.session_id, prior.next_seq)` and issues NO eager subscribe. The re-serve's `attach_as(sid,0)` is the sole controller registration.
2. `crates/spt-daemon/src/broker.rs`: `OutputLog.controller_epoch` is now `Arc<AtomicU64>`. `become_controller` (~line 360) does `let epoch = self.controller_epoch.fetch_add(1, AcqRel) + 1;` and passes `epoch` + `Arc::clone(&self.controller_epoch)` into `controller_writer`. `controller_writer` (~line 716) checks `if controller_epoch.load(Acquire) != epoch { return; }` BEFORE each frame on BOTH the initial-batch loop AND the live `rx.recv()` loop — so a SUPERSEDED writer flushes nothing further.
3. (defense) the seeded `session_cursors` routes the consumer through `read_event`'s dedup-below + snap-above path (brain.rs ~582), not the strict reject-gap legacy path (~599).

INVARIANT to prove: on one brain↔broker connection the controller seq stream is monotonic non-decreasing; exactly one live `controller_writer` per connection; a superseded writer writes nothing further.

## What to write

### UNIT (in `crates/spt-daemon/src/broker.rs` `#[cfg(test)] mod tests` — white-box; `become_controller`/`controller_writer`/`OutputLog` are private, in-crate tests can reach them):
- **Epoch-gate keystone**: build an `OutputLog`, append seq 0 and seq 1 to the ring, then call `become_controller` TWICE on the SAME `SharedSend` (a real socket/socketpair you read the other end of) — first `from_seq=1` (writer-A, initial=[1]), then `from_seq=0` (writer-B, initial=[0,1]). Drain the socket and assert the bytes that arrive are **monotonic non-decreasing in seq** and **byte-exact** — i.e. writer-A's superseded `seq 1` must NOT appear before writer-B's `seq 0` (post-fix: writer-A is epoch-gated silent → the wire shows a clean 0,1; never 1,0,1). Find how existing broker unit tests construct a `SharedSend` / read frames (grep for `SharedSend`, `socketpair`, `write_frame`, existing `become_controller`/`add_viewer`/`controller_writer` tests) and mirror that harness. To make the race observable/deterministic pre-fix, force writer-A to actually flush seq 1 before writer-B exists (e.g. let writer-A run/block on the socket, sync, then the second become_controller) — POST-FIX must be DETERMINISTICALLY green regardless of interleaving (the gate guarantees it); that is the real bar. If full pre-fix determinism is impractical, it is acceptable for the test to be deterministic-green post-fix and flaky-red pre-fix — but VERIFY the red by temporarily reverting ONLY the epoch-gate `return` and confirming the test fails, then restore.
- **handoff seeds session_cursors**: a unit (brain.rs `#[cfg(test)]` if there's a brain unit harness, else fold into the broker int) asserting that after `Brain::handoff`, `session_cursor(prior.session_id) == Some(prior.next_seq)` and that handoff did NOT send a subscribe frame (no eager controller registration). Use whatever in-crate brain test scaffolding exists; if handoff needs a live broker, put this assertion in the int file instead.

Tag each `// [unit->REQ-HAZARD-CONTROLLER-WRITER-REORDER]` directly above the test fn.

### INT (the keystone — `crates/spt-daemon/tests/attach.rs` or `tests/broker.rs`, real broker + real brain, NO mocks):
- Deterministically drive the two-`become_controller`-on-one-connection ordering through the PUBLIC path (a brain that subscribes from_seq=1 then attach_as/subscribe from_seq=0 on the same conn, reproducing the pre-fix handoff+re-serve sequence) and assert the operator/wire view is monotonic + byte-exact and the session stays live. Reuse the harness in `attach_survives_target_brain_restart_exactly_once` (study it — it already sets up life1/life2/operator/streams).
- Confirm `attach_survives_target_brain_restart_exactly_once` itself PASSES post-fix (this is doyle's RED-on-revert carrier; he will run it 20× isolated on Linux/kitsubito — you just confirm green locally).
Tag `// [int->REQ-HAZARD-CONTROLLER-WRITER-REORDER]`.

## Seam sweep + gates (run ALL, report exact results)
The W1 controller-writer model is shared — after writing, run the FULL seam sweep and confirm green:
- `cargo test -p spt-daemon --test attach`
- `cargo test -p spt-daemon --test broker`
- `cargo test -p spt-daemon` (lib units incl. your broker unit) and the tests `brain_swap`, `inject_control_wedge`, `netbroker`, `daemon_lifecycle_real_brain` if present.
- `cargo clippy --workspace --all-targets -- -D warnings` (the CI form) = 0.
- `traceable-reqs check` → confirm `REQ-HAZARD-CONTROLLER-WRITER-REORDER` shows `+doc +impl +unit +int` and overall EXIT=0.
- `cargo run -p xtask -- gen` if docs-drift is generated that way (check how docs-drift is gated — there's an xtask gen; run it and confirm no diff).

NOTE: this is a Windows dev box; the restart race is a deterministic RED-carrier on Linux/forkpty, may be benign on Windows at low N (doyle noted the sibling input-flood keystone needed N~2000 to force RED on Windows). So locally on Windows the carrier test may pass trivially — that's expected; the BAR for your new keystone is that it's deterministically GREEN post-fix and you've verified the epoch-gate is load-bearing (revert-to-red check). Make the unit test platform-agnostic (it drives become_controller directly, not forkpty).

Report back: the exact test names + file:line you added, the seam-sweep + clippy + traceable + docs-drift results (paste the tails), and your revert-to-red verification outcome. If you cannot make a clean deterministic post-fix assertion, STOP and report why rather than weakening it.


<owl_reachability_notice>
You have an active owl perch. Launch this Agent with run_in_background: true so you stay reachable for incoming messages while it works.
</owl_reachability_notice>


Async agent launched successfully.
agentId: ae3c1f56da67aea47 (internal ID - do not mention to user. Use SendMessage with to: 'ae3c1f56da67aea47' to continue this agent.)
The agent is working in the background. You will be notified automatically when it completes.
Do not duplicate this agent's work — avoid working with the same files or topics it is using. Work on non-overlapping tasks, or briefly tell the user what you launched and end your response.
output_file: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\ae3c1f56da67aea47.output
Do NOT Read or tail this file via the shell tool — it is the full subagent JSONL transcript and reading it will overflow your context. If the user asks for progress, say the agent is still running; you'll get a completion notification.

Write P1c controller-writer reorder tests

Prompt:
Write the unit + int tests for v0.13.0 P1c: REQ-HAZARD-CONTROLLER-WRITER-REORDER. The implementation is DONE and compiles; you write ONLY tests + tag them. Do NOT change the fix code. Do NOT weaken any assertion.

## Context — the bug + the fix (already landed)
Design doc: `V0.13.0-P1C-CONTROLLER-WRITER-REORDER-PLAN.md` (repo root). Hazard: `docs/KNOWN-HAZARDS.md` §7.21. REQ: `traceable-reqs.toml` id `REQ-HAZARD-CONTROLLER-WRITER-REORDER`, required_stages `["doc","impl","unit","int"]` (doc+impl already satisfied; you must satisfy **unit** + **int**).

ROOT: on a brain-restart re-serve, the handoff brain registered as controller on the SAME session TWICE over the SAME `Brain::conn` socket → two `controller_writer` threads raced one socket → non-monotonic on-wire seq (`got seq 1 want 0`) → `crates/spt-daemon/tests/attach.rs::attach_survives_target_brain_restart_exactly_once` flaky (panic at `.expect("re-serve")` or HANG in `render_until`).

THE FIX (3 parts, all in place — read them):
1. `crates/spt-daemon/src/brain.rs` `Brain::handoff` (~line 330): dropped the eager `brain.subscribe(prior.session_id, prior.next_seq)` call; NOW seeds `session_cursors.insert(prior.session_id, prior.next_seq)` and issues NO eager subscribe. The re-serve's `attach_as(sid,0)` is the sole controller registration.
2. `crates/spt-daemon/src/broker.rs`: `OutputLog.controller_epoch` is now `Arc<AtomicU64>`. `become_controller` (~line 360) does `let epoch = self.controller_epoch.fetch_add(1, AcqRel) + 1;` and passes `epoch` + `Arc::clone(&self.controller_epoch)` into `controller_writer`. `controller_writer` (~line 716) checks `if controller_epoch.load(Acquire) != epoch { return; }` BEFORE each frame on BOTH the initial-batch loop AND the live `rx.recv()` loop — so a SUPERSEDED writer flushes nothing further.
3. (defense) the seeded `session_cursors` routes the consumer through `read_event`'s dedup-below + snap-above path (brain.rs ~582), not the strict reject-gap legacy path (~599).

INVARIANT to prove: on one brain↔broker connection the controller seq stream is monotonic non-decreasing; exactly one live `controller_writer` per connection; a superseded writer writes nothing further.

## What to write

### UNIT (in `crates/spt-daemon/src/broker.rs` `#[cfg(test)] mod tests` — white-box; `become_controller`/`controller_writer`/`OutputLog` are private, in-crate tests can reach them):
- **Epoch-gate keystone**: build an `OutputLog`, append seq 0 and seq 1 to the ring, then call `become_controller` TWICE on the SAME `SharedSend` (a real socket/socketpair you read the other end of) — first `from_seq=1` (writer-A, initial=[1]), then `from_seq=0` (writer-B, initial=[0,1]). Drain the socket and assert the bytes that arrive are **monotonic non-decreasing in seq** and **byte-exact** — i.e. writer-A's superseded `seq 1` must NOT appear before writer-B's `seq 0` (post-fix: writer-A is epoch-gated silent → the wire shows a clean 0,1; never 1,0,1). Find how existing broker unit tests construct a `SharedSend` / read frames (grep for `SharedSend`, `socketpair`, `write_frame`, existing `become_controller`/`add_viewer`/`controller_writer` tests) and mirror that harness. To make the race observable/deterministic pre-fix, force writer-A to actually flush seq 1 before writer-B exists (e.g. let writer-A run/block on the socket, sync, then the second become_controller) — POST-FIX must be DETERMINISTICALLY green regardless of interleaving (the gate guarantees it); that is the real bar. If full pre-fix determinism is impractical, it is acceptable for the test to be deterministic-green post-fix and flaky-red pre-fix — but VERIFY the red by temporarily reverting ONLY the epoch-gate `return` and confirming the test fails, then restore.
- **handoff seeds session_cursors**: a unit (brain.rs `#[cfg(test)]` if there's a brain unit harness, else fold into the broker int) asserting that after `Brain::handoff`, `session_cursor(prior.session_id) == Some(prior.next_seq)` and that handoff did NOT send a subscribe frame (no eager controller registration). Use whatever in-crate brain test scaffolding exists; if handoff needs a live broker, put this assertion in the int file instead.

Tag each `// [unit->REQ-HAZARD-CONTROLLER-WRITER-REORDER]` directly above the test fn.

### INT (the keystone — `crates/spt-daemon/tests/attach.rs` or `tests/broker.rs`, real broker + real brain, NO mocks):
- Deterministically drive the two-`become_controller`-on-one-connection ordering through the PUBLIC path (a brain that subscribes from_seq=1 then attach_as/subscribe from_seq=0 on the same conn, reproducing the pre-fix handoff+re-serve sequence) and assert the operator/wire view is monotonic + byte-exact and the session stays live. Reuse the harness in `attach_survives_target_brain_restart_exactly_once` (study it — it already sets up life1/life2/operator/streams).
- Confirm `attach_survives_target_brain_restart_exactly_once` itself PASSES post-fix (this is doyle's RED-on-revert carrier; he will run it 20× isolated on Linux/kitsubito — you just confirm green locally).
Tag `// [int->REQ-HAZARD-CONTROLLER-WRITER-REORDER]`.

## Seam sweep + gates (run ALL, report exact results)
The W1 controller-writer model is shared — after writing, run the FULL seam sweep and confirm green:
- `cargo test -p spt-daemon --test attach`
- `cargo test -p spt-daemon --test broker`
- `cargo test -p spt-daemon` (lib units incl. your broker unit) and the tests `brain_swap`, `inject_control_wedge`, `netbroker`, `daemon_lifecycle_real_brain` if present.
- `cargo clippy --workspace --all-targets -- -D warnings` (the CI form) = 0.
- `traceable-reqs check` → confirm `REQ-HAZARD-CONTROLLER-WRITER-REORDER` shows `+doc +impl +unit +int` and overall EXIT=0.
- `cargo run -p xtask -- gen` if docs-drift is generated that way (check how docs-drift is gated — there's an xtask gen; run it and confirm no diff).

NOTE: this is a Windows dev box; the restart race is a deterministic RED-carrier on Linux/forkpty, may be benign on Windows at low N (doyle noted the sibling input-flood keystone needed N~2000 to force RED on Windows). So locally on Windows the carrier test may pass trivially — that's expected; the BAR for your new keystone is that it's deterministically GREEN post-fix and you've verified the epoch-gate is load-bearing (revert-to-red check). Make the unit test platform-agnostic (it drives become_controller directly, not forkpty).

Report back: the exact test names + file:line you added, the seam-sweep + clippy + traceable + docs-drift results (paste the tails), and your revert-to-red verification outcome. If you cannot make a clean deterministic post-fix assertion, STOP and report why rather than weakening it.


Test engineer launched in background (writing broker.rs unit + attach.rs int, seam sweep, gates). I won't touch those files while it works.

Brief ack to doyle that P1c is underway:


Bash

echo "OWL=$OWL"; command -v "$OWL" 2>/dev/null || echo "(OWL var empty/unset)"

Check OWL command availability


<owl_messages>
<owl_message from="todlando-w185" priority="info">[WORKING_PERCH_NOTICE] Subagent working perch created.
Perch: todlando-w185
Agent type: spt-test-engineer
To message it: $OWL send todlando-w185 todlando</owl_message>
</owl_messages>

[OWL SYSTEM] Informational owl messages above — no acknowledgment needed. Continue your current task.


OWL=C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe
C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe


"$OWL" send doyle todlando <<'EOF'
P1c PICKED UP + building (@3749845 plan). Fixes landed local, compiles:
1. brain.rs handoff — dropped eager subscribe; seeds session_cursors (the re-serve's attach_as(sid,0) is the sole controller take).
2. broker.rs — controller_epoch -> Arc<AtomicU64>; controller_writer epoch-gated on BOTH loops (superseded writer flushes nothing); become_controller/mark_controller_gone/ControllerJob switched to atomic.
3. defense — seeded session_cursors routes consumer to dedup+snap, not reject-gap.
REQ-HAZARD-CONTROLLER-WRITER-REORDER minted [doc,impl,unit,int]; KH 7.21 + checklist row written. doc+impl tagged (traceable shows +doc +impl).
spt-test-engineer now on the keystone unit (white-box two-become on one socket, epoch-gate proof) + int (attach.rs reorder repro) + full W1 seam sweep + clippy/traceable/docs-drift. Surface green for your gate (incl the 20x kitsubito carrier run). Reachable.
Co-authored by: todlando
EOF

Send P1c status ack to doyle


➜ SENT:doyle


Sent. Status:

**P1c build underway** — last v0.13.0 ship-blocker (controller-writer reorder, pre-existing flaky resume race).

Done (landed local, compiles clean):
- **Fix #1** brain.rs `handoff` — dropped eager subscribe; re-serve's `attach_as(sid,0)` is sole controller take.
- **Fix #2** broker.rs — `controller_epoch` → `Arc<AtomicU64>`; `controller_writer` epoch-gated both loops (superseded writer flushes nothing).
- **Fix #3** seeds `session_cursors` → dedup+snap path, not reject-gap.
- REQ minted `[doc,impl,unit,int]`; KH 7.21 + checklist row. `traceable` shows `+doc +impl`.

In flight:
- `spt-test-engineer` (perch todlando-w185, background) — keystone unit (white-box two-become epoch-gate proof) + int reorder repro + W1 seam sweep + clippy/traceable/docs-drift.

Acked doyle. Waiting on test agent; will surface green for his gate (incl 20× kitsubito carrier). Reachable.


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="7fea8c44" type="msg" from="doyle">Design match — all 3 parts as specced, good. The controller_epoch→Arc&lt;AtomicU64&gt; + epoch-gate on BOTH controller_writer loops is exactly the W1-safe stop (no join-under-lock). REQ + KH 7.21 minted = right.

Gate plan when you surface green:
- I'll run the 20x kitsubito carrier myself (I have ssh reavu</EVENT-PART>
<EVENT-PART seq="2/5" id="7fea8c44">s@kitsubito + the timeout-wrapped harness from the diagnosis) — that's the deterministic RED-on-revert proof, so I want it on my side.
- I'll read the full diff (handoff + the atomic epoch plumbing across become_controller/mark_controller_gone/ControllerJob — that's the load-bearing change, want to verify no epoch-gate gap where a super</EVENT-PART>
<EVENT-PART seq="3/5" id="7fea8c44">seded writer slips one frame before observing the bump).
- Keystone unit: I'll check it's genuinely white-box non-vacuous (forces writer-A's initial write to RACE writer-B and proves epoch-gate suppresses it; flips RED if the gate's removed).
- Seam sweep: confirm every W1 controller-writer/handoff/resume test still green (attach/broker/</EVENT-PART>
<EVENT-PART seq="4/5" id="7fea8c44">resume/brain_swap/inject_control_wedge).

One thing to double-check before surfacing: the epoch-gate ordering. The gate must be read AFTER acquiring send.lock() (or the frame committed atomically with the epoch check) — else writer-A reads epoch=current, then B bumps epoch + writes, then A writes its stale frame = the same reorder. If </EVENT-PART>
<EVENT-PART seq="5/5" id="7fea8c44">the check is before the lock, there's a residual window. Confirm the gate closes it tight (the handoff single-take fix #1 already removes the from_seq=1 writer in the test path, but the broker hardening must hold for the general double-take too).

Surface when green. Reachable.

Co-authored by: doyle</EVENT-PART></event>
</task-notification>


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


doyle caught a real race: the epoch-gate is read **before** `send.lock()`, leaving a window — writer-A passes the gate (pre-bump), blocks on the lock, B bumps+writes, then A writes its stale frame. The check must be **inside** the lock so the epoch-read and `write_frame` are atomic against the other writer (both serialize on the same socket mutex). Tightening now.

Reading the exact current text:


716	fn controller_writer(
717	    sid: u64,
718	    send: SharedSend,
719	    initial: Vec<(u64, Vec<u8>)>,
720	    rx: Receiver<CtrlMsg>,
721	    delivered_through: Arc<AtomicU64>,
722	    epoch: u64,
723	    controller_epoch: Arc<AtomicU64>,
724	) {
725	    for (seq, bytes) in &initial {
726	        // P1c: a SUPERSEDED writer (a newer `become_controller` bumped the shared
727	        // epoch past ours) must write NOTHING further — otherwise its owned
728	        // initial batch races the new writer on the same socket and reorders the
729	        // seq stream. Dropping `tx` only ends the live loop below; this gate is
730	        // what stops the initial-batch flush.
731	        if controller_epoch.load(Ordering::Acquire) != epoch {
732	            return;
733	        }
734	        let frame = output_envelope(sid, *seq, bytes);
735	        let ok = match send.lock() {
736	            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
737	            Err(_) => false,
738	        };
739	        if !ok {
740	            return;
741	        }
742	        advance_delivered(&delivered_through, *seq);
743	    }
744	    while let Ok(msg) = rx.recv() {
745	        // Same gate on the live path: a superseded writer that still has queued
746	        // frames stops here rather than interleaving them with the new writer.
747	        if controller_epoch.load(Ordering::Acquire) != epoch {
748	            return;
749	        }
750	        let (frame, seq) = match &msg {
751	            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
752	            CtrlMsg::Control(frame) => (frame, None),
753	        };
754	        let ok = match send.lock() {
755	            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
756	            Err(_) => false,
757	        };
758	        if !ok {
759	            return;
760	        }
761	        if let Some(seq) = seq {
762	            advance_delivered(&delivered_through, seq);
763	        }
764	    }
765	}
766
767	/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present

fn controller_writer(
    sid: u64,
    send: SharedSend,
    initial: Vec<(u64, Vec<u8>)>,
    rx: Receiver<CtrlMsg>,
    delivered_through: Arc<AtomicU64>,
    epoch: u64,
    controller_epoch: Arc<AtomicU64>,
) {
    for (seq, bytes) in &initial {
        // P1c: a SUPERSEDED writer (a newer `become_controller` bumped the shared
        // epoch past ours) must write NOTHING further — otherwise its owned
        // initial batch races the new writer on the same socket and reorders the
        // seq stream. Dropping `tx` only ends the live loop below; this gate is
        // what stops the initial-batch flush.
        if controller_epoch.load(Ordering::Acquire) != epoch {
            return;
        }
        let frame = output_envelope(sid, *seq, bytes);
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
            Err(_) => false,
        };
        if !ok {
            return;
        }
        advance_delivered(&delivered_through, *seq);
    }
    while let Ok(msg) = rx.recv() {
        // Same gate on the live path: a superseded writer that still has queued
        // frames stops here rather than interleaving them with the new writer.
        if controller_epoch.load(Ordering::Acquire) != epoch {
            return;
        }
        let (frame, seq) = match &msg {
            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
            CtrlMsg::Control(frame) => (frame, None),
        };
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
            Err(_) => false,
        };
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}

/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present


Edit

    for (seq, bytes) in &initial {
        // P1c: a SUPERSEDED writer (a newer `become_controller` bumped the shared
        // epoch past ours) must write NOTHING further — otherwise its owned
        // initial batch races the new writer on the same socket and reorders the
        // seq stream. Dropping `tx` only ends the live loop below; this gate is
        // what stops the initial-batch flush.
        if controller_epoch.load(Ordering::Acquire) != epoch {
            return;
        }
        let frame = output_envelope(sid, *seq, bytes);
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
            Err(_) => false,
        };
        if !ok {
            return;
        }
        advance_delivered(&delivered_through, *seq);
    }
    while let Ok(msg) = rx.recv() {
        // Same gate on the live path: a superseded writer that still has queued
        // frames stops here rather than interleaving them with the new writer.
        if controller_epoch.load(Ordering::Acquire) != epoch {
            return;
        }
        let (frame, seq) = match &msg {
            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
            CtrlMsg::Control(frame) => (frame, None),
        };
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
            Err(_) => false,
        };
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}

    for (seq, bytes) in &initial {
        let frame = output_envelope(sid, *seq, bytes);
        // P1c: a SUPERSEDED writer (a newer `become_controller` bumped the shared
        // epoch past ours) must write NOTHING further — otherwise its owned
        // initial batch races the new writer on the same socket and reorders the
        // seq stream. The gate is read UNDER `send.lock()`, atomically with the
        // write: both writers serialize on this one socket mutex, and
        // `become_controller` bumps the epoch BEFORE spawning the new writer, so
        // whichever superseded writer next holds the lock observes the bump and
        // bails before committing — no check-then-block-then-write window. (A
        // frame the stale writer committed BEFORE the bump is irreducible at the
        // broker; fix #1 removes the contradictory double-take that produced it.)
        let ok = match send.lock() {
            Ok(mut s) => {
                if controller_epoch.load(Ordering::Acquire) != epoch {
                    return;
                }
                write_frame(&mut *s, &frame).is_ok()
            }
            Err(_) => false,
        };
        if !ok {
            return;
        }
        advance_delivered(&delivered_through, *seq);
    }
    while let Ok(msg) = rx.recv() {
        let (frame, seq) = match &msg {
            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
            CtrlMsg::Control(frame) => (frame, None),
        };
        // Same gate on the live path, read under the lock: a superseded writer
        // with queued frames stops here rather than interleaving with the new one.
        let ok = match send.lock() {
            Ok(mut s) => {
                if controller_epoch.load(Ordering::Acquire) != epoch {
                    return;
                }
                write_frame(&mut *s, frame).is_ok()
            }
            Err(_) => false,
        };
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    for (seq, bytes) in &initial {
        // P1c: a SUPERSEDED writer (a newer `become_controller` bumped the shared
        // epoch past ours) must write NOTHING further — otherwise its owned
        // initial batch races the new writer on the same socket and reorders the
        // seq stream. Dropping `tx` only ends the live loop below; this gate is
        // what stops the initial-batch flush.
        if controller_epoch.load(Ordering::Acquire) != epoch {
            return;
        }
        let frame = output_envelope(sid, *seq, bytes);
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
            Err(_) => false,
        };
        if !ok {
            return;
        }
        advance_delivered(&delivered_through, *seq);
    }
    while let Ok(msg) = rx.recv() {
        // Same gate on the live path: a superseded writer that still has queued
        // frames stops here rather than interleaving them with the new writer.
        if controller_epoch.load(Ordering::Acquire) != epoch {
            return;
        }
        let (frame, seq) = match &msg {
            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
            CtrlMsg::Control(frame) => (frame, None),
        };
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
            Err(_) => false,
        };
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}

    for (seq, bytes) in &initial {
        let frame = output_envelope(sid, *seq, bytes);
        // P1c: a SUPERSEDED writer (a newer `become_controller` bumped the shared
        // epoch past ours) must write NOTHING further — otherwise its owned
        // initial batch races the new writer on the same socket and reorders the
        // seq stream. The gate is read UNDER `send.lock()`, atomically with the
        // write: both writers serialize on this one socket mutex, and
        // `become_controller` bumps the epoch BEFORE spawning the new writer, so
        // whichever superseded writer next holds the lock observes the bump and
        // bails before committing — no check-then-block-then-write window. (A
        // frame the stale writer committed BEFORE the bump is irreducible at the
        // broker; fix #1 removes the contradictory double-take that produced it.)
        let ok = match send.lock() {
            Ok(mut s) => {
                if controller_epoch.load(Ordering::Acquire) != epoch {
                    return;
                }
                write_frame(&mut *s, &frame).is_ok()
            }
            Err(_) => false,
        };
        if !ok {
            return;
        }
        advance_delivered(&delivered_through, *seq);
    }
    while let Ok(msg) = rx.recv() {
        let (frame, seq) = match &msg {
            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
            CtrlMsg::Control(frame) => (frame, None),
        };
        // Same gate on the live path, read under the lock: a superseded writer
        // with queued frames stops here rather than interleaving with the new one.
        let ok = match send.lock() {
            Ok(mut s) => {
                if controller_epoch.load(Ordering::Acquire) != epoch {
                    return;
                }
                write_frame(&mut *s, frame).is_ok()
            }
            Err(_) => false,
        };
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}

+        let frame = output_envelope(sid, *seq, bytes);

-        // seq stream. Dropping `tx` only ends the live loop below; this gate is

-        // what stops the initial-batch flush.

-        if controller_epoch.load(Ordering::Acquire) != epoch {

-        let frame = output_envelope(sid, *seq, bytes);

+        // seq stream. The gate is read UNDER `send.lock()`, atomically with the

+        // write: both writers serialize on this one socket mutex, and

+        // `become_controller` bumps the epoch BEFORE spawning the new writer, so

+        // whichever superseded writer next holds the lock observes the bump and

+        // bails before committing — no check-then-block-then-write window. (A

+        // frame the stale writer committed BEFORE the bump is irreducible at the

+        // broker; fix #1 removes the contradictory double-take that produced it.)

-            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),

+                if controller_epoch.load(Ordering::Acquire) != epoch {

+                write_frame(&mut *s, &frame).is_ok()

-        // Same gate on the live path: a superseded writer that still has queued

-        // frames stops here rather than interleaving them with the new writer.

+        // Same gate on the live path, read under the lock: a superseded writer

+        // with queued frames stops here rather than interleaving with the new one.

-            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),

+                write_frame(&mut *s, frame).is_ok()


Compile-check the tightened gate:


Bash

cargo check -p spt-daemon 2>&1 | tail -8

Compile-check the tightened epoch gate


    Checking spt-daemon v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.61s


Compiles. Now notify the test subagent that I tightened `controller_writer` (gate now read under the lock) so its keystone asserts the final code and re-runs gates against it.


ToolSearch

Question: select:SendMessage


No matching deferred tools found

Question: select:SendMessage


Question: send message to running agent subagent continue


Question: send message to running agent subagent continue


No SendMessage in this harness — I'll reach the subagent through its working perch (the owl PreToolUse hook injects messages into its next tool call). Concise heads-up so its keystone targets the final code:


Bash

"$OWL" send todlando-w185 todlando <<'EOF'
HEADS-UP from todlando (parent): I just TIGHTENED controller_writer in crates/spt-daemon/src/broker.rs — the epoch gate is now read UNDER send.lock() (atomically with write_frame), on BOTH the initial-batch loop and the live rx.recv() loop, closing a check-then-block-then-write window doyle flagged. RE-READ broker.rs before any edit. Your edits should be CONFINED to the #[cfg(test)] mod tests block (end of file) + tests/*.rs — do NOT touch controller_writer/become_controller. The tightened gate makes post-fix MORE deterministic (a superseded writer that acquires the lock after the epoch bump bails before committing). Your keystone assertion (wire monotonic, superseded writer silent) is unchanged; revert-to-red still works by neutering the `if controller_epoch.load(Acquire) != epoch { return; }` inside the lock. Re-run all gates against this final code. Compiles green here.
EOF

Notify test subagent of the tightened gate


➜ QUEUED:todlando-w185


Now confirm to doyle that the window is closed:


"$OWL" send doyle todlando <<'EOF'
Good catch on the epoch-gate ordering — you're right, there was a residual window. FIXED before surfacing.

The gate is now read UNDER send.lock(), atomically with write_frame, on BOTH controller_writer loops (initial-batch + live rx.recv()). Reasoning: both writers serialize on the one socket Mutex; become_controller bumps the epoch (fetch_add) BEFORE spawning the new writer, so whichever superseded writer next ACQUIRES the lock observes the bump and returns before committing — no check-then-block-then-write gap. Was:
  if epoch_stale { return } ; lock(); write   // window between check and lock
Now:
  lock(); if epoch_stale { return } ; write    // check+write atomic under the mutex

Irreducible residual: a frame the stale writer COMMITTED before the bump can't be unwritten at the broker — that's exactly the contradictory from_seq=1-then-0 double-take, which fix #1 (handoff single-take) removes at the source. So: fix #1 eliminates the test-path double-take; fix #2 (this tightened gate) holds the single-live-writer invariant for any general double-take where the superseded writer hasn't yet committed. Compiles green.

Confirm the irreducible-residual framing matches your model. spt-test-engineer is on the keystone (will race writer-A's initial write vs writer-B + prove the gate suppresses it, revert-to-red). Surface when the full sweep is green.
Co-authored by: todlando
EOF

Confirm gate window closed to doyle


Sent. Status update:

**doyle's race caught + fixed.** He flagged a check-then-block-then-write window in the epoch gate. Tightened: gate now read **under `send.lock()`**, atomic with `write_frame`, both loops. A superseded writer that acquires the lock after the bump bails before committing. Irreducible residual (a frame committed pre-bump) is exactly the contradictory double-take that fix #1 removes at source.

- Compiles green.
- Notified test subagent to target the final code; keystone unchanged.
- Confirmed the reasoning + irreducible-residual framing back to doyle.

Waiting on `spt-test-engineer` (keystone unit + int + W1 seam sweep + clippy/traceable/docs-drift). Will surface green for doyle's gate (he runs the 20× kitsubito carrier). Reachable.


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="f09d237c" type="msg" from="doyle">Confirmed — irreducible-residual framing is correct and matches my model. The check-under-lock + bump-before-spawn ordering is right: a superseded writer can only commit a frame it had ALREADY written before the bump; everything after is suppressed.

And the key precision that makes it airtight: a residual reord</EVENT-PART>
<EVENT-PART seq="2/6" id="f09d237c">er requires the superseded writer to commit a HIGHER seq than the new writer's first frame — which ONLY happens on a DECREASING-from_seq re-take (writer-A replays from_seq=1, writer-B from_seq=0). For any normal/monotonic re-take (second from_seq &gt;= first), the superseded writer's committed frames are &lt;= the new floor, so the consumer </EVENT-PART>
<EVENT-PART seq="3/6" id="f09d237c">dedup-snaps them — no reorder. Fix #1 removes the one decreasing-floor case (handoff's from_seq=1-then-0); fix #2 bounds every other double-take to &quot;already-committed only.&quot; Sound.

ONE hardening to make the invariant self-enforcing (so a future caller can't silently reintroduce a decreasing-floor re-take): in become_controll</EVENT-PART>
<EVENT-PART seq="4/6" id="f09d237c">er, guard/assert that a controller re-take's from_seq does NOT go below the connection's already-delivered floor (delivered_through) — clamp-up or a loud debug_assert + log. That turns &quot;we removed the one bad caller&quot; into &quot;the broker structurally refuses the bad shape.&quot; Cheap, and it's exactly the kind of un-reintroducibl</EVENT-PART>
<EVENT-PART seq="5/6" id="f09d237c">e guard a REQ-HAZARD wants (these are bugs we pay for once). Your call whether it's in-scope for P1c or a noted follow-up — if it complicates the W1-safe path, note it and ship the core fix.

Keystone sounds right (race writer-A's initial vs writer-B, prove gate suppresses, revert-to-red). Surface when the sweep's green — I'll run th</EVENT-PART>
<EVENT-PART seq="6/6" id="f09d237c">e 20x kitsubito carrier + read the atomic-epoch diff on my side.

Co-authored by: doyle</EVENT-PART></event>
</task-notification>


doyle confirms the framing and proposes a self-enforcing guard: in `become_controller`, refuse a re-take whose `from_seq` < the connection's already-delivered floor. But there's a real problem with the literal form — let me reason it out before touching code.

`delivered_through` is **session-wide** (the `Arc<AtomicU64>` on `OutputLog`, shared by all controllers/viewers of that session), not per-connection. A **fresh operator attach** to an already-producing session legitimately does `become_controller(from_seq=0)` while `delivered_through` is high — it needs the full scrollback replay and the consumer dedups. So `from_seq >= delivered_through` is **not** an invariant; a guard/clamp on it would starve every normal fresh-attach/resume.

Confirming the scope before I respond:


cd C:/Users/decid/Documents/projects/spt-core && rg -n "delivered_through" crates/spt-daemon/src/broker.rs | head -20

Confirm delivered_through scope/usage


172:/// advances `delivered_through` on each successful write, and it is never evicted
250:    delivered_through: Arc<AtomicU64>,
282:            delivered_through: Arc::new(AtomicU64::new(0)),
349:    /// `delivered_through` cursor as it writes (D4-1: only on a successful write,
375:        let dt = Arc::clone(&self.delivered_through);
538:    /// owned by `sub`. Leaves `delivered_through` intact (D4-1). Re-stamps
604:    fn delivered_through(&self) -> u64 {
605:        self.delivered_through.load(Ordering::Acquire)
636:/// chunk and advances the authoritative `delivered_through` on a successful
688:/// Advance the shared `delivered_through` cursor to `seq + 1`, monotonically
713:/// `delivered_through`; `Control` (Displaced/…) does not.
721:    delivered_through: Arc<AtomicU64>,
749:        advance_delivered(&delivered_through, *seq);
771:            advance_delivered(&delivered_through, seq);
1339:                                    resume_seq: log.delivered_through(),


Confirmed. `delivered_through` is session-wide, monotonic-max (line 688), and `resume_seq` is read straight from it (1339) — a fresh/resume attach with `from_seq=0` against a high cursor is the *normal* full-replay-then-dedup path. The literal guard would misfire on every one. Responding to doyle:


"$OWL" send doyle todlando <<'EOF'
On the self-enforcing guard — I want it too, but the literal form (refuse/clamp a re-take where from_seq < delivered_through) is UNSAFE as stated, because delivered_through is SESSION-WIDE, not per-connection:

- It's the Arc<AtomicU64> on OutputLog, shared by every controller+viewer of the session, advanced monotonic-MAX by whichever writer is live (broker.rs:688,749,771). resume_seq is read straight off it (1339).
- A NORMAL fresh-operator attach to an already-producing session does become_controller(from_seq=0) while delivered_through is high — it WANTS the full ring replay and the consumer dedups (dedup-below+snap). That's the at-least-once resume boundary, not a bug.
- So `from_seq >= delivered_through` is NOT an invariant. A clamp-up would start initial=[frames>=delivered_through] → a fresh operator misses scrollback 0..N → broken attach. A debug_assert fires on every normal from_seq=0 attach.

The decreasing-floor HAZARD is specifically two writers on ONE connection where the prior advanced the cursor NON-CONTIGUOUSLY (seq1 w/o seq0). delivered_through can't even distinguish that — monotonic-max makes seq1-without-seq0 read as "2", falsely implying 0 was delivered. A structurally un-reintroducible guard needs a PER-CONNECTION contiguous-sent cursor, which doesn't exist today (the cursor is per-session) — that's a bigger change than P1c.

Proposal: SHIP the core P1c fix (handoff single-take + epoch-gate-under-lock + session_cursors seed — the decreasing-floor case is already removed at the one caller + bounded for any other to already-committed-only), and NOTE the structural per-connection-floor guard as a follow-up (DEFERRED.md + a REQ-HAZARD seed) rather than ship a guard that false-fires on normal attach. Per your "if it complicates the W1-safe path, note it and ship."

Concur? Or do you have a per-connection signal I'm not seeing that makes the guard safe?
Co-authored by: todlando
EOF

Respond to doyle re: guard unsafe as stated, propose follow-up


➜ SENT:doyle


Sent to doyle. Summary of where we are:

**doyle confirmed the irreducible-residual model is sound.** He proposed one extra hardening — a `become_controller` guard refusing a re-take below the connection's delivered floor.

**I pushed back (with evidence):** that guard is unsafe as literally stated — `delivered_through` is *session-wide* and monotonic-max, so a normal fresh/resume attach (`from_seq=0`, high cursor) routinely sits below it and needs the full replay. A clamp/assert there would starve normal attach. A truly structural guard needs a *per-connection contiguous-sent cursor* that doesn't exist today — bigger than P1c.

**Proposed:** ship the core fix (single-take + epoch-gate-under-lock + cursor seed already remove/bound the hazard), note the per-connection-floor guard as a DEFERRED follow-up + REQ-HAZARD seed. Asked doyle to concur or point to a per-connection signal I'm missing.

Waiting on: doyle's reply + `spt-test-engineer` (keystone + seam sweep + gates). Reachable.


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="8f6e300c" type="msg" from="doyle">You're right, I was wrong — concede fully. My guard conflated session-wide delivered_through (monotonic-MAX, shared, resume_seq reads it) with a per-connection contiguous-sent cursor. Two ways it breaks, both yours:
1. A fresh-operator from_seq=0 attach to a producing session is the NORMAL at-least-once resume (the</EVENT-PART>
<EVENT-PART seq="2/5" id="8f6e300c"> dedup-below+snap-above path exists for exactly this) — `from_seq &gt;= delivered_through` would reject it / clamp away scrollback 0..N + the debug_assert false-fires on every normal attach.
2. delivered_through is monotonic-MAX so it literally CAN'T distinguish the hazard (seq1-without-seq0 reads as &quot;2&quot;, falsely implying 0 was </EVENT-PART>
<EVENT-PART seq="3/5" id="8f6e300c">sent) — the wrong signal for the guard.

So the structurally-un-reintroducible guard needs a per-connection contiguous-sent cursor that doesn't exist today = a bigger change than P1c. Agreed.

CONCUR with your plan: SHIP the core P1c (handoff single-take + epoch-gate-under-lock + session_cursors seed) — the actual bug is fixed </EVENT-PART>
<EVENT-PART seq="4/5" id="8f6e300c">at the one caller + bounded to already-committed-only for any other, and the consumer snap-aboves. DEFER the per-connection-contiguous-floor structural guard: DEFERRED.md row + a REQ-HAZARD seed (required_stages=[], activate-don't-pre-fail) naming the precise shape (a future double-take with a non-contiguous prior advance on one connection cou</EVENT-PART>
<EVENT-PART seq="5/5" id="8f6e300c">ld commit one pre-bump frame → reorder; needs a per-connection contiguous cursor to refuse structurally). That's the honest, no-false-fire scope.

Good gate on my suggestion — that's the dialogue working. Finish the keystone + sweep, surface green. I'll run the 20x kitsubito carrier + diff read.

Co-authored by: doyle</EVENT-PART></event>
</task-notification>


doyle concurs fully. Now I'll add the DEFERRED.md row + the REQ-HAZARD seed (`required_stages=[]`, activate-don't-pre-fail) — my files, non-overlapping with the subagent's tests. Reading DEFERRED.md format:


cd C:/Users/decid/Documents/projects/spt-core && (ls DEFERRED.md 2>/dev/null && echo "---TAIL---" && tail -40 DEFERRED.md) || (echo "no DEFERRED.md at root; searching:" && rg --files -g "DEFERRED.md")

Locate and read DEFERRED.md tail


no DEFERRED.md at root; searching:
docs\DEFERRED.md