878	        (p.forward, p.detach)
879	    }
880
881	    // [unit->REQ-HAZARD-RC-ATTACH-FAILFAST] path (a): attaching to a perch stamped
882	    // status=offline (the B2-reconcile / endpoint-stop state) SHORT-CIRCUITS with
883	    // clean guidance — it returns Ok WITHOUT any broker dance (no daemon needed),
884	    // so it can never hang on a dead session.
885	    #[test]
886	    fn attach_offline_endpoint_short_circuits_clean() {
887	        let _home = crate::testutil::isolated_home();
888	        let id = "stopped-ep";
889	        let perch = spt_store::perch::resolve_perch_path(id, spt_store::perch::ParentHint::Infer);
890	        std::fs::create_dir_all(&perch).unwrap();
891	        let rec = spt_store::info::InfoJson::new(id, "t", std::process::id(), "sid", "live_agent");
892	        spt_store::info::write_info(&perch, &rec).unwrap();
893	        spt_store::info::set_status(&perch, spt_store::liveness::STATUS_OFFLINE).unwrap();
894
895	        // Offline status → the gate returns early (no broker), both intents.
896	        assert!(run_attach(id, AttachIntent::Control).is_ok());
897	        assert!(run_attach(id, AttachIntent::Viewer).is_ok());
898	    }
899
900	    // (REQ-RC-KEY-VT-TRANSLATE supersedes the W7 normalize_key_byte swap on Windows:
901	    // the exhaustive translate_key_event mapping unit + the event-detach unit live
902	    // alongside this module's tests; Backspace→0x7f / Ctrl+Backspace→0x08 carry the
903	    // relocated [unit->REQ-HAZARD-RC-INPUT-KEY-ENCODING] evidence there.)
904
905	    // [unit->REQ-HAZARD-RC-ATTACH-FAILFAST] path (b): the generous first-event
906	    // backstop fires ONLY when NOTHING has arrived past the grace — never for a
907	    // session that produced an event (a fresh/mid-init alive attach is safe), and
908	    // never before the grace elapses.
909	    #[test]
910	    fn first_event_stall_decision() {
911	        // No event + past the generous grace → stalled.
912	        assert!(first_event_stalled(false, FIRST_EVENT_GRACE));
913	        assert!(first_event_stalled(false, FIRST_EVENT_GRACE + Duration::from_secs(1)));
914	        // Any event seen → never stalled (the working/mid-init attach guard).
915	        assert!(!first_event_stalled(true, FIRST_EVENT_GRACE + Duration::from_secs(60)));
916	        // Before the grace → not yet (a slow-painting session still has time).
917	        assert!(!first_event_stalled(false, Duration::from_secs(1)));
918	        assert!(!first_event_stalled(false, FIRST_EVENT_GRACE - Duration::from_millis(1)));
919	    }
920
921	    // [unit->REQ-HAZARD-RC-EOF] a severed-broker-stream read error (UnexpectedEof
922	    // "failed to fill whole buffer", and the reset/abort/broken-pipe class) is
923	    // classified BrokerGone → graceful surface, NOT a fatal RC_FAIL crash; a
924	    // poll-slice timeout retries; any other error stays fatal.
925	    #[test]
926	    fn classify_read_err_eof_is_graceful_not_fatal() {
927	        use std::io::ErrorKind::*;
928	        // The exact kind behind "failed to fill whole buffer".
929	        assert_eq!(classify_read_err(UnexpectedEof), ReadDisposition::BrokerGone);
930	        // Same severed-stream class.
931	        assert_eq!(classify_read_err(BrokenPipe), ReadDisposition::BrokerGone);
932	        assert_eq!(classify_read_err(ConnectionReset), ReadDisposition::BrokerGone);
933	        assert_eq!(classify_read_err(ConnectionAborted), ReadDisposition::BrokerGone);
934	        // Poll-slice timeouts retry (no event this slice).
935	        assert_eq!(classify_read_err(WouldBlock), ReadDisposition::Retry);
936	        assert_eq!(classify_read_err(TimedOut), ReadDisposition::Retry);
937	        // A genuine unexpected failure still propagates.

        (p.forward, p.detach)
    }

    // [unit->REQ-HAZARD-RC-ATTACH-FAILFAST] path (a): attaching to a perch stamped
    // status=offline (the B2-reconcile / endpoint-stop state) SHORT-CIRCUITS with
    // clean guidance — it returns Ok WITHOUT any broker dance (no daemon needed),
    // so it can never hang on a dead session.
    #[test]
    fn attach_offline_endpoint_short_circuits_clean() {
        let _home = crate::testutil::isolated_home();
        let id = "stopped-ep";
        let perch = spt_store::perch::resolve_perch_path(id, spt_store::perch::ParentHint::Infer);
        std::fs::create_dir_all(&perch).unwrap();
        let rec = spt_store::info::InfoJson::new(id, "t", std::process::id(), "sid", "live_agent");
        spt_store::info::write_info(&perch, &rec).unwrap();
        spt_store::info::set_status(&perch, spt_store::liveness::STATUS_OFFLINE).unwrap();

        // Offline status → the gate returns early (no broker), both intents.
        assert!(run_attach(id, AttachIntent::Control).is_ok());
        assert!(run_attach(id, AttachIntent::Viewer).is_ok());
    }

    // (REQ-RC-KEY-VT-TRANSLATE supersedes the W7 normalize_key_byte swap on Windows:
    // the exhaustive translate_key_event mapping unit + the event-detach unit live
    // alongside this module's tests; Backspace→0x7f / Ctrl+Backspace→0x08 carry the
    // relocated [unit->REQ-HAZARD-RC-INPUT-KEY-ENCODING] evidence there.)

    // [unit->REQ-HAZARD-RC-ATTACH-FAILFAST] path (b): the generous first-event
    // backstop fires ONLY when NOTHING has arrived past the grace — never for a
    // session that produced an event (a fresh/mid-init alive attach is safe), and
    // never before the grace elapses.
    #[test]
    fn first_event_stall_decision() {
        // No event + past the generous grace → stalled.
        assert!(first_event_stalled(false, FIRST_EVENT_GRACE));
        assert!(first_event_stalled(false, FIRST_EVENT_GRACE + Duration::from_secs(1)));
        // Any event seen → never stalled (the working/mid-init attach guard).
        assert!(!first_event_stalled(true, FIRST_EVENT_GRACE + Duration::from_secs(60)));
        // Before the grace → not yet (a slow-painting session still has time).
        assert!(!first_event_stalled(false, Duration::from_secs(1)));
        assert!(!first_event_stalled(false, FIRST_EVENT_GRACE - Duration::from_millis(1)));
    }

    // [unit->REQ-HAZARD-RC-EOF] a severed-broker-stream read error (UnexpectedEof
    // "failed to fill whole buffer", and the reset/abort/broken-pipe class) is
    // classified BrokerGone → graceful surface, NOT a fatal RC_FAIL crash; a
    // poll-slice timeout retries; any other error stays fatal.
    #[test]
    fn classify_read_err_eof_is_graceful_not_fatal() {
        use std::io::ErrorKind::*;
        // The exact kind behind "failed to fill whole buffer".
        assert_eq!(classify_read_err(UnexpectedEof), ReadDisposition::BrokerGone);
        // Same severed-stream class.
        assert_eq!(classify_read_err(BrokenPipe), ReadDisposition::BrokerGone);
        assert_eq!(classify_read_err(ConnectionReset), ReadDisposition::BrokerGone);
        assert_eq!(classify_read_err(ConnectionAborted), ReadDisposition::BrokerGone);
        // Poll-slice timeouts retry (no event this slice).
        assert_eq!(classify_read_err(WouldBlock), ReadDisposition::Retry);
        assert_eq!(classify_read_err(TimedOut), ReadDisposition::Retry);
        // A genuine unexpected failure still propagates.


Grep

mod tests|cfg\(windows\)|fn translate_key_event_emits|fn key_event_step_detach_sm|use super


crates\spt\src\rc.rs:82:#[cfg(windows)]
crates\spt\src\rc.rs:141:#[cfg(windows)]
crates\spt\src\rc.rs:156:#[cfg(windows)]
crates\spt\src\rc.rs:171:#[cfg(windows)]
crates\spt\src\rc.rs:203:#[cfg(windows)]
crates\spt\src\rc.rs:220:#[cfg(windows)]
crates\spt\src\rc.rs:251:#[cfg(windows)]
crates\spt\src\rc.rs:265:#[cfg(windows)]
crates\spt\src\rc.rs:276:#[cfg(windows)]
crates\spt\src\rc.rs:287:#[cfg(windows)]
crates\spt\src\rc.rs:301:#[cfg(windows)]
crates\spt\src\rc.rs:312:#[cfg(windows)]
crates\spt\src\rc.rs:328:        #[cfg(windows)]
crates\spt\src\rc.rs:337:        #[cfg(windows)]
crates\spt\src\rc.rs:417:    #[cfg(windows)]
crates\spt\src\rc.rs:462:#[cfg(windows)]
crates\spt\src\rc.rs:873:mod tests {
crates\spt\src\rc.rs:874:    use super::*;
crates\spt\src\rc.rs:1004:    #[cfg(windows)]
crates\spt\src\rc.rs:1006:    fn translate_key_event_emits_exact_xterm_vt() {
crates\spt\src\rc.rs:1120:    #[cfg(windows)]
crates\spt\src\rc.rs:1122:    fn key_event_step_detach_sm() {
crates\spt\src\rc.rs:1181:    #[cfg(windows)]


1120	    #[cfg(windows)]
1121	    #[test]
1122	    fn key_event_step_detach_sm() {
1123	        use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
1124	        let ctrl = KeyModifiers::CONTROL;
1125	        let none = KeyModifiers::NONE;
1126	        let ke = |code, mods| KeyEvent::new(code, mods);
1127
1128	        // unarmed Ctrl+B → Swallow, arms.
1129	        let mut armed = false;
1130	        match key_event_step(&mut armed, ke(KeyCode::Char('b'), ctrl)) {
1131	            KeyAction::Swallow => {}
1132	            other => panic!("unarmed Ctrl+B should Swallow, got {:?}", action_name(&other)),
1133	        }
1134	        assert!(armed, "Ctrl+B must arm the SM");
1135
1136	        // armed + plain 'd' → Detach.
1137	        match key_event_step(&mut armed, ke(KeyCode::Char('d'), none)) {
1138	            KeyAction::Detach => {}
1139	            other => panic!("armed + 'd' should Detach, got {:?}", action_name(&other)),
1140	        }
1141	        assert!(!armed, "Detach disarms");
1142
1143	        // armed + Ctrl+B → Forward(literal 0x02), disarms.
1144	        armed = true;
1145	        match key_event_step(&mut armed, ke(KeyCode::Char('b'), ctrl)) {
1146	            KeyAction::Forward(v) => assert_eq!(v, vec![0x02]),
1147	            other => panic!("armed + Ctrl+B should Forward [0x02], got {:?}", action_name(&other)),
1148	        }
1149	        assert!(!armed);
1150
1151	        // armed + plain 'x' → Forward(0x02 then translated 'x'), disarms.
1152	        armed = true;
1153	        match key_event_step(&mut armed, ke(KeyCode::Char('x'), none)) {
1154	            KeyAction::Forward(v) => assert_eq!(v, vec![0x02, b'x']),
1155	            other => panic!("armed + 'x' should Forward [0x02,'x'], got {:?}", action_name(&other)),
1156	        }
1157	        assert!(!armed);
1158
1159	        // armed + Ctrl+D → NOT Detach: Forward(0x02 then 0x04). Detach key is PLAIN 'd'.
1160	        armed = true;
1161	        match key_event_step(&mut armed, ke(KeyCode::Char('d'), ctrl)) {
1162	            KeyAction::Forward(v) => assert_eq!(
1163	                v,
1164	                vec![0x02, 0x04],
1165	                "Ctrl+D must forward prefix + ^D, never detach"
1166	            ),
1167	            other => panic!("armed + Ctrl+D must NOT Detach, got {:?}", action_name(&other)),
1168	        }
1169	        assert!(!armed);
1170
1171	        // unarmed plain 'a' → Forward(translated 'a'), stays disarmed.
1172	        armed = false;
1173	        match key_event_step(&mut armed, ke(KeyCode::Char('a'), none)) {
1174	            KeyAction::Forward(v) => assert_eq!(v, vec![b'a']),
1175	            other => panic!("unarmed 'a' should Forward ['a'], got {:?}", action_name(&other)),
1176	        }
1177	        assert!(!armed);
1178	    }
1179
1180	    /// Name a KeyAction for panic messages without requiring Debug on the enum.
1181	    #[cfg(windows)]
1182	    fn action_name(a: &KeyAction) -> &'static str {
1183	        match a {
1184	            KeyAction::Detach => "Detach",
1185	            KeyAction::Forward(_) => "Forward",
1186	            KeyAction::Swallow => "Swallow",
1187	        }
1188	    }
1189	}
1190

    #[cfg(windows)]
    #[test]
    fn key_event_step_detach_sm() {
        use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
        let ctrl = KeyModifiers::CONTROL;
        let none = KeyModifiers::NONE;
        let ke = |code, mods| KeyEvent::new(code, mods);

        // unarmed Ctrl+B → Swallow, arms.
        let mut armed = false;
        match key_event_step(&mut armed, ke(KeyCode::Char('b'), ctrl)) {
            KeyAction::Swallow => {}
            other => panic!("unarmed Ctrl+B should Swallow, got {:?}", action_name(&other)),
        }
        assert!(armed, "Ctrl+B must arm the SM");

        // armed + plain 'd' → Detach.
        match key_event_step(&mut armed, ke(KeyCode::Char('d'), none)) {
            KeyAction::Detach => {}
            other => panic!("armed + 'd' should Detach, got {:?}", action_name(&other)),
        }
        assert!(!armed, "Detach disarms");

        // armed + Ctrl+B → Forward(literal 0x02), disarms.
        armed = true;
        match key_event_step(&mut armed, ke(KeyCode::Char('b'), ctrl)) {
            KeyAction::Forward(v) => assert_eq!(v, vec![0x02]),
            other => panic!("armed + Ctrl+B should Forward [0x02], got {:?}", action_name(&other)),
        }
        assert!(!armed);

        // armed + plain 'x' → Forward(0x02 then translated 'x'), disarms.
        armed = true;
        match key_event_step(&mut armed, ke(KeyCode::Char('x'), none)) {
            KeyAction::Forward(v) => assert_eq!(v, vec![0x02, b'x']),
            other => panic!("armed + 'x' should Forward [0x02,'x'], got {:?}", action_name(&other)),
        }
        assert!(!armed);

        // armed + Ctrl+D → NOT Detach: Forward(0x02 then 0x04). Detach key is PLAIN 'd'.
        armed = true;
        match key_event_step(&mut armed, ke(KeyCode::Char('d'), ctrl)) {
            KeyAction::Forward(v) => assert_eq!(
                v,
                vec![0x02, 0x04],
                "Ctrl+D must forward prefix + ^D, never detach"
            ),
            other => panic!("armed + Ctrl+D must NOT Detach, got {:?}", action_name(&other)),
        }
        assert!(!armed);

        // unarmed plain 'a' → Forward(translated 'a'), stays disarmed.
        armed = false;
        match key_event_step(&mut armed, ke(KeyCode::Char('a'), none)) {
            KeyAction::Forward(v) => assert_eq!(v, vec![b'a']),
            other => panic!("unarmed 'a' should Forward ['a'], got {:?}", action_name(&other)),
        }
        assert!(!armed);
    }

    /// Name a KeyAction for panic messages without requiring Debug on the enum.
    #[cfg(windows)]
    fn action_name(a: &KeyAction) -> &'static str {
        match a {
            KeyAction::Detach => "Detach",
            KeyAction::Forward(_) => "Forward",
            KeyAction::Swallow => "Swallow",
        }
    }
}


Edit

    /// Name a KeyAction for panic messages without requiring Debug on the enum.
    #[cfg(windows)]
    fn action_name(a: &KeyAction) -> &'static str {
        match a {
            KeyAction::Detach => "Detach",
            KeyAction::Forward(_) => "Forward",
            KeyAction::Swallow => "Swallow",
        }
    }
}

    /// Name a KeyAction for panic messages without requiring Debug on the enum.
    #[cfg(windows)]
    fn action_name(a: &KeyAction) -> &'static str {
        match a {
            KeyAction::Detach => "Detach",
            KeyAction::Forward(_) => "Forward",
            KeyAction::Swallow => "Swallow",
        }
    }

    // [unit->REQ-RC-WIN-PASTE] the bracketed-paste wrap is EXACT (`ESC[200~` …
    // `ESC[201~`) and the content is forwarded VERBATIM — a multi-line paste keeps
    // its embedded newlines untranslated (the no-submit-storm property: CC sees the
    // markers + treats the whole block as one paste, not a Enter-per-line storm).
    #[cfg(windows)]
    #[test]
    fn bracketed_paste_framing_is_exact_and_content_verbatim() {
        // Single line.
        assert_eq!(wrap_bracketed_paste(b"hi"), b"\x1b[200~hi\x1b[201~".to_vec());
        // Multi-line: newlines stay literal INSIDE the markers (no \r submit-storm,
        // no per-char translation), markers exactly once around the whole block.
        let content = b"line1\nline2\nline3";
        let mut expect = b"\x1b[200~".to_vec();
        expect.extend_from_slice(content);
        expect.extend_from_slice(b"\x1b[201~");
        assert_eq!(wrap_bracketed_paste(content), expect);
        // Empty content still frames (the caller — clipboard_paste — is what no-ops
        // on empty; the wrap itself is pure framing).
        assert_eq!(wrap_bracketed_paste(b""), b"\x1b[200~\x1b[201~".to_vec());
    }

    // [unit->REQ-RC-WIN-PASTE] the ctrl+V gesture is detected ONLY for Ctrl+v — a
    // bare 'v', a Ctrl+other, or 'v' with a different modifier is not the paste key.
    #[cfg(windows)]
    #[test]
    fn is_ctrl_v_only_for_ctrl_plus_v() {
        use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
        assert!(is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::CONTROL)));
        // Ctrl + Shift + v still counts (CONTROL present) — paste regardless of shift.
        assert!(is_ctrl_v(&KeyEvent::new(
            KeyCode::Char('v'),
            KeyModifiers::CONTROL | KeyModifiers::SHIFT
        )));
        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::NONE)));
        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)));
        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::ALT)));
    }

    // [unit->REQ-RC-WIN-PASTE] the right-click paste gesture is ONLY right-button
    // DOWN; every other mouse event (left/middle, up/move/drag/scroll) is dropped.
    #[cfg(windows)]
    #[test]
    fn mouse_is_paste_only_right_button_down() {
        use crossterm::event::{KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
        let me = |kind| MouseEvent {
            kind,
            column: 0,
            row: 0,
            modifiers: KeyModifiers::NONE,
        };
        assert!(mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Right))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Up(MouseButton::Right))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Left))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Middle))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Moved)));
        assert!(!mouse_is_paste(&me(MouseEventKind::Drag(MouseButton::Right))));
        assert!(!mouse_is_paste(&me(MouseEventKind::ScrollDown)));
    }

    // [unit->REQ-RC-WIN-PASTE] the gesture→paste decision with an INJECTED clipboard
    // reader (no real clipboard): a non-empty read ⇒ the bracketed paste; an empty
    // read OR a read failure (`None`) ⇒ a clean no-op (never inject garbage).
    #[cfg(windows)]
    #[test]
    fn clipboard_paste_wraps_nonempty_and_noops_on_empty_or_failure() {
        // Non-empty clipboard → bracketed paste of the verbatim content.
        assert_eq!(
            clipboard_paste(|| Some(b"pasted".to_vec())),
            Some(b"\x1b[200~pasted\x1b[201~".to_vec())
        );
        // Empty clipboard → no-op (not an empty bracketed frame — nothing to paste).
        assert_eq!(clipboard_paste(|| Some(Vec::new())), None);
        // Failed read → no-op.
        assert_eq!(clipboard_paste(|| None), None);
    }
}


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

    /// Name a KeyAction for panic messages without requiring Debug on the enum.
    #[cfg(windows)]
    fn action_name(a: &KeyAction) -> &'static str {
        match a {
            KeyAction::Detach => "Detach",
            KeyAction::Forward(_) => "Forward",
            KeyAction::Swallow => "Swallow",
        }
    }
}

    /// Name a KeyAction for panic messages without requiring Debug on the enum.
    #[cfg(windows)]
    fn action_name(a: &KeyAction) -> &'static str {
        match a {
            KeyAction::Detach => "Detach",
            KeyAction::Forward(_) => "Forward",
            KeyAction::Swallow => "Swallow",
        }
    }

    // [unit->REQ-RC-WIN-PASTE] the bracketed-paste wrap is EXACT (`ESC[200~` …
    // `ESC[201~`) and the content is forwarded VERBATIM — a multi-line paste keeps
    // its embedded newlines untranslated (the no-submit-storm property: CC sees the
    // markers + treats the whole block as one paste, not a Enter-per-line storm).
    #[cfg(windows)]
    #[test]
    fn bracketed_paste_framing_is_exact_and_content_verbatim() {
        // Single line.
        assert_eq!(wrap_bracketed_paste(b"hi"), b"\x1b[200~hi\x1b[201~".to_vec());
        // Multi-line: newlines stay literal INSIDE the markers (no \r submit-storm,
        // no per-char translation), markers exactly once around the whole block.
        let content = b"line1\nline2\nline3";
        let mut expect = b"\x1b[200~".to_vec();
        expect.extend_from_slice(content);
        expect.extend_from_slice(b"\x1b[201~");
        assert_eq!(wrap_bracketed_paste(content), expect);
        // Empty content still frames (the caller — clipboard_paste — is what no-ops
        // on empty; the wrap itself is pure framing).
        assert_eq!(wrap_bracketed_paste(b""), b"\x1b[200~\x1b[201~".to_vec());
    }

    // [unit->REQ-RC-WIN-PASTE] the ctrl+V gesture is detected ONLY for Ctrl+v — a
    // bare 'v', a Ctrl+other, or 'v' with a different modifier is not the paste key.
    #[cfg(windows)]
    #[test]
    fn is_ctrl_v_only_for_ctrl_plus_v() {
        use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
        assert!(is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::CONTROL)));
        // Ctrl + Shift + v still counts (CONTROL present) — paste regardless of shift.
        assert!(is_ctrl_v(&KeyEvent::new(
            KeyCode::Char('v'),
            KeyModifiers::CONTROL | KeyModifiers::SHIFT
        )));
        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::NONE)));
        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)));
        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::ALT)));
    }

    // [unit->REQ-RC-WIN-PASTE] the right-click paste gesture is ONLY right-button
    // DOWN; every other mouse event (left/middle, up/move/drag/scroll) is dropped.
    #[cfg(windows)]
    #[test]
    fn mouse_is_paste_only_right_button_down() {
        use crossterm::event::{KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
        let me = |kind| MouseEvent {
            kind,
            column: 0,
            row: 0,
            modifiers: KeyModifiers::NONE,
        };
        assert!(mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Right))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Up(MouseButton::Right))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Left))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Middle))));
        assert!(!mouse_is_paste(&me(MouseEventKind::Moved)));
        assert!(!mouse_is_paste(&me(MouseEventKind::Drag(MouseButton::Right))));
        assert!(!mouse_is_paste(&me(MouseEventKind::ScrollDown)));
    }

    // [unit->REQ-RC-WIN-PASTE] the gesture→paste decision with an INJECTED clipboard
    // reader (no real clipboard): a non-empty read ⇒ the bracketed paste; an empty
    // read OR a read failure (`None`) ⇒ a clean no-op (never inject garbage).
    #[cfg(windows)]
    #[test]
    fn clipboard_paste_wraps_nonempty_and_noops_on_empty_or_failure() {
        // Non-empty clipboard → bracketed paste of the verbatim content.
        assert_eq!(
            clipboard_paste(|| Some(b"pasted".to_vec())),
            Some(b"\x1b[200~pasted\x1b[201~".to_vec())
        );
        // Empty clipboard → no-op (not an empty bracketed frame — nothing to paste).
        assert_eq!(clipboard_paste(|| Some(Vec::new())), None);
        // Failed read → no-op.
        assert_eq!(clipboard_paste(|| None), None);
    }
}

+    // [unit->REQ-RC-WIN-PASTE] the bracketed-paste wrap is EXACT (`ESC[200~` …

+    // `ESC[201~`) and the content is forwarded VERBATIM — a multi-line paste keeps

+    // its embedded newlines untranslated (the no-submit-storm property: CC sees the

+    // markers + treats the whole block as one paste, not a Enter-per-line storm).

+    fn bracketed_paste_framing_is_exact_and_content_verbatim() {

+        assert_eq!(wrap_bracketed_paste(b"hi"), b"\x1b[200~hi\x1b[201~".to_vec());

+        // Multi-line: newlines stay literal INSIDE the markers (no \r submit-storm,

+        // no per-char translation), markers exactly once around the whole block.

+        let content = b"line1\nline2\nline3";

+        let mut expect = b"\x1b[200~".to_vec();

+        expect.extend_from_slice(content);

+        expect.extend_from_slice(b"\x1b[201~");

+        assert_eq!(wrap_bracketed_paste(content), expect);

+        // Empty content still frames (the caller — clipboard_paste — is what no-ops

+        // on empty; the wrap itself is pure framing).

+        assert_eq!(wrap_bracketed_paste(b""), b"\x1b[200~\x1b[201~".to_vec());

+    // [unit->REQ-RC-WIN-PASTE] the ctrl+V gesture is detected ONLY for Ctrl+v — a

+    // bare 'v', a Ctrl+other, or 'v' with a different modifier is not the paste key.

+    fn is_ctrl_v_only_for_ctrl_plus_v() {

+        use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

+        assert!(is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::CONTROL)));

+        // Ctrl + Shift + v still counts (CONTROL present) — paste regardless of shift.

+        assert!(is_ctrl_v(&KeyEvent::new(

+            KeyModifiers::CONTROL | KeyModifiers::SHIFT

+        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::NONE)));

+        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)));

+        assert!(!is_ctrl_v(&KeyEvent::new(KeyCode::Char('v'), KeyModifiers::ALT)));

+    // [unit->REQ-RC-WIN-PASTE] the right-click paste gesture is ONLY right-button

+    // DOWN; every other mouse event (left/middle, up/move/drag/scroll) is dropped.

+    fn mouse_is_paste_only_right_button_down() {

+        use crossterm::event::{KeyModifiers, MouseButton, MouseEvent, MouseEventKind};

+            modifiers: KeyModifiers::NONE,

+        assert!(mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Right))));

+        assert!(!mouse_is_paste(&me(MouseEventKind::Up(MouseButton::Right))));

+        assert!(!mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Left))));

+        assert!(!mouse_is_paste(&me(MouseEventKind::Down(MouseButton::Middle))));

+        assert!(!mouse_is_paste(&me(MouseEventKind::Moved)));

+        assert!(!mouse_is_paste(&me(MouseEventKind::Drag(MouseButton::Right))));

+        assert!(!mouse_is_paste(&me(MouseEventKind::ScrollDown)));

+    // [unit->REQ-RC-WIN-PASTE] the gesture→paste decision with an INJECTED clipboard

+    // reader (no real clipboard): a non-empty read ⇒ the bracketed paste; an empty

+    // read OR a read failure (`None`) ⇒ a clean no-op (never inject garbage).

+    fn clipboard_paste_wraps_nonempty_and_noops_on_empty_or_failure() {

+        // Non-empty clipboard → bracketed paste of the verbatim content.

+            clipboard_paste(|| Some(b"pasted".to_vec())),

+        // Empty clipboard → no-op (not an empty bracketed frame — nothing to paste).

+        assert_eq!(clipboard_paste(|| Some(Vec::new())), None);

+        assert_eq!(clipboard_paste(|| None), None);


Bash

cargo test -p spt --lib rc::tests 2>&1 | tail -25

Run rc unit tests


error: no library targets found in package `spt`


cargo test -p spt --bin spt rc::tests:: 2>&1 | tail -30

Run rc unit tests (bin)


   Compiling hickory-net v0.26.0-beta.4
   Compiling hyper-rustls v0.27.9
   Compiling tokio-websockets v0.13.2
   Compiling reqwest v0.13.4
   Compiling hickory-resolver v0.26.0-beta.4
   Compiling noq v0.18.0
   Compiling iroh-relay v0.98.0
   Compiling iroh v0.98.2
   Compiling spt-net v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-net)
   Compiling spt-daemon v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
   Compiling spt v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 36.23s
     Running unittests src\main.rs (target\debug\deps\spt-00174d284f6e881b.exe)

running 12 tests
test rc::tests::classify_read_err_eof_is_graceful_not_fatal ... ok
test rc::tests::bracketed_paste_framing_is_exact_and_content_verbatim ... ok
test rc::tests::detach_prefix_spans_chunks ... ok
test rc::tests::first_event_stall_decision ... ok
test rc::tests::is_ctrl_v_only_for_ctrl_plus_v ... ok
test rc::tests::clipboard_paste_wraps_nonempty_and_noops_on_empty_or_failure ... ok
test rc::tests::mouse_is_paste_only_right_button_down ... ok
test rc::tests::key_event_step_detach_sm ... ok
test rc::tests::translate_key_event_emits_exact_xterm_vt ... ok
test rc::tests::detach_keybind_semantics ... ok
test rc::tests::op_minter_distinct_and_monotonic ... ok
test rc::tests::attach_offline_endpoint_short_circuits_clean ... ok

test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 193 filtered out; finished in 0.01s


4 P1 unit tests pass (12/12 rc::tests). Now docs.


Updated task #11 status


Updated task #12 status


496	| 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) |
497	| 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` |
498

| 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` |


Edit

| 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.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) |


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)

| 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.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.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) |


Now CONTEXT note — append to the input-injection paragraph.


This is the input-side mirror of the output-side single-writer (controller/viewer writer threads, 7.12).

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.


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).

+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.

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`.


Add doc evidence tags. Check how prior rc doc stage is tagged.


CONTEXT.md:345:<!-- [doc->REQ-RC-KEY-VT-TRANSLATE] -->
docs\MANIFEST.md:96:<!-- [doc->REQ-SESSION-RESUME-TEMPLATE] -->
docs\KNOWN-HAZARDS.md:440:<!-- [doc->REQ-RC-KEY-VT-TRANSLATE] -->
docs-site\src\harness-contract\manifest.md:130:<!-- [doc->REQ-SESSION-RESUME-TEMPLATE] -->
V0.13.0-BUG2-RC-VT-TRANSLATE-PLAN.md:31:5. **Docs:** KNOWN-HAZARDS entry + CONTEXT note ("Windows rc translates console key events to standard xterm VT; Unix passes through"). `[doc->REQ-RC-KEY-VT-TRANSLATE]`.
V0.13.0-P2-SESSION-RESUME-DESIGN.md:27:- `[doc->REQ-SESSION-RESUME-TEMPLATE]`.


437	- **spt-core mapping:** impl `livehost.rs` `reconcile_hosted_liveness` Gap-B clear + the additive `SessionInfo.controller_by` observability field (`msg.rs`, populated in `broker.rs` `KIND_SESSIONS`); unit `livehost.rs` `pull_liveness…` extended (offlined sessionless perch clears `driven_by`; live/relay perches untouched); int `crates/spt-daemon/tests/driven_by_selfheal.rs` `gap_b` (real broker: reconcile offlines AND clears `driven_by`) + the A1/A2 characterization (`inject_control_wedge.rs` w5_a1/w5_a2).
438	- **Source:** v0.13.0 W5 (repro-first, todlando; doyle-assigned 2026-06-19). A2 (idle wedged-remote leg) deferred to `REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT`.
439
440	<!-- [doc->REQ-RC-KEY-VT-TRANSLATE] -->
441	### 7.16 `spt rc` translates Windows console KEY EVENTS to standard xterm VT (arrows/Home/End/F-keys reach the harness); supersedes the W7 byte-swap  `[REQ-RC-KEY-VT-TRANSLATE]`
442	- **Failure (operator dogfooding):** `spt rc` read raw stdin BYTES, but the Windows legacy console (no `ENABLE_VIRTUAL_TERMINAL_INPUT`) delivers arrows / Home / End / PgUp / PgDn / Insert / Delete / F-keys as console KEY_EVENTs, NOT stdin bytes — so the byte-pump saw nothing and those keys were DEAD (only byte-emitting keys like Backspace worked, via the 7.13 swap).

- **spt-core mapping:** impl `livehost.rs` `reconcile_hosted_liveness` Gap-B clear + the additive `SessionInfo.controller_by` observability field (`msg.rs`, populated in `broker.rs` `KIND_SESSIONS`); unit `livehost.rs` `pull_liveness…` extended (offlined sessionless perch clears `driven_by`; live/relay perches untouched); int `crates/spt-daemon/tests/driven_by_selfheal.rs` `gap_b` (real broker: reconcile offlines AND clears `driven_by`) + the A1/A2 characterization (`inject_control_wedge.rs` w5_a1/w5_a2).
- **Source:** v0.13.0 W5 (repro-first, todlando; doyle-assigned 2026-06-19). A2 (idle wedged-remote leg) deferred to `REQ-HAZARD-DRIVEN-BY-IDLE-REMOTE-EVICT`.

<!-- [doc->REQ-RC-KEY-VT-TRANSLATE] -->
### 7.16 `spt rc` translates Windows console KEY EVENTS to standard xterm VT (arrows/Home/End/F-keys reach the harness); supersedes the W7 byte-swap  `[REQ-RC-KEY-VT-TRANSLATE]`
- **Failure (operator dogfooding):** `spt rc` read raw stdin BYTES, but the Windows legacy console (no `ENABLE_VIRTUAL_TERMINAL_INPUT`) delivers arrows / Home / End / PgUp / PgDn / Insert / Delete / F-keys as console KEY_EVENTs, NOT stdin bytes — so the byte-pump saw nothing and those keys were DEAD (only byte-emitting keys like Backspace worked, via the 7.13 swap).


docs\KNOWN-HAZARDS.md:341:### 7.1 Local `api` mutation auth  `[REQ-HAZARD-LOCAL-API-AUTH]`
docs\KNOWN-HAZARDS.md:347:### 7.2 Idempotent delivery across brain restart  `[REQ-HAZARD-RESTART-IDEMPOTENT]`
docs\KNOWN-HAZARDS.md:353:### 7.3 Psyche outbound capture + sanitization  `[REQ-HAZARD-PSYCHE-OUTBOUND-PROXY]`
docs\KNOWN-HAZARDS.md:359:### 7.4 Per-agent pulse/psyche/echo scheduling must not serialize across agents  `[REQ-HAZARD-DAEMON-SCHED-NONBLOCKING]`
docs\KNOWN-HAZARDS.md:365:### 7.5 WAN-inbound origin is transport truth, never payload  `[REQ-HAZARD-WAN-ORIGIN-AUTH]`
docs\KNOWN-HAZARDS.md:373:### 7.6 Pump brain-IPC reads must be deadline-bounded (a blocked read wedges the whole pump)  `[REQ-HAZARD-PUMP-IPC-DEADLINE]`
docs\KNOWN-HAZARDS.md:380:### 7.7 A slow/dead/hostile remote VIEWER must never stall the controller, child, or drain  `[REQ-HAZARD-VIEWER-ISOLATION]`
docs\KNOWN-HAZARDS.md:387:### 7.8 The broker must never make a brain wait UNBOUNDED on a QUIC op (the pump-IPC-deadline B-half)  `[REQ-HAZARD-BROKER-QUIC-DEADLINE]`
docs\KNOWN-HAZARDS.md:394:### 7.9 A daemon-state wire change needs a deliberate BROKER restart (the broker is resident across a brain self-update)  `[REQ-HAZARD-BROKER-SEED-WIRE-SKEW]`
docs\KNOWN-HAZARDS.md:401:### 7.10 A VIEW is independent from the endpoint — closing the launching tab must NOT reap the daemon-hosted harness  `[REQ-HAZARD-VIEWER-CLOSE-DETACH]`
docs\KNOWN-HAZARDS.md:408:### 7.11 A dead PTY child + a dropped operator pump must NOT wedge the broker for other clients  `[REQ-HAZARD-ATTACH-WEDGE]`
docs\KNOWN-HAZARDS.md:414:### 7.12 Controller output must NOT be written inline on the drain thread — a backed-up controller wedges the session  `[REQ-HAZARD-INJECT-CONTROL-COEXIST]`
docs\KNOWN-HAZARDS.md:420:### 7.13 `spt rc` must SWAP Windows legacy-console Backspace/Ctrl+Backspace (`^H`↔DEL); do NOT enable VT console input  `[REQ-HAZARD-RC-INPUT-KEY-ENCODING]`
docs\KNOWN-HAZARDS.md:427:### 7.14 The effect journal must NOT hold its lock across the PTY write (or fsync every keystroke) — interactive input stutters then wedges  `[REQ-HAZARD-EFFECT-JOURNAL-PTY-WEDGE]`
docs\KNOWN-HAZARDS.md:433:### 7.15 An OFFLINE spt-hosted endpoint must NOT render phantom `ONLINE+CONTROLLED` — clear `driven_by` when its session is gone  `[REQ-HAZARD-DRIVEN-BY-SELFHEAL]`
docs\KNOWN-HAZARDS.md:441:### 7.16 `spt rc` translates Windows console KEY EVENTS to standard xterm VT (arrows/Home/End/F-keys reach the harness); supersedes the W7 byte-swap  `[REQ-RC-KEY-VT-TRANSLATE]`


Add a detailed ### 7.18 narrative section with the doc tag (mirrors 7.16). Find end of 7.16 section.


441	### 7.16 `spt rc` translates Windows console KEY EVENTS to standard xterm VT (arrows/Home/End/F-keys reach the harness); supersedes the W7 byte-swap  `[REQ-RC-KEY-VT-TRANSLATE]`
442	- **Failure (operator dogfooding):** `spt rc` read raw stdin BYTES, but the Windows legacy console (no `ENABLE_VIRTUAL_TERMINAL_INPUT`) delivers arrows / Home / End / PgUp / PgDn / Insert / Delete / F-keys as console KEY_EVENTs, NOT stdin bytes — so the byte-pump saw nothing and those keys were DEAD (only byte-emitting keys like Backspace worked, via the 7.13 swap).
443	- **Invariant:** on Windows, read crossterm KEY EVENTS (the picker already does) and translate each to STANDARD xterm VT via the pure `translate_key_event` (copy a known-correct xterm table verbatim: arrows `ESC[A..D`, Home `ESC[H` / End `ESC[F`, `~` keys `ESC[<n>~`, modified `ESC[1;<m><final>` / `ESC[<n>;<m>~` with `m = 1 + Shift + 2·Alt + 4·Ctrl`, F1–F4 `ESC OP..S` / F5–F12 `ESC[<n>~`), forwarded through the SAME rc pump — the harness receives ordinary xterm VT (AGNOSTIC; NOT win32-input-mode, the 7.13 rejected lever). Press-only (drop Repeat/Release). Detach stays the `ctrl-b d` PREFIX, event-sourced (Ctrl+B arms; armed + plain `d` ⇒ Detach; armed + Ctrl+B ⇒ literal `0x02`; armed + other ⇒ `0x02` + translated). NON-tty stdin (piped / tests) falls back to the byte path (keeps the e2e byte-injection working). UNIX UNCHANGED (cfg-split; its raw-mode stream already delivers VT). SUPERSEDES 7.13's `normalize_key_byte` swap on Windows — Backspace → `0x7f` and Ctrl+Backspace → `0x08` are emitted NATIVELY by the translator.
444	- **spt-core mapping:** impl `rc.rs` `translate_key_event` + `csi_final`/`csi_tilde`/`f_key` + `key_event_step` (event detach SM) + `spawn_stdin_reader` cfg-split (`spawn_stdin_reader_events` on a tty / `spawn_stdin_reader_bytes` non-tty + Unix); unit the EXHAUSTIVE `translate_key_event` mapping + the event-detach SM (the Backspace/Ctrl+Backspace arm carries the relocated `REQ-HAZARD-RC-INPUT-KEY-ENCODING` evidence); NO int (live console = HITL, operator re-capture — REQ-RUN-PICKER/RC-1 precedent).
445	- **Source:** v0.13.0 bug 2 (operator ruling: proper agnostic translator, ship-blocker; doyle design + Option-B detach ruling 2026-06-19).
446
447	---
448
449	## Conformance checklist (condensed)
450
451	| # | Invariant | spt-core surface |
452	|---|---|---|
453	| 1.1 | Grace wait precedes INIT_SIGNOFF | daemon teardown |
454	| 1.4/4.4 | Deferred rows excluded from event-stream drain | daemon spool drain |
455	| 2.1/5.1 | Stable PID/broker-handle over ephemeral PID | liveness detection |
456	| 2.3 | Handoff argv/IPC version-tolerant (newer brain ↔ older broker) | broker↔brain IPC, self-update |
457	| 2.4 | gen_start = now() on cold-start + handoff | per-instance generation |
458	| 3.1 | Ephemeral perch cleanup on all exit paths | `ring` (RAII guard) |
459	| 3.3 | Echo-commune before INIT_SIGNOFF | daemon psyche loop |
460	| 4.1 | Envelope decode order, `&` last | spt-proto (public, wire-versioned) |
461	| 4.2 | Parser panic-free + tolerant | spt-proto |
462	| 4.3 | Stale registry entries → fallback, never hard-fail | subnet registry resolution |
463	| 4.10 | Silent-node registry rows evicted (heard-map TTL); own rows never decay | registry pump eviction |
464	| 4.6 | Addressable-id charset reserves `:`/`@` delimiters | `spt_proto::id` at creation seams |
465	| 5.2 | tmp-write + atomic-rename + retry (EBUSY) | all state writes, binary swap |
466	| 5.3 | Timeout every harness subprocess | manifest invocations |
467	| 5.4 | Strip UNC prefix on serialized paths | spt-proto path normalization |
468	| 5.5 | ConPTY reader answers DSR (`ESC[6n`) | spt-term broker PTY reader |
469	| 7.7 | A wedged viewer is evicted, never stalls the controller/child/drain | broker `OutputLog` fan-out (controller/viewer) |
470	| 5.6 | Detached long-lived children spawn `bInheritHandles=FALSE` | daemon + shell spawn |

### 7.16 `spt rc` translates Windows console KEY EVENTS to standard xterm VT (arrows/Home/End/F-keys reach the harness); supersedes the W7 byte-swap  `[REQ-RC-KEY-VT-TRANSLATE]`
- **Failure (operator dogfooding):** `spt rc` read raw stdin BYTES, but the Windows legacy console (no `ENABLE_VIRTUAL_TERMINAL_INPUT`) delivers arrows / Home / End / PgUp / PgDn / Insert / Delete / F-keys as console KEY_EVENTs, NOT stdin bytes — so the byte-pump saw nothing and those keys were DEAD (only byte-emitting keys like Backspace worked, via the 7.13 swap).
- **Invariant:** on Windows, read crossterm KEY EVENTS (the picker already does) and translate each to STANDARD xterm VT via the pure `translate_key_event` (copy a known-correct xterm table verbatim: arrows `ESC[A..D`, Home `ESC[H` / End `ESC[F`, `~` keys `ESC[<n>~`, modified `ESC[1;<m><final>` / `ESC[<n>;<m>~` with `m = 1 + Shift + 2·Alt + 4·Ctrl`, F1–F4 `ESC OP..S` / F5–F12 `ESC[<n>~`), forwarded through the SAME rc pump — the harness receives ordinary xterm VT (AGNOSTIC; NOT win32-input-mode, the 7.13 rejected lever). Press-only (drop Repeat/Release). Detach stays the `ctrl-b d` PREFIX, event-sourced (Ctrl+B arms; armed + plain `d` ⇒ Detach; armed + Ctrl+B ⇒ literal `0x02`; armed + other ⇒ `0x02` + translated). NON-tty stdin (piped / tests) falls back to the byte path (keeps the e2e byte-injection working). UNIX UNCHANGED (cfg-split; its raw-mode stream already delivers VT). SUPERSEDES 7.13's `normalize_key_byte` swap on Windows — Backspace → `0x7f` and Ctrl+Backspace → `0x08` are emitted NATIVELY by the translator.
- **spt-core mapping:** impl `rc.rs` `translate_key_event` + `csi_final`/`csi_tilde`/`f_key` + `key_event_step` (event detach SM) + `spawn_stdin_reader` cfg-split (`spawn_stdin_reader_events` on a tty / `spawn_stdin_reader_bytes` non-tty + Unix); unit the EXHAUSTIVE `translate_key_event` mapping + the event-detach SM (the Backspace/Ctrl+Backspace arm carries the relocated `REQ-HAZARD-RC-INPUT-KEY-ENCODING` evidence); NO int (live console = HITL, operator re-capture — REQ-RUN-PICKER/RC-1 precedent).
- **Source:** v0.13.0 bug 2 (operator ruling: proper agnostic translator, ship-blocker; doyle design + Option-B detach ruling 2026-06-19).

---

## Conformance checklist (condensed)

| # | Invariant | spt-core surface |
|---|---|---|
| 1.1 | Grace wait precedes INIT_SIGNOFF | daemon teardown |
| 1.4/4.4 | Deferred rows excluded from event-stream drain | daemon spool drain |
| 2.1/5.1 | Stable PID/broker-handle over ephemeral PID | liveness detection |
| 2.3 | Handoff argv/IPC version-tolerant (newer brain ↔ older broker) | broker↔brain IPC, self-update |
| 2.4 | gen_start = now() on cold-start + handoff | per-instance generation |
| 3.1 | Ephemeral perch cleanup on all exit paths | `ring` (RAII guard) |
| 3.3 | Echo-commune before INIT_SIGNOFF | daemon psyche loop |
| 4.1 | Envelope decode order, `&` last | spt-proto (public, wire-versioned) |
| 4.2 | Parser panic-free + tolerant | spt-proto |
| 4.3 | Stale registry entries → fallback, never hard-fail | subnet registry resolution |
| 4.10 | Silent-node registry rows evicted (heard-map TTL); own rows never decay | registry pump eviction |
| 4.6 | Addressable-id charset reserves `:`/`@` delimiters | `spt_proto::id` at creation seams |
| 5.2 | tmp-write + atomic-rename + retry (EBUSY) | all state writes, binary swap |
| 5.3 | Timeout every harness subprocess | manifest invocations |
| 5.4 | Strip UNC prefix on serialized paths | spt-proto path normalization |
| 5.5 | ConPTY reader answers DSR (`ESC[6n`) | spt-term broker PTY reader |
| 7.7 | A wedged viewer is evicted, never stalls the controller/child/drain | broker `OutputLog` fan-out (controller/viewer) |
| 5.6 | Detached long-lived children spawn `bInheritHandles=FALSE` | daemon + shell spawn |


Activate REQ-RC-WIN-PASTE

Set required_stages = ["doc","impl","unit"] registry-first.

Add clipboard-win dep + read_clipboard

Add clipboard-win as cfg(windows) dep to spt crate; read_clipboard() helper (empty/fail = None).

Impl paste path in rc.rs

wrap_bracketed_paste helper; ctrl+V intercept + right-mouse paste in event loop (drop other mouse); RawGuard EnableMouseCapture +restore (cfg windows).

P1 unit tests

bracketed framing exact + verbatim; ctrl+V detect; mouse classify (right=paste/other=drop); injected-reader paste builder (empty/fail=no-op).

P1 docs + gates + commit + doyle

KNOWN-HAZARDS + CONTEXT note; clippy/traceable/docs-drift/Cargo.lock; commit (trailer); surface green to doyle.


- **Source:** v0.13.0 bug 2 (operator ruling: proper agnostic translator, ship-blocker; doyle design + Option-B detach ruling 2026-06-19).

---

## Conformance checklist (condensed)

- **Source:** v0.13.0 bug 2 (operator ruling: proper agnostic translator, ship-blocker; doyle design + Option-B detach ruling 2026-06-19).

<!-- [doc->REQ-RC-WIN-PASTE] -->
### 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)


- **Source:** v0.13.0 bug 2 (operator ruling: proper agnostic translator, ship-blocker; doyle design + Option-B detach ruling 2026-06-19).

---

## Conformance checklist (condensed)

- **Source:** v0.13.0 bug 2 (operator ruling: proper agnostic translator, ship-blocker; doyle design + Option-B detach ruling 2026-06-19).

<!-- [doc->REQ-RC-WIN-PASTE] -->
### 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)

- **spt-core mapping:** impl `rc.rs` `translate_key_event` + `csi_final`/`csi_tilde`/`f_key` + `key_event_step` (event detach SM) + `spawn_stdin_reader` cfg-split (`spawn_stdin_reader_events` on a tty / `spawn_stdin_reader_bytes` non-tty + Unix); unit the EXHAUSTIVE `translate_key_event` mapping + the event-detach SM (the Backspace/Ctrl+Backspace arm carries the relocated `REQ-HAZARD-RC-INPUT-KEY-ENCODING` evidence); NO int (live console = HITL, operator re-capture — REQ-RUN-PICKER/RC-1 precedent).

+### 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).


Now P1 gate battery: clippy workspace + traceable + docs-drift.


Bash

cargo clippy --workspace --all-targets -- -D warnings 2>&1 | tail -6

Workspace clippy


    Checking spt v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.97s


traceable-reqs check 2>&1 | grep -iE "RC-WIN-PASTE" ; echo "===exit==="; traceable-reqs check >/dev/null 2>&1; echo "exit:$?"

Traceable P1 + full exit


  [OK] REQ-RC-WIN-PASTE  required: [doc, impl, unit]  stages: +doc +impl +unit -int
===exit===
exit:0


cargo run -p xtask -- check 2>&1 | tail -3

Docs-drift


    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.51s
     Running `target\debug\xtask.exe check`
xtask check: OK


traceable EXIT=0 (+doc+impl+unit) · docs-drift OK · clippy 0. Rebuild real bin (links clipboard-win).


Rebuild real spt binary


   Compiling spt-net v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-net)
   Compiling spt-daemon v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
   Compiling spt v0.12.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 26.93s


Real bin built. Check git status, then commit.


Git status before commit


 M CONTEXT.md
 M Cargo.lock
 M crates/spt/Cargo.toml
 M crates/spt/src/rc.rs
 M docs-site/src/harness-contract/integration-checklist.md
 M docs-site/src/harness-contract/manifest.md
 M docs/KNOWN-HAZARDS.md
 M traceable-reqs.toml


git diff --stat docs-site/ && echo "===" && git diff docs-site/src/harness-contract/manifest.md | head -40

Inspect docs-site diff


 .../src/harness-contract/integration-checklist.md  | 10 ++++++++-
 docs-site/src/harness-contract/manifest.md         | 26 ++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)
===
diff --git a/docs-site/src/harness-contract/manifest.md b/docs-site/src/harness-contract/manifest.md
index 2e591d3..7fa0c71 100644
--- a/docs-site/src/harness-contract/manifest.md
+++ b/docs-site/src/harness-contract/manifest.md
@@ -272,6 +272,32 @@ activity = ["hook"]            # non-disruptive while the agent is working
 idle = ["pty", "hook"]
 ```

+## `[message-idle-translation-binary]` — spt-hosted idle delivery
+
+<!-- [doc->REQ-MSG-IDLE-TRANSLATION-BINARY] -->
+
+**Opt-in, spt-hosted only.** An adapter's **idle-delivery translation binary**: a
+pure `stdin → stdout` JSON-lines filter spt-core lifecycle-manages (spawned when
+the spt-hosted endpoint comes up, terminated when it goes down). spt-core feeds it
+the inbound `<EVENT>` message feed and reads back keystroke-commands, which it
+applies to the broker-held PTY **atomically** — a live `spt rc` controller's input
+is buffered during the emitted sequence and flushed after, so idle injection
+coexists with an attached operator (spt-core owns every PTY write). **Idle delivery
+only** — busy / mid-turn delivery stays your `[inject]` hook path.
+
+Declared as a **table** carrying a `path` scalar (a table can't be silently
+absorbed by a preceding section and stays extensible):
+
+```toml
+[message-idle-translation-binary]
+path = "cc-spt-idle-translate"     # the binary spt-core spawns + drives
+```
+
+- **stdin** (spt-core → binary, one JSON object per line): `{"type":"init","endpoint_id":…,"node":…}` first · `{"type":"event","envelope":"<EVENT…>"}` per inbound message (the `<EVENT>` envelope) · `{"type":"input"}` — a **content-free** ping each time the operator types, so the binary can track user-idle (the PTY input content is **never** duplicated to the binary).
+- **stdout** (binary → spt-core, one per line): `{"key":"ctrl+s"}` · `{"delay_ms":50}` · `{"text":"<payload>"}` · `{"key":"enter"}`, … (extensible vocabulary).
+- Unknown fields are **not** rejected here — a newer adapter declaring a future key against an older spt-core parses fine (the key is ignored), so the contract degrades gracefully.
+- The raw `payload + \r` inject is the **degenerate** case: a binary that emits `{"text":payload}{"key":"enter"}` with no choreography.
+
 ## `[identity]` — session identity

 How the harness's session id is obtained:


Activate REQ-RC-WIN-PASTE

Add clipboard-win dep + read_clipboard

Impl paste path in rc.rs

P1 unit tests

P1 docs + gates + commit + doyle


The docs-site changes are stale W2 (translation-binary) generated-page drift that `xtask check` regenerated — unrelated to P1. I'll keep the P1 commit clean and handle the regen separately. Commit P1 first.


git add CONTEXT.md Cargo.lock crates/spt/Cargo.toml crates/spt/src/rc.rs docs/KNOWN-HAZARDS.md traceable-reqs.toml && git commit -F - <<'EOF'
feat(rc): Windows ctrl+V + right-click paste via local clipboard → bracketed paste (REQ-RC-WIN-PASTE)

In an `spt rc` session neither ctrl+V nor right-click pasted. CC runs
daemon-side with NO access to the operator's LOCAL clipboard, so forwarding
`^V` can never paste — remote paste is fundamentally client-originated. The
Windows console also delivers a paste as synthetic per-char KEY EVENTs (no
crossterm `Event::Paste`), so a multi-line paste-as-keys became a `\r`
submit-storm.

On Windows (cfg-split, folding into the bug-2 event path), rc now reads the
LOCAL clipboard on a paste gesture and forwards a BRACKETED paste
(`ESC[200~` + content + `ESC[201~`). CC has bracketed-paste mode on
(`ESC[?2004h`), so it treats the markers as a paste — content intact, no
submit-storm, harness-agnostic. Content is forwarded VERBATIM (literal text,
no key translation).

- ctrl+V: intercept `Char('v')+CONTROL` in the event loop before translation,
  when not detach-armed (`ctrl-b` keeps precedence) → read_clipboard → wrap.
- right-click: RawGuard now EnableMouseCapture (disables console QuickEdit so a
  right-click reaches the app as `Event::Mouse`) on an interactive console only,
  restored on drop → right-button-down → read_clipboard → wrap. All other mouse
  dropped (CC has no mouse features).
- read_clipboard = clipboard-win (cfg windows); empty/failed read = clean no-op.
- Unix unchanged (its terminal pastes natively through the byte pump).

A paste chunk is one large StdinMsg → one send_attach_input → P0's single-writer
thread carries it without wedging (hard dependency on P0 @0675b37).

- unit: bracketed framing exact + content verbatim; ctrl+V detection; mouse
  classify (right-down ⇒ paste, all else ⇒ drop); injected-reader paste decision
  (non-empty ⇒ wrapped, empty/fail ⇒ no-op). NO int (live clipboard + console
  mouse = HITL, REQ-RUN-PICKER/RC-1 precedent).
- docs: KNOWN-HAZARDS 7.18 + CONTEXT input-injection note.

Gates: clippy --workspace -D warnings = 0; traceable EXIT=0 (+doc+impl+unit);
xtask docs-drift OK; Cargo.lock updated (clipboard-win 5.4.1 + error-code);
real spt bin rebuilt; rc unit 12/12.

Co-authored by: todlando
EOF

Commit P1 paste feature


warning: in the working copy of 'Cargo.lock', LF will be replaced by CRLF the next time Git touches it
[v0.13.0-delivery-control f7cf380] feat(rc): Windows ctrl+V + right-click paste via local clipboard → bracketed paste (REQ-RC-WIN-PASTE)
 6 files changed, 222 insertions(+), 4 deletions(-)


P1 committed @f7cf380. Now the separate docs-site regen commit (stale W2 generated-page sync).


git diff docs-site/src/harness-contract/integration-checklist.md | head -30

Inspect integration-checklist diff


(Bash completed with no output)


git --no-pager diff docs-site/src/harness-contract/integration-checklist.md

Show integration-checklist diff


git status --short && echo "===staged diff stat===" && git --no-pager diff --cached --stat

Recheck status


===staged diff stat===


git --no-pager show --stat --oneline HEAD | head -20

Show HEAD commit files


f7cf380 feat(rc): Windows ctrl+V + right-click paste via local clipboard → bracketed paste (REQ-RC-WIN-PASTE)
 CONTEXT.md            |   2 +-
 Cargo.lock            |  16 +++++
 crates/spt/Cargo.toml |   6 ++
 crates/spt/src/rc.rs  | 192 +++++++++++++++++++++++++++++++++++++++++++++++++-
 docs/KNOWN-HAZARDS.md |   8 +++
 traceable-reqs.toml   |   2 +-
 6 files changed, 222 insertions(+), 4 deletions(-)


Tree is clean — the docs-site mod was transient (line-ending renorm), nothing stray to commit. Final drift sanity + surface to doyle.


cargo run -p xtask -- check 2>&1 | tail -2 && echo "---status---" && git status --short

Final docs-drift + clean-tree check


     Running `target\debug\xtask.exe check`
xtask check: OK
---status---


Clean tree, drift OK. P1 done @f7cf380 — surface to doyle.


<owl_messages>
<owl_message from="doyle" priority="high">HEADS-UP (shared-tree): branch advanced to 8b36b03 on origin. Two things rode up:
1. Your P0 @0675b37 was committed LOCALLY but UNPUSHED — my docs commit carried it to origin (range 96d0780..8b36b03 includes it). P0 is gated, so it belongs there; just don't try to re-push it. Continue P1 from 8b36b03 (pull if your tree differs).
2. @8b36b03 = I published the two adapter manifest seams to the PUBLIC docs-site for perri (operator-directed, this release): [message-idle-translation-binary] (was MISSING from docs-site — only in the internal MANIFEST/ADR) + [session.resume] added to the integration-checklist (Group 3 + floor + cc worked example). docs-drift OK. These are docs-site only — zero overlap with your P1 (rc.rs).

Proceed P1. Surface green for gate.</owl_message>
</owl_messages>

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


Updated task #12 status


$OWL send doyle <<'EOF'
P1 GREEN @f7cf380 — REQ-RC-WIN-PASTE done, ready for your gate. The LAST build item.

FIX (cfg(windows), folds into the bug-2 event path; depends on P0 @0675b37 landed):
- rc reads the LOCAL clipboard on a paste gesture and injects a BRACKETED paste (wrap_bracketed_paste = ESC[200~ + content + ESC[201~). Content VERBATIM (no key translation) -> CC's bracketed-paste mode lands multi-line intact, no submit-storm, harness-agnostic.
- ctrl+V: intercept Char('v')+CONTROL in spawn_stdin_reader_events BEFORE translation, only when NOT detach-armed (ctrl-b keeps precedence).
- right-click: RawGuard now EnableMouseCapture (disables console QuickEdit) on an interactive console only, DisableMouseCapture on drop; right-button-down -> clipboard paste; ALL other mouse dropped (CC has no mouse features).
- read_clipboard = clipboard-win 5.4.1 (cfg windows, +error-code; 2 pkgs, minimal); empty/failed read = clean no-op (never inject garbage/panic).
- Unix UNCHANGED (byte pump pastes natively). A paste chunk = one StdinMsg -> P0's single-writer carries it, no wedge.

GATES (independently re-run):
- clippy --workspace --all-targets -D warnings = 0
- traceable EXIT=0; REQ-RC-WIN-PASTE +doc +impl +unit (NO int — live clipboard+console mouse = HITL, RC-1 precedent)
- xtask docs-drift OK; real spt bin rebuilt (links clipboard-win); Cargo.lock updated
- rc unit 12/12 incl 4 new: bracketed_paste_framing_is_exact_and_content_verbatim · is_ctrl_v_only_for_ctrl_plus_v · mouse_is_paste_only_right_button_down · clipboard_paste_wraps_nonempty_and_noops_on_empty_or_failure (injected reader, no real clipboard)

DOCS: KNOWN-HAZARDS 7.18 (full ### narrative + doc tag) + CONTEXT input-injection note (rc Windows paste client-originated; Unix rides the byte stream).

NOTE: kept the P1 commit clean — a stale W2 docs-site generated-page drift surfaced transiently under xtask regen but renormalized away; tree is clean, nothing stray committed.

HITL acceptance (operator): ctrl+V AND right-click both paste clipboard content (incl multi-line, no submit-storm) into a real CC rc session; no wedge (P0).

This was the last build item for v0.13.0 delivery-control. Awaiting your gate.
EOF

Send P1 green to doyle


➜ SENT:doyle


Ack doyle (both high-priority):
1. P0 @0675b37 already on origin via your docs commit (96d0780..8b36b03) — won't re-push it.
2. @8b36b03 = docs-site adapter seams ([message-idle-translation-binary] + [session.resume] integration-checklist) — that's exactly the transient docs-site drift I saw; it was your concurrent commit, zero overlap with my rc.rs. Good.

My P1 @f7cf380 is local, parented on 0675b37 — diverged from origin 8b36b03. Rebasing P1 onto the published base now.


git fetch origin 2>&1 | tail -3; echo "===local log==="; git --no-pager log --oneline -3; echo "===origin==="; git --no-pager log --oneline -3 origin/v0.13.0-delivery-control 2>&1 | head -5

Fetch + compare local vs origin


===local log===
f7cf380 feat(rc): Windows ctrl+V + right-click paste via local clipboard → bracketed paste (REQ-RC-WIN-PASTE)
8b36b03 docs(harness-contract): publish [message-idle-translation-binary] + checklist both seams
0675b37 fix(broker): per-session PTY input-writer thread; never wedge on a paste burst (REQ-HAZARD-PTY-INPUT-WRITER-WEDGE)
===origin===
8b36b03 docs(harness-contract): publish [message-idle-translation-binary] + checklist both seams
0675b37 fix(broker): per-session PTY input-writer thread; never wedge on a paste burst (REQ-HAZARD-PTY-INPUT-WRITER-WEDGE)
96d0780 feat(picker): drop tz token from resume-row local-time title (operator ruling)


No rebase needed — local history is already linear: `0675b37` (P0) → `8b36b03` (your docs) → `f7cf380` (P1). My P1 commit already parents on 8b36b03 (your docs commit was in my local HEAD before I committed P1 — explains the transient docs-site drift I saw). Tree doesn't differ; f7cf380 is just 1 ahead, unpushed, awaiting your gate.


$OWL send doyle <<'EOF'
Re shared-tree heads-up: no rebase needed — my P1 @f7cf380 ALREADY parents on your @8b36b03 (local history is linear: 0675b37 P0 -> 8b36b03 your docs -> f7cf380 P1). That's what I saw as transient docs-site drift earlier — your concurrent docs commit, already in my HEAD before I committed P1. Tree is clean, f7cf380 is 1 ahead/unpushed, leaving it for your gate (same as P0 rode up via your push). P1 green already surfaced. Awaiting gate.
EOF

Confirm P1 base to doyle


v0.13.0 delivery-control build complete — all items done.

**P0 @0675b37** (gate-passed by doyle): per-session PTY input-writer thread, paste-burst never wedges broker.
**P1 @f7cf380** (surfaced for gate): Windows ctrl+V + right-click → local-clipboard bracketed paste.
**P2 @153c9c9** (gate-passed earlier): session.resume template.

**P1 gates (independently re-run):** clippy `-D warnings`=0 · traceable EXIT=0 (+doc+impl+unit) · docs-drift OK · real bin rebuilt (links clipboard-win 5.4.1) · rc unit 12/12 (4 new).

History linear on doyle's published base 8b36b03; P1 unpushed, awaiting doyle's gate (P0 rode to origin via doyle's docs commit). No int for P1 — live clipboard+mouse = operator HITL.

Awaiting doyle's P1 gate verdict. That's the last build item for the milestone — after gate + operator HITL (ctrl+V/right-click paste in a real CC rc session, no wedge), v0.13.0 delivery-control is ship-ready.


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/3" id="a509370c" type="msg" from="doyle">P1 GATE-PASS @f7cf380 (REQ-RC-WIN-PASTE). The LAST build item — independently verified: clippy=0, rc units 12/12 (incl bracketed framing exact+verbatim / ctrl+V detection / mouse-paste-only-right-down / injected-reader no-op), traceable [doc,impl,unit], docs-drift OK, arm order correct (ctrl+V !armed guard preserves c</EVENT-PART>
<EVENT-PART seq="2/3" id="a509370c">trl-b precedence), clipboard-win cfg(windows). Pushed to origin (CI re-running on full round-2).

ALL v0.13.0 ROUND-2 BUILD ITEMS GATED: P0 (wedge) + P1 (paste) + P2 (resume+picker) + docs (both seams published). Outstanding build work = DONE.

NEXT (mine): batched PATH re-deploy from f7cf380 -&gt; operator HITL all four -&gt; CI g</EVENT-PART>
<EVENT-PART seq="3/3" id="a509370c">reen (incl P3/w5_a2 triage) -&gt; deployah v0.13.0. You're CLEAR on build. I may ping for P3/w5_a2_abandoned triage (the flaky-neighbor) if CI needs it. Excellent round — all four roots closed clean.</EVENT-PART></event>
</task-notification>


<owl_reachability_notice>
You have an active owl perch. When spawning subagents or doing work, use run_in_background: true so you stay reachable for incoming messages.
</owl_reachability_notice>


CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging. Fragments OK. Code/commits/security: write normal.


P1 GATE-PASS @f7cf380 confirmed. All v0.13.0 round-2 build items gated + pushed to origin: **P0** (paste-wedge) + **P1** (rc paste) + **P2** (resume+picker) + docs (both adapter seams). Build work done.

doyle owns the rest: batched PATH re-deploy → operator HITL (all four) → CI green (incl P3/w5_a2 triage) → deploy. I'm clear on build.

Standing by for a possible w5_a2_abandoned flaky-neighbor triage ping (pre-existing W5 attach-area test, not P0 — same class as the CI Linux reds). Nothing else outstanding on my side. All four roots closed clean.


<task-notification>
<task-id>b4cwwiglf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/8" id="0a203334" type="msg" from="doyle">URGENT DIAGNOSIS (v0.13.0 HITL re-open) — P1 ctrl+V WEDGES THE ENTIRE BROKER. Right-click paste (P1b) works perfectly (short+long multiline). ctrl+V is broken. NOT a fix yet — I need a CONFIRM-FIRST instrumented + isolation build to pin the root. Don't guess-fix.

HARD EVIDENCE (operator HITL, Windows Terminal</EVENT-PART>
<EVENT-PART seq="2/8" id="0a203334">):
- A 1-CHARACTER clipboard ctrl+V ALSO wedges → NOT a flood/volume (rules out dispatch-saturation by frame count).
- Wedge is PERMANENT + ENTIRE BROKER (can't attach new/existing), not one session.
- Session STAYS CONTROLLED — closing the rc tab did NOT register the detach (the controller's broker conn-handler is blocked).
- </EVENT-PART>
<EVENT-PART seq="3/8" id="0a203334">Right-click (same clipboard_paste-&gt;wrap-&gt;send path) does NOT wedge. So post-gesture our bytes are fine; the difference is the GESTURE SOURCE.

PARADOX to resolve: read_clipboard runs on the stdin EVENT thread (separate from the output pump), so a blocked clipboard read shouldn't stall output drain → shouldn't wedge the broker. Ye</EVENT-PART>
<EVENT-PART seq="4/8" id="0a203334">t it does. So either (a) WT's OWN ctrl+V paste accelerator injects something additional that hits the broker, or (b) our handler interacts with the broker controller-write path, or (c) something re-enters become_controller / the synchronous inline controller-write (attach.rs:120-134 known sync-write weakness) and blocks the per-conn handler.<b</EVENT-PART>
<EVENT-PART seq="5/8" id="0a203334">r>
BUILD ME A DIAGNOSTIC build (one deploy, multiple probes):
1. ISOLATION: gate our ctrl+V interception behind env SPT_RC_CTRLV (default on; SPT_RC_CTRLV=0 disables the is_ctrl_v arm, leaving WT to handle ctrl+V natively). Lets the operator test: does ctrl+V STILL wedge with OUR handler OFF? (yes =&gt; not our handler; no =&gt; our hand</EVENT-PART>
<EVENT-PART seq="6/8" id="0a203334">ler is the cause).
2. INSTRUMENT (env SPT_RC_DEBUG_PASTE): rc-side eprintln on — &quot;CTRLV arm fired&quot;, &quot;read_clipboard START&quot;, &quot;read_clipboard END len=N&quot; (a missing END line = read_clipboard blocks), &quot;sent N bytes&quot;; same for the right-mouse path for comparison.
3. INSTRUMENT broker-side (env SPT_BRO</EVENT-PART>
<EVENT-PART seq="7/8" id="0a203334">KER_DEBUG_INPUT): log in dispatch_input (&quot;INPUT recv N op=.. enqueue-done&quot;) AND the controller output-write / become_controller path (&quot;CTRL_WRITE start/end&quot;, &quot;become_controller replay start/end&quot;) so we see WHICH broker call blocks + whether the per-conn handler returns.

Deliver as a throwaway diagnostic bra</EVENT-PART>
<EVENT-PART seq="8/8" id="0a203334">nch/commit (NOT the gate). Operator runs daemon+rc with the envs, presses 1-char ctrl+V, captures broker stderr + rc stderr. Surface the build + I'll relay capture instructions. This is the v0.13.0 ship-blocker
...(truncated)</event>
</task-notification>


P1 ctrl+V wedges broker (HITL re-open). Confirm-first diagnostic build, no guess-fix. Reading the broker-side instrumentation points doyle flagged (attach.rs sync-write + send_effect).