6450	fn cmd_adapter_digest_proof(
6451	    adapters: &std::path::Path,
6452	    option: &str,
6453	    sample: Option<&str>,
6454	    session: Option<&str>,
6455	) -> i32 {
6456	    use spt_runtime::registry;
6457	    use std::time::Duration;
6458
6459	    // The record's `source_dir` is the adapter install dir — where a `.spt`
6460	    // ships its extractor binary; resolve the bare program there before PATH so
6461	    // proof runs exactly as the daemon does (REQ-INSTALL-11).
6462	    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
6463	        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
6464	        Err(e) => {
6465	            eprintln!("DIGEST_PROOF_FAIL:{option}: {e}");
6466	            return 1;
6467	        }
6468	    };
6469	    let Some(declared) = manifest.digest.clone() else {
6470	        eprintln!("DIGEST_PROOF_NO_SECTION:{option}: adapter declares no [digest] extractor");
6471	        return 2;
6472	    };
6473
6474	    // Force the sample as the source when provided; otherwise use the declared
6475	    // source (which is `{source}`-resolved below from the same key set).
6476	    let effective = match sample {
6477	        Some(s) => spt_runtime::manifest::Digest {
6478	            source: Some(s.to_string()),
6479	            ..declared.clone()
6480	        },
6481	        None => declared.clone(),
6482	    };
6483	    // Fill the SAME substitution keys the daemon fills at runtime
6484	    // (`spt_daemon::digest` activity-span: `{id}` + `{session_id}`) so proof is
6485	    // faithful — an extractor that proofs here works at runtime, and the
6486	    // published `{session_id}`-templated shape is runnable. `{source}` is added
6487	    // by `extract_digest` from the resolved/forced source. The session id is a
6488	    // placeholder unless `--session` pins one (the daemon supplies the live id).
6489	    let proof_session = session.unwrap_or("proof-session");
6490	    let mut keys = std::collections::BTreeMap::new();
6491	    keys.insert("id".to_string(), option.to_string());
6492	    keys.insert("session_id".to_string(), proof_session.to_string());
6493	    let lines = match spt_live::extract_digest(
6494	        &effective,
6495	        manifest.history.as_ref(),
6496	        &keys,
6497	        Duration::from_secs(30),
6498	        Some(install_dir.as_path()),
6499	    ) {
6500	        Ok(l) => l,
6501	        Err(e) => {
6502	            eprintln!("DIGEST_PROOF_EXTRACT_FAIL:{option}: {e}");
6503	            if sample.is_none() {
6504	                eprintln!("  tip: pass --sample <log> to run against a real session log");
6505	            }
6506	            eprintln!(
6507	                "  tip: digest-proof fills {{id}} and {{session_id}} (use --session <id> to pin \
6508	                 the session id); other keys are runtime-only and won't resolve here"
6509	            );
6510	            return 1;
6511	        }
6512	    };
6513
6514	    let config = spt_daemon::resolve_config(Some(&declared), &spt_daemon::DigestOverride::default());
6515	    let (digest, diag) =
6516	        spt_term::project_lines_diagnosed(lines.iter().map(String::as_str), &config);
6517
6518	    println!("=== digest-proof: {option} ===");
6519	    println!("parsed {} record(s), dropped {}", diag.parsed, diag.drop_count());
6520	    println!("\n--- parsed records ---");
6521	    for l in &lines {
6522	        if spt_term::record_to_tagged_result(l).is_ok() {
6523	            println!("  {l}");
6524	        }
6525	    }
6526	    println!("\n--- rendered digest ---");
6527	    let rendered = spt_daemon::render_digest(&digest);
6528	    if rendered.trim().is_empty() {
6529	        println!("  (empty)");
6530	    } else {
6531	        print!("{rendered}");
6532	    }
6533	    if !diag.dropped.is_empty() {
6534	        println!("\n--- dropped lines ---");
6535	        for d in &diag.dropped {
6536	            println!("  line {}: {} — {}", d.line_no, d.raw, d.reason);
6537	        }
6538	    }
6539
6540	    // A broken extractor (drops) or a silent empty (nothing parsed) fails the proof.
6541	    if diag.drop_count() > 0 {
6542	        eprintln!("DIGEST_PROOF_DROPS:{option}: {} line(s) did not match the contract", diag.drop_count());
6543	        return 1;
6544	    }
6545	    if diag.parsed == 0 {
6546	        eprintln!("DIGEST_PROOF_EMPTY:{option}: extractor produced no contract records (a silent-empty digest)");
6547	        return 1;
6548	    }
6549	    eprintln!("DIGEST_PROOF_OK:{option}");
6550	    0
6551	}
6552
6553	/// `spt adapter translate-proof <adapter> --event <envelope> [--session <id>]` —
6554	/// the author-time EMIT-half proof for `[message-idle-translation-binary]`
6555	/// (ADR-0022), symmetric to [`cmd_adapter_digest_proof`]. Spawns and feeds the
6556	/// adapter's declared translation binary EXACTLY as the daemon does at
6557	/// idle-delivery (the `init` line, then the `event` envelope) by driving the REAL
6558	/// `spt_daemon::translation` driver verbatim — no protocol reimplementation — and
6559	/// reads back the emitted keystroke-command stream, printing it author-readable.
6560	///
6561	/// This is the EMIT half ONLY: it proves the binary's spawn → feed → emit
6562	/// contract; it does NOT exercise the daemon's atomic PTY apply / controller
6563	/// buffering (that stays covered by the W2 inject_control_wedge int gate). It
6564	/// fills the SAME `{id}`/`{session_id}` keys into the `--event` envelope that the
6565	/// daemon fills at runtime, so an envelope that proofs here feeds faithfully live.
6566	///
6567	/// Exit codes mirror digest-proof: 0 ok, 1 on spawn-fail / zero commands /
6568	/// no-commit-or-output / unparseable, 2 when the adapter declares no
6569	/// `[message-idle-translation-binary]` section. The [`TranslationChild`] is kept
6570	/// alive across the whole drain; its `Drop` does the bounded no-zombie reap.
6571	// [impl->REQ-ADAPTER-TRANSLATE-PROOF]
6572	fn cmd_adapter_translate_proof(
6573	    adapters: &std::path::Path,
6574	    option: &str,
6575	    event: &str,
6576	    session: Option<&str>,
6577	) -> i32 {
6578	    use spt_daemon::translation::{key_to_bytes, KeyCmd, ToBinary, TranslationChild};
6579	    use spt_runtime::registry;
6580	    use std::sync::mpsc::{channel, RecvTimeoutError};
6581	    use std::time::{Duration, Instant};
6582
6583	    // Overall drain budget: stop once a `{commit}` arrives, or when this elapses
6584	    // (so a no-commit binary still surfaces what it emitted rather than hanging).
6585	    const DRAIN_TOTAL: Duration = Duration::from_secs(5);
6586	    // Per-recv poll slice — short so an idle stretch is noticed promptly.
6587	    const DRAIN_POLL: Duration = Duration::from_millis(250);
6588
6589	    // The record's `source_dir` is the adapter install dir — where a `.spt` ships
6590	    // its translation binary; resolve the declared `path` there before PATH so the
6591	    // proof spawns exactly as the daemon does (REQ-INSTALL-11), mirroring the
6592	    // daemon's harnesshost path resolution.
6593	    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
6594	        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
6595	        Err(e) => {
6596	            eprintln!("TRANSLATE_PROOF_FAIL:{option}: {e}");
6597	            return 1;
6598	        }
6599	    };
6600	    let Some(declared) = manifest.message_idle_translation_binary.clone() else {
6601	        eprintln!(
6602	            "TRANSLATE_PROOF_NO_SECTION:{option}: adapter declares no \
6603	             [message-idle-translation-binary]"
6604	        );
6605	        return 2;
6606	    };
6607	    let path = spt_runtime::runtime::resolve_program_in_dir(&declared.path, &install_dir);
6608
6609	    // Fill the SAME substitution keys the daemon fills into a delivered envelope
6610	    // (`{id}` = the endpoint/option, `{session_id}` = the live session) so the proof
6611	    // is faithful — an envelope that proofs here feeds at runtime. Cheap str
6612	    // replace: a no-op when the author used no tokens.
6613	    let proof_session = session.unwrap_or("proof-session");
6614	    let envelope = event
6615	        .replace("{id}", option)
6616	        .replace("{session_id}", proof_session);
6617
6618	    // Spawn + drive the REAL driver verbatim: spawn, send the identity `init` line
6619	    // (mirror broker.rs — endpoint_id = the option, node = this host's label), then
6620	    // the inbound `event` envelope.
6621	    let (cmd_tx, cmd_rx) = channel::<KeyCmd>();
6622	    let child = match TranslationChild::spawn(&path, cmd_tx) {
6623	        Ok(c) => c,
6624	        Err(e) => {
6625	            eprintln!("TRANSLATE_PROOF_SPAWN_FAIL:{option}: {path}: {e}");
6626	            return 1;
6627	        }
6628	    };
6629	    let _ = child.send(&ToBinary::Init {
6630	        endpoint_id: option.to_string(),
6631	        node: spt_store::hostlabel::os_hostname().unwrap_or_default(),
6632	    });
6633	    if let Err(e) = child.send(&ToBinary::Event { envelope }) {
6634	        eprintln!("TRANSLATE_PROOF_FEED_FAIL:{option}: {e}");
6635	        return 1;
6636	    }
6637
6638	    // Drain the emitted keystroke-command stream until a `{commit}` arrives or the
6639	    // overall budget elapses; the channel is fed by the driver's reader thread. An
6640	    // unparseable stdout line never reaches us as a KeyCmd — the driver logs it as
6641	    // `TRANSLATION_BIN_BAD_LINE` and skips it — so a binary that emits ONLY garbage
6642	    // surfaces here as zero commands (the gate below catches that as a failure).
6643	    let deadline = Instant::now() + DRAIN_TOTAL;
6644	    let mut cmds: Vec<KeyCmd> = Vec::new();
6645	    let mut committed = false;
6646	    loop {
6647	        let remaining = deadline.saturating_duration_since(Instant::now());
6648	        if remaining.is_zero() {
6649	            break;

fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,
    option: &str,
    sample: Option<&str>,
    session: Option<&str>,
) -> i32 {
    use spt_runtime::registry;
    use std::time::Duration;

    // The record's `source_dir` is the adapter install dir — where a `.spt`
    // ships its extractor binary; resolve the bare program there before PATH so
    // proof runs exactly as the daemon does (REQ-INSTALL-11).
    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
        Err(e) => {
            eprintln!("DIGEST_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };
    let Some(declared) = manifest.digest.clone() else {
        eprintln!("DIGEST_PROOF_NO_SECTION:{option}: adapter declares no [digest] extractor");
        return 2;
    };

    // Force the sample as the source when provided; otherwise use the declared
    // source (which is `{source}`-resolved below from the same key set).
    let effective = match sample {
        Some(s) => spt_runtime::manifest::Digest {
            source: Some(s.to_string()),
            ..declared.clone()
        },
        None => declared.clone(),
    };
    // Fill the SAME substitution keys the daemon fills at runtime
    // (`spt_daemon::digest` activity-span: `{id}` + `{session_id}`) so proof is
    // faithful — an extractor that proofs here works at runtime, and the
    // published `{session_id}`-templated shape is runnable. `{source}` is added
    // by `extract_digest` from the resolved/forced source. The session id is a
    // placeholder unless `--session` pins one (the daemon supplies the live id).
    let proof_session = session.unwrap_or("proof-session");
    let mut keys = std::collections::BTreeMap::new();
    keys.insert("id".to_string(), option.to_string());
    keys.insert("session_id".to_string(), proof_session.to_string());
    let lines = match spt_live::extract_digest(
        &effective,
        manifest.history.as_ref(),
        &keys,
        Duration::from_secs(30),
        Some(install_dir.as_path()),
    ) {
        Ok(l) => l,
        Err(e) => {
            eprintln!("DIGEST_PROOF_EXTRACT_FAIL:{option}: {e}");
            if sample.is_none() {
                eprintln!("  tip: pass --sample <log> to run against a real session log");
            }
            eprintln!(
                "  tip: digest-proof fills {{id}} and {{session_id}} (use --session <id> to pin \
                 the session id); other keys are runtime-only and won't resolve here"
            );
            return 1;
        }
    };

    let config = spt_daemon::resolve_config(Some(&declared), &spt_daemon::DigestOverride::default());
    let (digest, diag) =
        spt_term::project_lines_diagnosed(lines.iter().map(String::as_str), &config);

    println!("=== digest-proof: {option} ===");
    println!("parsed {} record(s), dropped {}", diag.parsed, diag.drop_count());
    println!("\n--- parsed records ---");
    for l in &lines {
        if spt_term::record_to_tagged_result(l).is_ok() {
            println!("  {l}");
        }
    }
    println!("\n--- rendered digest ---");
    let rendered = spt_daemon::render_digest(&digest);
    if rendered.trim().is_empty() {
        println!("  (empty)");
    } else {
        print!("{rendered}");
    }
    if !diag.dropped.is_empty() {
        println!("\n--- dropped lines ---");
        for d in &diag.dropped {
            println!("  line {}: {} — {}", d.line_no, d.raw, d.reason);
        }
    }

    // A broken extractor (drops) or a silent empty (nothing parsed) fails the proof.
    if diag.drop_count() > 0 {
        eprintln!("DIGEST_PROOF_DROPS:{option}: {} line(s) did not match the contract", diag.drop_count());
        return 1;
    }
    if diag.parsed == 0 {
        eprintln!("DIGEST_PROOF_EMPTY:{option}: extractor produced no contract records (a silent-empty digest)");
        return 1;
    }
    eprintln!("DIGEST_PROOF_OK:{option}");
    0
}

/// `spt adapter translate-proof <adapter> --event <envelope> [--session <id>]` —
/// the author-time EMIT-half proof for `[message-idle-translation-binary]`
/// (ADR-0022), symmetric to [`cmd_adapter_digest_proof`]. Spawns and feeds the
/// adapter's declared translation binary EXACTLY as the daemon does at
/// idle-delivery (the `init` line, then the `event` envelope) by driving the REAL
/// `spt_daemon::translation` driver verbatim — no protocol reimplementation — and
/// reads back the emitted keystroke-command stream, printing it author-readable.
///
/// This is the EMIT half ONLY: it proves the binary's spawn → feed → emit
/// contract; it does NOT exercise the daemon's atomic PTY apply / controller
/// buffering (that stays covered by the W2 inject_control_wedge int gate). It
/// fills the SAME `{id}`/`{session_id}` keys into the `--event` envelope that the
/// daemon fills at runtime, so an envelope that proofs here feeds faithfully live.
///
/// Exit codes mirror digest-proof: 0 ok, 1 on spawn-fail / zero commands /
/// no-commit-or-output / unparseable, 2 when the adapter declares no
/// `[message-idle-translation-binary]` section. The [`TranslationChild`] is kept
/// alive across the whole drain; its `Drop` does the bounded no-zombie reap.
// [impl->REQ-ADAPTER-TRANSLATE-PROOF]
fn cmd_adapter_translate_proof(
    adapters: &std::path::Path,
    option: &str,
    event: &str,
    session: Option<&str>,
) -> i32 {
    use spt_daemon::translation::{key_to_bytes, KeyCmd, ToBinary, TranslationChild};
    use spt_runtime::registry;
    use std::sync::mpsc::{channel, RecvTimeoutError};
    use std::time::{Duration, Instant};

    // Overall drain budget: stop once a `{commit}` arrives, or when this elapses
    // (so a no-commit binary still surfaces what it emitted rather than hanging).
    const DRAIN_TOTAL: Duration = Duration::from_secs(5);
    // Per-recv poll slice — short so an idle stretch is noticed promptly.
    const DRAIN_POLL: Duration = Duration::from_millis(250);

    // The record's `source_dir` is the adapter install dir — where a `.spt` ships
    // its translation binary; resolve the declared `path` there before PATH so the
    // proof spawns exactly as the daemon does (REQ-INSTALL-11), mirroring the
    // daemon's harnesshost path resolution.
    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
        Err(e) => {
            eprintln!("TRANSLATE_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };
    let Some(declared) = manifest.message_idle_translation_binary.clone() else {
        eprintln!(
            "TRANSLATE_PROOF_NO_SECTION:{option}: adapter declares no \
             [message-idle-translation-binary]"
        );
        return 2;
    };
    let path = spt_runtime::runtime::resolve_program_in_dir(&declared.path, &install_dir);

    // Fill the SAME substitution keys the daemon fills into a delivered envelope
    // (`{id}` = the endpoint/option, `{session_id}` = the live session) so the proof
    // is faithful — an envelope that proofs here feeds at runtime. Cheap str
    // replace: a no-op when the author used no tokens.
    let proof_session = session.unwrap_or("proof-session");
    let envelope = event
        .replace("{id}", option)
        .replace("{session_id}", proof_session);

    // Spawn + drive the REAL driver verbatim: spawn, send the identity `init` line
    // (mirror broker.rs — endpoint_id = the option, node = this host's label), then
    // the inbound `event` envelope.
    let (cmd_tx, cmd_rx) = channel::<KeyCmd>();
    let child = match TranslationChild::spawn(&path, cmd_tx) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("TRANSLATE_PROOF_SPAWN_FAIL:{option}: {path}: {e}");
            return 1;
        }
    };
    let _ = child.send(&ToBinary::Init {
        endpoint_id: option.to_string(),
        node: spt_store::hostlabel::os_hostname().unwrap_or_default(),
    });
    if let Err(e) = child.send(&ToBinary::Event { envelope }) {
        eprintln!("TRANSLATE_PROOF_FEED_FAIL:{option}: {e}");
        return 1;
    }

    // Drain the emitted keystroke-command stream until a `{commit}` arrives or the
    // overall budget elapses; the channel is fed by the driver's reader thread. An
    // unparseable stdout line never reaches us as a KeyCmd — the driver logs it as
    // `TRANSLATION_BIN_BAD_LINE` and skips it — so a binary that emits ONLY garbage
    // surfaces here as zero commands (the gate below catches that as a failure).
    let deadline = Instant::now() + DRAIN_TOTAL;
    let mut cmds: Vec<KeyCmd> = Vec::new();
    let mut committed = false;
    loop {
        let remaining = deadline.saturating_duration_since(Instant::now());
        if remaining.is_zero() {
            break;


/spt:send doyle a message about W4 for his gate and approval


Now the clap arg definitions + dispatch. Find the subcommand variants:


crates\spt\src\cli.rs:587:    DigestProof {
crates\spt\src\cli.rs:610:    TranslateProof {
crates\spt\src\cli.rs:5996:        AdapterCmd::DigestProof { option, sample, session } => {
crates\spt\src\cli.rs:5997:            cmd_adapter_digest_proof(&adapters, &option, sample.as_deref(), session.as_deref())
crates\spt\src\cli.rs:6000:        AdapterCmd::TranslateProof { option, event, session } => {
crates\spt\src\cli.rs:6001:            cmd_adapter_translate_proof(&adapters, &option, &event, session.as_deref())
crates\spt\src\cli.rs:6441:/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]` — run
crates\spt\src\cli.rs:6450:fn cmd_adapter_digest_proof(
crates\spt\src\cli.rs:6507:                "  tip: digest-proof fills {{id}} and {{session_id}} (use --session <id> to pin \
crates\spt\src\cli.rs:6518:    println!("=== digest-proof: {option} ===");
crates\spt\src\cli.rs:6553:/// `spt adapter translate-proof <adapter> --event <envelope> [--session <id>]` —
crates\spt\src\cli.rs:6555:/// (ADR-0022), symmetric to [`cmd_adapter_digest_proof`]. Spawns and feeds the
crates\spt\src\cli.rs:6567:/// Exit codes mirror digest-proof: 0 ok, 1 on spawn-fail / zero commands /
crates\spt\src\cli.rs:6572:fn cmd_adapter_translate_proof(
crates\spt\src\cli.rs:6674:    println!("=== translate-proof: {option} ===");
crates\spt\src\cli.rs:9359:        match parse(&["spt", "adapter", "digest-proof", "claude-spt", "--sample", "log.jsonl"])
crates\spt\src\cli.rs:9365:                action: AdapterCmd::DigestProof { option, sample, session },
crates\spt\src\cli.rs:9371:            _ => panic!("expected Adapter DigestProof"),
crates\spt\src\cli.rs:9375:    // [unit->REQ-TERM-5] digest-proof runs the declared extractor over a sample
crates\spt\src\cli.rs:9398:            cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None)
crates\spt\src\cli.rs:9427:            cmd_adapter_digest_proof(&adapters, "plain", Some(good.to_str().unwrap()), None),
crates\spt\src\cli.rs:9433:    // [unit->REQ-TERM-5] digest-proof fills the SAME keys the daemon fills at
crates\spt\src\cli.rs:9470:            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), None),
crates\spt\src\cli.rs:9476:            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), Some("sessA")),
crates\spt\src\cli.rs:9483:    // `adapter translate-proof <opt> --event <env> [--session <id>]` — `--event`
crates\spt\src\cli.rs:9490:            "translate-proof",
crates\spt\src\cli.rs:9500:                action: AdapterCmd::TranslateProof { option, event, session },
crates\spt\src\cli.rs:9506:            _ => panic!("expected Adapter TranslateProof"),
crates\spt\src\cli.rs:9512:            "translate-proof",
crates\spt\src\cli.rs:9524:                action: AdapterCmd::TranslateProof { session, .. },
crates\spt\src\cli.rs:9526:            _ => panic!("expected Adapter TranslateProof"),
crates\spt\src\cli.rs:9530:            parse(&["spt", "adapter", "translate-proof", "claude-spt"]).is_err(),
crates\spt\src\cli.rs:9552:    // [unit->REQ-ADAPTER-TRANSLATE-PROOF] translate-proof drives the REAL
crates\spt\src\cli.rs:9592:            cmd_adapter_translate_proof(&adapters, "cc", event, None),
crates\spt\src\cli.rs:9600:            cmd_adapter_translate_proof(&adapters, "cc", event, Some("sessA")),
crates\spt\src\cli.rs:9614:            cmd_adapter_translate_proof(&adapters, "plain", event, None),


580	    },
581	    /// Prove an adapter's `[digest]` extractor against a real log sample
582	    /// (ADR-0019). Runs the declared extractor over `--sample <log>` (or the
583	    /// declared source) and prints the parsed contract records, the rendered
584	    /// digest, and **every dropped line with its reason** — the author-time
585	    /// answer to "`spt endpoint digest` returns nothing" (no silent empty).
586	    /// Exit 1 if any line drops or nothing parses.
587	    DigestProof {
588	        /// `<adapter>` or `<adapter>:<profile>` (must declare `[digest]`).
589	        option: String,
590	        /// A real session-log sample to run the extractor over (recommended).
591	        #[arg(long)]
592	        sample: Option<String>,
593	        /// The `{session_id}` to fill into the extractor command (the daemon
594	        /// fills the live one at runtime). Defaults to a placeholder so a
595	        /// `{session_id}`-templated extractor — the published shape — proofs;
596	        /// pin a real id when the file the extractor locates depends on it.
597	        #[arg(long)]
598	        session: Option<String>,
599	    },
600	    /// Prove an adapter's `[message-idle-translation-binary]` against an inbound
601	    /// event (ADR-0022). Spawns and feeds the declared translation binary exactly
602	    /// as the daemon does at idle-delivery — sends the `init` line then the
603	    /// `--event` envelope and reads back the emitted keystroke-command stream
604	    /// (`{key}`/`{text}`/`{delay_ms}`/`{commit}`), printed author-readable. This is
605	    /// the EMIT half ONLY: it proves the binary's spawn-feed-emit contract; it does
606	    /// NOT exercise the daemon's atomic PTY apply or controller buffering. Fills
607	    /// `{id}` and `{session_id}` into the envelope the same way the daemon does
608	    /// (use `--session` to pin the session id). Exit 1 if the binary fails to
609	    /// spawn, emits nothing, emits no commit, or emits an unparseable line.
610	    TranslateProof {
611	        /// `<adapter>` or `<adapter>:<profile>` (must declare
612	        /// `[message-idle-translation-binary]`).
613	        option: String,
614	        /// The inbound `<EVENT…>` envelope to feed (ADR-0020). `{id}` and
615	        /// `{session_id}` tokens in it are filled as the daemon fills them.
616	        #[arg(long)]
617	        event: String,
618	        /// The `{session_id}` to fill into the event envelope (the daemon fills
619	        /// the live one at runtime). Defaults to a placeholder; pin a real id
620	        /// when the binary's behavior depends on it.
621	        #[arg(long)]
622	        session: Option<String>,
623	    },
624	    /// Set a `[strings]` dot-path on a **local** profile (`<adapter>:<profile>`).
625	    /// Sugar over editing the overlay file; refuses a shipped profile and a bare
626	    /// option (a local target is required — `create-profile` first).
627	    SetString {
628	        /// `<adapter>:<profile>` — the local profile to edit.
629	        option: String,
630	        /// Dot-separated key path into `[strings]`.
631	        key: String,
632	        /// The string value to store.
633	        value: String,
634	    },

    },
    /// Prove an adapter's `[digest]` extractor against a real log sample
    /// (ADR-0019). Runs the declared extractor over `--sample <log>` (or the
    /// declared source) and prints the parsed contract records, the rendered
    /// digest, and **every dropped line with its reason** — the author-time
    /// answer to "`spt endpoint digest` returns nothing" (no silent empty).
    /// Exit 1 if any line drops or nothing parses.
    DigestProof {
        /// `<adapter>` or `<adapter>:<profile>` (must declare `[digest]`).
        option: String,
        /// A real session-log sample to run the extractor over (recommended).
        #[arg(long)]
        sample: Option<String>,
        /// The `{session_id}` to fill into the extractor command (the daemon
        /// fills the live one at runtime). Defaults to a placeholder so a
        /// `{session_id}`-templated extractor — the published shape — proofs;
        /// pin a real id when the file the extractor locates depends on it.
        #[arg(long)]
        session: Option<String>,
    },
    /// Prove an adapter's `[message-idle-translation-binary]` against an inbound
    /// event (ADR-0022). Spawns and feeds the declared translation binary exactly
    /// as the daemon does at idle-delivery — sends the `init` line then the
    /// `--event` envelope and reads back the emitted keystroke-command stream
    /// (`{key}`/`{text}`/`{delay_ms}`/`{commit}`), printed author-readable. This is
    /// the EMIT half ONLY: it proves the binary's spawn-feed-emit contract; it does
    /// NOT exercise the daemon's atomic PTY apply or controller buffering. Fills
    /// `{id}` and `{session_id}` into the envelope the same way the daemon does
    /// (use `--session` to pin the session id). Exit 1 if the binary fails to
    /// spawn, emits nothing, emits no commit, or emits an unparseable line.
    TranslateProof {
        /// `<adapter>` or `<adapter>:<profile>` (must declare
        /// `[message-idle-translation-binary]`).
        option: String,
        /// The inbound `<EVENT…>` envelope to feed (ADR-0020). `{id}` and
        /// `{session_id}` tokens in it are filled as the daemon fills them.
        #[arg(long)]
        event: String,
        /// The `{session_id}` to fill into the event envelope (the daemon fills
        /// the live one at runtime). Defaults to a placeholder; pin a real id
        /// when the binary's behavior depends on it.
        #[arg(long)]
        session: Option<String>,
    },
    /// Set a `[strings]` dot-path on a **local** profile (`<adapter>:<profile>`).
    /// Sugar over editing the overlay file; refuses a shipped profile and a bare
    /// option (a local target is required — `create-profile` first).
    SetString {
        /// `<adapter>:<profile>` — the local profile to edit.
        option: String,
        /// Dot-separated key path into `[strings]`.
        key: String,
        /// The string value to store.
        value: String,
    },


Read `source_manifest_file` (the manifest-filename convention) to mirror it:


crates\spt-runtime\src\registry.rs:145:fn source_manifest_file(source: &Path) -> PathBuf {
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-146-    if source.is_dir() {
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-147-        source.join("manifest.toml")
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-148-    } else {
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-149-        source.to_path_buf()
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-150-    }
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-151-}
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-152-
C:\Users\decid\Documents\projects\spt-core\crates\spt-runtime\src\registry.rs-153-// ───────────────────── File-backed [strings] (REQ-MANIFEST-5) ────────────────


Got the convention (`<dir>/manifest.toml`). Now implement W5. Add the two flags to `DigestProof`:


Edit

        /// `{session_id}`-templated extractor — the published shape — proofs;
        /// pin a real id when the file the extractor locates depends on it.
        #[arg(long)]
        session: Option<String>,
    },
    /// Prove an adapter's `[message-idle-translation-binary]` against an inbound

        /// `{session_id}`-templated extractor — the published shape — proofs;
        /// pin a real id when the file the extractor locates depends on it.
        #[arg(long)]
        session: Option<String>,
        /// Proof against an on-disk install **dir** instead of the registered
        /// adapter (F-011): binaries resolve here (REQ-INSTALL-11) and the
        /// manifest defaults to `<dir>/manifest.toml`. No full extracted install
        /// needed — proof a DEV binary straight from its build dir.
        #[arg(long)]
        dir: Option<String>,
        /// Pin the manifest **file** for the proof (overrides `<dir>/manifest.toml`;
        /// absent `--dir`, its parent dir is the install dir). Lets a bare-file
        /// `gh_release` adapter proof without staging an extracted install.
        #[arg(long)]
        manifest: Option<String>,
    },
    /// Prove an adapter's `[message-idle-translation-binary]` against an inbound


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

        /// `{session_id}`-templated extractor — the published shape — proofs;
        /// pin a real id when the file the extractor locates depends on it.
        #[arg(long)]
        session: Option<String>,
    },
    /// Prove an adapter's `[message-idle-translation-binary]` against an inbound

        /// `{session_id}`-templated extractor — the published shape — proofs;
        /// pin a real id when the file the extractor locates depends on it.
        #[arg(long)]
        session: Option<String>,
        /// Proof against an on-disk install **dir** instead of the registered
        /// adapter (F-011): binaries resolve here (REQ-INSTALL-11) and the
        /// manifest defaults to `<dir>/manifest.toml`. No full extracted install
        /// needed — proof a DEV binary straight from its build dir.
        #[arg(long)]
        dir: Option<String>,
        /// Pin the manifest **file** for the proof (overrides `<dir>/manifest.toml`;
        /// absent `--dir`, its parent dir is the install dir). Lets a bare-file
        /// `gh_release` adapter proof without staging an extracted install.
        #[arg(long)]
        manifest: Option<String>,
    },
    /// Prove an adapter's `[message-idle-translation-binary]` against an inbound

+        /// Proof against an on-disk install **dir** instead of the registered

+        /// adapter (F-011): binaries resolve here (REQ-INSTALL-11) and the

+        /// manifest defaults to `<dir>/manifest.toml`. No full extracted install

+        /// needed — proof a DEV binary straight from its build dir.

+        /// Pin the manifest **file** for the proof (overrides `<dir>/manifest.toml`;

+        /// absent `--dir`, its parent dir is the install dir). Lets a bare-file

+        /// `gh_release` adapter proof without staging an extracted install.

/// event (ADR-0022). Spawns and feeds the declared translation binary exactly


        /// the live one at runtime). Defaults to a placeholder; pin a real id
        /// when the binary's behavior depends on it.
        #[arg(long)]
        session: Option<String>,
    },
    /// Set a `[strings]` dot-path on a **local** profile (`<adapter>:<profile>`).

        /// the live one at runtime). Defaults to a placeholder; pin a real id
        /// when the binary's behavior depends on it.
        #[arg(long)]
        session: Option<String>,
        /// Proof against an on-disk install **dir** instead of the registered
        /// adapter (F-011): the translation binary resolves here (REQ-INSTALL-11)
        /// and the manifest defaults to `<dir>/manifest.toml`. No full extracted
        /// install needed — proof a DEV binary straight from its build dir.
        #[arg(long)]
        dir: Option<String>,
        /// Pin the manifest **file** for the proof (overrides `<dir>/manifest.toml`;
        /// absent `--dir`, its parent dir is the install dir). Lets a bare-file
        /// `gh_release` adapter proof without staging an extracted install.
        #[arg(long)]
        manifest: Option<String>,
    },
    /// Set a `[strings]` dot-path on a **local** profile (`<adapter>:<profile>`).


        /// the live one at runtime). Defaults to a placeholder; pin a real id
        /// when the binary's behavior depends on it.
        #[arg(long)]
        session: Option<String>,
    },
    /// Set a `[strings]` dot-path on a **local** profile (`<adapter>:<profile>`).

        /// the live one at runtime). Defaults to a placeholder; pin a real id
        /// when the binary's behavior depends on it.
        #[arg(long)]
        session: Option<String>,
        /// Proof against an on-disk install **dir** instead of the registered
        /// adapter (F-011): the translation binary resolves here (REQ-INSTALL-11)
        /// and the manifest defaults to `<dir>/manifest.toml`. No full extracted
        /// install needed — proof a DEV binary straight from its build dir.
        #[arg(long)]
        dir: Option<String>,
        /// Pin the manifest **file** for the proof (overrides `<dir>/manifest.toml`;
        /// absent `--dir`, its parent dir is the install dir). Lets a bare-file
        /// `gh_release` adapter proof without staging an extracted install.
        #[arg(long)]
        manifest: Option<String>,
    },
    /// Set a `[strings]` dot-path on a **local** profile (`<adapter>:<profile>`).

+        /// adapter (F-011): the translation binary resolves here (REQ-INSTALL-11)

+        /// and the manifest defaults to `<dir>/manifest.toml`. No full extracted

+        /// install needed — proof a DEV binary straight from its build dir.

/// Sugar over editing the overlay file; refuses a shipped profile and a bare


Now the dispatch. Read it:


6004	                    println!("{}", serde_json::to_string(&value).unwrap_or_default());
6005	                }
6006	                0
6007	            }
6008	            Ok(None) => {
6009	                eprintln!("ADAPTER_STRING_UNSET:{option}: no value at '{key}'");
6010	                1
6011	            }
6012	            Err(e) => {
6013	                eprintln!("ADAPTER_STRING_FAIL:{option}: {e}");
6014	                1
6015	            }
6016	        },
6017	        // [impl->REQ-TERM-5]
6018	        AdapterCmd::DigestProof { option, sample, session } => {
6019	            cmd_adapter_digest_proof(&adapters, &option, sample.as_deref(), session.as_deref())
6020	        }
6021	        // [impl->REQ-ADAPTER-TRANSLATE-PROOF]
6022	        AdapterCmd::TranslateProof { option, event, session } => {
6023	            cmd_adapter_translate_proof(&adapters, &option, &event, session.as_deref())
6024	        }
6025	        // [impl->REQ-MANIFEST-3]
6026	        AdapterCmd::SetString { option, key, value } => {
6027	            let (adapter, profile) = spt_runtime::profile::split_option(&option);
6028	            let Some(profile) = profile else {
6029	                eprintln!(

                    println!("{}", serde_json::to_string(&value).unwrap_or_default());
                }
                0
            }
            Ok(None) => {
                eprintln!("ADAPTER_STRING_UNSET:{option}: no value at '{key}'");
                1
            }
            Err(e) => {
                eprintln!("ADAPTER_STRING_FAIL:{option}: {e}");
                1
            }
        },
        // [impl->REQ-TERM-5]
        AdapterCmd::DigestProof { option, sample, session } => {
            cmd_adapter_digest_proof(&adapters, &option, sample.as_deref(), session.as_deref())
        }
        // [impl->REQ-ADAPTER-TRANSLATE-PROOF]
        AdapterCmd::TranslateProof { option, event, session } => {
            cmd_adapter_translate_proof(&adapters, &option, &event, session.as_deref())
        }
        // [impl->REQ-MANIFEST-3]
        AdapterCmd::SetString { option, key, value } => {
            let (adapter, profile) = spt_runtime::profile::split_option(&option);
            let Some(profile) = profile else {
                eprintln!(


        // [impl->REQ-TERM-5]
        AdapterCmd::DigestProof { option, sample, session } => {
            cmd_adapter_digest_proof(&adapters, &option, sample.as_deref(), session.as_deref())
        }
        // [impl->REQ-ADAPTER-TRANSLATE-PROOF]
        AdapterCmd::TranslateProof { option, event, session } => {
            cmd_adapter_translate_proof(&adapters, &option, &event, session.as_deref())
        }

        // [impl->REQ-TERM-5]
        // [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]
        AdapterCmd::DigestProof { option, sample, session, dir, manifest } => {
            cmd_adapter_digest_proof(
                &adapters,
                &option,
                sample.as_deref(),
                session.as_deref(),
                dir.as_deref(),
                manifest.as_deref(),
            )
        }
        // [impl->REQ-ADAPTER-TRANSLATE-PROOF]
        // [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]
        AdapterCmd::TranslateProof { option, event, session, dir, manifest } => {
            cmd_adapter_translate_proof(
                &adapters,
                &option,
                &event,
                session.as_deref(),
                dir.as_deref(),
                manifest.as_deref(),
            )
        }


        // [impl->REQ-TERM-5]
        AdapterCmd::DigestProof { option, sample, session } => {
            cmd_adapter_digest_proof(&adapters, &option, sample.as_deref(), session.as_deref())
        }
        // [impl->REQ-ADAPTER-TRANSLATE-PROOF]
        AdapterCmd::TranslateProof { option, event, session } => {
            cmd_adapter_translate_proof(&adapters, &option, &event, session.as_deref())
        }

        // [impl->REQ-TERM-5]
        // [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]
        AdapterCmd::DigestProof { option, sample, session, dir, manifest } => {
            cmd_adapter_digest_proof(
                &adapters,
                &option,
                sample.as_deref(),
                session.as_deref(),
                dir.as_deref(),
                manifest.as_deref(),
            )
        }
        // [impl->REQ-ADAPTER-TRANSLATE-PROOF]
        // [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]
        AdapterCmd::TranslateProof { option, event, session, dir, manifest } => {
            cmd_adapter_translate_proof(
                &adapters,
                &option,
                &event,
                session.as_deref(),
                dir.as_deref(),
                manifest.as_deref(),
            )
        }

-        AdapterCmd::DigestProof { option, sample, session } => {

-            cmd_adapter_digest_proof(&adapters, &option, sample.as_deref(), session.as_deref())

+        // [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]

+        AdapterCmd::DigestProof { option, sample, session, dir, manifest } => {

-        AdapterCmd::TranslateProof { option, event, session } => {

-            cmd_adapter_translate_proof(&adapters, &option, &event, session.as_deref())

+        AdapterCmd::TranslateProof { option, event, session, dir, manifest } => {

+            cmd_adapter_translate_proof(

AdapterCmd::SetString { option, key, value } => {


Now the shared resolver helper + update both fn signatures/bodies. Add helper above `cmd_adapter_digest_proof`:


6455	/// stays verifiable (its `.sig` is fetched via `gh` too). Any fetch/parse/
6456	/// signature failure is an error (the caller refuses the update).
6457	/// (REQ-UPD-9, REQ-ADAPTER-GH-TRANSPORT)
6458	// [impl->REQ-UPD-9]
6459	// [impl->REQ-ADAPTER-GH-TRANSPORT]
6460	fn verify_staged_archive(
6461	    adapters: &std::path::Path,
6462	    repo: &str,
6463	    tag: &str,
6464	    asset: &str,
6465	    staged: &std::path::Path,
6466	    key_hex: &str,
6467	    transport: EffectiveTransport,
6468	) -> Result<(), String> {
6469	    let key = spt_daemon::parse_verifying_key(key_hex).map_err(|e| e.to_string())?;
6470	    let bytes = std::fs::read(staged).map_err(|e| e.to_string())?;
6471	    let tag_v = format!("v{tag}");
6472	    let github = adapters.join("_github");
6473	    let sig_bytes =
6474	        fetch_release_asset_bytes(repo, Some(&tag_v), &format!("{asset}.sig"), transport, &github)?;
6475	    let sig_hex = String::from_utf8(sig_bytes).map_err(|e| e.to_string())?;
6476	    spt_daemon::verify_detached(&bytes, sig_hex.trim(), &key).map_err(|e| e.to_string())
6477	}
6478
6479	/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]` — run
6480	/// the adapter's `[digest]` extractor over a sample and surface what it produced
6481	/// and what it dropped (ADR-0019 §diagnostics, REQ-TERM-5). The author-time half
6482	/// of the shared skip-diagnostic engine (the daemon emits the same at runtime),
6483	/// so it fills the same `{id}`/`{session_id}` keys the daemon does — a
6484	/// `{session_id}`-templated extractor (the published shape) proofs here exactly
6485	/// as it runs live. Exit 1 when any line drops or nothing parses — a
6486	/// deliberately-broken extractor never reads as a silent empty.
6487	// [impl->REQ-TERM-5]
6488	fn cmd_adapter_digest_proof(
6489	    adapters: &std::path::Path,

/// stays verifiable (its `.sig` is fetched via `gh` too). Any fetch/parse/
/// signature failure is an error (the caller refuses the update).
/// (REQ-UPD-9, REQ-ADAPTER-GH-TRANSPORT)
// [impl->REQ-UPD-9]
// [impl->REQ-ADAPTER-GH-TRANSPORT]
fn verify_staged_archive(
    adapters: &std::path::Path,
    repo: &str,
    tag: &str,
    asset: &str,
    staged: &std::path::Path,
    key_hex: &str,
    transport: EffectiveTransport,
) -> Result<(), String> {
    let key = spt_daemon::parse_verifying_key(key_hex).map_err(|e| e.to_string())?;
    let bytes = std::fs::read(staged).map_err(|e| e.to_string())?;
    let tag_v = format!("v{tag}");
    let github = adapters.join("_github");
    let sig_bytes =
        fetch_release_asset_bytes(repo, Some(&tag_v), &format!("{asset}.sig"), transport, &github)?;
    let sig_hex = String::from_utf8(sig_bytes).map_err(|e| e.to_string())?;
    spt_daemon::verify_detached(&bytes, sig_hex.trim(), &key).map_err(|e| e.to_string())
}

/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]` — run
/// the adapter's `[digest]` extractor over a sample and surface what it produced
/// and what it dropped (ADR-0019 §diagnostics, REQ-TERM-5). The author-time half
/// of the shared skip-diagnostic engine (the daemon emits the same at runtime),
/// so it fills the same `{id}`/`{session_id}` keys the daemon does — a
/// `{session_id}`-templated extractor (the published shape) proofs here exactly
/// as it runs live. Exit 1 when any line drops or nothing parses — a
/// deliberately-broken extractor never reads as a silent empty.
// [impl->REQ-TERM-5]
fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,


/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]` — run
/// the adapter's `[digest]` extractor over a sample and surface what it produced
/// and what it dropped (ADR-0019 §diagnostics, REQ-TERM-5). The author-time half
/// of the shared skip-diagnostic engine (the daemon emits the same at runtime),
/// so it fills the same `{id}`/`{session_id}` keys the daemon does — a
/// `{session_id}`-templated extractor (the published shape) proofs here exactly
/// as it runs live. Exit 1 when any line drops or nothing parses — a
/// deliberately-broken extractor never reads as a silent empty.
// [impl->REQ-TERM-5]
fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,

/// Resolve the `(install_dir, manifest)` a `*-proof` command runs against. With
/// no `--dir`/`--manifest` it resolves the REGISTERED option (the normal path,
/// `source_dir` = the extracted install). With either override it reads a DEV
/// manifest+dir straight off disk so an author proofs a binary that is NOT staged
/// as a full extracted install (perri F-011 — a bare-file `gh_release` adapter
/// otherwise can't be resolved): `--dir` is the install dir (binaries resolve
/// there, REQ-INSTALL-11) and defaults the manifest to `<dir>/manifest.toml`;
/// `--manifest` pins the manifest file (and, absent `--dir`, its parent dir is the
/// install dir). Mirrors `digest-proof --sample` pointing straight at a file.
// [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]
fn resolve_proof_target(
    adapters: &std::path::Path,
    option: &str,
    dir: Option<&str>,
    manifest: Option<&str>,
) -> Result<(std::path::PathBuf, spt_runtime::manifest::Manifest), String> {
    if dir.is_none() && manifest.is_none() {
        return spt_runtime::registry::resolve_option(adapters, option)
            .map(|(r, m)| (std::path::PathBuf::from(r.source_dir), m))
            .map_err(|e| e.to_string());
    }
    // Override: read the on-disk manifest. `--manifest` pins the file; else it is
    // `<dir>/manifest.toml` (the registry convention). The install dir is `--dir`,
    // else the manifest file's parent.
    let manifest_file = match manifest {
        Some(m) => std::path::PathBuf::from(m),
        None => std::path::Path::new(dir.expect("dir present when manifest absent"))
            .join("manifest.toml"),
    };
    let install_dir = match dir {
        Some(d) => std::path::PathBuf::from(d),
        None => manifest_file
            .parent()
            .map(std::path::Path::to_path_buf)
            .unwrap_or_else(|| std::path::PathBuf::from(".")),
    };
    let toml = std::fs::read_to_string(&manifest_file)
        .map_err(|e| format!("read manifest {}: {e}", manifest_file.display()))?;
    let m = spt_runtime::manifest::Manifest::from_toml_str(&toml)
        .map_err(|e| format!("parse manifest {}: {e}", manifest_file.display()))?;
    Ok((install_dir, m))
}

/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]
/// [--dir <path>] [--manifest <file>]` — run the adapter's `[digest]` extractor
/// over a sample and surface what it produced and what it dropped (ADR-0019
/// §diagnostics, REQ-TERM-5). The author-time half of the shared skip-diagnostic
/// engine (the daemon emits the same at runtime), so it fills the same
/// `{id}`/`{session_id}` keys the daemon does — a `{session_id}`-templated
/// extractor (the published shape) proofs here exactly as it runs live. Exit 1
/// when any line drops or nothing parses — a deliberately-broken extractor never
/// reads as a silent empty. `--dir`/`--manifest` proof a DEV install off disk
/// without staging an extracted install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).
// [impl->REQ-TERM-5]
fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,


/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]` — run
/// the adapter's `[digest]` extractor over a sample and surface what it produced
/// and what it dropped (ADR-0019 §diagnostics, REQ-TERM-5). The author-time half
/// of the shared skip-diagnostic engine (the daemon emits the same at runtime),
/// so it fills the same `{id}`/`{session_id}` keys the daemon does — a
/// `{session_id}`-templated extractor (the published shape) proofs here exactly
/// as it runs live. Exit 1 when any line drops or nothing parses — a
/// deliberately-broken extractor never reads as a silent empty.
// [impl->REQ-TERM-5]
fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,

/// Resolve the `(install_dir, manifest)` a `*-proof` command runs against. With
/// no `--dir`/`--manifest` it resolves the REGISTERED option (the normal path,
/// `source_dir` = the extracted install). With either override it reads a DEV
/// manifest+dir straight off disk so an author proofs a binary that is NOT staged
/// as a full extracted install (perri F-011 — a bare-file `gh_release` adapter
/// otherwise can't be resolved): `--dir` is the install dir (binaries resolve
/// there, REQ-INSTALL-11) and defaults the manifest to `<dir>/manifest.toml`;
/// `--manifest` pins the manifest file (and, absent `--dir`, its parent dir is the
/// install dir). Mirrors `digest-proof --sample` pointing straight at a file.
// [impl->REQ-ADAPTER-PROOF-DIR-OVERRIDE]
fn resolve_proof_target(
    adapters: &std::path::Path,
    option: &str,
    dir: Option<&str>,
    manifest: Option<&str>,
) -> Result<(std::path::PathBuf, spt_runtime::manifest::Manifest), String> {
    if dir.is_none() && manifest.is_none() {
        return spt_runtime::registry::resolve_option(adapters, option)
            .map(|(r, m)| (std::path::PathBuf::from(r.source_dir), m))
            .map_err(|e| e.to_string());
    }
    // Override: read the on-disk manifest. `--manifest` pins the file; else it is
    // `<dir>/manifest.toml` (the registry convention). The install dir is `--dir`,
    // else the manifest file's parent.
    let manifest_file = match manifest {
        Some(m) => std::path::PathBuf::from(m),
        None => std::path::Path::new(dir.expect("dir present when manifest absent"))
            .join("manifest.toml"),
    };
    let install_dir = match dir {
        Some(d) => std::path::PathBuf::from(d),
        None => manifest_file
            .parent()
            .map(std::path::Path::to_path_buf)
            .unwrap_or_else(|| std::path::PathBuf::from(".")),
    };
    let toml = std::fs::read_to_string(&manifest_file)
        .map_err(|e| format!("read manifest {}: {e}", manifest_file.display()))?;
    let m = spt_runtime::manifest::Manifest::from_toml_str(&toml)
        .map_err(|e| format!("parse manifest {}: {e}", manifest_file.display()))?;
    Ok((install_dir, m))
}

/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]
/// [--dir <path>] [--manifest <file>]` — run the adapter's `[digest]` extractor
/// over a sample and surface what it produced and what it dropped (ADR-0019
/// §diagnostics, REQ-TERM-5). The author-time half of the shared skip-diagnostic
/// engine (the daemon emits the same at runtime), so it fills the same
/// `{id}`/`{session_id}` keys the daemon does — a `{session_id}`-templated
/// extractor (the published shape) proofs here exactly as it runs live. Exit 1
/// when any line drops or nothing parses — a deliberately-broken extractor never
/// reads as a silent empty. `--dir`/`--manifest` proof a DEV install off disk
/// without staging an extracted install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).
// [impl->REQ-TERM-5]
fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,

spt_daemon::verify_detached(&bytes, sig_hex.trim(), &key).map_err(|e| e.to_string())

-/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]` — run

-/// the adapter's `[digest]` extractor over a sample and surface what it produced

-/// and what it dropped (ADR-0019 §diagnostics, REQ-TERM-5). The author-time half

-/// of the shared skip-diagnostic engine (the daemon emits the same at runtime),

-/// so it fills the same `{id}`/`{session_id}` keys the daemon does — a

-/// `{session_id}`-templated extractor (the published shape) proofs here exactly

-/// as it runs live. Exit 1 when any line drops or nothing parses — a

-/// deliberately-broken extractor never reads as a silent empty.

+/// Resolve the `(install_dir, manifest)` a `*-proof` command runs against. With

+/// no `--dir`/`--manifest` it resolves the REGISTERED option (the normal path,

+/// `source_dir` = the extracted install). With either override it reads a DEV

+/// manifest+dir straight off disk so an author proofs a binary that is NOT staged

+/// as a full extracted install (perri F-011 — a bare-file `gh_release` adapter

+/// otherwise can't be resolved): `--dir` is the install dir (binaries resolve

+/// there, REQ-INSTALL-11) and defaults the manifest to `<dir>/manifest.toml`;

+/// `--manifest` pins the manifest file (and, absent `--dir`, its parent dir is the

+/// install dir). Mirrors `digest-proof --sample` pointing straight at a file.

+) -> Result<(std::path::PathBuf, spt_runtime::manifest::Manifest), String> {

+    if dir.is_none() && manifest.is_none() {

+        return spt_runtime::registry::resolve_option(adapters, option)

+            .map(|(r, m)| (std::path::PathBuf::from(r.source_dir), m))

+            .map_err(|e| e.to_string());

+    // Override: read the on-disk manifest. `--manifest` pins the file; else it is

+    // `<dir>/manifest.toml` (the registry convention). The install dir is `--dir`,

+    // else the manifest file's parent.

+    let manifest_file = match manifest {

+        Some(m) => std::path::PathBuf::from(m),

+        None => std::path::Path::new(dir.expect("dir present when manifest absent"))

+        Some(d) => std::path::PathBuf::from(d),

+            .map(std::path::Path::to_path_buf)

+            .unwrap_or_else(|| std::path::PathBuf::from(".")),

+    let toml = std::fs::read_to_string(&manifest_file)

+        .map_err(|e| format!("read manifest {}: {e}", manifest_file.display()))?;

+    let m = spt_runtime::manifest::Manifest::from_toml_str(&toml)

+        .map_err(|e| format!("parse manifest {}: {e}", manifest_file.display()))?;

+/// `spt adapter digest-proof <adapter> [--sample <log>] [--session <id>]

+/// [--dir <path>] [--manifest <file>]` — run the adapter's `[digest]` extractor

+/// over a sample and surface what it produced and what it dropped (ADR-0019

+/// §diagnostics, REQ-TERM-5). The author-time half of the shared skip-diagnostic

+/// engine (the daemon emits the same at runtime), so it fills the same

+/// `{id}`/`{session_id}` keys the daemon does — a `{session_id}`-templated

+/// extractor (the published shape) proofs here exactly as it runs live. Exit 1

+/// when any line drops or nothing parses — a deliberately-broken extractor never

+/// reads as a silent empty. `--dir`/`--manifest` proof a DEV install off disk

+/// without staging an extracted install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).


Now update the digest-proof signature + body resolution:


fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,
    option: &str,
    sample: Option<&str>,
    session: Option<&str>,
) -> i32 {
    use spt_runtime::registry;
    use std::time::Duration;

    // The record's `source_dir` is the adapter install dir — where a `.spt`
    // ships its extractor binary; resolve the bare program there before PATH so
    // proof runs exactly as the daemon does (REQ-INSTALL-11).
    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
        Err(e) => {
            eprintln!("DIGEST_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };

fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,
    option: &str,
    sample: Option<&str>,
    session: Option<&str>,
    dir: Option<&str>,
    manifest_override: Option<&str>,
) -> i32 {
    use std::time::Duration;

    // The install dir is where a `.spt` ships its extractor binary; resolve the
    // bare program there before PATH so proof runs exactly as the daemon does
    // (REQ-INSTALL-11). Registered adapter → its `source_dir`; with
    // `--dir`/`--manifest` → an on-disk DEV install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).
    let (install_dir, manifest) = match resolve_proof_target(adapters, option, dir, manifest_override) {
        Ok(pair) => pair,
        Err(e) => {
            eprintln!("DIGEST_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };


fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,
    option: &str,
    sample: Option<&str>,
    session: Option<&str>,
) -> i32 {
    use spt_runtime::registry;
    use std::time::Duration;

    // The record's `source_dir` is the adapter install dir — where a `.spt`
    // ships its extractor binary; resolve the bare program there before PATH so
    // proof runs exactly as the daemon does (REQ-INSTALL-11).
    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
        Err(e) => {
            eprintln!("DIGEST_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };

fn cmd_adapter_digest_proof(
    adapters: &std::path::Path,
    option: &str,
    sample: Option<&str>,
    session: Option<&str>,
    dir: Option<&str>,
    manifest_override: Option<&str>,
) -> i32 {
    use std::time::Duration;

    // The install dir is where a `.spt` ships its extractor binary; resolve the
    // bare program there before PATH so proof runs exactly as the daemon does
    // (REQ-INSTALL-11). Registered adapter → its `source_dir`; with
    // `--dir`/`--manifest` → an on-disk DEV install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).
    let (install_dir, manifest) = match resolve_proof_target(adapters, option, dir, manifest_override) {
        Ok(pair) => pair,
        Err(e) => {
            eprintln!("DIGEST_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };

-    // The record's `source_dir` is the adapter install dir — where a `.spt`

-    // ships its extractor binary; resolve the bare program there before PATH so

-    // proof runs exactly as the daemon does (REQ-INSTALL-11).

-    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {

-        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),

+    // The install dir is where a `.spt` ships its extractor binary; resolve the

+    // bare program there before PATH so proof runs exactly as the daemon does

+    // (REQ-INSTALL-11). Registered adapter → its `source_dir`; with

+    // `--dir`/`--manifest` → an on-disk DEV install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).

+    let (install_dir, manifest) = match resolve_proof_target(adapters, option, dir, manifest_override) {


Now translate-proof. Update signature + body + remove now-unused `registry` import:


fn cmd_adapter_translate_proof(
    adapters: &std::path::Path,
    option: &str,
    event: &str,
    session: Option<&str>,
) -> i32 {
    use spt_daemon::translation::{key_to_bytes, KeyCmd, ToBinary, TranslationChild};
    use spt_runtime::registry;
    use std::sync::mpsc::{channel, RecvTimeoutError};
    use std::time::{Duration, Instant};

fn cmd_adapter_translate_proof(
    adapters: &std::path::Path,
    option: &str,
    event: &str,
    session: Option<&str>,
    dir: Option<&str>,
    manifest_override: Option<&str>,
) -> i32 {
    use spt_daemon::translation::{key_to_bytes, KeyCmd, ToBinary, TranslationChild};
    use std::sync::mpsc::{channel, RecvTimeoutError};
    use std::time::{Duration, Instant};


fn cmd_adapter_translate_proof(
    adapters: &std::path::Path,
    option: &str,
    event: &str,
    session: Option<&str>,
) -> i32 {
    use spt_daemon::translation::{key_to_bytes, KeyCmd, ToBinary, TranslationChild};
    use spt_runtime::registry;
    use std::sync::mpsc::{channel, RecvTimeoutError};
    use std::time::{Duration, Instant};

fn cmd_adapter_translate_proof(
    adapters: &std::path::Path,
    option: &str,
    event: &str,
    session: Option<&str>,
    dir: Option<&str>,
    manifest_override: Option<&str>,
) -> i32 {
    use spt_daemon::translation::{key_to_bytes, KeyCmd, ToBinary, TranslationChild};
    use std::sync::mpsc::{channel, RecvTimeoutError};
    use std::time::{Duration, Instant};


    // The record's `source_dir` is the adapter install dir — where a `.spt` ships
    // its translation binary; resolve the declared `path` there before PATH so the
    // proof spawns exactly as the daemon does (REQ-INSTALL-11), mirroring the
    // daemon's harnesshost path resolution.
    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
        Err(e) => {
            eprintln!("TRANSLATE_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };

    // The install dir is where a `.spt` ships its translation binary; resolve the
    // declared `path` there before PATH so the proof spawns exactly as the daemon
    // does (REQ-INSTALL-11), mirroring the daemon's harnesshost path resolution.
    // Registered adapter → its `source_dir`; with `--dir`/`--manifest` → an on-disk
    // DEV install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).
    let (install_dir, manifest) = match resolve_proof_target(adapters, option, dir, manifest_override) {
        Ok(pair) => pair,
        Err(e) => {
            eprintln!("TRANSLATE_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };


    // The record's `source_dir` is the adapter install dir — where a `.spt` ships
    // its translation binary; resolve the declared `path` there before PATH so the
    // proof spawns exactly as the daemon does (REQ-INSTALL-11), mirroring the
    // daemon's harnesshost path resolution.
    let (install_dir, manifest) = match registry::resolve_option(adapters, option) {
        Ok((r, m)) => (std::path::PathBuf::from(r.source_dir), m),
        Err(e) => {
            eprintln!("TRANSLATE_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };

    // The install dir is where a `.spt` ships its translation binary; resolve the
    // declared `path` there before PATH so the proof spawns exactly as the daemon
    // does (REQ-INSTALL-11), mirroring the daemon's harnesshost path resolution.
    // Registered adapter → its `source_dir`; with `--dir`/`--manifest` → an on-disk
    // DEV install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).
    let (install_dir, manifest) = match resolve_proof_target(adapters, option, dir, manifest_override) {
        Ok(pair) => pair,
        Err(e) => {
            eprintln!("TRANSLATE_PROOF_FAIL:{option}: {e}");
            return 1;
        }
    };

// Per-recv poll slice — short so an idle stretch is noticed promptly.

const DRAIN_POLL: Duration = Duration::from_millis(250);

-    // The record's `source_dir` is the adapter install dir — where a `.spt` ships

-    // its translation binary; resolve the declared `path` there before PATH so the

-    // proof spawns exactly as the daemon does (REQ-INSTALL-11), mirroring the

-    // daemon's harnesshost path resolution.

+    // The install dir is where a `.spt` ships its translation binary; resolve the

+    // declared `path` there before PATH so the proof spawns exactly as the daemon

+    // does (REQ-INSTALL-11), mirroring the daemon's harnesshost path resolution.

+    // Registered adapter → its `source_dir`; with `--dir`/`--manifest` → an on-disk

+    // DEV install (REQ-ADAPTER-PROOF-DIR-OVERRIDE).


Now fix existing parse() tests that destructure these variants. Read them:


9400	                    path: None,
9401	                    github: None,
9402	                    release: Some(_),
9403	                    tag: Some(_),
9404	                    ..
9405	                }
9406	            }
9407	        ));
9408	        match parse(&["spt", "adapter", "remove", "mock-shell", "--force"])
9409	            .unwrap()
9410	            .cmd
9411	        .unwrap()
9412	        {
9413	            Cmd::Adapter {
9414	                action: AdapterCmd::Remove { name, force },
9415	            } => {
9416	                assert_eq!(name, "mock-shell");
9417	                assert!(force);
9418	            }
9419	            _ => panic!("expected Adapter Remove"),
9420	        }
9421	        assert!(matches!(
9422	            parse(&["spt", "adapter", "list"]).unwrap().cmd.unwrap(),
9423	            Cmd::Adapter {
9424	                action: AdapterCmd::List
9425	            }
9426	        ));
9427	        match parse(&["spt", "adapter", "create-profile", "claude-spt", "work", "--from", "o.toml"])
9428	            .unwrap()
9429	            .cmd
9430	        .unwrap()
9431	        {
9432	            Cmd::Adapter {
9433	                action: AdapterCmd::CreateProfile { adapter, name, from },
9434	            } => {
9435	                assert_eq!((adapter.as_str(), name.as_str()), ("claude-spt", "work"));
9436	                assert_eq!(from.as_deref(), Some("o.toml"));
9437	            }
9438	            _ => panic!("expected Adapter CreateProfile"),
9439	        }
9440	        assert!(matches!(
9441	            parse(&["spt", "adapter", "delete-profile", "claude-spt", "work"]).unwrap().cmd.unwrap(),
9442	            Cmd::Adapter {
9443	                action: AdapterCmd::DeleteProfile { .. }
9444	            }
9445	        ));
9446	        match parse(&["spt", "adapter", "digest-proof", "claude-spt", "--sample", "log.jsonl"])
9447	            .unwrap()
9448	            .cmd
9449	        .unwrap()
9450	        {
9451	            Cmd::Adapter {
9452	                action: AdapterCmd::DigestProof { option, sample, session },
9453	            } => {
9454	                assert_eq!(option, "claude-spt");
9455	                assert_eq!(sample.as_deref(), Some("log.jsonl"));
9456	                assert_eq!(session, None);
9457	            }
9458	            _ => panic!("expected Adapter DigestProof"),
9459	        }
9460	    }
9461
9462	    // [unit->REQ-TERM-5] digest-proof runs the declared extractor over a sample
9463	    // and gates on the result: a clean contract sample passes (0); a sample with a
9464	    // non-contract line surfaces the drop and fails (1) — never a silent empty.
9465	    #[test]
9466	    fn adapter_digest_proof_gates_on_drops() {
9467	        #[cfg(unix)]
9468	        const PASSTHROUGH: &str = "cat";
9469	        #[cfg(windows)]
9470	        const PASSTHROUGH: &str = "cmd /C findstr /r \".\"";
9471
9472	        let _h = crate::testutil::isolated_home();
9473	        let adapters = spt_store::perch::adapters_dir();
9474	        // A harness adapter whose [digest] extractor passes the source through.
9475	        let manifest = format!(
9476	            "[adapter]\nname = \"cc\"\nkind = \"harness\"\nversion = \"1\"\n\
9477	             min_spt_core_version = \"0\"\n\n[digest]\nextractor = '{PASSTHROUGH}'\nsource = 'unused'\n"
9478	        );
9479	        let d = spt_store::perch::spt_home().join("srcs").join("cc");
9480	        std::fs::create_dir_all(&d).unwrap();
9481	        std::fs::write(d.join("manifest.toml"), manifest).unwrap();
9482	        spt_runtime::registry::register(&adapters, &d, 1).unwrap();
9483
9484	        let proof = |sample: &std::path::Path| {
9485	            cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None)
9486	        };
9487
9488	        // A clean contract sample → proof passes.
9489	        let good = spt_store::perch::spt_home().join("good.jsonl");

                    path: None,
                    github: None,
                    release: Some(_),
                    tag: Some(_),
                    ..
                }
            }
        ));
        match parse(&["spt", "adapter", "remove", "mock-shell", "--force"])
            .unwrap()
            .cmd
        .unwrap()
        {
            Cmd::Adapter {
                action: AdapterCmd::Remove { name, force },
            } => {
                assert_eq!(name, "mock-shell");
                assert!(force);
            }
            _ => panic!("expected Adapter Remove"),
        }
        assert!(matches!(
            parse(&["spt", "adapter", "list"]).unwrap().cmd.unwrap(),
            Cmd::Adapter {
                action: AdapterCmd::List
            }
        ));
        match parse(&["spt", "adapter", "create-profile", "claude-spt", "work", "--from", "o.toml"])
            .unwrap()
            .cmd
        .unwrap()
        {
            Cmd::Adapter {
                action: AdapterCmd::CreateProfile { adapter, name, from },
            } => {
                assert_eq!((adapter.as_str(), name.as_str()), ("claude-spt", "work"));
                assert_eq!(from.as_deref(), Some("o.toml"));
            }
            _ => panic!("expected Adapter CreateProfile"),
        }
        assert!(matches!(
            parse(&["spt", "adapter", "delete-profile", "claude-spt", "work"]).unwrap().cmd.unwrap(),
            Cmd::Adapter {
                action: AdapterCmd::DeleteProfile { .. }
            }
        ));
        match parse(&["spt", "adapter", "digest-proof", "claude-spt", "--sample", "log.jsonl"])
            .unwrap()
            .cmd
        .unwrap()
        {
            Cmd::Adapter {
                action: AdapterCmd::DigestProof { option, sample, session },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(sample.as_deref(), Some("log.jsonl"));
                assert_eq!(session, None);
            }
            _ => panic!("expected Adapter DigestProof"),
        }
    }

    // [unit->REQ-TERM-5] digest-proof runs the declared extractor over a sample
    // and gates on the result: a clean contract sample passes (0); a sample with a
    // non-contract line surfaces the drop and fails (1) — never a silent empty.
    #[test]
    fn adapter_digest_proof_gates_on_drops() {
        #[cfg(unix)]
        const PASSTHROUGH: &str = "cat";
        #[cfg(windows)]
        const PASSTHROUGH: &str = "cmd /C findstr /r \".\"";

        let _h = crate::testutil::isolated_home();
        let adapters = spt_store::perch::adapters_dir();
        // A harness adapter whose [digest] extractor passes the source through.
        let manifest = format!(
            "[adapter]\nname = \"cc\"\nkind = \"harness\"\nversion = \"1\"\n\
             min_spt_core_version = \"0\"\n\n[digest]\nextractor = '{PASSTHROUGH}'\nsource = 'unused'\n"
        );
        let d = spt_store::perch::spt_home().join("srcs").join("cc");
        std::fs::create_dir_all(&d).unwrap();
        std::fs::write(d.join("manifest.toml"), manifest).unwrap();
        spt_runtime::registry::register(&adapters, &d, 1).unwrap();

        let proof = |sample: &std::path::Path| {
            cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None)
        };

        // A clean contract sample → proof passes.
        let good = spt_store::perch::spt_home().join("good.jsonl");


Fix the DigestProof destructure (add `..`):


                action: AdapterCmd::DigestProof { option, sample, session },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(sample.as_deref(), Some("log.jsonl"));
                assert_eq!(session, None);
            }
            _ => panic!("expected Adapter DigestProof"),

                action: AdapterCmd::DigestProof { option, sample, session, dir, manifest },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(sample.as_deref(), Some("log.jsonl"));
                assert_eq!(session, None);
                assert_eq!(dir, None);
                assert_eq!(manifest, None);
            }
            _ => panic!("expected Adapter DigestProof"),


                action: AdapterCmd::DigestProof { option, sample, session },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(sample.as_deref(), Some("log.jsonl"));
                assert_eq!(session, None);
            }
            _ => panic!("expected Adapter DigestProof"),

                action: AdapterCmd::DigestProof { option, sample, session, dir, manifest },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(sample.as_deref(), Some("log.jsonl"));
                assert_eq!(session, None);
                assert_eq!(dir, None);
                assert_eq!(manifest, None);
            }
            _ => panic!("expected Adapter DigestProof"),

-                action: AdapterCmd::DigestProof { option, sample, session },

+                action: AdapterCmd::DigestProof { option, sample, session, dir, manifest },

+                assert_eq!(manifest, None);


Now find current line numbers for all cmd_* call sites in tests + the TranslateProof destructures:


crates\spt\src\cli.rs:6031:        AdapterCmd::TranslateProof { option, event, session, dir, manifest } => {
crates\spt\src\cli.rs:9487:            cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None)
crates\spt\src\cli.rs:9516:            cmd_adapter_digest_proof(&adapters, "plain", Some(good.to_str().unwrap()), None),
crates\spt\src\cli.rs:9559:            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), None),
crates\spt\src\cli.rs:9565:            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), Some("sessA")),
crates\spt\src\cli.rs:9589:                action: AdapterCmd::TranslateProof { option, event, session },
crates\spt\src\cli.rs:9613:                action: AdapterCmd::TranslateProof { session, .. },
crates\spt\src\cli.rs:9681:            cmd_adapter_translate_proof(&adapters, "cc", event, None),
crates\spt\src\cli.rs:9689:            cmd_adapter_translate_proof(&adapters, "cc", event, Some("sessA")),
crates\spt\src\cli.rs:9703:            cmd_adapter_translate_proof(&adapters, "plain", event, None),


Update all test call sites (+`None, None`) and the remaining TranslateProof destructure:


cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None)

cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None, None, None)


let proof = |sample: &std::path::Path| {

-            cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None)

+            cmd_adapter_digest_proof(&adapters, "cc", Some(sample.to_str().unwrap()), None, None, None)

// A clean contract sample → proof passes.


cmd_adapter_digest_proof(&adapters, "plain", Some(good.to_str().unwrap()), None),

cmd_adapter_digest_proof(&adapters, "plain", Some(good.to_str().unwrap()), None, None, None),


-            cmd_adapter_digest_proof(&adapters, "plain", Some(good.to_str().unwrap()), None),

+            cmd_adapter_digest_proof(&adapters, "plain", Some(good.to_str().unwrap()), None, None, None),


cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), None),

cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), None, None, None),


// BEFORE F-004: this hard-failed with "no value for substitution key

// {session_id}". Now the default placeholder resolves and the proof runs.

-            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), None),

+            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), None, None, None),

"a session_id-templated extractor proofs with the default placeholder"


cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), Some("sessA")),

cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), Some("sessA"), None, None),


// An explicit --session pins the id (same successful path).

-            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), Some("sessA")),

+            cmd_adapter_digest_proof(&adapters, "cc", Some(good.to_str().unwrap()), Some("sessA"), None, None),

"--session pins the id and still proofs"


cmd_adapter_translate_proof(&adapters, "cc", event, None),

cmd_adapter_translate_proof(&adapters, "cc", event, None, None, None),


// Default (choreo) mode → the fixture emits a committed sequence → pass.

-            cmd_adapter_translate_proof(&adapters, "cc", event, None),

+            cmd_adapter_translate_proof(&adapters, "cc", event, None, None, None),


cmd_adapter_translate_proof(&adapters, "cc", event, Some("sessA")),

cmd_adapter_translate_proof(&adapters, "cc", event, Some("sessA"), None, None),


// nocommit mode → commands emitted but no `{commit}` → fail (1).

-            cmd_adapter_translate_proof(&adapters, "cc", event, Some("sessA")),

+            cmd_adapter_translate_proof(&adapters, "cc", event, Some("sessA"), None, None),

"a sequence with no terminating commit fails the proof"


cmd_adapter_translate_proof(&adapters, "plain", event, None),

cmd_adapter_translate_proof(&adapters, "plain", event, None, None, None),


-            cmd_adapter_translate_proof(&adapters, "plain", event, None),

+            cmd_adapter_translate_proof(&adapters, "plain", event, None, None, None),

"no [message-idle-translation-binary] section → exit 2"


Now the TranslateProof destructure at 9589:


9580	            "claude-spt",
9581	            "--event",
9582	            "<EVENT type=\"msg\"/>",
9583	        ])
9584	        .unwrap()
9585	        .cmd
9586	        .unwrap()
9587	        {
9588	            Cmd::Adapter {
9589	                action: AdapterCmd::TranslateProof { option, event, session },
9590	            } => {
9591	                assert_eq!(option, "claude-spt");
9592	                assert_eq!(event, "<EVENT type=\"msg\"/>");
9593	                assert_eq!(session, None);
9594	            }
9595	            _ => panic!("expected Adapter TranslateProof"),
9596	        }
9597	        // `--session` pins the id; `--event` carrying `{id}`/`{session_id}` tokens.
9598	        match parse(&[
9599	            "spt",
9600	            "adapter",
9601	            "translate-proof",
9602	            "claude-spt",
9603	            "--event",
9604	            "<EVENT to=\"{id}\" sid=\"{session_id}\"/>",
9605	            "--session",
9606	            "sessA",
9607	        ])
9608	        .unwrap()
9609	        .cmd
9610	        .unwrap()
9611	        {
9612	            Cmd::Adapter {
9613	                action: AdapterCmd::TranslateProof { session, .. },
9614	            } => assert_eq!(session.as_deref(), Some("sessA")),
9615	            _ => panic!("expected Adapter TranslateProof"),
9616	        }
9617	        // `--event` is required: omitting it errors.
9618	        assert!(
9619	            parse(&["spt", "adapter", "translate-proof", "claude-spt"]).is_err(),

            "claude-spt",
            "--event",
            "<EVENT type=\"msg\"/>",
        ])
        .unwrap()
        .cmd
        .unwrap()
        {
            Cmd::Adapter {
                action: AdapterCmd::TranslateProof { option, event, session },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(event, "<EVENT type=\"msg\"/>");
                assert_eq!(session, None);
            }
            _ => panic!("expected Adapter TranslateProof"),
        }
        // `--session` pins the id; `--event` carrying `{id}`/`{session_id}` tokens.
        match parse(&[
            "spt",
            "adapter",
            "translate-proof",
            "claude-spt",
            "--event",
            "<EVENT to=\"{id}\" sid=\"{session_id}\"/>",
            "--session",
            "sessA",
        ])
        .unwrap()
        .cmd
        .unwrap()
        {
            Cmd::Adapter {
                action: AdapterCmd::TranslateProof { session, .. },
            } => assert_eq!(session.as_deref(), Some("sessA")),
            _ => panic!("expected Adapter TranslateProof"),
        }
        // `--event` is required: omitting it errors.
        assert!(
            parse(&["spt", "adapter", "translate-proof", "claude-spt"]).is_err(),


                action: AdapterCmd::TranslateProof { option, event, session },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(event, "<EVENT type=\"msg\"/>");
                assert_eq!(session, None);
            }

                action: AdapterCmd::TranslateProof { option, event, session, dir, manifest },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(event, "<EVENT type=\"msg\"/>");
                assert_eq!(session, None);
                assert_eq!(dir, None);
                assert_eq!(manifest, None);
            }


                action: AdapterCmd::TranslateProof { option, event, session },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(event, "<EVENT type=\"msg\"/>");
                assert_eq!(session, None);
            }

                action: AdapterCmd::TranslateProof { option, event, session, dir, manifest },
            } => {
                assert_eq!(option, "claude-spt");
                assert_eq!(event, "<EVENT type=\"msg\"/>");
                assert_eq!(session, None);
                assert_eq!(dir, None);
                assert_eq!(manifest, None);
            }

-                action: AdapterCmd::TranslateProof { option, event, session },

+                action: AdapterCmd::TranslateProof { option, event, session, dir, manifest },

+                assert_eq!(manifest, None);

_ => panic!("expected Adapter TranslateProof"),


Build the spt crate (tests) to catch remaining call sites/errors:


PowerShell

cargo build -p spt --bin spt --tests 2>&1 | Select-String -Pattern "error|warning: unused|cmd_adapter|resolve_proof|Finished" | Select-Object -First 40; "EXIT=$LASTEXITCODE"

Compile spt bin + tests


    Finished `dev` profile [unoptimized + debuginfo] target(s) in 20.14s
EXIT=0


Compiles clean. Check for existing proof integration tests to mirror with `--dir`:


Bash

grep -rln "digest-proof\|translate-proof\|digest_proof\|translate_proof" --include=*.rs crates/*/tests/ 2>/dev/null; echo "---existing int files---"; ls crates/spt/tests/ | grep -i "proof\|digest\|translate"

Find existing proof integration tests


crates/spt/tests/fixtures/translate_proof_fixture.rs
crates/spt/tests/live_adapt_translation_swap_e2e.rs
crates/spt/tests/translate_proof.rs
---existing int files---
translate_proof.rs


1	//! End-to-end int gate for `spt adapter translate-proof` (ADR-0022,
2	//! REQ-ADAPTER-TRANSLATE-PROOF): the author-time EMIT-half proof for
3	//! `[message-idle-translation-binary]`.
4	//!
5	//! This drives the REAL `spt` binary through the REAL command, against a REAL
6	//! external translation binary (`translate_proof_fixture`, a sibling `[[bin]]` in
7	//! this crate). No mocks: `translate-proof` spawns and feeds the fixture exactly
8	//! as the daemon does (the `init` line then the `--event` envelope), reads back
9	//! its emitted keystroke-command stream, prints it, and gates on the result. The
10	//! fixture's choreography (`ctrl+s` / delay / `text:XLATE_OK` / enter / delay /
11	//! `ctrl+s` / `ctrl+q` / `commit`) is the EMIT half of the W2 idle-delivery seam.
12
13	use std::path::PathBuf;
14	use std::process::Command;
15	use std::time::Duration;
16
17	mod common;
18	use common::CommandNoWindowExt;
19
20	use spt_store::perch;
21
22	/// Absolute path to a sibling binary in the same target dir as the `spt` test bin.
23	fn sibling_bin(name: &str) -> PathBuf {
24	    PathBuf::from(env!("CARGO_BIN_EXE_spt"))
25	        .with_file_name(format!("{name}{}", std::env::consts::EXE_SUFFIX))
26	}
27
28	/// Run `spt <args...>` under an isolated `SPT_HOME`, bounded, capturing output.
29	fn run_spt(
30	    spt_bin: &std::path::Path,
31	    home: &std::path::Path,
32	    args: &[&str],
33	    fixture_mode: Option<&str>,
34	) -> std::process::Output {
35	    let mut cmd = Command::new(spt_bin);
36	    cmd.no_window().args(args).env("SPT_HOME", home);
37	    if let Some(mode) = fixture_mode {
38	        cmd.env("XLATE_FIXTURE_MODE", mode);
39	    } else {
40	        cmd.env_remove("XLATE_FIXTURE_MODE");
41	    }
42	    cmd.output().expect("spawn spt adapter translate-proof")
43	}
44
45	// [int->REQ-ADAPTER-TRANSLATE-PROOF] the real command drives the real external
46	// translation binary end-to-end: the committed choreography proofs (exit 0 + the
47	// emitted stream printed), the `nocommit` variant of the SAME binary fails (exit
48	// 1, NO_COMMIT), and an adapter declaring no section exits 2.
49	#[test]
50	fn translate_proof_drives_the_real_translation_binary() {
51	    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
52	    let fixture = sibling_bin("translate_proof_fixture");
53	    assert!(
54	        fixture.exists(),
55	        "the translation-binary fixture must be built: {} \
56	         (cargo test -p spt builds it)",
57	        fixture.display()
58	    );
59
60	    let home = tempfile::tempdir().unwrap();
61
62	    // Register a harness adapter declaring [message-idle-translation-binary] with
63	    // the absolute fixture path. Register via the lib API into this home's adapter
64	    // registry (the child `spt` reads the same SPT_HOME).
65	    std::env::set_var("SPT_HOME", home.path());
66	    let adapters = perch::adapters_dir();
67	    let fixture_for_toml = fixture.to_string_lossy().replace('\\', "/");
68	    let manifest = format!(
69	        "[adapter]\nname = \"cc\"\nkind = \"harness\"\nversion = \"1\"\n\
70	         min_spt_core_version = \"0\"\n\n\
71	         [message-idle-translation-binary]\npath = '{fixture_for_toml}'\n"
72	    );
73	    let src = perch::spt_home().join("srcs").join("cc");
74	    std::fs::create_dir_all(&src).unwrap();
75	    std::fs::write(src.join("manifest.toml"), &manifest).unwrap();
76	    spt_runtime::registry::register(&adapters, &src, 1).unwrap();
77
78	    // A second adapter with NO translation-binary section (for the exit-2 case).
79	    let plain = "[adapter]\nname = \"plain\"\nkind = \"harness\"\nversion = \"1\"\n\
80	                 min_spt_core_version = \"0\"\n";
81	    let psrc = perch::spt_home().join("srcs").join("plain");
82	    std::fs::create_dir_all(&psrc).unwrap();
83	    std::fs::write(psrc.join("manifest.toml"), plain).unwrap();
84	    spt_runtime::registry::register(&adapters, &psrc, 1).unwrap();
85	    std::env::remove_var("SPT_HOME");
86
87	    let event = "<EVENT type=\"msg\" to=\"{id}\" sid=\"{session_id}\"/>";
88
89	    // ── Happy path: committed choreography → exit 0, stream printed. ──
90	    let out = run_spt(
91	        &spt_bin,
92	        home.path(),
93	        &["adapter", "translate-proof", "cc", "--event", event],
94	        None,
95	    );
96	    let stdout = String::from_utf8_lossy(&out.stdout);
97	    let stderr = String::from_utf8_lossy(&out.stderr);
98	    assert!(
99	        out.status.success(),
100	        "committed choreography must proof (exit 0).\nstdout:\n{stdout}\nstderr:\n{stderr}"
101	    );
102	    // The EMIT half is visible: the payload text and the commit are reported.
103	    assert!(
104	        stdout.contains("XLATE_OK"),
105	        "the emitted payload text must be surfaced.\nstdout:\n{stdout}"
106	    );
107	    assert!(
108	        stdout.contains("commit: yes"),
109	        "a committed sequence reports commit: yes.\nstdout:\n{stdout}"
110	    );
111	    assert!(
112	        stderr.contains("TRANSLATE_PROOF_OK:cc"),
113	        "the OK marker is emitted on stderr.\nstderr:\n{stderr}"
114	    );
115
116	    // ── No-commit variant of the SAME binary → exit 1, NO_COMMIT. ──
117	    let out = run_spt(
118	        &spt_bin,
119	        home.path(),
120	        &["adapter", "translate-proof", "cc", "--event", event, "--session", "sessA"],
121	        Some("nocommit"),
122	    );
123	    let stderr = String::from_utf8_lossy(&out.stderr);
124	    assert_eq!(
125	        out.status.code(),
126	        Some(1),
127	        "a sequence with no terminating commit fails (exit 1).\nstderr:\n{stderr}"
128	    );
129	    assert!(
130	        stderr.contains("TRANSLATE_PROOF_NO_COMMIT:cc"),
131	        "the no-commit gate marker is emitted.\nstderr:\n{stderr}"
132	    );
133
134	    // ── No section → exit 2. ──
135	    let out = run_spt(
136	        &spt_bin,
137	        home.path(),
138	        &["adapter", "translate-proof", "plain", "--event", event],
139	        None,
140	    );
141	    let stderr = String::from_utf8_lossy(&out.stderr);
142	    assert_eq!(
143	        out.status.code(),
144	        Some(2),
145	        "no [message-idle-translation-binary] section → exit 2.\nstderr:\n{stderr}"
146	    );
147	    assert!(
148	        stderr.contains("TRANSLATE_PROOF_NO_SECTION:plain"),
149	        "the no-section marker is emitted.\nstderr:\n{stderr}"
150	    );
151
152	    // Sanity: drain budget keeps the whole gate well under any wedge timeout.
153	    let _ = Duration::from_secs(5);
154	}
155

//! End-to-end int gate for `spt adapter translate-proof` (ADR-0022,
//! REQ-ADAPTER-TRANSLATE-PROOF): the author-time EMIT-half proof for
//! `[message-idle-translation-binary]`.
//!
//! This drives the REAL `spt` binary through the REAL command, against a REAL
//! external translation binary (`translate_proof_fixture`, a sibling `[[bin]]` in
//! this crate). No mocks: `translate-proof` spawns and feeds the fixture exactly
//! as the daemon does (the `init` line then the `--event` envelope), reads back
//! its emitted keystroke-command stream, prints it, and gates on the result. The
//! fixture's choreography (`ctrl+s` / delay / `text:XLATE_OK` / enter / delay /
//! `ctrl+s` / `ctrl+q` / `commit`) is the EMIT half of the W2 idle-delivery seam.

use std::path::PathBuf;
use std::process::Command;
use std::time::Duration;

mod common;
use common::CommandNoWindowExt;

use spt_store::perch;

/// Absolute path to a sibling binary in the same target dir as the `spt` test bin.
fn sibling_bin(name: &str) -> PathBuf {
    PathBuf::from(env!("CARGO_BIN_EXE_spt"))
        .with_file_name(format!("{name}{}", std::env::consts::EXE_SUFFIX))
}

/// Run `spt <args...>` under an isolated `SPT_HOME`, bounded, capturing output.
fn run_spt(
    spt_bin: &std::path::Path,
    home: &std::path::Path,
    args: &[&str],
    fixture_mode: Option<&str>,
) -> std::process::Output {
    let mut cmd = Command::new(spt_bin);
    cmd.no_window().args(args).env("SPT_HOME", home);
    if let Some(mode) = fixture_mode {
        cmd.env("XLATE_FIXTURE_MODE", mode);
    } else {
        cmd.env_remove("XLATE_FIXTURE_MODE");
    }
    cmd.output().expect("spawn spt adapter translate-proof")
}

// [int->REQ-ADAPTER-TRANSLATE-PROOF] the real command drives the real external
// translation binary end-to-end: the committed choreography proofs (exit 0 + the
// emitted stream printed), the `nocommit` variant of the SAME binary fails (exit
// 1, NO_COMMIT), and an adapter declaring no section exits 2.
#[test]
fn translate_proof_drives_the_real_translation_binary() {
    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
    let fixture = sibling_bin("translate_proof_fixture");
    assert!(
        fixture.exists(),
        "the translation-binary fixture must be built: {} \
         (cargo test -p spt builds it)",
        fixture.display()
    );

    let home = tempfile::tempdir().unwrap();

    // Register a harness adapter declaring [message-idle-translation-binary] with
    // the absolute fixture path. Register via the lib API into this home's adapter
    // registry (the child `spt` reads the same SPT_HOME).
    std::env::set_var("SPT_HOME", home.path());
    let adapters = perch::adapters_dir();
    let fixture_for_toml = fixture.to_string_lossy().replace('\\', "/");
    let manifest = format!(
        "[adapter]\nname = \"cc\"\nkind = \"harness\"\nversion = \"1\"\n\
         min_spt_core_version = \"0\"\n\n\
         [message-idle-translation-binary]\npath = '{fixture_for_toml}'\n"
    );
    let src = perch::spt_home().join("srcs").join("cc");
    std::fs::create_dir_all(&src).unwrap();
    std::fs::write(src.join("manifest.toml"), &manifest).unwrap();
    spt_runtime::registry::register(&adapters, &src, 1).unwrap();

    // A second adapter with NO translation-binary section (for the exit-2 case).
    let plain = "[adapter]\nname = \"plain\"\nkind = \"harness\"\nversion = \"1\"\n\
                 min_spt_core_version = \"0\"\n";
    let psrc = perch::spt_home().join("srcs").join("plain");
    std::fs::create_dir_all(&psrc).unwrap();
    std::fs::write(psrc.join("manifest.toml"), plain).unwrap();
    spt_runtime::registry::register(&adapters, &psrc, 1).unwrap();
    std::env::remove_var("SPT_HOME");

    let event = "<EVENT type=\"msg\" to=\"{id}\" sid=\"{session_id}\"/>";

    // ── Happy path: committed choreography → exit 0, stream printed. ──
    let out = run_spt(
        &spt_bin,
        home.path(),
        &["adapter", "translate-proof", "cc", "--event", event],
        None,
    );
    let stdout = String::from_utf8_lossy(&out.stdout);
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        out.status.success(),
        "committed choreography must proof (exit 0).\nstdout:\n{stdout}\nstderr:\n{stderr}"
    );
    // The EMIT half is visible: the payload text and the commit are reported.
    assert!(
        stdout.contains("XLATE_OK"),
        "the emitted payload text must be surfaced.\nstdout:\n{stdout}"
    );
    assert!(
        stdout.contains("commit: yes"),
        "a committed sequence reports commit: yes.\nstdout:\n{stdout}"
    );
    assert!(
        stderr.contains("TRANSLATE_PROOF_OK:cc"),
        "the OK marker is emitted on stderr.\nstderr:\n{stderr}"
    );

    // ── No-commit variant of the SAME binary → exit 1, NO_COMMIT. ──
    let out = run_spt(
        &spt_bin,
        home.path(),
        &["adapter", "translate-proof", "cc", "--event", event, "--session", "sessA"],
        Some("nocommit"),
    );
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert_eq!(
        out.status.code(),
        Some(1),
        "a sequence with no terminating commit fails (exit 1).\nstderr:\n{stderr}"
    );
    assert!(
        stderr.contains("TRANSLATE_PROOF_NO_COMMIT:cc"),
        "the no-commit gate marker is emitted.\nstderr:\n{stderr}"
    );

    // ── No section → exit 2. ──
    let out = run_spt(
        &spt_bin,
        home.path(),
        &["adapter", "translate-proof", "plain", "--event", event],
        None,
    );
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert_eq!(
        out.status.code(),
        Some(2),
        "no [message-idle-translation-binary] section → exit 2.\nstderr:\n{stderr}"
    );
    assert!(
        stderr.contains("TRANSLATE_PROOF_NO_SECTION:plain"),
        "the no-section marker is emitted.\nstderr:\n{stderr}"
    );

    // Sanity: drain budget keeps the whole gate well under any wedge timeout.
    let _ = Duration::from_secs(5);
}


Existing int pattern clear. Add the W5 int (`--dir`/`--manifest` resolves an UNREGISTERED on-disk install — the F-011 fix) to translate_proof.rs:


Edit

    // Sanity: drain budget keeps the whole gate well under any wedge timeout.
    let _ = Duration::from_secs(5);
}

    // Sanity: drain budget keeps the whole gate well under any wedge timeout.
    let _ = Duration::from_secs(5);
}

// [int->REQ-ADAPTER-PROOF-DIR-OVERRIDE] the `--dir`/`--manifest` override proofs a
// DEV install that is NOT registered (perri F-011: a bare-file adapter the registry
// can't resolve). Same real binary + real fixture as above, driven through the real
// `spt` command: an UNREGISTERED option fails to resolve WITHOUT the override, then
// proofs (exit 0, full EMIT stream) WITH `--dir`, and again with `--manifest`
// pinning the file directly — no extracted/registered install staged.
#[test]
fn translate_proof_dir_override_proofs_unregistered_install() {
    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
    let fixture = sibling_bin("translate_proof_fixture");
    assert!(
        fixture.exists(),
        "the translation-binary fixture must be built: {}",
        fixture.display()
    );

    let home = tempfile::tempdir().unwrap();

    // An on-disk DEV install dir with a manifest declaring the translation binary —
    // written, but DELIBERATELY NOT registered (no registry::register). This is the
    // bare-file shape `*-proof` could not resolve before the override.
    let devdir = home.path().join("devsrc");
    std::fs::create_dir_all(&devdir).unwrap();
    let fixture_for_toml = fixture.to_string_lossy().replace('\\', "/");
    let manifest = format!(
        "[adapter]\nname = \"dev\"\nkind = \"harness\"\nversion = \"1\"\n\
         min_spt_core_version = \"0\"\n\n\
         [message-idle-translation-binary]\npath = '{fixture_for_toml}'\n"
    );
    let manifest_file = devdir.join("manifest.toml");
    std::fs::write(&manifest_file, &manifest).unwrap();

    let event = "<EVENT type=\"msg\" to=\"{id}\" sid=\"{session_id}\"/>";
    let devdir_s = devdir.to_string_lossy().replace('\\', "/");
    let manifest_s = manifest_file.to_string_lossy().replace('\\', "/");

    // ── Control: WITHOUT the override, an unregistered option cannot resolve. ──
    let out = run_spt(
        &spt_bin,
        home.path(),
        &["adapter", "translate-proof", "dev", "--event", event],
        None,
    );
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert_eq!(
        out.status.code(),
        Some(1),
        "an unregistered adapter must fail to resolve without --dir/--manifest.\nstderr:\n{stderr}"
    );
    assert!(
        stderr.contains("TRANSLATE_PROOF_FAIL:dev"),
        "the resolve-fail marker is emitted.\nstderr:\n{stderr}"
    );

    // ── --dir <install dir>: manifest defaults to <dir>/manifest.toml → proofs. ──
    let out = run_spt(
        &spt_bin,
        home.path(),
        &["adapter", "translate-proof", "dev", "--event", event, "--dir", &devdir_s],
        None,
    );
    let stdout = String::from_utf8_lossy(&out.stdout);
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        out.status.success(),
        "--dir resolves the on-disk DEV install and proofs (exit 0).\nstdout:\n{stdout}\nstderr:\n{stderr}"
    );
    assert!(
        stdout.contains("XLATE_OK") && stderr.contains("TRANSLATE_PROOF_OK:dev"),
        "the override proof drives the real binary end-to-end.\nstdout:\n{stdout}\nstderr:\n{stderr}"
    );

    // ── --manifest <file>: pins the manifest directly (parent = install dir). ──
    let out = run_spt(
        &spt_bin,
        home.path(),
        &["adapter", "translate-proof", "dev", "--event", event, "--manifest", &manifest_s],
        None,
    );
    let stderr = String::from_utf8_lossy(&out.stderr);
    assert!(
        out.status.success() && stderr.contains("TRANSLATE_PROOF_OK:dev"),
        "--manifest pins the file and proofs the same install.\nstderr:\n{stderr}"
    );
}