Edit

        // Resolve endpoint → (input-writer, translation) under the lock, cloning the
        // Arcs out so the ENQUEUE happens without holding the table lock
        // (dispatch_input rule). The raw-inject path now enqueues to the lone
        // writer too, so it can never park the dispatch thread (P0).
        let resolved = {
            let sessions = self.sessions.lock().unwrap();
            sessions
                .values()
                .find(|h| h.endpoint == req.endpoint)
                .map(|h| (Arc::clone(&h.input), h.translation.clone()))
        };
        match resolved {
            Some((input, translation)) => {
                // Layer E: a live (non-faulted) translation binary OWNS the
                // choreography — queue the whole `<EVENT>` envelope to the inject
                // worker, which drives it through the binary and applies the
                // resulting keystrokes atomically w.r.t. the controller. Raw inject
                // is the DEGENERATE fallback when no binary is attached, it has
                // faulted, or the worker channel is gone.
                if let Some(tr) = &translation {
                    if !tr.faulted.load(Ordering::Relaxed) {
                        let envelope = String::from_utf8_lossy(&bytes).into_owned();
                        if tr.event_tx.send(envelope).is_ok() {
                            eprintln!(
                                "ENDPOINT_INJECT:{} ({} bytes → translation binary)",
                                req.endpoint,
                                bytes.len()
                            );
                            send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
                            return Ok(());
                        }
                        // worker receiver dropped → fall through to raw inject.
                    }
                }
                // IDLE/direct raw inject. Logged (REQ-SEND-SPT-HOSTED MVP): activity
                // is not yet gated, so note the raw write — never a silent mid-turn
                // interleave passed off as done.
                eprintln!(
                    "ENDPOINT_INJECT:{} ({} bytes, idle-direct — activity not yet gated)",
                    req.endpoint,
                    bytes.len()
                );
                input.enqueue(bytes);
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
                Ok(())
            }
            // No hosted session for this endpoint — tell the caller to spool.
            None => {
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));
                Ok(())
            }
        }

        // Resolve endpoint → translation under the lock, cloning the Arc out so the
        // event send happens without holding the table lock (dispatch_input rule).
        // The session's PTY input writer is intentionally NOT pulled here: idle
        // delivery is translation-binary-ONLY (ADR-0022 amendment) — with no
        // working binary the message SPOOLS, it is never written to the PTY.
        let resolved = {
            let sessions = self.sessions.lock().unwrap();
            sessions
                .values()
                .find(|h| h.endpoint == req.endpoint)
                .map(|h| h.translation.clone())
        };
        match resolved {
            Some(translation) => {
                // Layer E: a live (non-faulted) translation binary OWNS the
                // choreography — queue the whole `<EVENT>` envelope to the inject
                // worker, which drives it through the binary and applies the
                // resulting keystrokes atomically w.r.t. the controller.
                if let Some(tr) = &translation {
                    if !tr.faulted.load(Ordering::Relaxed) {
                        let envelope = String::from_utf8_lossy(&bytes).into_owned();
                        if tr.event_tx.send(envelope).is_ok() {
                            eprintln!(
                                "ENDPOINT_INJECT:{} ({} bytes → translation binary)",
                                req.endpoint,
                                bytes.len()
                            );
                            send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
                            return Ok(());
                        }
                        // worker receiver dropped → SPOOL (below), never raw inject.
                    }
                }
                // No WORKING translation binary (none declared, faulted, or its
                // inject worker is gone) → tell the caller to SPOOL (delivered=false).
                // Raw `payload + \r` is NOT a fallback: on a modern TUI it does not
                // submit — a non-delivering pseudo-write that MASKED F-019. The
                // inbound is never lost (it stays spooled, poll-fed) and the failure
                // is LOUD so a mis-declared/repaired binary is diagnosable.
                // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]
                eprintln!(
                    "ENDPOINT_INJECT:{}: no working translation binary (absent/faulted/worker-gone) -> SPOOLED, not injected",
                    req.endpoint
                );
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));
                Ok(())
            }
            // No hosted session for this endpoint — tell the caller to spool.
            None => {
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));
                Ok(())
            }
        }


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

        // Resolve endpoint → (input-writer, translation) under the lock, cloning the
        // Arcs out so the ENQUEUE happens without holding the table lock
        // (dispatch_input rule). The raw-inject path now enqueues to the lone
        // writer too, so it can never park the dispatch thread (P0).
        let resolved = {
            let sessions = self.sessions.lock().unwrap();
            sessions
                .values()
                .find(|h| h.endpoint == req.endpoint)
                .map(|h| (Arc::clone(&h.input), h.translation.clone()))
        };
        match resolved {
            Some((input, translation)) => {
                // Layer E: a live (non-faulted) translation binary OWNS the
                // choreography — queue the whole `<EVENT>` envelope to the inject
                // worker, which drives it through the binary and applies the
                // resulting keystrokes atomically w.r.t. the controller. Raw inject
                // is the DEGENERATE fallback when no binary is attached, it has
                // faulted, or the worker channel is gone.
                if let Some(tr) = &translation {
                    if !tr.faulted.load(Ordering::Relaxed) {
                        let envelope = String::from_utf8_lossy(&bytes).into_owned();
                        if tr.event_tx.send(envelope).is_ok() {
                            eprintln!(
                                "ENDPOINT_INJECT:{} ({} bytes → translation binary)",
                                req.endpoint,
                                bytes.len()
                            );
                            send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
                            return Ok(());
                        }
                        // worker receiver dropped → fall through to raw inject.
                    }
                }
                // IDLE/direct raw inject. Logged (REQ-SEND-SPT-HOSTED MVP): activity
                // is not yet gated, so note the raw write — never a silent mid-turn
                // interleave passed off as done.
                eprintln!(
                    "ENDPOINT_INJECT:{} ({} bytes, idle-direct — activity not yet gated)",
                    req.endpoint,
                    bytes.len()
                );
                input.enqueue(bytes);
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
                Ok(())
            }
            // No hosted session for this endpoint — tell the caller to spool.
            None => {
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));
                Ok(())
            }
        }

        // Resolve endpoint → translation under the lock, cloning the Arc out so the
        // event send happens without holding the table lock (dispatch_input rule).
        // The session's PTY input writer is intentionally NOT pulled here: idle
        // delivery is translation-binary-ONLY (ADR-0022 amendment) — with no
        // working binary the message SPOOLS, it is never written to the PTY.
        let resolved = {
            let sessions = self.sessions.lock().unwrap();
            sessions
                .values()
                .find(|h| h.endpoint == req.endpoint)
                .map(|h| h.translation.clone())
        };
        match resolved {
            Some(translation) => {
                // Layer E: a live (non-faulted) translation binary OWNS the
                // choreography — queue the whole `<EVENT>` envelope to the inject
                // worker, which drives it through the binary and applies the
                // resulting keystrokes atomically w.r.t. the controller.
                if let Some(tr) = &translation {
                    if !tr.faulted.load(Ordering::Relaxed) {
                        let envelope = String::from_utf8_lossy(&bytes).into_owned();
                        if tr.event_tx.send(envelope).is_ok() {
                            eprintln!(
                                "ENDPOINT_INJECT:{} ({} bytes → translation binary)",
                                req.endpoint,
                                bytes.len()
                            );
                            send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
                            return Ok(());
                        }
                        // worker receiver dropped → SPOOL (below), never raw inject.
                    }
                }
                // No WORKING translation binary (none declared, faulted, or its
                // inject worker is gone) → tell the caller to SPOOL (delivered=false).
                // Raw `payload + \r` is NOT a fallback: on a modern TUI it does not
                // submit — a non-delivering pseudo-write that MASKED F-019. The
                // inbound is never lost (it stays spooled, poll-fed) and the failure
                // is LOUD so a mis-declared/repaired binary is diagnosable.
                // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]
                eprintln!(
                    "ENDPOINT_INJECT:{}: no working translation binary (absent/faulted/worker-gone) -> SPOOLED, not injected",
                    req.endpoint
                );
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));
                Ok(())
            }
            // No hosted session for this endpoint — tell the caller to spool.
            None => {
                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));
                Ok(())
            }
        }

.map_err(|e| format!("bad endpoint-input payload: {e}"))?;

decode_bytes(&req.data_b64).map_err(|e| format!("bad endpoint-input base64: {e}"))?;

-        // Resolve endpoint → (input-writer, translation) under the lock, cloning the

-        // Arcs out so the ENQUEUE happens without holding the table lock

-        // (dispatch_input rule). The raw-inject path now enqueues to the lone

-        // writer too, so it can never park the dispatch thread (P0).

+        // Resolve endpoint → translation under the lock, cloning the Arc out so the

+        // event send happens without holding the table lock (dispatch_input rule).

+        // The session's PTY input writer is intentionally NOT pulled here: idle

+        // delivery is translation-binary-ONLY (ADR-0022 amendment) — with no

+        // working binary the message SPOOLS, it is never written to the PTY.

-                .map(|h| (Arc::clone(&h.input), h.translation.clone()))

+                .map(|h| h.translation.clone())

-            Some((input, translation)) => {

-                // resulting keystrokes atomically w.r.t. the controller. Raw inject

-                // is the DEGENERATE fallback when no binary is attached, it has

-                // faulted, or the worker channel is gone.

+                // resulting keystrokes atomically w.r.t. the controller.

-                        // worker receiver dropped → fall through to raw inject.

+                        // worker receiver dropped → SPOOL (below), never raw inject.

-                // IDLE/direct raw inject. Logged (REQ-SEND-SPT-HOSTED MVP): activity

-                // is not yet gated, so note the raw write — never a silent mid-turn

-                // interleave passed off as done.

+                // No WORKING translation binary (none declared, faulted, or its

+                // inject worker is gone) → tell the caller to SPOOL (delivered=false).

+                // Raw `payload + \r` is NOT a fallback: on a modern TUI it does not

+                // submit — a non-delivering pseudo-write that MASKED F-019. The

+                // inbound is never lost (it stays spooled, poll-fed) and the failure

+                // is LOUD so a mis-declared/repaired binary is diagnosable.

+                // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]

-                    "ENDPOINT_INJECT:{} ({} bytes, idle-direct — activity not yet gated)",

+                    "ENDPOINT_INJECT:{}: no working translation binary (absent/faulted/worker-gone) -> SPOOLED, not injected",

-                send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));

+                send_frame(send, &endpoint_injected_envelope(&req.endpoint, false));


Edits 2-3 — fault_translation + build_translation (logs + docs):


        Err(e) => {
            eprintln!("TRANSLATION_SPAWN_FAILED:{path}: {e} — degrading to raw inject");
            None
        }

        Err(e) => {
            eprintln!(
                "TRANSLATION_SPAWN_FAILED:{path}: {e} — no idle PTY delivery; inbound spools (declare/repair the binary)"
            );
            None
        }


        Err(e) => {
            eprintln!("TRANSLATION_SPAWN_FAILED:{path}: {e} — degrading to raw inject");
            None
        }

        Err(e) => {
            eprintln!(
                "TRANSLATION_SPAWN_FAILED:{path}: {e} — no idle PTY delivery; inbound spools (declare/repair the binary)"
            );
            None
        }

-            eprintln!("TRANSLATION_SPAWN_FAILED:{path}: {e} — degrading to raw inject");

+                "TRANSLATION_SPAWN_FAILED:{path}: {e} — no idle PTY delivery; inbound spools (declare/repair the binary)"


/// owns the atomic PTY apply, and return the `Arc<Translation>`. A spawn failure
/// FAILS CLOSED to raw inject (`None`, logged) — a missing binary must never cost
/// the operator their agent. `input` is the session's EXISTING PTY input writer

/// owns the atomic PTY apply, and return the `Arc<Translation>`. A spawn failure
/// FAILS CLOSED to SPOOLING (`None`, logged): `None` now MEANS the inbound spools
/// (idle delivery is translation-binary-only, never a raw PTY inject) — a missing
/// binary costs idle delivery, never the operator their agent. `input` is the session's EXISTING PTY input writer


/// owns the atomic PTY apply, and return the `Arc<Translation>`. A spawn failure
/// FAILS CLOSED to raw inject (`None`, logged) — a missing binary must never cost
/// the operator their agent. `input` is the session's EXISTING PTY input writer

/// owns the atomic PTY apply, and return the `Arc<Translation>`. A spawn failure
/// FAILS CLOSED to SPOOLING (`None`, logged): `None` now MEANS the inbound spools
/// (idle delivery is translation-binary-only, never a raw PTY inject) — a missing
/// binary costs idle delivery, never the operator their agent. `input` is the session's EXISTING PTY input writer

/// Bring up a translation binary at `path` for `endpoint_id`: spawn the

/// stdin→stdout filter, send the identity `Init`, start the inject worker that

-/// FAILS CLOSED to raw inject (`None`, logged) — a missing binary must never cost

-/// the operator their agent. `input` is the session's EXISTING PTY input writer

+/// FAILS CLOSED to SPOOLING (`None`, logged): `None` now MEANS the inbound spools

+/// (idle delivery is translation-binary-only, never a raw PTY inject) — a missing

+/// binary costs idle delivery, never the operator their agent. `input` is the session's EXISTING PTY input writer

/// (reused; only the translation child is created). Extracted verbatim from

/// `dispatch_spawn` so a live-update restart (`dispatch_adapter_apply`, W3d) can

/// rebuild a session's translation child from the NEW binary without disturbing


Edit — fault_translation doc + log:


/// FAULT a translation binary: mark the session degraded (so inbound delivery
/// reverts to raw inject and `dispatch_input` stops pinging), log it, and reap the
/// child. Never respawns — degrade-to-pass-through, never a dead operator.
fn fault_translation(faulted: &AtomicBool, child: &TranslationChild, why: &str) {
    faulted.store(true, Ordering::Relaxed);
    eprintln!("TRANSLATION_FAULT: {why} — terminating binary, reverting to raw inject");
    child.terminate();
}

/// FAULT a translation binary: mark the session degraded (so inbound delivery
/// SPOOLS until a working binary and `dispatch_input` stops pinging), log it, and
/// reap the child. Never respawns — inbound spools (poll-fed), never raw-injected,
/// never a dead operator.
fn fault_translation(faulted: &AtomicBool, child: &TranslationChild, why: &str) {
    faulted.store(true, Ordering::Relaxed);
    eprintln!(
        "TRANSLATION_FAULT: {why} — terminating binary; idle messages spool until a working binary"
    );
    child.terminate();
}


/// FAULT a translation binary: mark the session degraded (so inbound delivery
/// reverts to raw inject and `dispatch_input` stops pinging), log it, and reap the
/// child. Never respawns — degrade-to-pass-through, never a dead operator.
fn fault_translation(faulted: &AtomicBool, child: &TranslationChild, why: &str) {
    faulted.store(true, Ordering::Relaxed);
    eprintln!("TRANSLATION_FAULT: {why} — terminating binary, reverting to raw inject");
    child.terminate();
}

/// FAULT a translation binary: mark the session degraded (so inbound delivery
/// SPOOLS until a working binary and `dispatch_input` stops pinging), log it, and
/// reap the child. Never respawns — inbound spools (poll-fed), never raw-injected,
/// never a dead operator.
fn fault_translation(faulted: &AtomicBool, child: &TranslationChild, why: &str) {
    faulted.store(true, Ordering::Relaxed);
    eprintln!(
        "TRANSLATION_FAULT: {why} — terminating binary; idle messages spool until a working binary"
    );
    child.terminate();
}

-/// reverts to raw inject and `dispatch_input` stops pinging), log it, and reap the

-/// child. Never respawns — degrade-to-pass-through, never a dead operator.

+/// SPOOLS until a working binary and `dispatch_input` stops pinging), log it, and

+/// reap the child. Never respawns — inbound spools (poll-fed), never raw-injected,

-    eprintln!("TRANSLATION_FAULT: {why} — terminating binary, reverting to raw inject");

+        "TRANSLATION_FAULT: {why} — terminating binary; idle messages spool until a working binary"


Sweep remaining raw-inject framing in broker.rs systematically:


Grep

raw inject|raw-inject|degenerate|degrade|pass-through|reverts to raw|fall.?back.*raw


154:/// respawn → the session degrades to raw inject), and never re-opens. A wedged or
829:/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.
837:/// the session reverts to raw inject and stops pinging.
848:    /// session reverts to raw inject and `dispatch_input` stops pinging.
872:    /// no `[message-idle-translation-binary]` — then inbound delivery raw-injects.
1027:/// session degrades to raw inject). The loop ends when `event_tx` is dropped
1100:        // Command::new fails to find, silently degrading to raw inject.
1177:/// FAULT a translation binary: mark the session degraded (so inbound delivery
1179:/// reap the child. Never respawns — inbound spools (poll-fed), never raw-injected,
1252:    /// bind failure — the daemon degrades to a net-less broker rather than
1682:        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to raw inject
1921:                        // worker receiver dropped → SPOOL (below), never raw inject.
2433:    /// swallow dropped — advancing past a failed write would degrade


Several sites to reword (154, 829, 837, 848, 872, 1027, 1100, 1682). Read each in context:


148
149	/// MANDATORY bound on one injected keystroke sequence (W2 Layer C, ADR-0022 /
150	/// doyle non-negotiable = W1's "never park forever"): the inject worker holds the
151	/// floor — buffering operator input — only while a binary's response to one
152	/// `{type:event}` is in flight. If no `{commit}` arrives within this window the
153	/// worker FLUSHES the buffer, RELEASES the floor, FAULTS the binary (terminate, no
154	/// respawn → the session degrades to raw inject), and never re-opens. A wedged or
155	/// buggy translation binary can therefore stall operator input for at most this
156	/// long. Mirrors [`CONTROLLER_WRITE_DEADLINE`]'s order (5 s).
157	const INJECT_COMMIT_DEADLINE: Duration = Duration::from_secs(5);
158
159	/// The effective commit deadline. Production is the const above; the int gate
160	/// (Layer G no-commit FAULT test) shrinks it via `SPT_INJECT_COMMIT_DEADLINE_MS`
161	/// so the structural outcome (floor releases → buffered input flushes → binary

/// MANDATORY bound on one injected keystroke sequence (W2 Layer C, ADR-0022 /
/// doyle non-negotiable = W1's "never park forever"): the inject worker holds the
/// floor — buffering operator input — only while a binary's response to one
/// `{type:event}` is in flight. If no `{commit}` arrives within this window the
/// worker FLUSHES the buffer, RELEASES the floor, FAULTS the binary (terminate, no
/// respawn → the session degrades to raw inject), and never re-opens. A wedged or
/// buggy translation binary can therefore stall operator input for at most this
/// long. Mirrors [`CONTROLLER_WRITE_DEADLINE`]'s order (5 s).
const INJECT_COMMIT_DEADLINE: Duration = Duration::from_secs(5);

/// The effective commit deadline. Production is the const above; the int gate
/// (Layer G no-commit FAULT test) shrinks it via `SPT_INJECT_COMMIT_DEADLINE_MS`
/// so the structural outcome (floor releases → buffered input flushes → binary


820	            // operator sees it), but the durable resume cursor must NOT jump past the
821	            // gap — see [`contiguous_advance`].
822	            contiguous_advance(&delivered_through, seq);
823	        }
824	    }
825	}
826
827	/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present
828	/// ONLY when the spawn carried a `translation_binary` path; absent → the session
829	/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.
830	///
831	/// Holds the shared inject floor (the controller-input buffer, also touched by
832	/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
833	/// (events serialize — never two uncommitted sequences interleaved), the shared
834	/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
835	/// while the worker drives an event), and the FAULT flag the worker raises when a
836	/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
837	/// the session reverts to raw inject and stops pinging.
838	struct Translation {
839	    /// Controller-input buffer + inject-floor flag, shared with `dispatch_input`.
840	    floor: Arc<Mutex<InjectFloor>>,
841	    /// Queue one inbound `<EVENT>` envelope to the inject worker. Events QUEUE here
842	    /// and are driven one-at-a-time so two sequences never interleave.
843	    event_tx: Sender<String>,
844	    /// The live child — shared so `dispatch_input` fires content-free pings while
845	    /// the worker (which also holds an `Arc`) drives events / faults the binary.
846	    child: Arc<TranslationChild>,
847	    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
848	    /// session reverts to raw inject and `dispatch_input` stops pinging.
849	    faulted: Arc<AtomicBool>,
850	}
851
852	/// A child the broker hosts under a PTY — the §B-owned resources for one
853	/// session, plus its gapless-resume output log.
854	struct HostedSession {
855	    /// The PTY + child (PTY master/writer + spawned child process).
856	    session: Arc<PtySession>,
857	    /// The session's dedicated PTY input-writer (v0.13.0 P0,
858	    /// REQ-HAZARD-PTY-INPUT-WRITER-WEDGE): the SOLE caller of the blocking
859	    /// `write_input`. Every dispatch-side write ENQUEUES here and returns at once,
860	    /// so a paste burst that fills the harness's input buffer parks only the
861	    /// writer thread, never the broker dispatch thread.
862	    input: Arc<InputWriter>,
863	    /// The output drain pump (kept so teardown can stop it).
864	    #[allow(dead_code)] // held for ownership/teardown; not read directly
865	    drain: Drain,
866	    /// The sequenced, buffered output + current subscriber.
867	    log: Arc<Mutex<OutputLog>>,
868	    /// The owning endpoint (the spawn's label; empty = unlabeled). The
869	    /// inbound dispatcher's gate subject for remote-drive (D9-1).
870	    endpoint: String,
871	    /// The translation-binary attachment (Layer C), or `None` for a session with
872	    /// no `[message-idle-translation-binary]` — then inbound delivery raw-injects.
873	    translation: Option<Arc<Translation>>,
874	    /// The adapter name owning this session (W3a, ADR-0025) — empty for a
875	    /// legacy/test/shell spawn. A live adapter update finds every live session
876	    /// whose `adapter` matches to cycle its resident children.
877	    adapter: String,
878	    /// The adapter's install dir for this session (W3a) — `None` if unknown. The
879	    /// apply target for a live update's CRC swap.

            // operator sees it), but the durable resume cursor must NOT jump past the
            // gap — see [`contiguous_advance`].
            contiguous_advance(&delivered_through, seq);
        }
    }
}

/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present
/// ONLY when the spawn carried a `translation_binary` path; absent → the session
/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.
///
/// Holds the shared inject floor (the controller-input buffer, also touched by
/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
/// (events serialize — never two uncommitted sequences interleaved), the shared
/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
/// while the worker drives an event), and the FAULT flag the worker raises when a
/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session reverts to raw inject and stops pinging.
struct Translation {
    /// Controller-input buffer + inject-floor flag, shared with `dispatch_input`.
    floor: Arc<Mutex<InjectFloor>>,
    /// Queue one inbound `<EVENT>` envelope to the inject worker. Events QUEUE here
    /// and are driven one-at-a-time so two sequences never interleave.
    event_tx: Sender<String>,
    /// The live child — shared so `dispatch_input` fires content-free pings while
    /// the worker (which also holds an `Arc`) drives events / faults the binary.
    child: Arc<TranslationChild>,
    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
    /// session reverts to raw inject and `dispatch_input` stops pinging.
    faulted: Arc<AtomicBool>,
}

/// A child the broker hosts under a PTY — the §B-owned resources for one
/// session, plus its gapless-resume output log.
struct HostedSession {
    /// The PTY + child (PTY master/writer + spawned child process).
    session: Arc<PtySession>,
    /// The session's dedicated PTY input-writer (v0.13.0 P0,
    /// REQ-HAZARD-PTY-INPUT-WRITER-WEDGE): the SOLE caller of the blocking
    /// `write_input`. Every dispatch-side write ENQUEUES here and returns at once,
    /// so a paste burst that fills the harness's input buffer parks only the
    /// writer thread, never the broker dispatch thread.
    input: Arc<InputWriter>,
    /// The output drain pump (kept so teardown can stop it).
    #[allow(dead_code)] // held for ownership/teardown; not read directly
    drain: Drain,
    /// The sequenced, buffered output + current subscriber.
    log: Arc<Mutex<OutputLog>>,
    /// The owning endpoint (the spawn's label; empty = unlabeled). The
    /// inbound dispatcher's gate subject for remote-drive (D9-1).
    endpoint: String,
    /// The translation-binary attachment (Layer C), or `None` for a session with
    /// no `[message-idle-translation-binary]` — then inbound delivery raw-injects.
    translation: Option<Arc<Translation>>,
    /// The adapter name owning this session (W3a, ADR-0025) — empty for a
    /// legacy/test/shell spawn. A live adapter update finds every live session
    /// whose `adapter` matches to cycle its resident children.
    adapter: String,
    /// The adapter's install dir for this session (W3a) — `None` if unknown. The
    /// apply target for a live update's CRC swap.


Now reword sites 154, 829, 837, 848, 872:


Edit

/// worker FLUSHES the buffer, RELEASES the floor, FAULTS the binary (terminate, no
/// respawn → the session degrades to raw inject), and never re-opens. A wedged or

/// worker FLUSHES the buffer, RELEASES the floor, FAULTS the binary (terminate, no
/// respawn → idle inbound then SPOOLS, never raw-injected), and never re-opens. A wedged or


/// worker FLUSHES the buffer, RELEASES the floor, FAULTS the binary (terminate, no
/// respawn → the session degrades to raw inject), and never re-opens. A wedged or

/// worker FLUSHES the buffer, RELEASES the floor, FAULTS the binary (terminate, no
/// respawn → idle inbound then SPOOLS, never raw-injected), and never re-opens. A wedged or

/// floor — buffering operator input — only while a binary's response to one

/// `{type:event}` is in flight. If no `{commit}` arrives within this window the

-/// respawn → the session degrades to raw inject), and never re-opens. A wedged or

+/// respawn → idle inbound then SPOOLS, never raw-injected), and never re-opens. A wedged or

/// buggy translation binary can therefore stall operator input for at most this

/// long. Mirrors [`CONTROLLER_WRITE_DEADLINE`]'s order (5 s).

const INJECT_COMMIT_DEADLINE: Duration = Duration::from_secs(5);


/// ONLY when the spawn carried a `translation_binary` path; absent → the session
/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.

/// ONLY when the spawn carried a `translation_binary` path; absent → the session
/// has no idle PTY delivery and inbound SPOOLS (ADR-0022 amendment — raw inject
/// was removed as a delivery path; it did not submit on a modern TUI).


/// ONLY when the spawn carried a `translation_binary` path; absent → the session
/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.

/// ONLY when the spawn carried a `translation_binary` path; absent → the session
/// has no idle PTY delivery and inbound SPOOLS (ADR-0022 amendment — raw inject
/// was removed as a delivery path; it did not submit on a modern TUI).

/// The per-session translation-binary attachment (W2 Layer C, ADR-0022). Present

-/// uses the degenerate raw-inject path (Layer E fallback) exactly as before.

+/// has no idle PTY delivery and inbound SPOOLS (ADR-0022 amendment — raw inject

+/// was removed as a delivery path; it did not submit on a modern TUI).

/// Holds the shared inject floor (the controller-input buffer, also touched by

/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker


    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
    /// session reverts to raw inject and `dispatch_input` stops pinging.
    faulted: Arc<AtomicBool>,

    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
    /// session's idle inbound then SPOOLS (never raw-injected) and `dispatch_input`
    /// stops pinging.
    faulted: Arc<AtomicBool>,


    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
    /// session reverts to raw inject and `dispatch_input` stops pinging.
    faulted: Arc<AtomicBool>,

    /// Raised by the worker on FAULT (commit-deadline miss / binary death): the
    /// session's idle inbound then SPOOLS (never raw-injected) and `dispatch_input`
    /// stops pinging.
    faulted: Arc<AtomicBool>,

/// the worker (which also holds an `Arc`) drives events / faults the binary.

-    /// session reverts to raw inject and `dispatch_input` stops pinging.

+    /// session's idle inbound then SPOOLS (never raw-injected) and `dispatch_input`


    /// The translation-binary attachment (Layer C), or `None` for a session with
    /// no `[message-idle-translation-binary]` — then inbound delivery raw-injects.
    translation: Option<Arc<Translation>>,

    /// The translation-binary attachment (Layer C), or `None` for a session with
    /// no `[message-idle-translation-binary]` — then idle inbound delivery SPOOLS
    /// (no raw PTY inject; ADR-0022 amendment).
    translation: Option<Arc<Translation>>,


    /// The translation-binary attachment (Layer C), or `None` for a session with
    /// no `[message-idle-translation-binary]` — then inbound delivery raw-injects.
    translation: Option<Arc<Translation>>,

    /// The translation-binary attachment (Layer C), or `None` for a session with
    /// no `[message-idle-translation-binary]` — then idle inbound delivery SPOOLS
    /// (no raw PTY inject; ADR-0022 amendment).
    translation: Option<Arc<Translation>>,

/// inbound dispatcher's gate subject for remote-drive (D9-1).

-    /// no `[message-idle-translation-binary]` — then inbound delivery raw-injects.

+    /// no `[message-idle-translation-binary]` — then idle inbound delivery SPOOLS

+    /// (no raw PTY inject; ADR-0022 amendment).

/// The adapter name owning this session (W3a, ADR-0025) — empty for a

/// legacy/test/shell spawn. A live adapter update finds every live session


Now sites 1027, 1100, 1682. Read 1024-1028, 1095-1101, 1678-1686:


1025	/// then drain `{key}`/`{text}`/`{delay_ms}` into `session.write_input` until
1026	/// `{commit}` — bounded by [`INJECT_COMMIT_DEADLINE`]. On commit: flush the
1027	/// buffered controller bytes AFTER the injected sequence and release the floor. On
1028	/// deadline-miss or binary death: flush+release anyway (operator input is never
1029	/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the

/// then drain `{key}`/`{text}`/`{delay_ms}` into `session.write_input` until
/// `{commit}` — bounded by [`INJECT_COMMIT_DEADLINE`]. On commit: flush the
/// buffered controller bytes AFTER the injected sequence and release the floor. On
/// deadline-miss or binary death: flush+release anyway (operator input is never
/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the


1093	/// reuse the stale cached `SpawnReq.translation_binary`). (W3d)
1094	// [impl->REQ-ADAPTER-LIVE-UPDATE]
1095	fn read_translation_path(install_dir: &std::path::Path) -> Option<String> {
1096	    let s = std::fs::read_to_string(install_dir.join("manifest.toml")).ok()?;
1097	    let manifest = spt_runtime::Manifest::from_toml_str(&s).ok()?;
1098	    manifest
1099	        .message_idle_translation_binary
1100	        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
1101	        // install dir (same as the initial spawn in harnesshost) so the W3d
1102	        // live-update RESPAWN also runs the real binary — not a verbatim path that

/// reuse the stale cached `SpawnReq.translation_binary`). (W3d)
// [impl->REQ-ADAPTER-LIVE-UPDATE]
fn read_translation_path(install_dir: &std::path::Path) -> Option<String> {
    let s = std::fs::read_to_string(install_dir.join("manifest.toml")).ok()?;
    let manifest = spt_runtime::Manifest::from_toml_str(&s).ok()?;
    manifest
        .message_idle_translation_binary
        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
        // install dir (same as the initial spawn in harnesshost) so the W3d
        // live-update RESPAWN also runs the real binary — not a verbatim path that


1676	                    }
1677	                }
1678	            }
1679	            // Reap the dead session from the table.
1680	            sessions_exit.lock().unwrap().remove(&id);
1681	        });
1682
1683	        // Layer C: if the spawn carried a translation binary, bring it up with the
1684	        // session — spawn the pure stdin→stdout filter + the inject worker that
1685	        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to raw inject
1686	        // (logged, `None`) rather than aborting the session — a missing binary must
1687	        // never cost the operator their agent.
1688	        let translation = req
1689	            .translation_binary
1690	            .as_deref()
1691	            .and_then(|path| build_translation(path, &req.endpoint, &input));

                    }
                }
            }
            // Reap the dead session from the table.
            sessions_exit.lock().unwrap().remove(&id);
        });

        // Layer C: if the spawn carried a translation binary, bring it up with the
        // session — spawn the pure stdin→stdout filter + the inject worker that
        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to raw inject
        // (logged, `None`) rather than aborting the session — a missing binary must
        // never cost the operator their agent.
        let translation = req
            .translation_binary
            .as_deref()
            .and_then(|path| build_translation(path, &req.endpoint, &input));


1026	/// `{commit}` — bounded by [`INJECT_COMMIT_DEADLINE`]. On commit: flush the
1027	/// buffered controller bytes AFTER the injected sequence and release the floor. On
1028	/// deadline-miss or binary death: flush+release anyway (operator input is never
1029	/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the

/// `{commit}` — bounded by [`INJECT_COMMIT_DEADLINE`]. On commit: flush the
/// buffered controller bytes AFTER the injected sequence and release the floor. On
/// deadline-miss or binary death: flush+release anyway (operator input is never
/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the


1029	/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the
1030	/// session degrades to raw inject). The loop ends when `event_tx` is dropped
1031	/// (session teardown), reaping the child.

/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the
/// session degrades to raw inject). The loop ends when `event_tx` is dropped
/// (session teardown), reaping the child.


/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the
/// session degrades to raw inject). The loop ends when `event_tx` is dropped
/// (session teardown), reaping the child.

/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (idle
/// inbound then SPOOLS, never raw-injected). The loop ends when `event_tx` is dropped
/// (session teardown), reaping the child.


/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the
/// session degrades to raw inject). The loop ends when `event_tx` is dropped
/// (session teardown), reaping the child.

/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (idle
/// inbound then SPOOLS, never raw-injected). The loop ends when `event_tx` is dropped
/// (session teardown), reaping the child.

/// `{commit}` — bounded by [`INJECT_COMMIT_DEADLINE`]. On commit: flush the

/// buffered controller bytes AFTER the injected sequence and release the floor. On

/// deadline-miss or binary death: flush+release anyway (operator input is never

-/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (the

-/// session degrades to raw inject). The loop ends when `event_tx` is dropped

+/// stranded), then FAULT — terminate the binary, set `faulted`, and exit (idle

+/// inbound then SPOOLS, never raw-injected). The loop ends when `event_tx` is dropped

/// The atomic open→drain→commit→flush is the W1 coexistence mechanism: an injected


        // Layer C: if the spawn carried a translation binary, bring it up with the
        // session — spawn the pure stdin→stdout filter + the inject worker that
        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to raw inject
        // (logged, `None`) rather than aborting the session — a missing binary must
        // never cost the operator their agent.

        // Layer C: if the spawn carried a translation binary, bring it up with the
        // session — spawn the pure stdin→stdout filter + the inject worker that
        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to SPOOLING
        // (logged, `None` → idle inbound spools, never raw-injected) rather than
        // aborting the session — a missing binary costs idle delivery, never the
        // operator their agent.


        // Layer C: if the spawn carried a translation binary, bring it up with the
        // session — spawn the pure stdin→stdout filter + the inject worker that
        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to raw inject
        // (logged, `None`) rather than aborting the session — a missing binary must
        // never cost the operator their agent.

        // Layer C: if the spawn carried a translation binary, bring it up with the
        // session — spawn the pure stdin→stdout filter + the inject worker that
        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to SPOOLING
        // (logged, `None` → idle inbound spools, never raw-injected) rather than
        // aborting the session — a missing binary costs idle delivery, never the
        // operator their agent.

-        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to raw inject

-        // (logged, `None`) rather than aborting the session — a missing binary must

-        // never cost the operator their agent.

+        // owns the atomic PTY apply. A spawn failure FAILS CLOSED to SPOOLING

+        // (logged, `None` → idle inbound spools, never raw-injected) rather than

+        // aborting the session — a missing binary costs idle delivery, never the


Site 1100 — the F-019 comment (consequence of bad path is now spool, not raw inject):


1098	    manifest
1099	        .message_idle_translation_binary
1100	        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
1101	        // install dir (same as the initial spawn in harnesshost) so the W3d
1102	        // live-update RESPAWN also runs the real binary — not a verbatim path that
1103	        // Command::new fails to find, silently degrading to raw inject.
1104	        // [impl->REQ-INSTALL-11]
1105	        .map(|b| spt_runtime::runtime::resolve_program_in_dir(&b.path, install_dir))
1106	}

    manifest
        .message_idle_translation_binary
        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
        // install dir (same as the initial spawn in harnesshost) so the W3d
        // live-update RESPAWN also runs the real binary — not a verbatim path that
        // Command::new fails to find, silently degrading to raw inject.
        // [impl->REQ-INSTALL-11]
        .map(|b| spt_runtime::runtime::resolve_program_in_dir(&b.path, install_dir))
}


        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
        // install dir (same as the initial spawn in harnesshost) so the W3d
        // live-update RESPAWN also runs the real binary — not a verbatim path that
        // Command::new fails to find, silently degrading to raw inject.
        // [impl->REQ-INSTALL-11]

        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
        // install dir (same as the initial spawn in harnesshost) so the W3d
        // live-update RESPAWN also runs the real binary — not a verbatim path that
        // Command::new fails to find, spawn-failing the binary so idle inbound spools.
        // [impl->REQ-INSTALL-11]


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

        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
        // install dir (same as the initial spawn in harnesshost) so the W3d
        // live-update RESPAWN also runs the real binary — not a verbatim path that
        // Command::new fails to find, silently degrading to raw inject.
        // [impl->REQ-INSTALL-11]

        // F-019 / REQ-INSTALL-11: resolve the bare/relative shipped path against the
        // install dir (same as the initial spawn in harnesshost) so the W3d
        // live-update RESPAWN also runs the real binary — not a verbatim path that
        // Command::new fails to find, spawn-failing the binary so idle inbound spools.
        // [impl->REQ-INSTALL-11]

-        // Command::new fails to find, silently degrading to raw inject.

+        // Command::new fails to find, spawn-failing the binary so idle inbound spools.


broker.rs swept. Now translation.rs ~238 + any harnesshost framing:


Grep

raw inject|raw-inject|degenerate|degrade.*inject|falls back to the degenerate


crates\spt-daemon\src\broker.rs:154:/// respawn → idle inbound then SPOOLS, never raw-injected), and never re-opens. A wedged or
crates\spt-daemon\src\broker.rs:829:/// has no idle PTY delivery and inbound SPOOLS (ADR-0022 amendment — raw inject
crates\spt-daemon\src\broker.rs:838:/// the session reverts to raw inject and stops pinging.
crates\spt-daemon\src\broker.rs:849:    /// session's idle inbound then SPOOLS (never raw-injected) and `dispatch_input`
crates\spt-daemon\src\broker.rs:1030:/// inbound then SPOOLS, never raw-injected). The loop ends when `event_tx` is dropped
crates\spt-daemon\src\broker.rs:1182:/// reap the child. Never respawns — inbound spools (poll-fed), never raw-injected,
crates\spt-daemon\src\broker.rs:1686:        // (logged, `None` → idle inbound spools, never raw-injected) rather than
crates\spt-daemon\src\broker.rs:1925:                        // worker receiver dropped → SPOOL (below), never raw inject.
crates\spt-daemon\src\bin\xlate_choreo_fixture.rs:78:            // floor, and FAULT this binary (revert to raw inject).
crates\spt-daemon\src\bin\xlate_choreo_fixture.rs:84:            // post-fault raw inject (raw_fallback), both observed via the child's
crates\spt-daemon\src\consent.rs:75:/// D8b — the subnet-scoped form); consent is the degenerate (unscoped) call.
crates\spt-daemon\src\harnesshost.rs:297:    // of failing against PATH and silently degrading to raw inject. Falls back to
crates\spt-daemon\src\harnesshost.rs:360:    // declares none yields `translation_binary = None` (the degenerate raw-inject /
crates\spt-daemon\src\msg.rs:223:    /// it on session-down. `None` ⇒ no translation binary (the v0.11.0 degenerate
crates\spt-daemon\src\msg.rs:224:    /// raw-inject / spool path). Additive + serde-defaulted: an N-1 brain/broker
crates\spt-daemon\src\translation.rs:76:    /// the buffered controller input race-free. The degenerate v0.11.0 raw-inject
crates\spt-daemon\src\translation.rs:238:    /// error (the broker logs it and falls back to the degenerate raw inject).


More sites remain: broker.rs:838, harnesshost.rs:297/360, msg.rs:223, translation.rs:76/238, plus the fixture (xlate_choreo_fixture.rs:78/84). Read the struct doc tail + the others:


830	/// was removed as a delivery path; it did not submit on a modern TUI).
831	///
832	/// Holds the shared inject floor (the controller-input buffer, also touched by
833	/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
834	/// (events serialize — never two uncommitted sequences interleaved), the shared
835	/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
836	/// while the worker drives an event), and the FAULT flag the worker raises when a
837	/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
838	/// the session reverts to raw inject and stops pinging.

/// was removed as a delivery path; it did not submit on a modern TUI).
///
/// Holds the shared inject floor (the controller-input buffer, also touched by
/// `dispatch_input`), the queue that feeds inbound `<EVENT>`s to the inject worker
/// (events serialize — never two uncommitted sequences interleaved), the shared
/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings
/// while the worker drives an event), and the FAULT flag the worker raises when a
/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session reverts to raw inject and stops pinging.


292
293	    // [unit->REQ-INSTALL-11] F-019: the idle-delivery translation binary resolves
294	    // against the adapter install dir — a bare shipped path
295	    // ("cc-spt-idle-translate") becomes the absolute <install_dir>/<name>(.exe) when
296	    // that file exists, so the broker's Command::new spawns the REAL binary instead
297	    // of failing against PATH and silently degrading to raw inject. Falls back to
298	    // the bare path (PATH) when install_dir is None or the file is absent; None path
299	    // stays None. Mirrors runtime::resolve_program_in_dir's own tests.
300	    #[test]
301	    fn translation_binary_resolves_against_install_dir() {
302	        let dir = tempfile::tempdir().unwrap();
303	        let bare = "cc-spt-idle-translate";
304	        // The adapter ships the binary in its install dir under the bare name
305	        // (.exe on Windows, where resolve adds the suffix).
306	        #[cfg(windows)]
307	        let shipped = dir.path().join(format!("{bare}.exe"));
308	        #[cfg(not(windows))]
309	        let shipped = dir.path().join(bare);
310	        std::fs::write(&shipped, b"stub").unwrap();
311	        let dir_str = dir.path().to_string_lossy().into_owned();
312
313	        // Shipped + install_dir → absolute resolved path to the real binary.
314	        assert_eq!(
315	            resolve_translation_binary(Some(bare.to_string()), Some(&dir_str)).as_deref(),
316	            Some(shipped.to_string_lossy().as_ref()),
317	            "a bare shipped path resolves to the absolute install-dir binary"
318	        );
319	        // No install_dir → bare path passes through (PATH fallback).
320	        assert_eq!(
321	            resolve_translation_binary(Some(bare.to_string()), None).as_deref(),
322	            Some(bare),
323	            "no install_dir → bare PATH fallback"
324	        );
325	        // install_dir present but file absent → bare fallback (PATH).
326	        let empty = tempfile::tempdir().unwrap();
327	        let empty_str = empty.path().to_string_lossy().into_owned();
328	        assert_eq!(
329	            resolve_translation_binary(Some(bare.to_string()), Some(&empty_str)).as_deref(),
330	            Some(bare),
331	            "file absent in install_dir → bare PATH fallback"
332	        );
333	        // No declared binary → nothing to resolve.
334	        assert_eq!(resolve_translation_binary(None, Some(&dir_str)), None);
335	    }
336
337	    // [unit->REQ-HAZARD-ENV-SUBST] a manifest [env.<VAR>] direction=inject value
338	    // is {key}-substituted from the same catalog and returned for the spawn — so a
339	    // flagless harness gets SPT_ENDPOINT_ID populated (not empty → seed-by-PPID).
340	    #[test]
341	    fn prepare_fills_env_inject_values_with_substitution() {
342	        let m = spt_runtime::Manifest::from_toml_str(
343	            "[adapter]\nname = \"mock\"\nkind = \"harness\"\nversion = \"1\"\n\
344	             min_spt_core_version = \"0\"\n\n[session.self]\ncommand = 'claude'\n\n\
345	             [env.SPT_ENDPOINT_ID]\ndirection = \"inject\"\nvalue = \"{id}\"\n\n\
346	             [env.SPT_SESSION]\ndirection = \"inject\"\nvalue = \"sess-{session_id}\"\n\n\
347	             [env.SPT_READ_ONLY]\ndirection = \"read\"\n",
348	        )
349	        .unwrap();
350	        let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m, false).unwrap();
351	        assert_eq!(prepared.env.get("SPT_ENDPOINT_ID").map(String::as_str), Some("wall-b"));
352	        assert_eq!(prepared.env.get("SPT_SESSION").map(String::as_str), Some("sess-abc"));
353	        // A `read` directive injects nothing.
354	        assert!(!prepared.env.contains_key("SPT_READ_ONLY"));
355	    }
356
357	    // [unit->REQ-MSG-IDLE-TRANSLATION-BINARY] a declared
358	    // `[message-idle-translation-binary].path` is threaded into the prepared spawn
359	    // (so the broker can lifecycle-manage it, ADR-0022 / W2); an adapter that
360	    // declares none yields `translation_binary = None` (the degenerate raw-inject /
361	    // spool path).
362	    #[test]
363	    fn prepare_threads_translation_binary_path_when_declared() {

    // [unit->REQ-INSTALL-11] F-019: the idle-delivery translation binary resolves
    // against the adapter install dir — a bare shipped path
    // ("cc-spt-idle-translate") becomes the absolute <install_dir>/<name>(.exe) when
    // that file exists, so the broker's Command::new spawns the REAL binary instead
    // of failing against PATH and silently degrading to raw inject. Falls back to
    // the bare path (PATH) when install_dir is None or the file is absent; None path
    // stays None. Mirrors runtime::resolve_program_in_dir's own tests.
    #[test]
    fn translation_binary_resolves_against_install_dir() {
        let dir = tempfile::tempdir().unwrap();
        let bare = "cc-spt-idle-translate";
        // The adapter ships the binary in its install dir under the bare name
        // (.exe on Windows, where resolve adds the suffix).
        #[cfg(windows)]
        let shipped = dir.path().join(format!("{bare}.exe"));
        #[cfg(not(windows))]
        let shipped = dir.path().join(bare);
        std::fs::write(&shipped, b"stub").unwrap();
        let dir_str = dir.path().to_string_lossy().into_owned();

        // Shipped + install_dir → absolute resolved path to the real binary.
        assert_eq!(
            resolve_translation_binary(Some(bare.to_string()), Some(&dir_str)).as_deref(),
            Some(shipped.to_string_lossy().as_ref()),
            "a bare shipped path resolves to the absolute install-dir binary"
        );
        // No install_dir → bare path passes through (PATH fallback).
        assert_eq!(
            resolve_translation_binary(Some(bare.to_string()), None).as_deref(),
            Some(bare),
            "no install_dir → bare PATH fallback"
        );
        // install_dir present but file absent → bare fallback (PATH).
        let empty = tempfile::tempdir().unwrap();
        let empty_str = empty.path().to_string_lossy().into_owned();
        assert_eq!(
            resolve_translation_binary(Some(bare.to_string()), Some(&empty_str)).as_deref(),
            Some(bare),
            "file absent in install_dir → bare PATH fallback"
        );
        // No declared binary → nothing to resolve.
        assert_eq!(resolve_translation_binary(None, Some(&dir_str)), None);
    }

    // [unit->REQ-HAZARD-ENV-SUBST] a manifest [env.<VAR>] direction=inject value
    // is {key}-substituted from the same catalog and returned for the spawn — so a
    // flagless harness gets SPT_ENDPOINT_ID populated (not empty → seed-by-PPID).
    #[test]
    fn prepare_fills_env_inject_values_with_substitution() {
        let m = spt_runtime::Manifest::from_toml_str(
            "[adapter]\nname = \"mock\"\nkind = \"harness\"\nversion = \"1\"\n\
             min_spt_core_version = \"0\"\n\n[session.self]\ncommand = 'claude'\n\n\
             [env.SPT_ENDPOINT_ID]\ndirection = \"inject\"\nvalue = \"{id}\"\n\n\
             [env.SPT_SESSION]\ndirection = \"inject\"\nvalue = \"sess-{session_id}\"\n\n\
             [env.SPT_READ_ONLY]\ndirection = \"read\"\n",
        )
        .unwrap();
        let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m, false).unwrap();
        assert_eq!(prepared.env.get("SPT_ENDPOINT_ID").map(String::as_str), Some("wall-b"));
        assert_eq!(prepared.env.get("SPT_SESSION").map(String::as_str), Some("sess-abc"));
        // A `read` directive injects nothing.
        assert!(!prepared.env.contains_key("SPT_READ_ONLY"));
    }

    // [unit->REQ-MSG-IDLE-TRANSLATION-BINARY] a declared
    // `[message-idle-translation-binary].path` is threaded into the prepared spawn
    // (so the broker can lifecycle-manage it, ADR-0022 / W2); an adapter that
    // declares none yields `translation_binary = None` (the degenerate raw-inject /
    // spool path).
    #[test]
    fn prepare_threads_translation_binary_path_when_declared() {


Edit

/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session reverts to raw inject and stops pinging.

/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session's idle inbound SPOOLS (never raw-injected) and pinging stops.


/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session reverts to raw inject and stops pinging.

/// sequence misses its [`INJECT_COMMIT_DEADLINE`] or the binary dies — after which
/// the session's idle inbound SPOOLS (never raw-injected) and pinging stops.

/// child handle (so `dispatch_input` can fire content-free `{type:input}` pings

/// while the worker drives an event), and the FAULT flag the worker raises when a

-/// the session reverts to raw inject and stops pinging.

+/// the session's idle inbound SPOOLS (never raw-injected) and pinging stops.

/// Controller-input buffer + inject-floor flag, shared with `dispatch_input`.


// of failing against PATH and silently degrading to raw inject. Falls back to

// of failing against PATH and silently spawn-failing (idle inbound spools). Falls back to


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

// against the adapter install dir — a bare shipped path

// ("cc-spt-idle-translate") becomes the absolute <install_dir>/<name>(.exe) when

// that file exists, so the broker's Command::new spawns the REAL binary instead

-    // of failing against PATH and silently degrading to raw inject. Falls back to

+    // of failing against PATH and silently spawn-failing (idle inbound spools). Falls back to

// the bare path (PATH) when install_dir is None or the file is absent; None path

// stays None. Mirrors runtime::resolve_program_in_dir's own tests.


    // declares none yields `translation_binary = None` (the degenerate raw-inject /
    // spool path).

    // declares none yields `translation_binary = None` (the idle-inbound spool path —
    // no raw PTY inject; ADR-0022 amendment).


    // declares none yields `translation_binary = None` (the degenerate raw-inject /
    // spool path).

    // declares none yields `translation_binary = None` (the idle-inbound spool path —
    // no raw PTY inject; ADR-0022 amendment).

// [unit->REQ-MSG-IDLE-TRANSLATION-BINARY] a declared

// `[message-idle-translation-binary].path` is threaded into the prepared spawn

// (so the broker can lifecycle-manage it, ADR-0022 / W2); an adapter that

-    // declares none yields `translation_binary = None` (the degenerate raw-inject /

+    // declares none yields `translation_binary = None` (the idle-inbound spool path —

+    // no raw PTY inject; ADR-0022 amendment).


219	    pub env: std::collections::BTreeMap<String, String>,
220	    /// Path to the adapter's idle-delivery translation binary, if it declares one
221	    /// (`[message-idle-translation-binary].path`, ADR-0022 / W2). The broker
222	    /// LIFECYCLE-manages it: spawns it alongside this spt-hosted session and reaps
223	    /// it on session-down. `None` ⇒ no translation binary (the v0.11.0 degenerate
224	    /// raw-inject / spool path). Additive + serde-defaulted: an N-1 brain/broker
225	    /// omits it and the session simply runs without a translation binary.
226	    #[serde(default, skip_serializing_if = "Option::is_none")]
227	    pub translation_binary: Option<String>,
228	    /// The adapter name owning this session (W3a, ADR-0025). The broker records

    pub env: std::collections::BTreeMap<String, String>,
    /// Path to the adapter's idle-delivery translation binary, if it declares one
    /// (`[message-idle-translation-binary].path`, ADR-0022 / W2). The broker
    /// LIFECYCLE-manages it: spawns it alongside this spt-hosted session and reaps
    /// it on session-down. `None` ⇒ no translation binary (the v0.11.0 degenerate
    /// raw-inject / spool path). Additive + serde-defaulted: an N-1 brain/broker
    /// omits it and the session simply runs without a translation binary.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub translation_binary: Option<String>,
    /// The adapter name owning this session (W3a, ADR-0025). The broker records


72	    Delay { delay_ms: u64 },
73	    /// Sequence terminator (doyle OPT-1 ruling, pending confirm): the binary
74	    /// emits `{"commit":true}` after the last keystroke of its response to an
75	    /// event, so the broker knows the atomic sequence is complete and can flush
76	    /// the buffered controller input race-free. The degenerate v0.11.0 raw-inject
77	    /// is `{text:payload}{key:"enter"}{commit:true}`.
78	    Commit { commit: bool },
79	}

    Delay { delay_ms: u64 },
    /// Sequence terminator (doyle OPT-1 ruling, pending confirm): the binary
    /// emits `{"commit":true}` after the last keystroke of its response to an
    /// event, so the broker knows the atomic sequence is complete and can flush
    /// the buffered controller input race-free. The degenerate v0.11.0 raw-inject
    /// is `{text:payload}{key:"enter"}{commit:true}`.
    Commit { commit: bool },
}


232	}
233
234	impl TranslationChild {
235	    /// Spawn `path` as a stdin→stdout filter, forwarding each parsed [`KeyCmd`] on
236	    /// `cmds`. stderr is inherited so an adapter author can see the binary's own
237	    /// diagnostics. Fails closed: a missing/unspawnable binary returns the OS
238	    /// error (the broker logs it and falls back to the degenerate raw inject).
239	    // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]
240	    pub fn spawn(path: &str, cmds: Sender<KeyCmd>) -> io::Result<Self> {
241	        let mut child = Command::new(path)

}

impl TranslationChild {
    /// Spawn `path` as a stdin→stdout filter, forwarding each parsed [`KeyCmd`] on
    /// `cmds`. stderr is inherited so an adapter author can see the binary's own
    /// diagnostics. Fails closed: a missing/unspawnable binary returns the OS
    /// error (the broker logs it and falls back to the degenerate raw inject).
    // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]
    pub fn spawn(path: &str, cmds: Sender<KeyCmd>) -> io::Result<Self> {
        let mut child = Command::new(path)


    /// it on session-down. `None` ⇒ no translation binary (the v0.11.0 degenerate
    /// raw-inject / spool path). Additive + serde-defaulted: an N-1 brain/broker

    /// it on session-down. `None` ⇒ no translation binary (idle inbound spools; the
    /// v0.11.0 raw-inject was removed as a delivery path, ADR-0022 amendment).
    /// Additive + serde-defaulted: an N-1 brain/broker


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

    /// it on session-down. `None` ⇒ no translation binary (the v0.11.0 degenerate
    /// raw-inject / spool path). Additive + serde-defaulted: an N-1 brain/broker

    /// it on session-down. `None` ⇒ no translation binary (idle inbound spools; the
    /// v0.11.0 raw-inject was removed as a delivery path, ADR-0022 amendment).
    /// Additive + serde-defaulted: an N-1 brain/broker

/// Path to the adapter's idle-delivery translation binary, if it declares one

/// (`[message-idle-translation-binary].path`, ADR-0022 / W2). The broker

/// LIFECYCLE-manages it: spawns it alongside this spt-hosted session and reaps

-    /// it on session-down. `None` ⇒ no translation binary (the v0.11.0 degenerate

-    /// raw-inject / spool path). Additive + serde-defaulted: an N-1 brain/broker

+    /// it on session-down. `None` ⇒ no translation binary (idle inbound spools; the

+    /// v0.11.0 raw-inject was removed as a delivery path, ADR-0022 amendment).

+    /// Additive + serde-defaulted: an N-1 brain/broker

/// omits it and the session simply runs without a translation binary.

#[serde(default, skip_serializing_if = "Option::is_none")]


    /// the buffered controller input race-free. The degenerate v0.11.0 raw-inject
    /// is `{text:payload}{key:"enter"}{commit:true}`.

    /// the buffered controller input race-free. (The v0.11.0 raw-inject —
    /// `{text:payload}{key:"enter"}` — was removed as a delivery path: it did not
    /// submit on a modern TUI; idle inbound now spools when no binary delivers.)


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

    /// the buffered controller input race-free. The degenerate v0.11.0 raw-inject
    /// is `{text:payload}{key:"enter"}{commit:true}`.

    /// the buffered controller input race-free. (The v0.11.0 raw-inject —
    /// `{text:payload}{key:"enter"}` — was removed as a delivery path: it did not
    /// submit on a modern TUI; idle inbound now spools when no binary delivers.)

/// Sequence terminator (doyle OPT-1 ruling, pending confirm): the binary

/// emits `{"commit":true}` after the last keystroke of its response to an

/// event, so the broker knows the atomic sequence is complete and can flush

-    /// the buffered controller input race-free. The degenerate v0.11.0 raw-inject

-    /// is `{text:payload}{key:"enter"}{commit:true}`.

+    /// the buffered controller input race-free. (The v0.11.0 raw-inject —

+    /// `{text:payload}{key:"enter"}` — was removed as a delivery path: it did not

+    /// submit on a modern TUI; idle inbound now spools when no binary delivers.)


    /// error (the broker logs it and falls back to the degenerate raw inject).
    // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]

    /// error (the broker logs it and `None` → idle inbound spools, never raw-injected).
    // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]


    /// error (the broker logs it and falls back to the degenerate raw inject).
    // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]

    /// error (the broker logs it and `None` → idle inbound spools, never raw-injected).
    // [impl->REQ-MSG-IDLE-TRANSLATION-BINARY]

/// Spawn `path` as a stdin→stdout filter, forwarding each parsed [`KeyCmd`] on

/// `cmds`. stderr is inherited so an adapter author can see the binary's own

/// diagnostics. Fails closed: a missing/unspawnable binary returns the OS

-    /// error (the broker logs it and falls back to the degenerate raw inject).

+    /// error (the broker logs it and `None` → idle inbound spools, never raw-injected).

pub fn spawn(path: &str, cmds: Sender<KeyCmd>) -> io::Result<Self> {


Prod comment sweep done. Now the test-impact analysis ([[behavior-change-grep-tests-not-comments]]) — find assertions on the OLD raw-inject behavior, and CHANGE 5 (cli.rs try_broker_inject honest report):


Found 19 files
crates\spt-daemon\src\msg.rs
crates\spt-daemon\src\broker.rs
docs\adr\0022-spt-hosted-idle-delivery-translation-binary.md
docs\KNOWN-HAZARDS.md
F-019-TRANSLATION-BINARY-INSTALL-DIR-RESOLVE-DESIGN.md
CONTEXT.md
crates\spt\src\cli.rs
traceable-reqs.toml
docs-site\src\harness-contract\manifest.md
crates\spt-daemon\tests\inject_control_wedge.rs
crates\spt-daemon\tests\broker.rs
crates\spt-daemon\src\brain.rs
docs\DEFERRED.md
crates\spt-daemon\src\bin\xlate_choreo_fixture.rs
V0.13.0-VIEWER-DRAIN-DECOUPLE-JIT.md
V0.13.0-SKIP-TO-LIVE-JIT.md
V0.13.0-P1C-CONTROLLER-WRITER-REORDER-PLAN.md
V0.13.0-DELIVERY-CONTROL-JIT.md
crates\spt-daemon\tests\wanmsg.rs


Look at CHANGE 5 (cli.rs try_broker_inject + send report) and the int-gate assertions:


8://! Exit codes: `0` success (delivered, queued, replied, listed) · `1` runtime
95:    /// Drains the spooled backlog first; each received message prints to
332:    /// Soft-stop a perch (spool preserved).
334:    /// Removes the ready marker and unregisters the perch; the spool is
769:    /// manifest's `[shell.capabilities]`, spooled on the shell perch, and
783:    /// daemon, and drained by the shell's `api drive-poll --link`. NEVER spooled
984:    /// Produced into the replicated notification spool and first-fired at
2626:/// replicated spool (id minted from this node's EpochSource), then
3507:/// write error) returns `false` so the caller falls back to the spool — the
3510:fn try_broker_inject(target: &str, from: &str, body: &str) -> bool {
3522:fn cmd_send(
3551:    // typed-envelope rails (spool/TCP/WAN) and renders `<EVENT type="user-msg">`
3557:            "USER_MSG_RESTAMP:{target}: sender is not a user-backed origin — delivered as plain msg"
3563:    // (broker holds its PTY, no `api listen` relay) would otherwise spool
3566:    // normal deliver path (spool / WAN), so the message is never lost. Skipped
3573:        && try_broker_inject(&target, &from, body)
3611:            // and ship to the instance's node; its daemon delivers/spools.
3744:        for (from, body) in agent.drain_spool() {
3759:        for (from, body) in agent.drain_spool() {
3821:    // unregister the address; spool.db and info.json are preserved.
5739:- The spooled backlog drains first; then each delivery prints to stdout as it
5757:Going offline cleanly: `spt endpoint stop <id>` (the spool is preserved;
5769:- `SENT:<target>`   — delivered live to a listening agent.
5770:- `QUEUED:<target>` — the target is not listening; the message spooled and
7103:    // Fill the SAME substitution keys the daemon fills into a delivered envelope
7278:    /// Drain spooled frames per the manifest's `command_receipt`: **stdin**
7620:            // with the link key, spool on the shell perch, then drain per the
7621:            // manifest's command_receipt. The spool is the durability — a
7651:                // NOT spool — route the approval prompt (the same plumbing the
7669:            // latest-wins in-memory slot — NEVER spooled, NEVER woken. An offline
7680:            // → drop identical to local; never spooled on either leg). Mirrors
7703:                    eprintln!("DRIVE_DROPPED:{id} type={drive_type} (shell offline — drive is never spooled)");
7819:                    spt_daemon::shellchan::spool_shell_frame(&owlery, &owner, &id, &stamped)
7870:                    spt_daemon::shellchan::spool_shell_frame(&owlery, &owner, &id, &stamped)
10929:    // relay receipt): `shell cmd` vocabulary-checks then spools a MAC-stamped
10931:    // refused); `shell send` spools text + progress-queryable file frames;
10932:    // `api emit` refuses an undeclared sensory type, drops without spooling
10981:        // Vocabulary bounds: unknown op + extra args refuse, nothing spooled.
10984:        assert_eq!(spt_store::spool::pending_count_at(&shell_perch).unwrap(), 0);
10986:        // A vocabulary-checked command spools one stamped frame (alias

[Showing results with pagination = limit: 40]


Read the try_broker_inject + cmd_send broker path (3505-3615):


3500	/// Deliver `body` to an spt-hosted endpoint by injecting the rendered `<EVENT>`
3501	/// envelope into its broker-held PTY (REQ-SEND-SPT-HOSTED, fix A). The bytes are
3502	/// the SAME whole `<EVENT>` every arriving-message surface emits (ADR-0020, via
3503	/// [`render_event_whole`] — one whole envelope, never EVENT-PART-chunked: the PTY
3504	/// has no Monitor per-line cap), so the harness parses one format regardless of
3505	/// topology. Returns `true` only on a CONFIRMED broker write (so the caller
3506	/// reports `Sent`, un-lying "online"); any miss (daemon down, no hosted session,
3507	/// write error) returns `false` so the caller falls back to the spool — the
3508	/// message is never lost.
3509	// [impl->REQ-SEND-SPT-HOSTED]
3510	fn try_broker_inject(target: &str, from: &str, body: &str) -> bool {
3511	    if !spt_daemon::is_running() {
3512	        return false;
3513	    }
3514	    let mut bytes = spt_msg::emit::render_event_whole(from, body);
3515	    bytes.push('\n');
3516	    match spt_daemon::brain::Brain::cold_start(&spt_daemon::broker_socket_name(), now_ms()) {
3517	        Ok(mut brain) => brain.inject_endpoint(target, bytes.as_bytes()).unwrap_or(false),
3518	        Err(_) => false,
3519	    }
3520	}
3521
3522	fn cmd_send(
3523	    target: Option<String>,
3524	    from: Option<String>,
3525	    reply_to: Option<String>,
3526	    deferred: bool,
3527	    user_msg: bool,
3528	) -> i32 {
3529	    let is_reply = reply_to.is_some();
3530	    // --reply-to provides the target when no positional target is given.
3531	    let target = match target.or_else(|| reply_to.clone()) {
3532	        Some(t) => t,
3533	        None => {
3534	            eprintln!("USAGE: a target (or --reply-to <sender>) is required");
3535	            return 2;
3536	        }
3537	    };
3538	    let from = resolve_from(from);
3539	    // Trim stdin like cmd_notify (REQ-HAZARD-ENVELOPE-CR-LINESAFE parity): a
3540	    // Windows `echo | spt send` pipes a trailing CRLF — strip it so the message
3541	    // has no trailing blank line (the codec also neutralizes any interior CR).
3542	    let body = read_stdin();
3543	    let body = body.trim();
3544	    let owlery = perch::owlery_dir();
3545
3546	    // user-msg identity gate (REQ-MSG-5): the requested type is honored only
3547	    // from a user-backed LOCAL origin — keyed on the *session-proven* self
3548	    // identity ([`roster::detect_self_id`]), never the `--from` reply label.
3549	    // An agent-family sender is re-stamped to plain `msg`, loudly. The honored
3550	    // form is a pre-composed `user-msg` envelope that rides the verbatim
3551	    // typed-envelope rails (spool/TCP/WAN) and renders `<EVENT type="user-msg">`
3552	    // at the receiver; the empty-body refusal is preserved (no compose on empty).
3553	    let (body_to_send, restamped) =
3554	        apply_user_msg_gate(roster::detect_self_id().as_deref(), &from, body, user_msg);
3555	    if restamped {
3556	        eprintln!(
3557	            "USER_MSG_RESTAMP:{target}: sender is not a user-backed origin — delivered as plain msg"
3558	        );
3559	    }
3560	    let body = body_to_send.as_str();
3561
3562	    // spt-hosted PTY-inject path (REQ-SEND-SPT-HOSTED): an spt-hosted endpoint
3563	    // (broker holds its PTY, no `api listen` relay) would otherwise spool
3564	    // silently — inject the rendered <EVENT> straight into its PTY instead. Only
3565	    // a CONFIRMED broker write counts as Sent; any miss falls through to the
3566	    // normal deliver path (spool / WAN), so the message is never lost. Skipped
3567	    // for deferred (the no-interrupt hook channel) and empty bodies (let
3568	    // deliver::send return Empty).
3569	    let outcome = if deferred {
3570	        deliver::send_deferred(&target, &from, body)
3571	    } else if !body.is_empty()
3572	        && is_spt_hosted_no_relay(&target, &owlery)
3573	        && try_broker_inject(&target, &from, body)
3574	    {
3575	        SendOutcome::Sent
3576	    } else {
3577	        deliver::send(&target, &from, body, &owlery)
3578	    };
3579
3580	    match outcome {
3581	        SendOutcome::Sent => {
3582	            ok_line(format!(
3583	                "{}:{}",
3584	                if is_reply { "REPLIED" } else { "SENT" },
3585	                target
3586	            ));
3587	            0
3588	        }
3589	        SendOutcome::Queued => {
3590	            let label = if deferred {
3591	                "DEFERRED"
3592	            } else if is_reply {
3593	                "REPLIED(QUEUED)"
3594	            } else {
3595	                "QUEUED"
3596	            };
3597	            ok_line(format!("{}:{}", label, target));
3598	            0
3599	        }
3600	        SendOutcome::NoPerch => {
3601	            if deferred {
3602	                // The wire record carries no deferred class (v1) — a remote
3603	                // deferred send is a documented seam, not a silent reclass.
3604	                eprintln!(
3605	                    "NO_PERCH:{} is not listening (deferred stays local-only)",
3606	                    target
3607	                );
3608	                return 1;
3609	            }
3610	            // The WAN leg (D9-1-4): resolve against the registry snapshots
3611	            // and ship to the instance's node; its daemon delivers/spools.
3612	            // [impl->REQ-NET-1]
3613	            match crate::wansend::wan_send(&target, &from, body) {
3614	                crate::wansend::WanSendOutcome::Sent { node } => {
3615	                    ok_line(format!(
3616	                        "{}:{}@{}",
3617	                        if is_reply {
3618	                            "REPLIED(WAN)"
3619	                        } else {

/// Deliver `body` to an spt-hosted endpoint by injecting the rendered `<EVENT>`
/// envelope into its broker-held PTY (REQ-SEND-SPT-HOSTED, fix A). The bytes are
/// the SAME whole `<EVENT>` every arriving-message surface emits (ADR-0020, via
/// [`render_event_whole`] — one whole envelope, never EVENT-PART-chunked: the PTY
/// has no Monitor per-line cap), so the harness parses one format regardless of
/// topology. Returns `true` only on a CONFIRMED broker write (so the caller
/// reports `Sent`, un-lying "online"); any miss (daemon down, no hosted session,
/// write error) returns `false` so the caller falls back to the spool — the
/// message is never lost.
// [impl->REQ-SEND-SPT-HOSTED]
fn try_broker_inject(target: &str, from: &str, body: &str) -> bool {
    if !spt_daemon::is_running() {
        return false;
    }
    let mut bytes = spt_msg::emit::render_event_whole(from, body);
    bytes.push('\n');
    match spt_daemon::brain::Brain::cold_start(&spt_daemon::broker_socket_name(), now_ms()) {
        Ok(mut brain) => brain.inject_endpoint(target, bytes.as_bytes()).unwrap_or(false),
        Err(_) => false,
    }
}

fn cmd_send(
    target: Option<String>,
    from: Option<String>,
    reply_to: Option<String>,
    deferred: bool,
    user_msg: bool,
) -> i32 {
    let is_reply = reply_to.is_some();
    // --reply-to provides the target when no positional target is given.
    let target = match target.or_else(|| reply_to.clone()) {
        Some(t) => t,
        None => {
            eprintln!("USAGE: a target (or --reply-to <sender>) is required");
            return 2;
        }
    };
    let from = resolve_from(from);
    // Trim stdin like cmd_notify (REQ-HAZARD-ENVELOPE-CR-LINESAFE parity): a
    // Windows `echo | spt send` pipes a trailing CRLF — strip it so the message
    // has no trailing blank line (the codec also neutralizes any interior CR).
    let body = read_stdin();
    let body = body.trim();
    let owlery = perch::owlery_dir();

    // user-msg identity gate (REQ-MSG-5): the requested type is honored only
    // from a user-backed LOCAL origin — keyed on the *session-proven* self
    // identity ([`roster::detect_self_id`]), never the `--from` reply label.
    // An agent-family sender is re-stamped to plain `msg`, loudly. The honored
    // form is a pre-composed `user-msg` envelope that rides the verbatim
    // typed-envelope rails (spool/TCP/WAN) and renders `<EVENT type="user-msg">`
    // at the receiver; the empty-body refusal is preserved (no compose on empty).
    let (body_to_send, restamped) =
        apply_user_msg_gate(roster::detect_self_id().as_deref(), &from, body, user_msg);
    if restamped {
        eprintln!(
            "USER_MSG_RESTAMP:{target}: sender is not a user-backed origin — delivered as plain msg"
        );
    }
    let body = body_to_send.as_str();

    // spt-hosted PTY-inject path (REQ-SEND-SPT-HOSTED): an spt-hosted endpoint
    // (broker holds its PTY, no `api listen` relay) would otherwise spool
    // silently — inject the rendered <EVENT> straight into its PTY instead. Only
    // a CONFIRMED broker write counts as Sent; any miss falls through to the
    // normal deliver path (spool / WAN), so the message is never lost. Skipped
    // for deferred (the no-interrupt hook channel) and empty bodies (let
    // deliver::send return Empty).
    let outcome = if deferred {
        deliver::send_deferred(&target, &from, body)
    } else if !body.is_empty()
        && is_spt_hosted_no_relay(&target, &owlery)
        && try_broker_inject(&target, &from, body)
    {
        SendOutcome::Sent
    } else {
        deliver::send(&target, &from, body, &owlery)
    };

    match outcome {
        SendOutcome::Sent => {
            ok_line(format!(
                "{}:{}",
                if is_reply { "REPLIED" } else { "SENT" },
                target
            ));
            0
        }
        SendOutcome::Queued => {
            let label = if deferred {
                "DEFERRED"
            } else if is_reply {
                "REPLIED(QUEUED)"
            } else {
                "QUEUED"
            };
            ok_line(format!("{}:{}", label, target));
            0
        }
        SendOutcome::NoPerch => {
            if deferred {
                // The wire record carries no deferred class (v1) — a remote
                // deferred send is a documented seam, not a silent reclass.
                eprintln!(
                    "NO_PERCH:{} is not listening (deferred stays local-only)",
                    target
                );
                return 1;
            }
            // The WAN leg (D9-1-4): resolve against the registry snapshots
            // and ship to the instance's node; its daemon delivers/spools.
            // [impl->REQ-NET-1]
            match crate::wansend::wan_send(&target, &from, body) {
                crate::wansend::WanSendOutcome::Sent { node } => {
                    ok_line(format!(
                        "{}:{}@{}",
                        if is_reply {
                            "REPLIED(WAN)"
                        } else {


CHANGE 5 is already correct: `try_broker_inject` returns `inject_endpoint(...).unwrap_or(false)` → on delivered=FALSE, cmd_send falls through to `deliver::send` → spools → reports QUEUED (honest), never "SENT". Verify `inject_endpoint` passes the bool through:


crates\spt-daemon\src\brain.rs:1189:    pub fn inject_endpoint(&mut self, endpoint: &str, bytes: &[u8]) -> io::Result<bool> {
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1190-        self.send(
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1191-            KIND_ENDPOINT_INPUT,
crates\spt-daemon\src\brain.rs-1192-            serde_json::to_value(EndpointInputReq {
crates\spt-daemon\src\brain.rs-1193-                endpoint: endpoint.to_string(),
crates\spt-daemon\src\brain.rs-1194-                data_b64: encode_bytes(bytes),
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1195-            })
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1196-            .expect("EndpointInputReq serializes"),
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1197-        )?;
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1198-        loop {
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1199-            match self.read_event()? {
crates\spt-daemon\src\brain.rs-1200-                BrokerEvent::Other(env) if env.kind == KIND_ENDPOINT_INJECTED => {
crates\spt-daemon\src\brain.rs-1201-                    let reply: EndpointInjected = serde_json::from_value(env.payload)
crates\spt-daemon\src\brain.rs-1202-                        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1203-                    return Ok(reply.delivered);
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1204-                }
crates\spt-daemon\src\brain.rs-1205-                BrokerEvent::Error { message } => return Err(io::Error::other(message)),
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1206-                _ => continue,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1207-            }
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1208-        }
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1209-    }
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1210-
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\brain.rs-1211-    /// List the broker's hosted sessions (id + owning endpoint) — the inbound
--
crates\spt-daemon\src\broker.rs:53:    applied_envelope, decode_bytes, displaced_envelope, endpoint_injected_envelope,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-54-    evicted_envelope, net_dialed_envelope, output_envelope,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-55-    size_envelope, subscribed_envelope, AdapterApplyReq, BrainRestarted, EndpointInputReq,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-56-    ErrorEvent, ExitEvent, InputReq, KillReq, NetDialReq, NetPresenceSubscribeReq, NetSent,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-57-    NetStatusReply, NetStreamOpenReq, NetStreamOpened, NetStreamSendReq, NetStreamSubscribeReq,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-58-    NetStreamsReply, PairJoinReply, PairJoinReq, ResizeReq, SessionInfo, SessionsReply, SpawnReq,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-59-    Spawned, SubscribeOutcome, SubscribeReq, UnsubscribeReq, KIND_BRAIN_RESTART, KIND_BRAIN_RESTARTED, KIND_ENDPOINT_INPUT, KIND_ERROR, KIND_EXIT,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-60-    KIND_INPUT, KIND_KILL, KIND_NET_DIAL, KIND_NET_DIAL_LOOPBACK, KIND_NET_PRESENCE_SUBSCRIBE,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-61-    KIND_NET_SENT,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-62-    KIND_ADAPTER_APPLY, KIND_APPLIED,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-63-    KIND_NET_STATUS, KIND_NET_STATUS_REPLY, KIND_NET_STREAMS, KIND_NET_STREAMS_REPLY,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-64-    KIND_NET_STREAM_OPEN, KIND_NET_STREAM_OPENED, KIND_NET_STREAM_SEND, KIND_NET_STREAM_SUBSCRIBE,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-65-    KIND_PAIR_JOIN, KIND_PAIR_JOINED, KIND_RESIZE, KIND_SESSIONS, KIND_SESSIONS_REPLY, KIND_SPAWN,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-66-    KIND_SPAWNED, KIND_SUBSCRIBE, KIND_UNSUBSCRIBE,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-67-};
crates\spt-daemon\src\broker.rs-68-use crate::nethost::{NetHost, NET_EFFECT_SESSION};
crates\spt-daemon\src\broker.rs-69-use crate::translation::{key_to_bytes, InjectFloor, KeyCmd, ToBinary, TranslationChild};
crates\spt-daemon\src\broker.rs-70-use crate::transport::{recv_hello, DaemonTransport, LocalSocketTransport};
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-71-
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-72-/// The shared, serialized send side of one brain connection. Output frames,
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-73-/// replay frames, command acks, and exit/error events all write through this.
crates\spt-daemon\src\broker.rs-74-\ `pub(crate)`: the net host's stream logs (D4b) live-send through the same
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-75-/// handle under the same lock discipline.
--
crates\spt-daemon\src\broker.rs:1922:                            send_frame(send, &endpoint_injected_envelope(&req.endpoint, true));
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1923-                            return Ok(());
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1924-                        }
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1925-                        // worker receiver dropped → SPOOL (below), never raw inject.
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1926-                    }
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1927-                }
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1928-                // No WORKING translation binary (none declared, faulted, or its
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1929-                // inject worker is gone) → tell the caller to SPOOL (delivered=false).
crates\spt-daemon\src\broker.rs-1930-                \ Raw `payload + \r` is NOT a fallback: on a modern TUI it does not
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1931-                // submit — a non-delivering pseudo-write that MASKED F-019. The
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1932-                // inbound is never lost (it stays spooled, poll-fed) and the failure
C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon\src\broker.rs-1933-                // is LOUD so a mis-declared/repaired binary is diagnosable.

[Showing results with pagination = limit: 60]


CHANGE 5 fully verified — bool threads cleanly, no cli.rs edit. Now the behavior flip's test impact: any test asserting `delivered=true` for a hosted-but-no-binary session must flip to false/spool. Find them:


crates\spt-daemon\tests\digest.rs:162:        .expect("subscriber registered (base delivered)");
crates\spt-daemon\tests\dispatch.rs:35:use spt_store::spool;
crates\spt-daemon\tests\dispatch.rs:341:// [int->REQ-NET-1] a WAN message feed lands in B's perch spool end to end —
crates\spt-daemon\tests\dispatch.rs:355:    // An offline perch at B so the funnel takes the atomic spool path.
crates\spt-daemon\tests\dispatch.rs:396:    let mut spooled = false;
crates\spt-daemon\tests\dispatch.rs:398:        let pending = spool::peek_all_at(&perch_path).expect("peek");
crates\spt-daemon\tests\dispatch.rs:403:            spooled = true;
crates\spt-daemon\tests\dispatch.rs:409:        spooled,
crates\spt-daemon\tests\dispatch.rs:410:        "the dispatcher funneled the WAN message into the spool"
crates\spt-daemon\tests\dispatch.rs:570:// REMOTE (nothing delivered, nothing marked at A); the row rides the
crates\spt-daemon\tests\dispatch.rs:573:// endpoint's spool, marks land on the row. Activity shifting redirects
crates\spt-daemon\tests\dispatch.rs:627:    // "A"'s side: the presence MRA redirects — unmarked, undelivered. (The
crates\spt-daemon\tests\dispatch.rs:664:        spool::pending_count_at(&perch_path).unwrap(),
crates\spt-daemon\tests\dispatch.rs:666:        "A delivered nothing"
crates\spt-daemon\tests\dispatch.rs:680:        let pending = spool::peek_all_at(&perch_path).expect("peek");
crates\spt-daemon\tests\dispatch.rs:694:    // The surface delivers to the spool BEFORE marking the row (notif.rs
crates\spt-daemon\tests\dispatch.rs:715:// MAC-stamped in B's spool under that token, and a foreign owner resolves
crates\spt-daemon\tests\dispatch.rs:779:    // Drive: the vocabulary-checked command spools MAC-stamped at B.
crates\spt-daemon\tests\dispatch.rs:795:    assert!(detail.contains("spooled op=note"), "{detail}");
crates\spt-daemon\tests\dispatch.rs:796:    let rows = spool::peek_all_at(&shell_perch).expect("spool");
crates\spt-daemon\tests\dispatch.rs:797:    assert_eq!(rows.len(), 1, "exactly one frame spooled at B");
crates\spt-daemon\tests\broker.rs:30:    KIND_ENDPOINT_INJECTED, KIND_ENDPOINT_INPUT, KIND_ERROR, KIND_EXIT, KIND_INPUT, KIND_KILL,
crates\spt-daemon\tests\broker.rs:189:/// `delivered=true`. An inject for an UNKNOWN endpoint replies `delivered=false`
crates\spt-daemon\tests\broker.rs:190:/// and writes nothing (the caller then falls back to the spool).
crates\spt-daemon\tests\broker.rs:230:    // through the real PTY, and the broker acks delivered=true.
crates\spt-daemon\tests\broker.rs:240:    let mut delivered: Option<bool> = None;
crates\spt-daemon\tests\broker.rs:246:        if echoed && delivered.is_some() {
crates\spt-daemon\tests\broker.rs:251:            Ok(f) if f.kind == KIND_ENDPOINT_INJECTED => {
crates\spt-daemon\tests\broker.rs:252:                delivered = Some(
crates\spt-daemon\tests\broker.rs:255:                        .delivered,
crates\spt-daemon\tests\broker.rs:262:    assert_eq!(delivered, Some(true), "ack delivered=true for the hosted endpoint");
crates\spt-daemon\tests\broker.rs:269:    // Inject for an UNKNOWN endpoint: delivered=false, nothing written.
crates\spt-daemon\tests\broker.rs:282:            Ok(f) if f.kind == KIND_ENDPOINT_INJECTED => {
crates\spt-daemon\tests\broker.rs:286:                        .delivered,
crates\spt-daemon\tests\broker.rs:294:    assert_eq!(miss, Some(false), "unknown endpoint acks delivered=false");
crates\spt-daemon\tests\broker.rs:370:    // frame is delivered. The old loop broke on the first `Err` OR on `EXIT`, so it
crates\spt-daemon\tests\broker.rs:740:/// (the no-ack write is delivered; only the ack frame is suppressed).
crates\spt-daemon\tests\broker.rs:833:        "the ack=false input must still be DELIVERED to the PTY (only the ack frame \
crates\spt-daemon\tests\broker.rs:1015:                return; // already delivered — at-least-once boundary re-send; dedup
crates\spt-daemon\tests\broker.rs:1042:    // below). Seeds the ring with ≥2 seqs and advances `delivered_through`.
crates\spt-daemon\tests\attach.rs:478:// populated ring replays and is delivered in order. The prior loopback E2Es only
crates\spt-daemon\tests\attach.rs:1063:// controller frames (advancing nothing — `delivered_through` freezes), which
crates\spt-daemon\tests\attach.rs:1074:// 200KB burst) → the ring rolls PAST the frozen delivered_through → the dropped
crates\spt-daemon\tests\attach.rs:1246:    // CONSUMPTION cursor, not the successor connection's delivered state).
crates\spt-daemon\tests\attach.rs:1384:    // broker's re-send of [0,K] is re-delivered (the operator's render cursor
crates\spt-daemon\tests\notifsync.rs:131:// [unit->REQ-NOTIF-1] two daemons converge a notif spool over the broker-owned
crates\spt-daemon\tests\notifsync.rs:140:fn notif_spools_converge_over_the_wire_and_dismiss_replicates() {
crates\spt-daemon\tests\access.rs:30:use spt_store::spool;
crates\spt-daemon\tests\access.rs:142:// node's unsolicited message — nothing spools; (2) the same node passes once
crates\spt-daemon\tests\access.rs:180:    // (1) Unsolicited from a non-listed node → refused at the gate, no spool.
crates\spt-daemon\tests\access.rs:201:        spool::pending_count_at(&perch_path).expect("count"),
crates\spt-daemon\tests\access.rs:203:        "nothing spooled"
crates\spt-daemon\tests\access.rs:215:        WanOutcome::Spooled,
crates\spt-daemon\tests\access.rs:230:        WanOutcome::Spooled,
crates\spt-daemon\tests\access.rs:234:    let pending = spool::peek_all_at(&perch_path).expect("peek");
crates\spt-daemon\tests\netstream.rs:14://! - **D** — every chunk is delivered exactly once (no duplicate seq): the
crates\spt-daemon\tests\netstream.rs:173:    // transport cursor (whatever read-count delivered those bytes — coalescing may
crates\spt-daemon\tests\pump.rs:146:// A's pump pushes its registry advertisement and notif spool to B and pulls
crates\spt-daemon\tests\resume.rs:9://! the broker's per-session delivered cursor (`resume_seq`).
crates\spt-daemon\tests\resume.rs:13://!   resumes from where output was last delivered (NOT a full replay from 0 — the

[Showing results with pagination = limit: 60]


Found the load-bearing test: `tests/broker.rs:189-294` — injects into a hosted session with NO translation binary, asserts `delivered=true` + PTY echo (the OLD raw-inject behavior). This must flip. Read it fully: