#49 / #267 -- CALL-PATH MAP, LANE COMPARISON, PER-FIX EVIDENCE TABLE, AND THE DISCRIMINATOR. Source and history only. Nothing run. The causal claim is carried as a HYPOTHESIS throughout, per your ruling. == 1. THE ACTUAL CURRENT REFRESH PATH, MAPPED TO ITS KILL SITE == cli.rs:2552 DaemonCmd::Refresh => cmd_daemon_refresh() cli.rs:8160 cmd_daemon_refresh() applyhost.rs:364 refresh_brain(broker_name) connect_retry(broker_name) Brain::request_brain_restart() <- "the SAME supervisor brain-restart signal the post-swap apply rides" brainproc.rs ~1075 supervisor loop, `if restart.take()` emit BRAIN_UPDATE_RESTART: "planned restart -- cycling brain onto the swapped binary" brainproc.rs:1081 child.kill() child.wait() planned = true FIVE kill sites exist in brainproc.rs; I read all five and only ONE is on this path. The other four are: trial Stopped (998), promote-rejected wrong-bytes rollback (1015), trial-timeout alive-never- ready (1055), and supervisor stop (1071). So the refresh/update path reaches exactly brainproc.rs:1081, and it is a kill, not a close. THAT IS A PATH FACT, not yet a cause: it does not establish what happens to the outgoing generation's controller conns, which is the open question. CONNECTION OWNERSHIP AND CLEANUP ORDERING, as far as source shows: the kill is followed by `child.wait()` and `planned = true`, with no conn-close step between the signal and the kill on this path. The promotion gate is real and still present -- broker.rs:2815 and :5906 both carry [impl->REQ-UPDATE-PROMOTE-DRAINED], documented as "is ANY hosted session's LOCAL controller ... wedged past `deadline`", read by the brain-generation promotion DRAINED gate. So the gate that the seed's root-claim names still exists under its own requirement. What I have NOT established is whether a refresh in the field drives it. == 2. MAIN VERSUS THE PENDING MILESTONE ANCESTRY: NO DIVERGENCE ON THIS PATH == Diffing main...3e7eaf67 over brainproc.rs, broker.rs, applyhost.rs and rc.rs: crates/spt-daemon/src/brainproc.rs UNCHANGED crates/spt-daemon/src/broker.rs UNCHANGED crates/spt-daemon/src/applyhost.rs UNCHANGED crates/spt/src/rc.rs +350 / -8 So the entire refresh/brain-cycle path is IDENTICAL between main and the pending milestone ancestry. The lane's only divergence here is client-side rc, which is #230's stale-attach advisory. Nothing pending changes the mechanism under discussion, and no unlanded fix is waiting to land for #49. == 3. PER-FIX TABLE: COMMIT, TESTED SCENARIO, WHAT IT EXCLUDES, WHAT IT NEVER EXERCISED == 0d0559f2 REQ-RC-RECONNECT "B-3 -- rc viewport auto-reconnects on a severed transport" TESTED twohost rig run 28772170556 @279df99 + the BrokerGone reheal fixture in rc.rs EXCLUDES that rc print-and-exits on a severed transport; BrokerGone and Severed both re-drive NEVER EXERCISED a brain cycle that produces NO EOF-shaped signal. The reconnectable class is EOF-shaped by construction, so nothing in this evidence speaks to a conn that stays open and stops moving. c9346c0a REQ-HAZARD-BROKER-VIEWER-BRAIN-DECOUPLE "W2 broker viewer/brain decouple -- stall-evict a wedged controller + observability" TESTED an NtSuspendProcess rig on the brain, explicitly "no update involved" EXCLUDES an UNBOUNDED block on a suspended brain; BRAIN_WRITE_DEADLINE = 15 s now evicts NEVER EXERCISED A BRAIN THAT IS KILLED RATHER THAN SUSPENDED. This is the sharpest gap in the whole table: a SUSPENDED process still holds its pipe ends open, while a KILLED one has its handles torn down by the OS. Those are different close/EOF stories at the broker's read end, and the rig only ever produced the first. The fix may well be correct and still never have met the refresh path's conditions. 3d632b92 REQ-RC-RECONNECT-TRUTH "rc is connect-only, never auto-starts a daemon, bounded reconnect" TESTED the WMI auto-launch repro and the "Reconnecting to local daemon..." forever freeze EXCLUDES rc resurrecting a daemon against an operator stop; an unbounded reconnect banner NEVER EXERCISED a freeze while the connection remains ESTABLISHED. Every arm presupposes the connection was already lost. fb499dbf REQ-BRAIN-RESUME-NO-CONTROL-STEAL "resume re-attaches as Viewer, never steal a controller" TESTED the multi-broker int rig, v0.30.4 EXCLUDES resume_sessions stealing the controller of a session the brain does not drive NEVER EXERCISED the SharedSend conn deadlock -- which is not speculation: the registry records that this fix was FIELD-VERIFIED AND RE-WEDGED, and that re-wedge is what produced the next row. 03c71093 REQ-BRAIN-RESUME-NO-CONN-DEADLOCK "daemon resume is cursor-only -- kills the SharedSend brain-conn deadlock" TESTED the confirmer rig, both severities RED-first, and explicitly dead-peer-INDEPENDENT EXCLUDES the brain self-subscribing its own request/reply IPC conn into a lock cycle NEVER EXERCISED what becomes of the OUTGOING generation's controller conns after brainproc.rs:1081 kills it. That is the territory the deferred seed names. 66e930ce REQ-DAEMON-REFRESH -- the verb, not a fix. Its text is why #49's and #267's repro routes are one path: refresh is the apply_staged brain-cycle MINUS THE SWAP. == 4. THE HYPOTHESIS, LABELLED AS ONE == HYPOTHESIS (not the archaeology's finding): the five shipped changes each repaired a real and different consequence, while the mechanism the deferred seed names -- outgoing controller conns after a hard kill -- was never repaired, so a residual freeze survives every one of them. WHAT WOULD FALSIFY IT, and what I cannot currently exclude: equivalent clean-close work may have landed under a different requirement id (an empty required_stages proves the REQUIREMENT is inactive, nothing more); the refresh path may never reach the wedged-conn state at all; and the v0.30.4 re-wedge supports only that ONE failed-fix sequence, not every later report. #49's wording establishes neither "no EOF" nor an unbounded freeze -- I inferred both and should not have. == 5. THE SMALLEST RUNTIME DISCRIMINATOR, USING ONLY EXISTING INSTRUMENTS == No new rig, no code change, no new instrument. One `spt daemon refresh` on a daemon with an attached rc session, with daemon stderr captured, then read the ORDER and TIMESTAMPS of log lines that already exist in the tree: CYCLE BOUNDARY BRAIN_UPDATE_RESTART (brainproc.rs:1081, the planned kill -- the t0 marker) BRAIN_RESTARTED / BRAIN_UP / BRAIN_RESUMED Q1 does the outgoing connection close? BRAIN_CONN_RETIRED present for the OLD generation around t0 => the conn was retired; ABSENT => it was not, which is the seed's predicted shape BRAIN_BROKER_LOST the alternative close story Q2 does a blocked sink reach eviction? BRAIN_SUBSCRIBER_STALL_EVICT present => the deadline path ran; its delta from t0 tests the 15 s claim directly BRAIN_WRITE_DEADLINE / BRAIN_WRITE_DEADLINE_MS Q3 does promotion wait on drained state? BRAIN_PROMOTED minus t0. If promotion lands only AFTER the evict in Q2, the DRAINED gate (REQ-UPDATE-PROMOTE-DRAINED, broker.rs:2815 / :5906) waited; if it lands before, the gate was not the wait. Q4 what survives reattach? BRAIN_RESUMED, plus the rc-side observable after a detach and fresh attach FOUR OUTCOMES, EACH DECIDING SOMETHING DIFFERENT: the freeze's measured duration against t0 settles bounded-at-15 s versus unbounded, which is the question #49's word "freeze" does not answer. Q1 absent plus Q2 present at about 15 s would support the hypothesis; Q1 present would weaken it sharply; Q2 absent would mean the blocked-sink path is not involved at all and the hypothesis needs replacing rather than refining. Every token above I verified exists in the current tree. I am proposing this, not requesting it -- W2 retains the window and I will not compete with it.