{
  "summary": "W1c proves replacement attach workers re-enter the existing stream, seat successfully, then lose their shared receiving carrier at the 15-second write bound. It does not identify the worker's blocked instruction. Strongest concrete consumer candidate is replay-input flushing with ack=true on the receiving connection; a separate outbound forwarding wait remains a competing candidate. The observed os233 failures take the ordinary three-attempt retry path, not the seat-blocked circuit breaker.",
  "files": [
    {
      "path": "crates/spt-daemon/src/attach.rs",
      "description": "Attach reconstruction, separate wire connection, replay handling, pending-input ack discrepancy, output forwarding."
    },
    {
      "path": "crates/spt-daemon/src/brain.rs",
      "description": "Send-only versus request/reply APIs; blocking write_frame carrier; separate stream/session cursors."
    },
    {
      "path": "crates/spt-daemon/src/dispatch.rs",
      "description": "Pinned opener classification, fresh worker creation, ring-floor subscription, failure classification and retry terminalization."
    },
    {
      "path": "crates/spt-daemon/src/nethost.rs",
      "description": "Durable stream replay, independently renewed subscriber lease, replay-before-live writer, bounded send_stream bridge."
    },
    {
      "path": "crates/spt-daemon/src/broker.rs",
      "description": "Subscribe reply ordering, input effect dedup and optional Applied acknowledgement, idempotent same-connection establish."
    },
    {
      "path": "crates/spt-daemon/tests/attach.rs",
      "description": "Existing prepopulated-ring and manually orchestrated two-input restart scenarios."
    },
    {
      "path": "crates/spt-daemon/tests/redispatch.rs",
      "description": "Real dispatcher-generation restart with an eight-chunk rolled stream ring."
    },
    {
      "path": "crates/spt-daemon/tests/redispatch_stall.rs",
      "description": "Tests for deliberately wedged subscriber retirement, producer isolation, lease renewal, and classified retry."
    },
    {
      "path": ".spt/preserved/307/hertz-w1bc/w1c-run1/home-w1c/logs/daemon.stderr.log",
      "description": "Actual daemon evidence: initial retirement, successful replacement subscriptions, three timed poisons and os233 worker outcomes."
    },
    {
      "path": ".spt/preserved/307/hertz-w1bc/w1c-run1/events.jsonl",
      "description": "Input submission, refresh timing and fresh numeric network-canary observations."
    }
  ],
  "architecture": "A dispatcher worker cold-starts its receiving Brain, subscribes to the existing inbound stream's retained floor, seeds the pinned Request, and subscribes to session output on that same IPC connection. A second Brain forwards output onto the held stream. Stream and controller writers share the receiving connection; the one serve thread alternates reading that connection and synchronously writing commands. NetHost owns durable stream history and generation-local subscriber leases independently of brain lifetimes.",
  "report": "## Scope and evidence status\nRead-only investigation only: no edits, runtime commands, tests, builds, or live probes. No LSP tool was exposed; navigation used source search and line reads. Runtime facts below come from preserved artifacts or the supplied W1c observations, not a newly executed experiment. All causal mechanisms remain RCA CANDIDATES.\n\n## Observed W1c sequence\nActual log: `.spt/preserved/307/hertz-w1bc/w1c-run1/home-w1c/logs/daemon.stderr.log`.\n- Lines 45–54: planned brain restart; old controller conn17 closes at wall_ms1789523803471 with cause=detach; organic BrokenPipe retirement, then transport-close. This is not evidence that an old conn17 poison remained installed throughout the replacement attempts.\n- Lines 65–70: replacement conn70 subscribes stream1 from_seq=0 and seats session1. It processes the original generation1789523756051 Request twice: first decision=controller, then decision=idempotent. Conn88 repeats this at81–86; conn104 at96–101.\n- Lines72–80,87–95,102–110: each fresh connection times out at approximately15 seconds; stream-sub-writer-poison explicitly says `live write failed`; controller writer exits TimedOut; slot closes cause=detach; worker reports os233. Worker elapsed_ms values are15037,15041,15052, attempts0,1,2.\n- IMPORTANT discriminator: `live write failed` comes from nethost.rs225–227, reached only AFTER replay iteration217–223 completed. This rules against the narrower statement 'this subscriber writer is still blocked writing its initial replay'. It does NOT prove the brain application consumed all replay records: frames may have reached IPC buffers while application handling fell behind.\n- events.jsonl113 records baseline tag022 submission;114 ends the45-second baseline;116 onward starts refresh;135,138,143,146,151 show fresh numeric canaries and zero active dial tasks while conn70 is seated/stalling. Example135: canary_age23ms, active_dial_tasks0, query_elapsed0. The supplied child ledger/receipt observations establish that baseline input reached the child and subsequent live input did not. Submission events alone would not prove child receipt.\n- The supplied raw-output observation is that each replacement can produce a screen snapshot but no continuing live stream. No source claim below treats that snapshot as successful full-duplex recovery.\n\n## Exact replacement-worker path\n1. dispatch.rs702–709 establishes a new dispatcher connection and fresh claims map per generation. Existing broker-owned inbound streams are enumerated; locally initiated/empty rows are excluded at733–738.\n2. worker1034–1108 queries/classifies the opener, excludes finished Attach rows, then opens a fresh serving Brain. first_line927–948 prefers the broker-pinned opener; only explicit unknown-verb allows the older-broker replay-peek fallback. Thus ordinary transport failure does not intentionally manufacture a peek subscriber.\n3. Attach branch1177–1200 resolves endpoint/session BEFORE subscription. This avoids a sessions() request/reply consuming interleaved attach events. At1209–1219 it calls serve_attach with the table's floor_seq plus the pinned Request.\n4. attach.rs483 sends net_stream_subscribe. nethost.rs545–602 snapshots retained frames from the requested floor and installs a writer with a fresh lease. Its writer sends all replay frames, then live frames, in that order (217–227).\n5. attach.rs501 opens an independent wire Brain. At537–568 the pinned Request seeds the same handler used for wire Requests. handle_attach_request420–439 sends the session subscribe and sets attached=true. The replayed Request can trigger the same handler again at621–648.\n6. brain.rs1453–1479 sets session_id/cursor, enables cold-output baselining for from_seq0 and sends subscribe. It does not wait for Subscribed. subscribe_with2511–2551 and net_stream_subscribe2384–2395 are send-only.\n7. Broker dispatch_subscribe8790–8806 resolves the controller seat, then sends Subscribed on the same receiving SharedSend. The duplicate nonzero-generation establish on the SAME conn is idempotent at broker.rs3193–3230: it returns Controller without replacing the writer or producing a second initial batch. W1c's idempotent breadcrumbs show this prior fix's intended branch is reached.\n8. The serve loop reads via read_event_until or controller gap-resume (attach.rs570–610). Incoming stream Input before role confirmation is buffered(680–682); after Controller confirmation it is forwarded with send_effect_no_ack(671–677). Subscribed handling sets role then invokes feed_rest and flushes pending_input(734–755).\n9. Output forwarding is gated by attached, NOT confirmed role (attach.rs890–899). Therefore one cold snapshot can be forwarded before the Subscribed branch and its pending-input work finish. One snapshot does not establish that the worker reached a sustainable read loop.\n\n## Ranked candidates and distinguishing predictions\n### 1. Replay-input batch creates a receiving-connection write/read cycle — strongest concrete source discrepancy, not proven\nSource: attach.rs754–755 flushes every pending_input using `brain.send_effect(...)`; brain.rs921–923 selects ack=true. This differs from the confirmed-controller path's explicit no-ack operation at attach.rs674–677 / brain.rs934–935. Both are synchronous writes, not acknowledgement waits: send_effect_inner943–957 calls send, and send2554–2559 directly calls write_frame.\n\nBroker input dedups the old operator operation IDs but still sends Applied when ack=true (broker.rs8898–8936). Thus already-applied history is not necessarily traffic-free. A replacement can buffer historical Input before Subscribed, then issue a batch of request writes without returning to the receiving read loop. If its incoming IPC writes back up while the broker handler is blocked sending Applied or another reply behind stream/controller output, the worker cannot drain the receiving connection. Its independently fed stream/controller writers then hit the15-second carrier bound. A pre-Subscribed output snapshot remains compatible with this cycle.\n\nWhy refresh-specific: a cold initial attach has little historical input; replacement starts from retained stream floor0 again, not the previous worker's net_cursor. Every retry can reconstruct the same backlog. The broker's effect journal prevents double typing but does not prevent these replay requests or requested duplicate acknowledgements.\n\nCritical limitation: the artifacts examined do not expose pending_input length, acknowledgement count, pipe occupancy, or a worker stack. The roughly thirteen baseline tag submissions visible in events are NOT sufficient by themselves to prove that the batch fills a pipe; actual Input record fragmentation and scheduling matter. Do not label this established deadlock.\n\nFalsifiable predictions: during the stall, the worker is in write_frame reached from pending_input flush; broker handler is at an Applied reply or another receiving-conn reply; pending_input was nonempty; application read_event progress stops at that flush. If pending_input is empty on every attempt, or the worker is instead parked in outbound wire forwarding/read_event, this specific mechanism is false. In an authorized later trial, retaining equivalent PTY output but removing pre-refresh input history should weaken this candidate; a fresh operator stream with no history should distinguish it from a history-independent egress fault. These are predictions, not executed changes.\n\n### 2. A synchronous outbound forwarding write stops the sole receive loop — competing candidate\nattach.rs899 calls wire.net_stream_send(None); brain.rs2108–2120 writes its IPC command synchronously and returns without NetSent waiting. Broker dispatch_net_stream_send9832–9835 calls NetHost.send_stream directly; nethost.rs1912–1921 enters bounded_block_on, waits for the stream send mutex, then write_all/finish. The two-connection split removes the old same-handler output-forwarding cycle but does not make the serving thread itself asynchronous. If the wire connection's handler stops consuming commands, the serve thread can block on a subsequent wire write and cease reading its separate receiver. A snapshot can fit through before backlog develops.\n\nFresh canary is compatible with a yielding per-stream mutex/transport wait or ordinary blocking IPC on another thread. It argues against net-runtime-wide starvation, not against every individual stream wait. active_dial_tasks0 narrows away active dial congestion, not stream-send work.\n\nLimits: ordinary stream pumps are bounded-overwrite, not the retentive shell-tunnel wait (nethost.rs1132–1137); send_stream has a10-second async-operation timeout(nethost.rs59–73,1469–1479). Therefore an unbounded 'QUIC await' is not a supported claim here. The proposed cycle needs evidence of blocked wire IPC/handler, repeated bounded operations, or an error reply blocking the undrained wire side; current log attribution points to the receiving conn, not a demonstrated wire failure.\n\nPredictions: worker stack is at wire.net_stream_send/write_frame, pending-input flush already finished or never entered; wire handler is in send_stream or reporting its failure; no more read_event calls occur on conn70. A worker demonstrably continuing to read receiving events through the15-second window would falsify this simple sole-reader-stop model.\n\n### 3. Pure cursor/duplicate-establish explanation — lower-ranked and insufficient alone\nbrain.rs1311–1336 performs stream dedup/forward-gap rejection; attach_as_coded resets the session cursor to from_seq0 and baseline flag(1464–1471). A replayed opener invokes that reset even when the broker preserves the existing same-conn writer. That is worth correlating with decoded output, but a cursor mistake alone would ordinarily leave the worker reading/dropping events or produce an explicit stream/output gap, not a15-second non-draining carrier timeout. W1c has os233 after poison, not a logged gap. The old 'same Request replaces its own controller writer' account is specifically contradicted by decision=idempotent and broker3193–3230.\n\nPrediction: a pure cursor issue shows continued read_event throughput with discarded/misbaselined output, or an explicit gap error, rather than a stalled synchronous write. Treat it as a potential contributor only if those facts appear.\n\n## Replay and acknowledgement facts that prevent misleading RCA\n- There is no NetSent acknowledgement wait on ordinary attach output: op=None exits net_stream_send at2118–2120. The journaled wait loop2124–2130 does discard unrelated events, but it is NOT the normal forwarding branch.\n- send_effect(ack=true) is not a synchronous wait-for-Applied API. Its danger here is generated reply traffic and a write-only batch, not a hidden `recv(Applied)` call.\n- NetStreamData consumption advances the local cursor at brain1330, with no consumption acknowledgement in that branch. New dispatcher workers subscribe from retained floor(dispatch1215), not a persisted per-worker read cursor. Stream retention plus PTY effect dedup supports replay, but does not inherently bound replay-side request traffic.\n- A fresh StreamLease is minted on every subscriber installation(nethost580–600). Old-writer death should cancel only the old lease. 'The original conn17 failure permanently canceled all successor leases' is not supported by current source.\n- Module comments describing controller replay as inline/synchronous in attach.rs484–500 are historical context, not proof of current controller writer implementation. Do not rebuild that old mechanism merely from those comments.\n\n## Why prior fixes can be working without curing this W1c failure\n1. Physical connection poisoning bounds a non-draining carrier; the W1c logs show retirement and replacement, so it is containing each incident. It does not force the replacement serve thread to consume.\n2. Async StreamLog subscriber writers plus fail-first halt remove replay-under-log-lock producer stalls and serial writes past a dead conn(nethost189–242). LIVE failure in W1c demonstrates the replacement reached beyond replay writing; it does not demonstrate healthy application drain.\n3. Fresh leases prevent a predecessor's delayed failure canceling a successor(nethost580–600), but a successor can poison its OWN lease if it stops draining.\n4. Pinned opener fixes classification after ring roll; the two-conn split fixes a specific forwarding/receive-handler coupling; idempotent same-conn establish preserves the controller writer. None changes pending_input's ack=true flush or removes synchronous writes from the sole serve thread.\n5. Crucial retry mismatch: classify_failure(dispatch373–383) recognizes only 'subscriber busy', 'replay halted', 'lease canceled' as SeatBlocked. Observed `No process is on the other end of the pipe. (os error233)` matches none, so it is Transient. outcome_transition503–545 increments attempts, waits500ms then1000ms via349–350, and becomes Terminal at MAX_DISPATCH_ATTEMPTS3(343). W1c attempts0/1/2 and spacing match that path. The seat-blocked global breaker is NOT demonstrated by these failures. Consequently three successful carrier replacements can each hit the same consumer problem and then leave the active stream unclaimed for that dispatcher generation. The next dispatcher generation recreates its claims map, allowing the cycle again.\n\n## What matching tests cover, and what they do not establish\n- tests/attach.rs547–650: prepopulated PTY ring, fresh loopback attach, final TICK39 rendered, then detach. This checks initial screen delivery, not sustained recovery of an old stream carrying a substantial input history.\n- tests/attach.rs867–1032: restart exactly-once test hand-steps life1, submits one old and one dead-window input, then life2 serve_attach; its seed_request is None. It is useful edge-idempotency coverage but not the production pinned-opener-plus-replayed-opener ordering under real rc traffic.\n- tests/redispatch.rs579–678: production dispatcher restart after shrinking the stream ring to8 chunks and rolling the opener out with40 inputs. It verifies pinned classification and MARK-B input/output recovery, but deliberately removes the full old input history and original Request from retained replay. W1c instead logs floor0 and duplicate Request handling on every attempt.\n- tests/redispatch_stall.rs332,415,546,607 identify tests for deliberately wedged subscribers, lease cancellation, opener failure, and injected SeatBlocked retry. Those contracts can all hold while a reconstructed real attach consumer stalls itself; os233 is a different classification from the injected subscriber-busy case.\n- tests/attach_idempotent_replay.rs17–30 and broker3193–3230 address same-connection duplicate establish without re-synthesizing history. W1c's idempotent decision is evidence against claiming that previously fixed writer-replacement mechanism has been reproven here.\n\n## Bottom line\nThe strongest defensible RCA candidate is: refresh reconstructs the SAME held attach and retained input history; a synchronous operation in that replacement's sole consumer thread stops draining the shared stream/controller IPC carrier;15-second conn poison contains the damage, but ordinary os233 retries reconstruct the same conditions three times and then terminalize the claim. The pending-input ack=true batch is the most specific remaining source-backed suspect, not a proven cause. The missing discriminator is the receiving worker's exact blocked call plus pending-input/read progress, not another global canary. Nothing in this analysis answers Q3; a real signed pending trial remains separate."
}