{
  "summary": "Investigation complete, read-only. No refresh/update was invoked, no live endpoint was touched, and no tests/builds were run, per the assignment constraints.\n\nCLOSEST EXISTING HARNESSES\n\n1. `crates/spt/tests/daemon_refresh_e2e.rs::daemon_refresh_cycles_brain_while_hosted_endpoint_survives` is the closest full production-process refresh harness. It creates a temporary `SPT_HOME`, installs a private mock adapter, launches the real `spt daemon run` broker/supervisor process, starts a real broker-hosted dummy-harness PTY, invokes the real `spt daemon refresh` CLI, and proves brain PID/generation changed while broker PID, harness PID, and ONLINE status survived. Command: `cargo nextest run -p spt --test daemon_refresh_e2e`. Critical limitation: it deliberately writes an invalid `identity/node.key` to make the rig NET-LESS, starts only one endpoint, never starts `spt rc` or any attach stream, never injects PTY input, and never reads post-refresh PTY output. It therefore cannot detect loss/replay of dispatcher-owned controller streams.\n\n2. `crates/spt/tests/resume_no_control_steal_e2e.rs::brain_respawn_keeps_every_session_controller_and_still_promotes` has the best existing N-controller brain-replacement rig. It runs a real `spt daemon brain` child under `supervise_brain`, an in-process real broker/NetHost, and N real ticking PTYs, with one raw local controller connection per PTY. It proves every held controller continues receiving output across a real brain-process replacement. Command: `cargo nextest run -p spt --test resume_no_control_steal_e2e`. Limitation: these are broker `KIND_SPAWN` auto-controller connections, not remote/local `spt rc` NetStreams; it asserts output only, not operator-input→PTY→output round trips; it does not run the `daemon refresh` CLI.\n\n3. `crates/spt-daemon/tests/attach.rs::attach_survives_target_brain_restart_exactly_once` is the strongest existing bidirectional attach-restart proof. Two real loopback NetHosts/brokers host a real echo PTY; an operator sends `MARKER_ONE`, the serving brain dies, `MARKER_TWO` is sent in the dead window, and a successor re-serves the same broker-held stream. It asserts both markers on the rendered viewport and source PTY screen. Focused command: `cargo nextest run -p spt-daemon --test attach -E 'test(attach_survives_target_brain_restart_exactly_once)'`. Critical limitation: the test already knows the stream family/id and manually calls `serve_attach` on the successor. It bypasses production `run_dispatch_loop` rediscovery and `peek_first_line` classification, which is exactly where refresh recovery can fail.\n\n4. `crates/spt-daemon/tests/dispatch.rs::dispatcher_serves_a_remote_drive_attach_undriven` and `dispatcher_resolves_attach_by_endpoint_and_refuses_a_stale_endpoint` exercise the production dispatcher with real brokers, NetHosts, echo PTYs, attach requests, controller input, and echoed output. Focused commands: `cargo nextest run -p spt-daemon --test dispatch -E 'test(dispatcher_serves_a_remote_drive_attach_undriven)'` and `cargo nextest run -p spt-daemon --test dispatch -E 'test(dispatcher_resolves_attach_by_endpoint_and_refuses_a_stale_endpoint)'`. Limitation: the dispatcher is started once; neither test kills/replaces it, cycles a brain, fills/evicts a stream ring, or leaves stale finished attach streams for a successor dispatcher.\n\n5. Adjacent evidence: `crates/spt/tests/attach_wedge_e2e.rs` is the closest real detached-daemon + real `spt rc` process rig, but tests abrupt rc/PTY death and subsequent new attach, not refresh; `crates/spt/tests/brain_respawn_rename.rs` tests a real daemon/binary rename/brain respawn but has no PTY/controller; `crates/spt/tests/brain_survive.rs` tests process-level survival across swapped brain bytes but not production attach redispatch; `crates/spt-daemon/tests/brain_resume_conn_deadlock.rs`, `brain_decouple.rs`, and `conn_blackhole_lifecycle.rs` cover non-draining subscriber, connection retirement, and unrelated-session liveness but not the remote attach dispatcher lifecycle.\n\nCONFIRMED CALL CHAIN AND OWNERSHIP\n\n- PTY data plane: `Broker` owns each `PtySession`, input writer, output ring, controller/viewer table, child PID, and effect journal. `broker.rs::handle_conn` explicitly detaches a dead subscriber but leaves sessions running; NetHost likewise detaches a dead stream subscriber while preserving streams/rings.\n- Controller/viewer connection: `serve_attach` owns the serving side of an rc viewport. Its primary `Brain` connection subscribes both to the peer-initiated NetStream and to the PTY as controller/viewer; a second `wire` Brain connection forwards session output back onto the NetStream to avoid same-connection replay deadlock. Controller input is decoded from `AttachRecord::Input` and delivered with `send_effect_no_ack`; output is encoded as `AttachRecord::Output`.\n- Daemon brain refresh: `cmd_daemon_refresh` calls `refresh_brain`; the supervisor cycles only the brain child. `run_brain` invokes cursor-only `Brain::resume_session_cursors`, intentionally making no PTY subscriptions because the main heartbeat connection is non-draining. Thus `BRAIN_RESUMED` logs session cursors, not rc controller streams. This is correct and orthogonal to rc restoration.\n- NetStream/rc restoration: after `run_brain` reaches its heartbeat, `spawn_net_consumers` starts a fresh dispatcher. `run_dispatch_loop` calls `Brain::net_streams`, selects peer-initiated streams, and spawns `worker`; `worker` calls `peek_first_line(stream_id, from_seq=0)`, classifies the protocol, then creates a fresh Brain and calls `serve_attach`. `serve_attach` replays the Request, resolves endpoint→session, and `attach_as(..., Some(origin_node))` silently retakes the same-origin controller. Therefore the architecture does contain an automatic rc re-serve path; saying the successor only restores `BRAIN_RESUMED` cursors omits this separate dispatcher reconstruction path.\n- Broker lifecycle: a brain-only refresh must not change broker PID, PTY PID, NetHost connection, or NetStream table. A broker restart is a separate lifecycle and severs these ephemeral controller transports; it must not be conflated with brain refresh.\n\nCAUSAL CANDIDATES\n\nA. Stale finished-stream redispatch/takeover — primary candidate, `[INFERENCE]`. `NetStreamInfo` exposes `finished`, but `run_dispatch_loop` selects on only `!initiated_locally`, `next_seq > 0`, and fresh `claimed` membership; it does not exclude finished streams. Every brain generation starts with an empty `claimed` set. Consequently a successor can redispatch historic, already-finished attach streams, replay their old Request records, and transiently/silently retake a same-origin controller while active streams are also being reconstructed. Multiple historical streams can race in HashMap iteration/worker scheduling. This is a direct candidate for partial session recovery and controller churn.\n\nB. Evicted opener makes redispatch one-shot fail — structurally confirmed, causal application `[INFERENCE]`. `NetHost::StreamLog` is a bounded ordinary ring of 4,096 transport-read chunks and evicts its oldest chunks. `run_dispatch_loop` nevertheless reconstructs protocol family exclusively by subscribing from logical zero and parsing the first retained NDJSON line. If the original attach `Request` has rolled out—or the retained floor begins in the middle of an NDJSON record—the first parsable retained record is typically `Input`/`Resize` or invalid JSON. `classify_first_line` deliberately returns `Unknown` for mid-protocol records. The worker exits, but the dispatcher has already inserted the stream ID into `claimed`, so that generation never retries it. Detach+reattach creates a new stream whose Request is again at its retained head, explaining immediate recovery.\n\nC. Partial 4/5 observation: `[INFERENCE]` both candidates naturally produce a non-uniform blast radius. Finished-stream history differs per endpoint, and opener retention depends on the amount/coalescing of operator→target traffic. Four older/high-input streams can have lost their Request or have more stale finished predecessors while one newer/quieter stream retains a classifiable opener and re-serves normally. This is more specific than an undifferentiated PTY freeze: PTY children and broker rings can remain alive while only selected controller forwarding workers fail to reconstruct.\n\nSMALLEST SAFE DETERMINISTIC FEEDBACK LOOP\n\nCompose the `resume_no_control_steal_e2e` real-brain supervisor with the `dispatch.rs` real remote-attach echo flow in a new isolated integration test (suggested name `refresh_rc_stream_recovery_e2e`; avoid `update`/`setup`/`install` in the test-binary name because Windows installer heuristics can cause UAC error 740).\n\nUse a temporary process-global `SPT_HOME`; one in-process target `Broker::bind_in_with_net` with loopback-only `NetHost`; one in-process operator broker; and the real `CARGO_BIN_EXE_spt` brain subprocess under `supervise_brain`. No discovery, relay, production socket, production perch, or live user process is involved. Wire the broker's `BrainRestart` signal exactly as the supervisor tests do, then invoke the private-home `spt daemon refresh` CLI if exact verb coverage is desired; alternatively call `BrainRestart::request` for the narrower brain-replacement loop.\n\nUse N=3 because it is the minimum that can express “most but not all”: spawn three labeled real echo PTYs (`cat` on Unix, `findstr .` on Windows); open three endpoint-addressed Control attach streams; send unique PRE markers and require all three to echo. Preserve the Request in stream 3. For streams 1 and 2, deterministically shape the broker-held peer ring into the natural post-rollover state by calling the test-held target `NetHost::drain_stream(peer_stream_id)`, assert the drained bytes contained the attach Request, then send/observe another input so the retained head is a mid-protocol Input record. This avoids 4,096 timing-sensitive writes or hundreds of MiB of artificial traffic while representing the same observable broker state as bounded-ring rollover.\n\nCycle the real brain and wait for a changed PID/generation plus `BRAIN_NET_CONSUMERS_UP`/a bounded dispatcher-ready condition—not merely `brain.ready`, because current `run_brain` writes ready before its first 500 ms heartbeat starts net consumers. Then send unique POST markers down all three existing controller streams and collect results with bounded `read_event_until` loops. Required pass contract after a fix: all 3 existing streams return their own POST marker exactly once; all PTY PIDs and the broker remain unchanged. Current discriminating failure: stream 3 (retained opener) recovers while streams 1 and 2 time out because successor classification sees Input rather than Request. After collecting that failure state, detach and create fresh attach streams for 1 and 2 and prove RECOVER markers echo; this verifies the reported detach+reattach recovery without masking the original failed assertion.\n\nAdd a separate deterministic selection test for candidate A: construct active and finished peer-initiated attach streams and assert successor claim selection excludes `finished=true` rows before any worker can replay an old Request. Do not rely on concurrent worker ordering to prove this invariant.\n\nThe single end-to-end pass/fail signal should be: `pre_echoes == 3 && broker_pid_same && pty_pids_same && brain_pid_changed && existing_post_echoes == 3`. Diagnostic output should include, per stream: stream ID, `finished`, `next_seq`, whether opener was retained/drained, pre/post echo, and fresh-reattach recovery. This separates PTY survival from controller-stream restoration and makes a failed run actionable.\n\nHISTORICAL RULES\n\n- `CONTEXT.md`, ADR-0004, and ADR-0018 require the stable broker to own PTYs and streams while only the brain is replaceable; routine refresh/update may not terminate or suspend endpoints.\n- `docs/KNOWN-HAZARDS.md` §7.36 covers a suspended/black-holed brain subscriber freezing controller output and requires broker-side stall eviction/recovery; §7.38 requires bounded, cancelable, poison-on-failure physical broker writes. These guards address blocked physical subscriber connections, but do not prove successor dispatch classification/reclaim correctness.\n- `docs/DEBUG-ROLLOUT.md` requires local source-built, isolated-home test tooling and forbids production CLI/debug trust shortcuts. The proposed loop follows that rule: loopback, temp home, scoped PIDs, no shared release/update cache, and no live sessions.",
  "files": [
    {
      "path": "crates/spt/tests/daemon_refresh_e2e.rs",
      "description": "Closest real `spt daemon run` + real `spt daemon refresh` E2E. Proves broker/harness/perch survival and brain PID/generation change, but deliberately NET-LESS and has no rc/controller traffic."
    },
    {
      "path": "crates/spt/tests/resume_no_control_steal_e2e.rs",
      "description": "Best N-session, real brain-subprocess replacement harness. `spawn_ticker_controller` holds raw spawn-time local controllers and counts output across supervisor restart/promotion; ideal base for a composed dispatcher recovery rig."
    },
    {
      "path": "crates/spt-daemon/tests/attach.rs",
      "description": "Contains `attach_survives_target_brain_restart_exactly_once`, real echo PTY and bidirectional dead-window markers, plus controller/viewer role tests. Successor manually calls `serve_attach`, bypassing dispatcher rediscovery."
    },
    {
      "path": "crates/spt-daemon/tests/dispatch.rs",
      "description": "Production `run_dispatch_loop` integration rig with two loopback brokers. `dispatcher_serves_a_remote_drive_attach_undriven` and endpoint-addressed counterpart prove Request→Input→echo, but do not restart the dispatcher or test retained-ring floors/finished streams."
    },
    {
      "path": "crates/spt-daemon/src/dispatch.rs",
      "description": "Critical reconstruction seam: `run_dispatch_loop`, `worker`, `peek_first_line`, `classify_first_line`, `spawn_dispatcher`. A fresh per-process `claimed` set reclaims peer streams; selection currently does not exclude `finished`, and family recovery depends on retained opener bytes."
    },
    {
      "path": "crates/spt-daemon/src/nethost.rs",
      "description": "Broker-owned NetStream data plane. `StreamLog` has a bounded 4,096-transport-chunk ordinary ring, evicts its front, supports one replaceable subscriber, and preserves stream info across brain subscriber disconnect. `NetStreamInfo.finished` is available to dispatcher selection."
    },
    {
      "path": "crates/spt-daemon/src/attach.rs",
      "description": "`serve_attach` exact controller/viewer server path. Uses separate receive/controller and wire-forward Brain connections; subscribes the NetStream, replays Request, attaches by origin, journals input, and forwards sequenced PTY output."
    },
    {
      "path": "crates/spt-daemon/src/brainproc.rs",
      "description": "`run_brain` startup and supervisor lifecycle. Uses cursor-only `resume_session_cursors`, writes ready, then starts dispatcher/peer pump on the first net-enabled heartbeat via `spawn_net_consumers`."
    },
    {
      "path": "crates/spt-daemon/src/brain.rs",
      "description": "`resume_session_cursors`/`resume_sessions`, attach APIs, NetStream APIs, and planned brain restart request. Documents why the supervised main brain must not subscribe PTY output on its heartbeat connection."
    },
    {
      "path": "crates/spt-daemon/src/broker.rs",
      "description": "PTY owner and connection cleanup. `handle_conn` detaches dead session/NetStream subscribers but leaves sessions and stream rings alive; `dispatch_spawn`, `dispatch_subscribe`, and `resolve_subscribe` implement spawn-time controller and same-origin silent retake."
    },
    {
      "path": "crates/spt/src/rc.rs",
      "description": "Operator-side production viewport pump. Local attach is a loopback NetStream through the same `serve_attach` dispatcher path as cross-node attach; detach finishes the viewport without killing the broker-owned PTY."
    },
    {
      "path": "crates/spt/tests/attach_wedge_e2e.rs",
      "description": "Closest existing real detached daemon + dummy harness + real rc subprocess rig. Covers abrupt rc/PTY failure and later attach, but no brain refresh."
    },
    {
      "path": "crates/spt/tests/brain_respawn_rename.rs",
      "description": "Real daemon process and applied-binary rename/brain respawn path proof; has no hosted PTY/controller assertions."
    },
    {
      "path": "crates/spt-daemon/tests/brain_resume_conn_deadlock.rs",
      "description": "Deterministic real PTY flood rig proving cursor-only daemon-brain resume avoids request/reply connection self-deadlock; adjacent subscriber hazard, not remote attach redispatch."
    },
    {
      "path": "crates/spt-daemon/tests/brain_decouple.rs",
      "description": "Real broker/PTY controller black-hole and physical connection retirement evidence for KNOWN-HAZARDS §7.36/§7.38; no refresh or dispatcher reconstruction."
    },
    {
      "path": "crates/spt-daemon/tests/conn_blackhole_lifecycle.rs",
      "description": "Windows deterministic black-holed-controller lifecycle harness; proves unrelated sessions continue and bad physical conn retires, but not brain refresh."
    },
    {
      "path": "docs/KNOWN-HAZARDS.md",
      "description": "Historical controller/brain subscriber hazards, especially §7.36 broker-viewer-brain decoupling and §7.38 bounded/cancelable physical writes; useful to distinguish prior blocked-connection failures from reconstruction failures."
    },
    {
      "path": "docs/DEBUG-ROLLOUT.md",
      "description": "Requires isolated source-built debug validation rather than production update/debug surfaces; supports temp-home, loopback-only feedback loop."
    },
    {
      "path": "docs/adr/0004-single-daemon-broker-brain-split-and-self-update.md",
      "description": "Original stable-broker/restartable-brain and no-endpoint-termination update contract."
    },
    {
      "path": "docs/adr/0018-broker-brain-process-isolation-restoration.md",
      "description": "Restored process boundary: broker owns PTYs/sockets/NetHost; brain-owned consumers are reconstructed after replacement."
    }
  ],
  "architecture": "The broker is the durable-in-process data plane for a daemon lifetime: it owns PTY masters/children, output logs, input effects, NetHost connections, and NetStream rings. `spt rc` does not own the PTY; it owns an operator viewport carried by a broker-held stream. On the target node, a brain-process dispatcher classifies that peer stream and launches a `serve_attach` worker whose broker IPC subscriptions temporarily bind the stream to the PTY controller/viewer slot. A brain refresh kills these control-plane workers but must leave the broker data plane untouched. The successor main brain deliberately restores only session cursors on its non-draining heartbeat connection; independently, its newly spawned dispatcher must rediscover and re-serve every still-active peer stream. Existing tests prove each segment separately but not their composition. The uncovered join is successor stream claim/classification: finished streams are not filtered, and active stream family is re-derived from a bounded byte ring whose opener may no longer exist. The proposed N=3 isolated supervisor+dispatcher+echo-PTY test makes that join deterministic and distinguishes session survival, active controller reconstruction, and detach/reattach recovery."
}