/// `cwd`: the project-scoped working directory the harness PTY lands in
/// (REQ-HOST-RUN-2). `None` inherits the broker's cwd (the W1 bringup shortcut);
/// `Some(dir)` is what a project-scoped launcher (`cc <id>` at a project root)
/// passes so the harness reads the RIGHT `.claude`/session history/digest source.
// [impl->REQ-HOST-RUN-1]
// [impl->REQ-HOST-RUN-2]
pub fn launch_harness_brokered_in(
    broker_name: &str,
    endpoint_id: &str,
    adapter_name: &str,
    session_id: &str,
    manifest: &Manifest,
    cwd: Option<&str>,
) -> Result<HarnessSpawned, String> {
    let PreparedSpawn {
        tokens,
        env,
        translation_binary,
    } = prepare_harness_spawn(endpoint_id, adapter_name, session_id, manifest)?;

/// `cwd`: the project-scoped working directory the harness PTY lands in
/// (REQ-HOST-RUN-2). `None` inherits the broker's cwd (the W1 bringup shortcut);
/// `Some(dir)` is what a project-scoped launcher (`cc <id>` at a project root)
/// passes so the harness reads the RIGHT `.claude`/session history/digest source.
///
/// `is_resume`: forwarded to [`prepare_harness_spawn`] — `true` selects the
/// adapter's `[session.resume]` native-resume command (REQ-SESSION-RESUME-TEMPLATE)
/// when declared, else falls back to `[session.self]`.
// [impl->REQ-HOST-RUN-1]
// [impl->REQ-HOST-RUN-2]
// [impl->REQ-SESSION-RESUME-TEMPLATE]
pub fn launch_harness_brokered_in(
    broker_name: &str,
    endpoint_id: &str,
    adapter_name: &str,
    session_id: &str,
    manifest: &Manifest,
    cwd: Option<&str>,
    is_resume: bool,
) -> Result<HarnessSpawned, String> {
    let PreparedSpawn {
        tokens,
        env,
        translation_binary,
    } = prepare_harness_spawn(endpoint_id, adapter_name, session_id, manifest, is_resume)?;

+/// `is_resume`: forwarded to [`prepare_harness_spawn`] — `true` selects the

+/// adapter's `[session.resume]` native-resume command (REQ-SESSION-RESUME-TEMPLATE)

+/// when declared, else falls back to `[session.self]`.

-    } = prepare_harness_spawn(endpoint_id, adapter_name, session_id, manifest)?;

+    } = prepare_harness_spawn(endpoint_id, adapter_name, session_id, manifest, is_resume)?;

let (program, args) = tokens.split_first().expect("non-empty checked");

Brain::cold_start(broker_name, now_ms()).map_err(|e| format!("broker connect: {e}"))?;


Find all callers of the two changed fns.


adapters\mock\src\main.rs:45:            // `spt endpoint run` → `launch_harness_brokered_in` → broker PTY → `rc`
crates\spt\tests\dummy_harness_e2e.rs:4://! `spt endpoint run` → `launch_harness_brokered_in` → broker PTY → `spt rc` attach
crates\spt\tests\dummy_harness_e2e.rs:310:// `cmd_endpoint_run` the flow is `launch_harness_brokered_in` (returns once the
crates\spt\src\cli.rs:1318:    match spt_daemon::harnesshost::launch_harness_brokered_in(
crates\spt\src\cli.rs:1359:    // launch_harness_brokered_in returns once the harness PROCESS is spawned, but the
crates\spt-daemon\tests\broker.rs:499:/// endpoint-run's `prepare_harness_spawn` fills `[env.SPT_ENDPOINT_ID] = "{id}"`
crates\spt-daemon\tests\broker.rs:547:        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest)
crates\spt-daemon\tests\broker.rs:553:        "prepare_harness_spawn fills [env] SPT_ENDPOINT_ID = {{id}}"
crates\spt-daemon\tests\attach.rs:286:// attach, end to end: `harnesshost::launch_harness_brokered_in` spawns a harness's
crates\spt-daemon\tests\attach.rs:313:    let spawned = spt_daemon::harnesshost::launch_harness_brokered_in(
crates\spt-daemon\src\harnesshost.rs:8://! **broker-held PTY** ([`launch_harness_brokered_in`]). The harness still
crates\spt-daemon\src\harnesshost.rs:66:/// [`launch_harness_brokered_in`] is the only side effect. Fails closed: a
crates\spt-daemon\src\harnesshost.rs:78:pub fn prepare_harness_spawn(
crates\spt-daemon\src\harnesshost.rs:178:/// `is_resume`: forwarded to [`prepare_harness_spawn`] — `true` selects the
crates\spt-daemon\src\harnesshost.rs:184:pub fn launch_harness_brokered_in(
crates\spt-daemon\src\harnesshost.rs:197:    } = prepare_harness_spawn(endpoint_id, adapter_name, session_id, manifest, is_resume)?;
crates\spt-daemon\src\harnesshost.rs:236:        let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m).unwrap();
crates\spt-daemon\src\harnesshost.rs:263:        let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m).unwrap();
crates\spt-daemon\src\harnesshost.rs:284:        let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with).unwrap();
crates\spt-daemon\src\harnesshost.rs:292:        let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without).unwrap();
crates\spt-daemon\src\harnesshost.rs:308:        assert!(prepare_harness_spawn("e", "sh", "s", &shell)
crates\spt-daemon\src\harnesshost.rs:318:        assert!(prepare_harness_spawn("e", "h", "s", &no_self)
crates\spt-daemon\src\harnesshost.rs:324:        assert!(prepare_harness_spawn("e", "mock", "s", &bad)


Update callers. cmd_endpoint_run first, then test callsites.


228
229	    // [unit->REQ-HOST-RUN-1] the spt-hosted bringup is template-driven: the
230	    // endpoint id and the spawn-time session id ride argv via {id}/{session_id}
231	    // substitution into [session.self] (so the harness binds to exactly this
232	    // endpoint), tokenized program-first.
233	    #[test]
234	    fn prepare_fills_id_and_session_into_self_command() {
235	        let m = harness_manifest("mock-session --id {id} --session-id {session_id}");
236	        let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m).unwrap();
237	        assert_eq!(
238	            prepared.tokens,
239	            vec![
240	                "mock-session".to_string(),
241	                "--id".to_string(),
242	                "doyle".to_string(),
243	                "--session-id".to_string(),
244	                "sess-abc".to_string(),
245	            ]
246	        );
247	        assert!(prepared.env.is_empty(), "no [env] declared → no inject");
248	    }
249
250	    // [unit->REQ-HAZARD-ENV-SUBST] a manifest [env.<VAR>] direction=inject value
251	    // is {key}-substituted from the same catalog and returned for the spawn — so a
252	    // flagless harness gets SPT_ENDPOINT_ID populated (not empty → seed-by-PPID).
253	    #[test]
254	    fn prepare_fills_env_inject_values_with_substitution() {
255	        let m = spt_runtime::Manifest::from_toml_str(
256	            "[adapter]\nname = \"mock\"\nkind = \"harness\"\nversion = \"1\"\n\
257	             min_spt_core_version = \"0\"\n\n[session.self]\ncommand = 'claude'\n\n\
258	             [env.SPT_ENDPOINT_ID]\ndirection = \"inject\"\nvalue = \"{id}\"\n\n\
259	             [env.SPT_SESSION]\ndirection = \"inject\"\nvalue = \"sess-{session_id}\"\n\n\
260	             [env.SPT_READ_ONLY]\ndirection = \"read\"\n",
261	        )
262	        .unwrap();
263	        let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m).unwrap();
264	        assert_eq!(prepared.env.get("SPT_ENDPOINT_ID").map(String::as_str), Some("wall-b"));
265	        assert_eq!(prepared.env.get("SPT_SESSION").map(String::as_str), Some("sess-abc"));
266	        // A `read` directive injects nothing.
267	        assert!(!prepared.env.contains_key("SPT_READ_ONLY"));
268	    }
269
270	    // [unit->REQ-MSG-IDLE-TRANSLATION-BINARY] a declared
271	    // `[message-idle-translation-binary].path` is threaded into the prepared spawn
272	    // (so the broker can lifecycle-manage it, ADR-0022 / W2); an adapter that
273	    // declares none yields `translation_binary = None` (the degenerate raw-inject /
274	    // spool path).
275	    #[test]
276	    fn prepare_threads_translation_binary_path_when_declared() {
277	        // declared → carried.
278	        let with = spt_runtime::Manifest::from_toml_str(
279	            "[adapter]\nname = \"cc\"\nkind = \"harness\"\nversion = \"1\"\n\
280	             min_spt_core_version = \"0\"\n\n[session.self]\ncommand = 'claude'\n\n\
281	             [message-idle-translation-binary]\npath = \"cc-spt-idle-translate\"\n",
282	        )
283	        .unwrap();
284	        let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with).unwrap();
285	        assert_eq!(
286	            prepared.translation_binary.as_deref(),
287	            Some("cc-spt-idle-translate")
288	        );
289
290	        // absent → None.
291	        let without = harness_manifest("claude");
292	        let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without).unwrap();
293	        assert!(prepared.translation_binary.is_none());
294	    }
295
296	    // [unit->REQ-HOST-RUN-1] fails closed before any spawn: a shell-kind adapter
297	    // is refused (endpoint run hosts harnesses), a manifest with no [session.self]
298	    // is refused (nothing to bring up), and an unresolved {placeholder} errs
299	    // naming the missing key.
300	    #[test]
301	    fn prepare_fails_closed_on_wrong_kind_missing_role_and_bad_template() {
302	        // Shell-kind adapter refused.
303	        let shell = spt_runtime::Manifest::from_toml_str(
304	            "[adapter]\nname = \"sh\"\nkind = \"shell\"\nversion = \"1\"\n\
305	             min_spt_core_version = \"0\"\n\n[shell]\nspawn = 'sh'\n",
306	        )
307	        .unwrap();
308	        assert!(prepare_harness_spawn("e", "sh", "s", &shell)
309	            .unwrap_err()
310	            .contains("not a harness"));
311
312	        // Harness with no [session.self] refused.
313	        let no_self = spt_runtime::Manifest::from_toml_str(
314	            "[adapter]\nname = \"h\"\nkind = \"harness\"\nversion = \"1\"\n\
315	             min_spt_core_version = \"0\"\n",
316	        )
317	        .unwrap();
318	        assert!(prepare_harness_spawn("e", "h", "s", &no_self)
319	            .unwrap_err()
320	            .contains("no [session.self]"));
321
322	        // Unknown {placeholder} errs naming the key.
323	        let bad = harness_manifest("mock-session --id {id} --boom {not_a_key}");
324	        assert!(prepare_harness_spawn("e", "mock", "s", &bad)
325	            .unwrap_err()
326	            .contains("not_a_key"));
327	    }

    // [unit->REQ-HOST-RUN-1] the spt-hosted bringup is template-driven: the
    // endpoint id and the spawn-time session id ride argv via {id}/{session_id}
    // substitution into [session.self] (so the harness binds to exactly this
    // endpoint), tokenized program-first.
    #[test]
    fn prepare_fills_id_and_session_into_self_command() {
        let m = harness_manifest("mock-session --id {id} --session-id {session_id}");
        let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m).unwrap();
        assert_eq!(
            prepared.tokens,
            vec![
                "mock-session".to_string(),
                "--id".to_string(),
                "doyle".to_string(),
                "--session-id".to_string(),
                "sess-abc".to_string(),
            ]
        );
        assert!(prepared.env.is_empty(), "no [env] declared → no inject");
    }

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

    // [unit->REQ-MSG-IDLE-TRANSLATION-BINARY] a declared
    // `[message-idle-translation-binary].path` is threaded into the prepared spawn
    // (so the broker can lifecycle-manage it, ADR-0022 / W2); an adapter that
    // declares none yields `translation_binary = None` (the degenerate raw-inject /
    // spool path).
    #[test]
    fn prepare_threads_translation_binary_path_when_declared() {
        // declared → carried.
        let with = spt_runtime::Manifest::from_toml_str(
            "[adapter]\nname = \"cc\"\nkind = \"harness\"\nversion = \"1\"\n\
             min_spt_core_version = \"0\"\n\n[session.self]\ncommand = 'claude'\n\n\
             [message-idle-translation-binary]\npath = \"cc-spt-idle-translate\"\n",
        )
        .unwrap();
        let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with).unwrap();
        assert_eq!(
            prepared.translation_binary.as_deref(),
            Some("cc-spt-idle-translate")
        );

        // absent → None.
        let without = harness_manifest("claude");
        let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without).unwrap();
        assert!(prepared.translation_binary.is_none());
    }

    // [unit->REQ-HOST-RUN-1] fails closed before any spawn: a shell-kind adapter
    // is refused (endpoint run hosts harnesses), a manifest with no [session.self]
    // is refused (nothing to bring up), and an unresolved {placeholder} errs
    // naming the missing key.
    #[test]
    fn prepare_fails_closed_on_wrong_kind_missing_role_and_bad_template() {
        // Shell-kind adapter refused.
        let shell = spt_runtime::Manifest::from_toml_str(
            "[adapter]\nname = \"sh\"\nkind = \"shell\"\nversion = \"1\"\n\
             min_spt_core_version = \"0\"\n\n[shell]\nspawn = 'sh'\n",
        )
        .unwrap();
        assert!(prepare_harness_spawn("e", "sh", "s", &shell)
            .unwrap_err()
            .contains("not a harness"));

        // Harness with no [session.self] refused.
        let no_self = spt_runtime::Manifest::from_toml_str(
            "[adapter]\nname = \"h\"\nkind = \"harness\"\nversion = \"1\"\n\
             min_spt_core_version = \"0\"\n",
        )
        .unwrap();
        assert!(prepare_harness_spawn("e", "h", "s", &no_self)
            .unwrap_err()
            .contains("no [session.self]"));

        // Unknown {placeholder} errs naming the key.
        let bad = harness_manifest("mock-session --id {id} --boom {not_a_key}");
        assert!(prepare_harness_spawn("e", "mock", "s", &bad)
            .unwrap_err()
            .contains("not_a_key"));
    }


Add `false` to existing harnesshost test callsites (fresh path):


Edit

let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m).unwrap();

let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m, false).unwrap();


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

let m = harness_manifest("mock-session --id {id} --session-id {session_id}");

-        let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m).unwrap();

+        let prepared = prepare_harness_spawn("doyle", "mock", "sess-abc", &m, false).unwrap();


let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m).unwrap();

let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m, false).unwrap();


-        let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m).unwrap();

+        let prepared = prepare_harness_spawn("wall-b", "mock", "abc", &m, false).unwrap();


let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with).unwrap();

let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with, false).unwrap();


-        let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with).unwrap();

+        let prepared = prepare_harness_spawn("wall-a", "cc", "s", &with, false).unwrap();


let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without).unwrap();

let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without, false).unwrap();


let without = harness_manifest("claude");

-        let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without).unwrap();

+        let prepared = prepare_harness_spawn("wall-a", "mock", "s", &without, false).unwrap();


assert!(prepare_harness_spawn("e", "sh", "s", &shell)

assert!(prepare_harness_spawn("e", "sh", "s", &shell, false)


min_spt_core_version = \"0\"\n\n[shell]\nspawn = 'sh'\n",

-        assert!(prepare_harness_spawn("e", "sh", "s", &shell)

+        assert!(prepare_harness_spawn("e", "sh", "s", &shell, false)


assert!(prepare_harness_spawn("e", "h", "s", &no_self)

assert!(prepare_harness_spawn("e", "h", "s", &no_self, false)


-        assert!(prepare_harness_spawn("e", "h", "s", &no_self)

+        assert!(prepare_harness_spawn("e", "h", "s", &no_self, false)


assert!(prepare_harness_spawn("e", "mock", "s", &bad)

assert!(prepare_harness_spawn("e", "mock", "s", &bad, false)


// Unknown {placeholder} errs naming the key.

let bad = harness_manifest("mock-session --id {id} --boom {not_a_key}");

-        assert!(prepare_harness_spawn("e", "mock", "s", &bad)

+        assert!(prepare_harness_spawn("e", "mock", "s", &bad, false)


Activate REQ-SESSION-RESUME-TEMPLATE [doc,impl,unit,int]

Set required_stages in traceable-reqs.toml; registry-first before evidence.

Add [session.resume] manifest role

manifest.rs: resume: Option<SessionRole> on Session + roles() + is_empty(), mirror psyche_resume.

Add per-row cwd to session ledger

sessions.rs SessionEntry gets cwd: Option<String> additive serde-default; populate at boundary (reporting.rs) + bind (startup.rs) append sites from rec.cwd.

Resume-vs-self template selection in spawn path

harnesshost prepare_harness_spawn + launch_harness_brokered_in take is_resume; select session.resume when set+declared else self_ fallback. cmd_endpoint_run threads resume + cwd fallback chain (row->info.cwd->current_dir).

Picker: thread row cwd + title reformat

ResumeRow.cwd, Outcome::Run.cwd, resume_outcome threads cwd, data.rs maps e.cwd, run() dispatch passes cwd to cmd_endpoint_run; ResumeRow::title() local-time reformat (D).

Public docs: [session.resume] (MANIFEST + harness-contract)

docs/MANIFEST.md + harness-contract page teach [session.resume] so perri builds blind. doc->REQ tag. docs-drift regen.

Tests: unit + int (delegate spt-test-engineer)

unit: manifest parse/roundtrip resume, resume-vs-self selection, cwd fallback chain, ledger back-compat. int: dummy-harness resume cmd != self, runs in recorded cwd. Then clippy/traceable/docs-drift gates.


Now the broker.rs + attach.rs test callsites:


545	    let manifest = spt_runtime::Manifest::from_toml_str(&manifest_toml).unwrap();
546	    let prepared =
547	        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest)
548	            .expect("prepare");
549	    // F-013: the id is filled into the env value (not empty).
550	    assert_eq!(
551	        prepared.env.get("SPT_ENDPOINT_ID").map(String::as_str),
552	        Some("wall-b"),
553	        "prepare_harness_spawn fills [env] SPT_ENDPOINT_ID = {{id}}"
554	    );

    let manifest = spt_runtime::Manifest::from_toml_str(&manifest_toml).unwrap();
    let prepared =
        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest)
            .expect("prepare");
    // F-013: the id is filled into the env value (not empty).
    assert_eq!(
        prepared.env.get("SPT_ENDPOINT_ID").map(String::as_str),
        Some("wall-b"),
        "prepare_harness_spawn fills [env] SPT_ENDPOINT_ID = {{id}}"
    );


311	    ))
312	    .expect("harness manifest");
313	    let spawned = spt_daemon::harnesshost::launch_harness_brokered_in(
314	        &name_a, "cc-ep", "mock", "sess-1", &manifest, None,
315	    )
316	    .expect("spt-hosted bringup spawns into broker A's PTY");
317
318	    // Operator on B attaches CROSS-NODE to the brought-up session.
319	    let mut target = connect_retry(&name_a);
320	    let a_addr = target.net_status().expect("a status").addr;
321	    let mut operator = connect_retry(&name_b);
322	    let b_node = operator
323	        .net_status()
324	        .expect("b status")

    ))
    .expect("harness manifest");
    let spawned = spt_daemon::harnesshost::launch_harness_brokered_in(
        &name_a, "cc-ep", "mock", "sess-1", &manifest, None,
    )
    .expect("spt-hosted bringup spawns into broker A's PTY");

    // Operator on B attaches CROSS-NODE to the brought-up session.
    let mut target = connect_retry(&name_a);
    let a_addr = target.net_status().expect("a status").addr;
    let mut operator = connect_retry(&name_b);
    let b_node = operator
        .net_status()
        .expect("b status")


        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest)
            .expect("prepare");

        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest, false)
            .expect("prepare");


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

        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest)
            .expect("prepare");

        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest, false)
            .expect("prepare");

let manifest = spt_runtime::Manifest::from_toml_str(&manifest_toml).unwrap();

-        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest)

+        spt_daemon::harnesshost::prepare_harness_spawn("wall-b", "mock", "sid-1", &manifest, false)

// F-013: the id is filled into the env value (not empty).


        &name_a, "cc-ep", "mock", "sess-1", &manifest, None,
    )

        &name_a, "cc-ep", "mock", "sess-1", &manifest, None, false,
    )


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

        &name_a, "cc-ep", "mock", "sess-1", &manifest, None,
    )

        &name_a, "cc-ep", "mock", "sess-1", &manifest, None, false,
    )

let spawned = spt_daemon::harnesshost::launch_harness_brokered_in(

-        &name_a, "cc-ep", "mock", "sess-1", &manifest, None,

+        &name_a, "cc-ep", "mock", "sess-1", &manifest, None, false,

.expect("spt-hosted bringup spawns into broker A's PTY");


**cmd_endpoint_run** — add cwd param + is_resume + fallback chain. Verifying ParentHint path:


Grep

ParentHint|resolve_perch_path|pub fn read_info|pub cwd


crates\spt-store\src\perch.rs:3://! [`resolve_perch_path`]; nothing composes perch paths by hand. If the layout
crates\spt-store\src\perch.rs:212:/// **not** perch kinds: [`resolve_perch_path`] never resolves them, and
crates\spt-store\src\perch.rs:297:pub enum ParentHint<'a> {
crates\spt-store\src\perch.rs:308:pub fn resolve_perch_path(child_id: &str, hint: ParentHint<'_>) -> PathBuf {
crates\spt-store\src\perch.rs:309:    resolve_perch_path_in(&owlery_dir(), child_id, hint)
crates\spt-store\src\perch.rs:312:/// [`resolve_perch_path`] against an **explicit** owlery root rather than the
crates\spt-store\src\perch.rs:314:/// real resolver and [`resolve_perch_path`] is the canonical-root wrapper. Used
crates\spt-store\src\perch.rs:320:pub fn resolve_perch_path_in(owlery: &Path, child_id: &str, hint: ParentHint<'_>) -> PathBuf {
crates\spt-store\src\perch.rs:326:        ParentHint::Explicit(p) => p.to_string(),
crates\spt-store\src\perch.rs:327:        ParentHint::Infer => match infer_parent(child_id, kind) {
crates\spt-store\src\perch.rs:355:pub fn resolve_info_file(child_id: &str, hint: ParentHint<'_>) -> PathBuf {
crates\spt-store\src\perch.rs:356:    info_file_at(&resolve_perch_path(child_id, hint))
crates\spt-store\src\perch.rs:360:pub fn resolve_spool_db(child_id: &str, hint: ParentHint<'_>) -> PathBuf {
crates\spt-store\src\perch.rs:361:    resolve_perch_path(child_id, hint).join("spool.db")
crates\spt-store\src\perch.rs:365:pub fn resolve_ready_file(child_id: &str, hint: ParentHint<'_>) -> PathBuf {

[Showing results with pagination = limit: 15]


Edit

pub(crate) fn cmd_endpoint_run(
    adapter: &str,
    id: &str,
    resume: Option<String>,
    start: bool,
    // `--attach` is the default terminal action; the bool is accepted for flag
    // symmetry but `start`/`view` are what branch (attach = neither).
    _attach: bool,
    view: bool,
) -> i32 {

pub(crate) fn cmd_endpoint_run(
    adapter: &str,
    id: &str,
    resume: Option<String>,
    // The project cwd to land the harness PTY in. `None` ⇒ resolve via the
    // fallback chain (resumed ledger-row cwd → perch info.cwd → current_dir).
    // The picker threads the selected Resume row's recorded cwd here so a
    // cross-project resume restores the right dir (REQ-SESSION-RESUME-TEMPLATE).
    cwd: Option<String>,
    start: bool,
    // `--attach` is the default terminal action; the bool is accepted for flag
    // symmetry but `start`/`view` are what branch (attach = neither).
    _attach: bool,
    view: bool,
) -> i32 {


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)

pub(crate) fn cmd_endpoint_run(
    adapter: &str,
    id: &str,
    resume: Option<String>,
    start: bool,
    // `--attach` is the default terminal action; the bool is accepted for flag
    // symmetry but `start`/`view` are what branch (attach = neither).
    _attach: bool,
    view: bool,
) -> i32 {

pub(crate) fn cmd_endpoint_run(
    adapter: &str,
    id: &str,
    resume: Option<String>,
    // The project cwd to land the harness PTY in. `None` ⇒ resolve via the
    // fallback chain (resumed ledger-row cwd → perch info.cwd → current_dir).
    // The picker threads the selected Resume row's recorded cwd here so a
    // cross-project resume restores the right dir (REQ-SESSION-RESUME-TEMPLATE).
    cwd: Option<String>,
    start: bool,
    // `--attach` is the default terminal action; the bool is accepted for flag
    // symmetry but `start`/`view` are what branch (attach = neither).
    _attach: bool,
    view: bool,
) -> i32 {

+    // The project cwd to land the harness PTY in. `None` ⇒ resolve via the

+    // fallback chain (resumed ledger-row cwd → perch info.cwd → current_dir).

+    // The picker threads the selected Resume row's recorded cwd here so a

+    // cross-project resume restores the right dir (REQ-SESSION-RESUME-TEMPLATE).


    // A fresh bringup mints a provisional spawn-time session id; --resume carries
    // a prior one (the harness reports the real id post-spawn either way).
    let session_id = resume.unwrap_or_else(spt_daemon::harnesshost::mint_session_id);

    // A fresh bringup mints a provisional spawn-time session id; --resume carries
    // a prior one (the harness reports the real id post-spawn either way).
    // `is_resume` selects the adapter's native-resume command (REQ-SESSION-RESUME-TEMPLATE).
    let is_resume = resume.is_some();
    let session_id = resume.unwrap_or_else(spt_daemon::harnesshost::mint_session_id);


    // A fresh bringup mints a provisional spawn-time session id; --resume carries
    // a prior one (the harness reports the real id post-spawn either way).
    let session_id = resume.unwrap_or_else(spt_daemon::harnesshost::mint_session_id);

    // A fresh bringup mints a provisional spawn-time session id; --resume carries
    // a prior one (the harness reports the real id post-spawn either way).
    // `is_resume` selects the adapter's native-resume command (REQ-SESSION-RESUME-TEMPLATE).
    let is_resume = resume.is_some();
    let session_id = resume.unwrap_or_else(spt_daemon::harnesshost::mint_session_id);

+    // `is_resume` selects the adapter's native-resume command (REQ-SESSION-RESUME-TEMPLATE).

if let Err(e) = spt_daemon::daemon::ensure_running() {


Now the cwd resolution + the launch call:


    // REQ-HOST-RUN-2: land the harness PTY in the PROJECT cwd — where the user
    // invoked `spt endpoint run` (`cc <id>` at a project root) — NOT the
    // daemon's cwd. A project-scoped harness (Claude Code) reads its `.claude`,
    // session history, and digest source relative to cwd. A failure to read the
    // cwd falls back to the broker-inherited cwd (None) rather than aborting.
    let project_cwd = std::env::current_dir()
        .ok()
        .map(|p| p.to_string_lossy().into_owned());
    match spt_daemon::harnesshost::launch_harness_brokered_in(
        &spt_daemon::endpoint::broker_socket_name(),
        id,
        adapter,
        &session_id,
        &manifest,
        project_cwd.as_deref(),
    ) {

    // REQ-HOST-RUN-2: land the harness PTY in the PROJECT cwd — where the user
    // invoked `spt endpoint run` (`cc <id>` at a project root) — NOT the
    // daemon's cwd. A project-scoped harness (Claude Code) reads its `.claude`,
    // session history, and digest source relative to cwd.
    //
    // Resume cwd fallback chain (REQ-SESSION-RESUME-TEMPLATE): a caller-supplied
    // cwd (the picker threads the selected Resume row's recorded cwd) → on a
    // resume, the perch's `info.cwd` (W3 bind-cwd) → `current_dir` (today's
    // single-project behavior + pre-migration ledger rows). A fresh bringup never
    // consults `info.cwd` — it always lands in `current_dir` as before. A failure
    // to read the cwd falls back to the broker-inherited cwd (None).
    let project_cwd = cwd
        .or_else(|| {
            is_resume
                .then(|| {
                    let perch =
                        spt_store::perch::resolve_perch_path(id, spt_store::perch::ParentHint::Infer);
                    spt_store::info::read_info(&perch).and_then(|r| r.cwd)
                })
                .flatten()
        })
        .or_else(|| {
            std::env::current_dir()
                .ok()
                .map(|p| p.to_string_lossy().into_owned())
        });
    match spt_daemon::harnesshost::launch_harness_brokered_in(
        &spt_daemon::endpoint::broker_socket_name(),
        id,
        adapter,
        &session_id,
        &manifest,
        project_cwd.as_deref(),
        is_resume,
    ) {


    // REQ-HOST-RUN-2: land the harness PTY in the PROJECT cwd — where the user
    // invoked `spt endpoint run` (`cc <id>` at a project root) — NOT the
    // daemon's cwd. A project-scoped harness (Claude Code) reads its `.claude`,
    // session history, and digest source relative to cwd. A failure to read the
    // cwd falls back to the broker-inherited cwd (None) rather than aborting.
    let project_cwd = std::env::current_dir()
        .ok()
        .map(|p| p.to_string_lossy().into_owned());
    match spt_daemon::harnesshost::launch_harness_brokered_in(
        &spt_daemon::endpoint::broker_socket_name(),
        id,
        adapter,
        &session_id,
        &manifest,
        project_cwd.as_deref(),
    ) {

    // REQ-HOST-RUN-2: land the harness PTY in the PROJECT cwd — where the user
    // invoked `spt endpoint run` (`cc <id>` at a project root) — NOT the
    // daemon's cwd. A project-scoped harness (Claude Code) reads its `.claude`,
    // session history, and digest source relative to cwd.
    //
    // Resume cwd fallback chain (REQ-SESSION-RESUME-TEMPLATE): a caller-supplied
    // cwd (the picker threads the selected Resume row's recorded cwd) → on a
    // resume, the perch's `info.cwd` (W3 bind-cwd) → `current_dir` (today's
    // single-project behavior + pre-migration ledger rows). A fresh bringup never
    // consults `info.cwd` — it always lands in `current_dir` as before. A failure
    // to read the cwd falls back to the broker-inherited cwd (None).
    let project_cwd = cwd
        .or_else(|| {
            is_resume
                .then(|| {
                    let perch =
                        spt_store::perch::resolve_perch_path(id, spt_store::perch::ParentHint::Infer);
                    spt_store::info::read_info(&perch).and_then(|r| r.cwd)
                })
                .flatten()
        })
        .or_else(|| {
            std::env::current_dir()
                .ok()
                .map(|p| p.to_string_lossy().into_owned())
        });
    match spt_daemon::harnesshost::launch_harness_brokered_in(
        &spt_daemon::endpoint::broker_socket_name(),
        id,
        adapter,
        &session_id,
        &manifest,
        project_cwd.as_deref(),
        is_resume,
    ) {

-    // session history, and digest source relative to cwd. A failure to read the

-    // cwd falls back to the broker-inherited cwd (None) rather than aborting.

-    let project_cwd = std::env::current_dir()

-        .map(|p| p.to_string_lossy().into_owned());

+    // session history, and digest source relative to cwd.

+    // Resume cwd fallback chain (REQ-SESSION-RESUME-TEMPLATE): a caller-supplied

+    // cwd (the picker threads the selected Resume row's recorded cwd) → on a

+    // resume, the perch's `info.cwd` (W3 bind-cwd) → `current_dir` (today's

+    // single-project behavior + pre-migration ledger rows). A fresh bringup never

+    // consults `info.cwd` — it always lands in `current_dir` as before. A failure

+    // to read the cwd falls back to the broker-inherited cwd (None).

+                        spt_store::perch::resolve_perch_path(id, spt_store::perch::ParentHint::Infer);

+                    spt_store::info::read_info(&perch).and_then(|r| r.cwd)

+                .map(|p| p.to_string_lossy().into_owned())

// A TERSE machine line (no Rust `Some(..)` Debug leak, no "binds its


Add [session.resume] manifest role

Add per-row cwd to session ledger

Picker: thread row cwd + title reformat


Find cmd_endpoint_run callers (CLI dispatch + picker run):


crates\spt\src\picker\model.rs:82:/// `<adapter>[:profile]` address is what `cmd_endpoint_run` resolves.
crates\spt\src\picker\model.rs:309:/// fns (`cmd_endpoint_run` / `cmd_fork` / the rc pump) — no second bringup path
crates\spt\src\picker\model.rs:314:    /// `cmd_endpoint_run` (the one bringup core).
crates\spt\src\picker\model.rs:341:/// (read-only). Maps 1:1 onto `cmd_endpoint_run`'s start/attach/view flags.
crates\spt\src\picker\model.rs:660:    /// bring up via `cmd_endpoint_run` with the endpoint's recorded adapter.
crates\spt\src\picker\model.rs:716:    /// the session comes up detached — routes through `cmd_endpoint_run` like every
crates\spt\src\picker\model.rs:752:    /// prior session id (`cmd_endpoint_run --resume`). `headless` (the `h` keybind,
crates\spt\src\picker\model.rs:769:    /// and Fork (fresh id). All three converge on `cmd_endpoint_run` — one
crates\spt\src\picker\model.rs:1200:    // the id; Fork re-enters with a fresh id — all converging on cmd_endpoint_run.
crates\spt\src\picker\mod.rs:7://! routes through the EXISTING bringup fns ([`crate::cli::cmd_endpoint_run`] /
crates\spt\src\picker\mod.rs:58:    // Restore the terminal BEFORE dispatching — cmd_endpoint_run / the rc pump
crates\spt\src\picker\mod.rs:212:                // Screen transitions — all converge back on cmd_endpoint_run.
crates\spt\src\picker\mod.rs:270:            crate::cli::cmd_endpoint_run(&adapter, &id, resume, start, false, view)
crates\spt\src\cli.rs:1137:                    cmd_endpoint_run(&adapter, &id, resume, start, attach, view)
crates\spt\src\cli.rs:1265:pub(crate) fn cmd_endpoint_run(
crates\spt\src\cli.rs:1408:/// The harness-boot deadline `cmd_endpoint_run` waits for a freshly-spawned
crates\spt\src\cli.rs:8169:    // case), false on timeout (so cmd_endpoint_run fails loud, never falls through to
crates\spt\tests\dummy_harness_e2e.rs:310:// `cmd_endpoint_run` the flow is `launch_harness_brokered_in` (returns once the


255	fn dispatch(outcome: Outcome) -> i32 {
256	    match outcome {
257	        Outcome::Run {
258	            adapter,
259	            id,
260	            resume,
261	            mode,
262	        } => {
263	            let (start, view) = match mode {
264	                RunMode::Start => (true, false),
265	                RunMode::View => (false, true),
266	                RunMode::Attach => (false, false),
267	            };
268	            // attach is the default action (the `_attach` param is accepted for
269	            // flag symmetry; start/view are what branch).
270	            crate::cli::cmd_endpoint_run(&adapter, &id, resume, start, false, view)
271	        }
272	        // Attach to an already-live endpoint via the rc pump (not a re-bringup).
273	        // `intent` carries the status-conditional choice (View / Control /
274	        // Kick-and-attach), all through the SAME rc dispatch (single-bringup-path:
275	        // intent is a parameter, never a second attach path — REQ-KICK-1).
276	        Outcome::Attach { id, intent } => match crate::rc::run_attach(&id, intent) {
277	            Ok(()) => 0,
278	            Err(e) => {
279	                eprintln!("RC_FAIL:{id}: {e}");

fn dispatch(outcome: Outcome) -> i32 {
    match outcome {
        Outcome::Run {
            adapter,
            id,
            resume,
            mode,
        } => {
            let (start, view) = match mode {
                RunMode::Start => (true, false),
                RunMode::View => (false, true),
                RunMode::Attach => (false, false),
            };
            // attach is the default action (the `_attach` param is accepted for
            // flag symmetry; start/view are what branch).
            crate::cli::cmd_endpoint_run(&adapter, &id, resume, start, false, view)
        }
        // Attach to an already-live endpoint via the rc pump (not a re-bringup).
        // `intent` carries the status-conditional choice (View / Control /
        // Kick-and-attach), all through the SAME rc dispatch (single-bringup-path:
        // intent is a parameter, never a second attach path — REQ-KICK-1).
        Outcome::Attach { id, intent } => match crate::rc::run_attach(&id, intent) {
            Ok(()) => 0,
            Err(e) => {
                eprintln!("RC_FAIL:{id}: {e}");


1125	                // not threaded further. clap already conflicts it with --resume.
1126	                create: _,
1127	                resume,
1128	                start,
1129	                attach,
1130	                view,
1131	            } => match (adapter, id) {
1132	                // Both supplied → the REQ-HOST-RUN-1 non-interactive path
1133	                // (untouched). `--create` is the default-fresh made explicit; it
1134	                // only matters as the absence of `--resume`, so it is not
1135	                // threaded further. The interactive picker bakes the same path.
1136	                (Some(adapter), Some(id)) => {
1137	                    cmd_endpoint_run(&adapter, &id, resume, start, attach, view)
1138	                }
1139	                // Bare (or partial) → the interactive picker (REQ-RUN-PICKER).
1140	                // A lone --id pre-fills the create-new id; a lone --adapter

                // not threaded further. clap already conflicts it with --resume.
                create: _,
                resume,
                start,
                attach,
                view,
            } => match (adapter, id) {
                // Both supplied → the REQ-HOST-RUN-1 non-interactive path
                // (untouched). `--create` is the default-fresh made explicit; it
                // only matters as the absence of `--resume`, so it is not
                // threaded further. The interactive picker bakes the same path.
                (Some(adapter), Some(id)) => {
                    cmd_endpoint_run(&adapter, &id, resume, start, attach, view)
                }
                // Bare (or partial) → the interactive picker (REQ-RUN-PICKER).
                // A lone --id pre-fills the create-new id; a lone --adapter


Outcome::Run needs a `cwd` field. Reading all its construction sites.


495	        };
496	    }
497
498	    /// The create-new outcome (bare adapter address + typed id, fresh session,
499	    /// attach). `None` if the id is empty/invalid or no adapter is selected.
500	    pub fn create_outcome(&self) -> Option<Outcome> {
501	        if self.id_buffer.is_empty() || self.id_error.is_some() {
502	            return None;
503	        }
504	        let adapter = self.selected_adapter()?.address();
505	        Some(Outcome::Run {
506	            adapter,
507	            id: self.id_buffer.clone(),
508	            resume: None,
509	            mode: RunMode::Attach,
510	        })
511	    }
512
513	    // ── Pick-existing: category + list ───────────────────────────────────
514	    /// Cycle the category ring left (-1) or right (+1); resets the item cursor.

        };
    }

    /// The create-new outcome (bare adapter address + typed id, fresh session,
    /// attach). `None` if the id is empty/invalid or no adapter is selected.
    pub fn create_outcome(&self) -> Option<Outcome> {
        if self.id_buffer.is_empty() || self.id_error.is_some() {
            return None;
        }
        let adapter = self.selected_adapter()?.address();
        Some(Outcome::Run {
            adapter,
            id: self.id_buffer.clone(),
            resume: None,
            mode: RunMode::Attach,
        })
    }

    // ── Pick-existing: category + list ───────────────────────────────────
    /// Cycle the category ring left (-1) or right (+1); resets the item cursor.


660	    /// bring up via `cmd_endpoint_run` with the endpoint's recorded adapter.
661	    /// `Shortcut` bakes the current selection. The re-entrant options
662	    /// (Resume / ChangeAdapter / Instantiate / Fork) return `None` — they are
663	    /// SCREEN transitions, driven by [`enter_resume`](Self::enter_resume) /
664	    /// [`reenter_create`](Self::reenter_create). Every yielded variant routes
665	    /// through an existing bringup fn — no second bringup path.
666	    // [impl->REQ-RUN-PICKER]
667	    pub fn confirm_terminal(&self, opt: ConfirmOption) -> Option<Outcome> {
668	        let ep = self.selected_endpoint()?;
669	        let online = ep.status == EpStatus::Online;
670	        let bringup = |mode| Outcome::Run {
671	            adapter: ep.adapter_profile.clone(),
672	            id: ep.id.clone(),
673	            resume: None,
674	            mode,
675	        };
676	        use spt_net::net::attach::AttachIntent;
677	        match opt {
678	            // An online endpoint's PTY is live → attach the rc pump; an offline
679	            // one is brought up then ATTACHED by default (W4 UX: Start-now lands you
680	            // in the live session, not detached with no stdout — the `h` keybind is
681	            // the headless escape, see [`start_headless_outcome`](Self::start_headless_outcome)).
682	            // Attach = Control intent.
683	            ConfirmOption::Attach | ConfirmOption::Start => Some(if online {
684	                Outcome::Attach { id: ep.id.clone(), intent: AttachIntent::Control }
685	            } else {
686	                bringup(RunMode::Attach)
687	            }),
688	            ConfirmOption::View => Some(if online {
689	                Outcome::Attach { id: ep.id.clone(), intent: AttachIntent::Viewer }
690	            } else {
691	                bringup(RunMode::View)
692	            }),
693	            // Kick = Take intent: only offered on an online, controlled endpoint.
694	            ConfirmOption::Kick => Some(Outcome::Attach {
695	                id: ep.id.clone(),
696	                intent: AttachIntent::Take,
697	            }),
698	            ConfirmOption::Shortcut => Some(Outcome::Shortcut {
699	                adapter: ep.adapter_profile.clone(),
700	                id: ep.id.clone(),
701	                resume: None,
702	                mode: RunMode::Attach,
703	            }),
704	            // Screen transitions, not immediate outcomes.
705	            ConfirmOption::Resume
706	            | ConfirmOption::ChangeAdapter
707	            | ConfirmOption::Instantiate
708	            | ConfirmOption::Fork => None,
709	        }
710	    }
711
712	    /// The HEADLESS bringup (start, no attach) for the selected endpoint — the
713	    /// `h` escape on the Confirm screen (W4 UX). Only meaningful for an OFFLINE
714	    /// endpoint (an online one is already up); returns `None` otherwise so the key
715	    /// is a no-op. Mirrors the default attach bringup but bakes `RunMode::Start` so
716	    /// the session comes up detached — routes through `cmd_endpoint_run` like every
717	    /// other bringup (no second path).
718	    // [impl->REQ-RUN-PICKER]
719	    pub fn start_headless_outcome(&self) -> Option<Outcome> {
720	        let ep = self.selected_endpoint()?;
721	        if ep.status != EpStatus::Offline {
722	            return None;
723	        }
724	        Some(Outcome::Run {
725	            adapter: ep.adapter_profile.clone(),
726	            id: ep.id.clone(),
727	            resume: None,
728	            mode: RunMode::Start,
729	        })

    /// bring up via `cmd_endpoint_run` with the endpoint's recorded adapter.
    /// `Shortcut` bakes the current selection. The re-entrant options
    /// (Resume / ChangeAdapter / Instantiate / Fork) return `None` — they are
    /// SCREEN transitions, driven by [`enter_resume`](Self::enter_resume) /
    /// [`reenter_create`](Self::reenter_create). Every yielded variant routes
    /// through an existing bringup fn — no second bringup path.
    // [impl->REQ-RUN-PICKER]
    pub fn confirm_terminal(&self, opt: ConfirmOption) -> Option<Outcome> {
        let ep = self.selected_endpoint()?;
        let online = ep.status == EpStatus::Online;
        let bringup = |mode| Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            mode,
        };
        use spt_net::net::attach::AttachIntent;
        match opt {
            // An online endpoint's PTY is live → attach the rc pump; an offline
            // one is brought up then ATTACHED by default (W4 UX: Start-now lands you
            // in the live session, not detached with no stdout — the `h` keybind is
            // the headless escape, see [`start_headless_outcome`](Self::start_headless_outcome)).
            // Attach = Control intent.
            ConfirmOption::Attach | ConfirmOption::Start => Some(if online {
                Outcome::Attach { id: ep.id.clone(), intent: AttachIntent::Control }
            } else {
                bringup(RunMode::Attach)
            }),
            ConfirmOption::View => Some(if online {
                Outcome::Attach { id: ep.id.clone(), intent: AttachIntent::Viewer }
            } else {
                bringup(RunMode::View)
            }),
            // Kick = Take intent: only offered on an online, controlled endpoint.
            ConfirmOption::Kick => Some(Outcome::Attach {
                id: ep.id.clone(),
                intent: AttachIntent::Take,
            }),
            ConfirmOption::Shortcut => Some(Outcome::Shortcut {
                adapter: ep.adapter_profile.clone(),
                id: ep.id.clone(),
                resume: None,
                mode: RunMode::Attach,
            }),
            // Screen transitions, not immediate outcomes.
            ConfirmOption::Resume
            | ConfirmOption::ChangeAdapter
            | ConfirmOption::Instantiate
            | ConfirmOption::Fork => None,
        }
    }

    /// The HEADLESS bringup (start, no attach) for the selected endpoint — the
    /// `h` escape on the Confirm screen (W4 UX). Only meaningful for an OFFLINE
    /// endpoint (an online one is already up); returns `None` otherwise so the key
    /// is a no-op. Mirrors the default attach bringup but bakes `RunMode::Start` so
    /// the session comes up detached — routes through `cmd_endpoint_run` like every
    /// other bringup (no second path).
    // [impl->REQ-RUN-PICKER]
    pub fn start_headless_outcome(&self) -> Option<Outcome> {
        let ep = self.selected_endpoint()?;
        if ep.status != EpStatus::Offline {
            return None;
        }
        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            mode: RunMode::Start,
        })


Adding `cwd` to `Outcome::Run` enum + all construction sites.


    Run {
        adapter: String,
        id: String,
        resume: Option<String>,
        mode: RunMode,
    },

    Run {
        adapter: String,
        id: String,
        resume: Option<String>,
        /// The project cwd to land the harness in. `Some` only on a Resume
        /// outcome (the selected ledger row's recorded cwd, REQ-SESSION-RESUME-TEMPLATE);
        /// `None` for fresh/headless bringups → `cmd_endpoint_run` resolves it.
        cwd: Option<String>,
        mode: RunMode,
    },


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

    Run {
        adapter: String,
        id: String,
        resume: Option<String>,
        mode: RunMode,
    },

    Run {
        adapter: String,
        id: String,
        resume: Option<String>,
        /// The project cwd to land the harness in. `Some` only on a Resume
        /// outcome (the selected ledger row's recorded cwd, REQ-SESSION-RESUME-TEMPLATE);
        /// `None` for fresh/headless bringups → `cmd_endpoint_run` resolves it.
        cwd: Option<String>,
        mode: RunMode,
    },

+        /// The project cwd to land the harness in. `Some` only on a Resume

+        /// outcome (the selected ledger row's recorded cwd, REQ-SESSION-RESUME-TEMPLATE);

+        /// `None` for fresh/headless bringups → `cmd_endpoint_run` resolves it.

/// Attach to an ALREADY-LIVE endpoint's PTY — routes through the rc pump


        Some(Outcome::Run {
            adapter,
            id: self.id_buffer.clone(),
            resume: None,
            mode: RunMode::Attach,
        })

        Some(Outcome::Run {
            adapter,
            id: self.id_buffer.clone(),
            resume: None,
            cwd: None,
            mode: RunMode::Attach,
        })


        Some(Outcome::Run {
            adapter,
            id: self.id_buffer.clone(),
            resume: None,
            mode: RunMode::Attach,
        })

        Some(Outcome::Run {
            adapter,
            id: self.id_buffer.clone(),
            resume: None,
            cwd: None,
            mode: RunMode::Attach,
        })


        let bringup = |mode| Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            mode,
        };

        let bringup = |mode| Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            cwd: None,
            mode,
        };


        let bringup = |mode| Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            mode,
        };

        let bringup = |mode| Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            cwd: None,
            mode,
        };


        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            mode: RunMode::Start,
        })

        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            cwd: None,
            mode: RunMode::Start,
        })


        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            mode: RunMode::Start,
        })

        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: None,
            cwd: None,
            mode: RunMode::Start,
        })


Now `resume_outcome` threads the row cwd:


    pub fn resume_outcome(&self, headless: bool) -> Option<Outcome> {
        let session_id = self.selected_resume()?.session_id.clone();
        let ep = self.selected_endpoint()?;
        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: Some(session_id),
            mode: if headless { RunMode::Start } else { RunMode::Attach },
        })
    }

    pub fn resume_outcome(&self, headless: bool) -> Option<Outcome> {
        let row = self.selected_resume()?;
        let session_id = row.session_id.clone();
        // Thread the row's recorded cwd so the resumed harness lands in the
        // session's project dir — CC resolves a transcript by session_id + cwd
        // (REQ-SESSION-RESUME-TEMPLATE). An old row with no cwd resolves through
        // cmd_endpoint_run's fallback chain (info.cwd → current_dir).
        let cwd = row.cwd.clone();
        let ep = self.selected_endpoint()?;
        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: Some(session_id),
            cwd,
            mode: if headless { RunMode::Start } else { RunMode::Attach },
        })
    }


    pub fn resume_outcome(&self, headless: bool) -> Option<Outcome> {
        let session_id = self.selected_resume()?.session_id.clone();
        let ep = self.selected_endpoint()?;
        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: Some(session_id),
            mode: if headless { RunMode::Start } else { RunMode::Attach },
        })
    }

    pub fn resume_outcome(&self, headless: bool) -> Option<Outcome> {
        let row = self.selected_resume()?;
        let session_id = row.session_id.clone();
        // Thread the row's recorded cwd so the resumed harness lands in the
        // session's project dir — CC resolves a transcript by session_id + cwd
        // (REQ-SESSION-RESUME-TEMPLATE). An old row with no cwd resolves through
        // cmd_endpoint_run's fallback chain (info.cwd → current_dir).
        let cwd = row.cwd.clone();
        let ep = self.selected_endpoint()?;
        Some(Outcome::Run {
            adapter: ep.adapter_profile.clone(),
            id: ep.id.clone(),
            resume: Some(session_id),
            cwd,
            mode: if headless { RunMode::Start } else { RunMode::Attach },
        })
    }

/// (`RunMode::Attach`) so a resume lands you back in the live session.

-        let session_id = self.selected_resume()?.session_id.clone();

+        let row = self.selected_resume()?;

+        let session_id = row.session_id.clone();

+        // Thread the row's recorded cwd so the resumed harness lands in the

+        // session's project dir — CC resolves a transcript by session_id + cwd

+        // (REQ-SESSION-RESUME-TEMPLATE). An old row with no cwd resolves through

+        // cmd_endpoint_run's fallback chain (info.cwd → current_dir).


ResumeRow gets a `cwd` field + title reformat (D). Doing the struct + title now:


    /// The boundary token (`boot`/`clear`/`compact`) — the title fallback when
    /// no project association exists (the ledger row carries no project).
    pub trigger: String,
    /// Best-effort project name (empty if none derivable).
    pub project: String,
}

    /// The boundary token (`boot`/`clear`/`compact`) — the title fallback when
    /// no project association exists (the ledger row carries no project).
    pub trigger: String,
    /// Best-effort project name (empty if none derivable).
    pub project: String,
    /// The session's recorded project cwd (ledger `cwd`, REQ-SESSION-RESUME-TEMPLATE).
    /// `None` for a pre-migration row → resume falls back to info.cwd/current_dir.
    pub cwd: Option<String>,
}


    /// The boundary token (`boot`/`clear`/`compact`) — the title fallback when
    /// no project association exists (the ledger row carries no project).
    pub trigger: String,
    /// Best-effort project name (empty if none derivable).
    pub project: String,
}

    /// The boundary token (`boot`/`clear`/`compact`) — the title fallback when
    /// no project association exists (the ledger row carries no project).
    pub trigger: String,
    /// Best-effort project name (empty if none derivable).
    pub project: String,
    /// The session's recorded project cwd (ledger `cwd`, REQ-SESSION-RESUME-TEMPLATE).
    /// `None` for a pre-migration row → resume falls back to info.cwd/current_dir.
    pub cwd: Option<String>,
}

+    /// The session's recorded project cwd (ledger `cwd`, REQ-SESSION-RESUME-TEMPLATE).

+    /// `None` for a pre-migration row → resume falls back to info.cwd/current_dir.


    /// The option title: `<project|trigger> @ <ts> (…<id5>)` (picker spec).
    pub fn title(&self) -> String {
        let head = if self.project.is_empty() {
            &self.trigger
        } else {
            &self.project
        };
        let id5: String = {
            let s = &self.session_id;
            let n = s.chars().count();
            s.chars().skip(n.saturating_sub(5)).collect()
        };
        format!("{head} @ {} (…{id5})", self.ts)
    }
}

    /// The option title: `<project|trigger> - <local_time> (…<id5>)` (picker
    /// spec + design D). `local_time` renders the stored RFC3339-UTC `ts` in the
    /// operator's LOCAL timezone for human legibility.
    pub fn title(&self) -> String {
        let head = if self.project.is_empty() {
            &self.trigger
        } else {
            &self.project
        };
        let id5: String = {
            let s = &self.session_id;
            let n = s.chars().count();
            s.chars().skip(n.saturating_sub(5)).collect()
        };
        format!("{head} - {} (…{id5})", Self::local_time(&self.ts))
    }

    /// Render an RFC3339-UTC `ts` as a local-time human stamp
    /// `HH:MM<AM|PM> <offset> MM-DD-YYYY` (e.g. `03:45PM -0800 06-19-2026`). The
    /// tz field is the numeric UTC offset (`%z`): chrono carries no reliable
    /// short tz-abbreviation for `Local` across platforms (Windows `%Z` emits a
    /// long name like "Pacific Standard Time"), so the offset is the
    /// deterministic, operator-confirmable form (design D, TZ-caveat resolve).
    /// A parse failure degrades to the raw `ts` — the picker degrades, never
    /// panics.
    // [impl->REQ-SESSION-RESUME-TEMPLATE]
    fn local_time(ts: &str) -> String {
        match chrono::DateTime::parse_from_rfc3339(ts) {
            Ok(dt) => dt
                .with_timezone(&chrono::Local)
                .format("%I:%M%p %z %m-%d-%Y")
                .to_string(),
            Err(_) => ts.to_string(),
        }
    }
}


    /// The option title: `<project|trigger> @ <ts> (…<id5>)` (picker spec).
    pub fn title(&self) -> String {
        let head = if self.project.is_empty() {
            &self.trigger
        } else {
            &self.project
        };
        let id5: String = {
            let s = &self.session_id;
            let n = s.chars().count();
            s.chars().skip(n.saturating_sub(5)).collect()
        };
        format!("{head} @ {} (…{id5})", self.ts)
    }
}

    /// The option title: `<project|trigger> - <local_time> (…<id5>)` (picker
    /// spec + design D). `local_time` renders the stored RFC3339-UTC `ts` in the
    /// operator's LOCAL timezone for human legibility.
    pub fn title(&self) -> String {
        let head = if self.project.is_empty() {
            &self.trigger
        } else {
            &self.project
        };
        let id5: String = {
            let s = &self.session_id;
            let n = s.chars().count();
            s.chars().skip(n.saturating_sub(5)).collect()
        };
        format!("{head} - {} (…{id5})", Self::local_time(&self.ts))
    }

    /// Render an RFC3339-UTC `ts` as a local-time human stamp
    /// `HH:MM<AM|PM> <offset> MM-DD-YYYY` (e.g. `03:45PM -0800 06-19-2026`). The
    /// tz field is the numeric UTC offset (`%z`): chrono carries no reliable
    /// short tz-abbreviation for `Local` across platforms (Windows `%Z` emits a
    /// long name like "Pacific Standard Time"), so the offset is the
    /// deterministic, operator-confirmable form (design D, TZ-caveat resolve).
    /// A parse failure degrades to the raw `ts` — the picker degrades, never
    /// panics.
    // [impl->REQ-SESSION-RESUME-TEMPLATE]
    fn local_time(ts: &str) -> String {
        match chrono::DateTime::parse_from_rfc3339(ts) {
            Ok(dt) => dt
                .with_timezone(&chrono::Local)
                .format("%I:%M%p %z %m-%d-%Y")
                .to_string(),
            Err(_) => ts.to_string(),
        }
    }
}

-    /// The option title: `<project|trigger> @ <ts> (…<id5>)` (picker spec).

+    /// The option title: `<project|trigger> - <local_time> (…<id5>)` (picker

+    /// spec + design D). `local_time` renders the stored RFC3339-UTC `ts` in the

+    /// operator's LOCAL timezone for human legibility.

-        format!("{head} @ {} (…{id5})", self.ts)

+        format!("{head} - {} (…{id5})", Self::local_time(&self.ts))

+    /// Render an RFC3339-UTC `ts` as a local-time human stamp

+    /// tz field is the numeric UTC offset (`%z`): chrono carries no reliable

+    /// short tz-abbreviation for `Local` across platforms (Windows `%Z` emits a

+    /// long name like "Pacific Standard Time"), so the offset is the

+    /// deterministic, operator-confirmable form (design D, TZ-caveat resolve).

+    /// A parse failure degrades to the raw `ts` — the picker degrades, never

+    // [impl->REQ-SESSION-RESUME-TEMPLATE]

+    fn local_time(ts: &str) -> String {

+        match chrono::DateTime::parse_from_rfc3339(ts) {

+                .with_timezone(&chrono::Local)

/// A pick-existing category. Left/right cycles through these.


Verify chrono dep + clock feature in crates/spt:


crates\spt\Cargo.toml:50:chrono = { version = "0.4", default-features = false, features = ["clock"] }


chrono ✓. Now data.rs maps `e.cwd`:


        .map(|e| ResumeRow {
            session_id: e.session_id,
            ts: e.ts,
            trigger: e.trigger.token().to_string(),
            project: project.clone(),
        })

        .map(|e| ResumeRow {
            session_id: e.session_id,
            ts: e.ts,
            trigger: e.trigger.token().to_string(),
            project: project.clone(),
            // Carry the row's recorded cwd so resume lands in its project dir
            // (REQ-SESSION-RESUME-TEMPLATE). `None` for a pre-migration row.
            cwd: e.cwd,
        })


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

        .map(|e| ResumeRow {
            session_id: e.session_id,
            ts: e.ts,
            trigger: e.trigger.token().to_string(),
            project: project.clone(),
        })

        .map(|e| ResumeRow {
            session_id: e.session_id,
            ts: e.ts,
            trigger: e.trigger.token().to_string(),
            project: project.clone(),
            // Carry the row's recorded cwd so resume lands in its project dir
            // (REQ-SESSION-RESUME-TEMPLATE). `None` for a pre-migration row.
            cwd: e.cwd,
        })

+            // Carry the row's recorded cwd so resume lands in its project dir

+            // (REQ-SESSION-RESUME-TEMPLATE). `None` for a pre-migration row.

rows.reverse(); // ledger is oldest→newest; show newest first


Now the dispatch in mod.rs + CLI callsite + remaining ResumeRow/Outcome::Run test constructions. Reading the test sites:


920	        assert_eq!(gw.display_status(), EpDisplay::Online);
921
922	        // The labels match the spec.
923	        assert_eq!(EpDisplay::Offline.label(), "OFFLINE");
924	        assert_eq!(EpDisplay::Online.label(), "ONLINE");
925	        assert_eq!(EpDisplay::HarnessOnly.label(), "ONLINE - HARNESS ONLY");
926	        assert_eq!(EpDisplay::Controlled.label(), "ONLINE + CONTROLLED");
927	    }
928
929	    // [unit->REQ-RUN-PICKER] the adapter tree nests profiles under each adapter,
930	    // bare-root first then shipped then local, each composite-addressable.
931	    #[test]
932	    fn adapter_tree_nests_profiles() {
933	        let rows = build_adapter_tree(&[
934	            info("claude-spt", &["fast"], &["mine"]),
935	            info("ccs", &[], &[]),
936	        ]);
937	        let addrs: Vec<String> = rows.iter().map(|r| r.address()).collect();
938	        assert_eq!(
939	            addrs,
940	            vec!["claude-spt", "claude-spt:fast", "claude-spt:mine", "ccs"]
941	        );
942	        assert!(!rows[0].is_leaf, "bare-adapter root is not a leaf");
943	        assert!(rows[1].is_leaf, "a profile is a leaf");
944	    }
945
946	    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.
947	    #[test]
948	    fn resume_title_render() {
949	        let with_proj = ResumeRow {

        assert_eq!(gw.display_status(), EpDisplay::Online);

        // The labels match the spec.
        assert_eq!(EpDisplay::Offline.label(), "OFFLINE");
        assert_eq!(EpDisplay::Online.label(), "ONLINE");
        assert_eq!(EpDisplay::HarnessOnly.label(), "ONLINE - HARNESS ONLY");
        assert_eq!(EpDisplay::Controlled.label(), "ONLINE + CONTROLLED");
    }

    // [unit->REQ-RUN-PICKER] the adapter tree nests profiles under each adapter,
    // bare-root first then shipped then local, each composite-addressable.
    #[test]
    fn adapter_tree_nests_profiles() {
        let rows = build_adapter_tree(&[
            info("claude-spt", &["fast"], &["mine"]),
            info("ccs", &[], &[]),
        ]);
        let addrs: Vec<String> = rows.iter().map(|r| r.address()).collect();
        assert_eq!(
            addrs,
            vec!["claude-spt", "claude-spt:fast", "claude-spt:mine", "ccs"]
        );
        assert!(!rows[0].is_leaf, "bare-adapter root is not a leaf");
        assert!(rows[1].is_leaf, "a profile is a leaf");
    }

    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.
    #[test]
    fn resume_title_render() {
        let with_proj = ResumeRow {


Add [session.resume] manifest role

Add per-row cwd to session ledger

Picker: thread row cwd + title reformat


946	    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.
947	    #[test]
948	    fn resume_title_render() {
949	        let with_proj = ResumeRow {
950	            session_id: "abcdef12345".to_string(),
951	            ts: "2026-06-14T10:00:00Z".to_string(),
952	            trigger: "clear".to_string(),
953	            project: "spt-core".to_string(),
954	        };
955	        assert_eq!(with_proj.title(), "spt-core @ 2026-06-14T10:00:00Z (…12345)");
956	        let no_proj = ResumeRow {
957	            session_id: "xy".to_string(),
958	            ts: "T".to_string(),
959	            trigger: "boot".to_string(),
960	            project: String::new(),
961	        };
962	        // short id (<5) takes the whole thing; no project falls back to trigger.
963	        assert_eq!(no_proj.title(), "boot @ T (…xy)");
964	    }
965
966	    // [unit->REQ-RUN-PICKER] a bare picker OPENS on Pick-existing (W4 UX); the Kind
967	    // layer is a jump-target (reached via Esc/back) that still routes into the right
968	    // branch.
969	    #[test]
970	    fn kind_routes() {
971	        let mut m = PickerModel::new("p".into(), vec![], vec![], None, None);
972	        assert_eq!(m.screen, Screen::PickExisting, "bare picker opens on Pick-existing");
973	        m.screen = Screen::Kind; // jump back to the Layer-1 menu
974	        m.kind_cursor = 0;
975	        m.enter_kind();
976	        assert_eq!(m.screen, Screen::CreateAdapter);
977	        m.back();
978	        m.kind_cursor = 1;
979	        m.enter_kind();
980	        assert_eq!(m.screen, Screen::PickExisting);
981	    }
982
983	    // [unit->REQ-RUN-PICKER] cursor windowing clamps (no wrap) at both ends.
984	    #[test]
985	    fn cursor_clamps() {
986	        assert_eq!(PickerModel::step(0, 3, -1), 0);
987	        assert_eq!(PickerModel::step(2, 3, 1), 2);
988	        assert_eq!(PickerModel::step(1, 3, 1), 2);
989	        assert_eq!(PickerModel::step(0, 0, 1), 0);
990	    }

    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.
    #[test]
    fn resume_title_render() {
        let with_proj = ResumeRow {
            session_id: "abcdef12345".to_string(),
            ts: "2026-06-14T10:00:00Z".to_string(),
            trigger: "clear".to_string(),
            project: "spt-core".to_string(),
        };
        assert_eq!(with_proj.title(), "spt-core @ 2026-06-14T10:00:00Z (…12345)");
        let no_proj = ResumeRow {
            session_id: "xy".to_string(),
            ts: "T".to_string(),
            trigger: "boot".to_string(),
            project: String::new(),
        };
        // short id (<5) takes the whole thing; no project falls back to trigger.
        assert_eq!(no_proj.title(), "boot @ T (…xy)");
    }

    // [unit->REQ-RUN-PICKER] a bare picker OPENS on Pick-existing (W4 UX); the Kind
    // layer is a jump-target (reached via Esc/back) that still routes into the right
    // branch.
    #[test]
    fn kind_routes() {
        let mut m = PickerModel::new("p".into(), vec![], vec![], None, None);
        assert_eq!(m.screen, Screen::PickExisting, "bare picker opens on Pick-existing");
        m.screen = Screen::Kind; // jump back to the Layer-1 menu
        m.kind_cursor = 0;
        m.enter_kind();
        assert_eq!(m.screen, Screen::CreateAdapter);
        m.back();
        m.kind_cursor = 1;
        m.enter_kind();
        assert_eq!(m.screen, Screen::PickExisting);
    }

    // [unit->REQ-RUN-PICKER] cursor windowing clamps (no wrap) at both ends.
    #[test]
    fn cursor_clamps() {
        assert_eq!(PickerModel::step(0, 3, -1), 0);
        assert_eq!(PickerModel::step(2, 3, 1), 2);
        assert_eq!(PickerModel::step(1, 3, 1), 2);
        assert_eq!(PickerModel::step(0, 0, 1), 0);
    }


Updating the existing title unit to new shape (local-time + parse-fail passthrough):


    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.
    #[test]
    fn resume_title_render() {
        let with_proj = ResumeRow {
            session_id: "abcdef12345".to_string(),
            ts: "2026-06-14T10:00:00Z".to_string(),
            trigger: "clear".to_string(),
            project: "spt-core".to_string(),
        };
        assert_eq!(with_proj.title(), "spt-core @ 2026-06-14T10:00:00Z (…12345)");
        let no_proj = ResumeRow {
            session_id: "xy".to_string(),
            ts: "T".to_string(),
            trigger: "boot".to_string(),
            project: String::new(),
        };
        // short id (<5) takes the whole thing; no project falls back to trigger.
        assert_eq!(no_proj.title(), "boot @ T (…xy)");
    }

    // [unit->REQ-SESSION-RESUME-TEMPLATE] resume titles render
    // `<project|trigger> - <local_time> (…id5)` (design D): ` - ` separator, a
    // local-time stamp (`HH:MM<AM|PM> <offset> MM-DD-YYYY`), the trailing `…id5`.
    // The time portion is machine-LOCAL, so assert the SHAPE, not an exact clock.
    #[test]
    fn resume_title_render() {
        let with_proj = ResumeRow {
            session_id: "abcdef12345".to_string(),
            ts: "2026-06-14T10:00:00Z".to_string(),
            trigger: "clear".to_string(),
            project: "spt-core".to_string(),
            cwd: None,
        };
        let t = with_proj.title();
        assert!(t.starts_with("spt-core - "), "project head + ` - ` sep: {t}");
        assert!(t.ends_with(" (…12345)"), "trailing …id5: {t}");
        assert!(
            t.contains("AM") || t.contains("PM"),
            "12-hour AM/PM local-time stamp: {t}"
        );
        assert!(!t.contains(" @ "), "old `@ <rfc3339>` form is gone: {t}");

        // Parse failure degrades to the raw ts (never panics). Short id (<5) takes
        // the whole thing; no project falls back to the trigger.
        let no_proj = ResumeRow {
            session_id: "xy".to_string(),
            ts: "T".to_string(),
            trigger: "boot".to_string(),
            project: String::new(),
            cwd: None,
        };
        assert_eq!(no_proj.title(), "boot - T (…xy)");
    }


    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.
    #[test]
    fn resume_title_render() {
        let with_proj = ResumeRow {
            session_id: "abcdef12345".to_string(),
            ts: "2026-06-14T10:00:00Z".to_string(),
            trigger: "clear".to_string(),
            project: "spt-core".to_string(),
        };
        assert_eq!(with_proj.title(), "spt-core @ 2026-06-14T10:00:00Z (…12345)");
        let no_proj = ResumeRow {
            session_id: "xy".to_string(),
            ts: "T".to_string(),
            trigger: "boot".to_string(),
            project: String::new(),
        };
        // short id (<5) takes the whole thing; no project falls back to trigger.
        assert_eq!(no_proj.title(), "boot @ T (…xy)");
    }

    // [unit->REQ-SESSION-RESUME-TEMPLATE] resume titles render
    // `<project|trigger> - <local_time> (…id5)` (design D): ` - ` separator, a
    // local-time stamp (`HH:MM<AM|PM> <offset> MM-DD-YYYY`), the trailing `…id5`.
    // The time portion is machine-LOCAL, so assert the SHAPE, not an exact clock.
    #[test]
    fn resume_title_render() {
        let with_proj = ResumeRow {
            session_id: "abcdef12345".to_string(),
            ts: "2026-06-14T10:00:00Z".to_string(),
            trigger: "clear".to_string(),
            project: "spt-core".to_string(),
            cwd: None,
        };
        let t = with_proj.title();
        assert!(t.starts_with("spt-core - "), "project head + ` - ` sep: {t}");
        assert!(t.ends_with(" (…12345)"), "trailing …id5: {t}");
        assert!(
            t.contains("AM") || t.contains("PM"),
            "12-hour AM/PM local-time stamp: {t}"
        );
        assert!(!t.contains(" @ "), "old `@ <rfc3339>` form is gone: {t}");

        // Parse failure degrades to the raw ts (never panics). Short id (<5) takes
        // the whole thing; no project falls back to the trigger.
        let no_proj = ResumeRow {
            session_id: "xy".to_string(),
            ts: "T".to_string(),
            trigger: "boot".to_string(),
            project: String::new(),
            cwd: None,
        };
        assert_eq!(no_proj.title(), "boot - T (…xy)");
    }

assert!(rows[1].is_leaf, "a profile is a leaf");

-    // [unit->REQ-RUN-PICKER] resume titles render `<project|trigger> @ ts (…id5)`.

+    // [unit->REQ-SESSION-RESUME-TEMPLATE] resume titles render

+    // `<project|trigger> - <local_time> (…id5)` (design D): ` - ` separator, a

+    // local-time stamp (`HH:MM<AM|PM> <offset> MM-DD-YYYY`), the trailing `…id5`.

+    // The time portion is machine-LOCAL, so assert the SHAPE, not an exact clock.

+        assert!(t.starts_with("spt-core - "), "project head + ` - ` sep: {t}");

+        assert!(t.ends_with(" (…12345)"), "trailing …id5: {t}");

+            t.contains("AM") || t.contains("PM"),

+            "12-hour AM/PM local-time stamp: {t}"

+        assert!(!t.contains(" @ "), "old `@ <rfc3339>` form is gone: {t}");

+        // Parse failure degrades to the raw ts (never panics). Short id (<5) takes

+        // the whole thing; no project falls back to the trigger.

-        // short id (<5) takes the whole thing; no project falls back to trigger.

-        assert_eq!(no_proj.title(), "boot @ T (…xy)");

+        assert_eq!(no_proj.title(), "boot - T (…xy)");

// [unit->REQ-RUN-PICKER] a bare picker OPENS on Pick-existing (W4 UX); the Kind


1030	        m.category = Category::Subnet;
1031	        let ids: Vec<&str> = m
1032	            .visible_indices()
1033	            .iter()
1034	            .map(|&i| m.endpoints[i].id.as_str())
1035	            .collect();
1036	        assert_eq!(ids, vec!["remote"]);
1037	    }
1038
1039	    // [unit->REQ-RUN-PICKER] type-to-filter narrows the visible set.
1040	    #[test]
1041	    fn filter_narrows() {
1042	        let eps = vec![
1043	            ep("doyle", "g", EpStatus::Online, true),
1044	            ep("todlando", "g", EpStatus::Online, true),
1045	        ];
1046	        let mut m = PickerModel::new("p".into(), vec![], eps, None, None);
1047	        m.category = Category::Local;
1048	        assert_eq!(m.visible_indices().len(), 2);
1049	        m.start_filter();
1050	        for c in "doy".chars() {
1051	            m.filter_push(c);
1052	        }
1053	        let vis = m.visible_indices();
1054	        assert_eq!(vis.len(), 1);
1055	        assert_eq!(m.endpoints[vis[0]].id, "doyle");
1056	    }
1057
1058	    // [unit->REQ-RUN-PICKER] confirm options are status-dependent: offline+local
1059	    // with ledger rows gets Resume; online does not; remote gets Instantiate.

        m.category = Category::Subnet;
        let ids: Vec<&str> = m
            .visible_indices()
            .iter()
            .map(|&i| m.endpoints[i].id.as_str())
            .collect();
        assert_eq!(ids, vec!["remote"]);
    }

    // [unit->REQ-RUN-PICKER] type-to-filter narrows the visible set.
    #[test]
    fn filter_narrows() {
        let eps = vec![
            ep("doyle", "g", EpStatus::Online, true),
            ep("todlando", "g", EpStatus::Online, true),
        ];
        let mut m = PickerModel::new("p".into(), vec![], eps, None, None);
        m.category = Category::Local;
        assert_eq!(m.visible_indices().len(), 2);
        m.start_filter();
        for c in "doy".chars() {
            m.filter_push(c);
        }
        let vis = m.visible_indices();
        assert_eq!(vis.len(), 1);
        assert_eq!(m.endpoints[vis[0]].id, "doyle");
    }

    // [unit->REQ-RUN-PICKER] confirm options are status-dependent: offline+local
    // with ledger rows gets Resume; online does not; remote gets Instantiate.


Line numbers shifted from edits — re-grep remaining test constructions:


crates\spt\src\picker\view.rs:561:        e.resume_rows = vec![ResumeRow {
crates\spt\src\picker\view.rs:585:        e.resume_rows = vec![ResumeRow {
crates\spt\src\picker\model.rs:138:pub struct ResumeRow {
crates\spt\src\picker\model.rs:151:impl ResumeRow {
crates\spt\src\picker\model.rs:533:        Some(Outcome::Run {
crates\spt\src\picker\model.rs:699:        let bringup = |mode| Outcome::Run {
crates\spt\src\picker\model.rs:754:        Some(Outcome::Run {
crates\spt\src\picker\model.rs:796:        Some(Outcome::Run {
crates\spt\src\picker\model.rs:952:        let with_proj = ResumeRow {
crates\spt\src\picker\model.rs:970:        let no_proj = ResumeRow {
crates\spt\src\picker\model.rs:1063:        offline_local.resume_rows = vec![ResumeRow {
crates\spt\src\picker\model.rs:1122:            Outcome::Run { adapter, id, resume, mode } => {
crates\spt\src\picker\model.rs:1182:            Some(Outcome::Run {
crates\spt\src\picker\model.rs:1193:            Some(Outcome::Run {
crates\spt\src\picker\model.rs:1218:            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new() },
crates\spt\src\picker\model.rs:1219:            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new() },
crates\spt\src\picker\model.rs:1231:            Some(Outcome::Run {
crates\spt\src\picker\model.rs:1240:            Some(Outcome::Run {


Reading the remaining test blocks to patch precisely:


1060	    #[test]
1061	    fn confirm_options_status_dependent() {
1062	        let mut offline_local = ep("a", "g", EpStatus::Offline, true);
1063	        offline_local.resume_rows = vec![ResumeRow {
1064	            session_id: "s".into(),
1065	            ts: "t".into(),
1066	            trigger: "boot".into(),
1067	            project: String::new(),
1068	        }];
1069	        let mut m = PickerModel::new("p".into(), vec![], vec![offline_local], None, None);
1070	        m.category = Category::Local;
1071	        m.enter_pick();
1072	        let opts = m.confirm_options();
1073	        assert!(opts.contains(&ConfirmOption::Resume), "offline+local+ledger ⇒ Resume");
1074	        assert!(opts.contains(&ConfirmOption::ChangeAdapter), "offline ⇒ ChangeAdapter");

    #[test]
    fn confirm_options_status_dependent() {
        let mut offline_local = ep("a", "g", EpStatus::Offline, true);
        offline_local.resume_rows = vec![ResumeRow {
            session_id: "s".into(),
            ts: "t".into(),
            trigger: "boot".into(),
            project: String::new(),
        }];
        let mut m = PickerModel::new("p".into(), vec![], vec![offline_local], None, None);
        m.category = Category::Local;
        m.enter_pick();
        let opts = m.confirm_options();
        assert!(opts.contains(&ConfirmOption::Resume), "offline+local+ledger ⇒ Resume");
        assert!(opts.contains(&ConfirmOption::ChangeAdapter), "offline ⇒ ChangeAdapter");


1115	        m.enter_adapter();
1116	        assert_eq!(m.screen, Screen::CreateId);
1117	        assert!(m.create_outcome().is_none(), "empty id ⇒ no outcome");
1118	        for c in "doyle".chars() {
1119	            m.id_push(c);
1120	        }
1121	        match m.create_outcome().unwrap() {
1122	            Outcome::Run { adapter, id, resume, mode } => {
1123	                assert_eq!(adapter, "claude-spt:fast");
1124	                assert_eq!(id, "doyle");
1125	                assert_eq!(resume, None);
1126	                assert_eq!(mode, RunMode::Attach);
1127	            }
1128	            other => panic!("expected Run, got {other:?}"),
1129	        }
1130	        // an invalid char sets the error and blocks the outcome.
1131	        m.id_push(' ');
1132	        assert!(m.id_error.is_some());
1133	        assert!(m.create_outcome().is_none());
1134	    }
1135
1136	    // [unit->REQ-RUN-PICKER] back navigation unwinds the screen graph, signalling
1137	    // cancellation only from the kind layer.
1138	    #[test]
1139	    fn back_unwinds() {
1140	        let mut m = PickerModel::new("p".into(), vec![], vec![], None, None);
1141	        m.screen = Screen::Confirm;
1142	        assert!(!m.back());
1143	        assert_eq!(m.screen, Screen::PickExisting);
1144	        assert!(!m.back());
1145	        assert_eq!(m.screen, Screen::Kind);
1146	        assert!(m.back(), "back from kind cancels the picker");
1147	    }
1148
1149	    // [unit->REQ-RUN-PICKER] confirm terminal actions route correctly: an online
1150	    // endpoint attaches via the rc pump; an offline one brings up via Run with
1151	    // its recorded adapter; Shortcut bakes the selection.
1152	    #[test]
1153	    fn confirm_terminal_routes() {
1154	        let online = ep("live", "g", EpStatus::Online, true);
1155	        let mut m = PickerModel::new("p".into(), vec![], vec![online], None, None);
1156	        m.category = Category::Local;
1157	        m.enter_pick();
1158	        use spt_net::net::attach::AttachIntent;
1159	        assert_eq!(
1160	            m.confirm_terminal(ConfirmOption::Attach),
1161	            Some(Outcome::Attach { id: "live".into(), intent: AttachIntent::Control })
1162	        );
1163	        assert_eq!(
1164	            m.confirm_terminal(ConfirmOption::View),
1165	            Some(Outcome::Attach { id: "live".into(), intent: AttachIntent::Viewer })
1166	        );
1167	        match m.confirm_terminal(ConfirmOption::Shortcut).unwrap() {
1168	            Outcome::Shortcut { adapter, id, .. } => {
1169	                assert_eq!((adapter.as_str(), id.as_str()), ("claude-spt", "live"));
1170	            }
1171	            o => panic!("expected Shortcut, got {o:?}"),
1172	        }
1173
1174	        // offline Start → bringup (Run) that ATTACHES by default (W4 UX: Start-now
1175	        // lands you in the live session, not detached).
1176	        let off = ep("cold", "g", EpStatus::Offline, true);
1177	        let mut m2 = PickerModel::new("p".into(), vec![], vec![off], None, None);
1178	        m2.category = Category::Local;
1179	        m2.enter_pick();
1180	        assert_eq!(
1181	            m2.confirm_terminal(ConfirmOption::Start),
1182	            Some(Outcome::Run {
1183	                adapter: "claude-spt".into(),
1184	                id: "cold".into(),
1185	                resume: None,
1186	                mode: RunMode::Attach,
1187	            }),
1188	            "offline Start now attaches by default"
1189	        );
1190	        // the `h` headless escape brings the SAME offline endpoint up detached.
1191	        assert_eq!(
1192	            m2.start_headless_outcome(),
1193	            Some(Outcome::Run {
1194	                adapter: "claude-spt".into(),
1195	                id: "cold".into(),
1196	                resume: None,
1197	                mode: RunMode::Start,
1198	            }),
1199	            "h → headless (Start) bringup"
1200	        );
1201	        // the re-entrant options yield no immediate outcome.
1202	        assert_eq!(m2.confirm_terminal(ConfirmOption::Fork), None);
1203	        assert_eq!(m2.confirm_terminal(ConfirmOption::ChangeAdapter), None);
1204
1205	        // h is a no-op on an ONLINE endpoint (already up, nothing to start).
1206	        let online2 = ep("hot", "g", EpStatus::Online, true);
1207	        let mut m3 = PickerModel::new("p".into(), vec![], vec![online2], None, None);
1208	        m3.category = Category::Local;
1209	        m3.enter_pick();
1210	        assert_eq!(m3.start_headless_outcome(), None, "online ⇒ h is a no-op");
1211	    }
1212
1213	    // [unit->REQ-RUN-PICKER] resume picks a prior session and bakes --resume.
1214	    #[test]
1215	    fn resume_outcome_bakes_session() {
1216	        let mut e = ep("cold", "g", EpStatus::Offline, true);
1217	        e.resume_rows = vec![
1218	            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new() },
1219	            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new() },
1220	        ];
1221	        let mut m = PickerModel::new("p".into(), vec![], vec![e], None, None);
1222	        m.category = Category::Local;
1223	        m.enter_pick();
1224	        m.enter_resume();
1225	        assert_eq!(m.screen, Screen::Resume);
1226	        m.move_resume(1);
1227	        assert_eq!(m.selected_resume().unwrap().session_id, "new");
1228	        // Default resume ATTACHES (W4 UX); `h` resumes headless (detached).
1229	        assert_eq!(
1230	            m.resume_outcome(false),
1231	            Some(Outcome::Run {
1232	                adapter: "claude-spt".into(),
1233	                id: "cold".into(),
1234	                resume: Some("new".into()),
1235	                mode: RunMode::Attach,
1236	            })
1237	        );
1238	        assert_eq!(
1239	            m.resume_outcome(true),
1240	            Some(Outcome::Run {
1241	                adapter: "claude-spt".into(),
1242	                id: "cold".into(),
1243	                resume: Some("new".into()),
1244	                mode: RunMode::Start,
1245	            }),
1246	            "h → headless resume"
1247	        );
1248	        assert!(!m.back() && m.screen == Screen::Confirm, "back → confirm");
1249	    }

        m.enter_adapter();
        assert_eq!(m.screen, Screen::CreateId);
        assert!(m.create_outcome().is_none(), "empty id ⇒ no outcome");
        for c in "doyle".chars() {
            m.id_push(c);
        }
        match m.create_outcome().unwrap() {
            Outcome::Run { adapter, id, resume, mode } => {
                assert_eq!(adapter, "claude-spt:fast");
                assert_eq!(id, "doyle");
                assert_eq!(resume, None);
                assert_eq!(mode, RunMode::Attach);
            }
            other => panic!("expected Run, got {other:?}"),
        }
        // an invalid char sets the error and blocks the outcome.
        m.id_push(' ');
        assert!(m.id_error.is_some());
        assert!(m.create_outcome().is_none());
    }

    // [unit->REQ-RUN-PICKER] back navigation unwinds the screen graph, signalling
    // cancellation only from the kind layer.
    #[test]
    fn back_unwinds() {
        let mut m = PickerModel::new("p".into(), vec![], vec![], None, None);
        m.screen = Screen::Confirm;
        assert!(!m.back());
        assert_eq!(m.screen, Screen::PickExisting);
        assert!(!m.back());
        assert_eq!(m.screen, Screen::Kind);
        assert!(m.back(), "back from kind cancels the picker");
    }

    // [unit->REQ-RUN-PICKER] confirm terminal actions route correctly: an online
    // endpoint attaches via the rc pump; an offline one brings up via Run with
    // its recorded adapter; Shortcut bakes the selection.
    #[test]
    fn confirm_terminal_routes() {
        let online = ep("live", "g", EpStatus::Online, true);
        let mut m = PickerModel::new("p".into(), vec![], vec![online], None, None);
        m.category = Category::Local;
        m.enter_pick();
        use spt_net::net::attach::AttachIntent;
        assert_eq!(
            m.confirm_terminal(ConfirmOption::Attach),
            Some(Outcome::Attach { id: "live".into(), intent: AttachIntent::Control })
        );
        assert_eq!(
            m.confirm_terminal(ConfirmOption::View),
            Some(Outcome::Attach { id: "live".into(), intent: AttachIntent::Viewer })
        );
        match m.confirm_terminal(ConfirmOption::Shortcut).unwrap() {
            Outcome::Shortcut { adapter, id, .. } => {
                assert_eq!((adapter.as_str(), id.as_str()), ("claude-spt", "live"));
            }
            o => panic!("expected Shortcut, got {o:?}"),
        }

        // offline Start → bringup (Run) that ATTACHES by default (W4 UX: Start-now
        // lands you in the live session, not detached).
        let off = ep("cold", "g", EpStatus::Offline, true);
        let mut m2 = PickerModel::new("p".into(), vec![], vec![off], None, None);
        m2.category = Category::Local;
        m2.enter_pick();
        assert_eq!(
            m2.confirm_terminal(ConfirmOption::Start),
            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                mode: RunMode::Attach,
            }),
            "offline Start now attaches by default"
        );
        // the `h` headless escape brings the SAME offline endpoint up detached.
        assert_eq!(
            m2.start_headless_outcome(),
            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                mode: RunMode::Start,
            }),
            "h → headless (Start) bringup"
        );
        // the re-entrant options yield no immediate outcome.
        assert_eq!(m2.confirm_terminal(ConfirmOption::Fork), None);
        assert_eq!(m2.confirm_terminal(ConfirmOption::ChangeAdapter), None);

        // h is a no-op on an ONLINE endpoint (already up, nothing to start).
        let online2 = ep("hot", "g", EpStatus::Online, true);
        let mut m3 = PickerModel::new("p".into(), vec![], vec![online2], None, None);
        m3.category = Category::Local;
        m3.enter_pick();
        assert_eq!(m3.start_headless_outcome(), None, "online ⇒ h is a no-op");
    }

    // [unit->REQ-RUN-PICKER] resume picks a prior session and bakes --resume.
    #[test]
    fn resume_outcome_bakes_session() {
        let mut e = ep("cold", "g", EpStatus::Offline, true);
        e.resume_rows = vec![
            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new() },
            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new() },
        ];
        let mut m = PickerModel::new("p".into(), vec![], vec![e], None, None);
        m.category = Category::Local;
        m.enter_pick();
        m.enter_resume();
        assert_eq!(m.screen, Screen::Resume);
        m.move_resume(1);
        assert_eq!(m.selected_resume().unwrap().session_id, "new");
        // Default resume ATTACHES (W4 UX); `h` resumes headless (detached).
        assert_eq!(
            m.resume_outcome(false),
            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: Some("new".into()),
                mode: RunMode::Attach,
            })
        );
        assert_eq!(
            m.resume_outcome(true),
            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: Some("new".into()),
                mode: RunMode::Start,
            }),
            "h → headless resume"
        );
        assert!(!m.back() && m.screen == Screen::Confirm, "back → confirm");
    }


Patching test sites in model.rs:


        offline_local.resume_rows = vec![ResumeRow {
            session_id: "s".into(),
            ts: "t".into(),
            trigger: "boot".into(),
            project: String::new(),
        }];

        offline_local.resume_rows = vec![ResumeRow {
            session_id: "s".into(),
            ts: "t".into(),
            trigger: "boot".into(),
            project: String::new(),
            cwd: None,
        }];


        offline_local.resume_rows = vec![ResumeRow {
            session_id: "s".into(),
            ts: "t".into(),
            trigger: "boot".into(),
            project: String::new(),
        }];

        offline_local.resume_rows = vec![ResumeRow {
            session_id: "s".into(),
            ts: "t".into(),
            trigger: "boot".into(),
            project: String::new(),
            cwd: None,
        }];

let mut m = PickerModel::new("p".into(), vec![], vec![offline_local], None, None);


            Outcome::Run { adapter, id, resume, mode } => {
                assert_eq!(adapter, "claude-spt:fast");

            Outcome::Run { adapter, id, resume, cwd: _, mode } => {
                assert_eq!(adapter, "claude-spt:fast");


            Outcome::Run { adapter, id, resume, mode } => {
                assert_eq!(adapter, "claude-spt:fast");

            Outcome::Run { adapter, id, resume, cwd: _, mode } => {
                assert_eq!(adapter, "claude-spt:fast");

-            Outcome::Run { adapter, id, resume, mode } => {

+            Outcome::Run { adapter, id, resume, cwd: _, mode } => {


            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                mode: RunMode::Attach,
            }),
            "offline Start now attaches by default"

            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                cwd: None,
                mode: RunMode::Attach,
            }),
            "offline Start now attaches by default"


            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                mode: RunMode::Attach,
            }),
            "offline Start now attaches by default"

            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                cwd: None,
                mode: RunMode::Attach,
            }),
            "offline Start now attaches by default"


            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                mode: RunMode::Start,
            }),
            "h → headless (Start) bringup"

            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                cwd: None,
                mode: RunMode::Start,
            }),
            "h → headless (Start) bringup"


            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                mode: RunMode::Start,
            }),
            "h → headless (Start) bringup"

            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: None,
                cwd: None,
                mode: RunMode::Start,
            }),
            "h → headless (Start) bringup"


Now the resume_outcome_bakes_session test (inline ResumeRows + expected Run with cwd):


            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new() },
            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new() },

            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new(), cwd: None },
            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new(), cwd: Some("/proj/new".into()) },


            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new() },
            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new() },

            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new(), cwd: None },
            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new(), cwd: Some("/proj/new".into()) },

let mut e = ep("cold", "g", EpStatus::Offline, true);

-            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new() },

-            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new() },

+            ResumeRow { session_id: "old".into(), ts: "t1".into(), trigger: "boot".into(), project: String::new(), cwd: None },

+            ResumeRow { session_id: "new".into(), ts: "t2".into(), trigger: "clear".into(), project: String::new(), cwd: Some("/proj/new".into()) },

let mut m = PickerModel::new("p".into(), vec![], vec![e], None, None);


            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: Some("new".into()),
                mode: RunMode::Attach,
            })
        );

            Some(Outcome::Run {
                adapter: "claude-spt".into(),
                id: "cold".into(),
                resume: Some("new".into()),
                // resume threads the selected row's recorded cwd (REQ-SESSION-RESUME-TEMPLATE).
                cwd: Some("/proj/new".into()),
                mode: RunMode::Attach,
            })
        );