---
name: w2-c2-stream-lifetime-design
description: "W2-C2 design refresh COMPLETE 2026-07-22 (doyle, code-read during release window): clean leg ALREADY SHIPPED (ADR-0040 d6) — REQ text stale; real build = Leg A replay filter + Leg B presence-FIN synthesis; dispatch-ready for todlando when release lane clears"
metadata: 
  node_type: memory
  type: project
  originSessionId: 6e6b5fd6-f946-49ba-af0a-1ba39274b043
  modified: 2026-07-23T03:25:04.576Z
---

**W2-C2 design refresh (doyle 2026-07-22, code-read only, tree @7c0f12d verified zero W3 delta on these seams). Dispatch-ready for todlando after v0.41.0 publish + hertz field-verify dispatch ([[daemon-lifecycle-progress]]).**

**LEG 0 — VERIFY-FIRST FINDING: the C2 clean case is ALREADY SHIPPED (ADR-0040 decision 6), REQ-STREAM-LIFETIME-CLASS's title text is STALE.** Full chain in tree: opener-declared class (msg.rs:810-836, wire field msg.rs:851-855, serde-skip N-1 safe) → sole ConnectionBound opener = rc attach/view (attach.rs:667-677; every inter-brain opener Durable) → conn-exit sweep FINs + terminal-retires (broker.rs:4118-4123 tracking, 4227-4235 populate, 4429-4446 teardown) → serve_attach EOF arm detach_session_gen(serve_gen) (attach.rs:580-597) → late-close gen guard (broker.rs:2179-2192, unit @8416) → converge_perch_stamps clears on-disk stamps. Int coverage exists (endpoint_lifecycle.rs:241 full-chain). "Deliberately restart-durable attach stream" in the REQ title describes pre-0040 state. At dispatch: RESCOPE the REQ (verify-note + Leg A), like C3's retirement — not build-from-scratch.

**LEG A — restart-replay re-establishment = REQ-STREAM-LIFETIME-CLASS's real residual build.** ADR-0038 dispatch replay re-serves every retained opener Attach after broker restart; a ConnectionBound opener REPLAYING is a class contradiction (all conns die with the broker process — its conn cannot exist post-restart). Fix: class-scoped terminal retirement at replay — dispatcher's restart-durable classification (ADR-0038 d2) gains lifetime (NetStreamOpenReq.lifetime already on the wire); replay filter terminal-retires ConnectionBound openers instead of re-serving. Precise, NOT ADR-0038's rejected clear-table-on-restart (Durable/brain-swap untouched). RED-first rig: kill rc opener raw → restart broker → pre-fix seat + CONTROLLED re-established (survival re-verified post-W1, [[daemon-lifecycle-progress]] W2 section) → post-fix clear. Runs UPSTREAM of REQ-ATTACH-IDEMPOTENT-REPLAY's key (endpoint/session, by, conn, gen, from_seq) — filtered openers never reach serve.

**LEG B — live no-FIN half-open = REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT (D4c oracle, 2026-06-19 ruling): presence-driven FIN synthesis.** On transport conn death at the target (QUIC conn.closed() — nethost.rs:1000-1021 already retires stream rows + appends PRESENCE_DISCONNECTED), synthesize NetStreamEof toward serve workers of streams transported on that conn → serve_attach's EXISTING EOF arm = the one exit shape (W3 principle: one transaction, one exit, any entry); every validated identity guard rides free.
- ⚠ **INSTRUMENT FIRST — discriminating observable:** does conn.closed() row-retirement ALREADY surface NetStreamEof to serve_attach? inject_control_wedge's "clears on NEITHER platform at ANY window" was measured on in-process loopback (true half-open UNSTAGEABLE there, its own comment @2300-2305) — real-QUIC netsplit NEVER measured; QUIC idle-timeout may already deliver the heal. Rig = twohost netsplit leg, sibling probe first (clean-FIN detach must be visible before the no-FIN absence claim counts, [[absence-needs-sibling-probe]]).
- **HONEST SCOPE LIMIT:** presence synthesis closes DEAD-conn-no-FIN only. Alive-but-WEDGED controller (conn open, writer parked — KH 7.15 A2 idle-wedged leg) is invisible to presence; needs active-probe/timeout policy; may stay deferred with the boundary stated in the KH.

**LEG C — taxonomy ruling (the banked "both REQs on one mechanism"):** teardown authority = opener-declared class + transport liveness, enforced at the THREE places decision 6 couldn't see (conn-exit sweep = shipped; restart replay = Leg A; transport death sans FIN = Leg B). Target rows stay class-blind (accepted mirror rows Durable @nethost.rs:1028-1043, loopback peer row Durable @1763-1771 — correct: class is opener-side). Two classes suffice, no third. Doc vehicle: ADR-0040 amendment. KH 7.29 + 7.15 wording updates ride.

**⭐ LEG A CARRIER RULING (2026-07-22, W2 underway — todlando @fb93700 activation+rescope, JIT committed, self-clearing):** todlando's additive-lifetime-on-NetStreamOpenerReply proposal REJECTED as first resort — REACH: the re-served row is the ACCEPTED row (nethost.rs:1030-1041 registers Durable class-blind by design) or loopback PEER row (1763-1771, same) — the classed opener-side row is NOT the served row, so the reply-field carrier reads Durable for exactly the leaked population. PLACEMENT if owed: BROKER-side filter at enumeration/claim (B2 stream_infos precedent), no wire change; sharp key = transport-conn liveness, not class. SEQUENCE: RED rig FIRST — Leg A's premise may be FALSIFIED post-d6 (sweep + conn.closed() retirement @1000-1021 + ADR-0038 retired-exclusion may already close replay); rig-can't-red = SUCCESS → rescope like Leg 0; red transcript names the row → picks the filter key. NO wire change in any branch without a red transcript naming the unfiltered row. Two sub-questions the red settles: does eligibility-only "retire" actually exclude on the hit path; did the sweep run for the kill shape. Hazard REQ activated int-only (todlando, ratified — impl/unit pre-activation would pre-fail outcome (a)). **✅ LEG A COLOR: GREEN — PREMISE FALSIFIED (todlando @713f5d0, doyle-verified + ratified 2026-07-22). Restart-replay population closed BY COMPOSITION: conn-exit sweep → FIN → finished=true → finished_row_is_terminal(Attach,true) retire-on-sight (dispatch.rs:418, worker path ~888). NO wire field, NO broker filter built. REQ-STREAM-LIFETIME-CLASS rescoped ["int"] only (deactivate-not-pre-fail); rig = restart_replay_lifetime.rs (residual ordering: gen dies FIRST then opener dies RAW; anti-vacuity = 3 preconditions + sibling-probe-FIRST-else-BROKEN-RIG).** ⭐⭐ **FINDING 1 (non-obvious, doyle code-verified): ADR-0038 d1 holds through its LIFECYCLE-STATE arm at the WORKER, NOT its enumeration arm — finished rows STILL ENUMERATE claimable; two designers in one week reasoned from the non-load-bearing arm (incl. doyle's B2-shaped filter proposal).** Sub-Q2: sweep ran for the kill shape. ⚠ SCOPE: measurement is LOOPBACK; cross-node conn.closed() arm NOT exercised — stays Leg B. Rig defect en route: net-streams applies ADR-0040 d2 server-side filter → opener rows invisible to EVERY brain client → rig reads table in-process off Arc<Broker>. **AMENDMENT RULING: one landing after Leg B; carries (1) composition record + loopback scope limit, (2) d1-arms clarification cross-ref w/ named reasoning hazard, (3) carrier reach fact as rejected-alternative, (4) rig-defect note. Leg B next: twohost real-QUIC, sibling clean-FIN probe first.**

**⏳ LEG B (2026-07-22): code-grounded PREDICTION banked (todlando @da5952e, stopped at JIT stop-point, doyle ruled OPTION 1 = twohost run).** Chain predicts (a)-already-heals: closed-watcher retain (nethost.rs:1000-1021) removes ROWS but the serving seat lives on the Arc'd StreamLog the QUIC pump also holds; pump finishes on torn end EXACTLY like clean (explicit design comment); finish (nethost.rs:519) enqueues EOF envelope to installed seat; removal breaks only NEW lookups (dispatch absorbs as terminal-pre-serve). Marked PREDICTION-not-measurement. Real unmeasured thing = the WINDOW (QUIC idle-timeout time-to-EOF) + removal/EOF ordering race — loopback structurally can't stage it (why the old "clears on NEITHER platform" line was read stronger than it is). **RULING RIDERS: sibling clean-FIN probe first (its number = baseline); run-window discipline both boxes (no CI in flight; do NOT disturb hertz's held viewer; census before/after, reap own children by path only); measure time-to-EOF at serving worker + time-to-stamp-clear + record (don't chase) any removal/EOF race window; outcome map pre-registered ((a)+number = KH-wording-with-NUMBERS + coverage, synthesis NOT built; LARGE number = report, no invented threshold — keepalive-vs-synthesis is a design call on the number; ambiguous/racy = trace to doyle); twohost plumbing fights back = STOP and report. KH boundary sentence in every branch: dead-conn-no-FIN only, 7.15 A2 wedged-alive deferred with boundary STATED.**

**Leg B STAGING (todlando @c1f962a, doyle-ratified + 1 flag): NetHost::close() vs BARE DROP = one code path one boolean both legs (close flushes QUIC CLOSE → prompt closed-watcher = clean-FIN baseline; bare drop + process alive = silent half-open → idle timeout). Ruled out: in-process fake death (runbook lesson), process kill (dead socket draws ICMP = not silent), conn_blackhole reuse (that's the 7.15 A2 LOCAL wedge — the repo's 'blackhole' is the deferred boundary case, not this). Numbers PRE-REGISTERED: MESH_KEEP_ALIVE=15s / MESH_MAX_IDLE=60s (endpoint.rs) → predict clean ~ms, torn ~60s. ⚠ DOYLE FLAG: bare drop normally closes the UDP socket → ICMP unreachable = leaked signal; rig must PROVE port still bound post-drop (hold/leak endpoint + assert), and DISCRIMINATOR pre-registered: torn ≪60s (~15-20s/instant) = suspect ICMP path, investigate staging BEFORE banking. Route: [twohost] tagged push, both self-hosted runners, box quiet confirmed.**
**⭐ STAGING v2 (@8f95717, doyle flag CONFIRMED-CAUGHT + local-first APPROVED): bare drop DOES close the socket (NetHost owns runtime+Arc<NetEndpoint>) → ICMP leak — todlando corrected-not-restated. CHOSEN: FREEZE THE RUNTIME (park both worker_threads(2) @nethost.rs:1238 in non-yielding loops → QUIC never polled, port stays bound BY CONSTRUCTION, no ICMP/elevation) = sleeping-laptop shape. Boundary made explicit: frozen-runtime = QUIC-STACK-DEAD (transport-death class, idle timeout fires); NOT 7.15 A2 alive-but-wedged (transport answers, app parked, no timeout ever) — rig must not be read as covering A2. ⭐⭐ TWO-LOOPBACKS FINDING: in-process duplex (net_dial_loopback/open_loopback_pair, RecvHalf::Loopback — no QUIC no idle timeout; what Leg A's rig used AND why the old characterization looked unstageable) vs BindScope::Loopback + real net_dial = REAL QUIC over 127.0.0.1 w/ real MESH_MAX_IDLE. → no-FIN half-open stageable LOCALLY. PLAN: local-first discovery (both numbers + race note), twohost = LAN confirmation. Orders: Leg A scope text corrected loopback→in-process-duplex before it fossilizes; two-loopbacks naming = amendment payload item 5; discriminator/numbers/census carry over.**

**✅ LEG B MEASURED + RULED (todlando @e1d3add, doyle 2026-07-23): outcome (a) — heal EXISTS, idle-timeout-bound. Clean FIN seat-release 120/115ms; torn 65057/65047ms (MESH_MAX_IDLE 60s + PTO slack; >60s satisfies the no-leaked-signal discriminator). Staging honest: freeze_transport_for_test parks the 2-worker net runtime, port-bound asserted both legs, reactor canary (0-15ms→2006ms + fresh pre-assert) = instrument's own sibling probe; venue = real QUIC via redispatch D1 two-broker dial, NOT in-process duplex. ⭐ Run-1 rig defect caught by stop condition: DISPATCH:<id> emits ONLY on the NON-Served arm — ANOMALY channel, not progress log; re-aimed at seat release (worker's own act), anomaly line kept as separate baseline-not-failure-path assert (7.55 lesson, new costume). Race recorded-derived not probed (row leaves table 0-30ms BEFORE seat release → new-subscribe window sees no-such-stream while seat serves). Boundary in rig+KH 7.15+7.29: QUIC-STACK-DEAD peer only; A2 alive-but-wedged never times out, stays deferred. Heavy-at-birth (nextest+ci HEAVY, 65s IS the measurement). ⭐ DOYLE DESIGN DECISION ON THE NUMBER: ACCEPT AS DOCUMENTED — no presence-FIN synthesis, no keepalive tune. Grounds: (1) latch ≠ lockout (different-conn attach = fix-6 successor takes seat immediately); (2) residual harm = stale status truth ~65s after rare abrupt transport death, self-healing on a bound; (3) a second teardown mechanism to shave 60s off a converging case = where bugs live; (4) keepalive = global QUIC tradeoff, not spent on a cosmetic minute. KH 7.15 carries 65s as fact + takeover sentence; revisit lever = keepalive tune (product-feel call), named not built. Hazard REQ stays int-only. TWOHOST: HOLD → folds into gate-battery window after Leg C (one occupancy window; KH says measured-local, LAN confirmation outstanding). LEG C: GO — one landing, 7-item payload (Leg A composition + duplex correction; d1-arms clarification; carrier reach rejected-alt; net-streams-filter rig note; two-loopbacks split; Leg B record + boundary narrowing + accept-decision w/ grounds; anomaly-channel clause todlando's call). Then fixture-bin fold-in → PR → doyle gate (battery once vs final tree + twohost confirm same window).**

**✅ LEG C LANDED + RATIFIED (todlando @2e4dfd1 ADR-0040 Amendment 1 all-7-payload + @28d8ae5 fixture-bin 4-sites→one current_exe resolver, PROVEN DISCRIMINATING via old-path-emptied probe; Amendment-1 numbering + purely-additive both ratified).**
**⭐⭐ W2 RIDER (operator-ordered 2026-07-22 evening, doyle root-caused SAME NIGHT, spec dispatched): STALE-UPDATE-NOTICE FIELD BUG — todlando woken ~19:54 by "v0.41.0 available" on the already-0.41.0 box.** CHAIN (all verified): row f15d837b…:50114 minted by **GRAVITY-NVDA-PC (BIGNET, runs PRE-0.40.0 spt)** via the LEGACY producer = subnet-scoped + NULL coalesce_key (scoped/keyed producer shipped 0.40.0); replicated fleet-wide; **modern catch-up dismissal (pump/update.rs dismiss_staged_notif_if_caught_up) keys ONLY on NOTIF_KEY_UPDATE_STAGED → structurally blind to keyless rows → undismissable-by-machinery on every current node**; surfaced to todlando + Librarian (Athenaeum-Library), would boundary-resurface to every endpoint once each forever. Diagnostic trail: no UPDATE_STAGED:74 breadcrumb either log gen (mint wasn't local); row anatomy scope=subnet/key=NULL = pre-0.40.0 shape; created 20:09:30 surfaced 20:09:37 seen=[Librarian,todlando]; node hex → GRAVITY via endpoint list. **MITIGATED: doyle dismissed the row manually (replicates subnet-wide) — bleeding stopped.** FIX = W2 rider (folds into v0.42.0 per operator): REQ-NOTIF-UPDATE-ROW-VERSION-RETIRE — version-grounded retirement sweep beside the key path (from_id=spt-update + body version parses + advertised<=running → dismiss; unparseable/newer NEVER dismissed; ⚠ NEVER key on kind alone — consent kind shared w/ grants act-gate; 4-leg RED-first rig + notifsync dismissed-merge assert). **KNOWN LIMIT (stated not hidden): origin's one-time cross-node wake at mint = legacy node's live first-fire, unpreventable from our side — operator lever = update/retire GRAVITY-NVDA-PC (will re-mint at EVERY future release until then).**

**Ruling ADOPTED + corrections folded into W2-JIT.md @555e04d (todlando self-verified the reach fact, then self-cleared pre-rig). ⭐ Transferable error-form named (todlando, banked): never design a descriptor/carrier for a row before asking WHICH ROW the code under test actually serves — the 7.55 vantage-point mistake (probe point can't see the console it names) in a new seam, missed one day after applying it correctly. Falsifiable-premise framing now guards BOTH legs in the JIT (non-repro reads as SUCCESS+rescope, not broken rig).**

**FIXTURE-BIN FOLD-IN (REQ-XTASK-SPT-BIN-TARGET-DIR class, 2nd instance):** 4 hardcode sites ignore CARGO_TARGET_DIR — attach_resize_capture.rs:193-198 (player_bin), capture_vehicle_fidelity.rs:59, rc_console_newline_presentation.rs:53, resize_console_mode_integrity.rs:48-52 (probe_bin); all CARGO_MANIFEST_DIR-pop-derived target/debug. Fix shape: current_exe()-derived sibling resolution (twohost.rs:437 seed_notify_shell precedent — honors CARGO_TARGET_DIR structurally; CARGO_BIN_EXE_* unavailable cross-package), one shared helper. ⚠ Explore-agent gotcha recorded: first map ran on a 16-commits-stale checkout and reported these helpers nonexistent — verify tree currency before trusting a negative.