//! LiveAgent lifecycle hosting — a thin shim over the daemon's re-hosted
//! lifecycle ([`spt_daemon::BrainLifecycle`]).
//!
//! M2b hosted the LiveAgent lifecycle (Psyche spawn, pulse-driven drop ingest +
//! echo, graceful signoff ordering) directly in the `api listen` runner. B3
//! consolidated that orchestration **down into `spt-daemon`** (REQ-DAEMON-1); the
//! brain now runs it as scheduled in-process loops paced by a durable config.
//! This module keeps the binary's call sites stable — `cmd_listen` /
//! `cmd_shutdown` — by forwarding to `BrainLifecycle`. The logic lives once, in
//! the daemon crate; the duplicate here is gone.
//
// [impl->REQ-SEAM-PSYCHE]
// [impl->REQ-DAEMON-1]

use spt_daemon::BrainLifecycle;
use spt_live::SignoffOutcome;
use spt_runtime::Manifest;
use spt_store::perch::{self, ParentHint};

/// The LiveAgent lifecycle host — a thin newtype over the daemon's
/// [`BrainLifecycle`]. Built from a live-capable manifest; `None` for a plain
/// relay endpoint (no Psyche).
pub struct LiveHost(BrainLifecycle);

impl LiveHost {
    /// Build a host for `id` from a live-capable manifest; `None` if the manifest
    /// is absent or declares no Psyche.
    pub fn new_opt(manifest: Option<&Manifest>, id: &str) -> Option<LiveHost> {
        BrainLifecycle::new_opt(manifest, id).map(LiveHost)
    }

    /// Sweep a stale signoff sentinel on listener start (3.2).
    pub fn sweep_on_start(&self) {
        self.0.sweep_on_start();
    }

    /// Spawn the Psyche companion (T2).
    pub fn spawn_psyche(
        &self,
        session_id: Option<&str>,
        event: Option<&str>,
    ) -> Result<PsycheHandle, String> {
        self.0.spawn_psyche(session_id, event)
    }

    /// One pulse tick (T5): ingest drops (T4) then fire a pending echo (T3).
    /// Returns the ingested drops and whether an echo fired (the historical shape
    /// the `api listen` runner consumes).
    pub fn pulse_tick(&self, session_id: Option<&str>) -> Result<(Vec<Ingested>, bool), String> {
        let TickReport {
            ingested,
            echo_fired,
        } = self.0.pulse_tick(session_id)?;
        Ok((ingested, echo_fired))
    }

    /// Execute the graceful signoff (T6): echo-commune before teardown (3.3),
    /// grace recheck before any signoff write (1.1).
    pub fn graceful_signoff(&self, session_id: Option<&str>) -> Result<SignoffOutcome, String> {
        self.0.graceful_signoff(session_id)
    }
}

/// `api shutdown <id>` — the graceful live-agent signoff path. Builds the live
/// host from the manifest and runs the echo-before-teardown ordering (T6). A
/// non-live manifest (no Psyche) is a plain soft-stop: just drop the `ready`
/// marker. Auth-gated by the dispatcher.
pub fn cmd_shutdown(id: &str, manifest: Option<&Manifest>) -> i32 {
    let session_id = perch_session_id(id);
    match LiveHost::new_opt(manifest, id) {
        Some(host) => match host.graceful_signoff(session_id.as_deref()) {
            Ok(outcome) => {
                eprintln!(
                    "SHUTDOWN:{id} echo_ran={} signed_off={}",
                    outcome.echo_ran, outcome.signed_off
                );
                0
            }
            Err(e) => {
                eprintln!("SHUTDOWN_FAIL:{id}: {e}");
                super::EXIT_REFUSED
            }
        },
        None => {
            // Non-live endpoint: soft-stop only (remove the online marker).
            let _ = std::fs::remove_file(perch::resolve_ready_file(id, ParentHint::Infer));
            eprintln!("SHUTDOWN:{id} (non-live soft-stop)");
            0
        }
    }
}

/// The bound session id from the perch's info.json, if any.
fn perch_session_id(id: &str) -> Option<String> {
    let path = perch::resolve_perch_path(id, ParentHint::Infer);
    spt_store::info::read_info(&path)
        .map(|i| i.session_id)
        .filter(|s| !s.is_empty())
}

-    /// Sweep a stale signoff sentinel on listener start (3.2).

-    /// Spawn the Psyche companion (T2).

-    ) -> Result<PsycheHandle, String> {

-        self.0.spawn_psyche(session_id, event)

-    /// One pulse tick (T5): ingest drops (T4) then fire a pending echo (T3).

-    /// Returns the ingested drops and whether an echo fired (the historical shape

-    /// the `api listen` runner consumes).

-    pub fn pulse_tick(&self, session_id: Option<&str>) -> Result<(Vec<Ingested>, bool), String> {

-        } = self.0.pulse_tick(session_id)?;


Update the `LiveHost` doc to reflect its reduced role (shutdown-only shim), then build spt to catch any compile fallout.


Edit

/// The LiveAgent lifecycle host — a thin newtype over the daemon's
/// [`BrainLifecycle`]. Built from a live-capable manifest; `None` for a plain
/// relay endpoint (no Psyche).
pub struct LiveHost(BrainLifecycle);

/// The LiveAgent **graceful-signoff** shim — a thin newtype over the daemon's
/// [`BrainLifecycle`]. Built from a live-capable manifest; `None` for a plain
/// relay endpoint (no Psyche). Since M11-W0.2 the Psyche/pulse lifecycle is
/// hosted entirely by the brain (`spt_daemon::livehost`); the only call left in
/// the `spt` process is the `api shutdown` path's echo-before-teardown signoff.
pub struct LiveHost(BrainLifecycle);


Bash

Build spt crate after W0.2 strip


The file crates\spt\src\api\live.rs has been updated successfully. (file state is current in your context — no need to Read it back)

/// The LiveAgent **graceful-signoff** shim — a thin newtype over the daemon's
/// [`BrainLifecycle`]. Built from a live-capable manifest; `None` for a plain
/// relay endpoint (no Psyche). Since M11-W0.2 the Psyche/pulse lifecycle is
/// hosted entirely by the brain (`spt_daemon::livehost`); the only call left in
/// the `spt` process is the `api shutdown` path's echo-before-teardown signoff.
pub struct LiveHost(BrainLifecycle);

//! LiveAgent lifecycle hosting — a thin shim over the daemon's re-hosted
//! lifecycle ([`spt_daemon::BrainLifecycle`]).
//!
//! M2b hosted the LiveAgent lifecycle (Psyche spawn, pulse-driven drop ingest +
//! echo, graceful signoff ordering) directly in the `api listen` runner. B3
//! consolidated that orchestration **down into `spt-daemon`** (REQ-DAEMON-1); the
//! brain now runs it as scheduled in-process loops paced by a durable config.
//! This module keeps the binary's call sites stable — `cmd_listen` /
//! `cmd_shutdown` — by forwarding to `BrainLifecycle`. The logic lives once, in
//! the daemon crate; the duplicate here is gone.
//
// [impl->REQ-SEAM-PSYCHE]
// [impl->REQ-DAEMON-1]

use spt_daemon::BrainLifecycle;
use spt_live::SignoffOutcome;
use spt_runtime::Manifest;
use spt_store::perch::{self, ParentHint};

/// The LiveAgent lifecycle host — a thin newtype over the daemon's
/// [`BrainLifecycle`]. Built from a live-capable manifest; `None` for a plain
/// relay endpoint (no Psyche).
pub struct LiveHost(BrainLifecycle);

impl LiveHost {
    /// Build a host for `id` from a live-capable manifest; `None` if the manifest
    /// is absent or declares no Psyche.
    pub fn new_opt(manifest: Option<&Manifest>, id: &str) -> Option<LiveHost> {
        BrainLifecycle::new_opt(manifest, id).map(LiveHost)
    }

    /// Execute the graceful signoff (T6): echo-commune before teardown (3.3),
    /// grace recheck before any signoff write (1.1).
    pub fn graceful_signoff(&self, session_id: Option<&str>) -> Result<SignoffOutcome, String> {
        self.0.graceful_signoff(session_id)
    }
}

/// `api shutdown <id>` — the graceful live-agent signoff path. Builds the live
/// host from the manifest and runs the echo-before-teardown ordering (T6). A
/// non-live manifest (no Psyche) is a plain soft-stop: just drop the `ready`
/// marker. Auth-gated by the dispatcher.
pub fn cmd_shutdown(id: &str, manifest: Option<&Manifest>) -> i32 {
    let session_id = perch_session_id(id);
    match LiveHost::new_opt(manifest, id) {
        Some(host) => match host.graceful_signoff(session_id.as_deref()) {
            Ok(outcome) => {
                eprintln!(
                    "SHUTDOWN:{id} echo_ran={} signed_off={}",
                    outcome.echo_ran, outcome.signed_off
                );
                0
            }
            Err(e) => {
                eprintln!("SHUTDOWN_FAIL:{id}: {e}");
                super::EXIT_REFUSED
            }
        },
        None => {
            // Non-live endpoint: soft-stop only (remove the online marker).
            let _ = std::fs::remove_file(perch::resolve_ready_file(id, ParentHint::Infer));
            eprintln!("SHUTDOWN:{id} (non-live soft-stop)");
            0
        }
    }
}

/// The bound session id from the perch's info.json, if any.
fn perch_session_id(id: &str) -> Option<String> {
    let path = perch::resolve_perch_path(id, ParentHint::Infer);
    spt_store::info::read_info(&path)
        .map(|i| i.session_id)
        .filter(|s| !s.is_empty())
}

-/// The LiveAgent lifecycle host — a thin newtype over the daemon's

+/// The LiveAgent **graceful-signoff** shim — a thin newtype over the daemon's

+/// relay endpoint (no Psyche). Since M11-W0.2 the Psyche/pulse lifecycle is

+/// hosted entirely by the brain (`spt_daemon::livehost`); the only call left in

+/// the `spt` process is the `api shutdown` path's echo-before-teardown signoff.


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


W0.2 strip builds clean. Now doyle's item (3): the **real** first-host int — drive real `api listen` (establish-marks-online) → assert status=online → `livehost::reconcile_once` hosts it (non-synthetic, unlike W0.1's hand-seeded status). Write it modeled on shell_e2e (in-proc daemon + real binary).


38	pub struct ApiArgs {
39	    /// adapter_name — the calling harness adapter. Required on every `api` call.
40	    #[arg(long)]
41	    adapter: String,
42	    /// Path to the adapter's runtime manifest (when the command needs it).
43	    #[arg(long)]
44	    manifest: Option<PathBuf>,
45	    #[command(subcommand)]
46	    cmd: ApiCmd,
47	}
48
49	/// The inbound surface. Lifecycle-execution commands (psyche/commune) are M2b;

pub struct ApiArgs {
    /// adapter_name — the calling harness adapter. Required on every `api` call.
    #[arg(long)]
    adapter: String,
    /// Path to the adapter's runtime manifest (when the command needs it).
    #[arg(long)]
    manifest: Option<PathBuf>,
    #[command(subcommand)]
    cmd: ApiCmd,
}

/// The inbound surface. Lifecycle-execution commands (psyche/commune) are M2b;


crates\spt\src\cli.rs:48:    long_about = None
crates\spt\src\cli.rs:66:        #[arg(long)]
crates\spt\src\cli.rs:69:        #[arg(long)]
crates\spt\src\cli.rs:72:        #[arg(long)]
crates\spt\src\cli.rs:77:        #[arg(long = "user-msg")]
crates\spt\src\cli.rs:87:        #[arg(long)]
crates\spt\src\cli.rs:90:        #[arg(long, default_value = "60")]
crates\spt\src\cli.rs:101:        /// for harnesses that cannot host a long-running listener).
crates\spt\src\cli.rs:102:        #[arg(long)]
crates\spt\src\cli.rs:106:        #[arg(long)]

[Showing results with pagination = limit: 10]


crates\spt\src\api\mod.rs:54:    Seed {
crates\spt\src\api\mod.rs:58:        session_id: String,
crates\spt\src\api\mod.rs:61:    Listen {
crates\spt\src\api\mod.rs:65:        parent_pid: Option<u32>,
crates\spt\src\api\mod.rs:68:        once: bool,
crates\spt\src\api\mod.rs:152:    /// Rebind the perch to a new session_id, preserving identity (a context clear/compact boundary).
crates\spt\src\api\mod.rs:214:    /// adapter's context channel. The per-session seen-set fires each hint once
crates\spt\src\api\mod.rs:217:        /// The harness session id keying the once-per-session seen-set.
crates\spt\src\api\mod.rs:249:    session_id: Option<String>,
crates\spt\src\api\mod.rs:254:        Proof::resolve(self.token.clone(), self.session_id.clone())
crates\spt\src\api\mod.rs:300:        // Harness-hosted startup (T4). Seed establishes; listen consumes+binds;
crates\spt\src\api\mod.rs:302:        ApiCmd::Seed { pid, session_id } => startup::cmd_seed(&ctx, pid, session_id),
crates\spt\src\api\mod.rs:303:        ApiCmd::Listen {
crates\spt\src\api\mod.rs:305:            parent_pid,
crates\spt\src\api\mod.rs:306:            once,
crates\spt\src\api\mod.rs:310:            parent_pid,
crates\spt\src\api\mod.rs:311:            once,
crates\spt\src\api\mod.rs:347:                auth.session_id,


52	pub enum ApiCmd {
53	    /// Harness-hosted startup: record an ephemeral seed keyed by parent pid.
54	    Seed {
55	        #[arg(long)]
56	        pid: u32,
57	        #[arg(long)]
58	        session_id: String,
59	    },
60	    /// Consume a seed and hold the perch + relay loop (blocks).
61	    Listen {
62	        id: String,
63	        /// Override the parent-pid anchor (defaults to the self-discovered PPID).
64	        #[arg(long)]
65	        parent_pid: Option<u32>,
66	        /// Drain backlog + one receive cycle, then exit (testability).
67	        #[arg(long)]
68	        once: bool,
69	        /// Home subnet for a NEW endpoint (required on a multi-subnet
70	        /// node — home is assigned at creation, never guessed).
71	        #[arg(long)]
72	        subnet: Option<String>,
73	    },
74	    /// Post-spawn bind of a session to its perch.
75	    Bind {
76	        id: String,
77	        /// The session id discovered post-spawn, written into the perch record.
78	        #[arg(long = "set-session-id")]
79	        bind_session: Option<String>,
80	        /// Home subnet for a NEW endpoint (see `listen`).
81	        #[arg(long)]
82	        subnet: Option<String>,
83	        /// The endpoint type tag (info.json `state`). Defaults to `live_agent`
84	        /// (the agent host); a non-agent endpoint — e.g. a `gateway` — binds
85	        /// with its own open-type tag (REQ-EP-1/REQ-EP-6). A revive keeps the
86	        /// prior type unless this overrides it.
87	        #[arg(long = "type", default_value = "live_agent")]
88	        endpoint_type: String,
89	        #[command(flatten)]
90	        auth: AuthFlags,
91	    },
92	    /// Shell-binary bind: the type=Shell flavor of `bind`. Resolves
93	    /// the instance **by link token alone** (the spawn template carries only
94	    /// `{link_token}` — "owner from the link") and flips it online. The
95	    /// credential IS the auth: no token, no bind.
96	    BindShell {
97	        /// The link token the broker minted at launch.
98	        #[arg(long = "link")]
99	        link_token: String,
100	    },
101	    /// Set activity state busy|idle (also arms the echo-gate sentinel).
102	    State {
103	        #[arg(value_parser = ["busy", "idle"])]
104	        state: String,
105	        id: String,
106	        #[arg(long)]
107	        no_gate: bool,
108	        #[command(flatten)]
109	        auth: AuthFlags,
110	    },
111	    /// Manage the echo-gate sentinel directly.
112	    EchoGate {
113	        #[arg(value_parser = ["set", "clear"])]
114	        action: String,
115	        id: String,
116	        #[command(flatten)]
117	        auth: AuthFlags,
118	    },
119	    /// Drain delivered messages (hook channel). With `--link` this is
120	    /// the shell-flavored relay drain: the link token is the auth, and
121	    /// the drained rows are the
122	    /// shell's MAC-stamped command/text/file frames.
123	    Poll {
124	        id: String,
125	        #[arg(long)]
126	        include_deferred: bool,

pub enum ApiCmd {
    /// Harness-hosted startup: record an ephemeral seed keyed by parent pid.
    Seed {
        #[arg(long)]
        pid: u32,
        #[arg(long)]
        session_id: String,
    },
    /// Consume a seed and hold the perch + relay loop (blocks).
    Listen {
        id: String,
        /// Override the parent-pid anchor (defaults to the self-discovered PPID).
        #[arg(long)]
        parent_pid: Option<u32>,
        /// Drain backlog + one receive cycle, then exit (testability).
        #[arg(long)]
        once: bool,
        /// Home subnet for a NEW endpoint (required on a multi-subnet
        /// node — home is assigned at creation, never guessed).
        #[arg(long)]
        subnet: Option<String>,
    },
    /// Post-spawn bind of a session to its perch.
    Bind {
        id: String,
        /// The session id discovered post-spawn, written into the perch record.
        #[arg(long = "set-session-id")]
        bind_session: Option<String>,
        /// Home subnet for a NEW endpoint (see `listen`).
        #[arg(long)]
        subnet: Option<String>,
        /// The endpoint type tag (info.json `state`). Defaults to `live_agent`
        /// (the agent host); a non-agent endpoint — e.g. a `gateway` — binds
        /// with its own open-type tag (REQ-EP-1/REQ-EP-6). A revive keeps the
        /// prior type unless this overrides it.
        #[arg(long = "type", default_value = "live_agent")]
        endpoint_type: String,
        #[command(flatten)]
        auth: AuthFlags,
    },
    /// Shell-binary bind: the type=Shell flavor of `bind`. Resolves
    /// the instance **by link token alone** (the spawn template carries only
    /// `{link_token}` — "owner from the link") and flips it online. The
    /// credential IS the auth: no token, no bind.
    BindShell {
        /// The link token the broker minted at launch.
        #[arg(long = "link")]
        link_token: String,
    },
    /// Set activity state busy|idle (also arms the echo-gate sentinel).
    State {
        #[arg(value_parser = ["busy", "idle"])]
        state: String,
        id: String,
        #[arg(long)]
        no_gate: bool,
        #[command(flatten)]
        auth: AuthFlags,
    },
    /// Manage the echo-gate sentinel directly.
    EchoGate {
        #[arg(value_parser = ["set", "clear"])]
        action: String,
        id: String,
        #[command(flatten)]
        auth: AuthFlags,
    },
    /// Drain delivered messages (hook channel). With `--link` this is
    /// the shell-flavored relay drain: the link token is the auth, and
    /// the drained rows are the
    /// shell's MAC-stamped command/text/file frames.
    Poll {
        id: String,
        #[arg(long)]
        include_deferred: bool,


CLI flags confirmed (`seed --pid --session-id`, `listen <id> --parent-pid --once`, all need `--adapter`/`--manifest`). The test depends on the seed carrying the adapter (so the perch's `info.adapter` resolves in reconcile). Verify `cmd_seed` stores `ctx.adapter`.


36	pub fn cmd_seed(ctx: &Ctx, parent_pid: u32, session_id: String) -> i32 {
37	    if session_id.is_empty() {
38	        eprintln!("USAGE: seed requires a non-empty --session-id");
39	        return EXIT_REFUSED;
40	    }
41	    let seed = Seed {
42	        parent_pid,
43	        session_id,
44	        adapter: ctx.adapter.clone(),
45	        cwd: std::env::current_dir()
46	            .ok()
47	            .map(|p| p.to_string_lossy().replace('\\', "/")),
48	        created: now_stamp(),
49	    };
50	    if let Err(e) = spt_daemon::ensure_running() {
51	        eprintln!("SEED_FAIL:{parent_pid}: daemon unavailable: {e}");
52	        return EXIT_REFUSED;
53	    }
54	    match spt_daemon::put_seed(&spt_daemon::seed_socket_name(), &seed) {
55	        Ok(()) => {
56	            eprintln!("SEEDED:{parent_pid}");
57	            0
58	        }
59	        Err(e) => {
60	            eprintln!("SEED_FAIL:{parent_pid}: {e}");
61	            EXIT_REFUSED
62	        }
63	    }
64	}
65

pub fn cmd_seed(ctx: &Ctx, parent_pid: u32, session_id: String) -> i32 {
    if session_id.is_empty() {
        eprintln!("USAGE: seed requires a non-empty --session-id");
        return EXIT_REFUSED;
    }
    let seed = Seed {
        parent_pid,
        session_id,
        adapter: ctx.adapter.clone(),
        cwd: std::env::current_dir()
            .ok()
            .map(|p| p.to_string_lossy().replace('\\', "/")),
        created: now_stamp(),
    };
    if let Err(e) = spt_daemon::ensure_running() {
        eprintln!("SEED_FAIL:{parent_pid}: daemon unavailable: {e}");
        return EXIT_REFUSED;
    }
    match spt_daemon::put_seed(&spt_daemon::seed_socket_name(), &seed) {
        Ok(()) => {
            eprintln!("SEEDED:{parent_pid}");
            0
        }
        Err(e) => {
            eprintln!("SEED_FAIL:{parent_pid}: {e}");
            EXIT_REFUSED
        }
    }
}


`cmd_seed` stores `ctx.adapter` → perch's `info.adapter` resolves in reconcile. All deps confirmed. Write the real first-host int.


Write

//! M11-W0.2 first-host int (REQ-DAEMON-1) — the REAL establish-marks-online path,
//! end-to-end through the actual `spt` binary (no synthetic-online seed like the
//! W0.1 units):
//!
//! a live-capable `spt api listen --once` BINDS the Self perch and marks it
//! `status=online` (the W0.2 first-host handoff — the Psyche no longer spawns in
//! this process) → the brain's `livehost::reconcile_once` then HOSTS the Psyche
//! lifecycle off that real online status → a graceful offline transition un-hosts
//! it. This proves the cutover wiring: the spt process marks online, the brain
//! reconcile reacts, exactly the shellwake CLI-flips / brain-reconciles pattern.
//
// [int->REQ-DAEMON-1]

use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::Arc;
use std::time::Duration;

mod common;
use common::CommandNoWindowExt;

use spt_daemon::brainproc::StartReason;
use spt_daemon::config::DaemonConfig;
use spt_daemon::livehost::{self, LiveSet};
use spt_store::liveness::{STATUS_OFFLINE, STATUS_ONLINE};
use spt_store::perch::{self, ParentHint};

// A no-op Psyche summarizer: spawns + exits 0 (the daemon online-stamp hosts it,
// not the pid).
#[cfg(windows)]
const NOOP: &str = "cmd /C rem";
#[cfg(unix)]
const NOOP: &str = "true";

/// In-process seed daemon so `api seed`/`api listen` succeed without a REAL
/// detached `spt daemon` (shell_e2e's pattern).
fn start_inproc_daemon() {
    let reg = std::sync::Arc::new(spt_daemon::SeedRegistry::new());
    let name = spt_daemon::seed_socket_name();
    std::thread::spawn(move || {
        let _ = spt_daemon::seedmap::serve_seed_control(&name, reg);
    });
    for _ in 0..400 {
        if spt_daemon::is_running() {
            return;
        }
        std::thread::sleep(Duration::from_millis(5));
    }
    panic!("in-process seed daemon did not come up");
}

/// `Command::output()` with a deadline, run off-thread (bounds a hang into a
/// clean failure).
fn output_bounded(mut cmd: Command, deadline: Duration) -> Output {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        let _ = tx.send(cmd.output());
    });
    rx.recv_timeout(deadline)
        .expect("captured spt call must complete")
        .expect("run spt")
}

#[test]
fn live_listen_marks_online_and_brain_reconcile_hosts() {
    // One test in this file: a plain set_var is safe; every child inherits the home.
    let home = tempfile::tempdir().unwrap();
    std::env::set_var("SPT_HOME", home.path());
    start_inproc_daemon();

    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));

    // A live-capable adapter (declares [session.psyche_init]) + register it so the
    // brain reconcile can resolve the manifest from disk by the perch's adapter.
    let drops = home.path().join("drops");
    std::fs::create_dir_all(&drops).unwrap();
    let dir = drops.to_string_lossy().replace('\\', "/");
    let manifest_toml = format!(
        "[adapter]\nname=\"mocklive\"\nkind=\"harness\"\nversion=\"1\"\n\
         min_spt_core_version=\"0\"\n\n\
         [session]\ncommune_dir=\"{dir}\"\nsignoff_dir=\"{dir}\"\n\n\
         [session.psyche_init]\ncommand='{NOOP}'\ncwd=\"{{psyche_dir}}\"\nkeys=[]\n"
    );
    let manifest_path = home.path().join("mocklive.toml");
    std::fs::write(&manifest_path, &manifest_toml).unwrap();
    let src = perch::spt_home().join("srcs").join("mocklive");
    std::fs::create_dir_all(&src).unwrap();
    std::fs::write(src.join("manifest.toml"), &manifest_toml).unwrap();
    spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();

    let pid = std::process::id().to_string();
    let mp = manifest_path.to_string_lossy().to_string();
    let spt = |args: &[&str]| {
        let mut cmd = Command::new(&spt_bin);
        cmd.no_window().args(args).env("SPT_HOME", home.path());
        output_bounded(cmd, Duration::from_secs(60))
    };

    // ── seed (keyed by THIS process's pid — alive, passes the recycle guard) ──
    let out = spt(&[
        "api", "--adapter", "mocklive", "seed", "--pid", &pid, "--session-id", "sid-1",
    ]);
    assert!(out.status.success(), "seed: {}", String::from_utf8_lossy(&out.stderr));

    // ── the REAL live listen --once: binds the Self perch + marks it online
    // (the W0.2 establish-marks-online), drains, exits. The Psyche does NOT spawn
    // in this process anymore.
    let out = spt(&[
        "api", "--adapter", "mocklive", "--manifest", &mp, "listen", "agent7",
        "--parent-pid", &pid, "--once",
    ]);
    assert!(out.status.success(), "listen: {}", String::from_utf8_lossy(&out.stderr));

    // The real establish-marks-online: the perch carries status=online, written by
    // cmd_listen (NOT hand-seeded).
    let self_perch = perch::resolve_perch_path("agent7", ParentHint::Infer);
    let status = spt_store::info::read_info(&self_perch).and_then(|i| i.status);
    assert_eq!(
        status.as_deref(),
        Some(STATUS_ONLINE),
        "a live `api listen` marks the perch online (the first-host handoff)"
    );

    // ── the brain reconcile hosts off that REAL online status ──
    let set = Arc::new(LiveSet::new());
    let cfg = DaemonConfig {
        pulse_period: Duration::from_millis(15),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert_eq!(
        set.len(),
        1,
        "the brain reconcile hosts the live-listen-onlined endpoint"
    );

    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert!(set.is_empty(), "an offline transition un-hosts the lifecycle");

    // Reap any REAL daemon that auto-started against this throwaway home (hygiene).
    if let Ok(p) = std::fs::read_to_string(home.path().join("daemon.pid")) {
        if let Ok(p) = p.trim().parse::<u32>() {
            #[cfg(windows)]
            let _ = Command::new("taskkill")
                .no_window()
                .args(["/PID", &p.to_string(), "/F"])
                .output();
            #[cfg(unix)]
            let _ = Command::new("kill").args(["-9", &p.to_string()]).output();
        }
    }
}


File created successfully at: crates\spt\tests\live_firsthost_e2e.rs (file state is current in your context — no need to Read it back)

//! M11-W0.2 first-host int (REQ-DAEMON-1) — the REAL establish-marks-online path,
//! end-to-end through the actual `spt` binary (no synthetic-online seed like the
//! W0.1 units):
//!
//! a live-capable `spt api listen --once` BINDS the Self perch and marks it
//! `status=online` (the W0.2 first-host handoff — the Psyche no longer spawns in
//! this process) → the brain's `livehost::reconcile_once` then HOSTS the Psyche
//! lifecycle off that real online status → a graceful offline transition un-hosts
//! it. This proves the cutover wiring: the spt process marks online, the brain
//! reconcile reacts, exactly the shellwake CLI-flips / brain-reconciles pattern.
//
// [int->REQ-DAEMON-1]

use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::Arc;
use std::time::Duration;

mod common;
use common::CommandNoWindowExt;

use spt_daemon::brainproc::StartReason;
use spt_daemon::config::DaemonConfig;
use spt_daemon::livehost::{self, LiveSet};
use spt_store::liveness::{STATUS_OFFLINE, STATUS_ONLINE};
use spt_store::perch::{self, ParentHint};

// A no-op Psyche summarizer: spawns + exits 0 (the daemon online-stamp hosts it,
// not the pid).
#[cfg(windows)]
const NOOP: &str = "cmd /C rem";
#[cfg(unix)]
const NOOP: &str = "true";

/// In-process seed daemon so `api seed`/`api listen` succeed without a REAL
/// detached `spt daemon` (shell_e2e's pattern).
fn start_inproc_daemon() {
    let reg = std::sync::Arc::new(spt_daemon::SeedRegistry::new());
    let name = spt_daemon::seed_socket_name();
    std::thread::spawn(move || {
        let _ = spt_daemon::seedmap::serve_seed_control(&name, reg);
    });
    for _ in 0..400 {
        if spt_daemon::is_running() {
            return;
        }
        std::thread::sleep(Duration::from_millis(5));
    }
    panic!("in-process seed daemon did not come up");
}

/// `Command::output()` with a deadline, run off-thread (bounds a hang into a
/// clean failure).
fn output_bounded(mut cmd: Command, deadline: Duration) -> Output {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        let _ = tx.send(cmd.output());
    });
    rx.recv_timeout(deadline)
        .expect("captured spt call must complete")
        .expect("run spt")
}

#[test]
fn live_listen_marks_online_and_brain_reconcile_hosts() {
    // One test in this file: a plain set_var is safe; every child inherits the home.
    let home = tempfile::tempdir().unwrap();
    std::env::set_var("SPT_HOME", home.path());
    start_inproc_daemon();

    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));

    // A live-capable adapter (declares [session.psyche_init]) + register it so the
    // brain reconcile can resolve the manifest from disk by the perch's adapter.
    let drops = home.path().join("drops");
    std::fs::create_dir_all(&drops).unwrap();
    let dir = drops.to_string_lossy().replace('\\', "/");
    let manifest_toml = format!(
        "[adapter]\nname=\"mocklive\"\nkind=\"harness\"\nversion=\"1\"\n\
         min_spt_core_version=\"0\"\n\n\
         [session]\ncommune_dir=\"{dir}\"\nsignoff_dir=\"{dir}\"\n\n\
         [session.psyche_init]\ncommand='{NOOP}'\ncwd=\"{{psyche_dir}}\"\nkeys=[]\n"
    );
    let manifest_path = home.path().join("mocklive.toml");
    std::fs::write(&manifest_path, &manifest_toml).unwrap();
    let src = perch::spt_home().join("srcs").join("mocklive");
    std::fs::create_dir_all(&src).unwrap();
    std::fs::write(src.join("manifest.toml"), &manifest_toml).unwrap();
    spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();

    let pid = std::process::id().to_string();
    let mp = manifest_path.to_string_lossy().to_string();
    let spt = |args: &[&str]| {
        let mut cmd = Command::new(&spt_bin);
        cmd.no_window().args(args).env("SPT_HOME", home.path());
        output_bounded(cmd, Duration::from_secs(60))
    };

    // ── seed (keyed by THIS process's pid — alive, passes the recycle guard) ──
    let out = spt(&[
        "api", "--adapter", "mocklive", "seed", "--pid", &pid, "--session-id", "sid-1",
    ]);
    assert!(out.status.success(), "seed: {}", String::from_utf8_lossy(&out.stderr));

    // ── the REAL live listen --once: binds the Self perch + marks it online
    // (the W0.2 establish-marks-online), drains, exits. The Psyche does NOT spawn
    // in this process anymore.
    let out = spt(&[
        "api", "--adapter", "mocklive", "--manifest", &mp, "listen", "agent7",
        "--parent-pid", &pid, "--once",
    ]);
    assert!(out.status.success(), "listen: {}", String::from_utf8_lossy(&out.stderr));

    // The real establish-marks-online: the perch carries status=online, written by
    // cmd_listen (NOT hand-seeded).
    let self_perch = perch::resolve_perch_path("agent7", ParentHint::Infer);
    let status = spt_store::info::read_info(&self_perch).and_then(|i| i.status);
    assert_eq!(
        status.as_deref(),
        Some(STATUS_ONLINE),
        "a live `api listen` marks the perch online (the first-host handoff)"
    );

    // ── the brain reconcile hosts off that REAL online status ──
    let set = Arc::new(LiveSet::new());
    let cfg = DaemonConfig {
        pulse_period: Duration::from_millis(15),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert_eq!(
        set.len(),
        1,
        "the brain reconcile hosts the live-listen-onlined endpoint"
    );

    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert!(set.is_empty(), "an offline transition un-hosts the lifecycle");

    // Reap any REAL daemon that auto-started against this throwaway home (hygiene).
    if let Ok(p) = std::fs::read_to_string(home.path().join("daemon.pid")) {
        if let Ok(p) = p.trim().parse::<u32>() {
            #[cfg(windows)]
            let _ = Command::new("taskkill")
                .no_window()
                .args(["/PID", &p.to_string(), "/F"])
                .output();
            #[cfg(unix)]
            let _ = Command::new("kill").args(["-9", &p.to_string()]).output();
        }
    }
}


Bash

cargo test -p spt --test live_firsthost_e2e -- --nocapture 2>&1 | tail -25

Build + run W0.2 first-host int


   Compiling spt-daemon v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
   Compiling spt v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 11.07s
     Running tests\live_firsthost_e2e.rs (target\debug\deps\live_firsthost_e2e-cc25fc6588affad6.exe)

running 1 test
LIVEHOST_PSYCHE:agent7 pid=99884
LIVEHOST_PSYCHE:agent7 pid=130116

thread 'live_listen_marks_online_and_brain_reconcile_hosts' (24996) panicked at crates\spt\tests\live_firsthost_e2e.rs:155:5:
an offline transition un-hosts the lifecycle
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
test live_listen_marks_online_and_brain_reconcile_hosts ... FAILED

failures:

failures:
    live_listen_marks_online_and_brain_reconcile_hosts

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.18s

error: test failed, to rerun pass `-p spt --test live_firsthost_e2e`


The host worked (set.len()==1 passed) — the failure is the un-host step: a **lost-update race** on info.json between the driver's per-tick `touch_active` (set_last_active) and the test's `set_status(OFFLINE)` — at 15ms pulse the driver clobbers offline back. In production (5s pulse) the driver parks between ticks so the window is negligible, but the fast test pulse hits it. Fix: long pulse (driver parks after the immediate first tick) + a settle sleep → deterministic un-host.


Edit

    let set = Arc::new(LiveSet::new());
    let cfg = DaemonConfig {
        pulse_period: Duration::from_millis(15),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());

    let set = Arc::new(LiveSet::new());
    // A LONG pulse: the driver fires once immediately (cold anchor) then parks.
    // This keeps the un-host step below deterministic — the driver's per-tick
    // touch_active (a set_last_active read-modify-write on info.json) is NOT racing
    // the offline flip; at the production 5s pulse the same window is negligible,
    // but a fast test pulse would let touch_active clobber the offline status back.
    let cfg = DaemonConfig {
        pulse_period: Duration::from_secs(10),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());


The file crates\spt\tests\live_firsthost_e2e.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    let set = Arc::new(LiveSet::new());
    // A LONG pulse: the driver fires once immediately (cold anchor) then parks.
    // This keeps the un-host step below deterministic — the driver's per-tick
    // touch_active (a set_last_active read-modify-write on info.json) is NOT racing
    // the offline flip; at the production 5s pulse the same window is negligible,
    // but a fast test pulse would let touch_active clobber the offline status back.
    let cfg = DaemonConfig {
        pulse_period: Duration::from_secs(10),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());

//! M11-W0.2 first-host int (REQ-DAEMON-1) — the REAL establish-marks-online path,
//! end-to-end through the actual `spt` binary (no synthetic-online seed like the
//! W0.1 units):
//!
//! a live-capable `spt api listen --once` BINDS the Self perch and marks it
//! `status=online` (the W0.2 first-host handoff — the Psyche no longer spawns in
//! this process) → the brain's `livehost::reconcile_once` then HOSTS the Psyche
//! lifecycle off that real online status → a graceful offline transition un-hosts
//! it. This proves the cutover wiring: the spt process marks online, the brain
//! reconcile reacts, exactly the shellwake CLI-flips / brain-reconciles pattern.
//
// [int->REQ-DAEMON-1]

use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::Arc;
use std::time::Duration;

mod common;
use common::CommandNoWindowExt;

use spt_daemon::brainproc::StartReason;
use spt_daemon::config::DaemonConfig;
use spt_daemon::livehost::{self, LiveSet};
use spt_store::liveness::{STATUS_OFFLINE, STATUS_ONLINE};
use spt_store::perch::{self, ParentHint};

// A no-op Psyche summarizer: spawns + exits 0 (the daemon online-stamp hosts it,
// not the pid).
#[cfg(windows)]
const NOOP: &str = "cmd /C rem";
#[cfg(unix)]
const NOOP: &str = "true";

/// In-process seed daemon so `api seed`/`api listen` succeed without a REAL
/// detached `spt daemon` (shell_e2e's pattern).
fn start_inproc_daemon() {
    let reg = std::sync::Arc::new(spt_daemon::SeedRegistry::new());
    let name = spt_daemon::seed_socket_name();
    std::thread::spawn(move || {
        let _ = spt_daemon::seedmap::serve_seed_control(&name, reg);
    });
    for _ in 0..400 {
        if spt_daemon::is_running() {
            return;
        }
        std::thread::sleep(Duration::from_millis(5));
    }
    panic!("in-process seed daemon did not come up");
}

/// `Command::output()` with a deadline, run off-thread (bounds a hang into a
/// clean failure).
fn output_bounded(mut cmd: Command, deadline: Duration) -> Output {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        let _ = tx.send(cmd.output());
    });
    rx.recv_timeout(deadline)
        .expect("captured spt call must complete")
        .expect("run spt")
}

#[test]
fn live_listen_marks_online_and_brain_reconcile_hosts() {
    // One test in this file: a plain set_var is safe; every child inherits the home.
    let home = tempfile::tempdir().unwrap();
    std::env::set_var("SPT_HOME", home.path());
    start_inproc_daemon();

    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));

    // A live-capable adapter (declares [session.psyche_init]) + register it so the
    // brain reconcile can resolve the manifest from disk by the perch's adapter.
    let drops = home.path().join("drops");
    std::fs::create_dir_all(&drops).unwrap();
    let dir = drops.to_string_lossy().replace('\\', "/");
    let manifest_toml = format!(
        "[adapter]\nname=\"mocklive\"\nkind=\"harness\"\nversion=\"1\"\n\
         min_spt_core_version=\"0\"\n\n\
         [session]\ncommune_dir=\"{dir}\"\nsignoff_dir=\"{dir}\"\n\n\
         [session.psyche_init]\ncommand='{NOOP}'\ncwd=\"{{psyche_dir}}\"\nkeys=[]\n"
    );
    let manifest_path = home.path().join("mocklive.toml");
    std::fs::write(&manifest_path, &manifest_toml).unwrap();
    let src = perch::spt_home().join("srcs").join("mocklive");
    std::fs::create_dir_all(&src).unwrap();
    std::fs::write(src.join("manifest.toml"), &manifest_toml).unwrap();
    spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();

    let pid = std::process::id().to_string();
    let mp = manifest_path.to_string_lossy().to_string();
    let spt = |args: &[&str]| {
        let mut cmd = Command::new(&spt_bin);
        cmd.no_window().args(args).env("SPT_HOME", home.path());
        output_bounded(cmd, Duration::from_secs(60))
    };

    // ── seed (keyed by THIS process's pid — alive, passes the recycle guard) ──
    let out = spt(&[
        "api", "--adapter", "mocklive", "seed", "--pid", &pid, "--session-id", "sid-1",
    ]);
    assert!(out.status.success(), "seed: {}", String::from_utf8_lossy(&out.stderr));

    // ── the REAL live listen --once: binds the Self perch + marks it online
    // (the W0.2 establish-marks-online), drains, exits. The Psyche does NOT spawn
    // in this process anymore.
    let out = spt(&[
        "api", "--adapter", "mocklive", "--manifest", &mp, "listen", "agent7",
        "--parent-pid", &pid, "--once",
    ]);
    assert!(out.status.success(), "listen: {}", String::from_utf8_lossy(&out.stderr));

    // The real establish-marks-online: the perch carries status=online, written by
    // cmd_listen (NOT hand-seeded).
    let self_perch = perch::resolve_perch_path("agent7", ParentHint::Infer);
    let status = spt_store::info::read_info(&self_perch).and_then(|i| i.status);
    assert_eq!(
        status.as_deref(),
        Some(STATUS_ONLINE),
        "a live `api listen` marks the perch online (the first-host handoff)"
    );

    // ── the brain reconcile hosts off that REAL online status ──
    let set = Arc::new(LiveSet::new());
    let cfg = DaemonConfig {
        pulse_period: Duration::from_millis(15),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert_eq!(
        set.len(),
        1,
        "the brain reconcile hosts the live-listen-onlined endpoint"
    );

    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert!(set.is_empty(), "an offline transition un-hosts the lifecycle");

    // Reap any REAL daemon that auto-started against this throwaway home (hygiene).
    if let Ok(p) = std::fs::read_to_string(home.path().join("daemon.pid")) {
        if let Ok(p) = p.trim().parse::<u32>() {
            #[cfg(windows)]
            let _ = Command::new("taskkill")
                .no_window()
                .args(["/PID", &p.to_string(), "/F"])
                .output();
            #[cfg(unix)]
            let _ = Command::new("kill").args(["-9", &p.to_string()]).output();
        }
    }
}

+    // A LONG pulse: the driver fires once immediately (cold anchor) then parks.

+    // This keeps the un-host step below deterministic — the driver's per-tick

+    // touch_active (a set_last_active read-modify-write on info.json) is NOT racing

+    // the offline flip; at the production 5s pulse the same window is negligible,

+    // but a fast test pulse would let touch_active clobber the offline status back.

-        pulse_period: Duration::from_millis(15),

+        pulse_period: Duration::from_secs(10),


    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();

    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    // Let the driver's immediate first pulse settle (then it parks for 10s) so the
    // offline flip is not clobbered by a concurrent set_last_active.
    std::thread::sleep(Duration::from_millis(300));
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();


    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    // Let the driver's immediate first pulse settle (then it parks for 10s) so the
    // offline flip is not clobbered by a concurrent set_last_active.
    std::thread::sleep(Duration::from_millis(300));
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();

//! M11-W0.2 first-host int (REQ-DAEMON-1) — the REAL establish-marks-online path,
//! end-to-end through the actual `spt` binary (no synthetic-online seed like the
//! W0.1 units):
//!
//! a live-capable `spt api listen --once` BINDS the Self perch and marks it
//! `status=online` (the W0.2 first-host handoff — the Psyche no longer spawns in
//! this process) → the brain's `livehost::reconcile_once` then HOSTS the Psyche
//! lifecycle off that real online status → a graceful offline transition un-hosts
//! it. This proves the cutover wiring: the spt process marks online, the brain
//! reconcile reacts, exactly the shellwake CLI-flips / brain-reconciles pattern.
//
// [int->REQ-DAEMON-1]

use std::path::PathBuf;
use std::process::{Command, Output};
use std::sync::Arc;
use std::time::Duration;

mod common;
use common::CommandNoWindowExt;

use spt_daemon::brainproc::StartReason;
use spt_daemon::config::DaemonConfig;
use spt_daemon::livehost::{self, LiveSet};
use spt_store::liveness::{STATUS_OFFLINE, STATUS_ONLINE};
use spt_store::perch::{self, ParentHint};

// A no-op Psyche summarizer: spawns + exits 0 (the daemon online-stamp hosts it,
// not the pid).
#[cfg(windows)]
const NOOP: &str = "cmd /C rem";
#[cfg(unix)]
const NOOP: &str = "true";

/// In-process seed daemon so `api seed`/`api listen` succeed without a REAL
/// detached `spt daemon` (shell_e2e's pattern).
fn start_inproc_daemon() {
    let reg = std::sync::Arc::new(spt_daemon::SeedRegistry::new());
    let name = spt_daemon::seed_socket_name();
    std::thread::spawn(move || {
        let _ = spt_daemon::seedmap::serve_seed_control(&name, reg);
    });
    for _ in 0..400 {
        if spt_daemon::is_running() {
            return;
        }
        std::thread::sleep(Duration::from_millis(5));
    }
    panic!("in-process seed daemon did not come up");
}

/// `Command::output()` with a deadline, run off-thread (bounds a hang into a
/// clean failure).
fn output_bounded(mut cmd: Command, deadline: Duration) -> Output {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        let _ = tx.send(cmd.output());
    });
    rx.recv_timeout(deadline)
        .expect("captured spt call must complete")
        .expect("run spt")
}

#[test]
fn live_listen_marks_online_and_brain_reconcile_hosts() {
    // One test in this file: a plain set_var is safe; every child inherits the home.
    let home = tempfile::tempdir().unwrap();
    std::env::set_var("SPT_HOME", home.path());
    start_inproc_daemon();

    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));

    // A live-capable adapter (declares [session.psyche_init]) + register it so the
    // brain reconcile can resolve the manifest from disk by the perch's adapter.
    let drops = home.path().join("drops");
    std::fs::create_dir_all(&drops).unwrap();
    let dir = drops.to_string_lossy().replace('\\', "/");
    let manifest_toml = format!(
        "[adapter]\nname=\"mocklive\"\nkind=\"harness\"\nversion=\"1\"\n\
         min_spt_core_version=\"0\"\n\n\
         [session]\ncommune_dir=\"{dir}\"\nsignoff_dir=\"{dir}\"\n\n\
         [session.psyche_init]\ncommand='{NOOP}'\ncwd=\"{{psyche_dir}}\"\nkeys=[]\n"
    );
    let manifest_path = home.path().join("mocklive.toml");
    std::fs::write(&manifest_path, &manifest_toml).unwrap();
    let src = perch::spt_home().join("srcs").join("mocklive");
    std::fs::create_dir_all(&src).unwrap();
    std::fs::write(src.join("manifest.toml"), &manifest_toml).unwrap();
    spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();

    let pid = std::process::id().to_string();
    let mp = manifest_path.to_string_lossy().to_string();
    let spt = |args: &[&str]| {
        let mut cmd = Command::new(&spt_bin);
        cmd.no_window().args(args).env("SPT_HOME", home.path());
        output_bounded(cmd, Duration::from_secs(60))
    };

    // ── seed (keyed by THIS process's pid — alive, passes the recycle guard) ──
    let out = spt(&[
        "api", "--adapter", "mocklive", "seed", "--pid", &pid, "--session-id", "sid-1",
    ]);
    assert!(out.status.success(), "seed: {}", String::from_utf8_lossy(&out.stderr));

    // ── the REAL live listen --once: binds the Self perch + marks it online
    // (the W0.2 establish-marks-online), drains, exits. The Psyche does NOT spawn
    // in this process anymore.
    let out = spt(&[
        "api", "--adapter", "mocklive", "--manifest", &mp, "listen", "agent7",
        "--parent-pid", &pid, "--once",
    ]);
    assert!(out.status.success(), "listen: {}", String::from_utf8_lossy(&out.stderr));

    // The real establish-marks-online: the perch carries status=online, written by
    // cmd_listen (NOT hand-seeded).
    let self_perch = perch::resolve_perch_path("agent7", ParentHint::Infer);
    let status = spt_store::info::read_info(&self_perch).and_then(|i| i.status);
    assert_eq!(
        status.as_deref(),
        Some(STATUS_ONLINE),
        "a live `api listen` marks the perch online (the first-host handoff)"
    );

    // ── the brain reconcile hosts off that REAL online status ──
    let set = Arc::new(LiveSet::new());
    // A LONG pulse: the driver fires once immediately (cold anchor) then parks.
    // This keeps the un-host step below deterministic — the driver's per-tick
    // touch_active (a set_last_active read-modify-write on info.json) is NOT racing
    // the offline flip; at the production 5s pulse the same window is negligible,
    // but a fast test pulse would let touch_active clobber the offline status back.
    let cfg = DaemonConfig {
        pulse_period: Duration::from_secs(10),
        ..DaemonConfig::default()
    };
    let registered = spt_runtime::registry::registered(&perch::adapters_dir());
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert_eq!(
        set.len(),
        1,
        "the brain reconcile hosts the live-listen-onlined endpoint"
    );

    // ── a graceful offline transition un-hosts it (the exit guard, end-to-end) ──
    spt_store::info::set_status(&self_perch, STATUS_OFFLINE).unwrap();
    livehost::reconcile_once(
        &perch::owlery_dir(),
        &registered,
        &perch::adapters_dir(),
        &set,
        &cfg,
        StartReason::Cold,
    );
    assert!(set.is_empty(), "an offline transition un-hosts the lifecycle");

    // Reap any REAL daemon that auto-started against this throwaway home (hygiene).
    if let Ok(p) = std::fs::read_to_string(home.path().join("daemon.pid")) {
        if let Ok(p) = p.trim().parse::<u32>() {
            #[cfg(windows)]
            let _ = Command::new("taskkill")
                .no_window()
                .args(["/PID", &p.to_string(), "/F"])
                .output();
            #[cfg(unix)]
            let _ = Command::new("kill").args(["-9", &p.to_string()]).output();
        }
    }
}

+    // Let the driver's immediate first pulse settle (then it parks for 10s) so the

+    // offline flip is not clobbered by a concurrent set_last_active.

+    std::thread::sleep(Duration::from_millis(300));


Bash

cargo test -p spt --test live_firsthost_e2e 2>&1 | tail -12

Re-run W0.2 first-host int


   Compiling spt v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 3.34s
     Running tests\live_firsthost_e2e.rs (target\debug\deps\live_firsthost_e2e-cc25fc6588affad6.exe)

running 1 test
test live_listen_marks_online_and_brain_reconcile_hosts ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.42s


Int green. But the race means the W0.1 livehost unit (e) (15ms pulse + offline flip) is **flaky** for the same reason — it passed but could fail on CI. Harden (e) with the slow-pulse+settle fix (only (e) flips offline mid-drive). Also fold this `touch_active`-vs-`mark_offline` lost-update into the W0.4 hardening flag.


Edit

            let set = Arc::new(LiveSet::new());
            let cfg = fast_cfg();
            let run = |reason| {
                reconcile_once(
                    &perch::owlery_dir(),
                    &registered_now(),
                    &perch::adapters_dir(),
                    &set,
                    &cfg,
                    reason,
                )
            };
            run(StartReason::Cold);
            assert_eq!(set.len(), 1, "hosted while online");

            // Graceful signoff flips the Self perch offline.
            let self_perch = perch::resolve_perch_path("doyle", ParentHint::Infer);
            spt_store::info::set_status(&self_perch, spt_store::liveness::STATUS_OFFLINE).unwrap();

            let set = Arc::new(LiveSet::new());
            // A LONG pulse so the driver fires once then parks — the offline flip
            // below is then not racing the per-tick touch_active (a set_last_active
            // read-modify-write that would otherwise clobber the offline status
            // back). Negligible at the production 5s pulse; deterministic here.
            let cfg = DaemonConfig {
                pulse_period: Duration::from_secs(10),
                ..DaemonConfig::default()
            };
            let run = |reason| {
                reconcile_once(
                    &perch::owlery_dir(),
                    &registered_now(),
                    &perch::adapters_dir(),
                    &set,
                    &cfg,
                    reason,
                )
            };
            run(StartReason::Cold);
            assert_eq!(set.len(), 1, "hosted while online");

            // Let the driver's immediate first pulse settle (then it parks 10s).
            std::thread::sleep(Duration::from_millis(300));
            // Graceful signoff flips the Self perch offline.
            let self_perch = perch::resolve_perch_path("doyle", ParentHint::Infer);
            spt_store::info::set_status(&self_perch, spt_store::liveness::STATUS_OFFLINE).unwrap();


The file crates\spt-daemon\src\livehost.rs has been updated successfully. (file state is current in your context — no need to Read it back)

            let set = Arc::new(LiveSet::new());
            let cfg = fast_cfg();
            let run = |reason| {
                reconcile_once(
                    &perch::owlery_dir(),
                    &registered_now(),
                    &perch::adapters_dir(),
                    &set,
                    &cfg,
                    reason,
                )
            };
            run(StartReason::Cold);
            assert_eq!(set.len(), 1, "hosted while online");

            // Graceful signoff flips the Self perch offline.
            let self_perch = perch::resolve_perch_path("doyle", ParentHint::Infer);
            spt_store::info::set_status(&self_perch, spt_store::liveness::STATUS_OFFLINE).unwrap();

            let set = Arc::new(LiveSet::new());
            // A LONG pulse so the driver fires once then parks — the offline flip
            // below is then not racing the per-tick touch_active (a set_last_active
            // read-modify-write that would otherwise clobber the offline status
            // back). Negligible at the production 5s pulse; deterministic here.
            let cfg = DaemonConfig {
                pulse_period: Duration::from_secs(10),
                ..DaemonConfig::default()
            };
            let run = |reason| {
                reconcile_once(
                    &perch::owlery_dir(),
                    &registered_now(),
                    &perch::adapters_dir(),
                    &set,
                    &cfg,
                    reason,
                )
            };
            run(StartReason::Cold);
            assert_eq!(set.len(), 1, "hosted while online");

            // Let the driver's immediate first pulse settle (then it parks 10s).
            std::thread::sleep(Duration::from_millis(300));
            // Graceful signoff flips the Self perch offline.
            let self_perch = perch::resolve_perch_path("doyle", ParentHint::Infer);
            spt_store::info::set_status(&self_perch, spt_store::liveness::STATUS_OFFLINE).unwrap();

+            // A LONG pulse so the driver fires once then parks — the offline flip

+            // below is then not racing the per-tick touch_active (a set_last_active

+            // read-modify-write that would otherwise clobber the offline status

+            // back). Negligible at the production 5s pulse; deterministic here.

+                pulse_period: Duration::from_secs(10),

+                ..DaemonConfig::default()

+            // Let the driver's immediate first pulse settle (then it parks 10s).

+            std::thread::sleep(Duration::from_millis(300));


Run livehost units (confirm (e) deterministic with slow pulse) + check which existing tests exercise the interim listen-Psyche behavior (the strip could break them).


Bash

cargo test -p spt-daemon --lib livehost 2>&1 | tail -10

Re-run livehost units with (e) fix


running 5 tests
test livehost::tests::fresh_brain_rehydrates_an_online_live_endpoint ... ok
test livehost::tests::reconcile_does_not_double_host ... ok
test livehost::tests::reconcile_hosts_an_online_live_endpoint_and_drives_its_pulse ... ok
test livehost::tests::reconcile_skips_an_offline_live_endpoint ... ok
test livehost::tests::reconcile_unhosts_on_offline_transition ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 288 filtered out; finished in 0.68s


crates\spt\tests\contract_e2e.rs:362:        listen_err.contains("PSYCHE_SPAWNED:"),


livehost 5/5 green. `contract_e2e.rs:362` asserts `PSYCHE_SPAWNED:` from listen — the strip removed that. Read + update it to the W0.2 behavior (listen marks online, no inline psyche).


330	        drops.path().join(format!("{id}-commune.md")),
331	        "Source: self-commune\nmid-session note",
332	    )
333	    .unwrap();
334
335	    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse
336	    // tick (ingests the drop), and exits.
337	    let listen = Command::new(&spt_bin).no_window()
338	        .args([
339	            "api",
340	            "--adapter",
341	            "mock",
342	            "--manifest",
343	            &manifest_arg,
344	            "listen",
345	            id,
346	            "--parent-pid",
347	            &anchor,
348	            "--once",
349	        ])
350	        .env("SPT_HOME", home.path())
351	        .output()
352	        .expect("run api listen");
353	    let listen_err = String::from_utf8_lossy(&listen.stderr);
354	    assert!(
355	        listen.status.success(),
356	        "api listen exit: {:?}\n{listen_err}",
357	        listen.status
358	    );
359
360	    // 1. The Psyche spawned as a nested perch.
361	    assert!(
362	        listen_err.contains("PSYCHE_SPAWNED:"),
363	        "psyche must spawn: {listen_err}"
364	    );
365	    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
366	    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");
367	    assert_eq!(
368	        prec.state, "psyche",
369	        "nested perch must be a psyche endpoint"
370	    );
371
372	    // 2. The commune drop was ingested + deleted by spt-core (single-writer).
373	    assert!(
374	        !drops.path().join(format!("{id}-commune.md")).exists(),
375	        "commune drop must be ingested+deleted"
376	    );
377	    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked
378	    // two-tier context store — the per-perch flat context.md is gone.
379	    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);

        drops.path().join(format!("{id}-commune.md")),
        "Source: self-commune\nmid-session note",
    )
    .unwrap();

    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse
    // tick (ingests the drop), and exits.
    let listen = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "listen",
            id,
            "--parent-pid",
            &anchor,
            "--once",
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api listen");
    let listen_err = String::from_utf8_lossy(&listen.stderr);
    assert!(
        listen.status.success(),
        "api listen exit: {:?}\n{listen_err}",
        listen.status
    );

    // 1. The Psyche spawned as a nested perch.
    assert!(
        listen_err.contains("PSYCHE_SPAWNED:"),
        "psyche must spawn: {listen_err}"
    );
    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");
    assert_eq!(
        prec.state, "psyche",
        "nested perch must be a psyche endpoint"
    );

    // 2. The commune drop was ingested + deleted by spt-core (single-writer).
    assert!(
        !drops.path().join(format!("{id}-commune.md")).exists(),
        "commune drop must be ingested+deleted"
    );
    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked
    // two-tier context store — the per-perch flat context.md is gone.
    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);


contract_e2e's listen section asserts the *interim* lifecycle (psyche-spawn + pulse-ingest + context-routing) — all moved to the brain in W0.2. Must rework it to drive the brain-hosted path (preserve coverage). Read the full lifecycle section.


290	    let drop_dir = drops.path().display().to_string();
291	    manifest.session.commune_dir = Some(drop_dir.clone());
292	    manifest.session.signoff_dir = Some(drop_dir.clone());
293	    manifest.session.psyche_init.as_mut().unwrap().command =
294	        format!("\"{mock}\" --mode psyche --id {{id}} --session-id {{session_id}}");
295	    manifest.session.echo_commune.as_mut().unwrap().command =
296	        format!("\"{mock}\" --mode echo --id {{id}}");
297
298	    // Write the rig-rewritten manifest to a temp file for `--manifest`.
299	    let manifest_file = home.path().join("mock-live.toml");
300	    std::fs::write(
301	        &manifest_file,
302	        toml::to_string(&manifest).expect("serialize manifest"),
303	    )
304	    .unwrap();
305	    let manifest_arg = manifest_file.display().to_string();
306
307	    let id = "live-e2e";
308	    let session_id = "sid-live-1";
309	    let anchor = std::process::id().to_string();
310
311	    // Seed the startup record keyed by the anchor pid.
312	    let seed = Command::new(&spt_bin).no_window()
313	        .args([
314	            "api",
315	            "--adapter",
316	            "mock",
317	            "seed",
318	            "--pid",
319	            &anchor,
320	            "--session-id",
321	            session_id,
322	        ])
323	        .env("SPT_HOME", home.path())
324	        .status()
325	        .expect("run api seed");
326	    assert!(seed.success(), "api seed exit: {seed:?}");
327
328	    // Pre-drop a commune so the pulse tick in `listen --once` ingests it.
329	    std::fs::write(
330	        drops.path().join(format!("{id}-commune.md")),
331	        "Source: self-commune\nmid-session note",
332	    )
333	    .unwrap();
334
335	    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse
336	    // tick (ingests the drop), and exits.
337	    let listen = Command::new(&spt_bin).no_window()
338	        .args([
339	            "api",
340	            "--adapter",
341	            "mock",
342	            "--manifest",
343	            &manifest_arg,
344	            "listen",
345	            id,
346	            "--parent-pid",
347	            &anchor,
348	            "--once",
349	        ])
350	        .env("SPT_HOME", home.path())
351	        .output()
352	        .expect("run api listen");
353	    let listen_err = String::from_utf8_lossy(&listen.stderr);
354	    assert!(
355	        listen.status.success(),
356	        "api listen exit: {:?}\n{listen_err}",
357	        listen.status
358	    );
359
360	    // 1. The Psyche spawned as a nested perch.
361	    assert!(
362	        listen_err.contains("PSYCHE_SPAWNED:"),
363	        "psyche must spawn: {listen_err}"
364	    );
365	    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
366	    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");
367	    assert_eq!(
368	        prec.state, "psyche",
369	        "nested perch must be a psyche endpoint"
370	    );
371
372	    // 2. The commune drop was ingested + deleted by spt-core (single-writer).
373	    assert!(
374	        !drops.path().join(format!("{id}-commune.md")).exists(),
375	        "commune drop must be ingested+deleted"
376	    );
377	    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked
378	    // two-tier context store — the per-perch flat context.md is gone.
379	    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);
380	    assert!(
381	        std::fs::read_to_string(&ctx)
382	            .unwrap()
383	            .contains("mid-session note"),
384	        "ingested commune must land in the live context tier"
385	    );
386
387	    // 3. Graceful shutdown fires the echo-commune BEFORE teardown (3.3).
388	    let shutdown = Command::new(&spt_bin).no_window()
389	        .args([
390	            "api",
391	            "--adapter",
392	            "mock",
393	            "--manifest",
394	            &manifest_arg,
395	            "shutdown",
396	            id,
397	            "--session-id",
398	            session_id,
399	        ])
400	        .env("SPT_HOME", home.path())
401	        .output()
402	        .expect("run api shutdown");
403	    let sh_err = String::from_utf8_lossy(&shutdown.stderr);
404	    assert!(
405	        shutdown.status.success(),
406	        "api shutdown exit: {:?}\n{sh_err}",
407	        shutdown.status
408	    );
409	    assert!(

    let drop_dir = drops.path().display().to_string();
    manifest.session.commune_dir = Some(drop_dir.clone());
    manifest.session.signoff_dir = Some(drop_dir.clone());
    manifest.session.psyche_init.as_mut().unwrap().command =
        format!("\"{mock}\" --mode psyche --id {{id}} --session-id {{session_id}}");
    manifest.session.echo_commune.as_mut().unwrap().command =
        format!("\"{mock}\" --mode echo --id {{id}}");

    // Write the rig-rewritten manifest to a temp file for `--manifest`.
    let manifest_file = home.path().join("mock-live.toml");
    std::fs::write(
        &manifest_file,
        toml::to_string(&manifest).expect("serialize manifest"),
    )
    .unwrap();
    let manifest_arg = manifest_file.display().to_string();

    let id = "live-e2e";
    let session_id = "sid-live-1";
    let anchor = std::process::id().to_string();

    // Seed the startup record keyed by the anchor pid.
    let seed = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "seed",
            "--pid",
            &anchor,
            "--session-id",
            session_id,
        ])
        .env("SPT_HOME", home.path())
        .status()
        .expect("run api seed");
    assert!(seed.success(), "api seed exit: {seed:?}");

    // Pre-drop a commune so the pulse tick in `listen --once` ingests it.
    std::fs::write(
        drops.path().join(format!("{id}-commune.md")),
        "Source: self-commune\nmid-session note",
    )
    .unwrap();

    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse
    // tick (ingests the drop), and exits.
    let listen = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "listen",
            id,
            "--parent-pid",
            &anchor,
            "--once",
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api listen");
    let listen_err = String::from_utf8_lossy(&listen.stderr);
    assert!(
        listen.status.success(),
        "api listen exit: {:?}\n{listen_err}",
        listen.status
    );

    // 1. The Psyche spawned as a nested perch.
    assert!(
        listen_err.contains("PSYCHE_SPAWNED:"),
        "psyche must spawn: {listen_err}"
    );
    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");
    assert_eq!(
        prec.state, "psyche",
        "nested perch must be a psyche endpoint"
    );

    // 2. The commune drop was ingested + deleted by spt-core (single-writer).
    assert!(
        !drops.path().join(format!("{id}-commune.md")).exists(),
        "commune drop must be ingested+deleted"
    );
    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked
    // two-tier context store — the per-perch flat context.md is gone.
    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);
    assert!(
        std::fs::read_to_string(&ctx)
            .unwrap()
            .contains("mid-session note"),
        "ingested commune must land in the live context tier"
    );

    // 3. Graceful shutdown fires the echo-commune BEFORE teardown (3.3).
    let shutdown = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "shutdown",
            id,
            "--session-id",
            session_id,
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api shutdown");
    let sh_err = String::from_utf8_lossy(&shutdown.stderr);
    assert!(
        shutdown.status.success(),
        "api shutdown exit: {:?}\n{sh_err}",
        shutdown.status
    );
    assert!(


contract_e2e's section needs reworking to the brain-hosted path. Need to know its setup (does it register the adapter? imports?) for the brain reconcile to resolve. Read the setup region + imports.


255	/// ingested + deleted by spt-core → a graceful `spt shutdown` fires the
256	/// echo-commune *before* teardown.
257	#[test]
258	fn live_agent_lifecycle_e2e() {
259	    // [int->REQ-SEAM-PSYCHE]
260	    // [int->REQ-SEAM-HISTORY]
261	    // [int->REQ-API-3]
262	    let _serial = E2E_LOCK.lock().unwrap_or_else(|e| e.into_inner());
263	    let home = tempfile::tempdir().unwrap();
264	    std::env::set_var("SPT_HOME", home.path());
265	    start_inproc_daemon();
266
267	    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
268	    let mock_bin = sibling_bin("mock-session");
269	    assert!(
270	        mock_bin.exists(),
271	        "mock-session must be built: {}",
272	        mock_bin.display()
273	    );
274
275	    // An absolute drop dir for the watched commune/signoff files (the committed
276	    // manifest's relative ".mock" is rig-rewritten here, like [session.self]).
277	    let drops = tempfile::tempdir().unwrap();
278
279	    // Load the canonical mock manifest, then point its lifecycle templates at the
280	    // freshly-built helper (PATH isn't set up) + the absolute drop dir.
281	    let manifest_path = concat!(
282	        env!("CARGO_MANIFEST_DIR"),
283	        "/../../adapters/mock/manifest.toml"
284	    );
285	    let mut manifest = Manifest::from_toml_str(
286	        &std::fs::read_to_string(manifest_path).expect("read mock manifest"),
287	    )
288	    .expect("mock manifest valid");
289	    let mock = mock_bin.display().to_string();
290	    let drop_dir = drops.path().display().to_string();
291	    manifest.session.commune_dir = Some(drop_dir.clone());
292	    manifest.session.signoff_dir = Some(drop_dir.clone());
293	    manifest.session.psyche_init.as_mut().unwrap().command =
294	        format!("\"{mock}\" --mode psyche --id {{id}} --session-id {{session_id}}");

/// ingested + deleted by spt-core → a graceful `spt shutdown` fires the
/// echo-commune *before* teardown.
#[test]
fn live_agent_lifecycle_e2e() {
    // [int->REQ-SEAM-PSYCHE]
    // [int->REQ-SEAM-HISTORY]
    // [int->REQ-API-3]
    let _serial = E2E_LOCK.lock().unwrap_or_else(|e| e.into_inner());
    let home = tempfile::tempdir().unwrap();
    std::env::set_var("SPT_HOME", home.path());
    start_inproc_daemon();

    let spt_bin = PathBuf::from(env!("CARGO_BIN_EXE_spt"));
    let mock_bin = sibling_bin("mock-session");
    assert!(
        mock_bin.exists(),
        "mock-session must be built: {}",
        mock_bin.display()
    );

    // An absolute drop dir for the watched commune/signoff files (the committed
    // manifest's relative ".mock" is rig-rewritten here, like [session.self]).
    let drops = tempfile::tempdir().unwrap();

    // Load the canonical mock manifest, then point its lifecycle templates at the
    // freshly-built helper (PATH isn't set up) + the absolute drop dir.
    let manifest_path = concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/../../adapters/mock/manifest.toml"
    );
    let mut manifest = Manifest::from_toml_str(
        &std::fs::read_to_string(manifest_path).expect("read mock manifest"),
    )
    .expect("mock manifest valid");
    let mock = mock_bin.display().to_string();
    let drop_dir = drops.path().display().to_string();
    manifest.session.commune_dir = Some(drop_dir.clone());
    manifest.session.signoff_dir = Some(drop_dir.clone());
    manifest.session.psyche_init.as_mut().unwrap().command =
        format!("\"{mock}\" --mode psyche --id {{id}} --session-id {{session_id}}");


crates\spt\tests\contract_e2e.rs:16:use std::path::PathBuf;
crates\spt\tests\contract_e2e.rs:17:use std::process::Command;
crates\spt\tests\contract_e2e.rs:18:use std::sync::Mutex;
crates\spt\tests\contract_e2e.rs:19:use std::time::Duration;
crates\spt\tests\contract_e2e.rs:22:use common::CommandNoWindowExt;
crates\spt\tests\contract_e2e.rs:28:use spt_runtime::{AgentRuntime, Manifest, ManifestRuntime};
crates\spt\tests\contract_e2e.rs:29:use spt_store::perch::{self, ParentHint};
crates\spt\tests\contract_e2e.rs:30:use spt_store::spool;


contract_e2e (`live_agent_lifecycle_e2e`, tags REQ-SEAM-PSYCHE/HISTORY/API-3) drove the whole lifecycle through interim listen. Rework to W0.2: listen marks online → register manifest → brain reconcile hosts (psyche + first-tick commune ingest) → assert; shutdown-echo unchanged. Preserves all coverage via the brain. The shutdown echo is `graceful_signoff`-unconditional (no echo-gate race with the driver).


Edit

    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse
    // tick (ingests the drop), and exits.
    let listen = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "listen",
            id,
            "--parent-pid",
            &anchor,
            "--once",
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api listen");
    let listen_err = String::from_utf8_lossy(&listen.stderr);
    assert!(
        listen.status.success(),
        "api listen exit: {:?}\n{listen_err}",
        listen.status
    );

    // 1. The Psyche spawned as a nested perch.
    assert!(
        listen_err.contains("PSYCHE_SPAWNED:"),
        "psyche must spawn: {listen_err}"
    );
    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");
    assert_eq!(
        prec.state, "psyche",
        "nested perch must be a psyche endpoint"
    );

    // 2. The commune drop was ingested + deleted by spt-core (single-writer).
    assert!(
        !drops.path().join(format!("{id}-commune.md")).exists(),
        "commune drop must be ingested+deleted"
    );
    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked
    // two-tier context store — the per-perch flat context.md is gone.
    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);
    assert!(
        std::fs::read_to_string(&ctx)
            .unwrap()
            .contains("mid-session note"),
        "ingested commune must land in the live context tier"
    );

    // Register the (rig-rewritten) manifest so the brain's livehost reconcile can
    // resolve it from disk by the perch's adapter (M11-W0.2: the lifecycle is
    // brain-hosted; the brain resolves the manifest, the harness no longer hands it
    // over for hosting).
    {
        let src = perch::spt_home().join("srcs").join("mock");
        std::fs::create_dir_all(&src).unwrap();
        std::fs::write(
            src.join("manifest.toml"),
            toml::to_string(&manifest).expect("serialize manifest"),
        )
        .unwrap();
        spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();
    }

    // Listen (live-capable manifest): M11-W0.2 — BINDS the perch and marks it
    // status=online (the first-host handoff); the Psyche/pulse no longer spawn in
    // THIS process. The brain hosts the lifecycle (driven below).
    let listen = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "listen",
            id,
            "--parent-pid",
            &anchor,
            "--once",
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api listen");
    let listen_err = String::from_utf8_lossy(&listen.stderr);
    assert!(
        listen.status.success(),
        "api listen exit: {:?}\n{listen_err}",
        listen.status
    );

    // 1. listen marks the live perch ONLINE (the first-host handoff) — it does NOT
    // spawn the Psyche itself anymore.
    let self_perch = perch::resolve_perch_path(id, ParentHint::Infer);
    assert_eq!(
        spt_store::info::read_info(&self_perch)
            .and_then(|i| i.status)
            .as_deref(),
        Some(spt_store::liveness::STATUS_ONLINE),
        "listen marks the live-capable perch online: {listen_err}"
    );

    // 2. The brain reconcile hosts the lifecycle: spawns the Psyche (nested perch)
    // and its pulse driver ingests the pre-dropped commune on the immediate first
    // tick. (Exactly the production path — the brain owns the loop, CONTEXT:194.)
    let set = std::sync::Arc::new(spt_daemon::livehost::LiveSet::new());
    spt_daemon::livehost::reconcile_once(
        &perch::owlery_dir(),
        &spt_runtime::registry::registered(&perch::adapters_dir()),
        &perch::adapters_dir(),
        &set,
        &spt_daemon::config::DaemonConfig::default(),
        spt_daemon::brainproc::StartReason::Cold,
    );
    assert_eq!(set.len(), 1, "the brain hosts the onlined live endpoint");

    // The Psyche spawned as a nested perch (by the brain host).
    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
    let deadline = std::time::Instant::now() + Duration::from_secs(30);
    loop {
        if spt_store::info::read_info(&psyche_perch).map(|p| p.state == "psyche") == Some(true) {
            break;
        }
        assert!(
            std::time::Instant::now() < deadline,
            "the brain must spawn the psyche nested perch"
        );
        std::thread::sleep(Duration::from_millis(50));
    }

    // The commune drop was ingested + deleted by the brain pulse (single-writer)
    // and routed whole to the LIVE tier of the tracked two-tier context store.
    let commune = drops.path().join(format!("{id}-commune.md"));
    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);
    let deadline = std::time::Instant::now() + Duration::from_secs(30);
    loop {
        let ingested = !commune.exists()
            && std::fs::read_to_string(&ctx)
                .map(|s| s.contains("mid-session note"))
                .unwrap_or(false);
        if ingested {
            break;
        }
        assert!(
            std::time::Instant::now() < deadline,
            "the brain pulse must ingest the commune into the live context tier"
        );
        std::thread::sleep(Duration::from_millis(50));
    }


The file crates\spt\tests\contract_e2e.rs has been updated successfully. (file state is current in your context — no need to Read it back)

    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse
    // tick (ingests the drop), and exits.
    let listen = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "listen",
            id,
            "--parent-pid",
            &anchor,
            "--once",
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api listen");
    let listen_err = String::from_utf8_lossy(&listen.stderr);
    assert!(
        listen.status.success(),
        "api listen exit: {:?}\n{listen_err}",
        listen.status
    );

    // 1. The Psyche spawned as a nested perch.
    assert!(
        listen_err.contains("PSYCHE_SPAWNED:"),
        "psyche must spawn: {listen_err}"
    );
    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");
    assert_eq!(
        prec.state, "psyche",
        "nested perch must be a psyche endpoint"
    );

    // 2. The commune drop was ingested + deleted by spt-core (single-writer).
    assert!(
        !drops.path().join(format!("{id}-commune.md")).exists(),
        "commune drop must be ingested+deleted"
    );
    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked
    // two-tier context store — the per-perch flat context.md is gone.
    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);
    assert!(
        std::fs::read_to_string(&ctx)
            .unwrap()
            .contains("mid-session note"),
        "ingested commune must land in the live context tier"
    );

    // Register the (rig-rewritten) manifest so the brain's livehost reconcile can
    // resolve it from disk by the perch's adapter (M11-W0.2: the lifecycle is
    // brain-hosted; the brain resolves the manifest, the harness no longer hands it
    // over for hosting).
    {
        let src = perch::spt_home().join("srcs").join("mock");
        std::fs::create_dir_all(&src).unwrap();
        std::fs::write(
            src.join("manifest.toml"),
            toml::to_string(&manifest).expect("serialize manifest"),
        )
        .unwrap();
        spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();
    }

    // Listen (live-capable manifest): M11-W0.2 — BINDS the perch and marks it
    // status=online (the first-host handoff); the Psyche/pulse no longer spawn in
    // THIS process. The brain hosts the lifecycle (driven below).
    let listen = Command::new(&spt_bin).no_window()
        .args([
            "api",
            "--adapter",
            "mock",
            "--manifest",
            &manifest_arg,
            "listen",
            id,
            "--parent-pid",
            &anchor,
            "--once",
        ])
        .env("SPT_HOME", home.path())
        .output()
        .expect("run api listen");
    let listen_err = String::from_utf8_lossy(&listen.stderr);
    assert!(
        listen.status.success(),
        "api listen exit: {:?}\n{listen_err}",
        listen.status
    );

    // 1. listen marks the live perch ONLINE (the first-host handoff) — it does NOT
    // spawn the Psyche itself anymore.
    let self_perch = perch::resolve_perch_path(id, ParentHint::Infer);
    assert_eq!(
        spt_store::info::read_info(&self_perch)
            .and_then(|i| i.status)
            .as_deref(),
        Some(spt_store::liveness::STATUS_ONLINE),
        "listen marks the live-capable perch online: {listen_err}"
    );

    // 2. The brain reconcile hosts the lifecycle: spawns the Psyche (nested perch)
    // and its pulse driver ingests the pre-dropped commune on the immediate first
    // tick. (Exactly the production path — the brain owns the loop, CONTEXT:194.)
    let set = std::sync::Arc::new(spt_daemon::livehost::LiveSet::new());
    spt_daemon::livehost::reconcile_once(
        &perch::owlery_dir(),
        &spt_runtime::registry::registered(&perch::adapters_dir()),
        &perch::adapters_dir(),
        &set,
        &spt_daemon::config::DaemonConfig::default(),
        spt_daemon::brainproc::StartReason::Cold,
    );
    assert_eq!(set.len(), 1, "the brain hosts the onlined live endpoint");

    // The Psyche spawned as a nested perch (by the brain host).
    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));
    let deadline = std::time::Instant::now() + Duration::from_secs(30);
    loop {
        if spt_store::info::read_info(&psyche_perch).map(|p| p.state == "psyche") == Some(true) {
            break;
        }
        assert!(
            std::time::Instant::now() < deadline,
            "the brain must spawn the psyche nested perch"
        );
        std::thread::sleep(Duration::from_millis(50));
    }

    // The commune drop was ingested + deleted by the brain pulse (single-writer)
    // and routed whole to the LIVE tier of the tracked two-tier context store.
    let commune = drops.path().join(format!("{id}-commune.md"));
    let ctx = spt_store::contextstore::live_context_file(&home.path().join("tracked"), id);
    let deadline = std::time::Instant::now() + Duration::from_secs(30);
    loop {
        let ingested = !commune.exists()
            && std::fs::read_to_string(&ctx)
                .map(|s| s.contains("mid-session note"))
                .unwrap_or(false);
        if ingested {
            break;
        }
        assert!(
            std::time::Instant::now() < deadline,
            "the brain pulse must ingest the commune into the live context tier"
        );
        std::thread::sleep(Duration::from_millis(50));
    }

-    // Listen (live-capable manifest): binds, spawns the Psyche, runs one pulse

-    // tick (ingests the drop), and exits.

+    // Register the (rig-rewritten) manifest so the brain's livehost reconcile can

+    // resolve it from disk by the perch's adapter (M11-W0.2: the lifecycle is

+    // brain-hosted; the brain resolves the manifest, the harness no longer hands it

+        let src = perch::spt_home().join("srcs").join("mock");

+        std::fs::create_dir_all(&src).unwrap();

+            toml::to_string(&manifest).expect("serialize manifest"),

+        spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();

+    // Listen (live-capable manifest): M11-W0.2 — BINDS the perch and marks it

+    // status=online (the first-host handoff); the Psyche/pulse no longer spawn in

+    // THIS process. The brain hosts the lifecycle (driven below).

-    // 1. The Psyche spawned as a nested perch.

-        listen_err.contains("PSYCHE_SPAWNED:"),

-        "psyche must spawn: {listen_err}"

-    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));

-    let prec = spt_store::info::read_info(&psyche_perch).expect("psyche info.json");

+    // 1. listen marks the live perch ONLINE (the first-host handoff) — it does NOT

+    // spawn the Psyche itself anymore.

+    let self_perch = perch::resolve_perch_path(id, ParentHint::Infer);

-        "nested perch must be a psyche endpoint"

+        spt_store::info::read_info(&self_perch)

+        Some(spt_store::liveness::STATUS_ONLINE),

+        "listen marks the live-capable perch online: {listen_err}"

-    // 2. The commune drop was ingested + deleted by spt-core (single-writer).

-        !drops.path().join(format!("{id}-commune.md")).exists(),

-        "commune drop must be ingested+deleted"

+    // 2. The brain reconcile hosts the lifecycle: spawns the Psyche (nested perch)

+    // and its pulse driver ingests the pre-dropped commune on the immediate first

+    // tick. (Exactly the production path — the brain owns the loop, CONTEXT:194.)

+    let set = std::sync::Arc::new(spt_daemon::livehost::LiveSet::new());

+    spt_daemon::livehost::reconcile_once(

+        &spt_runtime::registry::registered(&perch::adapters_dir()),

+        &spt_daemon::config::DaemonConfig::default(),

+        spt_daemon::brainproc::StartReason::Cold,

-    // (D6a) an untagged commune routes whole to the LIVE tier of the tracked

-    // two-tier context store — the per-perch flat context.md is gone.

+    assert_eq!(set.len(), 1, "the brain hosts the onlined live endpoint");

+    // The Psyche spawned as a nested perch (by the brain host).

+    let psyche_perch = perch::resolve_perch_path("live-e2e-psyche", ParentHint::Explicit(id));

+    let deadline = std::time::Instant::now() + Duration::from_secs(30);

+        if spt_store::info::read_info(&psyche_perch).map(|p| p.state == "psyche") == Some(true) {

+            std::time::Instant::now() < deadline,

+            "the brain must spawn the psyche nested perch"

+        std::thread::sleep(Duration::from_millis(50));

+    // The commune drop was ingested + deleted by the brain pulse (single-writer)

+    // and routed whole to the LIVE tier of the tracked two-tier context store.

+    let commune = drops.path().join(format!("{id}-commune.md"));

-            .contains("mid-session note"),

-        "ingested commune must land in the live context tier"

+        let ingested = !commune.exists()

+            && std::fs::read_to_string(&ctx)

+                .map(|s| s.contains("mid-session note"))

+            "the brain pulse must ingest the commune into the live context tier"

// 3. Graceful shutdown fires the echo-commune BEFORE teardown (3.3).

let shutdown = Command::new(&spt_bin).no_window()


Bash

cargo test -p spt --test contract_e2e 2>&1 | tail -20

Run reworked contract_e2e


   Compiling spt-daemon v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
   Compiling spt v0.7.1 (C:\Users\decid\Documents\projects\spt-core\crates\spt)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 7.00s
     Running tests\contract_e2e.rs (target\debug\deps\contract_e2e-c4bceb7c1fd6f769.exe)

running 4 tests
test cold_api_call_autostarts_daemon_and_handoff_is_in_memory ... ok
test mock_adapter_drives_the_full_contract ... ok
SEEDED:106996
test live_agent_lifecycle_e2e ... ok
SEEDED:106996
test seed_then_listen_binds_and_relays ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.98s


All 4 contract_e2e pass (incl `seed_then_listen_binds_and_relays` — confirms the Relay swap relays correctly). Pre-flight clippy `--workspace` (catch strip fallout) + traceable, then a broad test sweep.


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

Workspace clippy after W0.2 strip


traceable-reqs check 2>&1 | grep -iE "error|FAIL|missing|REQ-SEAM-PSYCHE|REQ-DAEMON-1\b" | head; echo "EXIT=${PIPESTATUS[0]}"

traceable check


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


       Peer address seeding, both cold starts: durable peer-addrs.json (identity dir) maps peer pubkey → last-known dialable address; the pump's resolver consults it FIRST with id-only discovery fallback on miss or dial failure (a stale addr never strands a peer); written by the pairing ceremony (both sides, from the live connection) and by the pump on successful connect; post-join first sync and post-restart resync converge in seconds, not ~1 min (M8 decisions 14, 20)
  [OK] REQ-DAEMON-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Net-bind boot-race resilience: a daemon that comes up net-less (NetHost::start failed — e.g. the systemd unit autostarted before the network/DNS stack was ready, `Failed to create an address lookup service`) must SELF-HEAL — retry the net bring-up in the background with capped backoff and, on success, attach net to the broker + spawn the dispatcher/peer-pump (which today are gated on `net_up` at boot and so never start, leaving the node silently unreachable until a manual restart — kitsubito 2026-06-08). Status surfaces the net-less state honestly (a net-less broker renders as 'no connection', not only a pump-STALLED line with a bogus pre-boot heartbeat age). The installer's autostart unit waits for the network (`Wants=/After=network-online.target`) as belt-and-suspenders.
       A surfaced concurrent context pair is durably preserved (both versions, tracked artifacts) until a strictly dominating write clears it; no reconcile failure path discards an unmerged version (6.6, ADR-0013)
       Concurrent SQLite openers (registry/spool) must not fail with 'database is locked' (4.7)
       Stale registry entries degrade to fallback, never hard-fail (4.3)
       File-backed adapter [strings] (M12-W3-T3.1): a [strings] dot-path value MAY be an inline-table FILE POINTER `key = { file = "rel/path" }` resolved to the file's contents at get-string time, keeping large bodies (skill-instructions, hint text) out of the manifest. A value-position table with a `file` key IS the pointer form (reserved — cannot double as data). Per-adapter aux storage `adapters/<adapter>/strings/`; pointers resolve relative to it with CONTAINMENT (reject `..`/absolute escaping the dir). UPDATE-SAFETY: a LOCAL profile's file-pointers resolve relative to the user-owned local-profile dir (NOT adapter-shipped strings/, which adapter updates overwrite), or the local profile inlines. Validate-at-register (fail-fast on a bad/escaping/missing pointer) + LAZY read at get-string (live file edits reflect, no re-register) + skip-diagnostics on missing-at-read (no hard-crash, mirrors [digest]). Rides the same leaf-replace profile overlay as the rest of [strings].
       cross-node Gateway user-msg honored via advertised endpoint_type: a user-msg from a Gateway-typed origin survives the receive_wan funnel as user-msg (vs the fail-closed re-stamp), keyed on the QUIC-handshake-proven origin node (never wire `from`). Trust boundary = subnet membership (operator-ratified 2026-06-13); no defense against an in-subnet member forging the type. Instance.endpoint_type is an additive serde-default field extending REQ-INST-7's data model. Absent/unknown type → re-stamp (N-1 rollout grace)
       NTP TOTP offset: the pairing ceremony queries NTP at ceremony time (both sides) and applies the derived offset to the TOTP calculation in-process only; system-clock fallback when NTP is unreachable (offline LAN pairing unaffected — NTP failure never blocks a pairing that succeeds today); never sets the OS clock; no background sync loop (M8 decision 18; field trigger: enlyzeam clock >1 min off exceeds the ±1 window)
  [OK] REQ-SEAM-PSYCHE  required: [impl, unit, int]  stages: -doc +impl +unit +int
EXIT=0


clippy `--workspace` clean, traceable EXIT 0 (REQ-DAEMON-1 + REQ-SEAM-PSYCHE OK). Run the full spt + spt-daemon test suites to catch anything else depending on the interim listen behavior.


cargo test -p spt-daemon -p spt 2>&1 | tail -35

Full spt-daemon + spt test suites


test one_source_two_consumers_spans_and_merges ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.15s

     Running tests\twohost.rs (target\debug\deps\twohost-b228396b3b27f4d8.exe)

running 2 tests
test two_host_ladder_role_a ... ok
test two_host_ladder_role_b ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests\wanmsg.rs (target\debug\deps\wanmsg-b37733a07477a322.exe)

running 3 tests
test wan_user_msg_from_unproven_origin_is_restamped_at_the_funnel ... ok
test wan_message_lands_exactly_once_under_transport_origin ... ok
test receiver_restart_replays_feed_without_double_delivery ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.98s

     Running tests\xfer.rs (target\debug\deps\xfer-e61aa6210da36b04.exe)

running 2 tests
test push_survives_target_brain_restart_exactly_once ... ok
test fetch_lands_byte_identical_with_progress_both_ends ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s

   Doc-tests spt_daemon

running 0 tests

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


Tail shows passes through the end (doc-tests = suite completed). Confirm zero failures across the whole run.


cargo test -p spt-daemon -p spt 2>&1 | grep -iE "test result: FAILED|error\[|panicked|FAILED" | head -20; echo "---done (empty=all green)---"

Scan full suite for any failures