448	### 7.18 `spt rc` paste is client-originated on Windows — read the LOCAL clipboard, inject a BRACKETED paste  `[REQ-RC-WIN-PASTE]`
449	- **Failure (operator dogfooding):** in an `spt rc` session neither ctrl+V nor right-click pasted (CC explicitly supports ctrl+V). `RawGuard` did only `enable_raw_mode` (no bracketed paste, no mouse capture, no clipboard interception); the Windows console delivers a paste as synthetic per-char KEY EVENTs (no crossterm `Event::Paste`), and ctrl+V translated to a bare `^V` forwarded to CC — but **CC runs daemon-side with NO access to the operator's LOCAL clipboard**, so remote paste is fundamentally CLIENT-ORIGINATED. A multi-line paste-as-keys also became a `\r` submit-storm.
450	- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a paste gesture rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). ctrl+V: intercept `Char('v')+CONTROL` in the event loop BEFORE translation, when not detach-armed (`ctrl-b` keeps precedence), → `read_clipboard` → bracketed paste. Right-click: `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste; ALL other mouse is DROPPED (CC has no mouse features, so capture costs nothing). `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). Rides 7.17's single-writer so a paste chunk never wedges the broker (hard dependency on P0).
451	- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `is_ctrl_v` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` ctrl+V/right-mouse arms; unit the exact bracketed framing + content-verbatim + ctrl+V detection + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
452	- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17).
453
454	---
455
456	## Conformance checklist (condensed)
457

### 7.18 `spt rc` paste is client-originated on Windows — read the LOCAL clipboard, inject a BRACKETED paste  `[REQ-RC-WIN-PASTE]`
- **Failure (operator dogfooding):** in an `spt rc` session neither ctrl+V nor right-click pasted (CC explicitly supports ctrl+V). `RawGuard` did only `enable_raw_mode` (no bracketed paste, no mouse capture, no clipboard interception); the Windows console delivers a paste as synthetic per-char KEY EVENTs (no crossterm `Event::Paste`), and ctrl+V translated to a bare `^V` forwarded to CC — but **CC runs daemon-side with NO access to the operator's LOCAL clipboard**, so remote paste is fundamentally CLIENT-ORIGINATED. A multi-line paste-as-keys also became a `\r` submit-storm.
- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a paste gesture rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). ctrl+V: intercept `Char('v')+CONTROL` in the event loop BEFORE translation, when not detach-armed (`ctrl-b` keeps precedence), → `read_clipboard` → bracketed paste. Right-click: `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste; ALL other mouse is DROPPED (CC has no mouse features, so capture costs nothing). `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). Rides 7.17's single-writer so a paste chunk never wedges the broker (hard dependency on P0).
- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `is_ctrl_v` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` ctrl+V/right-mouse arms; unit the exact bracketed framing + content-verbatim + ctrl+V detection + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17).

---

## Conformance checklist (condensed)


P1 ctrl+V wedge — diagnose then fix

HITL re-open: ctrl+V (~10+ chars) wedges broker; right-click fine. Diagnostic build @b39e8a0 (branch v0.13.0-p1-ctrlv-diag) surfaced to doyle with env matrix (SPT_RC_CTRLV isolation + SPT_RC_DEBUG_PASTE + SPT_BROKER_DEBUG_INPUT/OUTPUT). Awaiting operator stderr capture (via doyle) to pin input-dispatch vs controller-output-write block. Then design+build the real fix (doyle suspects symmetric-P0-for-output async controller writer) on a gated branch off f7cf380.

Mint REQ-HAZARD-INPUT-ACK-BACKPRESSURE + amend REQ-RC-WIN-PASTE

Registry-first: new hazard REQ [impl,unit,int] for the input-ack IPC deadlock. Amend REQ-RC-WIN-PASTE to right-click-only scope (ctrl+V = WT-native).

Primary fix: conditional ack

InputReq.ack serde-default=true; brain send_effect_no_ack for operator path (serve_attach); dispatch_input only send_frame(applied) when req.ack; shellchan keeps ack=true.

Secondary: remove dead ctrl+V interception

Remove is_ctrl_v arm + fn + unit; keep right-click bracketed paste + EnableMouseCapture. ctrl+V rides WT-native paste.

Docs: KNOWN-HAZARDS deadlock + amend 7.18

New KNOWN-HAZARDS entry for input-ack backpressure deadlock (+N-1 broker-resident caveat). Amend 7.18: ctrl+V WT-native, right-click bracketed only.

Tests (spt-test-engineer) + gates + commit + doyle

Delegate repro-first int (N=64 flood deadlock) + units (ack=true emits/false doesn't, exactly-once no-ack dedup). Gates clippy/traceable/docs-drift/real-bin; commit; surface green to doyle.

Scroll-forward: mouse_mode scan + scroll→SGR

REQ-RC-MOUSE-FORWARD: track CC mouse-mode via DECSET scan on output stream (Arc<AtomicBool> enabled+sgr); forward ScrollUp/Down as SGR reports when on; keep right-click paste; drop moved/clicks. Units: exact-byte SGR + DECSET scan.


Amend the 7.18 narrative (ctrl+V removed → right-click only) + add 7.19 (deadlock) and 7.20 (scroll).


Edit

- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a paste gesture rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). ctrl+V: intercept `Char('v')+CONTROL` in the event loop BEFORE translation, when not detach-armed (`ctrl-b` keeps precedence), → `read_clipboard` → bracketed paste. Right-click: `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste; ALL other mouse is DROPPED (CC has no mouse features, so capture costs nothing). `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). Rides 7.17's single-writer so a paste chunk never wedges the broker (hard dependency on P0).
- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `is_ctrl_v` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` ctrl+V/right-mouse arms; unit the exact bracketed framing + content-verbatim + ctrl+V detection + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17).

---

- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a RIGHT-CLICK rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste. `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). **ctrl+V is NOT intercepted** (P1b amendment, HITL re-open): Windows Terminal CONSUMES ctrl+V as its own paste accelerator — it delivers only a Key `kind=RELEASE` (never a Press, so a Press-guarded arm could never fire) AND injects the clipboard as a char-by-char KEY FLOOD spt-core cannot intercept. ctrl+V now rides WT's native paste as keystrokes (multi-line may submit-storm — acceptable; bracketed fidelity = right-click). The flood landing as keystrokes no longer wedges the broker (7.19).
- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` right-mouse arm; unit the exact bracketed framing + content-verbatim + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17). AMENDED v0.13.0 P1b: scope narrowed to right-click-only (the dead ctrl+V interception removed); scroll-forward is 7.20; the input-flood non-wedge is 7.19.

<!-- [doc->REQ-HAZARD-INPUT-ACK-BACKPRESSURE] -->
### 7.19 An operator input FLOOD must not deadlock the broker via the applied-ack on the same conn  `[REQ-HAZARD-INPUT-ACK-BACKPRESSURE]`
- **Failure (operator HITL, the ctrl+V re-open):** a flood of operator input on one brain↔broker conn wedged the WHOLE broker PERMANENTLY (no new/existing attach; the controller stayed latched — the per-conn handler couldn't process the detach). `serve_attach` processes a whole `NetStreamData` batch of N `Input` records in its inner loop, calling `send_effect` N times WITHOUT returning to `read_event()`; the broker answers each with `send_frame(applied_envelope)` on the SAME conn. Brain not reading → the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block → mutual full-duplex DEADLOCK. (Capture: 11 frames, `write_input` 11/11 — P0 holds; `ack send` START=11/END=10 — frame #11's ack never returns.) WT's ctrl+V paste-accelerator key-flood was the trigger; the deadlock is generic to ANY input flood.
- **Invariant:** the applied-ack is OPT-IN. `InputReq` carries `ack: bool` (serde `default = true`, N-1-safe). The fire-and-forward operator/rc path (`serve_attach`) sends `ack=false` via `Brain::send_effect_no_ack`; `dispatch_input` writes NO applied frame when `ack=false`, so the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` (one-at-a-time spool delivery, WAITS on `BrokerEvent::Applied`) keeps `send_effect` (`ack=true`). EXACTLY-ONCE preserved: the broker dedups by `(session, op_id)` at the applied-set regardless of the ack. **N-1 caveat:** an OLD resident broker (the self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent broker-resident-wire-change class, see 7.9).
- **spt-core mapping:** impl `msg.rs` `InputReq.ack` (`default_true`) + `brain.rs` `send_effect_no_ack`/`send_effect_inner` + `attach.rs` `serve_attach` operator path → `send_effect_no_ack` + `broker.rs` `dispatch_input` gates `send_frame(applied)` on `req.ack`; unit the serde default + the ack-emitted-iff-true + the no-ack-path exactly-once dedup; int (keystone, repro-first) a flood of N>pipe-buffer input frames through `serve_attach` on one conn — PRE-FIX deadlocks, POST-FIX drains all N + the session stays live + a concurrent attach opens (real broker+brain, no mocks).
- **Source:** v0.13.0 P1b (operator HITL re-open; doyle /diagnose on the enhanced rc+broker capture 2026-06-19). The real ship-blocker behind the ctrl+V wedge.

<!-- [doc->REQ-RC-MOUSE-FORWARD] -->
### 7.20 `spt rc` must forward the scroll wheel to the harness (our mouse capture steals WT's native scroll)  `[REQ-RC-MOUSE-FORWARD]`
- **Failure (operator HITL):** scroll stopped working in `spt rc`. P1's `EnableMouseCapture` (for right-click paste, 7.18) makes Windows Terminal forward ALL mouse — including the wheel — to rc instead of scrolling its own buffer, but the rc mouse handler dropped everything except right-button-down → scroll DIED (and WT's native scrollback is stolen under the capture).
- **Invariant:** on Windows, TRACK the harness's mouse-reporting mode from its OUTPUT — scan for DECSET `ESC[?1000h/1002h/1003h` (mouse on) + `ESC[?1006h` (SGR ext) and their `…l` (off) into a shared `MouseMode{enabled,sgr}` (the pump writes from the output render path, the stdin reader reads); the scan survives a sequence SPLIT across output chunks (a bounded carry buffer). The mouse handler: right-button-DOWN → bracketed clipboard paste (7.18, unchanged); `ScrollUp/Down` → an xterm SGR mouse report (`ESC[<64;col+1;row+1M` up / `ESC[<65;…M` down; 0-based crossterm → 1-based xterm), forwarded ONLY when `enabled && sgr` (else DROP — a legacy report the harness may misread is garbage); Moved/drag/left/middle DROP (scroll is the need; click-forward risks garbage, no click-to-position). UNIX UNCHANGED (no capture; the terminal scrolls natively).
- **spt-core mapping:** impl `rc.rs` `MouseMode` + `MouseModeScanner`/`parse_decset_private`/`apply_mouse_mode` (carry-buffer scan, fed from the pump's output path) + `scroll_dir` + `scroll_sgr` + the `spawn_stdin_reader_events` scroll arm (threaded via `Arc<MouseMode>`); unit `scroll_dir` classify + `scroll_sgr` exact bytes + the DECSET scan (set/reset, combined, mixed, and split-across-chunks); NO int (live console mouse = HITL).
- **Source:** v0.13.0 P1b (operator HITL; doyle design 2026-06-19). Bundled with 7.19.

---


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

- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a paste gesture rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). ctrl+V: intercept `Char('v')+CONTROL` in the event loop BEFORE translation, when not detach-armed (`ctrl-b` keeps precedence), → `read_clipboard` → bracketed paste. Right-click: `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste; ALL other mouse is DROPPED (CC has no mouse features, so capture costs nothing). `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). Rides 7.17's single-writer so a paste chunk never wedges the broker (hard dependency on P0).
- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `is_ctrl_v` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` ctrl+V/right-mouse arms; unit the exact bracketed framing + content-verbatim + ctrl+V detection + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17).

---

- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a RIGHT-CLICK rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste. `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). **ctrl+V is NOT intercepted** (P1b amendment, HITL re-open): Windows Terminal CONSUMES ctrl+V as its own paste accelerator — it delivers only a Key `kind=RELEASE` (never a Press, so a Press-guarded arm could never fire) AND injects the clipboard as a char-by-char KEY FLOOD spt-core cannot intercept. ctrl+V now rides WT's native paste as keystrokes (multi-line may submit-storm — acceptable; bracketed fidelity = right-click). The flood landing as keystrokes no longer wedges the broker (7.19).
- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` right-mouse arm; unit the exact bracketed framing + content-verbatim + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17). AMENDED v0.13.0 P1b: scope narrowed to right-click-only (the dead ctrl+V interception removed); scroll-forward is 7.20; the input-flood non-wedge is 7.19.

<!-- [doc->REQ-HAZARD-INPUT-ACK-BACKPRESSURE] -->
### 7.19 An operator input FLOOD must not deadlock the broker via the applied-ack on the same conn  `[REQ-HAZARD-INPUT-ACK-BACKPRESSURE]`
- **Failure (operator HITL, the ctrl+V re-open):** a flood of operator input on one brain↔broker conn wedged the WHOLE broker PERMANENTLY (no new/existing attach; the controller stayed latched — the per-conn handler couldn't process the detach). `serve_attach` processes a whole `NetStreamData` batch of N `Input` records in its inner loop, calling `send_effect` N times WITHOUT returning to `read_event()`; the broker answers each with `send_frame(applied_envelope)` on the SAME conn. Brain not reading → the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block → mutual full-duplex DEADLOCK. (Capture: 11 frames, `write_input` 11/11 — P0 holds; `ack send` START=11/END=10 — frame #11's ack never returns.) WT's ctrl+V paste-accelerator key-flood was the trigger; the deadlock is generic to ANY input flood.
- **Invariant:** the applied-ack is OPT-IN. `InputReq` carries `ack: bool` (serde `default = true`, N-1-safe). The fire-and-forward operator/rc path (`serve_attach`) sends `ack=false` via `Brain::send_effect_no_ack`; `dispatch_input` writes NO applied frame when `ack=false`, so the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` (one-at-a-time spool delivery, WAITS on `BrokerEvent::Applied`) keeps `send_effect` (`ack=true`). EXACTLY-ONCE preserved: the broker dedups by `(session, op_id)` at the applied-set regardless of the ack. **N-1 caveat:** an OLD resident broker (the self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent broker-resident-wire-change class, see 7.9).
- **spt-core mapping:** impl `msg.rs` `InputReq.ack` (`default_true`) + `brain.rs` `send_effect_no_ack`/`send_effect_inner` + `attach.rs` `serve_attach` operator path → `send_effect_no_ack` + `broker.rs` `dispatch_input` gates `send_frame(applied)` on `req.ack`; unit the serde default + the ack-emitted-iff-true + the no-ack-path exactly-once dedup; int (keystone, repro-first) a flood of N>pipe-buffer input frames through `serve_attach` on one conn — PRE-FIX deadlocks, POST-FIX drains all N + the session stays live + a concurrent attach opens (real broker+brain, no mocks).
- **Source:** v0.13.0 P1b (operator HITL re-open; doyle /diagnose on the enhanced rc+broker capture 2026-06-19). The real ship-blocker behind the ctrl+V wedge.

<!-- [doc->REQ-RC-MOUSE-FORWARD] -->
### 7.20 `spt rc` must forward the scroll wheel to the harness (our mouse capture steals WT's native scroll)  `[REQ-RC-MOUSE-FORWARD]`
- **Failure (operator HITL):** scroll stopped working in `spt rc`. P1's `EnableMouseCapture` (for right-click paste, 7.18) makes Windows Terminal forward ALL mouse — including the wheel — to rc instead of scrolling its own buffer, but the rc mouse handler dropped everything except right-button-down → scroll DIED (and WT's native scrollback is stolen under the capture).
- **Invariant:** on Windows, TRACK the harness's mouse-reporting mode from its OUTPUT — scan for DECSET `ESC[?1000h/1002h/1003h` (mouse on) + `ESC[?1006h` (SGR ext) and their `…l` (off) into a shared `MouseMode{enabled,sgr}` (the pump writes from the output render path, the stdin reader reads); the scan survives a sequence SPLIT across output chunks (a bounded carry buffer). The mouse handler: right-button-DOWN → bracketed clipboard paste (7.18, unchanged); `ScrollUp/Down` → an xterm SGR mouse report (`ESC[<64;col+1;row+1M` up / `ESC[<65;…M` down; 0-based crossterm → 1-based xterm), forwarded ONLY when `enabled && sgr` (else DROP — a legacy report the harness may misread is garbage); Moved/drag/left/middle DROP (scroll is the need; click-forward risks garbage, no click-to-position). UNIX UNCHANGED (no capture; the terminal scrolls natively).
- **spt-core mapping:** impl `rc.rs` `MouseMode` + `MouseModeScanner`/`parse_decset_private`/`apply_mouse_mode` (carry-buffer scan, fed from the pump's output path) + `scroll_dir` + `scroll_sgr` + the `spawn_stdin_reader_events` scroll arm (threaded via `Arc<MouseMode>`); unit `scroll_dir` classify + `scroll_sgr` exact bytes + the DECSET scan (set/reset, combined, mixed, and split-across-chunks); NO int (live console mouse = HITL).
- **Source:** v0.13.0 P1b (operator HITL; doyle design 2026-06-19). Bundled with 7.19.

---

### 7.18 `spt rc` paste is client-originated on Windows — read the LOCAL clipboard, inject a BRACKETED paste  `[REQ-RC-WIN-PASTE]`

- **Failure (operator dogfooding):** in an `spt rc` session neither ctrl+V nor right-click pasted (CC explicitly supports ctrl+V). `RawGuard` did only `enable_raw_mode` (no bracketed paste, no mouse capture, no clipboard interception); the Windows console delivers a paste as synthetic per-char KEY EVENTs (no crossterm `Event::Paste`), and ctrl+V translated to a bare `^V` forwarded to CC — but **CC runs daemon-side with NO access to the operator's LOCAL clipboard**, so remote paste is fundamentally CLIENT-ORIGINATED. A multi-line paste-as-keys also became a `\r` submit-storm.

-- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a paste gesture rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). ctrl+V: intercept `Char('v')+CONTROL` in the event loop BEFORE translation, when not detach-armed (`ctrl-b` keeps precedence), → `read_clipboard` → bracketed paste. Right-click: `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste; ALL other mouse is DROPPED (CC has no mouse features, so capture costs nothing). `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). Rides 7.17's single-writer so a paste chunk never wedges the broker (hard dependency on P0).

-- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `is_ctrl_v` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` ctrl+V/right-mouse arms; unit the exact bracketed framing + content-verbatim + ctrl+V detection + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).

-- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17).

+- **Invariant:** on Windows (cfg-split; folds into the 7.16 event path), on a RIGHT-CLICK rc reads the LOCAL clipboard itself and forwards a BRACKETED paste — `wrap_bracketed_paste` = `ESC[200~` + content + `ESC[201~`. CC has bracketed-paste mode on (its TUI sets `ESC[?2004h`), so it treats the synthesized markers as a PASTE: content lands intact, NO submit-storm, harness-AGNOSTIC (standard xterm contract). `RawGuard` also `EnableMouseCapture` (disables console QuickEdit + enables `ENABLE_MOUSE_INPUT` so a right-click surfaces as `Event::Mouse`) on an interactive console only, restored on drop → right-button-down → `read_clipboard` → bracketed paste. `read_clipboard` = the `clipboard-win` crate; empty/failed read ⇒ a clean no-op (never inject garbage, never panic). Content forwarded VERBATIM (literal pasted text, no per-char translation). UNIX UNCHANGED (its terminal pastes natively through the byte pump). **ctrl+V is NOT intercepted** (P1b amendment, HITL re-open): Windows Terminal CONSUMES ctrl+V as its own paste accelerator — it delivers only a Key `kind=RELEASE` (never a Press, so a Press-guarded arm could never fire) AND injects the clipboard as a char-by-char KEY FLOOD spt-core cannot intercept. ctrl+V now rides WT's native paste as keystrokes (multi-line may submit-storm — acceptable; bracketed fidelity = right-click). The flood landing as keystrokes no longer wedges the broker (7.19).

+- **spt-core mapping:** impl `rc.rs` `wrap_bracketed_paste` + `mouse_is_paste` + `clipboard_paste` + `read_clipboard` + `windows_mouse_wanted` + `RawGuard` (mouse capture/restore) + the `spawn_stdin_reader_events` right-mouse arm; unit the exact bracketed framing + content-verbatim + mouse classify (right-down ⇒ paste, all else ⇒ drop) + the injected-reader paste decision (non-empty ⇒ wrapped, empty/fail ⇒ no-op); NO int (live clipboard + console mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).

+- **Source:** v0.13.0 P1 (operator HITL; doyle design 2026-06-19). Depends on P0 (7.17). AMENDED v0.13.0 P1b: scope narrowed to right-click-only (the dead ctrl+V interception removed); scroll-forward is 7.20; the input-flood non-wedge is 7.19.

+### 7.19 An operator input FLOOD must not deadlock the broker via the applied-ack on the same conn  `[REQ-HAZARD-INPUT-ACK-BACKPRESSURE]`

+- **Failure (operator HITL, the ctrl+V re-open):** a flood of operator input on one brain↔broker conn wedged the WHOLE broker PERMANENTLY (no new/existing attach; the controller stayed latched — the per-conn handler couldn't process the detach). `serve_attach` processes a whole `NetStreamData` batch of N `Input` records in its inner loop, calling `send_effect` N times WITHOUT returning to `read_event()`; the broker answers each with `send_frame(applied_envelope)` on the SAME conn. Brain not reading → the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block → mutual full-duplex DEADLOCK. (Capture: 11 frames, `write_input` 11/11 — P0 holds; `ack send` START=11/END=10 — frame #11's ack never returns.) WT's ctrl+V paste-accelerator key-flood was the trigger; the deadlock is generic to ANY input flood.

+- **Invariant:** the applied-ack is OPT-IN. `InputReq` carries `ack: bool` (serde `default = true`, N-1-safe). The fire-and-forward operator/rc path (`serve_attach`) sends `ack=false` via `Brain::send_effect_no_ack`; `dispatch_input` writes NO applied frame when `ack=false`, so the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` (one-at-a-time spool delivery, WAITS on `BrokerEvent::Applied`) keeps `send_effect` (`ack=true`). EXACTLY-ONCE preserved: the broker dedups by `(session, op_id)` at the applied-set regardless of the ack. **N-1 caveat:** an OLD resident broker (the self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent broker-resident-wire-change class, see 7.9).

+- **spt-core mapping:** impl `msg.rs` `InputReq.ack` (`default_true`) + `brain.rs` `send_effect_no_ack`/`send_effect_inner` + `attach.rs` `serve_attach` operator path → `send_effect_no_ack` + `broker.rs` `dispatch_input` gates `send_frame(applied)` on `req.ack`; unit the serde default + the ack-emitted-iff-true + the no-ack-path exactly-once dedup; int (keystone, repro-first) a flood of N>pipe-buffer input frames through `serve_attach` on one conn — PRE-FIX deadlocks, POST-FIX drains all N + the session stays live + a concurrent attach opens (real broker+brain, no mocks).

+- **Source:** v0.13.0 P1b (operator HITL re-open; doyle /diagnose on the enhanced rc+broker capture 2026-06-19). The real ship-blocker behind the ctrl+V wedge.

+### 7.20 `spt rc` must forward the scroll wheel to the harness (our mouse capture steals WT's native scroll)  `[REQ-RC-MOUSE-FORWARD]`

+- **Failure (operator HITL):** scroll stopped working in `spt rc`. P1's `EnableMouseCapture` (for right-click paste, 7.18) makes Windows Terminal forward ALL mouse — including the wheel — to rc instead of scrolling its own buffer, but the rc mouse handler dropped everything except right-button-down → scroll DIED (and WT's native scrollback is stolen under the capture).

+- **Invariant:** on Windows, TRACK the harness's mouse-reporting mode from its OUTPUT — scan for DECSET `ESC[?1000h/1002h/1003h` (mouse on) + `ESC[?1006h` (SGR ext) and their `…l` (off) into a shared `MouseMode{enabled,sgr}` (the pump writes from the output render path, the stdin reader reads); the scan survives a sequence SPLIT across output chunks (a bounded carry buffer). The mouse handler: right-button-DOWN → bracketed clipboard paste (7.18, unchanged); `ScrollUp/Down` → an xterm SGR mouse report (`ESC[<64;col+1;row+1M` up / `ESC[<65;…M` down; 0-based crossterm → 1-based xterm), forwarded ONLY when `enabled && sgr` (else DROP — a legacy report the harness may misread is garbage); Moved/drag/left/middle DROP (scroll is the need; click-forward risks garbage, no click-to-position). UNIX UNCHANGED (no capture; the terminal scrolls natively).

+- **spt-core mapping:** impl `rc.rs` `MouseMode` + `MouseModeScanner`/`parse_decset_private`/`apply_mouse_mode` (carry-buffer scan, fed from the pump's output path) + `scroll_dir` + `scroll_sgr` + the `spawn_stdin_reader_events` scroll arm (threaded via `Arc<MouseMode>`); unit `scroll_dir` classify + `scroll_sgr` exact bytes + the DECSET scan (set/reset, combined, mixed, and split-across-chunks); NO int (live console mouse = HITL).

+- **Source:** v0.13.0 P1b (operator HITL; doyle design 2026-06-19). Bundled with 7.19.


Now the summary table rows (7.18 amend + 7.19/7.20). Read line 505.


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


docs\KNOWN-HAZARDS.md:518:[Omitted long matching line]
docs\KNOWN-HAZARDS.md:519:[Omitted long matching line]


517	| 7.16 | `spt rc` on Windows reads crossterm KEY EVENTS and translates to standard xterm VT (arrows/Home/End/PgUp/Dn/Ins/Del/F-keys + modifiers reach the harness; agnostic, NOT win32-input-mode); `ctrl-b d` detach preserved event-sourced; non-tty + Unix keep the byte path; supersedes the 7.13 byte-swap | `rc.rs` `translate_key_event`/`key_event_step`/`spawn_stdin_reader_events`, unit-only (live = HITL) |
518	| 7.17 | PTY **input** is single-writer: each session spawns ONE dedicated input-writer thread = the SOLE caller of the blocking `write_input`, fed by a bounded FIFO (`sync_channel`). Every caller (`dispatch_input`, `dispatch_endpoint_input`, the inject worker/floor flush) ENQUEUES (`try_send`) + returns at once — a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. Full queue ⇒ DROP excess + stamp the perch `input_backpressure` (heal-on-resume: cleared on the next accepted enqueue); the daemon NEVER wedges on a stuck harness. Completes the W1b-deferred fix (2) of 7.14. The OUTPUT-side single-writer (7.12) mirror, applied to input | broker `InputWriter`/`input_writer`/`flush_inject_floor`/`run_inject_worker`/`dispatch_input`, `spt_store::info::set_input_backpressure`, int `inject_control_wedge.rs` |
519	| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard, so forwarding `^V` can never paste. On a ctrl+V or right-click gesture rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic. Content is forwarded VERBATIM (literal text, no key translation). RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop; all non-right-down mouse is dropped (CC has no mouse features). cfg(windows) only — Unix pastes natively through the byte pump. Rides 7.17's single-writer so a paste chunk never wedges | `rc.rs` `wrap_bracketed_paste`/`is_ctrl_v`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |
520

| 7.16 | `spt rc` on Windows reads crossterm KEY EVENTS and translates to standard xterm VT (arrows/Home/End/PgUp/Dn/Ins/Del/F-keys + modifiers reach the harness; agnostic, NOT win32-input-mode); `ctrl-b d` detach preserved event-sourced; non-tty + Unix keep the byte path; supersedes the 7.13 byte-swap | `rc.rs` `translate_key_event`/`key_event_step`/`spawn_stdin_reader_events`, unit-only (live = HITL) |
| 7.17 | PTY **input** is single-writer: each session spawns ONE dedicated input-writer thread = the SOLE caller of the blocking `write_input`, fed by a bounded FIFO (`sync_channel`). Every caller (`dispatch_input`, `dispatch_endpoint_input`, the inject worker/floor flush) ENQUEUES (`try_send`) + returns at once — a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. Full queue ⇒ DROP excess + stamp the perch `input_backpressure` (heal-on-resume: cleared on the next accepted enqueue); the daemon NEVER wedges on a stuck harness. Completes the W1b-deferred fix (2) of 7.14. The OUTPUT-side single-writer (7.12) mirror, applied to input | broker `InputWriter`/`input_writer`/`flush_inject_floor`/`run_inject_worker`/`dispatch_input`, `spt_store::info::set_input_backpressure`, int `inject_control_wedge.rs` |
| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard, so forwarding `^V` can never paste. On a ctrl+V or right-click gesture rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic. Content is forwarded VERBATIM (literal text, no key translation). RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop; all non-right-down mouse is dropped (CC has no mouse features). cfg(windows) only — Unix pastes natively through the byte pump. Rides 7.17's single-writer so a paste chunk never wedges | `rc.rs` `wrap_bracketed_paste`/`is_ctrl_v`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |


| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard, so forwarding `^V` can never paste. On a ctrl+V or right-click gesture rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic. Content is forwarded VERBATIM (literal text, no key translation). RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop; all non-right-down mouse is dropped (CC has no mouse features). cfg(windows) only — Unix pastes natively through the byte pump. Rides 7.17's single-writer so a paste chunk never wedges | `rc.rs` `wrap_bracketed_paste`/`is_ctrl_v`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |

| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard. On a RIGHT-CLICK rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic, content VERBATIM. RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop. **ctrl+V is NOT intercepted** (P1b): WT consumes it (Key RELEASE only, never Press) + injects the clipboard as a key flood — it rides WT's native paste as keystrokes (no wedge, 7.19; bracketed = right-click). cfg(windows) only — Unix pastes natively | `rc.rs` `wrap_bracketed_paste`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |
| 7.19 | An operator input FLOOD must not deadlock the broker: `serve_attach` sends N `Input` frames on one conn without reading, the broker acks each on the SAME conn → the return direction fills (~pipe buffer) → `send_frame` blocks the per-conn handler → mutual full-duplex DEADLOCK (permanent broker wedge, controller latched). FIX: opt-in ack — `InputReq.ack` (serde default true); the operator/rc path sends `ack=false` (`send_effect_no_ack`), `dispatch_input` skips the applied frame → the handler never writes back while draining the flood. `shellchan` keeps `ack=true` (its `Applied`-wait). Exactly-once unaffected (dedup at the applied-set). N-1: an old resident broker still acks until restart (7.9 class) | `msg.rs` `InputReq.ack`, `brain.rs` `send_effect_no_ack`, `attach.rs` `serve_attach`, `broker.rs` `dispatch_input`, int (flood repro) |
| 7.20 | `spt rc` must forward the SCROLL wheel to the harness — our mouse capture (for right-click paste, 7.18) steals WT's native scroll. Track the harness's mouse-reporting mode from its OUTPUT (DECSET `ESC[?1000/1002/1003h` + `1006h` SGR, and `…l` off; scan survives a split across output chunks) into `MouseMode{enabled,sgr}`; forward `ScrollUp/Down` as an xterm SGR report (`ESC[<64/65;col+1;row+1M`) ONLY when `enabled && sgr`, else drop; Moved/drag/clicks dropped. cfg(windows) only — Unix scrolls natively | `rc.rs` `MouseMode`/`MouseModeScanner`/`scroll_dir`/`scroll_sgr`/`spawn_stdin_reader_events`, unit-only (live mouse = HITL) |


| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard. On a RIGHT-CLICK rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic, content VERBATIM. RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop. **ctrl+V is NOT intercepted** (P1b): WT consumes it (Key RELEASE only, never Press) + injects the clipboard as a key flood — it rides WT's native paste as keystrokes (no wedge, 7.19; bracketed = right-click). cfg(windows) only — Unix pastes natively | `rc.rs` `wrap_bracketed_paste`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |
| 7.19 | An operator input FLOOD must not deadlock the broker: `serve_attach` sends N `Input` frames on one conn without reading, the broker acks each on the SAME conn → the return direction fills (~pipe buffer) → `send_frame` blocks the per-conn handler → mutual full-duplex DEADLOCK (permanent broker wedge, controller latched). FIX: opt-in ack — `InputReq.ack` (serde default true); the operator/rc path sends `ack=false` (`send_effect_no_ack`), `dispatch_input` skips the applied frame → the handler never writes back while draining the flood. `shellchan` keeps `ack=true` (its `Applied`-wait). Exactly-once unaffected (dedup at the applied-set). N-1: an old resident broker still acks until restart (7.9 class) | `msg.rs` `InputReq.ack`, `brain.rs` `send_effect_no_ack`, `attach.rs` `serve_attach`, `broker.rs` `dispatch_input`, int (flood repro) |
| 7.20 | `spt rc` must forward the SCROLL wheel to the harness — our mouse capture (for right-click paste, 7.18) steals WT's native scroll. Track the harness's mouse-reporting mode from its OUTPUT (DECSET `ESC[?1000/1002/1003h` + `1006h` SGR, and `…l` off; scan survives a split across output chunks) into `MouseMode{enabled,sgr}`; forward `ScrollUp/Down` as an xterm SGR report (`ESC[<64/65;col+1;row+1M`) ONLY when `enabled && sgr`, else drop; Moved/drag/clicks dropped. cfg(windows) only — Unix scrolls natively | `rc.rs` `MouseMode`/`MouseModeScanner`/`scroll_dir`/`scroll_sgr`/`spawn_stdin_reader_events`, unit-only (live mouse = HITL) |

| 7.15 | `reconcile_hosted_liveness` clears stale `driven_by` when it offlines a sessionless controllable perch (no live broker session) — an OFFLINE endpoint never renders phantom `ONLINE+CONTROLLED`; race-free (no session ⇒ no concurrent broker re-stamp). `controller_by==None` is ambiguous (local controller reads None) so it is NOT a clear trigger; the idle wedged-remote leg is deferred (`REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT`) | `livehost.rs` `reconcile_hosted_liveness`, int `driven_by_selfheal.rs`/`inject_control_wedge.rs` |

| 7.16 | `spt rc` on Windows reads crossterm KEY EVENTS and translates to standard xterm VT (arrows/Home/End/PgUp/Dn/Ins/Del/F-keys + modifiers reach the harness; agnostic, NOT win32-input-mode); `ctrl-b d` detach preserved event-sourced; non-tty + Unix keep the byte path; supersedes the 7.13 byte-swap | `rc.rs` `translate_key_event`/`key_event_step`/`spawn_stdin_reader_events`, unit-only (live = HITL) |

| 7.17 | PTY **input** is single-writer: each session spawns ONE dedicated input-writer thread = the SOLE caller of the blocking `write_input`, fed by a bounded FIFO (`sync_channel`). Every caller (`dispatch_input`, `dispatch_endpoint_input`, the inject worker/floor flush) ENQUEUES (`try_send`) + returns at once — a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. Full queue ⇒ DROP excess + stamp the perch `input_backpressure` (heal-on-resume: cleared on the next accepted enqueue); the daemon NEVER wedges on a stuck harness. Completes the W1b-deferred fix (2) of 7.14. The OUTPUT-side single-writer (7.12) mirror, applied to input | broker `InputWriter`/`input_writer`/`flush_inject_floor`/`run_inject_worker`/`dispatch_input`, `spt_store::info::set_input_backpressure`, int `inject_control_wedge.rs` |

-| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard, so forwarding `^V` can never paste. On a ctrl+V or right-click gesture rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic. Content is forwarded VERBATIM (literal text, no key translation). RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop; all non-right-down mouse is dropped (CC has no mouse features). cfg(windows) only — Unix pastes natively through the byte pump. Rides 7.17's single-writer so a paste chunk never wedges | `rc.rs` `wrap_bracketed_paste`/`is_ctrl_v`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |

+| 7.18 | `spt rc` paste is **client-originated** on Windows: CC runs daemon-side with no access to the operator's LOCAL clipboard. On a RIGHT-CLICK rc reads the local clipboard itself and injects a BRACKETED paste (`ESC[200~` + content + `ESC[201~`) — CC has bracketed-paste mode on (`ESC[?2004h`), so a multi-line paste lands intact with NO `\r` submit-storm, harness-agnostic, content VERBATIM. RawGuard captures the mouse (disables console QuickEdit so right-click reaches the app) on an interactive console only + restores on drop. **ctrl+V is NOT intercepted** (P1b): WT consumes it (Key RELEASE only, never Press) + injects the clipboard as a key flood — it rides WT's native paste as keystrokes (no wedge, 7.19; bracketed = right-click). cfg(windows) only — Unix pastes natively | `rc.rs` `wrap_bracketed_paste`/`mouse_is_paste`/`clipboard_paste`/`read_clipboard`/`RawGuard`/`spawn_stdin_reader_events`, unit-only (live clipboard+mouse = HITL) |

+| 7.19 | An operator input FLOOD must not deadlock the broker: `serve_attach` sends N `Input` frames on one conn without reading, the broker acks each on the SAME conn → the return direction fills (~pipe buffer) → `send_frame` blocks the per-conn handler → mutual full-duplex DEADLOCK (permanent broker wedge, controller latched). FIX: opt-in ack — `InputReq.ack` (serde default true); the operator/rc path sends `ack=false` (`send_effect_no_ack`), `dispatch_input` skips the applied frame → the handler never writes back while draining the flood. `shellchan` keeps `ack=true` (its `Applied`-wait). Exactly-once unaffected (dedup at the applied-set). N-1: an old resident broker still acks until restart (7.9 class) | `msg.rs` `InputReq.ack`, `brain.rs` `send_effect_no_ack`, `attach.rs` `serve_attach`, `broker.rs` `dispatch_input`, int (flood repro) |

+| 7.20 | `spt rc` must forward the SCROLL wheel to the harness — our mouse capture (for right-click paste, 7.18) steals WT's native scroll. Track the harness's mouse-reporting mode from its OUTPUT (DECSET `ESC[?1000/1002/1003h` + `1006h` SGR, and `…l` off; scan survives a split across output chunks) into `MouseMode{enabled,sgr}`; forward `ScrollUp/Down` as an xterm SGR report (`ESC[<64/65;col+1;row+1M`) ONLY when `enabled && sgr`, else drop; Moved/drag/clicks dropped. cfg(windows) only — Unix scrolls natively | `rc.rs` `MouseMode`/`MouseModeScanner`/`scroll_dir`/`scroll_sgr`/`spawn_stdin_reader_events`, unit-only (live mouse = HITL) |


Make INPUT-ACK doc stage consistent (I wrote a tagged KNOWN-HAZARDS entry). Add `doc` to its stages.


required_stages = ["impl", "unit", "int"]  # ACTIVATED v0.13.0 P1b (todlando, the real ship-blocker behind the ctrl+V wedge; doyle gates). impl = InputReq.ack (serde default_true)

required_stages = ["doc", "impl", "unit", "int"]  # ACTIVATED v0.13.0 P1b (todlando, the real ship-blocker behind the ctrl+V wedge; doyle gates). doc = KNOWN-HAZARDS 7.19 (the deadlock + the N-1 broker-resident caveat). impl = InputReq.ack (serde default_true)


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

title = "A FLOOD of operator input on one brain↔broker connection deadlocks the broker PERMANENTLY (entire broker — no new/existing attach; the controller stays latched because the per-conn handler can't process the detach). ROOT (doyle /diagnose, code-grounded + HITL capture, the v0.13.0 P1 ctrl+V re-open): `serve_attach` processes a whole `NetStreamData` batch of N operator `Input` records in its inner `for rec in decoder.push()` loop, calling `brain.send_effect(op_id, &bytes)` N times WITHOUT returning to `read_event()` — so the brain writes N `KIND_INPUT` frames back-to-back and drains nothing. The broker's single-threaded per-conn handler answers EACH with `send_frame(applied_envelope)` on the SAME conn (B5 exactly-once ack, KNOWN-HAZARDS 7.2). With the brain not reading, the broker→brain return direction fills (~10 frames = the IPC pipe buffer) → `send_frame` BLOCKS → the handler stops reading → the brain's writes block too → mutual full-duplex DEADLOCK. Capture pinned it: 11 input frames, write_input 11/11 (P0 holds — the PTY write is fine), ack send START=11 / END=10 (frame #11's applied-ack never returns). Same class as the v0.12.1 L0 two-conn split. Windows Terminal's ctrl+V paste accelerator was the trigger (injects the clipboard as a char-by-char key flood) but the deadlock is generic to ANY input flood, NOT ctrl+V-specific and NOT a P0 (PTY-write) or W1 (output-drain) regression. The applied-ack is load-bearing ONLY for `shellchan` (one-at-a-time spool delivery WAITS on `BrokerEvent::Applied`); `serve_attach` DISCARDS it (the operator/rc path is fire-and-forward, op_id for dedup only, never gates on the ack). FIX (doyle-approved): CONDITIONAL ACK — `InputReq` gains `ack: bool` (serde default = true, N-1-safe: an older brain's input still acks = today's behavior). `serve_attach`'s operator path calls `send_effect_no_ack` (ack=false) → `dispatch_input` writes NO applied frame → the per-conn handler never writes back while servicing the flood → it always drains → no deadlock (cures ANY input flood). `shellchan` keeps `send_effect` (ack=true) and its `Applied`-wait. Exactly-once PRESERVED: the broker still dedups by (session, op_id) at the applied-set regardless of the ack. N-1 caveat: an OLD resident broker (self-update window) ignores `ack=false` → still acks → the deadlock persists until a broker restart (inherent KNOWN-HAZARDS 7.9 broker-resident-wire-change class). (v0.13.0)"

-required_stages = ["impl", "unit", "int"]  # ACTIVATED v0.13.0 P1b (todlando, the real ship-blocker behind the ctrl+V wedge; doyle gates). impl = InputReq.ack (serde default_true) + Brain::send_effect_no_ack (send_effect_inner shared body) + serve_attach operator path -> send_effect_no_ack + dispatch_input gates send_frame(applied) on req.ack. unit = InputReq.ack serde default=true on an absent field (N-1) + dispatch_input emits the applied frame when ack=true and NONE when ack=false + exactly-once on the no-ack path (a replayed/duplicate op_id still dedups at apply_once, no double PTY write). int = REPRO-FIRST (the keystone, the exact gate the wedge needed): a flood of N>pipe-buffer (deterministic, e.g. 64) operator input frames through serve_attach on ONE conn -> PRE-FIX deadlocks (ack-send-START-without-END, handler stops draining; a concurrent attach / the detach cannot be serviced) -> POST-FIX drains all N + the session stays live + a concurrent attach still OPENS. Real broker+brain, NO mocks. doc = KNOWN-HAZARDS entry (the deadlock + the N-1 broker-resident caveat). clippy/traceable/docs-drift."

+required_stages = ["doc", "impl", "unit", "int"]  # ACTIVATED v0.13.0 P1b (todlando, the real ship-blocker behind the ctrl+V wedge; doyle gates). doc = KNOWN-HAZARDS 7.19 (the deadlock + the N-1 broker-resident caveat). impl = InputReq.ack (serde default_true) + Brain::send_effect_no_ack (send_effect_inner shared body) + serve_attach operator path -> send_effect_no_ack + dispatch_input gates send_frame(applied) on req.ack. unit = InputReq.ack serde default=true on an absent field (N-1) + dispatch_input emits the applied frame when ack=true and NONE when ack=false + exactly-once on the no-ack path (a replayed/duplicate op_id still dedups at apply_once, no double PTY write). int = REPRO-FIRST (the keystone, the exact gate the wedge needed): a flood of N>pipe-buffer (deterministic, e.g. 64) operator input frames through serve_attach on ONE conn -> PRE-FIX deadlocks (ack-send-START-without-END, handler stops draining; a concurrent attach / the detach cannot be serviced) -> POST-FIX drains all N + the session stays live + a concurrent attach still OPENS. Real broker+brain, NO mocks. doc = KNOWN-HAZARDS entry (the deadlock + the N-1 broker-resident caveat). clippy/traceable/docs-drift."

# ── P1b sibling (operator HITL 2026-06-19): rc scroll broken — capture-stolen wheel must forward to the harness ──


Amend CONTEXT note (right-click-only + deadlock + scroll). Find it.


Grep

rc paste is client-originated|ctrl\+V or right-click


CONTEXT.md:438:[Omitted long matching line]


P1 ctrl+V wedge — diagnose then fix

Primary fix: conditional ack


437	**input injection**:
438	Two granularities, both first-class. `send-keys` — raw byte stream including escape sequences (e.g. `Ctrl-C`). `send-line` — cooked line-level input flushed on newline (the common case; matches how user input reaches an agent prompt). **PTY input is single-writer (v0.13.0 P0, KNOWN-HAZARDS 7.17):** broker-side, every write to a hosted session's PTY goes through ONE per-session input-writer thread — the sole caller of the blocking `write_input`, fed by a bounded FIFO. All callers (operator keystrokes, message injection, the inject-floor flush) ENQUEUE and return immediately, so a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. A genuinely wedged harness (queue full) DROPS excess input + stamps the perch `input_backpressure` (a visible signal, healed on resume); the daemon never wedges. This is the input-side mirror of the output-side single-writer (controller/viewer writer threads, 7.12). **rc paste is client-originated on Windows (v0.13.0 P1, KNOWN-HAZARDS 7.18):** the harness runs daemon-side with no access to the operator's local clipboard, so `spt rc` itself reads the local clipboard on a ctrl+V or right-click gesture and injects a *bracketed* paste (`ESC[200~`…`ESC[201~`) — the harness's bracketed-paste mode lands a multi-line paste intact with no per-newline submit-storm, content verbatim. cfg(windows) only; Unix terminals paste natively through the byte pump.
439

**input injection**:
Two granularities, both first-class. `send-keys` — raw byte stream including escape sequences (e.g. `Ctrl-C`). `send-line` — cooked line-level input flushed on newline (the common case; matches how user input reaches an agent prompt). **PTY input is single-writer (v0.13.0 P0, KNOWN-HAZARDS 7.17):** broker-side, every write to a hosted session's PTY goes through ONE per-session input-writer thread — the sole caller of the blocking `write_input`, fed by a bounded FIFO. All callers (operator keystrokes, message injection, the inject-floor flush) ENQUEUE and return immediately, so a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. A genuinely wedged harness (queue full) DROPS excess input + stamps the perch `input_backpressure` (a visible signal, healed on resume); the daemon never wedges. This is the input-side mirror of the output-side single-writer (controller/viewer writer threads, 7.12). **rc paste is client-originated on Windows (v0.13.0 P1, KNOWN-HAZARDS 7.18):** the harness runs daemon-side with no access to the operator's local clipboard, so `spt rc` itself reads the local clipboard on a ctrl+V or right-click gesture and injects a *bracketed* paste (`ESC[200~`…`ESC[201~`) — the harness's bracketed-paste mode lands a multi-line paste intact with no per-newline submit-storm, content verbatim. cfg(windows) only; Unix terminals paste natively through the byte pump.


Edit

**rc paste is client-originated on Windows (v0.13.0 P1, KNOWN-HAZARDS 7.18):** the harness runs daemon-side with no access to the operator's local clipboard, so `spt rc` itself reads the local clipboard on a ctrl+V or right-click gesture and injects a *bracketed* paste (`ESC[200~`…`ESC[201~`) — the harness's bracketed-paste mode lands a multi-line paste intact with no per-newline submit-storm, content verbatim. cfg(windows) only; Unix terminals paste natively through the byte pump.

**rc paste is client-originated on Windows (v0.13.0 P1/P1b, KNOWN-HAZARDS 7.18):** the harness runs daemon-side with no access to the operator's local clipboard, so `spt rc` itself reads the local clipboard on a **right-click** and injects a *bracketed* paste (`ESC[200~`…`ESC[201~`) — the harness's bracketed-paste mode lands a multi-line paste intact with no per-newline submit-storm, content verbatim. (ctrl+V is NOT intercepted: Windows Terminal consumes it as its own paste accelerator and injects the clipboard as keystrokes — which the broker no longer wedges on, 7.19.) Because that capture also steals WT's native scroll, rc forwards the **scroll wheel** to the harness as SGR mouse reports when the harness has mouse reporting on (7.20). cfg(windows) only; Unix terminals paste + scroll natively through the byte pump. **An operator input flood must not deadlock the broker (v0.13.0 P1b, KNOWN-HAZARDS 7.19):** the applied-ack is opt-in (`InputReq.ack`) — the fire-and-forward rc path sends no-ack so the broker's per-conn handler never blocks writing an ack back onto the same conn it is draining a flood from; `shellchan` (which waits on the ack) keeps it. Exactly-once is unaffected (dedup at the applied-set).


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

The trait spt-core exposes over "anything you can write input to and read output from" — delivered in M3a as `spt_term::SessionSurface` (`write_input` + `resize`, with `send_keys`/`send_line` as first-class injection). Native PTY (`spt_term::PtySession`: ConPTY on Windows 10+, `forkpty(3)` on Unix, via the `portable-pty` crate) is the day-one implementation. The abstraction generalizes so future implementations — network-attached surfaces (for `spt-node` remote control), GUI text panes, etc. — are uniform to consumers. Pre-Windows-10 (winpty) is explicitly dropped. **OS-neutral by contract (Spike #4/#5):** the trait bakes in neither `forkpty`'s raw-ordered-pipe nor ConPTY's repaint-on-resize screen-buffer semantics; output is a plain byte stream (`spt_term::OutputStream`, bounded backpressure) with OS-independent replay.

-Two granularities, both first-class. `send-keys` — raw byte stream including escape sequences (e.g. `Ctrl-C`). `send-line` — cooked line-level input flushed on newline (the common case; matches how user input reaches an agent prompt). **PTY input is single-writer (v0.13.0 P0, KNOWN-HAZARDS 7.17):** broker-side, every write to a hosted session's PTY goes through ONE per-session input-writer thread — the sole caller of the blocking `write_input`, fed by a bounded FIFO. All callers (operator keystrokes, message injection, the inject-floor flush) ENQUEUE and return immediately, so a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. A genuinely wedged harness (queue full) DROPS excess input + stamps the perch `input_backpressure` (a visible signal, healed on resume); the daemon never wedges. This is the input-side mirror of the output-side single-writer (controller/viewer writer threads, 7.12). **rc paste is client-originated on Windows (v0.13.0 P1, KNOWN-HAZARDS 7.18):** the harness runs daemon-side with no access to the operator's local clipboard, so `spt rc` itself reads the local clipboard on a ctrl+V or right-click gesture and injects a *bracketed* paste (`ESC[200~`…`ESC[201~`) — the harness's bracketed-paste mode lands a multi-line paste intact with no per-newline submit-storm, content verbatim. cfg(windows) only; Unix terminals paste natively through the byte pump.

+Two granularities, both first-class. `send-keys` — raw byte stream including escape sequences (e.g. `Ctrl-C`). `send-line` — cooked line-level input flushed on newline (the common case; matches how user input reaches an agent prompt). **PTY input is single-writer (v0.13.0 P0, KNOWN-HAZARDS 7.17):** broker-side, every write to a hosted session's PTY goes through ONE per-session input-writer thread — the sole caller of the blocking `write_input`, fed by a bounded FIFO. All callers (operator keystrokes, message injection, the inject-floor flush) ENQUEUE and return immediately, so a paste burst that fills the harness input buffer parks only that thread, never the broker dispatch thread. A genuinely wedged harness (queue full) DROPS excess input + stamps the perch `input_backpressure` (a visible signal, healed on resume); the daemon never wedges. This is the input-side mirror of the output-side single-writer (controller/viewer writer threads, 7.12). **rc paste is client-originated on Windows (v0.13.0 P1/P1b, KNOWN-HAZARDS 7.18):** the harness runs daemon-side with no access to the operator's local clipboard, so `spt rc` itself reads the local clipboard on a **right-click** and injects a *bracketed* paste (`ESC[200~`…`ESC[201~`) — the harness's bracketed-paste mode lands a multi-line paste intact with no per-newline submit-storm, content verbatim. (ctrl+V is NOT intercepted: Windows Terminal consumes it as its own paste accelerator and injects the clipboard as keystrokes — which the broker no longer wedges on, 7.19.) Because that capture also steals WT's native scroll, rc forwards the **scroll wheel** to the harness as SGR mouse reports when the harness has mouse reporting on (7.20). cfg(windows) only; Unix terminals paste + scroll natively through the byte pump. **An operator input flood must not deadlock the broker (v0.13.0 P1b, KNOWN-HAZARDS 7.19):** the applied-ack is opt-in (`InputReq.ack`) — the fire-and-forward rc path sends no-ack so the broker's per-conn handler never blocks writing an ack back onto the same conn it is draining a flood from; `shellchan` (which waits on the ack) keeps it. Exactly-once is unaffected (dedup at the applied-set).

In-memory ring buffer per session for v1. **On-disk spillover for long sessions is an intended, tracked feature** (deferred past v1, not forgotten) — see `docs/DEFERRED.md`.


Docs done. Verify traceable + docs-drift before delegating tests.


Bash

cargo run -p xtask -- check 2>&1 | tail -3; echo "=== traceable ==="; traceable-reqs check 2>&1 | grep -iE "INPUT-ACK-BACKPRESSURE|RC-MOUSE-FORWARD|RC-WIN-PASTE"

Docs-drift + traceable for new reqs


    Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.13s
     Running `target\debug\xtask.exe check`
xtask check: OK
=== traceable ===
  [--] REQ-HAZARD-INPUT-ACK-BACKPRESSURE  required: [doc, impl, unit, int]  stages: +doc +impl -unit -int
  [OK] REQ-RC-MOUSE-FORWARD  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       On Windows, `spt rc` must FORWARD scroll-wheel events to the harness when the harness has mouse reporting on. ROOT (operator HITL): P1's RawGuard EnableMouseCapture (added for right-click paste, REQ-RC-WIN-PASTE) makes Windows Terminal forward ALL mouse — including the scroll wheel — to rc instead of scrolling its own buffer, but the rc mouse handler dropped everything except right-button-down → scroll DIED (and WT's native scrollback is stolen by the capture). Operator ruling: keep mouse capture + right-click bracketed paste AND forward scroll to the harness. FIX (doyle design, cfg(windows), folds into the rc mouse handler): TRACK the harness's mouse-reporting mode by scanning its OUTPUT stream for the DECSET set/reset — ESC[?1000h/1002h/1003h (mouse on) + ESC[?1006h (SGR ext) and their ...l (off) — into a shared MouseMode{enabled,sgr} (pump writes from output, stdin reader reads); the scan survives a sequence SPLIT across output chunks (a bounded carry buffer). The mouse handler: right-button-DOWN -> bracketed clipboard paste (unchanged, REQ-RC-WIN-PASTE); ScrollUp/Down -> translate to an xterm SGR mouse report (ESC[<64;col+1;row+1M up / ESC[<65;..M down, 0-based crossterm -> 1-based xterm) and forward ONLY when enabled && sgr (else DROP — a legacy X10 report the harness may not parse is garbage); Moved/drag/left/middle -> DROP (scroll is the operator's need; click-forward risks garbage, no click-to-position). Unix UNCHANGED (no capture; the terminal scrolls natively). (v0.13.0)
  [OK] REQ-RC-WIN-PASTE  required: [doc, impl, unit]  stages: +doc +impl +unit -int
  [must] missing_stage REQ-HAZARD-INPUT-ACK-BACKPRESSURE stage=int — Requirement is missing integration test evidence
  [must] missing_stage REQ-HAZARD-INPUT-ACK-BACKPRESSURE stage=unit — Requirement is missing unit test evidence


REQ-RC-MOUSE-FORWARD + REQ-RC-WIN-PASTE fully covered (+doc+impl+unit); INPUT-ACK has +doc+impl, needs unit+int. Docs-drift OK. Delegating the broker deadlock int + ack units to spt-test-engineer.


Updated task #17 status


Updated task #19 status


Updated task #18 status


Agent

Write input-ack deadlock tests

Prompt:
Write unit + integration tests for v0.13.0 P1b: REQ-HAZARD-INPUT-ACK-BACKPRESSURE (the input-flood broker deadlock). The impl is DONE on branch `v0.13.0-p1b-ack-deadlock` (HEAD is your working tree). Your job is ONLY tests + their evidence tags. Do NOT change the impl unless a test reveals a real bug (flag it to me, don't silently rewrite).

## The bug + fix (so your tests are non-vacuous)
A flood of operator input on ONE brain↔broker conn permanently DEADLOCKED the whole broker. `serve_attach` (attach.rs) processes a whole `NetStreamData` batch of N `Input` records in its inner `for rec in decoder.push()` loop, calling `brain.send_effect(op_id, &bytes)` N times WITHOUT returning to `read_event()`. The broker's single-threaded per-conn handler answered EACH KIND_INPUT with `send_frame(applied_envelope)` on the SAME conn. With the brain not reading, the broker→brain return direction fills (~10 frames = IPC pipe buffer) → `send_frame` BLOCKS → handler stops reading → brain's writes block → mutual full-duplex deadlock (permanent broker wedge; the controller stays latched because the handler can't process the detach).

FIX (committed): `InputReq` gained `ack: bool` (msg.rs, serde `default = "default_true"`). The fire-and-forward operator path (`serve_attach` controller arm) now calls `Brain::send_effect_no_ack` (ack=false); `dispatch_input` (broker.rs) only `send_frame(applied_envelope)` when `req.ack`. `shellchan` keeps `send_effect` (ack=true) and its `BrokerEvent::Applied` wait (shellchan.rs:210). Exactly-once is preserved: the broker dedups by `(session, op_id)` at the journal applied-set regardless of the ack.

## Read first
- `crates/spt-daemon/src/msg.rs` InputReq (the new `ack` field + `default_true`).
- `crates/spt-daemon/src/brain.rs` `send_effect` / `send_effect_no_ack` / `send_effect_inner`.
- `crates/spt-daemon/src/attach.rs` `serve_attach` (the `Some(ServeRole::Controller) => brain.send_effect_no_ack(...)` arm).
- `crates/spt-daemon/src/broker.rs` `dispatch_input` (the `if req.ack { send_frame(...applied...) }` gate).
- `crates/spt-daemon/tests/broker.rs` and `crates/spt-daemon/tests/inject_control_wedge.rs` — mirror their REAL broker (+ real PTY where needed) harness patterns, NO mocks. Study how they spin a broker, open a conn, send KIND_INPUT, read frames.

## Tests to add — tag `// [unit->REQ-HAZARD-INPUT-ACK-BACKPRESSURE]` / `// [int->REQ-HAZARD-INPUT-ACK-BACKPRESSURE]` ON/ABOVE the real evidence.

UNIT (prefer `crates/spt-daemon/src/msg.rs` #[cfg(test)] for the serde one; the ack-gate + exactly-once ones can be a real-broker test in `tests/broker.rs` if a pure unit isn't clean — your call):
1. **serde default N-1**: an `input` payload JSON with NO `ack` field deserializes to `InputReq { ack: true, .. }` (the old-brain wire stays acked). Also round-trips `ack:false`.
2. **ack gate**: against a REAL broker + a real spawned PTY session, send a KIND_INPUT with `op_id=Some` and `ack=true` → the broker replies a `KIND_APPLIED` frame; send one with `ack=false` → NO applied frame is sent (assert the next frame is NOT an applied / a bounded read sees none). This is the crux unit — prove the gate.
3. **exactly-once on the no-ack path**: replay the SAME `(session, op_id)` with `ack=false` twice → the byte is written to the PTY exactly ONCE (dedup at apply_once), no double-type. (Mirror the existing idempotency test in `tests/broker.rs`/`idempotent.rs`.)

INT (KEYSTONE, REPRO-FIRST, the exact gate the wedge needed) — in `crates/spt-daemon/tests/inject_control_wedge.rs` (or a new `tests/input_ack_deadlock.rs` if cleaner), real broker + real brain/serve_attach, NO mocks:
4. Drive a FLOOD of **N=64** (deterministic, > the ~10 IPC pipe buffer) operator `Input` records through the real `serve_attach` path on ONE conn (the rc→net-stream→serve_attach→send_effect_no_ack route — mirror how an rc attach is wired; the loopback/net-stream attach in the existing int tests is the model).
   - **PRE-FIX (document the RED in a comment, and ideally prove it):** with `send_effect` (ack=true), the flood deadlocks — the broker per-conn handler blocks on the ack send (ack-send-START-without-END), a concurrent attach or the detach can't be serviced. If you can express the pre-fix RED by a local toggle/comment, do; at minimum the test must be structured so reverting the fix (re-acking) makes it hang/fail.
   - **POST-FIX (GREEN):** all 64 frames drain (reach the PTY), the session stays live, AND a CONCURRENT `spt rc`/attach still OPENS + is serviced (subscribe returns, gets output) while/after the flood. The detach registers. This is the non-vacuous proof the deadlock is gone.
   - Use a bounded watchdog so a regression FAILS (deadlock) rather than hangs the suite forever (mirror inject_control_wedge.rs's watchdog/teardown discipline).

## Gates (run before declaring done)
- spt-daemon e2e/int needs `--test-threads=1`. Run `cargo test -p spt-daemon -- --test-threads=1` GREEN (note: `w5_a2_abandoned` is a pre-existing flaky neighbor unrelated to this — if it hangs, run with `--skip w5_a2_abandoned` and say so).
- `cargo clippy --workspace --all-targets -- -D warnings` = 0.
- After your tests land, `traceable-reqs check` must show `+unit +int` for REQ-HAZARD-INPUT-ACK-BACKPRESSURE (currently `-unit -int`).
- Prove non-vacuousness: describe (comment) how each goes RED if the fix were reverted (the int's ack re-enabled → deadlock; the unit's gate → applied frame appears when ack=false).

Report back: test names added, the cargo test pass line, traceable +unit/+int confirmation, and any impl seam you had to touch (flag, don't hide). Work on the current branch `v0.13.0-p1b-ack-deadlock`; my impl is committed there shortly. Leave the rc.rs pure units alone (already written + passing) — your scope is the broker/brain ack-deadlock.


<owl_reachability_notice>
You have an active owl perch. Launch this Agent with run_in_background: true so you stay reachable for incoming messages while it works.
</owl_reachability_notice>


Async agent launched successfully.
agentId: abf0df6a952b1b22c (internal ID - do not mention to user. Use SendMessage with to: 'abf0df6a952b1b22c' to continue this agent.)
The agent is working in the background. You will be notified automatically when it completes.
Do not duplicate this agent's work — avoid working with the same files or topics it is using. Work on non-overlapping tasks, or briefly tell the user what you launched and end your response.
output_file: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\abf0df6a952b1b22c.output
Do NOT Read or tail this file via the shell tool — it is the full subagent JSONL transcript and reading it will overflow your context. If the user asks for progress, say the agent is still running; you'll get a completion notification.

Write input-ack deadlock tests

Prompt:
Write unit + integration tests for v0.13.0 P1b: REQ-HAZARD-INPUT-ACK-BACKPRESSURE (the input-flood broker deadlock). The impl is DONE on branch `v0.13.0-p1b-ack-deadlock` (HEAD is your working tree). Your job is ONLY tests + their evidence tags. Do NOT change the impl unless a test reveals a real bug (flag it to me, don't silently rewrite).

## The bug + fix (so your tests are non-vacuous)
A flood of operator input on ONE brain↔broker conn permanently DEADLOCKED the whole broker. `serve_attach` (attach.rs) processes a whole `NetStreamData` batch of N `Input` records in its inner `for rec in decoder.push()` loop, calling `brain.send_effect(op_id, &bytes)` N times WITHOUT returning to `read_event()`. The broker's single-threaded per-conn handler answered EACH KIND_INPUT with `send_frame(applied_envelope)` on the SAME conn. With the brain not reading, the broker→brain return direction fills (~10 frames = IPC pipe buffer) → `send_frame` BLOCKS → handler stops reading → brain's writes block → mutual full-duplex deadlock (permanent broker wedge; the controller stays latched because the handler can't process the detach).

FIX (committed): `InputReq` gained `ack: bool` (msg.rs, serde `default = "default_true"`). The fire-and-forward operator path (`serve_attach` controller arm) now calls `Brain::send_effect_no_ack` (ack=false); `dispatch_input` (broker.rs) only `send_frame(applied_envelope)` when `req.ack`. `shellchan` keeps `send_effect` (ack=true) and its `BrokerEvent::Applied` wait (shellchan.rs:210). Exactly-once is preserved: the broker dedups by `(session, op_id)` at the journal applied-set regardless of the ack.

## Read first
- `crates/spt-daemon/src/msg.rs` InputReq (the new `ack` field + `default_true`).
- `crates/spt-daemon/src/brain.rs` `send_effect` / `send_effect_no_ack` / `send_effect_inner`.
- `crates/spt-daemon/src/attach.rs` `serve_attach` (the `Some(ServeRole::Controller) => brain.send_effect_no_ack(...)` arm).
- `crates/spt-daemon/src/broker.rs` `dispatch_input` (the `if req.ack { send_frame(...applied...) }` gate).
- `crates/spt-daemon/tests/broker.rs` and `crates/spt-daemon/tests/inject_control_wedge.rs` — mirror their REAL broker (+ real PTY where needed) harness patterns, NO mocks. Study how they spin a broker, open a conn, send KIND_INPUT, read frames.

## Tests to add — tag `// [unit->REQ-HAZARD-INPUT-ACK-BACKPRESSURE]` / `// [int->REQ-HAZARD-INPUT-ACK-BACKPRESSURE]` ON/ABOVE the real evidence.

UNIT (prefer `crates/spt-daemon/src/msg.rs` #[cfg(test)] for the serde one; the ack-gate + exactly-once ones can be a real-broker test in `tests/broker.rs` if a pure unit isn't clean — your call):
1. **serde default N-1**: an `input` payload JSON with NO `ack` field deserializes to `InputReq { ack: true, .. }` (the old-brain wire stays acked). Also round-trips `ack:false`.
2. **ack gate**: against a REAL broker + a real spawned PTY session, send a KIND_INPUT with `op_id=Some` and `ack=true` → the broker replies a `KIND_APPLIED` frame; send one with `ack=false` → NO applied frame is sent (assert the next frame is NOT an applied / a bounded read sees none). This is the crux unit — prove the gate.
3. **exactly-once on the no-ack path**: replay the SAME `(session, op_id)` with `ack=false` twice → the byte is written to the PTY exactly ONCE (dedup at apply_once), no double-type. (Mirror the existing idempotency test in `tests/broker.rs`/`idempotent.rs`.)

INT (KEYSTONE, REPRO-FIRST, the exact gate the wedge needed) — in `crates/spt-daemon/tests/inject_control_wedge.rs` (or a new `tests/input_ack_deadlock.rs` if cleaner), real broker + real brain/serve_attach, NO mocks:
4. Drive a FLOOD of **N=64** (deterministic, > the ~10 IPC pipe buffer) operator `Input` records through the real `serve_attach` path on ONE conn (the rc→net-stream→serve_attach→send_effect_no_ack route — mirror how an rc attach is wired; the loopback/net-stream attach in the existing int tests is the model).
   - **PRE-FIX (document the RED in a comment, and ideally prove it):** with `send_effect` (ack=true), the flood deadlocks — the broker per-conn handler blocks on the ack send (ack-send-START-without-END), a concurrent attach or the detach can't be serviced. If you can express the pre-fix RED by a local toggle/comment, do; at minimum the test must be structured so reverting the fix (re-acking) makes it hang/fail.
   - **POST-FIX (GREEN):** all 64 frames drain (reach the PTY), the session stays live, AND a CONCURRENT `spt rc`/attach still OPENS + is serviced (subscribe returns, gets output) while/after the flood. The detach registers. This is the non-vacuous proof the deadlock is gone.
   - Use a bounded watchdog so a regression FAILS (deadlock) rather than hangs the suite forever (mirror inject_control_wedge.rs's watchdog/teardown discipline).

## Gates (run before declaring done)
- spt-daemon e2e/int needs `--test-threads=1`. Run `cargo test -p spt-daemon -- --test-threads=1` GREEN (note: `w5_a2_abandoned` is a pre-existing flaky neighbor unrelated to this — if it hangs, run with `--skip w5_a2_abandoned` and say so).
- `cargo clippy --workspace --all-targets -- -D warnings` = 0.
- After your tests land, `traceable-reqs check` must show `+unit +int` for REQ-HAZARD-INPUT-ACK-BACKPRESSURE (currently `-unit -int`).
- Prove non-vacuousness: describe (comment) how each goes RED if the fix were reverted (the int's ack re-enabled → deadlock; the unit's gate → applied frame appears when ack=false).

Report back: test names added, the cargo test pass line, traceable +unit/+int confirmation, and any impl seam you had to touch (flag, don't hide). Work on the current branch `v0.13.0-p1b-ack-deadlock`; my impl is committed there shortly. Leave the rc.rs pure units alone (already written + passing) — your scope is the broker/brain ack-deadlock.


Impl + rc units + docs + registry all done. Test agent (spt-test-engineer) is writing the broker deadlock int (N=64 flood repro) + ack units in the background — I'll commit the full bundle once it reports (staying off the broker/brain/msg test files to avoid overlap).

**P1b-rework bundle status** (branch `v0.13.0-p1b-ack-deadlock` off f7cf380):
- **Primary (deadlock):** `InputReq.ack` (serde default true, N-1-safe); operator path → `send_effect_no_ack`; `dispatch_input` gates the applied-ack on `req.ack`; shellchan keeps acking. Cures any input-flood deadlock; exactly-once preserved.
- **Secondary (ctrl+V):** removed the dead interception (WT delivers RELEASE-only + injects a key flood). Right-click bracketed paste kept.
- **Scroll-forward (new sibling REQ-RC-MOUSE-FORWARD):** track harness mouse-mode via DECSET scan on output (split-chunk safe), forward ScrollUp/Down as SGR reports when enabled+sgr.
- **Tests:** rc pure units 15/15 (scroll classify, scroll→SGR exact bytes, DECSET scan + split-chunk). Broker int+ack units delegated.
- **Gates so far:** clippy `-D warnings`=0 · docs-drift OK · traceable: REQ-RC-MOUSE-FORWARD +doc+impl+unit ✅, REQ-RC-WIN-PASTE +doc+impl+unit ✅ (ctrl+V removal didn't drop coverage), INPUT-ACK +doc+impl (unit/int pending agent).
- **Docs:** KNOWN-HAZARDS 7.18 amended + 7.19 (deadlock) + 7.20 (scroll); CONTEXT note updated.

Awaiting the test agent, then full gates → commit → surface the whole bundle green to doyle. Reachable.


<task-notification>
<task-id>bh6zpavhs</task-id>
<tool-use-id>toolu_015fKgivQFGcAJ8MXLHGTemZ</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bh6zpavhs.output</output-file>
<status>completed</status>
<summary>Background command "Run the ack-gate and exactly-once broker tests" completed (exit code 0)</summary>
</task-notification>


<task-notification>
<task-id>bkr24c80v</task-id>
<tool-use-id>toolu_01X6E49rDXuEj7yhbu4rspqY</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bkr24c80v.output</output-file>
<status>failed</status>
<summary>Background command "Run ack-gate + exactly-once broker tests, capture output" failed with exit code 255</summary>
</task-notification>


<task-notification>
<task-id>bgsym9x9x</task-id>
<tool-use-id>toolu_01Mta632gvZ9PSjbVTMNyxUH</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bgsym9x9x.output</output-file>
<status>completed</status>
<summary>Background command "Run the keystone ack-deadlock int test" completed (exit code 0)</summary>
</task-notification>


<task-notification>
<task-id>bs4gl5xqm</task-id>
<tool-use-id>toolu_01GxU1J222aWkeZHJ6iTXMG1</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bs4gl5xqm.output</output-file>
<status>completed</status>
<summary>Background command "Run keystone test, capture to file" completed (exit code 0)</summary>
</task-notification>


<task-notification>
<task-id>bxsi5ywpa</task-id>
<tool-use-id>toolu_014T279Sn8ZhnEVgCfBgn9Di</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\bxsi5ywpa.output</output-file>
<status>killed</status>
<summary>Background command "Wait for broker test result line" was stopped</summary>
</task-notification>


<task-notification>
<task-id>abf0df6a952b1b22c</task-id>
<tool-use-id>toolu_01MLaVQkknbXkKjGzXUZKUST</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\97787e5e-64b2-4afd-b3fe-becaa2ea8030\tasks\abf0df6a952b1b22c.output</output-file>
<status>completed</status>
<summary>Agent "Write input-ack deadlock tests" came to rest</summary>
<note>A task-notification fires each time this agent comes to rest with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note>
<result>All evidence tags are correctly placed on/above the real test evidence. Everything is verified and green.

## Summary

I wrote and verified the unit + integration tests for **v0.13.0 P1b: REQ-HAZARD-INPUT-ACK-BACKPRESSURE** (the input-flood broker deadlock). I touched only test code — the impl seam was left as committed (I temporarily reverted `serve_attach`'s controller arm twice to prove non-vacuousness, and restored it both times; the final state is `send_effect_no_ack`).

### Tests added

**Unit — serde (in `crates/spt-daemon/src/msg.rs` `#[cfg(test)]`):**
- `input_req_absent_ack_defaults_true_n_minus_1_safe` — an old-brain `input` payload with no `ack` field decodes to `ack=true` (N-1-safe).
- `input_req_ack_false_round_trips_faithfully` — `ack=false` serializes explicitly and survives the round-trip (no default-clobber).

**Unit/int — real broker + real PTY (in `crates/spt-daemon/tests/broker.rs`):**
- `ack_false_input_suppresses_applied_frame` — the **crux ack-gate**: against a real broker+PTY, `ack=false` produces NO `KIND_APPLIED` frame while `ack=true` does, proven by FIFO ordering (the first applied seen must carry the acked probe's op_id, never the no-ack op's). Both bytes still reach the PTY.
- `exactly_once_preserved_on_no_ack_path` — three replays of the same `(session, op_id)` with `ack=false` apply exactly once (authoritative `broker.journal().applied_count()==1` anchor, mirroring `idempotent.rs`).

**Int — keystone, new file `crates/spt-daemon/tests/input_ack_deadlock.rs`:**
- `input_flood_through_serve_attach_does_not_deadlock_broker` — floods FLOOD_N=64 controller `Input` records through the real `serve_attach` controller path (→ `send_effect_no_ack`) on one loopback conn, then proves the broker stays serviceable via direct `KIND_SESSIONS`/`KIND_NET_STATUS` probes on a fresh conn plus `flood_sent`. All waits are watchdog-bounded.

### Pass line / gates
- `cargo test -p spt-daemon --lib` → `348 passed` (incl. the 2 serde units)
- `cargo test -p spt-daemon --test broker -- --test-threads=1` → `7 passed` (5 existing + my 2)
- `cargo test -p spt-daemon --test input_ack_deadlock -- --test-threads=1` → `1 passed` (~32s)
- `cargo clippy --workspace --all-targets -- -D warnings` → 0
- `traceable-reqs check` → EXIT 0, `REQ-HAZARD-INPUT-ACK-BACKPRESSURE` shows `+doc +impl +unit +int` (was `-unit -int`)

### Non-vacuousness (empirically verified, not just claimed)
- **ack-gate unit:** reverting the `if req.ack` gate so `dispatch_input` always sends the applied frame makes the no-ack op's applied arrive first → assert flips RED.
- **keystone:** I reverted `serve_attach`'s controller arm to `send_effect` (ack=true) and ran it. At the spec's N=64 it is **forkpty-only RED** (Windows named pipes buffer past 64 small frames, so it's benign there — the same banked platform-divergence stance the W1b/P0 sibling wedge tests use; gravity-linux/cfg(unix) carries the deterministic RED). I additionally verified at **N≈2000 on revert the deadlock reproduces even on Windows** (`flood_sent=false`), confirming the mechanism cross-platform — but at that N the in-process loopback net send-window throttles even the fixed path, so 64 is the clean cross-platform GREEN value. This trade-off is documented in the test's module doc.

### Important harness traps surfaced (documented in the test + agent memory)
- `read_frame` on a Windows named pipe **ignores deadlines and blocks** once the broker stops pushing — every read loop is now on a worker thread bounded by `recv_timeout`. A naive trailing "settle" loop hung the suite during development; removed.
- A non-draining flood operator **self-stalls on its own conn** regardless of the fix → the flooder uses `cold_start_pump`. `net_stream_subscribe().is_ok()` only means "sent," not "serviced" → I use a direct `KIND_SESSIONS` probe as the liveness oracle. The keystone uses a **quiet child** (no output) to avoid confounding with the W1 output-drain hazard, and abandons (doesn't join) the serve threads at teardown.

### No product defects uncovered
The fix behaves exactly as specified: the no-ack path suppresses the applied frame, exactly-once is preserved at the journal applied-set, and the broker does not deadlock under flood.

Relevant files (all absolute):
- `C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\msg.rs` (2 serde unit tests, lines ~985–1060)
- `C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\tests\broker.rs` (2 tests + helpers)
- `C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\tests\input_ack_deadlock.rs` (keystone, new)

Run the full new suite locally with: `cargo test -p spt-daemon --lib input_req -- --test-threads=1` and `cargo test -p spt-daemon --test broker --test input_ack_deadlock -- --test-threads=1` (or `cargo nextest run -p spt-daemon` in CI). On gravity-linux, the keystone's RED-on-revert is deterministic at N=64.</result>
<usage><subagent_tokens>239930</subagent_tokens><tool_uses>153</tool_uses><duration_ms>5080912</duration_ms></usage>
</task-notification>


1	//! The broker↔brain message catalog: the kind discriminators and payload
2	//! shapes that ride inside the forward-compatible [`Envelope`].
3	//!
4	//! Split out from [`crate::frame`] (which owns the *envelope mechanics* and the
5	//! version handshake) so the message *vocabulary* can grow additively without
6	//! touching the wire-compat machinery. Every payload is a plain serde struct
7	//! with serde's default unknown-field tolerance, so a newer brain may add a
8	//! field and an older broker still decodes the message (KNOWN-HAZARDS 2.3).
9	//!
10	//! Bytes (PTY input/output) ride as base64 strings — JSON has no native byte
11	//! type, and base64 keeps the single envelope/codec path rather than forking a
12	//! binary frame format.
13	// [impl->REQ-DAEMON-2]
14
15	use base64::engine::general_purpose::STANDARD as B64;
16	use base64::Engine;
17	use serde::{Deserialize, Serialize};
18
19	use spt_net::net::attach::AttachIntent;
20
21	use crate::frame::Envelope;
22
23	/// Brain→broker: spawn a child under a fresh broker-owned PTY.
24	pub const KIND_SPAWN: &str = "spawn";
25	/// Broker→brain: a spawn succeeded; carries the assigned session id + child pid.
26	pub const KIND_SPAWNED: &str = "spawned";
27	/// Brain→broker: (re)subscribe to a session's output from a given sequence.
28	pub const KIND_SUBSCRIBE: &str = "subscribe";
29	/// Brain→broker: release this connection's controller/viewer role on a session
30	/// (W2.5, REQ-RCVIEW-1) — the explicit detach a `serve_attach` worker sends when
31	/// the operator's viewport ends, so `driven_by` / `viewer_count` clear promptly
32	/// even if the serving brain connection lives on (a connection drop also clears
33	/// the role, so this is idempotent belt-and-braces).
34	pub const KIND_UNSUBSCRIBE: &str = "unsubscribe";
35	/// Brain→broker: write raw bytes to a session's input (send-keys).
36	pub const KIND_INPUT: &str = "input";
37	/// Brain→broker: resize a session's PTY.
38	pub const KIND_RESIZE: &str = "resize";
39	/// Brain→broker: terminate a session's child.
40	pub const KIND_KILL: &str = "kill";
41	/// Broker→brain: a chunk of a session's output (pushed as the child produces it).
42	pub const KIND_OUTPUT: &str = "output";
43	/// Broker→brain: a session's child exited.
44	pub const KIND_EXIT: &str = "exit";
45	/// Broker→brain: the outcome of a [`KIND_SUBSCRIBE`] under the W2.5
46	/// controller/viewer model (REQ-RCVIEW-1 / REQ-KICK-1). Carries the resolved
47	/// role — controller / viewer / busy-refused / took-control — so the serving
48	/// brain can wake (controller-only), stay wake-neutral (viewer), or refuse the
49	/// operator (busy). A normal reply, not a new wire frame (the busy refusal is
50	/// rendered client-side by `rc` from the endpoint's `driven_by`).
51	pub const KIND_SUBSCRIBED: &str = "subscribed";
52	/// Broker→brain: the current PTY size of a session, pushed to a VIEWER on
53	/// attach and to every viewer on a controller resize (REQ-RCVIEW-1). The
54	/// serving brain forwards it as a [`spt_net::net::attach::AttachRecord::Size`]
55	/// so the viewer letterboxes client-side (never resizing the PTY itself —
56	/// resize is controller-exclusive).
57	pub const KIND_SIZE: &str = "size";
58	/// Broker→brain: you (a controller) have been DISPLACED by a different operator's
59	/// explicit `--take`/`kick` (REQ-KICK-1). The serving brain forwards this as a
60	/// loud [`spt_net::net::attach::AttachRecord::Displaced`] on the wire, then
61	/// detaches. Fired ONLY on a genuine cross-operator control-ownership change —
62	/// NEVER on a same-origin re-serve after a brain restart (the gate-#7 self-kick
63	/// trap: a successor re-taking the controller slot with the SAME `by` is silent).
64	pub const KIND_DISPLACED: &str = "displaced";
65	/// Broker→brain: an idempotent effect (an `input` carrying an `op_id`) is durably
66	/// applied **or** was already applied (deduped). The exactly-once ack: a brain
67	/// learns its effect landed, and a replay after a crash is acknowledged the same
68	/// way so the brain can retire it (B5 / KNOWN-HAZARDS 7.2).
69	pub const KIND_APPLIED: &str = "applied";
70	/// Broker→brain: a command failed (carries an operator-facing message).
71	pub const KIND_ERROR: &str = "error";
72	/// Brain→broker: report the broker-owned net endpoint's status (D4a). The brain
73	/// holds no endpoint of its own — the broker owns the node's network presence
74	/// (ADR-0004 §B), so status (node id, dialable address, conn count) is an IPC ask.
75	pub const KIND_NET_STATUS: &str = "net-status";
76	/// Broker→brain: the net endpoint status reply.
77	pub const KIND_NET_STATUS_REPLY: &str = "net-status-reply";
78	/// Brain→broker: dial a peer over the broker-owned endpoint (D4a). With an
79	/// `op_id` the dial is exactly-once (journaled like an idempotent `input`).
80	pub const KIND_NET_DIAL: &str = "net-dial";
81	/// Brain→broker: mint (or reuse) the broker's single in-process loopback
82	/// connection (M12 W1.5, B1) — the transport a LOCAL `spt rc` rides because QUIC
83	/// self-dial is refused by iroh. An explicit verb (NOT a sentinel addr on
84	/// [`KIND_NET_DIAL`]): loopback handling stays isolated from the dial hot path.
85	/// Null payload (the singleton is idempotent by reuse — no op id, no addr).
86	/// Answered with a [`KIND_NET_DIALED`] whose `remote_id_hex` is THIS node's own id.
87	pub const KIND_NET_DIAL_LOOPBACK: &str = "net-dial-loopback";
88	/// Broker→brain: a dial durably landed — fresh or deduped (`applied_now`
89	/// distinguishes, mirroring [`KIND_APPLIED`]). Carries the broker-minted conn id.
90	pub const KIND_NET_DIALED: &str = "net-dialed";
91	/// Brain→broker: open a bidi QUIC stream on a held connection (D4b). With an
92	/// `op_id` the open is exactly-once (journaled like a dial).
93	pub const KIND_NET_STREAM_OPEN: &str = "net-stream-open";
94	/// Broker→brain: a stream open durably landed (fresh or deduped).
95	pub const KIND_NET_STREAM_OPENED: &str = "net-stream-opened";
96	/// Brain→broker: write bytes to a held stream's send side (D4b). With an
97	/// `op_id` the write is exactly-once (journaled; acked with [`KIND_NET_SENT`]).
98	pub const KIND_NET_STREAM_SEND: &str = "net-stream-send";
99	/// Broker→brain: a journaled stream send durably landed (fresh or deduped).
100	pub const KIND_NET_SENT: &str = "net-sent";
101	/// Brain→broker: list the streams the broker currently holds (D4b). The query
102	/// model a re-attaching brain uses to find streams that arrived while it was
103	/// down — no announce buffer to miss.
104	pub const KIND_NET_STREAMS: &str = "net-streams";
105	/// Broker→brain: the held-streams reply.
106	pub const KIND_NET_STREAMS_REPLY: &str = "net-streams-reply";
107	/// Brain→broker: (re)subscribe to a stream's buffered reads from a cursor —
108	/// the PTY `subscribe` discipline applied to a QUIC stream (gapless resume).
109	pub const KIND_NET_STREAM_SUBSCRIBE: &str = "net-stream-subscribe";
110	/// Broker→brain: a sequenced chunk of a stream's reads (live or replayed).
111	pub const KIND_NET_STREAM_DATA: &str = "net-stream-data";
112	/// Broker→brain: a stream's read side finished cleanly; `next_seq` tells the
113	/// brain the final cursor so it can verify it missed nothing.
114	pub const KIND_NET_STREAM_EOF: &str = "net-stream-eof";
115	/// Brain→broker: (re)subscribe to the broker's conn-liveness event log from a
116	/// cursor (D4c) — the PresenceChannel broker seam. Same gapless-resume
117	/// discipline as a stream subscribe.
118	// [impl->REQ-EP-4]
119	pub const KIND_NET_PRESENCE_SUBSCRIBE: &str = "net-presence-subscribe";
120	/// Broker→brain: one sequenced conn-liveness event (live or replayed).
121	pub const KIND_NET_PRESENCE_EVENT: &str = "net-presence-event";
122
123	/// [`NetPresenceEvent::kind`]: a connection joined the broker's conn table.
124	pub const PRESENCE_CONNECTED: &str = "connected";
125	/// [`NetPresenceEvent::kind`]: a connection ended and left the conn table.
126	pub const PRESENCE_DISCONNECTED: &str = "disconnected";
127
128	/// Brain→broker: run the joiner leg of the subnet pairing ceremony (M7 D3,
129	/// REQ-SUBNET-2). The ceremony must run broker-side: it dials via the

... [929 lines truncated] ...