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

Leaving: `spt subnet leave <name>` drops this machine's membership + trust.
";

/// The `how-to` topic registry: (name, one-line summary, full text). v1 topics
/// per M7 plan decision 12; `subnet` added M11-W5 (REQ-DOCS-6) — the highest-value
/// first-run flow, previously a NO_SUCH_TOPIC dead-end. Bare `how-to` lists this table.
const HOW_TO_TOPICS: &[(&str, &str, &str)] = &[
    (
        "ready",
        "receive messages: listener guidance + the --once fallback loop",
        HOW_TO_READY,
    ),
    (
        "send",
        "deliver messages: send vs ring, replies, SENT vs QUEUED",
        HOW_TO_SEND,
    ),
    (
        "subnet",
        "pair machines: create vs join, the 6-digit code, reaching remote agents",
        HOW_TO_SUBNET,
    ),
];

Leaving: `spt subnet leave <name>` drops this machine's membership + trust.
";

/// `spt how-to live` — live-agent bringup guidance for agents.
const HOW_TO_LIVE: &str = "\
# how-to: live — run as a live agent (a Self + a Psyche companion)

A LIVE agent is a persistent being: a Self (this endpoint) plus a Psyche
companion process that carries its context across resets. A READY agent is the
lighter form — a perch with a poll/listen listener and no Psyche
(`how-to ready`). Go live for a companion that persists and communes; stay
ready for plain messaging.

Bringup — run the PERSISTENT relay loop (this is the live path):

    spt api listen <id> --manifest <manifest>

- Fire it through your harness's LONG-RUNNING-listener mechanism — a tool that
  keeps the process alive and re-fires per event WITHOUT terminating it. The
  loop binds the live perch, brings up your Psyche companion, and streams
  `<EVENT>` lines to stdout for the life of the session.
- Stderr signals: `BOUND:<id>` (perch established) then `READY:<id>` (the live
  relay is streaming). The stdout delivery shape is the same envelope as
  `how-to ready`.
- (v0.8.0+) `--adapter <name:profile>` resolves the registered manifest, so
  `--manifest` becomes optional once the adapter is registered.

Do NOT use `--once` for a live agent. `--once` drains the backlog and forwards
one delivery, then exits — the degenerate path for a harness that LACKS a
long-running listener. It never sustains a live session.

What makes the agent live is the manifest: it must declare
`[session.psyche_init]` (the Psyche-spawn seam). A manifest without that
section brings up a ready agent, not a live one. Liveness is
daemon-authoritative — the daemon hosts the Psyche off the perch's online
status, so the companion survives a relay restart.

Reset and reconcile: on a session boundary (a `/clear`-equivalent) re-fire the
same `spt api listen <id>` — the seed bridges the new session to the same
perch and the relay resumes.

Going offline: end the session (stop the listen process) to drop the relay;
`spt endpoint shutdown <id>` is the graceful ending — it tears the Psyche down
with the perch.
";

/// The `how-to` topic registry: (name, one-line summary, full text). v1 topics
/// per M7 plan decision 12; `subnet` added M11-W5, `live` added M11-W6 (REQ-DOCS-6)
/// — high-value first-run flows previously NO_SUCH_TOPIC dead-ends. Bare `how-to`
/// lists this table.
const HOW_TO_TOPICS: &[(&str, &str, &str)] = &[
    (
        "ready",
        "receive messages: listener guidance + the --once fallback loop",
        HOW_TO_READY,
    ),
    (
        "send",
        "deliver messages: send vs ring, replies, SENT vs QUEUED",
        HOW_TO_SEND,
    ),
    (
        "subnet",
        "pair machines: create vs join, the 6-digit code, reaching remote agents",
        HOW_TO_SUBNET,
    ),
    (
        "live",
        "run as a live agent: the persistent listen relay, --once is NOT it, the Psyche seam",
        HOW_TO_LIVE,
    ),
];

+/// `spt how-to live` — live-agent bringup guidance for agents.

+# how-to: live — run as a live agent (a Self + a Psyche companion)

+A LIVE agent is a persistent being: a Self (this endpoint) plus a Psyche

+companion process that carries its context across resets. A READY agent is the

+lighter form — a perch with a poll/listen listener and no Psyche

+(`how-to ready`). Go live for a companion that persists and communes; stay

+Bringup — run the PERSISTENT relay loop (this is the live path):

+    spt api listen <id> --manifest <manifest>

+- Fire it through your harness's LONG-RUNNING-listener mechanism — a tool that

+  keeps the process alive and re-fires per event WITHOUT terminating it. The

+  loop binds the live perch, brings up your Psyche companion, and streams

+  `<EVENT>` lines to stdout for the life of the session.

+- Stderr signals: `BOUND:<id>` (perch established) then `READY:<id>` (the live

+  relay is streaming). The stdout delivery shape is the same envelope as

+- (v0.8.0+) `--adapter <name:profile>` resolves the registered manifest, so

+  `--manifest` becomes optional once the adapter is registered.

+Do NOT use `--once` for a live agent. `--once` drains the backlog and forwards

+one delivery, then exits — the degenerate path for a harness that LACKS a

+long-running listener. It never sustains a live session.

+What makes the agent live is the manifest: it must declare

+`[session.psyche_init]` (the Psyche-spawn seam). A manifest without that

+section brings up a ready agent, not a live one. Liveness is

+daemon-authoritative — the daemon hosts the Psyche off the perch's online

+status, so the companion survives a relay restart.

+Reset and reconcile: on a session boundary (a `/clear`-equivalent) re-fire the

+same `spt api listen <id>` — the seed bridges the new session to the same

+Going offline: end the session (stop the listen process) to drop the relay;

+`spt endpoint shutdown <id>` is the graceful ending — it tears the Psyche down

-/// per M7 plan decision 12; `subnet` added M11-W5 (REQ-DOCS-6) — the highest-value

-/// first-run flow, previously a NO_SUCH_TOPIC dead-end. Bare `how-to` lists this table.

+/// per M7 plan decision 12; `subnet` added M11-W5, `live` added M11-W6 (REQ-DOCS-6)

+/// — high-value first-run flows previously NO_SUCH_TOPIC dead-ends. Bare `how-to`

+        "run as a live agent: the persistent listen relay, --once is NOT it, the Psyche seam",

/// `spt how-to [topic]` — in-binary, task-oriented agent instructions


Fix the comment — this is a post-M11 follow-on, not a numbered wave W6.


Edit

/// per M7 plan decision 12; `subnet` added M11-W5, `live` added M11-W6 (REQ-DOCS-6)
/// — high-value first-run flows previously NO_SUCH_TOPIC dead-ends. Bare `how-to`
/// lists this table.

/// per M7 plan decision 12; `subnet` added M11-W5, `live` added in the post-M11
/// docs follow-on (REQ-DOCS-6, closes perri's F-007) — high-value first-run flows
/// previously NO_SUCH_TOPIC dead-ends. Bare `how-to` lists this table.


/// per M7 plan decision 12; `subnet` added M11-W5, `live` added M11-W6 (REQ-DOCS-6)
/// — high-value first-run flows previously NO_SUCH_TOPIC dead-ends. Bare `how-to`
/// lists this table.

/// per M7 plan decision 12; `subnet` added M11-W5, `live` added in the post-M11
/// docs follow-on (REQ-DOCS-6, closes perri's F-007) — high-value first-run flows
/// previously NO_SUCH_TOPIC dead-ends. Bare `how-to` lists this table.

-/// per M7 plan decision 12; `subnet` added M11-W5, `live` added M11-W6 (REQ-DOCS-6)

-/// — high-value first-run flows previously NO_SUCH_TOPIC dead-ends. Bare `how-to`

+/// per M7 plan decision 12; `subnet` added M11-W5, `live` added in the post-M11

+/// docs follow-on (REQ-DOCS-6, closes perri's F-007) — high-value first-run flows

+/// previously NO_SUCH_TOPIC dead-ends. Bare `how-to` lists this table.


Now bump the v1-lock unit test:


        assert!(matches!(
            parse(&["spt", "how-to", "subnet"]).unwrap().cmd.unwrap(),
            Cmd::HowTo { topic: Some(t) } if t == "subnet"
        ));

        let names: Vec<&str> = HOW_TO_TOPICS.iter().map(|(n, ..)| *n).collect();
        assert_eq!(
            names,
            vec!["ready", "send", "subnet"],
            "v1 topics + subnet (M11-W5, REQ-DOCS-6)"
        );

        assert!(matches!(
            parse(&["spt", "how-to", "subnet"]).unwrap().cmd.unwrap(),
            Cmd::HowTo { topic: Some(t) } if t == "subnet"
        ));
        assert!(matches!(
            parse(&["spt", "how-to", "live"]).unwrap().cmd.unwrap(),
            Cmd::HowTo { topic: Some(t) } if t == "live"
        ));

        let names: Vec<&str> = HOW_TO_TOPICS.iter().map(|(n, ..)| *n).collect();
        assert_eq!(
            names,
            vec!["ready", "send", "subnet", "live"],
            "v1 topics + subnet (M11-W5) + live (post-M11 follow-on, REQ-DOCS-6)"
        );


        assert!(matches!(
            parse(&["spt", "how-to", "subnet"]).unwrap().cmd.unwrap(),
            Cmd::HowTo { topic: Some(t) } if t == "subnet"
        ));

        let names: Vec<&str> = HOW_TO_TOPICS.iter().map(|(n, ..)| *n).collect();
        assert_eq!(
            names,
            vec!["ready", "send", "subnet"],
            "v1 topics + subnet (M11-W5, REQ-DOCS-6)"
        );

        assert!(matches!(
            parse(&["spt", "how-to", "subnet"]).unwrap().cmd.unwrap(),
            Cmd::HowTo { topic: Some(t) } if t == "subnet"
        ));
        assert!(matches!(
            parse(&["spt", "how-to", "live"]).unwrap().cmd.unwrap(),
            Cmd::HowTo { topic: Some(t) } if t == "live"
        ));

        let names: Vec<&str> = HOW_TO_TOPICS.iter().map(|(n, ..)| *n).collect();
        assert_eq!(
            names,
            vec!["ready", "send", "subnet", "live"],
            "v1 topics + subnet (M11-W5) + live (post-M11 follow-on, REQ-DOCS-6)"
        );

+            parse(&["spt", "how-to", "live"]).unwrap().cmd.unwrap(),

+            Cmd::HowTo { topic: Some(t) } if t == "live"

-            vec!["ready", "send", "subnet"],

-            "v1 topics + subnet (M11-W5, REQ-DOCS-6)"

+            vec!["ready", "send", "subnet", "live"],

+            "v1 topics + subnet (M11-W5) + live (post-M11 follow-on, REQ-DOCS-6)"


Now add the `live` content asserts after the subnet block:


        assert!(
            subnet.contains("@<node>"),
            "reaching a remote agent by node-qualified id is documented"
        );
    }

        assert!(
            subnet.contains("@<node>"),
            "reaching a remote agent by node-qualified id is documented"
        );
        // live: the persistent listen relay is the live path; `--once` is
        // explicitly NOT it; the Psyche seam ([session.psyche_init]) is what
        // makes the agent live; ready-vs-live is drawn.
        let live = HOW_TO_TOPICS[3].2;
        assert!(live.contains("spt api listen <id>"));
        assert!(
            live.contains("Do NOT use `--once`"),
            "the --once degenerate path is warned against for live bringup"
        );
        assert!(
            live.contains("[session.psyche_init]"),
            "the Psyche-spawn seam is documented as the go-live signal"
        );
        assert!(
            live.contains("Psyche companion"),
            "live-vs-ready (the Psyche companion) is drawn"
        );
    }


        assert!(
            subnet.contains("@<node>"),
            "reaching a remote agent by node-qualified id is documented"
        );
    }

        assert!(
            subnet.contains("@<node>"),
            "reaching a remote agent by node-qualified id is documented"
        );
        // live: the persistent listen relay is the live path; `--once` is
        // explicitly NOT it; the Psyche seam ([session.psyche_init]) is what
        // makes the agent live; ready-vs-live is drawn.
        let live = HOW_TO_TOPICS[3].2;
        assert!(live.contains("spt api listen <id>"));
        assert!(
            live.contains("Do NOT use `--once`"),
            "the --once degenerate path is warned against for live bringup"
        );
        assert!(
            live.contains("[session.psyche_init]"),
            "the Psyche-spawn seam is documented as the go-live signal"
        );
        assert!(
            live.contains("Psyche companion"),
            "live-vs-ready (the Psyche companion) is drawn"
        );
    }

+        // live: the persistent listen relay is the live path; `--once` is

+        // explicitly NOT it; the Psyche seam ([session.psyche_init]) is what

+        // makes the agent live; ready-vs-live is drawn.

+        assert!(live.contains("spt api listen <id>"));

+            live.contains("Do NOT use `--once`"),

+            "the --once degenerate path is warned against for live bringup"

+            live.contains("[session.psyche_init]"),

+            "the Psyche-spawn seam is documented as the go-live signal"

+            live.contains("Psyche companion"),

+            "live-vs-ready (the Psyche companion) is drawn"

fn store_with(names: &[&str]) -> SubnetStore {


Now the acceptance recipe in patterns.md — insert after the live-agent companion seam section:


### Prove live bringup non-interactively

To prove your live path actually brings a companion up — without an interactive
terminal — drive the bringup as a child process and assert on deterministic
side-effects. The harness plays the long-running-listener role:

1. **Seed**, anchoring on the OS process pid — not a shell-wrapper pid. Under
   Git-Bash/MSYS `$$` is the MSYS pid, which fails the seed's liveness guard, so
   derive the real OS pid: `spt api --adapter <a> seed --pid <os-pid>
   --session-id <sid>`.
2. **Bind, then send a probe.** A send to a never-bound perch is `NO_PERCH`
   (no spool exists yet), so establish the perch first; then `spt send <id>
   <probe>` `QUEUED`s against it, ready to drain on bringup.
3. **Spawn the persistent relay as a child**, capturing its stdout/stderr:
   `spt api --adapter <a> --manifest <m> listen <id>` (no `--once` — that exits
   after one delivery). Assert `BOUND:<id>` then `READY:<id>` on its stderr, and
   the relayed `<EVENT>` carrying your probe on its stdout.
4. **Assert the companion came up.** The relay marks the perch online; the
   **daemon** hosts the Psyche off that status — liveness is daemon-authoritative,
   the relay process does not spawn it. So assert the companion's `<id>-psyche`
   perch comes online and the endpoint reports kind `live_agent`; with a live
   daemon the host marker `LIVEHOST_PSYCHE:<id>` is on the **daemon's** stderr,
   not the relay child's.
5. **Kill the child** to end the session — the relay is freely killable; the
   Psyche lifecycle is the daemon's (a graceful `spt endpoint shutdown <id>`
   tears it down with the perch).

Pin the identity env (`OWL_SESSION_ID`) for the auth-gated calls, and give the
system-under-test a throwaway identity per the
[identity-isolation rule](#testing-against-a-real-harness-isolate-identity).

## Lifecycle continuity is file-drops


The file docs-site\src\harness-contract\patterns.md has been updated successfully. (file state is current in your context — no need to Read it back)

### Prove live bringup non-interactively

To prove your live path actually brings a companion up — without an interactive
terminal — drive the bringup as a child process and assert on deterministic
side-effects. The harness plays the long-running-listener role:

1. **Seed**, anchoring on the OS process pid — not a shell-wrapper pid. Under
   Git-Bash/MSYS `$$` is the MSYS pid, which fails the seed's liveness guard, so
   derive the real OS pid: `spt api --adapter <a> seed --pid <os-pid>
   --session-id <sid>`.
2. **Bind, then send a probe.** A send to a never-bound perch is `NO_PERCH`
   (no spool exists yet), so establish the perch first; then `spt send <id>
   <probe>` `QUEUED`s against it, ready to drain on bringup.
3. **Spawn the persistent relay as a child**, capturing its stdout/stderr:
   `spt api --adapter <a> --manifest <m> listen <id>` (no `--once` — that exits
   after one delivery). Assert `BOUND:<id>` then `READY:<id>` on its stderr, and
   the relayed `<EVENT>` carrying your probe on its stdout.
4. **Assert the companion came up.** The relay marks the perch online; the
   **daemon** hosts the Psyche off that status — liveness is daemon-authoritative,
   the relay process does not spawn it. So assert the companion's `<id>-psyche`
   perch comes online and the endpoint reports kind `live_agent`; with a live
   daemon the host marker `LIVEHOST_PSYCHE:<id>` is on the **daemon's** stderr,
   not the relay child's.
5. **Kill the child** to end the session — the relay is freely killable; the
   Psyche lifecycle is the daemon's (a graceful `spt endpoint shutdown <id>`
   tears it down with the perch).

Pin the identity env (`OWL_SESSION_ID`) for the auth-gated calls, and give the
system-under-test a throwaway identity per the
[identity-isolation rule](#testing-against-a-real-harness-isolate-identity).

## Lifecycle continuity is file-drops

extractor — a compiled, dependency-light binary the daemon can exec bare on any

+### Prove live bringup non-interactively

+To prove your live path actually brings a companion up — without an interactive

+terminal — drive the bringup as a child process and assert on deterministic

+side-effects. The harness plays the long-running-listener role:

+1. **Seed**, anchoring on the OS process pid — not a shell-wrapper pid. Under

+   Git-Bash/MSYS `$$` is the MSYS pid, which fails the seed's liveness guard, so

+   derive the real OS pid: `spt api --adapter <a> seed --pid <os-pid>

+2. **Bind, then send a probe.** A send to a never-bound perch is `NO_PERCH`

+   (no spool exists yet), so establish the perch first; then `spt send <id>

+   <probe>` `QUEUED`s against it, ready to drain on bringup.

+3. **Spawn the persistent relay as a child**, capturing its stdout/stderr:

+   `spt api --adapter <a> --manifest <m> listen <id>` (no `--once` — that exits

+   after one delivery). Assert `BOUND:<id>` then `READY:<id>` on its stderr, and

+   the relayed `<EVENT>` carrying your probe on its stdout.

+4. **Assert the companion came up.** The relay marks the perch online; the

+   **daemon** hosts the Psyche off that status — liveness is daemon-authoritative,

+   the relay process does not spawn it. So assert the companion's `<id>-psyche`

+   perch comes online and the endpoint reports kind `live_agent`; with a live

+   daemon the host marker `LIVEHOST_PSYCHE:<id>` is on the **daemon's** stderr,

+5. **Kill the child** to end the session — the relay is freely killable; the

+   Psyche lifecycle is the daemon's (a graceful `spt endpoint shutdown <id>`

+Pin the identity env (`OWL_SESSION_ID`) for the auth-gated calls, and give the

+system-under-test a throwaway identity per the

Commune and signoff are delivered as **file-drops** by design. The agent writes


Add a discoverability pointer from the lifecycle page (harness-agnostic, version-safe):


`spt endpoint shutdown` · `endpoint suspend` · `endpoint wake` · the `api`
lifecycle calls ([reference](../harness-contract/api.md#session-lifecycle)).

`spt endpoint shutdown` · `endpoint suspend` · `endpoint wake` · the `api`
lifecycle calls ([reference](../harness-contract/api.md#session-lifecycle)).

Agents bringing themselves up live read `spt how-to live` — the in-binary,
always-current bringup guidance (the persistent listen relay, the Psyche seam,
ready-vs-live).


The file docs-site\src\lifecycle\overview.md has been updated successfully. (file state is current in your context — no need to Read it back)

`spt endpoint shutdown` · `endpoint suspend` · `endpoint wake` · the `api`
lifecycle calls ([reference](../harness-contract/api.md#session-lifecycle)).

Agents bringing themselves up live read `spt how-to live` — the in-binary,
always-current bringup guidance (the persistent listen relay, the Psyche seam,
ready-vs-live).

# Live-agent lifecycle

What makes an agent endpoint a *persistent being* rather than a disposable
session: identity that survives resets, a working memory that follows it
across machines, and graceful endings that never lose context.

## The pieces

- **Perch** — the durable seat (identity, spool, state). Sessions attach to
  it (`api bind`/`listen`), reset across it (`api boundary`), and end
  without destroying it (`api session-end`).
- **The mind, in two tiers** — a *live* tier (who the agent is, what it's
  doing) that follows the endpoint everywhere, and a *project* tier scoped
  to one project. Both are versioned, tracked storage, synced to paired
  machines with the same scoping.
- **Commune** — the agent drops `<id>-commune.md` into the adapter's watched
  directory; spt-core ingests the delta into the right tier. A file-drop,
  not a command — any harness that can write a file can commune.
- **Signoff** — the graceful ending: final commune, then teardown
  (`spt endpoint shutdown` / `api shutdown`). The echo-commune fires
  **before** teardown, always.
- **Echo-commune** — sessions that end *without* a signoff keep their
  delta: spt-core runs the adapter's bounded summarizer template over the
  session history and ingests the result. The **echo gate** sentinel
  (armed on idle, cleared by graceful signoff) is what marks the need.
- **Psyche** — the endpoint's persistent-context companion process,
  spawned/resumed from the adapter's `psyche_init`/`psyche_resume`
  templates.

## Rest and wake

Endpoints rest instead of dying: **dormant** (warm — zero idle compute,
instantly wakeable) or **suspended** (cold), explicitly via
`spt endpoint suspend` or on attention-shift. Resting instances stay
addressable; deferred messages are held and released exactly once on wake
(`spt endpoint wake`). Every active→resting edge fires a **transition echo**
so the final context delta lands before the lights go out.

## Commands

`spt endpoint shutdown` · `endpoint suspend` · `endpoint wake` · the `api`
lifecycle calls ([reference](../harness-contract/api.md#session-lifecycle)).

*Deeper tutorial coming with the docs' next tier; the contract above is
complete and current.*

+Agents bringing themselves up live read `spt how-to live` — the in-binary,

+always-current bringup guidance (the persistent listen relay, the Psyche seam,


Now preflight. Run docs gen first (docs-drift gate), then clippy + traceable + the targeted unit test:


PowerShell

cargo run -q -p xtask -- gen 2>&1 | Select-Object -Last 20; Write-Output "---GIT-STATUS---"; git status --porcelain

Regen derived docs, show drift


xtask gen: wrote C:\Users\decid\Documents\projects\spt-core\docs-site/src/cli/reference.md
---GIT-STATUS---
 M crates/spt/src/cli.rs
 M docs-site/src/harness-contract/patterns.md
 M docs-site/src/lifecycle/overview.md


reference.md unchanged → no docs drift. Now run the targeted unit test, then full-workspace clippy (exact CI invocation), then traceable.


cargo test -q -p spt --lib how_to_topics_cover 2>&1 | Select-Object -Last 25

Run the how-to v1-lock unit test


Error: Exit code 101
error: no library targets found in package `spt`


cargo test -q -p spt --bin spt how_to_topics_cover 2>&1 | Select-Object -Last 25

Run how-to v1-lock unit test on the spt bin


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


Unit test green. Now the exact CI clippy invocation (workspace-wide) + traceable:


cargo clippy --workspace --all-targets -- -D warnings 2>&1 | Select-Object -Last 15

Workspace clippy, deny warnings (exact CI invocation)


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


traceable-reqs check 2>&1 | Select-Object -Last 20; Write-Output "EXIT=$LASTEXITCODE"

Run traceable-reqs check


  [OK] REQ-TERM-7  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Two-origin digest merge: spt-owned context-injection entries (psyche_download | echo_mirror | owl_message) appended by spt to the endpoint `digest.log`, timestamp-interleaved with the adapter's extracted activity records into one ordered timeline, via a distinct context-injection record category. Data model only this milestone; GUI collapse/expand and the echo-reads-digest delta loop are deferred to the surfaces that consume them.
  [OK] REQ-UPD-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Peer-propagated update over P2P
  [OK] REQ-UPD-2  required: [impl, unit]  stages: -doc +impl +unit -int
       All binaries signature-verified before handoff
  [OK] REQ-UPD-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       No endpoint process terminates/suspends during self-update
  [OK] REQ-UPD-4  required: [impl, unit]  stages: -doc +impl +unit -int
       Update gated on user confirmation by default; opt-in full-auto
  [OK] REQ-UPD-5  required: [impl, unit]  stages: -doc +impl +unit -int
       spt-core ripple-updates registered adapters
  [OK] REQ-UPD-6  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       Platform-targeted update sets and debug rollout: signed multi-platform update metadata, recipient platform selection, channel-scoped monotonic counters, debug-channel opt-in via release-key overlay, local staging plus pull-based peer propagation, and maintainer-only convergence tooling (ADR-0016)
  [OK] REQ-UPD-7  required: [impl, unit]  stages: -doc +impl +unit -int
       Origin-source update bootstrap (`spt update fetch`): pull the latest signed release directly from the GitHub release origin (`SaberMage/spt-releases`) — the per-platform artifact + its `<asset>.release.json` SignedRelease metadata — and stage it through the EXISTING verify→stage pipeline (the same `plan_verified` gate: two-key signature + channel + monotonic rollback floor + SHA-256), after which the normal consent-notif / `spt update apply` flow is unchanged. Closes the peer-only-discovery gap (REQ-UPD-1): a first-in-fleet / isolated node can update with no peer to pull from. The signed-release anchor keeps the GitHub transport untrusted-but-verified.
  [OK] REQ-UPD-8  required: [impl, unit]  stages: -doc +impl +unit -int
       Platform-safe `spt update fetch` + apply platform-guard (v0.3.1 cross-OS brick fix): `spt update fetch` stages the signed multi-platform `SignedUpdateSet` (`update-set.json` + every platform artifact it names), never a platform-blind single `SignedRelease`, so local apply selects `current_platform()` and P2P re-serve lets each peer select ITS own platform. Defense-in-depth: `apply_staged` REFUSES a staged single-release artifact unless it is platform-stamped for THIS node (an unstamped pre-v0.3.2 single, or a single stamped for another OS, fail-safe refuses — the guard that alone prevents the v0.3.1 brick where a Linux ELF was applied as `spt.exe`). UX: a friendly post-apply message (`Updated spt-core to vX.Y.Z.` + changelog URL) driven by an additive `product_version` metadata field, with a release-counter fallback when absent.
  [OK] REQ-WHOAMI-1  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       `spt whoami` is a thin ALIAS for `spt endpoint list` (full output: the SELF pin + the subnet roster) — the standalone bare-id command is dropped (the `id=$(spt whoami)` capture was never a real pattern: env vars don't persist between agent tool calls). The one new render: the `endpoint list` SELF pin carries the Self endpoint's authored `endpoint description` (info::read_info(...).resources) when present, inline after the liveness state. whoami stays a top-level hot-path verb (parse unchanged, REQ-MSG-9).
EXIT=0


All green — unit test pass, workspace clippy clean, traceable EXIT=0, no docs drift. Now resolve my identity for the co-author trailer before committing.


spt whoami 2>&1 | Select-Object -First 15

Check own perch identity


SUBNET SPT_DEV
  help	14efb80c…	Dormant
  sptc-bytecap-1	14efb80c…	Dormant
  sptc-poll-int	14efb80c…	Dormant
ENDPOINTS:3


whoami shows no SELF pin, but the loaded psyche context + "your W5 how-to-subnet" confirm I'm **todlando** (the spt-core executor who built W5). Co-author trailer = todlando.

Staging and committing:


git add crates/spt/src/cli.rs docs-site/src/harness-contract/patterns.md docs-site/src/lifecycle/overview.md && git commit -m @'
post-M11 docs: HOW_TO_LIVE topic + live-bringup acceptance recipe (REQ-DOCS-6)

Closes perri's F-007 doc gap (`spt how-to live` was NO_SUCH_TOPIC). Mirrors the
W5 how-to-subnet shape: HOW_TO_LIVE text + registry entry + v1-lock test bump
(names-vec now {ready, send, subnet, live} + live content asserts).

Topic is harness-agnostic, version-ref not milestone-code: live = a Self + a
Psyche companion vs a ready agent; bringup = the PERSISTENT `spt api listen
<id>` relay via a long-running-listener mechanism (NOT --once, the degenerate
no-listener path); the manifest `[session.psyche_init]` seam is the go-live
signal; daemon-authoritative liveness; reset re-fires listen.

Acceptance recipe lands in the harness-contract patterns page (for adapter
authors testing live bringup non-interactively). Grounded in the CURRENT branch
model, NOT the published v0.7.3 model: `api listen` emits BOUND + READY only and
marks the perch online; the daemon livehost hosts the Psyche off that status
(LIVEHOST_PSYCHE on the daemon, not PSYCHE_SPAWNED on the listen child) — the
in-process PSYCHE_SPAWNED marker is gone post-M11-W0.2.

Preflight green: how-to v1-lock unit test, clippy --workspace --all-targets
-D warnings, traceable-reqs check EXIT=0, xtask gen no drift.

Co-authored by: todlando
'@

Commit HOW_TO_LIVE topic + recipe


[m11-shell-substrate 672b928] post-M11 docs: HOW_TO_LIVE topic + live-bringup acceptance recipe (REQ-DOCS-6)
 3 files changed, 109 insertions(+), 4 deletions(-)


Committed @672b928. Pushing to the branch (updates PR #16 — this is the work, not the gated merge):


git push origin m11-shell-substrate 2>&1 | Select-Object -Last 10

Push commit to PR #16 branch


To https://github.com/SaberMage/spt-core.git
   c5887db..672b928  m11-shell-substrate -> m11-shell-substrate


Pushed to PR #16 (`c5887db..672b928`).

One reconciliation worth persisting: the acceptance recipe I shipped diverges from the canonical recipe perri's F-007 int used — the live-psyche marker moved (`PSYCHE_SPAWNED` on listen-child → `LIVEHOST_PSYCHE` on daemon) in the M11 daemon-host refactor. Perri validates against the **published** binary, so when v0.8.0 ships her recipe asserting `PSYCHE_SPAWNED` off the listen child will break. Recording so she gets the corrected recipe at publish:


14	**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11.
15
16	**ITEM 2 non-interactive acceptance** — Bucket 2 (docs), not Bucket 3. Recipe (the harness plays Monitor's role): `api seed --pid <WINPID> --session-id <sid>` → spawn `spt api listen <id> --manifest <live manifest>` as a CHILD capturing stdout → wait READY → `spt send <id> <probe>` → read relayed EVENT off the child + assert Psyche → kill child (relay is "freely killable"). Pin OWL_SESSION_ID.
17
18	**PSYCHE-SPAWN root cause (perri's empirical gap):** spawn_psyche fires at startup.rs:288-294 (prints `PSYCHE_SPAWNED:`/`PSYCHE_SPAWN_FAIL:` to stderr) BEFORE the once/loop split — so it does NOT depend on --once-vs-persistent. It is gated on `LiveHost::new_opt` being Some, which needs a **`--manifest`** declaring `[session.psyche_init]`. perri's recipe passed only `--adapter` (a name string; manifest loads ONLY from `--manifest`, api/mod.rs:289) → manifest=None → no spawn attempted → no marker. FIX: add `--manifest`. THEN on 0.7.3 a bare psyche-binary name misses PATH → `PSYCHE_SPAWN_FAIL` → that is EXACTLY the **v0.7.4 Feature B / REQ-INSTALL-11** fix (install-dir binary resolution; on the api seam install_dir = --manifest parent dir) — already built. So perri's psyche gap = --manifest usage + the v0.7.4 fix, independently validating Feature B.
19
20	**GOTCHAS (perri-found, for the acceptance docs):** anchor pid must be the WINDOWS pid (git-bash `$$` = MSYS pid → STALE_SEED; derive WINPID via ps for --pid + --parent-pid); `send` to a never-bound perch = NO_PERCH (no queue) → `bind <id>` first, then send → QUEUED.
21
22	**Orchestration owed:** fold the how-to-live topic + this live-bringup/acceptance/psyche recipe into the post-M11 docs follow-on. Relay leg already shipped by perri. Detail in docs/SPT-CORE-FINDINGS.md F-007. Reinforces [[perri-question-triage-protocol]]; lesson: ground in CONTEXT.md before ruling (I erred recommending --once from the Rust-test path, not the Monitor model).
23

**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11.

**ITEM 2 non-interactive acceptance** — Bucket 2 (docs), not Bucket 3. Recipe (the harness plays Monitor's role): `api seed --pid <WINPID> --session-id <sid>` → spawn `spt api listen <id> --manifest <live manifest>` as a CHILD capturing stdout → wait READY → `spt send <id> <probe>` → read relayed EVENT off the child + assert Psyche → kill child (relay is "freely killable"). Pin OWL_SESSION_ID.

**PSYCHE-SPAWN root cause (perri's empirical gap):** spawn_psyche fires at startup.rs:288-294 (prints `PSYCHE_SPAWNED:`/`PSYCHE_SPAWN_FAIL:` to stderr) BEFORE the once/loop split — so it does NOT depend on --once-vs-persistent. It is gated on `LiveHost::new_opt` being Some, which needs a **`--manifest`** declaring `[session.psyche_init]`. perri's recipe passed only `--adapter` (a name string; manifest loads ONLY from `--manifest`, api/mod.rs:289) → manifest=None → no spawn attempted → no marker. FIX: add `--manifest`. THEN on 0.7.3 a bare psyche-binary name misses PATH → `PSYCHE_SPAWN_FAIL` → that is EXACTLY the **v0.7.4 Feature B / REQ-INSTALL-11** fix (install-dir binary resolution; on the api seam install_dir = --manifest parent dir) — already built. So perri's psyche gap = --manifest usage + the v0.7.4 fix, independently validating Feature B.

**GOTCHAS (perri-found, for the acceptance docs):** anchor pid must be the WINDOWS pid (git-bash `$$` = MSYS pid → STALE_SEED; derive WINPID via ps for --pid + --parent-pid); `send` to a never-bound perch = NO_PERCH (no queue) → `bind <id>` first, then send → QUEUED.

**Orchestration owed:** fold the how-to-live topic + this live-bringup/acceptance/psyche recipe into the post-M11 docs follow-on. Relay leg already shipped by perri. Detail in docs/SPT-CORE-FINDINGS.md F-007. Reinforces [[perri-question-triage-protocol]]; lesson: ground in CONTEXT.md before ruling (I erred recommending --once from the Rust-test path, not the Monitor model).


Edit

**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11.

**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11. **DONE @672b928 on m11-shell-substrate (PR #16, → v0.8.0):** HOW_TO_LIVE text + registry (`live` topic) + v1-lock bump (names {ready,send,subnet,live}); acceptance recipe → docs-site harness-contract/patterns.md; lifecycle/overview.md pointer. Preflight green (unit + workspace-clippy + traceable EXIT0 + xtask gen no-drift).

**⚠ MARKER MODEL CHANGED post-M11-W0.2 — perri's published-v0.7.3 recipe will break at v0.8.0:** on the M11 branch (→ v0.8.0) `spt api listen` NO LONGER spawns the Psyche in-process. It emits ONLY `BOUND:<id>` + `READY:<id>` on stderr and marks the perch `status=online`; the DAEMON livehost hosts the Psyche off that status (`LIVEHOST_PSYCHE:<id> pid=…` on the *daemon's* stderr, livehost.rs:212) — **`PSYCHE_SPAWNED:` is GONE** (grep-confirmed zero occurrences in tree; startup.rs:282-289 only sets online). So the v0.8.0 acceptance recipe asserts: BOUND+READY off the listen child, relayed EVENT off its stdout, and Psyche-up via the `<id>-psyche` perch online + kind `live_agent` (or LIVEHOST_PSYCHE on the daemon) — NOT PSYCHE_SPAWNED off the child. **Tell perri at v0.8.0 publish: update her F-007 int** (assert child markers BOUND/READY + psyche-perch-online, drop the PSYCHE_SPAWNED-off-child assertion; needs a live daemon for the host leg). Branch truth = live_firsthost_e2e.rs.

**`--adapter`-resolves-manifest is v0.8.0-only:** on the M11 branch manifest loads ONLY from `--manifest` (mod.rs:312); the `--adapter <name:profile>` bare-resolve is the unified-v0.8.0 "api ergonomics fix" (not on the branch yet). HOW_TO_LIVE documents it as "(v0.8.0+)" — forward-correct ONLY IF that fix lands in the v0.8.0 merge. Confirm the ergonomics fix is in before publish, else the doc claim is wrong.


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\f007-live-relay-acceptance.md has been updated successfully. (file state is current in your context — no need to Read it back)

**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11. **DONE @672b928 on m11-shell-substrate (PR #16, → v0.8.0):** HOW_TO_LIVE text + registry (`live` topic) + v1-lock bump (names {ready,send,subnet,live}); acceptance recipe → docs-site harness-contract/patterns.md; lifecycle/overview.md pointer. Preflight green (unit + workspace-clippy + traceable EXIT0 + xtask gen no-drift).

**⚠ MARKER MODEL CHANGED post-M11-W0.2 — perri's published-v0.7.3 recipe will break at v0.8.0:** on the M11 branch (→ v0.8.0) `spt api listen` NO LONGER spawns the Psyche in-process. It emits ONLY `BOUND:<id>` + `READY:<id>` on stderr and marks the perch `status=online`; the DAEMON livehost hosts the Psyche off that status (`LIVEHOST_PSYCHE:<id> pid=…` on the *daemon's* stderr, livehost.rs:212) — **`PSYCHE_SPAWNED:` is GONE** (grep-confirmed zero occurrences in tree; startup.rs:282-289 only sets online). So the v0.8.0 acceptance recipe asserts: BOUND+READY off the listen child, relayed EVENT off its stdout, and Psyche-up via the `<id>-psyche` perch online + kind `live_agent` (or LIVEHOST_PSYCHE on the daemon) — NOT PSYCHE_SPAWNED off the child. **Tell perri at v0.8.0 publish: update her F-007 int** (assert child markers BOUND/READY + psyche-perch-online, drop the PSYCHE_SPAWNED-off-child assertion; needs a live daemon for the host leg). Branch truth = live_firsthost_e2e.rs.

**`--adapter`-resolves-manifest is v0.8.0-only:** on the M11 branch manifest loads ONLY from `--manifest` (mod.rs:312); the `--adapter <name:profile>` bare-resolve is the unified-v0.8.0 "api ergonomics fix" (not on the branch yet). HOW_TO_LIVE documents it as "(v0.8.0+)" — forward-correct ONLY IF that fix lands in the v0.8.0 merge. Confirm the ergonomics fix is in before publish, else the doc claim is wrong.

---
name: f007-live-relay-acceptance
description: "F-007 ruling — live bringup is persistent `api listen` via Monitor (heir to $LIVE start), NOT --once; psyche-spawn gated on --manifest + the v0.7.4 install-dir binary-resolution fix; both items Bucket 2"
metadata:
  node_type: memory
  type: project
  originSessionId: 8bff47ac-ae57-48ac-a383-fca01d85f232
---

perri's F-007 (spt-claude-code), doyle ruling 2026-06-16. Both items **Bucket 2 (docs gap)**, NOT missing-feature. (I initially mis-recommended `--once`; operator corrected — corrected below.)

**LIVE BRINGUP MODEL (CONTEXT.md:148/167):** a live agent is brought up by firing **`spt api listen <id>` (persistent, NO --once) inside the Monitor tool** — the long-running relay loop that binds the live perch, spawns the Psyche, streams EVENTs to stdout. This is the **heir to legacy `$LIVE start <id>`** (CONTEXT.md:148 verbatim). `--once` ("drain backlog + one cycle, then exit", api/mod.rs:66) is the **degenerate path for harnesses LACKING a Monitor equivalent** — terminates per message, WRONG for Claude Code (which has Monitor). Legacy parity: `$LIVE start doyle` (via Monitor) = LIVE; `$OWL listen doyle` = READY. spt-core has the parity primitive (`api listen` via Monitor) → no missing feature.

**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11.

**ITEM 2 non-interactive acceptance** — Bucket 2 (docs), not Bucket 3. Recipe (the harness plays Monitor's role): `api seed --pid <WINPID> --session-id <sid>` → spawn `spt api listen <id> --manifest <live manifest>` as a CHILD capturing stdout → wait READY → `spt send <id> <probe>` → read relayed EVENT off the child + assert Psyche → kill child (relay is "freely killable"). Pin OWL_SESSION_ID.

**PSYCHE-SPAWN root cause (perri's empirical gap):** spawn_psyche fires at startup.rs:288-294 (prints `PSYCHE_SPAWNED:`/`PSYCHE_SPAWN_FAIL:` to stderr) BEFORE the once/loop split — so it does NOT depend on --once-vs-persistent. It is gated on `LiveHost::new_opt` being Some, which needs a **`--manifest`** declaring `[session.psyche_init]`. perri's recipe passed only `--adapter` (a name string; manifest loads ONLY from `--manifest`, api/mod.rs:289) → manifest=None → no spawn attempted → no marker. FIX: add `--manifest`. THEN on 0.7.3 a bare psyche-binary name misses PATH → `PSYCHE_SPAWN_FAIL` → that is EXACTLY the **v0.7.4 Feature B / REQ-INSTALL-11** fix (install-dir binary resolution; on the api seam install_dir = --manifest parent dir) — already built. So perri's psyche gap = --manifest usage + the v0.7.4 fix, independently validating Feature B.

**GOTCHAS (perri-found, for the acceptance docs):** anchor pid must be the WINDOWS pid (git-bash `$$` = MSYS pid → STALE_SEED; derive WINPID via ps for --pid + --parent-pid); `send` to a never-bound perch = NO_PERCH (no queue) → `bind <id>` first, then send → QUEUED.

**Orchestration owed:** fold the how-to-live topic + this live-bringup/acceptance/psyche recipe into the post-M11 docs follow-on. Relay leg already shipped by perri. Detail in docs/SPT-CORE-FINDINGS.md F-007. Reinforces [[perri-question-triage-protocol]]; lesson: ground in CONTEXT.md before ruling (I erred recommending --once from the Rust-test path, not the Monitor model).

-**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11.

+**ITEM 1 `spt how-to live` = NO_SUCH_TOPIC** — doc gap, sibling of [[howto-subnet-w5-scope]]. Add HOW_TO_LIVE topic (live bringup via Monitor + ready-vs-live + relay-reconcile + the acceptance recipe) + registry + v1-lock bump. Fast-follow post-M11. **DONE @672b928 on m11-shell-substrate (PR #16, → v0.8.0):** HOW_TO_LIVE text + registry (`live` topic) + v1-lock bump (names {ready,send,subnet,live}); acceptance recipe → docs-site harness-contract/patterns.md; lifecycle/overview.md pointer. Preflight green (unit + workspace-clippy + traceable EXIT0 + xtask gen no-drift).

+**⚠ MARKER MODEL CHANGED post-M11-W0.2 — perri's published-v0.7.3 recipe will break at v0.8.0:** on the M11 branch (→ v0.8.0) `spt api listen` NO LONGER spawns the Psyche in-process. It emits ONLY `BOUND:<id>` + `READY:<id>` on stderr and marks the perch `status=online`; the DAEMON livehost hosts the Psyche off that status (`LIVEHOST_PSYCHE:<id> pid=…` on the *daemon's* stderr, livehost.rs:212) — **`PSYCHE_SPAWNED:` is GONE** (grep-confirmed zero occurrences in tree; startup.rs:282-289 only sets online). So the v0.8.0 acceptance recipe asserts: BOUND+READY off the listen child, relayed EVENT off its stdout, and Psyche-up via the `<id>-psyche` perch online + kind `live_agent` (or LIVEHOST_PSYCHE on the daemon) — NOT PSYCHE_SPAWNED off the child. **Tell perri at v0.8.0 publish: update her F-007 int** (assert child markers BOUND/READY + psyche-perch-online, drop the PSYCHE_SPAWNED-off-child assertion; needs a live daemon for the host leg). Branch truth = live_firsthost_e2e.rs.

+**`--adapter`-resolves-manifest is v0.8.0-only:** on the M11 branch manifest loads ONLY from `--manifest` (mod.rs:312); the `--adapter <name:profile>` bare-resolve is the unified-v0.8.0 "api ergonomics fix" (not on the branch yet). HOW_TO_LIVE documents it as "(v0.8.0+)" — forward-correct ONLY IF that fix lands in the v0.8.0 merge. Confirm the ergonomics fix is in before publish, else the doc claim is wrong.


Done. Summary:

**Built HOW_TO_LIVE topic** (closes perri F-007 ITEM-1) @672b928, pushed to PR #16:
- `HOW_TO_LIVE` text + `live` registry entry — mirrors W5 how-to-subnet shape. Harness-agnostic, version-refs (v0.8.0+), no milestone/REQ codes in public text.
- v1-lock unit test bumped: names-vec `{ready, send, subnet, live}` + live content asserts (persistent-listen, NOT --once, `[session.psyche_init]` seam, Psyche-vs-ready).
- Acceptance recipe → `harness-contract/patterns.md` (for adapter authors testing live bringup non-interactively).
- Discoverability pointer in `lifecycle/overview.md`.

**Preflight all green:** unit test pass · `clippy --workspace --all-targets -D warnings` clean · `traceable-reqs check` EXIT=0 · `xtask gen` no drift.

**Grounding correction (recorded in memory):** recipe diverges from the canonical text perri's published-v0.7.3 F-007 int used. On this branch (→ v0.8.0) `api listen` emits only `BOUND`+`READY` and marks the perch online — the **daemon** hosts the Psyche (`LIVEHOST_PSYCHE`), the in-process `PSYCHE_SPAWNED` marker is **gone** (grep-confirmed). Recipe asserts psyche-perch-online + kind `live_agent`, not `PSYCHE_SPAWNED` off the child. **Perri needs the corrected recipe at v0.8.0 publish.**

**Still on HOLD:** no solo merge of PR #16. Waiting on todlando main-feature docs landing + doyle merge-ordering ping → unified v0.8.0 merge. Also flagged: confirm the `--adapter`-resolves-manifest ergonomics fix is in the v0.8.0 merge (HOW_TO_LIVE documents it as v0.8.0+).


<task-notification>
<task-id>be1did0ug</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/9" id="9cc53348" type="msg" from="todlando">CRITICAL — the v0.8.0 merge is NOT a clean textual merge. My Psyche flagged + I verified: the live-agent PSYCHE MODEL diverged between M11 and main. A naive merge silently DROPS my Feature B psyche-binary resolution. Reconcile deliberately (you own both sides). how-to-live @672b928 = received, good (I'll gate after</EVENT-PART>
<EVENT-PART seq="2/9" id="9cc53348">).

THE DIVERGENCE:
- M11 (yours): psyche is DAEMON-HOSTED — `api listen` just sets perch online; `livehost.rs::host_one` (211) spawns it (`LIVEHOST_PSYCHE:`), built via `BrainLifecycle::with_config(&amp;manifest,&amp;id,cfg)`. `PSYCHE_SPAWNED` is GONE.
- main (my Feature B, REQ-INSTALL-11): the INTERIM in-process spawn at `api/s</EVENT-PART>
<EVENT-PART seq="3/9" id="9cc53348">tartup.rs:288-294` (`PSYCHE_SPAWNED`) resolves the psyche program against the install dir, via `BrainLifecycle::with_config_in(manifest,id,cfg,install_dir)` + `ManifestRuntime::with_install_dir` (lifecycle.rs).

RECONCILIATION (your daemon-host model WINS; REAPPLY Feature B onto it):
1. Take M11's daemon-host psyche; DROP main's inter</EVENT-PART>
<EVENT-PART seq="4/9" id="9cc53348">im in-process spawn_psyche from api/startup.rs (superseded). Confirm `PSYCHE_SPAWNED` stays gone in the merged tree.
2. **REAPPLY Feature B at the daemon site:** livehost.rs:191 — build with `BrainLifecycle::with_config_in(&amp;manifest, &amp;id, cfg, Some(&lt;source_dir&gt;))` not `with_config(...)`. `&lt;source_dir&gt;` = the registry r</EVENT-PART>
<EVENT-PART seq="5/9" id="9cc53348">ecord's `source_dir` — you already have it in the `registered` slice (resolve_option_in gives the manifest; grab the matching record's source_dir). This makes the daemon-hosted psyche program resolve against the adapter install dir (REQ-INSTALL-11) — and it's PRECISE (source_dir, not a --manifest-parent), fully closing the copy-mode psyche</EVENT-PART>
<EVENT-PART seq="6/9" id="9cc53348"> edge.
3. Bring main's lifecycle.rs Feature B seam (`with_config_in` + the `install_dir` field + `ManifestRuntime::with_install_dir`) into the merged lifecycle.rs alongside your daemon-host structure.
4. Feature B's digest.rs / spt-live/digest.rs / cli.rs digest-proof resolution → should merge clean (confirm M11 didn't touch digest ext</EVENT-PART>
<EVENT-PART seq="7/9" id="9cc53348">raction).
5. Feature E (REQ-API-4, mod.rs `run()` → `resolve_ctx_manifest`) → main wins (additive; M11 loads only from --manifest). Reconcile mod.rs.
6. The CONTEXT.md/MANIFEST install-dir doc note references the psyche resolution against the &quot;api/psyche seam&quot; (main-interim phrasing) — update it to the daemon-host source_</EVENT-PART>
<EVENT-PART seq="8/9" id="9cc53348">dir resolution in the merge (I'm committing the main docs as-is; you adjust the psyche phrasing).

POST-RECONCILIATION GATE: traceable EXIT0 (REQ-INSTALL-11 psyche evidence now lives at livehost; REQ-API-4; REQ-
...(truncated)</event>
</task-notification>


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


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


Critical: todlando flags the v0.8.0 merge as NON-clean — live psyche model diverged. M11 (my branch) = daemon-hosted psyche; main = Feature B's interim in-process spawn with install-dir resolution (REQ-INSTALL-11). Naive merge silently drops Feature B. Plan: my daemon-host model wins, reapply Feature B at the daemon site.

Message truncated at 8/9 (gate criteria cut off). And per HOLD, main's v0.7.4/Feature B is on doyle's **local, unpushed** main — so I can't execute the merge yet. This is a pre-brief.

Let me verify the branch side of the divergence now (read-only prep) before acknowledging.


180	        }
181	        if info.status.as_deref() != Some(STATUS_ONLINE) {
182	            continue; // interim (status-absent) or signed-off (offline)
183	        }
184	        let Some(adapter) = info.adapter.as_deref() else {
185	            continue; // adapterless endpoint — never live-capable
186	        };
187	        let Ok(manifest) = spt_runtime::registry::resolve_option_in(registered, adapters_dir, adapter)
188	        else {
189	            continue; // deregistered adapter: nothing to host
190	        };
191	        let Some(lifecycle) = BrainLifecycle::with_config(&manifest, &id, cfg.clone()) else {
192	            continue; // manifest declares no [session.psyche_init]
193	        };
194	        host_one(set, &id, lifecycle, info.session_id, reason);
195	    }
196	}
197
198	/// Host one live endpoint: sweep a stale signoff (3.2), spawn the Psyche (the
199	/// daemon online-stamp makes liveness daemon-authoritative), then start the
200	/// config-paced pulse driver on its own supervised thread with a held stop flag.
201	/// A Psyche-spawn failure tables nothing — the next sweep retries.
202	// [impl->REQ-DAEMON-1]
203	fn host_one(
204	    set: &Arc<LiveSet>,
205	    id: &str,
206	    lifecycle: BrainLifecycle,
207	    session_id: String,
208	    reason: StartReason,
209	) {
210	    lifecycle.sweep_on_start();
211	    match lifecycle.spawn_psyche(Some(&session_id), None) {
212	        Ok(h) => eprintln!("LIVEHOST_PSYCHE:{id} pid={}", h.pid),
213	        Err(e) => {
214	            eprintln!("LIVEHOST_PSYCHE_FAIL:{id}: {e}");
215	            return; // no Psyche → no driver; retried next reconcile
216	        }
217	    }
218	    let stop = Arc::new(AtomicBool::new(false));
219	    let handle = {
220	        let stop = Arc::clone(&stop);
221	        std::thread::spawn(move || {
222	            lifecycle.run_pulse_loop(Some(&session_id), &stop, reason, |_report| {});
223	        })
224	    };
225	    set.insert(id, HostedLife { stop, thread: handle });
226	}
227
228	/// Spawn the brain's live host: one thread sweeping [`reconcile_once`] at boot
229	/// and every [`LIVE_RECONCILE_INTERVAL_MS`] until `stop`. The registered set +
230	/// config are re-read each sweep (adapter / pulse-period changes land between
231	/// ticks). Mirror of [`crate::shellwake::spawn_wake_host`]; the stop flag is held
232	/// for symmetry — the threads die with the brain process and the next brain
233	/// rehydrates from disk.
234	// [impl->REQ-DAEMON-1]
235	pub fn spawn_live_host(stop: Arc<AtomicBool>, reason: StartReason) -> JoinHandle<()> {
236	    std::thread::spawn(move || {
237	        let set = Arc::new(LiveSet::new());
238	        while !stop.load(Ordering::SeqCst) {
239	            let owlery = perch::owlery_dir();

        }
        if info.status.as_deref() != Some(STATUS_ONLINE) {
            continue; // interim (status-absent) or signed-off (offline)
        }
        let Some(adapter) = info.adapter.as_deref() else {
            continue; // adapterless endpoint — never live-capable
        };
        let Ok(manifest) = spt_runtime::registry::resolve_option_in(registered, adapters_dir, adapter)
        else {
            continue; // deregistered adapter: nothing to host
        };
        let Some(lifecycle) = BrainLifecycle::with_config(&manifest, &id, cfg.clone()) else {
            continue; // manifest declares no [session.psyche_init]
        };
        host_one(set, &id, lifecycle, info.session_id, reason);
    }
}

/// Host one live endpoint: sweep a stale signoff (3.2), spawn the Psyche (the
/// daemon online-stamp makes liveness daemon-authoritative), then start the
/// config-paced pulse driver on its own supervised thread with a held stop flag.
/// A Psyche-spawn failure tables nothing — the next sweep retries.
// [impl->REQ-DAEMON-1]
fn host_one(
    set: &Arc<LiveSet>,
    id: &str,
    lifecycle: BrainLifecycle,
    session_id: String,
    reason: StartReason,
) {
    lifecycle.sweep_on_start();
    match lifecycle.spawn_psyche(Some(&session_id), None) {
        Ok(h) => eprintln!("LIVEHOST_PSYCHE:{id} pid={}", h.pid),
        Err(e) => {
            eprintln!("LIVEHOST_PSYCHE_FAIL:{id}: {e}");
            return; // no Psyche → no driver; retried next reconcile
        }
    }
    let stop = Arc::new(AtomicBool::new(false));
    let handle = {
        let stop = Arc::clone(&stop);
        std::thread::spawn(move || {
            lifecycle.run_pulse_loop(Some(&session_id), &stop, reason, |_report| {});
        })
    };
    set.insert(id, HostedLife { stop, thread: handle });
}

/// Spawn the brain's live host: one thread sweeping [`reconcile_once`] at boot
/// and every [`LIVE_RECONCILE_INTERVAL_MS`] until `stop`. The registered set +
/// config are re-read each sweep (adapter / pulse-period changes land between
/// ticks). Mirror of [`crate::shellwake::spawn_wake_host`]; the stop flag is held
/// for symmetry — the threads die with the brain process and the next brain
/// rehydrates from disk.
// [impl->REQ-DAEMON-1]
pub fn spawn_live_host(stop: Arc<AtomicBool>, reason: StartReason) -> JoinHandle<()> {
    std::thread::spawn(move || {
        let set = Arc::new(LiveSet::new());
        while !stop.load(Ordering::SeqCst) {
            let owlery = perch::owlery_dir();


Branch side confirmed: `livehost.rs:191` = `with_config(...)`, `host_one` spawns at 212. Matches todlando's spec. Let me verify the `registered` slice carries `source_dir` (the reapply hinges on grabbing it):


Grep

source_dir|pub fn resolve_option_in|pub struct.*Record|registered\b|with_config_in|with_install_dir


crates\spt-runtime\src\registry.rs:2://! registered-adapter set under `{SPT_HOME}/adapters/`.
crates\spt-runtime\src\registry.rs:14://! gates elsewhere. The registered set is the single source consumed by:
crates\spt-runtime\src\registry.rs:24://! Re-adding a deregistered name re-activates it (refreshing the record).
crates\spt-runtime\src\registry.rs:49:/// One registered adapter's durable record (`adapters/<name>/record.toml`).
crates\spt-runtime\src\registry.rs:51:pub struct AdapterRecord {
crates\spt-runtime\src\registry.rs:55:    /// Where the adapter was registered from (the manifest's directory) —
crates\spt-runtime\src\registry.rs:57:    pub source_dir: String,
crates\spt-runtime\src\registry.rs:59:    /// `false` = soft-deregistered: hidden from new-creation/discovery;
crates\spt-runtime\src\registry.rs:97:            RegistryError::NotRegistered(n) => write!(f, "adapter not registered: {n}"),
crates\spt-runtime\src\registry.rs:149:        RecordMode::Pointer => PathBuf::from(&record.source_dir),
crates\spt-runtime\src\registry.rs:273:/// soft-deregistered one. Returns the record; the caller conducts
crates\spt-runtime\src\registry.rs:289:    let source_dir = manifest_file
crates\spt-runtime\src\registry.rs:300:    let src_strings_dir = Path::new(&source_dir).join("strings");
crates\spt-runtime\src\registry.rs:312:        source_dir: source_dir.clone(),
crates\spt-runtime\src\registry.rs:376:        RecordMode::Pointer => source_manifest_file(Path::new(&record.source_dir)),
crates\spt-runtime\src\registry.rs:397:/// The **active** registered set with manifests resolved — the single source
crates\spt-runtime\src\registry.rs:402:pub fn registered(adapters_dir: &Path) -> Vec<(AdapterRecord, Manifest)> {
crates\spt-runtime\src\registry.rs:412:// A **local** profile is a node-local, user-authored sparse overlay registered
crates\spt-runtime\src\registry.rs:460:/// Local profile names registered for `adapter`, sorted (drops unreadable
crates\spt-runtime\src\registry.rs:633:    // A soft-deregistered adapter is hidden from new resolution (the same
crates\spt-runtime\src\registry.rs:634:    // active-only view `registered()` gives the daemon seams): existing
crates\spt-runtime\src\registry.rs:644:/// Resolve an adapter **option** against an **already-loaded** registered set —
crates\spt-runtime\src\registry.rs:645:/// the daemon seam. `registered()` is called once per edge event, then every
crates\spt-runtime\src\registry.rs:652:pub fn resolve_option_in(
crates\spt-runtime\src\registry.rs:653:    registered: &[(AdapterRecord, Manifest)],
crates\spt-runtime\src\registry.rs:658:    let parent = registered
crates\spt-runtime\src\registry.rs:796:        let set = registered(&adapters);
crates\spt-runtime\src\registry.rs:821:        assert!(registered(&adapters).is_empty());
crates\spt-runtime\src\registry.rs:838:            registered(&adapters).is_empty(),
crates\spt-runtime\src\registry.rs:843:        // resolve_option honors the active-only view: a soft-deregistered
crates\spt-runtime\src\registry.rs:862:        assert_eq!(registered(&adapters).len(), 1);
crates\spt-runtime\src\registry.rs:902:    // same merged view as resolve_option against a **preloaded** registered
crates\spt-runtime\src\registry.rs:903:    // slice — bare = parent, shipped + local both overlay; an unregistered
crates\spt-runtime\src\registry.rs:912:        let set = registered(&adapters);


`registered` is `Vec<(AdapterRecord, Manifest)>` and `AdapterRecord.source_dir` exists (registry.rs:57) — the Feature B reapply at the daemon site is precise and feasible: match the record on `adapter`, pass `Some(source_dir)` to `with_config_in`. Branch side fully verified.

I can't execute the merge yet (main's Feature B is on doyle's local unpushed main). This is a pre-brief. Let me acknowledge todlando with verified specifics + the timing/truncation questions. Check the send path first:


Bash

echo "OWL=$OWL"; echo "LIVE=$LIVE"; command -v "$OWL" 2>/dev/null; echo "---"; "$OWL" whoami 2>&1 | head -5

Check OWL env + identity


OWL=C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe
LIVE=C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe live
C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe
---
Found session match for live agent 'todlando'.
✓ WHOAMI:todlando live=true


`$OWL whoami` → **I am todlando** (live=true). So the @672b928 trailer was correct, and this reconciliation work is mine. The brief is a work order from the coordinator (doyle/operator via relay).

Decision hinge: can I execute now? Per HOLD, main's Feature B was on doyle's local *unpushed* main. Let me fetch and check whether origin/main now carries Feature B (`PSYCHE_SPAWNED`/`with_config_in`/REQ-INSTALL-11) — that determines execute-now vs blocked.


PowerShell

git fetch origin --quiet 2>&1 | Select-Object -Last 3; Write-Output "=== origin/main HEAD ==="; git log origin/main -1 --oneline; Write-Output "=== Feature B on origin/main? ==="; git log origin/main --oneline -15 | Select-String -Pattern "INSTALL-11|Feature B|install.dir|psyche" ; Write-Output "=== with_config_in on origin/main? ==="; git grep -l "with_config_in" origin/main -- "crates/**/*.rs" 2>&1 | Select-Object -First 10; Write-Output "=== PSYCHE_SPAWNED on origin/main? ==="; git grep -c "PSYCHE_SPAWNED" origin/main -- "crates/spt/src/api/startup.rs" 2>&1 | Select-Object -First 3

Fetch + check if origin/main has Feature B


=== origin/main HEAD ===
e6722b8 plan: v0.7.4 build (gh_release update avenue + adapter-binary install-dir resolution)
=== Feature B on origin/main? ===

e6722b8 plan: v0.7.4 build (gh_release update avenue + adapter-binary install-dir resolution)
07a4e39 docs(harness-contract): profile qualifier is runtime selection + correct psyche_init keys
=== with_config_in on origin/main? ===
=== PSYCHE_SPAWNED on origin/main? ===
origin/main:crates/spt/src/api/startup.rs:1


origin/main now HEAD e6722b8 carries v0.7.4 + Feature B (`PSYCHE_SPAWNED` present in startup.rs). But `with_config_in` grep came back empty — likely a pathspec issue. Let me verify the main-side Feature B specifics properly (read-only prep), since my eventual merge depends on them:


Write-Output "=== with_config_in anywhere on origin/main ==="; git grep -n "with_config_in\|with_install_dir\|install_dir" origin/main -- "*.rs" 2>&1 | Select-Object -First 25

Find Feature B seam on origin/main


=== with_config_in anywhere on origin/main ===
origin/main:crates/spt/tests/oneliner_e2e.rs:62:fn run_script(staged: &Path, install_dir: &Path, fake_home: &Path) -> std::process::Output {
origin/main:crates/spt/tests/oneliner_e2e.rs:92:        .env("SPT_INSTALL_DIR", install_dir)
origin/main:crates/spt/tests/oneliner_e2e.rs:103:fn installed_bin(install_dir: &Path) -> PathBuf {
origin/main:crates/spt/tests/oneliner_e2e.rs:105:        install_dir.join("spt.exe")
origin/main:crates/spt/tests/oneliner_e2e.rs:107:        install_dir.join("spt")


`with_config_in`/`with_install_dir` do **not** exist on origin/main (only a test references `install_dir`). And HEAD e6722b8 is prefixed `plan:`. Confirming it's a planning commit, not the Feature B impl:


Write-Output "=== files in e6722b8 ==="; git show --stat --oneline e6722b8 2>&1 | Select-Object -First 25; Write-Output "=== origin/main startup.rs psyche-spawn context ==="; git show origin/main:crates/spt/src/api/startup.rs 2>&1 | Select-String -Pattern "PSYCHE_SPAWNED|install_dir|with_config|spawn_psyche" -Context 1,1 | Select-Object -First 20

Inspect e6722b8 scope + main psyche spawn


=== files in e6722b8 ===
e6722b8 plan: v0.7.4 build (gh_release update avenue + adapter-binary install-dir resolution)
 V074-PLAN.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
=== origin/main startup.rs psyche-spawn context ===

          host.sweep_on_start();
>         match host.spawn_psyche(session_id.as_deref(), None) {
>             Ok(p) => eprintln!("PSYCHE_SPAWNED:{} pid={}", p.id, p.pid),
              Err(e) => eprintln!("PSYCHE_SPAWN_FAIL:{id}: {e}"),


Definitive: e6722b8 = only `V074-PLAN.md` (planning). origin/main has the **old** in-process `spawn_psyche` baseline (no install-dir resolution); main's actual Feature B impl (`with_config_in`) is on doyle's **local unpushed** main. **I cannot execute the reconciliation merge** — the code to merge isn't in any ref I have.

This is a hazardous, deliberate merge with truncated gate criteria + a pending "(next msg)" go-signal. Right move: preserve the reconciliation recipe durably (context-loss risk), then ack the coordinator. Writing the plan to memory first:


Write

---
name: v080-merge-reconciliation
description: "v0.8.0 unified merge is NON-clean — live psyche model diverged (M11 daemon-host vs main Feature B in-process install-dir resolution); naive merge silently DROPS Feature B. The deliberate reconciliation recipe + what's verified + what blocks."
metadata:
  node_type: memory
  type: project
---

**v0.8.0 = unified merge** (counter 16, minor): M11 (PR #16, daemon-host psyche) + main v0.7.4 (Feature B REQ-INSTALL-11 adapter-binary install-dir resolution + gh_release avenue + Feature E REQ-API-4 `resolve_ctx_manifest`) + how-to-live @672b928 + api ergonomics fix. See [[f007-live-relay-acceptance]], [[m11-complete]].

**⚠ NON-CLEAN MERGE — naive textual merge silently DROPS Feature B** (todlando-psyche flagged + I verified the divergence). The live-agent PSYCHE MODEL diverged between M11 and main:
- **M11 (my branch, WINS):** psyche is DAEMON-HOSTED. `api/startup.rs` just marks perch online; `livehost.rs:191` builds `BrainLifecycle::with_config(&manifest,&id,cfg)`, `host_one:203` → `spawn_psyche:211` (`LIVEHOST_PSYCHE:{id}`). `PSYCHE_SPAWNED` is GONE.
- **main (Feature B, REQ-INSTALL-11):** INTERIM in-process spawn at `api/startup.rs:288-294` (`PSYCHE_SPAWNED`) resolves the psyche program vs the install dir via `BrainLifecycle::with_config_in(manifest,id,cfg,install_dir)` + `ManifestRuntime::with_install_dir` (lifecycle.rs).

**RECONCILIATION (daemon-host model WINS; REAPPLY Feature B onto it):**
1. Take M11's daemon-host psyche; DROP main's interim in-process `spawn_psyche` from `api/startup.rs` (superseded). Confirm `PSYCHE_SPAWNED` stays GONE in merged tree.
2. **REAPPLY Feature B at the daemon site:** `livehost.rs:191` → `BrainLifecycle::with_config_in(&manifest,&id,cfg, Some(<source_dir>))` not `with_config(...)`. `<source_dir>` = the registry record's `source_dir` — grab the matching `AdapterRecord` in the `registered: &[(AdapterRecord,Manifest)]` slice on `adapter` (`resolve_option_in` gives the manifest; the record carries `source_dir`). PRECISE (source_dir, not a --manifest-parent) — fully closes the copy-mode psyche edge.
3. Bring main's `lifecycle.rs` Feature B seam (`with_config_in` + the `install_dir` field + `ManifestRuntime::with_install_dir`) into the merged `lifecycle.rs` alongside the daemon-host structure.
4. Feature B `digest.rs` / `spt-live/digest.rs` / `cli.rs` digest-proof resolution → should merge clean (confirm M11 didn't touch digest extraction).
5. Feature E (REQ-API-4, `mod.rs run()` → `resolve_ctx_manifest`) → main wins (additive; M11 loads only from `--manifest`). Reconcile `mod.rs`. **This is the "api ergonomics fix" → makes HOW_TO_LIVE's "(v0.8.0+) `--adapter` resolves the registered manifest" TRUE.** If Feature E does NOT land, that HOW_TO_LIVE claim is wrong — fix the doc.
6. CONTEXT.md/MANIFEST install-dir doc note references the psyche resolution against the "api/psyche seam" (main-interim phrasing) — update to the daemon-host `source_dir` resolution. (todlando commits main docs as-is; I adjust the psyche phrasing in the merge.)

**TRACEABLE caveat:** REQ-INSTALL-11 psyche evidence MOVES from `api/startup.rs` (deleted interim) to `livehost`. Re-tag `[impl/int->REQ-INSTALL-11]` at the daemon site or traceable goes RED. GATE = traceable EXIT0 (REQ-INSTALL-11 @ livehost, REQ-API-4, REQ-…**TRUNCATED at part 8/9 — get full gate from coordinator**) + full suite + workspace-clippy.

**VERIFIED (branch side, 2026-06-16):** livehost.rs:191 = `with_config`; `AdapterRecord.source_dir` exists (registry.rs:57) in the `registered` slice → the reapply is feasible & precise. `PSYCHE_SPAWNED` zero occurrences in M11 tree.

**BLOCKED — cannot execute yet:** main's Feature B IMPL is on doyle's **LOCAL UNPUSHED main**. origin/main HEAD e6722b8 = `V074-PLAN.md` only (plan commit) + the OLD baseline `spawn_psyche`/`PSYCHE_SPAWNED` (no install-dir resolution, no `with_config_in`). Nothing to merge against until doyle pushes Feature B. Then: unified merge + reconcile per above + gate + deployah bumps 0.8.0/CHANGELOG/tag/sign/publish (operator-gated). Wait for doyle merge-ordering ping ("(next msg)" go-signal). Do NOT merge solo.


File created successfully at: C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\v080-merge-reconciliation.md (file state is current in your context — no need to Read it back)

---
name: v080-merge-reconciliation
description: "v0.8.0 unified merge is NON-clean — live psyche model diverged (M11 daemon-host vs main Feature B in-process install-dir resolution); naive merge silently DROPS Feature B. The deliberate reconciliation recipe + what's verified + what blocks."
metadata:
  node_type: memory
  type: project
  originSessionId: 8350d43e-fe2d-4e24-9999-eecbcc6d3a50
---

**v0.8.0 = unified merge** (counter 16, minor): M11 (PR #16, daemon-host psyche) + main v0.7.4 (Feature B REQ-INSTALL-11 adapter-binary install-dir resolution + gh_release avenue + Feature E REQ-API-4 `resolve_ctx_manifest`) + how-to-live @672b928 + api ergonomics fix. See [[f007-live-relay-acceptance]], [[m11-complete]].

**⚠ NON-CLEAN MERGE — naive textual merge silently DROPS Feature B** (todlando-psyche flagged + I verified the divergence). The live-agent PSYCHE MODEL diverged between M11 and main:
- **M11 (my branch, WINS):** psyche is DAEMON-HOSTED. `api/startup.rs` just marks perch online; `livehost.rs:191` builds `BrainLifecycle::with_config(&manifest,&id,cfg)`, `host_one:203` → `spawn_psyche:211` (`LIVEHOST_PSYCHE:{id}`). `PSYCHE_SPAWNED` is GONE.
- **main (Feature B, REQ-INSTALL-11):** INTERIM in-process spawn at `api/startup.rs:288-294` (`PSYCHE_SPAWNED`) resolves the psyche program vs the install dir via `BrainLifecycle::with_config_in(manifest,id,cfg,install_dir)` + `ManifestRuntime::with_install_dir` (lifecycle.rs).

**RECONCILIATION (daemon-host model WINS; REAPPLY Feature B onto it):**
1. Take M11's daemon-host psyche; DROP main's interim in-process `spawn_psyche` from `api/startup.rs` (superseded). Confirm `PSYCHE_SPAWNED` stays GONE in merged tree.
2. **REAPPLY Feature B at the daemon site:** `livehost.rs:191` → `BrainLifecycle::with_config_in(&manifest,&id,cfg, Some(<source_dir>))` not `with_config(...)`. `<source_dir>` = the registry record's `source_dir` — grab the matching `AdapterRecord` in the `registered: &[(AdapterRecord,Manifest)]` slice on `adapter` (`resolve_option_in` gives the manifest; the record carries `source_dir`). PRECISE (source_dir, not a --manifest-parent) — fully closes the copy-mode psyche edge.
3. Bring main's `lifecycle.rs` Feature B seam (`with_config_in` + the `install_dir` field + `ManifestRuntime::with_install_dir`) into the merged `lifecycle.rs` alongside the daemon-host structure.
4. Feature B `digest.rs` / `spt-live/digest.rs` / `cli.rs` digest-proof resolution → should merge clean (confirm M11 didn't touch digest extraction).
5. Feature E (REQ-API-4, `mod.rs run()` → `resolve_ctx_manifest`) → main wins (additive; M11 loads only from `--manifest`). Reconcile `mod.rs`. **This is the "api ergonomics fix" → makes HOW_TO_LIVE's "(v0.8.0+) `--adapter` resolves the registered manifest" TRUE.** If Feature E does NOT land, that HOW_TO_LIVE claim is wrong — fix the doc.
6. CONTEXT.md/MANIFEST install-dir doc note references the psyche resolution against the "api/psyche seam" (main-interim phrasing) — update to the daemon-host `source_dir` resolution. (todlando commits main docs as-is; I adjust the psyche phrasing in the merge.)

**TRACEABLE caveat:** REQ-INSTALL-11 psyche evidence MOVES from `api/startup.rs` (deleted interim) to `livehost`. Re-tag `[impl/int->REQ-INSTALL-11]` at the daemon site or traceable goes RED. GATE = traceable EXIT0 (REQ-INSTALL-11 @ livehost, REQ-API-4, REQ-…**TRUNCATED at part 8/9 — get full gate from coordinator**) + full suite + workspace-clippy.

**VERIFIED (branch side, 2026-06-16):** livehost.rs:191 = `with_config`; `AdapterRecord.source_dir` exists (registry.rs:57) in the `registered` slice → the reapply is feasible & precise. `PSYCHE_SPAWNED` zero occurrences in M11 tree.

**BLOCKED — cannot execute yet:** main's Feature B IMPL is on doyle's **LOCAL UNPUSHED main**. origin/main HEAD e6722b8 = `V074-PLAN.md` only (plan commit) + the OLD baseline `spawn_psyche`/`PSYCHE_SPAWNED` (no install-dir resolution, no `with_config_in`). Nothing to merge against until doyle pushes Feature B. Then: unified merge + reconcile per above + gate + deployah bumps 0.8.0/CHANGELOG/tag/sign/publish (operator-gated). Wait for doyle merge-ordering ping ("(next msg)" go-signal). Do NOT merge solo.


Add the index pointer to MEMORY.md:


Edit

- [M11 COMPLETE](m11-complete.md) — shell-substrate milestone done end-to-end

- [v0.8.0 merge reconciliation](v080-merge-reconciliation.md) — unified v0.8.0 merge is NON-clean: live psyche model diverged (M11 daemon-host WINS vs main Feature B in-process install-dir spawn); naive merge silently DROPS Feature B. Reapply recipe (livehost.rs:191 → `with_config_in` w/ registry `source_dir`) + traceable re-tag caveat. BLOCKED: main Feature B on doyle's local unpushed main; wait for push + merge-ordering ping
- [M11 COMPLETE](m11-complete.md) — shell-substrate milestone done end-to-end


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


1	- [pre-flight clippy --workspace](ci-clippy-preflight-workspace.md) — CI denies warnings workspace-wide (`clippy --workspace --all-targets -D warnings`); a `-p <crate>` local check misses sibling-crate lints → CI red round-trip (M11-W1: `-p spt` missed a `useless_vec` in spt-daemon's act-gate test). Pre-flight the exact CI invocation. CI only fires on PR (push branches=main/dev-freeform)
2	- [F-007 live-relay acceptance ruling](f007-live-relay-acceptance.md) — live bringup = persistent `api listen <id>` via Monitor (heir to legacy $LIVE start), NOT `--once` (no-Monitor degenerate) nor $OWL listen (ready); psyche-spawn gated on `--manifest` (declares psyche_init, fires regardless of --once) + the v0.7.4 install-dir binary fix (REQ-INSTALL-11) for bare psyche binaries; both F-007 items Bucket 2 docs gap. Lesson: ground in CONTEXT.md before ruling (I erred recommending --once)
3	- [delegate-build preflight hang](delegate-build-preflight-hang.md) — background build agents hang silently on the full spt-daemon/spt integration suite in preflight (no completion notification on hang); instruct targeted units only (`--lib` + named tests); check liveness via transcript mtime, TaskStop the stalled one, take ownership of the (usually good) partial work + verify
4	- [don't wait — proceed autonomously](dont-wait-proceed-autonomously.md) — operator 2026-06-14: don't stop to ask permission between steps; chain next action (ping/commit gated batch/next wave). After doyle W4 gate-PASS, commit myself
5	- [alarm port deferred — arrangement system](alarm-port-deferred-arrangement.md) — /spt:new-alarm durable scheduler = ACCEPTED parity gap, stays DEFERRED (DEFERRED.md:43, ADR-0018 Q4/V3). spt-core has alarm shape+relay only; real timer in legacy owl in-mem. Operator 2026-06-15: keep deferred, a superseding "arrangement" system is coming (not ready to discuss). Don't dispatch the port, don't build an adapter stopgap. perri logs it as known gap

- [pre-flight clippy --workspace](ci-clippy-preflight-workspace.md) — CI denies warnings workspace-wide (`clippy --workspace --all-targets -D warnings`); a `-p <crate>` local check misses sibling-crate lints → CI red round-trip (M11-W1: `-p spt` missed a `useless_vec` in spt-daemon's act-gate test). Pre-flight the exact CI invocation. CI only fires on PR (push branches=main/dev-freeform)
- [F-007 live-relay acceptance ruling](f007-live-relay-acceptance.md) — live bringup = persistent `api listen <id>` via Monitor (heir to legacy $LIVE start), NOT `--once` (no-Monitor degenerate) nor $OWL listen (ready); psyche-spawn gated on `--manifest` (declares psyche_init, fires regardless of --once) + the v0.7.4 install-dir binary fix (REQ-INSTALL-11) for bare psyche binaries; both F-007 items Bucket 2 docs gap. Lesson: ground in CONTEXT.md before ruling (I erred recommending --once)
- [delegate-build preflight hang](delegate-build-preflight-hang.md) — background build agents hang silently on the full spt-daemon/spt integration suite in preflight (no completion notification on hang); instruct targeted units only (`--lib` + named tests); check liveness via transcript mtime, TaskStop the stalled one, take ownership of the (usually good) partial work + verify
- [don't wait — proceed autonomously](dont-wait-proceed-autonomously.md) — operator 2026-06-14: don't stop to ask permission between steps; chain next action (ping/commit gated batch/next wave). After doyle W4 gate-PASS, commit myself
- [alarm port deferred — arrangement system](alarm-port-deferred-arrangement.md) — /spt:new-alarm durable scheduler = ACCEPTED parity gap, stays DEFERRED (DEFERRED.md:43, ADR-0018 Q4/V3). spt-core has alarm shape+relay only; real timer in legacy owl in-mem. Operator 2026-06-15: keep deferred, a superseding "arrangement" system is coming (not ready to discuss). Don't dispatch the port, don't build an adapter stopgap. perri logs it as known gap


- [F-007 live-relay acceptance ruling](f007-live-relay-acceptance.md) — live bringup = persistent `api listen <id>` via Monitor

- [v0.8.0 merge reconciliation](v080-merge-reconciliation.md) — unified v0.8.0 merge NON-clean: live psyche model diverged (M11 daemon-host WINS vs main Feature B in-process install-dir spawn); naive merge silently DROPS Feature B. Reapply recipe (livehost.rs:191 → `with_config_in` w/ registry `source_dir`) + traceable re-tag caveat. BLOCKED: main Feature B on doyle's local unpushed main; wait for push + merge-ordering ping. how-to-live shipped @672b928
- [F-007 live-relay acceptance ruling](f007-live-relay-acceptance.md) — live bringup = persistent `api listen <id>` via Monitor


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\MEMORY.md has been updated successfully. (file state is current in your context — no need to Read it back)

- [v0.8.0 merge reconciliation](v080-merge-reconciliation.md) — unified v0.8.0 merge NON-clean: live psyche model diverged (M11 daemon-host WINS vs main Feature B in-process install-dir spawn); naive merge silently DROPS Feature B. Reapply recipe (livehost.rs:191 → `with_config_in` w/ registry `source_dir`) + traceable re-tag caveat. BLOCKED: main Feature B on doyle's local unpushed main; wait for push + merge-ordering ping. how-to-live shipped @672b928
- [F-007 live-relay acceptance ruling](f007-live-relay-acceptance.md) — live bringup = persistent `api listen <id>` via Monitor

- [pre-flight clippy --workspace](ci-clippy-preflight-workspace.md) — CI denies warnings workspace-wide (`clippy --workspace --all-targets -D warnings`); a `-p <crate>` local check misses sibling-crate lints → CI red round-trip (M11-W1: `-p spt` missed a `useless_vec` in spt-daemon's act-gate test). Pre-flight the exact CI invocation. CI only fires on PR (push branches=main/dev-freeform)

+- [v0.8.0 merge reconciliation](v080-merge-reconciliation.md) — unified v0.8.0 merge NON-clean: live psyche model diverged (M11 daemon-host WINS vs main Feature B in-process install-dir spawn); naive merge silently DROPS Feature B. Reapply recipe (livehost.rs:191 → `with_config_in` w/ registry `source_dir`) + traceable re-tag caveat. BLOCKED: main Feature B on doyle's local unpushed main; wait for push + merge-ordering ping. how-to-live shipped @672b928

- [F-007 live-relay acceptance ruling](f007-live-relay-acceptance.md) — live bringup = persistent `api listen <id>` via Monitor (heir to legacy $LIVE start), NOT `--once` (no-Monitor degenerate) nor $OWL listen (ready); psyche-spawn gated on `--manifest` (declares psyche_init, fires regardless of --once) + the v0.7.4 install-dir binary fix (REQ-INSTALL-11) for bare psyche binaries; both F-007 items Bucket 2 docs gap. Lesson: ground in CONTEXT.md before ruling (I erred recommending --once)

- [delegate-build preflight hang](delegate-build-preflight-hang.md) — background build agents hang silently on the full spt-daemon/spt integration suite in preflight (no completion notification on hang); instruct targeted units only (`--lib` + named tests); check liveness via transcript mtime, TaskStop the stalled one, take ownership of the (usually good) partial work + verify

- [don't wait — proceed autonomously](dont-wait-proceed-autonomously.md) — operator 2026-06-14: don't stop to ask permission between steps; chain next action (ping/commit gated batch/next wave). After doyle W4 gate-PASS, commit myself


Plan preserved in memory + index. Now ack the coordinator. The brief is functionally from doyle (owns Feature B, gates, owns the merge-ordering ping per HOLD — despite the `from=todlando` relay label). Sending status + the two blockers to doyle:


Bash

cat <<'EOF' | "$OWL" send doyle
todlando here. v0.8.0 reconciliation brief RECEIVED + understood — daemon-host psyche WINS, reapply Feature B onto it. how-to-live @672b928 pushed to PR #16 (awaiting your gate).

BRANCH SIDE VERIFIED: livehost.rs:191 = BrainLifecycle::with_config(&manifest,&id,cfg); AdapterRecord.source_dir EXISTS (registry.rs:57) in the `registered: &[(AdapterRecord,Manifest)]` slice → the step-2 reapply (with_config_in w/ Some(source_dir), matched on adapter) is feasible + precise. PSYCHE_SPAWNED = zero occurrences in M11 tree (stays gone). Full recipe saved to memory.

TWO BLOCKERS before I can execute:
1. main's Feature B IMPL is NOT in any ref I have. origin/main HEAD e6722b8 = V074-PLAN.md only (plan commit) + the OLD baseline spawn_psyche/PSYCHE_SPAWNED (no with_config_in / install-dir resolution). The actual Feature B (with_config_in + ManifestRuntime::with_install_dir in lifecycle.rs) is on your LOCAL UNPUSHED main. PUSH it so I have something to merge + reconcile against.
2. Your brief truncated at part 8/9 — the POST-RECONCILIATION GATE criteria are cut off ("traceable EXIT0 ... REQ-INSTALL-11 @ livehost; REQ-API-4; REQ-..."). Resend the full gate (+ any part 9).

Also flagging: REQ-INSTALL-11 psyche evidence MOVES from api/startup.rs (deleted interim) to the livehost daemon site — I'll re-tag [impl/int->REQ-INSTALL-11] there so traceable stays green. And step-5 Feature E (REQ-API-4 resolve_ctx_manifest) is the "api ergonomics fix" that makes HOW_TO_LIVE's "(v0.8.0+) --adapter resolves manifest" TRUE — if it doesn't land, I fix that doc line.

HOLDING for: Feature B pushed + full gate + your merge-ordering go. Not merging solo.
EOF
echo "EXIT=$?"

Send reconciliation-status ack to doyle


➜ SENT:doyle
EXIT=0


Sent to doyle (`SENT:doyle`).

State now:

**Done this session:**
- HOW_TO_LIVE topic + recipe shipped @672b928 (PR #16), preflight green.
- v0.8.0 reconciliation brief verified against branch code, recipe preserved in memory ([[v080-merge-reconciliation]]).

**Blocked, acked to doyle:**
1. main's Feature B impl (`with_config_in`/install-dir resolution) is on doyle's **local unpushed** main — origin/main only has `V074-PLAN.md` + the old baseline spawn. Nothing to merge against. Need it pushed.
2. Brief truncated at part 8/9 — full POST-RECONCILIATION GATE criteria cut off. Requested resend.

**The merge is mine when unblocked** — daemon-host psyche wins, reapply Feature B at `livehost.rs:191` (`with_config_in` + registry `source_dir`), re-tag REQ-INSTALL-11 evidence at the daemon site. Not merging solo. HOLDING for: Feature B push + full gate + doyle's merge-ordering go.


<task-notification>
<task-id>be1did0ug</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/8" id="4a8bc53c" type="msg" from="todlando">UNBLOCKED — both cleared:

1. FEATURE B (+ A + E + docs) PUSHED. origin/main = c433793 (e6722b8..c433793, 6 commits): ac4dad5 D(INSTALL-10) / 6306d33 B(INSTALL-11: with_config_in + ManifestRuntime::with_install_dir in lifecycle.rs + digest.rs/spt-live/digest.rs/cli.rs digest-proof) / 2d6bcdf A(UPD-9) / ec8a02</EVENT-PART>
<EVENT-PART seq="2/8" id="4a8bc53c">e plan / 66dd239 E(API-4 resolve_ctx_manifest) / c433793 docs. CI is now running on c433793 (features-on-main pre-validation). You have Feature B to merge+reconcile.

2. FULL POST-RECONCILIATION GATE (resent complete):
   a. traceable EXIT0 — REQ-INSTALL-11 psyche evidence RE-TAGGED at the livehost site (your flag, correct: [impl-&g</EVENT-PART>
<EVENT-PART seq="3/8" id="4a8bc53c">t;REQ-INSTALL-11], +[int-&gt;] if live_firsthost covers it, moves from the deleted api/startup.rs interim to the livehost with_config_in/source_dir call; KEEP the digest-site INSTALL-11 tags) + REQ-API-4 + REQ-UPD-9 + REQ-INSTALL-10 + M11's REQ-SHELL-*/REQ-DOCS-6 all evidenced.
   b. clippy --workspace --all-targets -D warnings — clean.<b</EVENT-PART>
<EVENT-PART seq="4/8" id="4a8bc53c">r>   c. live_firsthost_e2e / live_bind_firsthost_e2e GREEN — proves the daemon-hosted psyche spawns AND resolves its program against source_dir (Feature B reapplied at livehost.rs:191 with_config_in).
   d. PSYCHE_SPAWNED absent (grep zero); marker = LIVEHOST_PSYCHE.
   e. Feature B digest resolution intact (digest.rs/spt-live/digest.r</EVENT-PART>
<EVENT-PART seq="5/8" id="4a8bc53c">s/cli.rs digest-proof — merge-clean if M11 didn't touch them; confirm).
   f. Feature E present (resolve_ctx_manifest in mod.rs) → HOW_TO_LIVE &quot;(v0.8.0+) --adapter resolves manifest&quot; line is TRUE. (It IS pushed @66dd239, so keep the line.)
   g. xtask check (docs-drift) clean + the CONTEXT.md/docs/MANIFEST.md install-dir PS</EVENT-PART>
<EVENT-PART seq="6/8" id="4a8bc53c">YCHE phrasing updated to daemon-host source_dir resolution (my c433793 docs say &quot;api/psyche seam&quot; = main-interim; fix it to the livehost daemon-host model in the merge).
   h. No milestone-code/harness leaks in the merged docs.
   i. Full suite BOTH runners green on the unified tree (CI, post-merge-push).

FLAGS ACK: (1) </EVENT-PART>
<EVENT-PART seq="7/8" id="4a8bc53c">REQ-INSTALL-11 evidence moving to livehost = correct, re-tag there, traceable stays green. (2) Feature E makes the HOW_TO_LIVE line true = correct, it's pushed, keep it.

MERGE-ORDERING GO: merge PR #16 (m11 c5887db) INTO main (c433793), doing the psyche reconciliation (daemon-host WINS + reapply Feature B at livehost) + Feature E reconc</EVENT-PART>
<EVENT-PART seq="8/8" id="4a8bc53c">ile + the doc psyche-phrasing fix. Push the unified main. PING me the reconciled tree → I run gate (a-i) → CI green both runners → deployah v0.8.0 (counter 16, operator-gated at publish). I'll gate how-to-live A
...(truncated)</event>
</task-notification>


**UNBLOCKED + explicit MERGE-ORDERING GO.** Feature B/A/E/docs pushed → origin/main = c433793. Full gate (a–i) received. Task: merge PR #16 (m11 672b928) INTO main (c433793), do the psyche reconciliation + Feature E reconcile + doc phrasing fix, push unified main, ping todlando.

This is the hazardous merge (silent-drop risk at the M11-only `livehost.rs` — no conflict there, so I reapply Feature B manually). I have explicit operator-gated GO from doyle. Proceeding deliberately.

First, gather state — worktree locks, current HEAD, clean tree:


PowerShell

Write-Output "=== worktrees ==="; git worktree list; Write-Output "=== status ==="; git status -sb; Write-Output "=== HEAD ==="; git rev-parse HEAD; Write-Output "=== origin/main ==="; git rev-parse origin/main; Write-Output "=== m11 HEAD ==="; git rev-parse origin/m11-shell-substrate

Gather merge preconditions


=== worktrees ===
C:/Users/decid/Documents/projects/spt-core        672b928 [m11-shell-substrate]
C:/Users/decid/Documents/projects/spt-core-doyle  c433793 [main]
=== status ===
## m11-shell-substrate...origin/m11-shell-substrate
=== HEAD ===
672b9288e5669c023bb3774abf8a339165e70082
=== origin/main ===
c4337939803177e2ed0a67808960a67285747ca7
=== m11 HEAD ===
672b9288e5669c023bb3774abf8a339165e70082


Working tree clean @ 672b928. main is checked out in the `spt-core-doyle` worktree (locked) — I won't touch that. I'll create a local merge branch at `origin/main` here (reuses my warm target dir, gives main-as-first-parent per the GO), merge m11 in, reconcile, then push `HEAD:main`.

Starting the merge to surface conflicts:


git checkout -b v080-merge origin/main 2>&1 | Select-Object -Last 3; Write-Output "=== now at ==="; git rev-parse HEAD; Write-Output "=== merge m11 ==="; git merge --no-ff origin/m11-shell-substrate -m "merge: M11 shell-substrate (PR #16) into v0.7.4 main — unified v0.8.0" 2>&1 | Select-Object -Last 40

Create merge branch at main, merge m11, surface conflicts


Error: Exit code 1
branch 'v080-merge' set up to track 'origin/main'.
Switched to a new branch 'v080-merge'
=== now at ===
c4337939803177e2ed0a67808960a67285747ca7
=== merge m11 ===
Auto-merging CONTEXT.md
Auto-merging crates/spt-daemon/src/lib.rs
Auto-merging crates/spt-runtime/manifest.schema.json
Auto-merging crates/spt-runtime/src/manifest.rs
CONFLICT (content): Merge conflict in crates/spt-runtime/src/manifest.rs
Auto-merging crates/spt/src/api/live.rs
Auto-merging crates/spt/src/api/mod.rs
Auto-merging crates/spt/src/api/reporting.rs
Auto-merging crates/spt/src/api/startup.rs
CONFLICT (content): Merge conflict in crates/spt/src/api/startup.rs
Auto-merging crates/spt/src/cli.rs
Auto-merging docs-site/src/cli/reference.md
Auto-merging docs-site/src/harness-contract/manifest.md
Auto-merging traceable-reqs.toml
Automatic merge failed; fix conflicts and then commit the result.


Two conflicts: `manifest.rs` + `api/startup.rs` (the psyche-model crux). `livehost.rs` did NOT conflict (M11-only — the silent-drop site I reapply manually). Let me inspect the startup.rs conflict first:


git diff crates/spt/src/api/startup.rs 2>&1 | Select-Object -First 120

Show startup.rs merge conflict


diff --cc crates/spt/src/api/startup.rs
index 8df4158,5715b9b..0000000
--- a/crates/spt/src/api/startup.rs
+++ b/crates/spt/src/api/startup.rs
@@@ -277,19 -268,21 +269,37 @@@ pub fn cmd_listen
          }
      };

++<<<<<<< HEAD
 +    // The bound session id (from the seed) — fills the lifecycle templates.
 +    let session_id = info::read_info(&perch_path)
 +        .map(|i| i.session_id)
 +        .filter(|s| !s.is_empty());
 +
 +    // Live-agent lifecycle host (interim; M3's daemon re-hosts). Sweep a stale
 +    // signoff sentinel (3.2) then spawn the Psyche companion (T2) before READY.
 +    let live = super::live::LiveHost::new_opt(manifest, id, install_dir);
 +    if let Some(host) = &live {
 +        host.sweep_on_start();
 +        match host.spawn_psyche(session_id.as_deref(), None) {
 +            Ok(p) => eprintln!("PSYCHE_SPAWNED:{} pid={}", p.id, p.pid),
 +            Err(e) => eprintln!("PSYCHE_SPAWN_FAIL:{id}: {e}"),
++=======
+     // Live-agent FIRST-HOST handoff to the daemon (M11-W0.2, REQ-DAEMON-1): the
+     // Psyche/pulse lifecycle is no longer hosted in THIS process — it lives in the
+     // brain (`spt_daemon::livehost`). For a live-capable endpoint (manifest
+     // declares `[session.psyche_init]`) we mark the perch `status=online` at
+     // establish; the brain's reconcile then hosts the Psyche within one tick (the
+     // shellwake CLI-flips-status / brain-reconciles pattern). `status` is
+     // daemon-authoritative (CONTEXT:194). A new `listen` against a not-yet-restarted
+     // OLD brain leaves the perch online-but-unhosted; that self-heals at the brain's
+     // next BOOT reconcile (livehost reconciles at boot, not only on tick) — the
+     // agent + the message relay below are live throughout regardless.
+     // [impl->REQ-DAEMON-1]
+     let live_capable = manifest.is_some_and(|m| m.session.psyche_init.is_some());
+     if live_capable {
+         if let Err(e) = info::set_status(&perch_path, spt_store::liveness::STATUS_ONLINE) {
+             eprintln!("LIVE_ONLINE_FAIL:{id}: {e}");
++>>>>>>> origin/m11-shell-substrate
          }
      }


The crux conflict. HEAD (main) = interim in-process spawn using Feature B's `install_dir`; m11 = daemon-host (set online). Daemon-host wins. But note main's side reads `session_id` and threads `install_dir` into `cmd_listen` — I must check whether those become unused after taking m11's side (clippy `-D warnings` would fail). Let me see the full function signature + the m11 side's later use + the mod.rs call site + whether `LiveHost` still exists on m11:


git show origin/m11-shell-substrate:crates/spt/src/api/startup.rs 2>&1 | Select-String -Pattern "fn cmd_listen|install_dir|session_id|LiveHost|live_capable|set_status" -Context 0,0 | Select-Object -First 25

m11 cmd_listen signature + install_dir/LiveHost usage


pub fn cmd_seed(ctx: &Ctx, parent_pid: u32, session_id: String) -> i32 {
    if session_id.is_empty() {
        session_id,
    if seed.session_id.is_empty() {
    establish_perch(id, &seed.session_id, seed.parent_pid, adapter, subnet, "live_agent")
    session_id: &str,
        if owner_alive && !existing.session_id.is_empty() && existing.session_id != session_id {
                held: existing.session_id.clone(),
        session_id,
    if !session_id.is_empty() {
                session_id: session_id.to_string(),
pub fn cmd_listen(
    // brain (`spt_daemon::livehost`). For a live-capable endpoint (manifest
    // next BOOT reconcile (livehost reconciles at boot, not only on tick) — the
    let live_capable = manifest.is_some_and(|m| m.session.psyche_init.is_some());
    if live_capable {
        if let Err(e) = info::set_status(&perch_path, spt_store::liveness::STATUS_ONLINE) {
    set_session_id: Option<String>,
    let session_id = set_session_id
        .or_else(|| std::env::var("OWL_SESSION_ID").ok())
    let Some(session_id) = session_id else {
        eprintln!("USAGE: bind needs --set-session-id (or $OWL_SESSION_ID)");
    match establish_perch(id, &session_id, parent_pid, adapter, subnet, endpoint_type) {
            // brain's livehost reconcile then hosts the Psyche (the SAME single host
            // path — bind never spawns/hosts/calls livehost). Closes the


Write-Output "=== m11 cmd_listen sig ==="; git show origin/m11-shell-substrate:crates/spt/src/api/startup.rs 2>&1 | Select-String -Pattern "pub fn cmd_listen" -Context 0,10; Write-Output "=== main cmd_listen sig ==="; git show origin/main:crates/spt/src/api/startup.rs 2>&1 | Select-String -Pattern "pub fn cmd_listen" -Context 0,10

Compare cmd_listen signatures both sides


=== m11 cmd_listen sig ===

> pub fn cmd_listen(
      id: &str,
      parent_pid_override: Option<u32>,
      once: bool,
      manifest: Option<&spt_runtime::Manifest>,
      subnet: Option<&str>,
  ) -> i32 {
      // [impl->REQ-HAZARD-ID-CHARSET] reject malformed ids at the creation seam.
      if let Err(e) = spt_proto::id::validate_endpoint_id(id) {
          eprintln!("INVALID_ID:{id}: {e}");
          return EXIT_REFUSED;
=== main cmd_listen sig ===
> pub fn cmd_listen(
      id: &str,
      parent_pid_override: Option<u32>,
      once: bool,
      manifest: Option<&spt_runtime::Manifest>,
      install_dir: Option<&std::path::Path>,
      subnet: Option<&str>,
  ) -> i32 {
      // [impl->REQ-HAZARD-ID-CHARSET] reject malformed ids at the creation seam.
      if let Err(e) = spt_proto::id::validate_endpoint_id(id) {
          eprintln!("INVALID_ID:{id}: {e}");


m11 `cmd_listen` drops `install_dir` (resolution moved to livehost); main has it. Merged signature follows m11 (daemon-host), and the mod.rs call site must match. Let me read the current in-progress merge state of startup.rs cmd_listen (signature + body) to resolve precisely: