100	/// is being attached, for the gate. `net_from_seq` is this brain's resume
101	/// cursor on the attach stream (0 cold — also the safe post-restart choice:
102	/// the replayed `Request`/`Input` records re-gate and dedup at the PTY
103	/// journal, and re-pumped output dedups at the operator's render cursor).
104	// [impl->REQ-INST-8]
105	// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
106	// [impl->REQ-HAZARD-ENDPOINT-RUN-ATTACH-OUTPUT]
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).
197	                                Some(ServeRole::Controller) => brain.send_effect(op_id, &bytes)?,
198	                                // Outcome not yet known — buffer; flushed on a
199	                                // controller confirmation, discarded on busy/viewer.
200	                                None => pending_input.push((op_id, bytes)),
201	                                // A viewer never reaches here (intent gate).
202	                                Some(ServeRole::Viewer) => {}
203	                            }
204	                        }
205	                        AttachRecord::Resize { rows, cols }
206	                            if attached && intent != AttachIntent::Viewer =>
207	                        {
208	                            // Controller → PTY resize (controller-EXCLUSIVE; the
209	                            // broker rejects it on a viewer's stream). A viewer's
210	                            // Resize is dropped here by the intent gate too.
211	                            let _ = brain.resize(rows, cols);
212	                        }
213	                        // Input/Resize before Request, a viewer's input/resize,
214	                        // or target-direction records echoed back: noise — ignore.
215	                        _ => {}
216	                    }
217	                }
218	            }
219
220	            // ── Broker → loop: the resolved controller/viewer role. ─────────
221	            BrokerEvent::Subscribed { outcome, .. } => {
222	                use crate::msg::SubscribeOutcome as O;
223	                match outcome {
224	                    O::Controller | O::TookControl => {
225	                        role = Some(ServeRole::Controller);
226	                        // A driver attached — the wake edge (controller-only;
227	                        // idempotent on an already-active seat).
228	                        feed_rest(endpoint, crate::resting::RestEvent::Wake);
229	                        // Flush any input that raced ahead of the reply.

/// is being attached, for the gate. `net_from_seq` is this brain's resume
/// cursor on the attach stream (0 cold — also the safe post-restart choice:
/// the replayed `Request`/`Input` records re-gate and dedup at the PTY
/// journal, and re-pumped output dedups at the operator's render cursor).
// [impl->REQ-INST-8]
// [impl->REQ-HAZARD-WAN-ORIGIN-AUTH]
// [impl->REQ-HAZARD-ENDPOINT-RUN-ATTACH-OUTPUT]
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).
                                Some(ServeRole::Controller) => brain.send_effect(op_id, &bytes)?,
                                // Outcome not yet known — buffer; flushed on a
                                // controller confirmation, discarded on busy/viewer.
                                None => pending_input.push((op_id, bytes)),
                                // A viewer never reaches here (intent gate).
                                Some(ServeRole::Viewer) => {}
                            }
                        }
                        AttachRecord::Resize { rows, cols }
                            if attached && intent != AttachIntent::Viewer =>
                        {
                            // Controller → PTY resize (controller-EXCLUSIVE; the
                            // broker rejects it on a viewer's stream). A viewer's
                            // Resize is dropped here by the intent gate too.
                            let _ = brain.resize(rows, cols);
                        }
                        // Input/Resize before Request, a viewer's input/resize,
                        // or target-direction records echoed back: noise — ignore.
                        _ => {}
                    }
                }
            }

            // ── Broker → loop: the resolved controller/viewer role. ─────────
            BrokerEvent::Subscribed { outcome, .. } => {
                use crate::msg::SubscribeOutcome as O;
                match outcome {
                    O::Controller | O::TookControl => {
                        role = Some(ServeRole::Controller);
                        // A driver attached — the wake edge (controller-only;
                        // idempotent on an already-active seat).
                        feed_rest(endpoint, crate::resting::RestEvent::Wake);
                        // Flush any input that raced ahead of the reply.


Building the confirm-first diagnostic on a throwaway branch (not the gate). Creating branch.


Bash

git checkout -b v0.13.0-p1-ctrlv-diag 2>&1 | tail -2 && git --no-pager log --oneline -1

Create diagnostic branch from f7cf380


Switched to a new branch 'v0.13.0-p1-ctrlv-diag'
f7cf380 feat(rc): Windows ctrl+V + right-click paste via local clipboard → bracketed paste (REQ-RC-WIN-PASTE)


Branch ready. Instrument rc.rs: isolation gate (SPT_RC_CTRLV) + paste debug (SPT_RC_DEBUG_PASTE).


462	#[cfg(windows)]
463	fn spawn_stdin_reader_events(tx: mpsc::Sender<StdinMsg>) {
464	    use crossterm::event::{self, Event, KeyEventKind};
465	    std::thread::spawn(move || {
466	        let mut armed = false;
467	        while let Ok(ev) = event::read() {
468	            match ev {
469	                // ctrl+V paste gesture (REQ-RC-WIN-PASTE): read the LOCAL clipboard
470	                // and inject a BRACKETED paste, BEFORE the detach SM / key
471	                // translation — but only when NOT detach-armed (ctrl-b takes
472	                // precedence; ctrl+V is not the prefix). A multi-line paste lands
473	                // intact with no submit-storm; an empty/failed clipboard no-ops.
474	                Event::Key(ke) if ke.kind == KeyEventKind::Press && !armed && is_ctrl_v(&ke) => {
475	                    if let Some(bytes) = clipboard_paste(read_clipboard) {
476	                        if tx.send(StdinMsg::Bytes(bytes)).is_err() {
477	                            break;
478	                        }
479	                    }
480	                }
481	                // Right-click paste gesture (REQ-RC-WIN-PASTE): same bracketed
482	                // clipboard paste. Every OTHER mouse event is dropped — CC has no
483	                // mouse features, so capturing mouse deprives it of nothing.
484	                Event::Mouse(me) => {
485	                    if mouse_is_paste(&me) {
486	                        if let Some(bytes) = clipboard_paste(read_clipboard) {
487	                            if tx.send(StdinMsg::Bytes(bytes)).is_err() {
488	                                break;
489	                            }
490	                        }
491	                    }
492	                }
493	                Event::Key(ke) if ke.kind == KeyEventKind::Press => {
494	                    match key_event_step(&mut armed, ke) {
495	                        KeyAction::Detach => {
496	                            let _ = tx.send(StdinMsg::Detach);
497	                            return;
498	                        }
499	                        KeyAction::Forward(bytes) => {
500	                            if !bytes.is_empty() && tx.send(StdinMsg::Bytes(bytes)).is_err() {
501	                                break;
502	                            }
503	                        }
504	                        KeyAction::Swallow => {}
505	                    }
506	                }
507	                Event::Paste(s) => {
508	                    if tx.send(StdinMsg::Bytes(s.into_bytes())).is_err() {
509	                        break;
510	                    }
511	                }
512	                _ => {}
513	            }
514	        }
515	    });
516	}
517
518	/// Attach a local terminal to the broker-held session for `endpoint_id` under
519	/// `intent` (REQ-RCVIEW-1 / REQ-KICK-1): `Control` drives a free endpoint (and is
520	/// busy-refused with guidance on a controlled one), `Viewer` watches read-only,
521	/// `Take` kicks the incumbent controller (loud `Displaced` notice to them) and
522	/// takes control. Returns when the session exits, the operator detaches, or an
523	/// error occurs.

#[cfg(windows)]
fn spawn_stdin_reader_events(tx: mpsc::Sender<StdinMsg>) {
    use crossterm::event::{self, Event, KeyEventKind};
    std::thread::spawn(move || {
        let mut armed = false;
        while let Ok(ev) = event::read() {
            match ev {
                // ctrl+V paste gesture (REQ-RC-WIN-PASTE): read the LOCAL clipboard
                // and inject a BRACKETED paste, BEFORE the detach SM / key
                // translation — but only when NOT detach-armed (ctrl-b takes
                // precedence; ctrl+V is not the prefix). A multi-line paste lands
                // intact with no submit-storm; an empty/failed clipboard no-ops.
                Event::Key(ke) if ke.kind == KeyEventKind::Press && !armed && is_ctrl_v(&ke) => {
                    if let Some(bytes) = clipboard_paste(read_clipboard) {
                        if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                            break;
                        }
                    }
                }
                // Right-click paste gesture (REQ-RC-WIN-PASTE): same bracketed
                // clipboard paste. Every OTHER mouse event is dropped — CC has no
                // mouse features, so capturing mouse deprives it of nothing.
                Event::Mouse(me) => {
                    if mouse_is_paste(&me) {
                        if let Some(bytes) = clipboard_paste(read_clipboard) {
                            if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                    }
                }
                Event::Key(ke) if ke.kind == KeyEventKind::Press => {
                    match key_event_step(&mut armed, ke) {
                        KeyAction::Detach => {
                            let _ = tx.send(StdinMsg::Detach);
                            return;
                        }
                        KeyAction::Forward(bytes) => {
                            if !bytes.is_empty() && tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                        KeyAction::Swallow => {}
                    }
                }
                Event::Paste(s) => {
                    if tx.send(StdinMsg::Bytes(s.into_bytes())).is_err() {
                        break;
                    }
                }
                _ => {}
            }
        }
    });
}

/// Attach a local terminal to the broker-held session for `endpoint_id` under
/// `intent` (REQ-RCVIEW-1 / REQ-KICK-1): `Control` drives a free endpoint (and is
/// busy-refused with guidance on a controlled one), `Viewer` watches read-only,
/// `Take` kicks the incumbent controller (loud `Displaced` notice to them) and
/// takes control. Returns when the session exits, the operator detaches, or an
/// error occurs.


Edit

#[cfg(windows)]
fn spawn_stdin_reader_events(tx: mpsc::Sender<StdinMsg>) {
    use crossterm::event::{self, Event, KeyEventKind};
    std::thread::spawn(move || {
        let mut armed = false;
        while let Ok(ev) = event::read() {
            match ev {
                // ctrl+V paste gesture (REQ-RC-WIN-PASTE): read the LOCAL clipboard
                // and inject a BRACKETED paste, BEFORE the detach SM / key
                // translation — but only when NOT detach-armed (ctrl-b takes
                // precedence; ctrl+V is not the prefix). A multi-line paste lands
                // intact with no submit-storm; an empty/failed clipboard no-ops.
                Event::Key(ke) if ke.kind == KeyEventKind::Press && !armed && is_ctrl_v(&ke) => {
                    if let Some(bytes) = clipboard_paste(read_clipboard) {
                        if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                            break;
                        }
                    }
                }
                // Right-click paste gesture (REQ-RC-WIN-PASTE): same bracketed
                // clipboard paste. Every OTHER mouse event is dropped — CC has no
                // mouse features, so capturing mouse deprives it of nothing.
                Event::Mouse(me) => {
                    if mouse_is_paste(&me) {
                        if let Some(bytes) = clipboard_paste(read_clipboard) {
                            if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                    }
                }
                Event::Key(ke) if ke.kind == KeyEventKind::Press => {
                    match key_event_step(&mut armed, ke) {
                        KeyAction::Detach => {
                            let _ = tx.send(StdinMsg::Detach);
                            return;
                        }
                        KeyAction::Forward(bytes) => {
                            if !bytes.is_empty() && tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                        KeyAction::Swallow => {}
                    }
                }
                Event::Paste(s) => {
                    if tx.send(StdinMsg::Bytes(s.into_bytes())).is_err() {
                        break;
                    }
                }
                _ => {}
            }
        }
    });
}

#[cfg(windows)]
fn spawn_stdin_reader_events(tx: mpsc::Sender<StdinMsg>) {
    use crossterm::event::{self, Event, KeyEventKind};
    std::thread::spawn(move || {
        // DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_RC_CTRLV=0` disables
        // OUR ctrl+V interception (the is_ctrl_v arm), leaving Windows Terminal to
        // handle ctrl+V natively — the isolation probe (does ctrl+V STILL wedge with
        // our handler OFF?). `SPT_RC_DEBUG_PASTE` traces the gesture/clipboard path.
        let ctrlv_on = std::env::var_os("SPT_RC_CTRLV").map_or(true, |v| v != "0");
        let dbg_paste = std::env::var_os("SPT_RC_DEBUG_PASTE").is_some();
        if dbg_paste {
            eprintln!("RC_PASTE: reader up (ctrlv_on={ctrlv_on})");
        }
        // Read the clipboard with START/END trace bracketing so a missing END line
        // pins read_clipboard as the blocking call.
        let traced_paste = |label: &str| -> Option<Vec<u8>> {
            if dbg_paste {
                eprintln!("RC_PASTE: {label} arm fired");
                eprintln!("RC_PASTE: read_clipboard START");
            }
            let content = read_clipboard();
            if dbg_paste {
                eprintln!(
                    "RC_PASTE: read_clipboard END len={}",
                    content.as_ref().map_or(0, |c| c.len())
                );
            }
            let bytes = content.filter(|c| !c.is_empty()).map(|c| wrap_bracketed_paste(&c));
            if dbg_paste {
                eprintln!("RC_PASTE: {label} sent {} bytes", bytes.as_ref().map_or(0, |b| b.len()));
            }
            bytes
        };
        let mut armed = false;
        while let Ok(ev) = event::read() {
            match ev {
                // ctrl+V paste gesture (REQ-RC-WIN-PASTE): read the LOCAL clipboard
                // and inject a BRACKETED paste, BEFORE the detach SM / key
                // translation — but only when NOT detach-armed (ctrl-b takes
                // precedence; ctrl+V is not the prefix). A multi-line paste lands
                // intact with no submit-storm; an empty/failed clipboard no-ops.
                Event::Key(ke)
                    if ke.kind == KeyEventKind::Press && !armed && ctrlv_on && is_ctrl_v(&ke) =>
                {
                    if let Some(bytes) = traced_paste("CTRLV") {
                        if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                            break;
                        }
                    }
                }
                // Right-click paste gesture (REQ-RC-WIN-PASTE): same bracketed
                // clipboard paste. Every OTHER mouse event is dropped — CC has no
                // mouse features, so capturing mouse deprives it of nothing.
                Event::Mouse(me) => {
                    if mouse_is_paste(&me) {
                        if let Some(bytes) = traced_paste("RMOUSE") {
                            if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                    }
                }
                Event::Key(ke) if ke.kind == KeyEventKind::Press => {
                    if dbg_paste && is_ctrl_v(&ke) {
                        // ctrl+V reaching the GENERAL key path (our arm off / armed):
                        // it falls through to key_event_step → forwards ^V (0x16).
                        eprintln!("RC_PASTE: ctrl+V fell through to key path (ctrlv_on={ctrlv_on} armed={armed})");
                    }
                    match key_event_step(&mut armed, ke) {
                        KeyAction::Detach => {
                            let _ = tx.send(StdinMsg::Detach);
                            return;
                        }
                        KeyAction::Forward(bytes) => {
                            if !bytes.is_empty() && tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                        KeyAction::Swallow => {}
                    }
                }
                Event::Paste(s) => {
                    // WT's NATIVE paste accelerator can surface here (a real
                    // Event::Paste) — log it so we can tell our injection apart from
                    // the terminal's own paste on a ctrl+V.
                    if dbg_paste {
                        eprintln!("RC_PASTE: Event::Paste len={}", s.len());
                    }
                    if tx.send(StdinMsg::Bytes(s.into_bytes())).is_err() {
                        break;
                    }
                }
                _ => {}
            }
        }
    });
}


The file C:\Users\decid\Documents\projects\spt-core\crates\spt\src\rc.rs has been updated successfully. (file state is current in your context — no need to Read it back)

#[cfg(windows)]
fn spawn_stdin_reader_events(tx: mpsc::Sender<StdinMsg>) {
    use crossterm::event::{self, Event, KeyEventKind};
    std::thread::spawn(move || {
        let mut armed = false;
        while let Ok(ev) = event::read() {
            match ev {
                // ctrl+V paste gesture (REQ-RC-WIN-PASTE): read the LOCAL clipboard
                // and inject a BRACKETED paste, BEFORE the detach SM / key
                // translation — but only when NOT detach-armed (ctrl-b takes
                // precedence; ctrl+V is not the prefix). A multi-line paste lands
                // intact with no submit-storm; an empty/failed clipboard no-ops.
                Event::Key(ke) if ke.kind == KeyEventKind::Press && !armed && is_ctrl_v(&ke) => {
                    if let Some(bytes) = clipboard_paste(read_clipboard) {
                        if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                            break;
                        }
                    }
                }
                // Right-click paste gesture (REQ-RC-WIN-PASTE): same bracketed
                // clipboard paste. Every OTHER mouse event is dropped — CC has no
                // mouse features, so capturing mouse deprives it of nothing.
                Event::Mouse(me) => {
                    if mouse_is_paste(&me) {
                        if let Some(bytes) = clipboard_paste(read_clipboard) {
                            if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                    }
                }
                Event::Key(ke) if ke.kind == KeyEventKind::Press => {
                    match key_event_step(&mut armed, ke) {
                        KeyAction::Detach => {
                            let _ = tx.send(StdinMsg::Detach);
                            return;
                        }
                        KeyAction::Forward(bytes) => {
                            if !bytes.is_empty() && tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                        KeyAction::Swallow => {}
                    }
                }
                Event::Paste(s) => {
                    if tx.send(StdinMsg::Bytes(s.into_bytes())).is_err() {
                        break;
                    }
                }
                _ => {}
            }
        }
    });
}

#[cfg(windows)]
fn spawn_stdin_reader_events(tx: mpsc::Sender<StdinMsg>) {
    use crossterm::event::{self, Event, KeyEventKind};
    std::thread::spawn(move || {
        // DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_RC_CTRLV=0` disables
        // OUR ctrl+V interception (the is_ctrl_v arm), leaving Windows Terminal to
        // handle ctrl+V natively — the isolation probe (does ctrl+V STILL wedge with
        // our handler OFF?). `SPT_RC_DEBUG_PASTE` traces the gesture/clipboard path.
        let ctrlv_on = std::env::var_os("SPT_RC_CTRLV").map_or(true, |v| v != "0");
        let dbg_paste = std::env::var_os("SPT_RC_DEBUG_PASTE").is_some();
        if dbg_paste {
            eprintln!("RC_PASTE: reader up (ctrlv_on={ctrlv_on})");
        }
        // Read the clipboard with START/END trace bracketing so a missing END line
        // pins read_clipboard as the blocking call.
        let traced_paste = |label: &str| -> Option<Vec<u8>> {
            if dbg_paste {
                eprintln!("RC_PASTE: {label} arm fired");
                eprintln!("RC_PASTE: read_clipboard START");
            }
            let content = read_clipboard();
            if dbg_paste {
                eprintln!(
                    "RC_PASTE: read_clipboard END len={}",
                    content.as_ref().map_or(0, |c| c.len())
                );
            }
            let bytes = content.filter(|c| !c.is_empty()).map(|c| wrap_bracketed_paste(&c));
            if dbg_paste {
                eprintln!("RC_PASTE: {label} sent {} bytes", bytes.as_ref().map_or(0, |b| b.len()));
            }
            bytes
        };
        let mut armed = false;
        while let Ok(ev) = event::read() {
            match ev {
                // ctrl+V paste gesture (REQ-RC-WIN-PASTE): read the LOCAL clipboard
                // and inject a BRACKETED paste, BEFORE the detach SM / key
                // translation — but only when NOT detach-armed (ctrl-b takes
                // precedence; ctrl+V is not the prefix). A multi-line paste lands
                // intact with no submit-storm; an empty/failed clipboard no-ops.
                Event::Key(ke)
                    if ke.kind == KeyEventKind::Press && !armed && ctrlv_on && is_ctrl_v(&ke) =>
                {
                    if let Some(bytes) = traced_paste("CTRLV") {
                        if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                            break;
                        }
                    }
                }
                // Right-click paste gesture (REQ-RC-WIN-PASTE): same bracketed
                // clipboard paste. Every OTHER mouse event is dropped — CC has no
                // mouse features, so capturing mouse deprives it of nothing.
                Event::Mouse(me) => {
                    if mouse_is_paste(&me) {
                        if let Some(bytes) = traced_paste("RMOUSE") {
                            if tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                    }
                }
                Event::Key(ke) if ke.kind == KeyEventKind::Press => {
                    if dbg_paste && is_ctrl_v(&ke) {
                        // ctrl+V reaching the GENERAL key path (our arm off / armed):
                        // it falls through to key_event_step → forwards ^V (0x16).
                        eprintln!("RC_PASTE: ctrl+V fell through to key path (ctrlv_on={ctrlv_on} armed={armed})");
                    }
                    match key_event_step(&mut armed, ke) {
                        KeyAction::Detach => {
                            let _ = tx.send(StdinMsg::Detach);
                            return;
                        }
                        KeyAction::Forward(bytes) => {
                            if !bytes.is_empty() && tx.send(StdinMsg::Bytes(bytes)).is_err() {
                                break;
                            }
                        }
                        KeyAction::Swallow => {}
                    }
                }
                Event::Paste(s) => {
                    // WT's NATIVE paste accelerator can surface here (a real
                    // Event::Paste) — log it so we can tell our injection apart from
                    // the terminal's own paste on a ctrl+V.
                    if dbg_paste {
                        eprintln!("RC_PASTE: Event::Paste len={}", s.len());
                    }
                    if tx.send(StdinMsg::Bytes(s.into_bytes())).is_err() {
                        break;
                    }
                }
                _ => {}
            }
        }
    });
}

+        // DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_RC_CTRLV=0` disables

+        // OUR ctrl+V interception (the is_ctrl_v arm), leaving Windows Terminal to

+        // handle ctrl+V natively — the isolation probe (does ctrl+V STILL wedge with

+        // our handler OFF?). `SPT_RC_DEBUG_PASTE` traces the gesture/clipboard path.

+        let ctrlv_on = std::env::var_os("SPT_RC_CTRLV").map_or(true, |v| v != "0");

+        let dbg_paste = std::env::var_os("SPT_RC_DEBUG_PASTE").is_some();

+            eprintln!("RC_PASTE: reader up (ctrlv_on={ctrlv_on})");

+        // Read the clipboard with START/END trace bracketing so a missing END line

+        // pins read_clipboard as the blocking call.

+        let traced_paste = |label: &str| -> Option<Vec<u8>> {

+                eprintln!("RC_PASTE: {label} arm fired");

+                eprintln!("RC_PASTE: read_clipboard START");

+            let content = read_clipboard();

+                    "RC_PASTE: read_clipboard END len={}",

+                    content.as_ref().map_or(0, |c| c.len())

+            let bytes = content.filter(|c| !c.is_empty()).map(|c| wrap_bracketed_paste(&c));

+                eprintln!("RC_PASTE: {label} sent {} bytes", bytes.as_ref().map_or(0, |b| b.len()));

-                Event::Key(ke) if ke.kind == KeyEventKind::Press && !armed && is_ctrl_v(&ke) => {

-                    if let Some(bytes) = clipboard_paste(read_clipboard) {

+                    if ke.kind == KeyEventKind::Press && !armed && ctrlv_on && is_ctrl_v(&ke) =>

+                    if let Some(bytes) = traced_paste("CTRLV") {

+                        if let Some(bytes) = traced_paste("RMOUSE") {

+                    if dbg_paste && is_ctrl_v(&ke) {

+                        // ctrl+V reaching the GENERAL key path (our arm off / armed):

+                        // it falls through to key_event_step → forwards ^V (0x16).

+                        eprintln!("RC_PASTE: ctrl+V fell through to key path (ctrlv_on={ctrlv_on} armed={armed})");

+                    // WT's NATIVE paste accelerator can surface here (a real

+                    // Event::Paste) — log it so we can tell our injection apart from

+                    // the terminal's own paste on a ctrl+V.

+                        eprintln!("RC_PASTE: Event::Paste len={}", s.len());


Now broker-side instrumentation (SPT_BROKER_DEBUG_INPUT) in dispatch_input + become_controller + controller deliver.


1594	        // under the sessions lock so the ENQUEUE happens without holding the table
1595	        // lock (mirrors dispatch_subscribe). The enqueue is non-blocking, so the
1596	        // dispatch thread is never parked here even when the harness has stalled
1597	        // (P0 / REQ-HAZARD-PTY-INPUT-WRITER-WEDGE).
1598	        let (input, translation) = {
1599	            let sessions = self.sessions.lock().unwrap();
1600	            let h = sessions
1601	                .get(&req.session_id)
1602	                .ok_or_else(|| format!("no such session {}", req.session_id))?;
1603	            (Arc::clone(&h.input), h.translation.clone())
1604	        };
1605	        // Layer C: when a (non-faulted) translation binary is attached, fire a
1606	        // CONTENT-FREE `{type:input}` ping so the binary can track operator idleness
1607	        // — best-effort, never carries the keystroke bytes (doyle gate #1, privacy).
1608	        if let Some(tr) = &translation {
1609	            if !tr.faulted.load(Ordering::Relaxed) {
1610	                let _ = tr.child.send(&ToBinary::Input);
1611	            }
1612	        }
1613	        // Layer C: if an injected sequence is mid-flight, the operator's keystrokes
1614	        // BUFFER in the inject floor and are flushed to the PTY after the sequence
1615	        // commits — so a stash/restore choreography can't be clobbered. Returns
1616	        // true when buffered (the caller must NOT write the PTY now).
1617	        let buffered = |bytes: &[u8]| -> bool {
1618	            translation
1619	                .as_ref()
1620	                .is_some_and(|tr| tr.floor.lock().unwrap().buffer_if_held(bytes))
1621	        };
1622	        match req.op_id {
1623	            Some(op) => {
1624	                let key = (req.session_id, op);
1625	                let outcome = self
1626	                    .journal
1627	                    .apply_once(key, EffectKind::PtyWrite, || {
1628	                        // Buffer-or-ENQUEUE INSIDE the journal effect so the op is
1629	                        // still recorded applied + acked (dedup-safe) whether the byte
1630	                        // buffers (inject floor held), enqueues, or is dropped under
1631	                        // backpressure. The effect is now the non-blocking enqueue —
1632	                        // PtyWrite is ephemeral, so Applied-at-enqueue is sound
1633	                        // (V0.13.0-P0 design); the ack means "accepted + ordered + will
1634	                        // land," and rc does not gate on landing.
1635	                        if !buffered(&bytes) {
1636	                            input.enqueue(bytes.clone());
1637	                        }
1638	                        Ok(())
1639	                    })
1640	                    .map_err(|e| format!("input enqueue failed: {e}"))?;
1641	                // Ack exactly-once: both Applied and Deduped mean "accepted + ordered".
1642	                send_frame(
1643	                    send,
1644	                    &applied_envelope(req.session_id, op, outcome == Outcome::Applied),
1645	                );
1646	                Ok(())
1647	            }
1648	            None => {
1649	                if !buffered(&bytes) {
1650	                    input.enqueue(bytes);
1651	                }
1652	                Ok(())
1653	            }
1654	        }
1655	    }

        // under the sessions lock so the ENQUEUE happens without holding the table
        // lock (mirrors dispatch_subscribe). The enqueue is non-blocking, so the
        // dispatch thread is never parked here even when the harness has stalled
        // (P0 / REQ-HAZARD-PTY-INPUT-WRITER-WEDGE).
        let (input, translation) = {
            let sessions = self.sessions.lock().unwrap();
            let h = sessions
                .get(&req.session_id)
                .ok_or_else(|| format!("no such session {}", req.session_id))?;
            (Arc::clone(&h.input), h.translation.clone())
        };
        // Layer C: when a (non-faulted) translation binary is attached, fire a
        // CONTENT-FREE `{type:input}` ping so the binary can track operator idleness
        // — best-effort, never carries the keystroke bytes (doyle gate #1, privacy).
        if let Some(tr) = &translation {
            if !tr.faulted.load(Ordering::Relaxed) {
                let _ = tr.child.send(&ToBinary::Input);
            }
        }
        // Layer C: if an injected sequence is mid-flight, the operator's keystrokes
        // BUFFER in the inject floor and are flushed to the PTY after the sequence
        // commits — so a stash/restore choreography can't be clobbered. Returns
        // true when buffered (the caller must NOT write the PTY now).
        let buffered = |bytes: &[u8]| -> bool {
            translation
                .as_ref()
                .is_some_and(|tr| tr.floor.lock().unwrap().buffer_if_held(bytes))
        };
        match req.op_id {
            Some(op) => {
                let key = (req.session_id, op);
                let outcome = self
                    .journal
                    .apply_once(key, EffectKind::PtyWrite, || {
                        // Buffer-or-ENQUEUE INSIDE the journal effect so the op is
                        // still recorded applied + acked (dedup-safe) whether the byte
                        // buffers (inject floor held), enqueues, or is dropped under
                        // backpressure. The effect is now the non-blocking enqueue —
                        // PtyWrite is ephemeral, so Applied-at-enqueue is sound
                        // (V0.13.0-P0 design); the ack means "accepted + ordered + will
                        // land," and rc does not gate on landing.
                        if !buffered(&bytes) {
                            input.enqueue(bytes.clone());
                        }
                        Ok(())
                    })
                    .map_err(|e| format!("input enqueue failed: {e}"))?;
                // Ack exactly-once: both Applied and Deduped mean "accepted + ordered".
                send_frame(
                    send,
                    &applied_envelope(req.session_id, op, outcome == Outcome::Applied),
                );
                Ok(())
            }
            None => {
                if !buffered(&bytes) {
                    input.enqueue(bytes);
                }
                Ok(())
            }
        }
    }


1585	    /// with an [`crate::msg::KIND_APPLIED`] frame so the brain can retire the
1586	    /// effect (B5 / KNOWN-HAZARDS 7.2). Without an `op_id` it is the legacy
1587	    /// direct write (no dedup, no ack).
1588	    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
1589	    fn dispatch_input(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
1590	        let req: InputReq =
1591	            serde_json::from_value(env.payload).map_err(|e| format!("bad input payload: {e}"))?;
1592	        let bytes = decode_bytes(&req.data_b64).map_err(|e| format!("bad input base64: {e}"))?;
1593	        // Clone the input-writer Arc (and the translation handle, if any) out from
1594	        // under the sessions lock so the ENQUEUE happens without holding the table

    /// with an [`crate::msg::KIND_APPLIED`] frame so the brain can retire the
    /// effect (B5 / KNOWN-HAZARDS 7.2). Without an `op_id` it is the legacy
    /// direct write (no dedup, no ack).
    // [impl->REQ-HAZARD-RESTART-IDEMPOTENT]
    fn dispatch_input(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: InputReq =
            serde_json::from_value(env.payload).map_err(|e| format!("bad input payload: {e}"))?;
        let bytes = decode_bytes(&req.data_b64).map_err(|e| format!("bad input base64: {e}"))?;
        // Clone the input-writer Arc (and the translation handle, if any) out from
        // under the sessions lock so the ENQUEUE happens without holding the table


Add broker debug helper + instrument dispatch_input.


    fn dispatch_input(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: InputReq =
            serde_json::from_value(env.payload).map_err(|e| format!("bad input payload: {e}"))?;
        let bytes = decode_bytes(&req.data_b64).map_err(|e| format!("bad input base64: {e}"))?;

    fn dispatch_input(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: InputReq =
            serde_json::from_value(env.payload).map_err(|e| format!("bad input payload: {e}"))?;
        let bytes = decode_bytes(&req.data_b64).map_err(|e| format!("bad input base64: {e}"))?;
        if broker_dbg_input() {
            eprintln!(
                "BROKER_INPUT: recv session={} op={:?} n={}",
                req.session_id,
                req.op_id,
                bytes.len()
            );
        }


The file C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    fn dispatch_input(&self, env: Envelope, send: &SharedSend) -> Result<(), String> {
        let req: InputReq =
            serde_json::from_value(env.payload).map_err(|e| format!("bad input payload: {e}"))?;
        let bytes = decode_bytes(&req.data_b64).map_err(|e| format!("bad input base64: {e}"))?;
        if broker_dbg_input() {
            eprintln!(
                "BROKER_INPUT: recv session={} op={:?} n={}",
                req.session_id,
                req.op_id,
                bytes.len()
            );
        }

+                "BROKER_INPUT: recv session={} op={:?} n={}",

// Clone the input-writer Arc (and the translation handle, if any) out from

// under the sessions lock so the ENQUEUE happens without holding the table

// lock (mirrors dispatch_subscribe). The enqueue is non-blocking, so the


                        if !buffered(&bytes) {
                            input.enqueue(bytes.clone());
                        }
                        Ok(())
                    })
                    .map_err(|e| format!("input enqueue failed: {e}"))?;
                // Ack exactly-once: both Applied and Deduped mean "accepted + ordered".
                send_frame(
                    send,
                    &applied_envelope(req.session_id, op, outcome == Outcome::Applied),
                );
                Ok(())

                        if !buffered(&bytes) {
                            input.enqueue(bytes.clone());
                        }
                        Ok(())
                    })
                    .map_err(|e| format!("input enqueue failed: {e}"))?;
                if broker_dbg_input() {
                    eprintln!("BROKER_INPUT: enqueue-done op={op} outcome={outcome:?}; ack send START");
                }
                // Ack exactly-once: both Applied and Deduped mean "accepted + ordered".
                send_frame(
                    send,
                    &applied_envelope(req.session_id, op, outcome == Outcome::Applied),
                );
                if broker_dbg_input() {
                    eprintln!("BROKER_INPUT: ack send END op={op}");
                }
                Ok(())


                        if !buffered(&bytes) {
                            input.enqueue(bytes.clone());
                        }
                        Ok(())
                    })
                    .map_err(|e| format!("input enqueue failed: {e}"))?;
                // Ack exactly-once: both Applied and Deduped mean "accepted + ordered".
                send_frame(
                    send,
                    &applied_envelope(req.session_id, op, outcome == Outcome::Applied),
                );
                Ok(())

                        if !buffered(&bytes) {
                            input.enqueue(bytes.clone());
                        }
                        Ok(())
                    })
                    .map_err(|e| format!("input enqueue failed: {e}"))?;
                if broker_dbg_input() {
                    eprintln!("BROKER_INPUT: enqueue-done op={op} outcome={outcome:?}; ack send START");
                }
                // Ack exactly-once: both Applied and Deduped mean "accepted + ordered".
                send_frame(
                    send,
                    &applied_envelope(req.session_id, op, outcome == Outcome::Applied),
                );
                if broker_dbg_input() {
                    eprintln!("BROKER_INPUT: ack send END op={op}");
                }
                Ok(())

+                if broker_dbg_input() {

+                    eprintln!("BROKER_INPUT: enqueue-done op={op} outcome={outcome:?}; ack send START");

+                    eprintln!("BROKER_INPUT: ack send END op={op}");


Add `broker_dbg_input()` helper + instrument `input_writer` (the actual PTY write) and `become_controller`.


fn input_queue_depth() -> usize {
    match std::env::var("SPT_INPUT_QUEUE_DEPTH") {

/// DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_BROKER_DEBUG_INPUT` traces
/// the broker input + controller-write path so a HITL capture pins WHICH call blocks
/// the per-conn handler (e.g. `ack send START` with no matching `END` = `send_frame`
/// to the rc socket parked the handler).
fn broker_dbg_input() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_INPUT").is_some()
}

fn input_queue_depth() -> usize {
    match std::env::var("SPT_INPUT_QUEUE_DEPTH") {


/// DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_BROKER_DEBUG_INPUT` traces
/// the broker input + controller-write path so a HITL capture pins WHICH call blocks
/// the per-conn handler (e.g. `ack send START` with no matching `END` = `send_frame`
/// to the rc socket parked the handler).
fn broker_dbg_input() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_INPUT").is_some()
}

fn input_queue_depth() -> usize {
    match std::env::var("SPT_INPUT_QUEUE_DEPTH") {

/// gate overrides it (`SPT_INPUT_QUEUE_DEPTH`) to a tiny value to force a

/// deterministic queue saturation — the depth's PRESENCE (bounded, drop-on-full)

/// is the invariant, not its exact value.

+/// DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_BROKER_DEBUG_INPUT` traces

+/// the broker input + controller-write path so a HITL capture pins WHICH call blocks

+/// the per-conn handler (e.g. `ack send START` with no matching `END` = `send_frame`

+/// to the rc socket parked the handler).

+    std::env::var_os("SPT_BROKER_DEBUG_INPUT").is_some()


fn input_writer(session: Arc<PtySession>, rx: Receiver<InputRecord>) {
    while let Ok(bytes) = rx.recv() {
        let _ = session.write_input(&bytes);
    }
}

fn input_writer(session: Arc<PtySession>, rx: Receiver<InputRecord>) {
    while let Ok(bytes) = rx.recv() {
        if broker_dbg_input() {
            eprintln!("BROKER_INPUT: write_input START n={}", bytes.len());
        }
        let _ = session.write_input(&bytes);
        if broker_dbg_input() {
            eprintln!("BROKER_INPUT: write_input END n={}", bytes.len());
        }
    }
}


fn input_writer(session: Arc<PtySession>, rx: Receiver<InputRecord>) {
    while let Ok(bytes) = rx.recv() {
        let _ = session.write_input(&bytes);
    }
}

fn input_writer(session: Arc<PtySession>, rx: Receiver<InputRecord>) {
    while let Ok(bytes) = rx.recv() {
        if broker_dbg_input() {
            eprintln!("BROKER_INPUT: write_input START n={}", bytes.len());
        }
        let _ = session.write_input(&bytes);
        if broker_dbg_input() {
            eprintln!("BROKER_INPUT: write_input END n={}", bytes.len());
        }
    }
}

/// when the last `tx` clone drops at session teardown.

+            eprintln!("BROKER_INPUT: write_input START n={}", bytes.len());

+            eprintln!("BROKER_INPUT: write_input END n={}", bytes.len());


Grep

fn become_controller|let writer = thread::spawn\(move \|\| controller_writer


crates\spt-daemon\src\broker.rs:349:    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
crates\spt-daemon\src\broker.rs:367:        let writer = thread::spawn(move || controller_writer(sid, writer_send, initial, rx, dt));


<owl_messages>
<owl_message from="doyle" priority="high">CORRECTION on the ctrl+V diagnosis — operator re-tested: a SINGLE-CHAR ctrl+V does NOT wedge; LARGER ctrl+V wedges. So it IS VOLUME/FLOOD-dependent (my "1-char also wedges" was an operator mis-test). Re-aim the probe.

REFRAMED ROOT: on Windows Terminal, ctrl+V is WT's PASTE ACCELERATOR → WT injects the clipboard as a FLOOD of per-char KEY EVENTS (N events for N chars). Right-click goes through our EnableMouseCapture → WT doesn't act → ONE bracketed frame (that's why right-click large is fine). So the asymmetry = WT's ctrl+V flood vs our single right-click frame.

WHERE THE FLOOD WEDGES (instrument BOTH, the probe must distinguish):
1. INPUT side: N key-event frames → broker single-threaded dispatch loop (broker.rs:1091). But P0's bounded FIFO try_send DROPS on full fast, so the loop should drain quickly — probably NOT permanent here.
2. OUTPUT side (MORE LIKELY the permanent wedge): CC RENDERS each of the N injected keystrokes → a large OUTPUT burst → the CONTROLLER's SYNCHRONOUS INLINE broker-write (the KNOWN deferred weakness, attach.rs:120-134: "controller replay is a SYNCHRONOUS inline write ... holds the broker's single-threaded per-conn handler; unifying onto the async dedicated-writer-thread + bounded-channel pattern (viewers) would remove the inline-replay block"). If the controller-write outpaces rc's drain → blocks the per-conn handler → PERMANENT broker wedge + controller never released (matches "stays controlled, detach didn't register").

So the LIKELY real fix = the SYMMETRIC P0 for OUTPUT: move the controller output-write onto the async writer-thread + bounded-queue pattern viewers already use (evict/backpressure, never block the per-conn handler). That fixes ANY output flood, not just ctrl+V.

PROBE (still confirm-first, one deploy): keep the isolation (SPT_RC_CTRLV=0) + instrument BOTH the input dispatch AND the controller output-write / become_controller path (CTRL_WRITE start/end, per-conn handler return) + log output byte volume. Operator pastes a LARGE clipboard via ctrl+V → logs show whether it's input-dispatch or controller-output-write that blocks. Then we design the fix (likely async controller writer).

Confirm you got the correction. Top priority — v0.13.0 ship-blocker.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


349	    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
350	        // Drop the prior controller sink first (its writer ends when tx drops),
351	        // and bump the generation so an in-flight deadline-evict for the old
352	        // controller can't unseat this one.
353	        self.controller = None;
354	        self.controller_epoch += 1;
355	        // Ring replay = the writer thread's initial batch (owned by the thread,
356	        // NOT pushed through the live channel — replay must never block the drain).
357	        let initial: Vec<(u64, Vec<u8>)> = self
358	            .ring
359	            .iter()
360	            .filter(|(s, _)| *s >= from_seq)
361	            .cloned()
362	            .collect();
363	        let (tx, rx) = sync_channel::<CtrlMsg>(CONTROLLER_CHANNEL_DEPTH);
364	        let writer_send = Arc::clone(&sub);
365	        let dt = Arc::clone(&self.delivered_through);
366	        let sid = self.session_id;
367	        let writer = thread::spawn(move || controller_writer(sid, writer_send, initial, rx, dt));
368	        self.controller = Some(ControllerSink {
369	            tx,
370	            send: sub,
371	            by,
372	            _writer: writer,
373	        });
374	        self.stamp_driven_by();
375	    }
376

    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
        // Drop the prior controller sink first (its writer ends when tx drops),
        // and bump the generation so an in-flight deadline-evict for the old
        // controller can't unseat this one.
        self.controller = None;
        self.controller_epoch += 1;
        // Ring replay = the writer thread's initial batch (owned by the thread,
        // NOT pushed through the live channel — replay must never block the drain).
        let initial: Vec<(u64, Vec<u8>)> = self
            .ring
            .iter()
            .filter(|(s, _)| *s >= from_seq)
            .cloned()
            .collect();
        let (tx, rx) = sync_channel::<CtrlMsg>(CONTROLLER_CHANNEL_DEPTH);
        let writer_send = Arc::clone(&sub);
        let dt = Arc::clone(&self.delivered_through);
        let sid = self.session_id;
        let writer = thread::spawn(move || controller_writer(sid, writer_send, initial, rx, dt));
        self.controller = Some(ControllerSink {
            tx,
            send: sub,
            by,
            _writer: writer,
        });
        self.stamp_driven_by();
    }


Got the correction, doyle — acknowledged:
- **NOT 1-char** (operator mis-test); **larger ctrl+V wedges = volume/flood-dependent.** Re-aiming.
- **Reframed root:** WT's ctrl+V paste accelerator floods N per-char KEY EVENTS; right-click rides our EnableMouseCapture → WT inert → ONE bracketed frame (why right-click large is fine). Asymmetry = WT flood vs our single frame.
- **Two wedge candidates, probe must distinguish:** (1) INPUT side N frames → dispatch (P0 FIFO try_send drops fast → likely transient); (2) OUTPUT side — CC renders N keystrokes → output burst → controller write → **permanent wedge** (matches "stays controlled, detach didn't register").
- Likely real fix = symmetric-P0-for-OUTPUT (async controller writer + bounded queue, like viewers) — but **confirm-first**, no guess-fix.

One thing I'll surface from the code: W1 (@8b5583e) ALREADY moved the controller output write off the drain onto `controller_writer` (async thread + bounded channel, `ControllerJob::deliver` bounded to `CONTROLLER_WRITE_DEADLINE`=5s → evict). And `become_controller` replay is now the writer thread's initial batch, not a sync inline write — so the attach.rs:120-134 comment may be **stale**. The probe will pin whether the permanent wedge is really the controller path or a lock held elsewhere (e.g. the sessions Mutex). Adding output-side + volume instrumentation now.

Sending ack:


Bash

$OWL send doyle <<'EOF'
Got the correction: NOT 1-char (operator mis-test) — LARGER ctrl+V wedges = VOLUME/FLOOD. Re-aiming the probe per your reframe (WT ctrl+V accelerator floods N per-char KEY EVENTS; right-click rides our mouse-capture = ONE bracketed frame = fine).

Building the probe to distinguish INPUT-dispatch flood vs OUTPUT controller-write flood, + output byte-volume logging, keeping the SPT_RC_CTRLV=0 isolation.

CODE-GROUNDED FLAG before you lock the fix: W1 (@8b5583e, REQ-HAZARD-INJECT-CONTROL-COEXIST) ALREADY moved the controller output-write OFF the drain thread onto the async controller_writer (dedicated thread + bounded sync_channel depth 4096 + ControllerJob::deliver bounded to CONTROLLER_WRITE_DEADLINE=5s -> mark_controller_gone evict). And become_controller's ring replay is now the writer thread's INITIAL BATCH, not a synchronous inline write. So the attach.rs:120-134 "synchronous inline controller-write" comment looks STALE (pre-W1). If so, an async-controller-writer fix is already in place for OUTPUT — meaning the permanent wedge may be elsewhere (e.g. a per-conn handler holding the sessions Mutex across a blocking op, or the brain-side serve_attach send_effect/IPC backpressure). The probe instruments dispatch_input (recv->enqueue-done->ack START/END = does the handler RETURN), input_writer (write_input START/END = P0 park), controller_writer (CTRL_WRITE start/end), ControllerJob::deliver (poll/evict), become_controller (START/END), + drain output volume. The ack-START-without-END line will pin a per-conn handler block; CTRL_WRITE-start-without-end pins the controller socket. Throwaway branch v0.13.0-p1-ctrlv-diag. Surfacing the build shortly.
EOF

Ack doyle correction


➜ SENT:doyle


Now instrument the output/controller path. Add `broker_dbg_output()` (SPT_BROKER_DEBUG_OUTPUT).


Edit

fn broker_dbg_input() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_INPUT").is_some()
}

fn broker_dbg_input() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_INPUT").is_some()
}

/// DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_BROKER_DEBUG_OUTPUT` traces
/// the controller OUTPUT-write path (the flood suspect): drain volume, the bounded
/// `ControllerJob::deliver` (poll/evict), `become_controller`, and the
/// `controller_writer` socket write. A `CTRL_WRITE start` with no matching `end`
/// pins the controller socket as the block.
fn broker_dbg_output() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_OUTPUT").is_some()
}


fn broker_dbg_input() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_INPUT").is_some()
}

/// DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_BROKER_DEBUG_OUTPUT` traces
/// the controller OUTPUT-write path (the flood suspect): drain volume, the bounded
/// `ControllerJob::deliver` (poll/evict), `become_controller`, and the
/// `controller_writer` socket write. A `CTRL_WRITE start` with no matching `end`
/// pins the controller socket as the block.
fn broker_dbg_output() -> bool {
    std::env::var_os("SPT_BROKER_DEBUG_OUTPUT").is_some()
}

+/// DIAGNOSTIC (v0.13.0 P1 ctrl+V wedge, throwaway): `SPT_BROKER_DEBUG_OUTPUT` traces

+/// the controller OUTPUT-write path (the flood suspect): drain volume, the bounded

+/// `ControllerJob::deliver` (poll/evict), `become_controller`, and the

+/// `controller_writer` socket write. A `CTRL_WRITE start` with no matching `end`

+/// pins the controller socket as the block.

+    std::env::var_os("SPT_BROKER_DEBUG_OUTPUT").is_some()


    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
        // Drop the prior controller sink first (its writer ends when tx drops),
        // and bump the generation so an in-flight deadline-evict for the old
        // controller can't unseat this one.
        self.controller = None;
        self.controller_epoch += 1;

    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
        if broker_dbg_output() {
            eprintln!(
                "BROKER_OUTPUT: become_controller START sid={} from_seq={} ring_len={}",
                self.session_id,
                from_seq,
                self.ring.len()
            );
        }
        // Drop the prior controller sink first (its writer ends when tx drops),
        // and bump the generation so an in-flight deadline-evict for the old
        // controller can't unseat this one.
        self.controller = None;
        self.controller_epoch += 1;


    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
        // Drop the prior controller sink first (its writer ends when tx drops),
        // and bump the generation so an in-flight deadline-evict for the old
        // controller can't unseat this one.
        self.controller = None;
        self.controller_epoch += 1;

    fn become_controller(&mut self, sub: SharedSend, by: Option<String>, from_seq: u64) {
        if broker_dbg_output() {
            eprintln!(
                "BROKER_OUTPUT: become_controller START sid={} from_seq={} ring_len={}",
                self.session_id,
                from_seq,
                self.ring.len()
            );
        }
        // Drop the prior controller sink first (its writer ends when tx drops),
        // and bump the generation so an in-flight deadline-evict for the old
        // controller can't unseat this one.
        self.controller = None;
        self.controller_epoch += 1;

/// ends its writer; bumping `controller_epoch` makes a stale deadline-evict a

/// no-op. Stamps `driven_by` to the new controller's identity.

+                "BROKER_OUTPUT: become_controller START sid={} from_seq={} ring_len={}",


        self.controller = Some(ControllerSink {
            tx,
            send: sub,
            by,
            _writer: writer,
        });
        self.stamp_driven_by();
    }

        self.controller = Some(ControllerSink {
            tx,
            send: sub,
            by,
            _writer: writer,
        });
        self.stamp_driven_by();
        if broker_dbg_output() {
            eprintln!("BROKER_OUTPUT: become_controller END sid={}", self.session_id);
        }
    }


        self.controller = Some(ControllerSink {
            tx,
            send: sub,
            by,
            _writer: writer,
        });
        self.stamp_driven_by();
    }

        self.controller = Some(ControllerSink {
            tx,
            send: sub,
            by,
            _writer: writer,
        });
        self.stamp_driven_by();
        if broker_dbg_output() {
            eprintln!("BROKER_OUTPUT: become_controller END sid={}", self.session_id);
        }
    }

+            eprintln!("BROKER_OUTPUT: become_controller END sid={}", self.session_id);

/// Detach the controller and UNLATCH the perch — the ONE unlatch path shared


Now instrument `controller_writer` (socket write) + `ControllerJob::deliver` (bounded poll/evict). Read them.


<owl_messages>
<owl_message from="doyle" priority="high">ctrl+V REFINEMENT: operator reports the wedge starts at ~10 chars of clipboard string length. That's a LOW, specific threshold — NOT a massive flood. Argues for either (a) a small bounded buffer filling at ~10 (the per-session input FIFO depth? check input_queue_depth() default — if ~the wedge size, the drop/stamp path may have a bug that blocks instead of dropping), or (b) the sync controller-OUTPUT-write blocking once CC's render output from ~10 injected keystrokes crosses a small IPC buffer. The probe MUST log exact byte/frame COUNTS at the wedge point (input frames in, FIFO depth/drops, output bytes written to the controller, which call blocks). ~10 is small enough to instrument precisely. Keep the isolation flag (SPT_RC_CTRLV=0) too.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


670	        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
671	        loop {
672	            match self.tx.try_send(msg) {
673	                Ok(()) => return Ok(()),
674	                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),
675	                Err(TrySendError::Full(returned)) => {
676	                    if Instant::now() >= deadline {
677	                        return Err(self.epoch);
678	                    }
679	                    msg = returned;
680	                    thread::sleep(CONTROLLER_SEND_POLL);
681	                }
682	            }
683	        }
684	    }
685	}
686
687	/// Advance the shared `delivered_through` cursor to `seq + 1`, monotonically
688	/// (D4-1, via [`advanced_cursor`]) and CAS-safe against a brief two-writer
689	/// overlap during a controller displace.
690	fn advance_delivered(dt: &AtomicU64, seq: u64) {
691	    let mut cur = dt.load(Ordering::Acquire);
692	    loop {
693	        let next = advanced_cursor(cur, seq, true);
694	        if next == cur {
695	            return;
696	        }
697	        match dt.compare_exchange_weak(cur, next, Ordering::AcqRel, Ordering::Acquire) {
698	            Ok(_) => return,
699	            Err(actual) => cur = actual,
700	        }
701	    }
702	}
703
704	/// The interactive CONTROLLER's dedicated writer thread (W1 /
705	/// REQ-HAZARD-INJECT-CONTROL-COEXIST) — the authoritative analog of
706	/// [`viewer_writer`]. Write the `initial` ring-replay batch (advancing the resume
707	/// cursor on each success), then drain live [`CtrlMsg`]s with a BLOCKING socket
708	/// write each — so a backed-up controller blocks ONLY this thread, never the
709	/// session drain (which used to write the controller inline under the log lock).
710	/// A failed write (dead operator) ends the thread; the drain's next bounded
711	/// send then sees `Disconnected` and detaches the row. `Output` advances
712	/// `delivered_through`; `Control` (Displaced/…) does not.
713	// [impl->REQ-HAZARD-INJECT-CONTROL-COEXIST]
714	fn controller_writer(
715	    sid: u64,
716	    send: SharedSend,
717	    initial: Vec<(u64, Vec<u8>)>,
718	    rx: Receiver<CtrlMsg>,
719	    delivered_through: Arc<AtomicU64>,
720	) {
721	    for (seq, bytes) in &initial {
722	        let frame = output_envelope(sid, *seq, bytes);
723	        let ok = match send.lock() {
724	            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
725	            Err(_) => false,
726	        };
727	        if !ok {
728	            return;
729	        }
730	        advance_delivered(&delivered_through, *seq);
731	    }
732	    while let Ok(msg) = rx.recv() {
733	        let (frame, seq) = match &msg {
734	            CtrlMsg::Output(seq, frame) => (frame, Some(*seq)),
735	            CtrlMsg::Control(frame) => (frame, None),
736	        };
737	        let ok = match send.lock() {
738	            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
739	            Err(_) => false,
740	        };
741	        if !ok {
742	            return;
743	        }
744	        if let Some(seq) = seq {
745	            advance_delivered(&delivered_through, seq);
746	        }
747	    }
748	}
749
750	/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present
751	/// ONLY when the spawn carried a `translation_binary` path; absent → the session
752	/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.
753	///
754	/// Holds the shared inject floor (the controller-input buffer, also touched by
755	/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
756	/// (events serialize — never two uncommitted sequences interleaved), the shared
757	/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
758	/// while the worker drives an event), and the FAULT flag the worker raises when a
759	/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
760	/// the session reverts to raw inject and stops pinging.
761	struct Translation {
762	    /// Controller-input buffer + inject-floor flag, shared with `dispatch_input`.
763	    floor: Arc<Mutex<InjectFloor>>,
764	    /// Queue one inbound `<EVENT>` envelope to the inject worker. Events QUEUE here
765	    /// and are driven one-at-a-time so two sequences never interleave.
766	    event_tx: Sender<String>,
767	    /// The live child — shared so `dispatch_input` fires content-free pings while
768	    /// the worker (which also holds an `Arc`) drives events / faults the binary.
769	    child: Arc<TranslationChild>,
770	    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
771	    /// session reverts to raw inject and `dispatch_input` stops pinging.
772	    faulted: Arc<AtomicBool>,
773	}
774
775	/// A child the broker hosts under a PTY — the §B-owned resources for one
776	/// session, plus its gapless-resume output log.
777	struct HostedSession {
778	    /// The PTY + child (PTY master/writer + spawned child process).
779	    session: Arc<PtySession>,

        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
        loop {
            match self.tx.try_send(msg) {
                Ok(()) => return Ok(()),
                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),
                Err(TrySendError::Full(returned)) => {
                    if Instant::now() >= deadline {
                        return Err(self.epoch);
                    }
                    msg = returned;
                    thread::sleep(CONTROLLER_SEND_POLL);
                }
            }
        }
    }
}

/// Advance the shared `delivered_through` cursor to `seq + 1`, monotonically
/// (D4-1, via [`advanced_cursor`]) and CAS-safe against a brief two-writer
/// overlap during a controller displace.
fn advance_delivered(dt: &AtomicU64, seq: u64) {
    let mut cur = dt.load(Ordering::Acquire);
    loop {
        let next = advanced_cursor(cur, seq, true);
        if next == cur {
            return;
        }
        match dt.compare_exchange_weak(cur, next, Ordering::AcqRel, Ordering::Acquire) {
            Ok(_) => return,
            Err(actual) => cur = actual,
        }
    }
}

/// The interactive CONTROLLER's dedicated writer thread (W1 /
/// REQ-HAZARD-INJECT-CONTROL-COEXIST) — the authoritative analog of
/// [`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.
    event_tx: Sender<String>,
    /// The live child — shared so `dispatch_input` fires content-free pings while
    /// the worker (which also holds an `Arc`) drives events / faults the binary.
    child: Arc<TranslationChild>,
    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
    /// session reverts to raw inject and `dispatch_input` stops pinging.
    faulted: Arc<AtomicBool>,
}

/// A child the broker hosts under a PTY — the §B-owned resources for one
/// session, plus its gapless-resume output log.
struct HostedSession {
    /// The PTY + child (PTY master/writer + spawned child process).
    session: Arc<PtySession>,


Acknowledged, doyle — ~10-char threshold is low + specific, not a massive flood. Re-aiming the probe to log **exact counts at the wedge point**: input frames in, FIFO depth/drops/backpressure, output bytes written to the controller (cumulative), and which call blocks. Keeping SPT_RC_CTRLV=0 isolation.

Quick code-check before I ack fully: `input_queue_depth()` default is **4096** records — so ~10 chars can't be filling the input FIFO. That argues against (a) on the input side and toward (b) the controller-output IPC buffer, OR something else with a ~10 boundary. The probe will count precisely.


Bash

$OWL send doyle <<'EOF'
Got the ~10-char refinement. Key code-check: input_queue_depth() DEFAULT = 4096 records (INPUT_QUEUE_DEPTH const), so ~10 chars CANNOT be filling the input FIFO — rules out your (a) on the input side (the drop/stamp path isn't even reached at 10). Points harder at (b): the controller-OUTPUT IPC buffer, OR a different ~10-boundary resource.

WT ctrl+V at ~10 chars = ~10 per-char KEY EVENTS -> ~10 separate input frames (each a send_attach_input) + possibly our 1 bracketed frame too (both fire: WT floods key events AND crossterm delivers a ctrl+V Press that our arm catches). So ~10-11 input frames -> ~10-11 PTY writes -> CC renders -> output burst back through the controller_writer socket.

Probe now logs EXACT COUNTS:
- INPUT: per dispatch_input (recv n= / enqueue accepted|DROPPED / backpressure) + cumulative frame count
- OUTPUT: controller_writer per-frame seq + byte count + CUMULATIVE bytes written; ControllerJob::deliver poll/FULL/evict; become_controller START/END
- per-conn handler return: ack send START/END (START w/o END = handler blocked on the rc socket write)
- input_writer write_input START/END (P0 PTY park)
- rc side: SPT_RC_DEBUG_PASTE (CTRLV arm fired / read_clipboard START/END / sent N) + SPT_RC_CTRLV=0 isolation + Event::Paste len (WT native paste detection)

So the capture will show, at the ~10 boundary: how many input frames arrived, how many output bytes the controller_writer wrote before it stalled, and whether ack-START-without-END (per-conn handler block) or CTRL_WRITE-start-without-end (controller socket block) fires. That pins input-vs-output + the exact byte count. Finishing the build now.
EOF

Ack ~10-char refinement


Now finish output instrumentation: `controller_writer` cumulative bytes + `ControllerJob::deliver` poll/evict.


Edit

    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);
        }
    }
}

    let dbg = broker_dbg_output();
    let mut total: u64 = 0; // cumulative payload bytes written to the controller socket
    for (seq, bytes) in &initial {
        let frame = output_envelope(sid, *seq, bytes);
        if dbg {
            eprintln!(
                "BROKER_OUTPUT: CTRL_WRITE start (replay) sid={sid} seq={seq} n={} total={total}",
                bytes.len()
            );
        }
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
            Err(_) => false,
        };
        if dbg {
            eprintln!("BROKER_OUTPUT: CTRL_WRITE end (replay) sid={sid} seq={seq} ok={ok}");
        }
        if !ok {
            return;
        }
        total += bytes.len() as u64;
        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),
        };
        if dbg {
            eprintln!(
                "BROKER_OUTPUT: CTRL_WRITE start (live) sid={sid} seq={seq:?} total={total}"
            );
        }
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
            Err(_) => false,
        };
        if dbg {
            eprintln!("BROKER_OUTPUT: CTRL_WRITE end (live) sid={sid} seq={seq:?} ok={ok} total={total}");
        }
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}


    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);
        }
    }
}

    let dbg = broker_dbg_output();
    let mut total: u64 = 0; // cumulative payload bytes written to the controller socket
    for (seq, bytes) in &initial {
        let frame = output_envelope(sid, *seq, bytes);
        if dbg {
            eprintln!(
                "BROKER_OUTPUT: CTRL_WRITE start (replay) sid={sid} seq={seq} n={} total={total}",
                bytes.len()
            );
        }
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, &frame).is_ok(),
            Err(_) => false,
        };
        if dbg {
            eprintln!("BROKER_OUTPUT: CTRL_WRITE end (replay) sid={sid} seq={seq} ok={ok}");
        }
        if !ok {
            return;
        }
        total += bytes.len() as u64;
        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),
        };
        if dbg {
            eprintln!(
                "BROKER_OUTPUT: CTRL_WRITE start (live) sid={sid} seq={seq:?} total={total}"
            );
        }
        let ok = match send.lock() {
            Ok(mut s) => write_frame(&mut *s, frame).is_ok(),
            Err(_) => false,
        };
        if dbg {
            eprintln!("BROKER_OUTPUT: CTRL_WRITE end (live) sid={sid} seq={seq:?} ok={ok} total={total}");
        }
        if !ok {
            return;
        }
        if let Some(seq) = seq {
            advance_delivered(&delivered_through, seq);
        }
    }
}

+    let mut total: u64 = 0; // cumulative payload bytes written to the controller socket

+                "BROKER_OUTPUT: CTRL_WRITE start (replay) sid={sid} seq={seq} n={} total={total}",

+            eprintln!("BROKER_OUTPUT: CTRL_WRITE end (replay) sid={sid} seq={seq} ok={ok}");

+                "BROKER_OUTPUT: CTRL_WRITE start (live) sid={sid} seq={seq:?} total={total}"

+            eprintln!("BROKER_OUTPUT: CTRL_WRITE end (live) sid={sid} seq={seq:?} ok={ok} total={total}");


Now `ControllerJob::deliver` poll/FULL/evict instrumentation.


665	    /// added latency; only a FULL channel polls every [`CONTROLLER_SEND_POLL`] up
666	    /// to [`CONTROLLER_WRITE_DEADLINE`]. This runs OFF the log lock (in the drain
667	    /// closure), so a stalled controller never holds the lock.
668	    fn deliver(self) -> Result<(), u64> {
669	        let mut msg = CtrlMsg::Output(self.seq, self.frame);
670	        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
671	        loop {
672	            match self.tx.try_send(msg) {
673	                Ok(()) => return Ok(()),
674	                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),
675	                Err(TrySendError::Full(returned)) => {
676	                    if Instant::now() >= deadline {
677	                        return Err(self.epoch);
678	                    }
679	                    msg = returned;
680	                    thread::sleep(CONTROLLER_SEND_POLL);
681	                }
682	            }
683	        }
684	    }

    /// added latency; only a FULL channel polls every [`CONTROLLER_SEND_POLL`] up
    /// to [`CONTROLLER_WRITE_DEADLINE`]. This runs OFF the log lock (in the drain
    /// closure), so a stalled controller never holds the lock.
    fn deliver(self) -> Result<(), u64> {
        let mut msg = CtrlMsg::Output(self.seq, self.frame);
        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
        loop {
            match self.tx.try_send(msg) {
                Ok(()) => return Ok(()),
                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),
                Err(TrySendError::Full(returned)) => {
                    if Instant::now() >= deadline {
                        return Err(self.epoch);
                    }
                    msg = returned;
                    thread::sleep(CONTROLLER_SEND_POLL);
                }
            }
        }
    }


    fn deliver(self) -> Result<(), u64> {
        let mut msg = CtrlMsg::Output(self.seq, self.frame);
        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
        loop {
            match self.tx.try_send(msg) {
                Ok(()) => return Ok(()),
                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),
                Err(TrySendError::Full(returned)) => {
                    if Instant::now() >= deadline {
                        return Err(self.epoch);
                    }
                    msg = returned;
                    thread::sleep(CONTROLLER_SEND_POLL);
                }
            }
        }
    }

    fn deliver(self) -> Result<(), u64> {
        let dbg = broker_dbg_output();
        let seq = self.seq;
        let mut msg = CtrlMsg::Output(self.seq, self.frame);
        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
        let mut polls: u32 = 0;
        loop {
            match self.tx.try_send(msg) {
                Ok(()) => {
                    if dbg && polls > 0 {
                        eprintln!("BROKER_OUTPUT: deliver OK seq={seq} after {polls} FULL-polls");
                    }
                    return Ok(());
                }
                Err(TrySendError::Disconnected(_)) => {
                    if dbg {
                        eprintln!("BROKER_OUTPUT: deliver DISCONNECTED seq={seq} -> evict");
                    }
                    return Err(self.epoch);
                }
                Err(TrySendError::Full(returned)) => {
                    if Instant::now() >= deadline {
                        if dbg {
                            eprintln!(
                                "BROKER_OUTPUT: deliver DEADLINE-EVICT seq={seq} after {polls} polls (controller channel FULL {CONTROLLER_CHANNEL_DEPTH})"
                            );
                        }
                        return Err(self.epoch);
                    }
                    if dbg && polls == 0 {
                        eprintln!("BROKER_OUTPUT: deliver FULL seq={seq} -> polling (channel depth {CONTROLLER_CHANNEL_DEPTH})");
                    }
                    polls += 1;
                    msg = returned;
                    thread::sleep(CONTROLLER_SEND_POLL);
                }
            }
        }
    }


    fn deliver(self) -> Result<(), u64> {
        let mut msg = CtrlMsg::Output(self.seq, self.frame);
        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
        loop {
            match self.tx.try_send(msg) {
                Ok(()) => return Ok(()),
                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),
                Err(TrySendError::Full(returned)) => {
                    if Instant::now() >= deadline {
                        return Err(self.epoch);
                    }
                    msg = returned;
                    thread::sleep(CONTROLLER_SEND_POLL);
                }
            }
        }
    }

    fn deliver(self) -> Result<(), u64> {
        let dbg = broker_dbg_output();
        let seq = self.seq;
        let mut msg = CtrlMsg::Output(self.seq, self.frame);
        let deadline = Instant::now() + CONTROLLER_WRITE_DEADLINE;
        let mut polls: u32 = 0;
        loop {
            match self.tx.try_send(msg) {
                Ok(()) => {
                    if dbg && polls > 0 {
                        eprintln!("BROKER_OUTPUT: deliver OK seq={seq} after {polls} FULL-polls");
                    }
                    return Ok(());
                }
                Err(TrySendError::Disconnected(_)) => {
                    if dbg {
                        eprintln!("BROKER_OUTPUT: deliver DISCONNECTED seq={seq} -> evict");
                    }
                    return Err(self.epoch);
                }
                Err(TrySendError::Full(returned)) => {
                    if Instant::now() >= deadline {
                        if dbg {
                            eprintln!(
                                "BROKER_OUTPUT: deliver DEADLINE-EVICT seq={seq} after {polls} polls (controller channel FULL {CONTROLLER_CHANNEL_DEPTH})"
                            );
                        }
                        return Err(self.epoch);
                    }
                    if dbg && polls == 0 {
                        eprintln!("BROKER_OUTPUT: deliver FULL seq={seq} -> polling (channel depth {CONTROLLER_CHANNEL_DEPTH})");
                    }
                    polls += 1;
                    msg = returned;
                    thread::sleep(CONTROLLER_SEND_POLL);
                }
            }
        }
    }

/// to [`CONTROLLER_WRITE_DEADLINE`]. This runs OFF the log lock (in the drain

/// closure), so a stalled controller never holds the lock.

-                Err(TrySendError::Disconnected(_)) => return Err(self.epoch),

+                    if dbg && polls > 0 {

+                        eprintln!("BROKER_OUTPUT: deliver OK seq={seq} after {polls} FULL-polls");

+                Err(TrySendError::Disconnected(_)) => {

+                        eprintln!("BROKER_OUTPUT: deliver DISCONNECTED seq={seq} -> evict");

+                    return Err(self.epoch);

+                                "BROKER_OUTPUT: deliver DEADLINE-EVICT seq={seq} after {polls} polls (controller channel FULL {CONTROLLER_CHANNEL_DEPTH})"

+                    if dbg && polls == 0 {

+                        eprintln!("BROKER_OUTPUT: deliver FULL seq={seq} -> polling (channel depth {CONTROLLER_CHANNEL_DEPTH})");


Now `InputWriter::enqueue` accepted/dropped logging.


    fn enqueue(&self, bytes: InputRecord) -> bool {
        match self.tx.try_send(bytes) {
            Ok(()) => {
                // Heal: input is flowing again — clear a prior backpressure stamp.
                if self.backpressure.swap(false, Ordering::AcqRel) {
                    self.stamp_backpressure(false);
                }
                true
            }
            Err(TrySendError::Full(_)) => {
                // DROP + surface, but stamp only on the rising edge.
                if !self.backpressure.swap(true, Ordering::AcqRel) {
                    self.stamp_backpressure(true);
                }
                false
            }
            // The writer thread is gone (session teardown) — nothing to do.
            Err(TrySendError::Disconnected(_)) => false,
        }
    }

    fn enqueue(&self, bytes: InputRecord) -> bool {
        let dbg = broker_dbg_input();
        let n = bytes.len();
        match self.tx.try_send(bytes) {
            Ok(()) => {
                // Heal: input is flowing again — clear a prior backpressure stamp.
                if self.backpressure.swap(false, Ordering::AcqRel) {
                    self.stamp_backpressure(false);
                }
                if dbg {
                    eprintln!("BROKER_INPUT: enqueue ACCEPTED n={n}");
                }
                true
            }
            Err(TrySendError::Full(_)) => {
                // DROP + surface, but stamp only on the rising edge.
                if !self.backpressure.swap(true, Ordering::AcqRel) {
                    self.stamp_backpressure(true);
                }
                if dbg {
                    eprintln!("BROKER_INPUT: enqueue DROPPED (FIFO full, depth {}) n={n} -> INPUT_BACKPRESSURE", input_queue_depth());
                }
                false
            }
            // The writer thread is gone (session teardown) — nothing to do.
            Err(TrySendError::Disconnected(_)) => {
                if dbg {
                    eprintln!("BROKER_INPUT: enqueue DISCONNECTED n={n} (writer gone)");
                }
                false
            }
        }
    }