695	/// [`viewer_writer`]. Write the `initial` ring-replay batch (advancing the resume
696	/// cursor on each success), then drain live [`CtrlMsg`]s with a BLOCKING socket
697	/// write each — so a backed-up controller blocks ONLY this thread, never the
698	/// session drain (which used to write the controller inline under the log lock).
699	/// A failed write (dead operator) ends the thread; the drain's next bounded
700	/// send then sees `Disconnected` and detaches the row. `Output` advances
701	/// `delivered_through`; `Control` (Displaced/…) does not.
702	// [impl->REQ-HAZARD-INJECT-CONTROL-COEXIST]
703	fn controller_writer(
704	    sid: u64,
705	    send: SharedSend,
706	    initial: Vec<(u64, Vec<u8>)>,
707	    rx: Receiver<CtrlMsg>,
708	    delivered_through: Arc<AtomicU64>,
709	) {
710	    for (seq, bytes) in &initial {
711	        let frame = output_envelope(sid, *seq, bytes);
712	        let ok = match send.lock() {
713	            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
714	            Err(_) => false,
715	        };
716	        if !ok {
717	            return;
718	        }
719	        advance_delivered(&delivered_through, *seq);
720	    }
721	    while let Ok(msg) = rx.recv() {
722	        let (frame, seq) = match &msg {
723	            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
724	            CtrlMsg::Control(frame) => (frame, None),
725	        };
726	        let ok = match send.lock() {
727	            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
728	            Err(_) => false,
729	        };
730	        if !ok {
731	            return;
732	        }
733	        if let Some(seq) = seq {
734	            advance_delivered(&delivered_through, seq);
735	        }
736	    }
737	}
738
739	/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present
740	/// ONLY when the spawn carried a `translation_binary` path; absent → the session
741	/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.
742	///
743	/// Holds the shared inject floor (the controller-input buffer, also touched by
744	/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
745	/// (events serialize — never two uncommitted sequences interleaved), the shared
746	/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
747	/// while the worker drives an event), and the FAULT flag the worker raises when a
748	/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
749	/// the session reverts to raw inject and stops pinging.
750	struct Translation {
751	    /// Controller-input buffer + inject-floor flag, shared with `dispatch_input`.
752	    floor: Arc<Mutex<InjectFloor>>,
753	    /// Queue one inbound `<EVENT>` envelope to the inject worker. Events QUEUE here
754	    /// and are driven one-at-a-time so two sequences never interleave.

/// [`viewer_writer`]. Write the `initial` ring-replay batch (advancing the resume
/// cursor on each success), then drain live [`CtrlMsg`]s with a BLOCKING socket
/// write each — so a backed-up controller blocks ONLY this thread, never the
/// session drain (which used to write the controller inline under the log lock).
/// A failed write (dead operator) ends the thread; the drain's next bounded
/// send then sees `Disconnected` and detaches the row. `Output` advances
/// `delivered_through`; `Control` (Displaced/…) does not.
// [impl->REQ-HAZARD-INJECT-CONTROL-COEXIST]
fn controller_writer(
    sid: u64,
    send: SharedSend,
    initial: Vec<(u64, Vec<u8>)>,
    rx: Receiver<CtrlMsg>,
    delivered_through: Arc<AtomicU64>,
) {
    for (seq, bytes) in &initial {
        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() {
        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
/// ONLY when the spawn carried a `translation_binary` path; absent → the session
/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.
///
/// Holds the shared inject floor (the controller-input buffer, also touched by
/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
/// (events serialize — never two uncommitted sequences interleaved), the shared
/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
/// while the worker drives an event), and the FAULT flag the worker raises when a
/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session reverts to raw inject and stops pinging.
struct Translation {
    /// Controller-input buffer + inject-floor flag, shared with `dispatch_input`.
    floor: Arc<Mutex<InjectFloor>>,
    /// Queue one inbound `<EVENT>` envelope to the inject worker. Events QUEUE here
    /// and are driven one-at-a-time so two sequences never interleave.


check your ongoing Monitor tool call's output


560	        loop {
561	            let env = self.read_frame_until(deadline)?;
562	            match env.kind.as_str() {
563	                KIND_OUTPUT => {
564	                    let ev: OutputEvent = serde_json::from_value(env.payload)
565	                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
566	                    // A **resume-mode** brain (the per-session map is populated by
567	                    // `resume_sessions`) routes EVERY session through the map —
568	                    // including a freshly-spawned or first-seen one, which seeds
569	                    // itself at its first `seq` via `entry().or_insert` (D4-2b,
570	                    // doyle amendment 5: this closes the mixed-mode gap where N≥2
571	                    // fresh spawns would otherwise share the single `next_seq` and
572	                    // silently drop the later sessions' seq-0 output). The broker's
573	                    // `Mutex<OutputLog>`-held wire order guarantees first-sight of a
574	                    // new session IS seq 0, so the seed == 0 for a spawn; a resumed
575	                    // session already holds its `resume_seq` seed (the `entry`
576	                    // returns it, so the boundary-dedup still applies). Discipline:
577	                    // **dedup-below + snap-above, NO reject-gap** — drop an
578	                    // already-delivered seq (the at-least-once boundary re-send),
579	                    // accept-and-snap everything `>= cursor` (the contiguous case
580	                    // AND a post-eviction ring-floor jump, which the broker's
581	                    // non-reordering replay makes the only legitimate forward jump).
582	                    if !self.session_cursors.is_empty() {
583	                        let cursor = self.session_cursors.entry(ev.session_id).or_insert(ev.seq);
584	                        if ev.seq < *cursor {
585	                            continue; // replay duplicate / boundary re-send — dedup
586	                        }
587	                        *cursor = ev.seq + 1;
588	                        let bytes = decode_bytes(&ev.data_b64)
589	                            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
590	                        return Ok(BrokerEvent::Output {
591	                            session_id: ev.session_id,
592	                            seq: ev.seq,
593	                            bytes,
594	                        });
595	                    }
596	                    // Legacy single-session path (map empty — a pure spawn/drive
597	                    // seat): strict accept-next / drop-dup / reject-gap on the one
598	                    // `next_seq` cursor, byte-for-byte unchanged.
599	                    if ev.seq < self.next_seq {
600	                        continue; // already processed — a replay duplicate; dedup
601	                    }
602	                    if ev.seq > self.next_seq {
603	                        return Err(io::Error::new(
604	                            io::ErrorKind::InvalidData,
605	                            format!("output gap: got seq {} want {}", ev.seq, self.next_seq),
606	                        ));
607	                    }
608	                    self.next_seq = ev.seq + 1;
609	                    let bytes = decode_bytes(&ev.data_b64)
610	                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
611	                    return Ok(BrokerEvent::Output {
612	                        session_id: ev.session_id,
613	                        seq: ev.seq,
614	                        bytes,
615	                    });
616	                }
617	                KIND_SPAWNED => {
618	                    let ev: Spawned = serde_json::from_value(env.payload)
619	                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
620	                    return Ok(BrokerEvent::Spawned {
621	                        session_id: ev.session_id,
622	                        pid: ev.pid,
623	                    });
624	                }
625	                KIND_EXIT => {
626	                    let ev: ExitEvent = serde_json::from_value(env.payload)
627	                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
628	                    return Ok(BrokerEvent::Exit {
629	                        session_id: ev.session_id,

        loop {
            let env = self.read_frame_until(deadline)?;
            match env.kind.as_str() {
                KIND_OUTPUT => {
                    let ev: OutputEvent = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    // A **resume-mode** brain (the per-session map is populated by
                    // `resume_sessions`) routes EVERY session through the map —
                    // including a freshly-spawned or first-seen one, which seeds
                    // itself at its first `seq` via `entry().or_insert` (D4-2b,
                    // doyle amendment 5: this closes the mixed-mode gap where N≥2
                    // fresh spawns would otherwise share the single `next_seq` and
                    // silently drop the later sessions' seq-0 output). The broker's
                    // `Mutex<OutputLog>`-held wire order guarantees first-sight of a
                    // new session IS seq 0, so the seed == 0 for a spawn; a resumed
                    // session already holds its `resume_seq` seed (the `entry`
                    // returns it, so the boundary-dedup still applies). Discipline:
                    // **dedup-below + snap-above, NO reject-gap** — drop an
                    // already-delivered seq (the at-least-once boundary re-send),
                    // accept-and-snap everything `>= cursor` (the contiguous case
                    // AND a post-eviction ring-floor jump, which the broker's
                    // non-reordering replay makes the only legitimate forward jump).
                    if !self.session_cursors.is_empty() {
                        let cursor = self.session_cursors.entry(ev.session_id).or_insert(ev.seq);
                        if ev.seq < *cursor {
                            continue; // replay duplicate / boundary re-send — dedup
                        }
                        *cursor = ev.seq + 1;
                        let bytes = decode_bytes(&ev.data_b64)
                            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                        return Ok(BrokerEvent::Output {
                            session_id: ev.session_id,
                            seq: ev.seq,
                            bytes,
                        });
                    }
                    // Legacy single-session path (map empty — a pure spawn/drive
                    // seat): strict accept-next / drop-dup / reject-gap on the one
                    // `next_seq` cursor, byte-for-byte unchanged.
                    if ev.seq < self.next_seq {
                        continue; // already processed — a replay duplicate; dedup
                    }
                    if ev.seq > self.next_seq {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidData,
                            format!("output gap: got seq {} want {}", ev.seq, self.next_seq),
                        ));
                    }
                    self.next_seq = ev.seq + 1;
                    let bytes = decode_bytes(&ev.data_b64)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Output {
                        session_id: ev.session_id,
                        seq: ev.seq,
                        bytes,
                    });
                }
                KIND_SPAWNED => {
                    let ev: Spawned = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Spawned {
                        session_id: ev.session_id,
                        pid: ev.pid,
                    });
                }
                KIND_EXIT => {
                    let ev: ExitEvent = serde_json::from_value(env.payload)
                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                    return Ok(BrokerEvent::Exit {
                        session_id: ev.session_id,


172:/// advances `delivered_through` on each successful write, and it is never evicted
174:struct ControllerSink {
209:    controller: Option<ControllerSink>,
214:    controller_epoch: u64,
246:    delivered_through: Arc<AtomicU64>,
272:            controller: None,
273:            controller_epoch: 0,
278:            delivered_through: Arc::new(AtomicU64::new(0)),
329:            epoch: self.controller_epoch,
345:    /// `delivered_through` cursor as it writes (D4-1: only on a successful write,
347:    /// ends its writer; bumping `controller_epoch` makes a stale deadline-evict a
354:        self.controller_epoch += 1;
365:        let dt = Arc::clone(&self.delivered_through);
393:        if self.controller_epoch == epoch {
526:    /// owned by `sub`. Leaves `delivered_through` intact (D4-1). Re-stamps
592:    fn delivered_through(&self) -> u64 {
593:        self.delivered_through.load(Ordering::Acquire)
624:/// chunk and advances the authoritative `delivered_through` on a successful
676:/// Advance the shared `delivered_through` cursor to `seq + 1`, monotonically
701:/// `delivered_through`; `Control` (Displaced/…) does not.
708:    delivered_through: Arc<AtomicU64>,
719:        advance_delivered(&delivered_through, *seq);
734:            advance_delivered(&delivered_through, seq);
1025:pub struct Broker {
1302:                                    resume_seq: log.delivered_through(),


Now I understand defect B: `controller_writer` flushes its `initial` batch (broker.rs:710-720) unconditionally — no epoch check. `controller_epoch` is a plain `u64` (line 214), so the writer can't see it. Fix #2 needs it shared.

Reading the remaining seams: ControllerSink, subscribe, attach flow, the flaky test, resume_sessions.


168	/// Symmetric with [`ViewerSink`] — a dedicated writer thread does the BLOCKING
169	/// socket write so a backed-up controller can never stall the session drain
170	/// thread (the v0.12.x wedge: the controller write was inline on the drain,
171	/// holding `Mutex<OutputLog>`). Unlike a viewer it is AUTHORITATIVE: its writer
172	/// advances `delivered_through` on each successful write, and it is never evicted
173	/// on a single full `try_send` — only after [`CONTROLLER_WRITE_DEADLINE`].
174	struct ControllerSink {
175	    /// The bounded live-frame queue to the controller's writer thread.
176	    tx: SyncSender<CtrlMsg>,
177	    /// The controller's IPC send half — kept for identity matching on detach and
178	    /// for the `exit`-frame fan-out ([`OutputLog::all_sinks`]).
179	    send: SharedSend,
180	    /// The controller's identity: `Some(node)` = a remote operator, `None` =
181	    /// local (the brain's spawn-reader / resume). Ownership keys on this.
182	    by: Option<String>,
183	    /// The writer thread handle (held for ownership; exits when `tx` drops — the
184	    /// drain's bounded `send_timeout` then sees `Disconnected` and detaches — or
185	    /// when a socket write fails).
186	    _writer: JoinHandle<()>,
187	}
188
189	/// A bounded, sequenced output log for one session — the gapless-resume buffer
190	/// AND the W2.5 controller/viewer fan-out hub (REQ-RCVIEW-1).
191	///
192	/// The session's single drain thread is the only producer ([`OutputLog::append`]).
193	/// Output fans out to ONE interactive `controller` (the authoritative,
194	/// cursor-advancing, blocking path) plus ANY NUMBER of read-only `viewers` (each
195	/// an isolated bounded queue + writer thread, evicted on overflow). Every
196	/// controller send and viewer `try_send` happens while the enclosing
197	/// `Mutex<OutputLog>` is held, so frames reach each sink in strict `seq` order.
198	struct OutputLog {
199	    session_id: u64,
200	    next_seq: u64,
201	    ring: VecDeque<(u64, Vec<u8>)>,
202	    cap_chunks: usize,
203	    /// The single interactive controller's isolated sink (W1: a dedicated writer
204	    /// thread + bounded channel + authoritative cursor). `None` ⇒ undriven. The
205	    /// controller's identity (`Some(node)` remote / `None` local) lives in
206	    /// [`ControllerSink::by`] and keys ownership — a same-`by` re-subscribe
207	    /// silently re-takes (no `Displaced`, the gate-#7 self-kick guard); only a
208	    /// DIFFERENT remote's `Take` displaces.
209	    controller: Option<ControllerSink>,
210	    /// Monotonic controller generation. Bumped on every [`become_controller`];
211	    /// the drain's deadline-evict ([`mark_controller_gone`]) carries the epoch it
212	    /// observed and only detaches if it still matches — so a slow-controller
213	    /// timeout can never unseat a NEWER controller that attached meanwhile.
214	    controller_epoch: u64,
215	    /// The read-only viewers, keyed by a broker-minted viewer id.
216	    viewers: HashMap<u64, ViewerSink>,
217	    /// Monotonic viewer-id source.

/// Symmetric with [`ViewerSink`] — a dedicated writer thread does the BLOCKING
/// socket write so a backed-up controller can never stall the session drain
/// thread (the v0.12.x wedge: the controller write was inline on the drain,
/// holding `Mutex<OutputLog>`). Unlike a viewer it is AUTHORITATIVE: its writer
/// advances `delivered_through` on each successful write, and it is never evicted
/// on a single full `try_send` — only after [`CONTROLLER_WRITE_DEADLINE`].
struct ControllerSink {
    /// The bounded live-frame queue to the controller's writer thread.
    tx: SyncSender<CtrlMsg>,
    /// The controller's IPC send half — kept for identity matching on detach and
    /// for the `exit`-frame fan-out ([`OutputLog::all_sinks`]).
    send: SharedSend,
    /// The controller's identity: `Some(node)` = a remote operator, `None` =
    /// local (the brain's spawn-reader / resume). Ownership keys on this.
    by: Option<String>,
    /// The writer thread handle (held for ownership; exits when `tx` drops — the
    /// drain's bounded `send_timeout` then sees `Disconnected` and detaches — or
    /// when a socket write fails).
    _writer: JoinHandle<()>,
}

/// A bounded, sequenced output log for one session — the gapless-resume buffer
/// AND the W2.5 controller/viewer fan-out hub (REQ-RCVIEW-1).
///
/// The session's single drain thread is the only producer ([`OutputLog::append`]).
/// Output fans out to ONE interactive `controller` (the authoritative,
/// cursor-advancing, blocking path) plus ANY NUMBER of read-only `viewers` (each
/// an isolated bounded queue + writer thread, evicted on overflow). Every
/// controller send and viewer `try_send` happens while the enclosing
/// `Mutex<OutputLog>` is held, so frames reach each sink in strict `seq` order.
struct OutputLog {
    session_id: u64,
    next_seq: u64,
    ring: VecDeque<(u64, Vec<u8>)>,
    cap_chunks: usize,
    /// The single interactive controller's isolated sink (W1: a dedicated writer
    /// thread + bounded channel + authoritative cursor). `None` ⇒ undriven. The
    /// controller's identity (`Some(node)` remote / `None` local) lives in
    /// [`ControllerSink::by`] and keys ownership — a same-`by` re-subscribe
    /// silently re-takes (no `Displaced`, the gate-#7 self-kick guard); only a
    /// DIFFERENT remote's `Take` displaces.
    controller: Option<ControllerSink>,
    /// Monotonic controller generation. Bumped on every [`become_controller`];
    /// the drain's deadline-evict ([`mark_controller_gone`]) carries the epoch it
    /// observed and only detaches if it still matches — so a slow-controller
    /// timeout can never unseat a NEWER controller that attached meanwhile.
    controller_epoch: u64,
    /// The read-only viewers, keyed by a broker-minted viewer id.
    viewers: HashMap<u64, ViewerSink>,
    /// Monotonic viewer-id source.


Grep

fn subscribe|fn resume_sessions|fn attach_as|fn serve_attach


src\brain.rs:795:    pub fn attach_as(
src\brain.rs:824:    pub fn resume_sessions(&mut self) -> io::Result<Vec<u64>> {
src\brain.rs:1126:    fn subscribe(&mut self, session_id: u64, from_seq: u64) -> io::Result<()> {
src\brain.rs:1146:    fn subscribe_with(
src\attach.rs:107:pub fn serve_attach(
src\digesthub.rs:144:    fn subscribe(&self, id: &str) -> (Digest, u64, Receiver<DigestUpdate>) {
src\digesthub.rs:153:    pub fn subscriber_count(&self, id: &str) -> usize {
src\digesthub.rs:494:    fn subscribe_returns_current_base() {
src\msg.rs:745:pub fn subscribed_envelope(session_id: u64, outcome: SubscribeOutcome) -> Envelope {
src\nethost.rs:1212:    pub(crate) fn subscribe_stream(
src\nethost.rs:1247:    pub(crate) fn subscribe_presence(&self, sub: SharedSend, from_seq: u64) {


790	    /// loop) reads to learn whether it became controller / viewer / was refused.
791	    /// Like [`Brain::attach`] this is a second reader, not a logic handoff (the
792	    /// generation is untouched).
793	    ///
794	    /// [`serve_attach`]: crate::attach::serve_attach
795	    pub fn attach_as(
796	        &mut self,
797	        session_id: u64,
798	        from_seq: u64,
799	        intent: AttachIntent,
800	        by: Option<&str>,
801	    ) -> io::Result<()> {
802	        self.session_id = Some(session_id);
803	        self.next_seq = from_seq;
804	        self.subscribe_with(session_id, from_seq, intent, by.map(str::to_string))
805	    }
806
807	    /// Cold-start multi-session resume (restoration D4-2, ADR-0018 Q6): query the
808	    /// broker for **every** hosted session and re-attach **each** in resume mode
809	    /// from the broker's per-session delivered cursor (`resume_seq`). This is the
810	    /// production replacement for the retired single-session `BrainState` handoff
811	    /// frame — a brain the supervisor respawns (crash *or* update) reconstructs
812	    /// all session continuity by querying the persistent side, never a brain→brain
813	    /// message. Returns the ids re-attached (empty when the broker hosts none —
814	    /// the supervised daemon brain's no-op-today case).
815	    ///
816	    /// Each session is seeded into [`Self::session_cursors`] at its `resume_seq`,
817	    /// so [`Brain::read_event`] dedups the broker's at-least-once boundary re-send
818	    /// and accepts a post-eviction ring-floor jump (no reject-gap). Output is
819	    /// at-least-once; input/effects stay exactly-once via the broker-owned
820	    /// `EffectJournal`. The generation is **not** touched — this is a cold start
821	    /// (generation custody is the broker's, ADR-0018 V2), not a `handoff`.
822	    // [impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]
823	    // [impl->REQ-DAEMON-2]
824	    pub fn resume_sessions(&mut self) -> io::Result<Vec<u64>> {
825	        let reply = self.sessions()?;
826	        let mut resumed = Vec::with_capacity(reply.sessions.len());
827	        for info in reply.sessions {
828	            // Seed the dedup cursor at the broker's resume point BEFORE the
829	            // subscribe replay starts arriving, then re-attach from there.
830	            self.session_cursors
831	                .insert(info.session_id, info.resume_seq);
832	            self.subscribe(info.session_id, info.resume_seq)?;
833	            resumed.push(info.session_id);
834	        }
835	        Ok(resumed)
836	    }
837
838	    /// The per-session resume cursor (the next seq still needed for a session
839	    /// re-attached by [`Brain::resume_sessions`]) — observability / tests.
840	    pub fn session_cursor(&self, session_id: u64) -> Option<u64> {
841	        self.session_cursors.get(&session_id).copied()
842	    }
843
844
845	    /// Query the broker-owned net endpoint's status (D4a): node id, dialable
846	    /// address, conn count — or `enabled: false` on a net-less broker. Reads
847	    /// until the reply (consuming interleaved events like [`Brain::spawn_session`]).
848	    // [impl->REQ-DAEMON-2]
849	    pub fn net_status(&mut self) -> io::Result<NetStatusReply> {

    /// loop) reads to learn whether it became controller / viewer / was refused.
    /// Like [`Brain::attach`] this is a second reader, not a logic handoff (the
    /// generation is untouched).
    ///
    /// [`serve_attach`]: crate::attach::serve_attach
    pub fn attach_as(
        &mut self,
        session_id: u64,
        from_seq: u64,
        intent: AttachIntent,
        by: Option<&str>,
    ) -> io::Result<()> {
        self.session_id = Some(session_id);
        self.next_seq = from_seq;
        self.subscribe_with(session_id, from_seq, intent, by.map(str::to_string))
    }

    /// Cold-start multi-session resume (restoration D4-2, ADR-0018 Q6): query the
    /// broker for **every** hosted session and re-attach **each** in resume mode
    /// from the broker's per-session delivered cursor (`resume_seq`). This is the
    /// production replacement for the retired single-session `BrainState` handoff
    /// frame — a brain the supervisor respawns (crash *or* update) reconstructs
    /// all session continuity by querying the persistent side, never a brain→brain
    /// message. Returns the ids re-attached (empty when the broker hosts none —
    /// the supervised daemon brain's no-op-today case).
    ///
    /// Each session is seeded into [`Self::session_cursors`] at its `resume_seq`,
    /// so [`Brain::read_event`] dedups the broker's at-least-once boundary re-send
    /// and accepts a post-eviction ring-floor jump (no reject-gap). Output is
    /// at-least-once; input/effects stay exactly-once via the broker-owned
    /// `EffectJournal`. The generation is **not** touched — this is a cold start
    /// (generation custody is the broker's, ADR-0018 V2), not a `handoff`.
    // [impl->REQ-HAZARD-BROKER-PROCESS-ISOLATION]
    // [impl->REQ-DAEMON-2]
    pub fn resume_sessions(&mut self) -> io::Result<Vec<u64>> {
        let reply = self.sessions()?;
        let mut resumed = Vec::with_capacity(reply.sessions.len());
        for info in reply.sessions {
            // Seed the dedup cursor at the broker's resume point BEFORE the
            // subscribe replay starts arriving, then re-attach from there.
            self.session_cursors
                .insert(info.session_id, info.resume_seq);
            self.subscribe(info.session_id, info.resume_seq)?;
            resumed.push(info.session_id);
        }
        Ok(resumed)
    }

    /// The per-session resume cursor (the next seq still needed for a session
    /// re-attached by [`Brain::resume_sessions`]) — observability / tests.
    pub fn session_cursor(&self, session_id: u64) -> Option<u64> {
        self.session_cursors.get(&session_id).copied()
    }

    /// Query the broker-owned net endpoint's status (D4a): node id, dialable
    /// address, conn count — or `enabled: false` on a net-less broker. Reads
    /// until the reply (consuming interleaved events like [`Brain::spawn_session`]).
    // [impl->REQ-DAEMON-2]
    pub fn net_status(&mut self) -> io::Result<NetStatusReply> {


1120	    }
1121
1122	    /// Re-attach to a session's output from `from_seq` (handoff resume) — a
1123	    /// LOCAL re-take (intent `Control`, identity `None`). A `None` identity never
1124	    /// displaces a remote controller (the broker falls it back to a silent
1125	    /// viewer) — the gate-#7 local-resume guard.
1126	    fn subscribe(&mut self, session_id: u64, from_seq: u64) -> io::Result<()> {
1127	        self.subscribe_with(session_id, from_seq, AttachIntent::Control, None)
1128	    }
1129
1130	    /// Release this connection's controller/viewer role on `session_id`
1131	    /// (REQ-RCVIEW-1) — the explicit detach a `serve_attach` worker sends when
1132	    /// the viewport ends so the broker clears `driven_by` / `viewer_count`
1133	    /// promptly. Best-effort: a dropped connection clears the role anyway.
1134	    pub fn detach_session(&mut self, session_id: u64) -> io::Result<()> {
1135	        self.send(
1136	            KIND_UNSUBSCRIBE,
1137	            serde_json::to_value(UnsubscribeReq { session_id })
1138	                .expect("UnsubscribeReq serializes"),
1139	        )
1140	    }
1141
1142	    /// The role-aware subscribe (REQ-RCVIEW-1): `intent` selects viewer/control/
1143	    /// take, `by` carries the operator identity (`Some(node)` remote, `None`
1144	    /// local). The broker replies the resolved [`SubscribeOutcome`] as a
1145	    /// [`BrokerEvent::Subscribed`].
1146	    fn subscribe_with(
1147	        &mut self,
1148	        session_id: u64,
1149	        from_seq: u64,
1150	        intent: AttachIntent,
1151	        by: Option<String>,
1152	    ) -> io::Result<()> {
1153	        self.send(
1154	            KIND_SUBSCRIBE,
1155	            serde_json::to_value(SubscribeReq {
1156	                session_id,
1157	                from_seq,
1158	                intent,
1159	                by,
1160	            })
1161	            .expect("SubscribeReq serializes"),
1162	        )
1163	    }
1164
1165	    fn send(&mut self, kind: &str, payload: serde_json::Value) -> io::Result<()> {
1166	        let env = Envelope::new(kind, payload);
1167	        match &mut self.conn {
1168	            BrainConn::Whole(stream) => write_frame(stream, &env),
1169	            BrainConn::Split { send, .. } => write_frame(send, &env),
1170	        }
1171	    }
1172
1173	    /// Read one framed [`Envelope`], bounded by an optional wall-clock
1174	    /// `deadline`. The carrier dispatch (REQ-HAZARD-PUMP-IPC-DEADLINE):
1175	    ///
1176	    /// - [`BrainConn::Whole`] — the ordinary blocking client: a direct
1177	    ///   [`read_frame`]; `deadline` is irrelevant (a whole brain never sets
1178	    ///   `io_timeout`, so callers always pass `None` here).
1179	    /// - [`BrainConn::Split`] — the pump carrier: the reader thread already owns
1180	    ///   the blocking read, so a bounded read is just [`Receiver::recv_timeout`]
1181	    ///   on the remaining budget. A `Timeout` surfaces [`io::ErrorKind::TimedOut`]
1182	    ///   — the poison signal the pump escalates to a supervised restart (NEVER a
1183	    ///   per-peer retry). A `Disconnected` channel means the reader thread exited
1184	    ///   (the conn hung up); surface it as `UnexpectedEof`.
1185	    // [impl->REQ-HAZARD-PUMP-IPC-DEADLINE]
1186	    fn read_frame_until(&mut self, deadline: Option<Instant>) -> io::Result<Envelope> {
1187	        match &mut self.conn {
1188	            BrainConn::Whole(stream) => read_frame(stream),
1189	            BrainConn::Split { frames, .. } => match deadline {

    }

    /// Re-attach to a session's output from `from_seq` (handoff resume) — a
    /// LOCAL re-take (intent `Control`, identity `None`). A `None` identity never
    /// displaces a remote controller (the broker falls it back to a silent
    /// viewer) — the gate-#7 local-resume guard.
    fn subscribe(&mut self, session_id: u64, from_seq: u64) -> io::Result<()> {
        self.subscribe_with(session_id, from_seq, AttachIntent::Control, None)
    }

    /// Release this connection's controller/viewer role on `session_id`
    /// (REQ-RCVIEW-1) — the explicit detach a `serve_attach` worker sends when
    /// the viewport ends so the broker clears `driven_by` / `viewer_count`
    /// promptly. Best-effort: a dropped connection clears the role anyway.
    pub fn detach_session(&mut self, session_id: u64) -> io::Result<()> {
        self.send(
            KIND_UNSUBSCRIBE,
            serde_json::to_value(UnsubscribeReq { session_id })
                .expect("UnsubscribeReq serializes"),
        )
    }

    /// The role-aware subscribe (REQ-RCVIEW-1): `intent` selects viewer/control/
    /// take, `by` carries the operator identity (`Some(node)` remote, `None`
    /// local). The broker replies the resolved [`SubscribeOutcome`] as a
    /// [`BrokerEvent::Subscribed`].
    fn subscribe_with(
        &mut self,
        session_id: u64,
        from_seq: u64,
        intent: AttachIntent,
        by: Option<String>,
    ) -> io::Result<()> {
        self.send(
            KIND_SUBSCRIBE,
            serde_json::to_value(SubscribeReq {
                session_id,
                from_seq,
                intent,
                by,
            })
            .expect("SubscribeReq serializes"),
        )
    }

    fn send(&mut self, kind: &str, payload: serde_json::Value) -> io::Result<()> {
        let env = Envelope::new(kind, payload);
        match &mut self.conn {
            BrainConn::Whole(stream) => write_frame(stream, &env),
            BrainConn::Split { send, .. } => write_frame(send, &env),
        }
    }

    /// Read one framed [`Envelope`], bounded by an optional wall-clock
    /// `deadline`. The carrier dispatch (REQ-HAZARD-PUMP-IPC-DEADLINE):
    ///
    /// - [`BrainConn::Whole`] — the ordinary blocking client: a direct
    ///   [`read_frame`]; `deadline` is irrelevant (a whole brain never sets
    ///   `io_timeout`, so callers always pass `None` here).
    /// - [`BrainConn::Split`] — the pump carrier: the reader thread already owns
    ///   the blocking read, so a bounded read is just [`Receiver::recv_timeout`]
    ///   on the remaining budget. A `Timeout` surfaces [`io::ErrorKind::TimedOut`]
    ///   — the poison signal the pump escalates to a supervised restart (NEVER a
    ///   per-peer retry). A `Disconnected` channel means the reader thread exited
    ///   (the conn hung up); surface it as `UnexpectedEof`.
    // [impl->REQ-HAZARD-PUMP-IPC-DEADLINE]
    fn read_frame_until(&mut self, deadline: Option<Instant>) -> io::Result<Envelope> {
        match &mut self.conn {
            BrainConn::Whole(stream) => read_frame(stream),
            BrainConn::Split { frames, .. } => match deadline {


Reading the flaky test + serve_attach to ground the fix interactions.


660	    let (stream_a, origin) = wait_for_stream(&mut life1);
661	    life1
662	        .net_stream_subscribe(stream_a, 0)
663	        .expect("life1 subscribe");
664	    let mut decoder = AttachDecoder::new();
665	    let mut sent: Vec<u8> = Vec::new(); // bytes life 1 pumped onto the wire
666	    'life1: loop {
667	        match life1.read_event().expect("life1 event") {
668	            BrokerEvent::NetStreamData {
669	                stream_id, bytes, ..
670	            } if stream_id == stream_a => {
671	                for rec in decoder.push(&bytes) {
672	                    match rec {
673	                        AttachRecord::Request {
674	                            session_id,
675	                            from_seq,
676	                            ..
677	                        } => {
678	                            life1.attach(session_id, from_seq).expect("attach");
679	                        }
680	                        AttachRecord::Input { data_b64, op_id } => {
681	                            let b = decode_bytes(&data_b64).expect("b64");
682	                            life1.send_effect(op_id, &b).expect("journaled input");
683	                        }
684	                        _ => {}
685	                    }
686	                }
687	            }
688	            BrokerEvent::Output { seq, bytes, .. } => {
689	                let line = ndjson::encode_line(&AttachRecord::Output {
690	                    seq,
691	                    data_b64: spt_daemon::msg::encode_bytes(&bytes),
692	                });
693	                life1
694	                    .net_stream_send(stream_a, &line, None, false)
695	                    .expect("output send");
696	                sent.extend_from_slice(&bytes);
697	                if count(&sent, b"MARKER_ONE") >= 1 {
698	                    break 'life1; // crash with the marker on the wire
699	                }
700	            }
701	            BrokerEvent::Error { message } => panic!("life1 broker error: {message}"),
702	            _ => {}
703	        }
704	    }
705	    let state = life1.snapshot();
706	    drop(life1); // the crash — broker A, session, stream, journal all survive
707
708	    // Into the DEAD window: B types the second marker (buffered by A's broker).
709	    send_attach_input(&mut operator, stream_b, b"MARKER_TWO\r", 11).expect("input 2");
710
711	    // Life 2: handoff, then re-serve the SAME stream from seq 0 — the
712	    // worst-case replay. serve_attach re-handles the replayed Request (gate +
713	    // re-attach from 0, so every output record re-transmits — B's render
714	    // cursor drops the duplicates), replays MARKER_ONE's input (PTY-write
715	    // journal dedups op 10 — typed once), and pumps the dead window's
716	    // MARKER_TWO.
717	    let mut life2 = Brain::handoff(&name_a, state, 2).expect("handoff");
718	    let origin2 = origin.clone();
719	    let serve_broker = name_a.clone();
720	    let server = thread::spawn(move || {
721	        let outcome = serve_attach(&mut life2, &serve_broker, stream_a, "ling", &origin2, 0)
722	            .expect("re-serve");
723	        (outcome, life2)
724	    });
725
726	    // B renders through the dead-window marker, then detaches.
727	    let mut op_decoder = AttachDecoder::new();
728	    let (mut cursor, mut viewport) = (0u64, Vec::new());
729	    render_until(
730	        &mut operator,
731	        stream_b,
732	        &mut op_decoder,
733	        &mut cursor,
734	        &mut viewport,
735	        b"MARKER_TWO",
736	    );
737	    operator
738	        .net_stream_send(stream_b, &[], None, true)
739	        .expect("detach");
740	    let (outcome, mut life2) = server.join().expect("serve thread");
741	    assert_eq!(outcome, AttachServeOutcome::Detached);
742
743	    // Exactly-once, stated against ground truth: replay the session's OWN
744	    // output log locally from seq 0 with the same stop rule — the remote
745	    // viewport must be BYTE-IDENTICAL through the first dead-window marker
746	    // (an echoing child legitimately repeats a typed marker; what must never
747	    // differ is wire vs local). A dup or gap from the restart/replay would
748	    // break the prefix equality. Compared as prefixes-through-the-needle
749	    // because each side's *overshoot* past the marker is honest chunking

    let (stream_a, origin) = wait_for_stream(&mut life1);
    life1
        .net_stream_subscribe(stream_a, 0)
        .expect("life1 subscribe");
    let mut decoder = AttachDecoder::new();
    let mut sent: Vec<u8> = Vec::new(); // bytes life 1 pumped onto the wire
    'life1: loop {
        match life1.read_event().expect("life1 event") {
            BrokerEvent::NetStreamData {
                stream_id, bytes, ..
            } if stream_id == stream_a => {
                for rec in decoder.push(&bytes) {
                    match rec {
                        AttachRecord::Request {
                            session_id,
                            from_seq,
                            ..
                        } => {
                            life1.attach(session_id, from_seq).expect("attach");
                        }
                        AttachRecord::Input { data_b64, op_id } => {
                            let b = decode_bytes(&data_b64).expect("b64");
                            life1.send_effect(op_id, &b).expect("journaled input");
                        }
                        _ => {}
                    }
                }
            }
            BrokerEvent::Output { seq, bytes, .. } => {
                let line = ndjson::encode_line(&AttachRecord::Output {
                    seq,
                    data_b64: spt_daemon::msg::encode_bytes(&bytes),
                });
                life1
                    .net_stream_send(stream_a, &line, None, false)
                    .expect("output send");
                sent.extend_from_slice(&bytes);
                if count(&sent, b"MARKER_ONE") >= 1 {
                    break 'life1; // crash with the marker on the wire
                }
            }
            BrokerEvent::Error { message } => panic!("life1 broker error: {message}"),
            _ => {}
        }
    }
    let state = life1.snapshot();
    drop(life1); // the crash — broker A, session, stream, journal all survive

    // Into the DEAD window: B types the second marker (buffered by A's broker).
    send_attach_input(&mut operator, stream_b, b"MARKER_TWO\r", 11).expect("input 2");

    // Life 2: handoff, then re-serve the SAME stream from seq 0 — the
    // worst-case replay. serve_attach re-handles the replayed Request (gate +
    // re-attach from 0, so every output record re-transmits — B's render
    // cursor drops the duplicates), replays MARKER_ONE's input (PTY-write
    // journal dedups op 10 — typed once), and pumps the dead window's
    // MARKER_TWO.
    let mut life2 = Brain::handoff(&name_a, state, 2).expect("handoff");
    let origin2 = origin.clone();
    let serve_broker = name_a.clone();
    let server = thread::spawn(move || {
        let outcome = serve_attach(&mut life2, &serve_broker, stream_a, "ling", &origin2, 0)
            .expect("re-serve");
        (outcome, life2)
    });

    // B renders through the dead-window marker, then detaches.
    let mut op_decoder = AttachDecoder::new();
    let (mut cursor, mut viewport) = (0u64, Vec::new());
    render_until(
        &mut operator,
        stream_b,
        &mut op_decoder,
        &mut cursor,
        &mut viewport,
        b"MARKER_TWO",
    );
    operator
        .net_stream_send(stream_b, &[], None, true)
        .expect("detach");
    let (outcome, mut life2) = server.join().expect("serve thread");
    assert_eq!(outcome, AttachServeOutcome::Detached);

    // Exactly-once, stated against ground truth: replay the session's OWN
    // output log locally from seq 0 with the same stop rule — the remote
    // viewport must be BYTE-IDENTICAL through the first dead-window marker
    // (an echoing child legitimately repeats a typed marker; what must never
    // differ is wire vs local). A dup or gap from the restart/replay would
    // break the prefix equality. Compared as prefixes-through-the-needle
    // because each side's *overshoot* past the marker is honest chunking


107	pub fn serve_attach(
108	    brain: &mut Brain,
109	    broker_name: &str,
110	    stream_id: u64,
111	    endpoint: &str,
112	    origin_node: &str,
113	    net_from_seq: u64,
114	) -> io::Result<AttachServeOutcome> {
115	    brain.net_stream_subscribe(stream_id, net_from_seq)?;
116	    // TWO-CONN SPLIT (REQ-HAZARD-ENDPOINT-RUN-ATTACH-OUTPUT): the wire FORWARDS
117	    // (session Output / Size / Exit / Displaced / detach-finish onto the operator
118	    // stream) ride a SEPARATE broker connection from `brain`. `brain` RECEIVES
119	    // session output (broker→serve) — including the controller's synchronous,
120	    // inline, multi-frame ring replay (`become_controller`), which holds the
121	    // broker's single-threaded per-conn handler for the whole replay. If the
122	    // forwards rode `brain` too, that stuck handler could not read them →
123	    // serve→broker backs up while broker→serve replay backs up → mutual IPC
124	    // backpressure DEADLOCK, and the operator receives ZERO bytes (the bug: an
125	    // attach to an ALREADY-PRODUCING endpoint-run harness, non-empty ring). The
126	    // two directions on independent single-threaded handlers cannot block each
127	    // other. `send_stream` is stream_id-keyed (conn-agnostic), so forwarding from
128	    // a second conn is correct. Order is preserved: this one serve thread reads
129	    // every Output in seq order on `brain` and forwards it in that same order on
130	    // `wire`. (DEEPER ROOT, post-v0.12.1 follow-up: the CONTROLLER replay is a
131	    // SYNCHRONOUS inline write, while VIEWERS use the async dedicated-writer-
132	    // thread + bounded-channel pattern (`add_viewer`/`viewer_writer`); unifying
133	    // the controller onto that async path would remove the inline-replay block at
134	    // the source, but it touches the authoritative cursor-advancing path.)
135	    let mut wire = Brain::cold_start(broker_name, now_ms())?;
136	    let mut decoder = AttachDecoder::new();
137	    let mut attached = false;
138	    // The Request's intent — gates wire input (a viewer's keystrokes never reach
139	    // the PTY) independent of the broker's later role reply (no IPC-ordering
140	    // race on the input gate).
141	    let mut intent = AttachIntent::Control;
142	    // The broker-confirmed role (arrives as `Subscribed`). `None` until then:
143	    // controller input that races ahead of the reply is BUFFERED, not dropped
144	    // (the replayed-input-after-restart case) and not leaked to the PTY before
145	    // we know we control the session (the busy-refuse case).
146	    let mut role: Option<ServeRole> = None;
147	    let mut pending_input: Vec<(u64, Vec<u8>)> = Vec::new();
148	    // The attached session — captured from the Request so the terminal paths can
149	    // explicitly release this worker's role (clearing driven_by / viewer_count
150	    // promptly, not only on connection drop).
151	    let mut attached_session: Option<u64> = None;
152
153	    loop {
154	        match brain.read_event()? {
155	            // ── Wire → session: requests, input, resize. ───────────────────
156	            BrokerEvent::NetStreamData {
157	                stream_id: sid,
158	                bytes,
159	                ..
160	            } if sid == stream_id => {
161	                for rec in decoder.push(&bytes) {
162	                    match rec {
163	                        AttachRecord::Request {
164	                            session_id,
165	                            from_seq,
166	                            intent: req_intent,
167	                        } => {
168	                            // Unsolicited wire inbound — the ADR-0009 gate, under
169	                            // the transport-proven origin. Viewing is gated
170	                            // IDENTICALLY to driving in v1 (REQ-RCVIEW-1, Gap A:
171	                            // watching reveals full session contents).
172	                            if access_check(endpoint, origin_node, InboundClass::Unsolicited)
173	                                == AccessDecision::Deny
174	                            {
175	                                wire.net_stream_send(stream_id, &[], None, true)?;
176	                                return Ok(AttachServeOutcome::Refused);
177	                            }
178	                            intent = req_intent;
179	                            attached_session = Some(session_id);
180	                            // Role-aware subscribe (REQ-RCVIEW-1): the broker
181	                            // resolves controller / viewer / busy / take by the
182	                            // intent + this handshake-proven origin, and replies a
183	                            // `Subscribed`. driven_by / viewer_count are now
184	                            // BROKER-owned (single writer — resolves the
185	                            // clear-race), so this loop no longer stamps them.
186	                            brain.attach_as(session_id, from_seq, req_intent, Some(origin_node))?;
187	                            attached = true;
188	                        }
189	                        AttachRecord::Input { data_b64, op_id }
190	                            if attached && intent != AttachIntent::Viewer =>
191	                        {
192	                            let bytes = decode_bytes(&data_b64)
193	                                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
194	                            match role {
195	                                // Confirmed controller — exactly-once at the PTY
196	                                // (the operator-minted id rides the B5 journal). NO-ACK
197	                                // (REQ-HAZARD-INPUT-ACK-BACKPRESSURE): this path is
198	                                // fire-and-forward (the ack is discarded here) and can
199	                                // flood N input frames on this one conn; an acked write
200	                                // back would fill the return direction and deadlock the
201	                                // broker's per-conn handler. Dedup still holds at the
202	                                // broker (op_id applied-set).
203	                                // [impl->REQ-HAZARD-INPUT-ACK-BACKPRESSURE]
204	                                Some(ServeRole::Controller) => {
205	                                    brain.send_effect_no_ack(op_id, &bytes)?
206	                                }

pub fn serve_attach(
    brain: &mut Brain,
    broker_name: &str,
    stream_id: u64,
    endpoint: &str,
    origin_node: &str,
    net_from_seq: u64,
) -> io::Result<AttachServeOutcome> {
    brain.net_stream_subscribe(stream_id, net_from_seq)?;
    // TWO-CONN SPLIT (REQ-HAZARD-ENDPOINT-RUN-ATTACH-OUTPUT): the wire FORWARDS
    // (session Output / Size / Exit / Displaced / detach-finish onto the operator
    // stream) ride a SEPARATE broker connection from `brain`. `brain` RECEIVES
    // session output (broker→serve) — including the controller's synchronous,
    // inline, multi-frame ring replay (`become_controller`), which holds the
    // broker's single-threaded per-conn handler for the whole replay. If the
    // forwards rode `brain` too, that stuck handler could not read them →
    // serve→broker backs up while broker→serve replay backs up → mutual IPC
    // backpressure DEADLOCK, and the operator receives ZERO bytes (the bug: an
    // attach to an ALREADY-PRODUCING endpoint-run harness, non-empty ring). The
    // two directions on independent single-threaded handlers cannot block each
    // other. `send_stream` is stream_id-keyed (conn-agnostic), so forwarding from
    // a second conn is correct. Order is preserved: this one serve thread reads
    // every Output in seq order on `brain` and forwards it in that same order on
    // `wire`. (DEEPER ROOT, post-v0.12.1 follow-up: the CONTROLLER replay is a
    // SYNCHRONOUS inline write, while VIEWERS use the async dedicated-writer-
    // thread + bounded-channel pattern (`add_viewer`/`viewer_writer`); unifying
    // the controller onto that async path would remove the inline-replay block at
    // the source, but it touches the authoritative cursor-advancing path.)
    let mut wire = Brain::cold_start(broker_name, now_ms())?;
    let mut decoder = AttachDecoder::new();
    let mut attached = false;
    // The Request's intent — gates wire input (a viewer's keystrokes never reach
    // the PTY) independent of the broker's later role reply (no IPC-ordering
    // race on the input gate).
    let mut intent = AttachIntent::Control;
    // The broker-confirmed role (arrives as `Subscribed`). `None` until then:
    // controller input that races ahead of the reply is BUFFERED, not dropped
    // (the replayed-input-after-restart case) and not leaked to the PTY before
    // we know we control the session (the busy-refuse case).
    let mut role: Option<ServeRole> = None;
    let mut pending_input: Vec<(u64, Vec<u8>)> = Vec::new();
    // The attached session — captured from the Request so the terminal paths can
    // explicitly release this worker's role (clearing driven_by / viewer_count
    // promptly, not only on connection drop).
    let mut attached_session: Option<u64> = None;

    loop {
        match brain.read_event()? {
            // ── Wire → session: requests, input, resize. ───────────────────
            BrokerEvent::NetStreamData {
                stream_id: sid,
                bytes,
                ..
            } if sid == stream_id => {
                for rec in decoder.push(&bytes) {
                    match rec {
                        AttachRecord::Request {
                            session_id,
                            from_seq,
                            intent: req_intent,
                        } => {
                            // Unsolicited wire inbound — the ADR-0009 gate, under
                            // the transport-proven origin. Viewing is gated
                            // IDENTICALLY to driving in v1 (REQ-RCVIEW-1, Gap A:
                            // watching reveals full session contents).
                            if access_check(endpoint, origin_node, InboundClass::Unsolicited)
                                == AccessDecision::Deny
                            {
                                wire.net_stream_send(stream_id, &[], None, true)?;
                                return Ok(AttachServeOutcome::Refused);
                            }
                            intent = req_intent;
                            attached_session = Some(session_id);
                            // Role-aware subscribe (REQ-RCVIEW-1): the broker
                            // resolves controller / viewer / busy / take by the
                            // intent + this handshake-proven origin, and replies a
                            // `Subscribed`. driven_by / viewer_count are now
                            // BROKER-owned (single writer — resolves the
                            // clear-race), so this loop no longer stamps them.
                            brain.attach_as(session_id, from_seq, req_intent, Some(origin_node))?;
                            attached = true;
                        }
                        AttachRecord::Input { data_b64, op_id }
                            if attached && intent != AttachIntent::Viewer =>
                        {
                            let bytes = decode_bytes(&data_b64)
                                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
                            match role {
                                // Confirmed controller — exactly-once at the PTY
                                // (the operator-minted id rides the B5 journal). NO-ACK
                                // (REQ-HAZARD-INPUT-ACK-BACKPRESSURE): this path is
                                // fire-and-forward (the ack is discarded here) and can
                                // flood N input frames on this one conn; an acked write
                                // back would fill the return direction and deadlock the
                                // broker's per-conn handler. Dedup still holds at the
                                // broker (op_id applied-set).
                                // [impl->REQ-HAZARD-INPUT-ACK-BACKPRESSURE]
                                Some(ServeRole::Controller) => {
                                    brain.send_effect_no_ack(op_id, &bytes)?
                                }


Now I have the full picture. The fix interactions are clear. Let me see the deadline-evict epoch usage (line 329) and confirm atomics are imported.


300	        self.next_seq += 1;
301	        self.ring.push_back((seq, bytes.to_vec()));
302	        while self.ring.len() > self.cap_chunks {
303	            self.ring.pop_front();
304	        }
305	        let frame = output_envelope(self.session_id, seq, bytes);
306	        // Viewers: non-blocking fan-out; a full / disconnected queue = eviction.
307	        if !self.viewers.is_empty() {
308	            let mut evicted: Vec<u64> = Vec::new();
309	            for (vid, sink) in self.viewers.iter() {
310	                if viewer_send_evicts(&sink.tx.try_send(frame.clone())) {
311	                    evicted.push(*vid);
312	                }
313	            }
314	            if !evicted.is_empty() {
315	                for vid in evicted {
316	                    self.viewers.remove(&vid);
317	                }
318	                self.stamp_viewer_count();
319	            }
320	        }
321	        // Controller: hand the frame to the caller for an off-lock, bounded send
322	        // into the controller writer thread's channel (which does the blocking
323	        // socket write + advances the authoritative cursor). The drain never
324	        // blocks under the log lock again.
325	        self.controller.as_ref().map(|c| ControllerJob {
326	            tx: c.tx.clone(),
327	            seq,
328	            frame,
329	            epoch: self.controller_epoch,
330	        })
331	    }

        self.next_seq += 1;
        self.ring.push_back((seq, bytes.to_vec()));
        while self.ring.len() > self.cap_chunks {
            self.ring.pop_front();
        }
        let frame = output_envelope(self.session_id, seq, bytes);
        // Viewers: non-blocking fan-out; a full / disconnected queue = eviction.
        if !self.viewers.is_empty() {
            let mut evicted: Vec<u64> = Vec::new();
            for (vid, sink) in self.viewers.iter() {
                if viewer_send_evicts(&sink.tx.try_send(frame.clone())) {
                    evicted.push(*vid);
                }
            }
            if !evicted.is_empty() {
                for vid in evicted {
                    self.viewers.remove(&vid);
                }
                self.stamp_viewer_count();
            }
        }
        // Controller: hand the frame to the caller for an off-lock, bounded send
        // into the controller writer thread's channel (which does the blocking
        // socket write + advances the authoritative cursor). The drain never
        // blocks under the log lock again.
        self.controller.as_ref().map(|c| ControllerJob {
            tx: c.tx.clone(),
            seq,
            frame,
            epoch: self.controller_epoch,
        })
    }


36:use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
243:    /// W1: now an `Arc<AtomicU64>` shared with the controller's writer thread,
246:    delivered_through: Arc<AtomicU64>,
278:            delivered_through: Arc::new(AtomicU64::new(0)),
593:        self.delivered_through.load(Ordering::Acquire)
679:fn advance_delivered(dt: &AtomicU64, seq: u64) {
680:    let mut cur = dt.load(Ordering::Acquire);
686:        match dt.compare_exchange_weak(cur, next, Ordering::AcqRel, Ordering::Acquire) {
708:    delivered_through: Arc<AtomicU64>,
862:                if self.backpressure.swap(false, Ordering::AcqRel) {
869:                if !self.backpressure.swap(true, Ordering::AcqRel) {
1015:    faulted.store(true, Ordering::Relaxed);
1028:    next_id: AtomicU64,
1092:            next_id: AtomicU64::new(1),
1379:        let id = self.next_id.fetch_add(1, Ordering::Relaxed);

[Showing results with pagination = limit: 15]


I now have full ground truth. Key insight: **fix #1** (remove handoff's eager subscribe) eliminates writer-A in the real path; **fix #2** (epoch-gate the writer) is the broker-level hardening the keystone test exercises; **fix #3** aligns the consumer with production. All three have distinct coverage.

Let me see the REQ registry format to mint correctly (rule 3 — first).


1259	# ── P1 (operator HITL 2026-06-19, post-deploy dogfood; doyle /diagnose + design): rc paste broken (ctrl+V + right-click) ──
1260	[[requirements]]
1261	id = "REQ-RC-WIN-PASTE"
1262	title = "In an `spt rc` session neither ctrl+V nor right-click pastes (CC explicitly supports ctrl+V). ROOT (doyle /diagnose): RawGuard does only enable_raw_mode (no bracketed paste / no mouse capture / no clipboard interception); the Windows console delivers a paste as synthetic per-char KEY EVENTs (no crossterm Event::Paste), and ctrl+V translates to bare ^V forwarded to CC — but CC runs DAEMON-SIDE with NO access to the operator's LOCAL clipboard, so remote paste is fundamentally CLIENT-ORIGINATED. A multi-line paste-as-keys also becomes a \\r submit-storm. FIX (doyle design, V0.13.0-P1-RC-PASTE-DESIGN.md, cfg(windows), folds into the bug-2 event path): on a paste gesture rc reads the LOCAL clipboard + forwards a BRACKETED PASTE (ESC[200~ + content + ESC[201~); CC has bracketed-paste mode on (its TUI sets ESC[?2004h) so it treats it as a paste — content intact, no submit-storm, harness-AGNOSTIC. ctrl+V: intercept Char('v')+CONTROL in the event loop -> read_clipboard -> bracketed paste. Right-click: RawGuard also EnableMouseCapture (disables console QuickEdit + enables ENABLE_MOUSE_INPUT so right-click surfaces as Event::Mouse on legacy cmd/powershell) -> right-button -> read_clipboard -> bracketed paste; DROP all other mouse (CC has no mouse features, operator-confirmed, so capture costs nothing). read_clipboard = clipboard-win crate (cfg(windows), minimal); empty/failed = clean no-op. Content forwarded VERBATIM (literal pasted text, no per-char translation). Unix UNCHANGED (its terminal pastes natively through the byte pump). DEPENDS ON P0 (a paste chunk must not wedge the broker). (v0.13.0)"
1263	required_stages = ["doc", "impl", "unit"]  # ACTIVATED v0.13.0 P1 (todlando, last build item; doyle gates). DEPENDS ON P0 (@0675b37, landed) — a paste chunk rides P0's single-writer thread without wedging. NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent; pure pieces unit-tested). impl = (cfg(windows), folds into the bug-2 event path) `wrap_bracketed_paste` (ESC[200~ + content + ESC[201~) + ctrl+V intercept (Char('v')+CONTROL, when not detach-armed) + right-mouse paste in `spawn_stdin_reader_events` (other mouse dropped) + `read_clipboard` (clipboard-win, empty/fail = clean no-op) + RawGuard EnableMouseCapture(+DisableMouseCapture on drop). unit = bracketed framing exact + content verbatim (no translation) + ctrl+V detect + mouse classify (right-down ⇒ paste, all else ⇒ drop) + injected-reader paste builder (Some ⇒ wrapped, empty/None ⇒ no-op). doc = KNOWN-HAZARDS + CONTEXT note (rc Windows paste client-originated: ctrl+V/right-click read the LOCAL clipboard + inject a bracketed paste; Unix rides the terminal byte stream). PLANNED: impl = RawGuard EnableMouseCapture (+restore on drop) + ctrl+V/right-mouse clipboard intercept in the bug-2 event path + bracketed-paste wrap + clipboard-win read. unit = bracketed-paste framing (exact ESC[200~/ESC[201~) + gesture->paste decision (ctrl+V/right-mouse=>clipboard-wrap, other mouse=>drop, injected reader) + content verbatim. doc = KNOWN-HAZARDS + CONTEXT note (rc Windows paste client-originated; Unix rides the terminal byte stream). DEPENDS ON P0. doyle gates; operator HITL (ctrl+V AND right-click paste, multi-line, no submit-storm, no wedge). AMENDED v0.13.0 P1b (HITL re-open 2026-06-19, doyle): SCOPE narrowed to RIGHT-CLICK-ONLY for the client-originated bracketed paste — the ctrl+V interception is REMOVED (dead + redundant): Windows Terminal CONSUMES ctrl+V as its own paste accelerator (delivers only a Key kind=RELEASE, never a Press, so the Press-guarded is_ctrl_v arm could NEVER fire) AND injects the clipboard as a char-by-char KEY FLOOD spt-core cannot intercept. ctrl+V now rides WT's native paste as keystrokes (multi-line may submit-storm — acceptable per operator; bracketed fidelity = right-click). The is_ctrl_v fn + its unit are removed; impl/unit evidence relocates to the surviving right-click path (wrap_bracketed_paste/mouse_is_paste/clipboard_paste/read_clipboard/RawGuard mouse-capture + their units). The flood-no-wedge guarantee moves to REQ-HAZARD-INPUT-ACK-BACKPRESSURE (the deadlock that the ctrl+V flood exposed). Right-click bracketed paste + mouse-capture UNCHANGED."
1264
1265	# ── P1b (operator HITL re-open 2026-06-19, doyle /diagnose on the enhanced rc+broker capture): an input FLOOD deadlocks the broker via the applied-ack ──
1266	[[requirements]]
1267	id = "REQ-HAZARD-INPUT-ACK-BACKPRESSURE"
1268	title = "A FLOOD of operator input on one brain↔broker connection deadlocks the broker PERMANENTLY (entire broker — no new/existing attach; the controller stays latched because the per-conn handler can't process the detach). ROOT (doyle /diagnose, code-grounded + HITL capture, the v0.13.0 P1 ctrl+V re-open): `serve_attach` processes a whole `NetStreamData` batch of N operator `Input` records in its inner `for rec in decoder.push()` loop, calling `brain.send_effect(op_id, &bytes)` N times WITHOUT returning to `read_event()` — so the brain writes N `KIND_INPUT` frames back-to-back and drains nothing. The broker's single-threaded per-conn handler answers EACH with `send_frame(applied_envelope)` on the SAME conn (B5 exactly-once ack, KNOWN-HAZARDS 7.2). With the brain not reading, the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block too → mutual full-duplex DEADLOCK. Capture pinned it: 11 input frames, write_input 11/11 (P0 holds — the PTY write is fine), ack send START=11 / END=10 (frame #11's applied-ack never returns). Same class as the v0.12.1 L0 two-conn split. Windows Terminal's ctrl+V paste accelerator was the trigger (injects the clipboard as a char-by-char key flood) but the deadlock is generic to ANY input flood, NOT ctrl+V-specific and NOT a P0 (PTY-write) or W1 (output-drain) regression. The applied-ack is load-bearing ONLY for `shellchan` (one-at-a-time spool delivery WAITS on `BrokerEvent::Applied`); `serve_attach` DISCARDS it (the operator/rc path is fire-and-forward, op_id for dedup only, never gates on the ack). FIX (doyle-approved): CONDITIONAL ACK — `InputReq` gains `ack: bool` (serde default = true, N-1-safe: an older brain's input still acks = today's behavior). `serve_attach`'s operator path calls `send_effect_no_ack` (ack=false) → `dispatch_input` writes NO applied frame → the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` keeps `send_effect` (ack=true) and its `Applied`-wait. Exactly-once PRESERVED: the broker still dedups by (session, op_id) at the applied-set regardless of the ack. N-1 caveat: an OLD resident broker (self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent KNOWN-HAZARDS 7.9 broker-resident-wire-change class). (v0.13.0)"
1269	required_stages = ["doc", "impl", "unit", "int"]  # ACTIVATED v0.13.0 P1b (todlando, the real ship-blocker behind the ctrl+V wedge; doyle gates). doc = KNOWN-HAZARDS 7.19 (the deadlock + the N-1 broker-resident caveat). impl = InputReq.ack (serde default_true) + Brain::send_effect_no_ack (send_effect_inner shared body) + serve_attach operator path -> send_effect_no_ack + dispatch_input gates send_frame(applied) on req.ack. unit = InputReq.ack serde default=true on an absent field (N-1) + dispatch_input emits the applied frame when ack=true and NONE when ack=false + exactly-once on the no-ack path (a replayed/duplicate op_id still dedups at apply_once, no double PTY write). int = REPRO-FIRST (the keystone, the exact gate the wedge needed): a flood of N>pipe-buffer (deterministic, e.g. 64) operator input frames through serve_attach on ONE conn -> PRE-FIX deadlocks (ack-send-START-without-END, handler stops draining; a concurrent attach / the detach cannot be serviced) -> POST-FIX drains all N + the session stays live + a concurrent attach still OPENS. Real broker+brain, NO mocks. doc = KNOWN-HAZARDS entry (the deadlock + the N-1 broker-resident caveat). clippy/traceable/docs-drift."
1270
1271	# ── P1b sibling (operator HITL 2026-06-19): rc scroll broken — capture-stolen wheel must forward to the harness ──
1272	[[requirements]]
1273	id = "REQ-RC-MOUSE-FORWARD"
1274	title = "On Windows, `spt rc` must FORWARD scroll-wheel events to the harness when the harness has mouse reporting on. ROOT (operator HITL): P1's RawGuard EnableMouseCapture (added for right-click paste, REQ-RC-WIN-PASTE) makes Windows Terminal forward ALL mouse — including the scroll wheel — to rc instead of scrolling its own buffer, but the rc mouse handler dropped everything except right-button-down → scroll DIED (and WT's native scrollback is stolen by the capture). Operator ruling: keep mouse capture + right-click bracketed paste AND forward scroll to the harness. FIX (doyle design, cfg(windows), folds into the rc mouse handler): TRACK the harness's mouse-reporting mode by scanning its OUTPUT stream for the DECSET set/reset — ESC[?1000h/1002h/1003h (mouse on) + ESC[?1006h (SGR ext) and their ...l (off) — into a shared MouseMode{enabled,sgr} (pump writes from output, stdin reader reads); the scan survives a sequence SPLIT across output chunks (a bounded carry buffer). The mouse handler: right-button-DOWN -> bracketed clipboard paste (unchanged, REQ-RC-WIN-PASTE); ScrollUp/Down -> translate to an xterm SGR mouse report (ESC[<64;col+1;row+1M up / ESC[<65;..M down, 0-based crossterm -> 1-based xterm) and forward ONLY when enabled && sgr (else DROP — a legacy X10 report the harness may not parse is garbage); Moved/drag/left/middle -> DROP (scroll is the operator's need; click-forward risks garbage, no click-to-position). Unix UNCHANGED (no capture; the terminal scrolls natively). (v0.13.0)"
1275	required_stages = ["doc", "impl", "unit"]  # ACTIVATED v0.13.0 P1b (todlando, operator-ruled scroll fix bundled with the ack-deadlock; doyle gates). NO int (live console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent; the pure translator + scan unit-tested). impl = (cfg(windows)) MouseMode{enabled,sgr atomics} + MouseModeScanner (carry-buffer DECSET scan, parse_decset_private/apply_mouse_mode) fed from the pump's output-render path + scroll_dir + scroll_sgr (SGR encode) + the spawn_stdin_reader_events scroll arm (forward when enabled&&sgr) threaded via Arc<MouseMode> (pump writes / reader reads). unit = scroll_dir classify (wheel-only) + scroll_sgr EXACT xterm bytes (up=64/down=65, col+1/row+1) + DECSET scan (1000/1002/1003/1006 set&reset, combined ESC[?1002;1006h, mixed stream, and SPLIT across two feeds — the carry-buffer edge). doc = KNOWN-HAZARDS entry (rc Windows scroll forwarded as SGR when the harness reports mouse; capture is for right-click paste). doyle gates; operator HITL (scroll works in a real CC rc session; right-click still pastes)."
1276

# ── P1 (operator HITL 2026-06-19, post-deploy dogfood; doyle /diagnose + design): rc paste broken (ctrl+V + right-click) ──
[[requirements]]
id = "REQ-RC-WIN-PASTE"
title = "In an `spt rc` session neither ctrl+V nor right-click pastes (CC explicitly supports ctrl+V). ROOT (doyle /diagnose): RawGuard does only enable_raw_mode (no bracketed paste / no mouse capture / no clipboard interception); the Windows console delivers a paste as synthetic per-char KEY EVENTs (no crossterm Event::Paste), and ctrl+V translates to bare ^V forwarded to CC — but CC runs DAEMON-SIDE with NO access to the operator's LOCAL clipboard, so remote paste is fundamentally CLIENT-ORIGINATED. A multi-line paste-as-keys also becomes a \\r submit-storm. FIX (doyle design, V0.13.0-P1-RC-PASTE-DESIGN.md, cfg(windows), folds into the bug-2 event path): on a paste gesture rc reads the LOCAL clipboard + forwards a BRACKETED PASTE (ESC[200~ + content + ESC[201~); CC has bracketed-paste mode on (its TUI sets ESC[?2004h) so it treats it as a paste — content intact, no submit-storm, harness-AGNOSTIC. ctrl+V: intercept Char('v')+CONTROL in the event loop -> read_clipboard -> bracketed paste. Right-click: RawGuard also EnableMouseCapture (disables console QuickEdit + enables ENABLE_MOUSE_INPUT so right-click surfaces as Event::Mouse on legacy cmd/powershell) -> right-button -> read_clipboard -> bracketed paste; DROP all other mouse (CC has no mouse features, operator-confirmed, so capture costs nothing). read_clipboard = clipboard-win crate (cfg(windows), minimal); empty/failed = clean no-op. Content forwarded VERBATIM (literal pasted text, no per-char translation). Unix UNCHANGED (its terminal pastes natively through the byte pump). DEPENDS ON P0 (a paste chunk must not wedge the broker). (v0.13.0)"
required_stages = ["doc", "impl", "unit"]  # ACTIVATED v0.13.0 P1 (todlando, last build item; doyle gates). DEPENDS ON P0 (@0675b37, landed) — a paste chunk rides P0's single-writer thread without wedging. NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent; pure pieces unit-tested). impl = (cfg(windows), folds into the bug-2 event path) `wrap_bracketed_paste` (ESC[200~ + content + ESC[201~) + ctrl+V intercept (Char('v')+CONTROL, when not detach-armed) + right-mouse paste in `spawn_stdin_reader_events` (other mouse dropped) + `read_clipboard` (clipboard-win, empty/fail = clean no-op) + RawGuard EnableMouseCapture(+DisableMouseCapture on drop). unit = bracketed framing exact + content verbatim (no translation) + ctrl+V detect + mouse classify (right-down ⇒ paste, all else ⇒ drop) + injected-reader paste builder (Some ⇒ wrapped, empty/None ⇒ no-op). doc = KNOWN-HAZARDS + CONTEXT note (rc Windows paste client-originated: ctrl+V/right-click read the LOCAL clipboard + inject a bracketed paste; Unix rides the terminal byte stream). PLANNED: impl = RawGuard EnableMouseCapture (+restore on drop) + ctrl+V/right-mouse clipboard intercept in the bug-2 event path + bracketed-paste wrap + clipboard-win read. unit = bracketed-paste framing (exact ESC[200~/ESC[201~) + gesture->paste decision (ctrl+V/right-mouse=>clipboard-wrap, other mouse=>drop, injected reader) + content verbatim. doc = KNOWN-HAZARDS + CONTEXT note (rc Windows paste client-originated; Unix rides the terminal byte stream). DEPENDS ON P0. doyle gates; operator HITL (ctrl+V AND right-click paste, multi-line, no submit-storm, no wedge). AMENDED v0.13.0 P1b (HITL re-open 2026-06-19, doyle): SCOPE narrowed to RIGHT-CLICK-ONLY for the client-originated bracketed paste — the ctrl+V interception is REMOVED (dead + redundant): Windows Terminal CONSUMES ctrl+V as its own paste accelerator (delivers only a Key kind=RELEASE, never a Press, so the Press-guarded is_ctrl_v arm could NEVER fire) AND injects the clipboard as a char-by-char KEY FLOOD spt-core cannot intercept. ctrl+V now rides WT's native paste as keystrokes (multi-line may submit-storm — acceptable per operator; bracketed fidelity = right-click). The is_ctrl_v fn + its unit are removed; impl/unit evidence relocates to the surviving right-click path (wrap_bracketed_paste/mouse_is_paste/clipboard_paste/read_clipboard/RawGuard mouse-capture + their units). The flood-no-wedge guarantee moves to REQ-HAZARD-INPUT-ACK-BACKPRESSURE (the deadlock that the ctrl+V flood exposed). Right-click bracketed paste + mouse-capture UNCHANGED."

# ── P1b (operator HITL re-open 2026-06-19, doyle /diagnose on the enhanced rc+broker capture): an input FLOOD deadlocks the broker via the applied-ack ──
[[requirements]]
id = "REQ-HAZARD-INPUT-ACK-BACKPRESSURE"
title = "A FLOOD of operator input on one brain↔broker connection deadlocks the broker PERMANENTLY (entire broker — no new/existing attach; the controller stays latched because the per-conn handler can't process the detach). ROOT (doyle /diagnose, code-grounded + HITL capture, the v0.13.0 P1 ctrl+V re-open): `serve_attach` processes a whole `NetStreamData` batch of N operator `Input` records in its inner `for rec in decoder.push()` loop, calling `brain.send_effect(op_id, &bytes)` N times WITHOUT returning to `read_event()` — so the brain writes N `KIND_INPUT` frames back-to-back and drains nothing. The broker's single-threaded per-conn handler answers EACH with `send_frame(applied_envelope)` on the SAME conn (B5 exactly-once ack, KNOWN-HAZARDS 7.2). With the brain not reading, the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block too → mutual full-duplex DEADLOCK. Capture pinned it: 11 input frames, write_input 11/11 (P0 holds — the PTY write is fine), ack send START=11 / END=10 (frame #11's applied-ack never returns). Same class as the v0.12.1 L0 two-conn split. Windows Terminal's ctrl+V paste accelerator was the trigger (injects the clipboard as a char-by-char key flood) but the deadlock is generic to ANY input flood, NOT ctrl+V-specific and NOT a P0 (PTY-write) or W1 (output-drain) regression. The applied-ack is load-bearing ONLY for `shellchan` (one-at-a-time spool delivery WAITS on `BrokerEvent::Applied`); `serve_attach` DISCARDS it (the operator/rc path is fire-and-forward, op_id for dedup only, never gates on the ack). FIX (doyle-approved): CONDITIONAL ACK — `InputReq` gains `ack: bool` (serde default = true, N-1-safe: an older brain's input still acks = today's behavior). `serve_attach`'s operator path calls `send_effect_no_ack` (ack=false) → `dispatch_input` writes NO applied frame → the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` keeps `send_effect` (ack=true) and its `Applied`-wait. Exactly-once PRESERVED: the broker still dedups by (session, op_id) at the applied-set regardless of the ack. N-1 caveat: an OLD resident broker (self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent KNOWN-HAZARDS 7.9 broker-resident-wire-change class). (v0.13.0)"
required_stages = ["doc", "impl", "unit", "int"]  # ACTIVATED v0.13.0 P1b (todlando, the real ship-blocker behind the ctrl+V wedge; doyle gates). doc = KNOWN-HAZARDS 7.19 (the deadlock + the N-1 broker-resident caveat). impl = InputReq.ack (serde default_true) + Brain::send_effect_no_ack (send_effect_inner shared body) + serve_attach operator path -> send_effect_no_ack + dispatch_input gates send_frame(applied) on req.ack. unit = InputReq.ack serde default=true on an absent field (N-1) + dispatch_input emits the applied frame when ack=true and NONE when ack=false + exactly-once on the no-ack path (a replayed/duplicate op_id still dedups at apply_once, no double PTY write). int = REPRO-FIRST (the keystone, the exact gate the wedge needed): a flood of N>pipe-buffer (deterministic, e.g. 64) operator input frames through serve_attach on ONE conn -> PRE-FIX deadlocks (ack-send-START-without-END, handler stops draining; a concurrent attach / the detach cannot be serviced) -> POST-FIX drains all N + the session stays live + a concurrent attach still OPENS. Real broker+brain, NO mocks. doc = KNOWN-HAZARDS entry (the deadlock + the N-1 broker-resident caveat). clippy/traceable/docs-drift."

# ── P1b sibling (operator HITL 2026-06-19): rc scroll broken — capture-stolen wheel must forward to the harness ──
[[requirements]]
id = "REQ-RC-MOUSE-FORWARD"
title = "On Windows, `spt rc` must FORWARD scroll-wheel events to the harness when the harness has mouse reporting on. ROOT (operator HITL): P1's RawGuard EnableMouseCapture (added for right-click paste, REQ-RC-WIN-PASTE) makes Windows Terminal forward ALL mouse — including the scroll wheel — to rc instead of scrolling its own buffer, but the rc mouse handler dropped everything except right-button-down → scroll DIED (and WT's native scrollback is stolen by the capture). Operator ruling: keep mouse capture + right-click bracketed paste AND forward scroll to the harness. FIX (doyle design, cfg(windows), folds into the rc mouse handler): TRACK the harness's mouse-reporting mode by scanning its OUTPUT stream for the DECSET set/reset — ESC[?1000h/1002h/1003h (mouse on) + ESC[?1006h (SGR ext) and their ...l (off) — into a shared MouseMode{enabled,sgr} (pump writes from output, stdin reader reads); the scan survives a sequence SPLIT across output chunks (a bounded carry buffer). The mouse handler: right-button-DOWN -> bracketed clipboard paste (unchanged, REQ-RC-WIN-PASTE); ScrollUp/Down -> translate to an xterm SGR mouse report (ESC[<64;col+1;row+1M up / ESC[<65;..M down, 0-based crossterm -> 1-based xterm) and forward ONLY when enabled && sgr (else DROP — a legacy X10 report the harness may not parse is garbage); Moved/drag/left/middle -> DROP (scroll is the operator's need; click-forward risks garbage, no click-to-position). Unix UNCHANGED (no capture; the terminal scrolls natively). (v0.13.0)"
required_stages = ["doc", "impl", "unit"]  # ACTIVATED v0.13.0 P1b (todlando, operator-ruled scroll fix bundled with the ack-deadlock; doyle gates). NO int (live console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent; the pure translator + scan unit-tested). impl = (cfg(windows)) MouseMode{enabled,sgr atomics} + MouseModeScanner (carry-buffer DECSET scan, parse_decset_private/apply_mouse_mode) fed from the pump's output-render path + scroll_dir + scroll_sgr (SGR encode) + the spawn_stdin_reader_events scroll arm (forward when enabled&&sgr) threaded via Arc<MouseMode> (pump writes / reader reads). unit = scroll_dir classify (wheel-only) + scroll_sgr EXACT xterm bytes (up=64/down=65, col+1/row+1) + DECSET scan (1000/1002/1003/1006 set&reset, combined ESC[?1002;1006h, mixed stream, and SPLIT across two feeds — the carry-buffer edge). doc = KNOWN-HAZARDS entry (rc Windows scroll forwarded as SGR when the harness reports mouse; capture is for right-click paste). doyle gates; operator HITL (scroll works in a real CC rc session; right-click still pastes)."


<system-reminder>Warning: the file exists but is shorter than the provided offset (1276). The file has 1276 lines.</system-reminder>


File ends at 1275. Minting the REQ now (rule 3 — first), then I'll implement.


Edit

required_stages = ["doc", "impl", "unit"]  # ACTIVATED v0.13.0 P1b (todlando, operator-ruled scroll fix bundled with the ack-deadlock; doyle gates). NO int (live console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent; the pure translator + scan unit-tested). impl = (cfg(windows)) MouseMode{enabled,sgr atomics} + MouseModeScanner (carry-buffer DECSET scan, parse_decset_private/apply_mouse_mode) fed from the pump's output-render path + scroll_dir + scroll_sgr (SGR encode) + the spawn_stdin_reader_events scroll arm (forward when enabled&&sgr) threaded via Arc<MouseMode> (pump writes / reader reads). unit = scroll_dir classify (wheel-only) + scroll_sgr EXACT xterm bytes (up=64/down=65, col+1/row+1) + DECSET scan (1000/1002/1003/1006 set&reset, combined ESC[?1002;1006h, mixed stream, and SPLIT across two feeds — the carry-buffer edge). doc = KNOWN-HAZARDS entry (rc Windows scroll forwarded as SGR when the harness reports mouse; capture is for right-click paste). doyle gates; operator HITL (scroll works in a real CC rc session; right-click still pastes)."

required_stages = ["doc", "impl", "unit"]  # ACTIVATED v0.13.0 P1b (todlando, operator-ruled scroll fix bundled with the ack-deadlock; doyle gates). NO int (live console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent; the pure translator + scan unit-tested). impl = (cfg(windows)) MouseMode{enabled,sgr atomics} + MouseModeScanner (carry-buffer DECSET scan, parse_decset_private/apply_mouse_mode) fed from the pump's output-render path + scroll_dir + scroll_sgr (SGR encode) + the spawn_stdin_reader_events scroll arm (forward when enabled&&sgr) threaded via Arc<MouseMode> (pump writes / reader reads). unit = scroll_dir classify (wheel-only) + scroll_sgr EXACT xterm bytes (up=64/down=65, col+1/row+1) + DECSET scan (1000/1002/1003/1006 set&reset, combined ESC[?1002;1006h, mixed stream, and SPLIT across two feeds — the carry-buffer edge). doc = KNOWN-HAZARDS entry (rc Windows scroll forwarded as SGR when the harness reports mouse; capture is for right-click paste). doyle gates; operator HITL (scroll works in a real CC rc session; right-click still pastes)."

# ── P1c (operator-ruled root-fix before v0.13.0 ship; doyle root-cause via instrumented repro on kitsubito, 2026-06-20): controller-writer reorder on brain-restart re-serve ──
[[requirements]]
id = "REQ-HAZARD-CONTROLLER-WRITER-REORDER"
title = "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)"
required_stages = ["doc", "impl", "unit", "int"]  # ACTIVATED v0.13.0 P1c (todlando, the last v0.13.0 ship-blocker — pre-existing flaky resume race that gates green-both-runners; doyle root-caused + designed, doyle gates). doc = KNOWN-HAZARDS entry (the reorder + the single-live-writer-per-connection invariant + the two compounding defects) + CONTEXT note. impl = (1) Brain::handoff drops the eager subscribe(prior.session_id, prior.next_seq) call; (2) OutputLog.controller_epoch -> Arc<AtomicU64>, become_controller passes the new epoch + Arc::clone into controller_writer, which returns when controller_epoch != its snapshot (both the initial-batch loop and the live recv loop), become_controller/mark_controller_gone/the ControllerJob epoch read switch to atomic load/fetch_add; (3) Brain::handoff seeds session_cursors.insert(prior.session_id, prior.next_seq). unit = epoch-gated controller_writer: a superseded writer (epoch bumped) writes NOTHING from its initial batch (real broker socket, two become_controller on one connection -> only the latest writer's monotonic 0,1 reaches the wire) + handoff seeds session_cursors (session_cursor(sid) == prior.next_seq) + handoff issues no eager subscribe. int = the keystone: deterministically force two become_controller-on-one-connection (handoff-then-reserve ordering) on a real broker+brain, NO mocks -> PRE-FIX the wire reorders (got seq 1 want 0 / gap or byte-drop) -> POST-FIX the wire stays monotonic + byte-exact + the session stays live; PLUS attach_survives_target_brain_restart_exactly_once green (doyle runs it 20x isolated single-threaded timeout-wrapped on Linux/kitsubito — the deterministic RED-on-revert carrier). Full seam sweep (attach/broker/resume/brain_swap/inject_control_wedge — the shared W1 controller-writer model). clippy/traceable/docs-drift."