88	title = "The claude-spt [digest] extractor (claude-spt-digest) maps Claude Code's JSONL transcript to the published digest-record NDJSON contract (role∈{input,agent,tool}; text/tool/ts), emitting RAW records for spt-core's renderer (ADR-0019)"
89	required_stages = ["impl", "unit", "int"]  # impl: tools/claude-spt-digest (Rust — CC JSONL -> digest NDJSON; per-session locate of <slug>/<session_id>.jsonl under the projects root via the {session_id} the command receives; native UTF-8 stdout; defensive ~ expansion) + ci/digest/build.sh gate. unit: the crate's cargo tests (9: input/agent/tool mapping, arg-key preference, thinking+tool_result+noise+bad-json drops, UTF-8 round-trip, contract invariants, tilde expansion) run by ci/digest/build.sh. int: ci/digest/digest-proof-int.sh — `spt adapter digest-proof claude-spt --sample` on live v0.7.2 ran the published-shape extractor command (`--session {session_id} --in {source}`) and rendered DIGEST_PROOF_OK (5 parsed / 0 dropped). GREEN 2026-06-15 (registry left clean via soft-remove). Was F-004-blocked (digest-proof --sample passed an empty substitution-key map, cli.rs:5135) — doyle's spt-core PR#14 fix (v0.7.2, release counter 14) now fills {id}+{session_id} + an optional --session, matching runtime. doc deferred (the mapping is documented in the module + manifest [digest] comment; a docs-site section lands with the doc slice).
90
91	[[requirements]]
92	id = "REQ-SKILL-LIVE"
93	title = "/sptc:live upgrades THIS CC session to a LiveAgent: the claude-spt:live profile overlays [session.psyche_init] (daemon spawns the Psyche), the claude-spt-psyche runner keeps the Psyche claude alive (one --continue turn per pulse, NOT one-shot `claude -p`), and the live body activates the :live composite + a resident Monitor relay as the single delivery pipe"
94	required_stages = ["doc", "impl", "unit", "int"]  # doc: adapter/strings/skills/live.md (activation + poll/relay reconcile + Psyche-is-daemon-managed). impl: [profiles.live.session.psyche_init] in adapter/claude-spt.toml (the LiveAgent seam, 4 daemon keys) + tools/claude-spt-psyche (the resident runner). unit: claude-spt-psyche cargo tests (arg parse, seed/pulse/poll command construction, blank-pulse guard) run by ci/psyche/build.sh. int: ci/psyche/live-relay-int.sh — plays CC's Monitor role (spawns `spt api --adapter claude-spt:live --manifest <claude-spt.toml> listen <id>` as a persistent CHILD, NOT --once) under a disposable id, and asserts BOTH legs on real spt 0.7.3: PSYCHE-SPAWN (`PSYCHE_SPAWNED:{id}-psyche pid=…`, fired because the merged manifest declares psyche_init — startup.rs spawn_psyche, doyle-traced 2026-06-16) + RELAY (BOUND/READY + a probe relayed as the escaped <EVENT> envelope + live_agent kind). PASS live. Two gotchas baked in (doyle, post-M11 how-to-live docs): anchor on the WINDOWS pid not git-bash `$$` (else STALE_SEED); `bind` before `send` (else NO_PERCH, no queue). The psyche-binary resolves via the F-006 PATH interim on 0.7.3; on v0.8.0 Feature B/REQ-INSTALL-11 resolves it from the manifest dir (the int skips the psyche leg with a logged note only if neither holds). The int version-branches the PSYCHE-SPAWN MARKER (M11 restructure, doyle 2026-06-16): <0.8.0 asserts PSYCHE_SPAWNED off the listen child (in-process spawn); >=0.8.0 asserts the {id}-psyche perch ONLINE (daemon livehost hosts it off the perch online status; LIVEHOST_PSYCHE on the daemon) — the >=0.8.0 leg is PROVISIONAL, finalized against the merged tree at the v0.8.0 publish ping. Relay leg unchanged across versions. Old "daemon loop staged" / "no non-interactive bringup" blockers both CLEARED (loop shipped 0.7.3; --manifest + persistent-child-listen is the non-interactive path — F-007 resolved to docs-gap, not missing feature).
95
96	[[requirements]]
97	id = "REQ-UPS-INJECTION"
98	title = "UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands"
99	required_stages = ["doc", "impl", "unit", "int"]  # doc: ADR-0002. impl: user-prompt-submit.sh (skill-injection + drain) + _common.sh. unit: render_frames + sptc_skill_key tests. int: ci/hooks/poll-int.sh — the poll->additionalContext drain round-trip vs live v0.7.1 (<EVENT> envelope, render_frames confirm-match, multi-message). UPS-fires-on-slash empirical = ADR-0002 validation (CC 2.1.177); skill-injection resolve = registration-int 4c.
100
101	# ── Milestone: CI (autonomous fleet) — minted on-start. Deterministic gates + git push-trigger +
102	# spt reporting bus. Seeded from SCOPE.md "CI model" + docs/CI.md. Real-claude acceptance is a
103	# later slice (doc-only now). All harness-agnostic shell; runs on the Win+Linux fleet.
104
105	[[requirements]]
106	id = "REQ-CI-GATES"
107	title = "Deterministic gates (shell-syntax + unit tests + traceable-reqs check + manifest-schema + docs-drift), each a binary pass/fail, runnable as one command"
108	required_stages = ["doc", "impl", "unit"]
109
110	[[requirements]]
111	id = "REQ-CI-MANUAL"
112	title = "A manual 'run gates' command always exists — the same gate scripts runnable by hand on any fleet host"
113	required_stages = ["doc", "impl"]
114
115	[[requirements]]
116	id = "REQ-CI-TRIGGER"
117	title = "A git push hook fires the gates: pings a fleet runner-agent over spt rather than polling"

title = "The claude-spt [digest] extractor (claude-spt-digest) maps Claude Code's JSONL transcript to the published digest-record NDJSON contract (role∈{input,agent,tool}; text/tool/ts), emitting RAW records for spt-core's renderer (ADR-0019)"
required_stages = ["impl", "unit", "int"]  # impl: tools/claude-spt-digest (Rust — CC JSONL -> digest NDJSON; per-session locate of <slug>/<session_id>.jsonl under the projects root via the {session_id} the command receives; native UTF-8 stdout; defensive ~ expansion) + ci/digest/build.sh gate. unit: the crate's cargo tests (9: input/agent/tool mapping, arg-key preference, thinking+tool_result+noise+bad-json drops, UTF-8 round-trip, contract invariants, tilde expansion) run by ci/digest/build.sh. int: ci/digest/digest-proof-int.sh — `spt adapter digest-proof claude-spt --sample` on live v0.7.2 ran the published-shape extractor command (`--session {session_id} --in {source}`) and rendered DIGEST_PROOF_OK (5 parsed / 0 dropped). GREEN 2026-06-15 (registry left clean via soft-remove). Was F-004-blocked (digest-proof --sample passed an empty substitution-key map, cli.rs:5135) — doyle's spt-core PR#14 fix (v0.7.2, release counter 14) now fills {id}+{session_id} + an optional --session, matching runtime. doc deferred (the mapping is documented in the module + manifest [digest] comment; a docs-site section lands with the doc slice).

[[requirements]]
id = "REQ-SKILL-LIVE"
title = "/sptc:live upgrades THIS CC session to a LiveAgent: the claude-spt:live profile overlays [session.psyche_init] (daemon spawns the Psyche), the claude-spt-psyche runner keeps the Psyche claude alive (one --continue turn per pulse, NOT one-shot `claude -p`), and the live body activates the :live composite + a resident Monitor relay as the single delivery pipe"
required_stages = ["doc", "impl", "unit", "int"]  # doc: adapter/strings/skills/live.md (activation + poll/relay reconcile + Psyche-is-daemon-managed). impl: [profiles.live.session.psyche_init] in adapter/claude-spt.toml (the LiveAgent seam, 4 daemon keys) + tools/claude-spt-psyche (the resident runner). unit: claude-spt-psyche cargo tests (arg parse, seed/pulse/poll command construction, blank-pulse guard) run by ci/psyche/build.sh. int: ci/psyche/live-relay-int.sh — plays CC's Monitor role (spawns `spt api --adapter claude-spt:live --manifest <claude-spt.toml> listen <id>` as a persistent CHILD, NOT --once) under a disposable id, and asserts BOTH legs on real spt 0.7.3: PSYCHE-SPAWN (`PSYCHE_SPAWNED:{id}-psyche pid=…`, fired because the merged manifest declares psyche_init — startup.rs spawn_psyche, doyle-traced 2026-06-16) + RELAY (BOUND/READY + a probe relayed as the escaped <EVENT> envelope + live_agent kind). PASS live. Two gotchas baked in (doyle, post-M11 how-to-live docs): anchor on the WINDOWS pid not git-bash `$$` (else STALE_SEED); `bind` before `send` (else NO_PERCH, no queue). The psyche-binary resolves via the F-006 PATH interim on 0.7.3; on v0.8.0 Feature B/REQ-INSTALL-11 resolves it from the manifest dir (the int skips the psyche leg with a logged note only if neither holds). The int version-branches the PSYCHE-SPAWN MARKER (M11 restructure, doyle 2026-06-16): <0.8.0 asserts PSYCHE_SPAWNED off the listen child (in-process spawn); >=0.8.0 asserts the {id}-psyche perch ONLINE (daemon livehost hosts it off the perch online status; LIVEHOST_PSYCHE on the daemon) — the >=0.8.0 leg is PROVISIONAL, finalized against the merged tree at the v0.8.0 publish ping. Relay leg unchanged across versions. Old "daemon loop staged" / "no non-interactive bringup" blockers both CLEARED (loop shipped 0.7.3; --manifest + persistent-child-listen is the non-interactive path — F-007 resolved to docs-gap, not missing feature).

[[requirements]]
id = "REQ-UPS-INJECTION"
title = "UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands"
required_stages = ["doc", "impl", "unit", "int"]  # doc: ADR-0002. impl: user-prompt-submit.sh (skill-injection + drain) + _common.sh. unit: render_frames + sptc_skill_key tests. int: ci/hooks/poll-int.sh — the poll->additionalContext drain round-trip vs live v0.7.1 (<EVENT> envelope, render_frames confirm-match, multi-message). UPS-fires-on-slash empirical = ADR-0002 validation (CC 2.1.177); skill-injection resolve = registration-int 4c.

# ── Milestone: CI (autonomous fleet) — minted on-start. Deterministic gates + git push-trigger +
# spt reporting bus. Seeded from SCOPE.md "CI model" + docs/CI.md. Real-claude acceptance is a
# later slice (doc-only now). All harness-agnostic shell; runs on the Win+Linux fleet.

[[requirements]]
id = "REQ-CI-GATES"
title = "Deterministic gates (shell-syntax + unit tests + traceable-reqs check + manifest-schema + docs-drift), each a binary pass/fail, runnable as one command"
required_stages = ["doc", "impl", "unit"]

[[requirements]]
id = "REQ-CI-MANUAL"
title = "A manual 'run gates' command always exists — the same gate scripts runnable by hand on any fleet host"
required_stages = ["doc", "impl"]

[[requirements]]
id = "REQ-CI-TRIGGER"
title = "A git push hook fires the gates: pings a fleet runner-agent over spt rather than polling"


All design questions resolved — doyle CODE-confirmed Option A (livehost.rs:282 gates psyche on state==live_agent; ready perch skipped regardless of base psyche_init). Bind/boundary stay explicit `--adapter`; only seed + live-listen go bare. No more blockers. ACK + write JIT plan, then execute.


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && "$OWL" send doyle perri <<'EOF'
perri — Option A LOCKED + code-confirmed (livehost.rs:282 state==live_agent gate; listen actualizes, poll stays psyche-less). Bind/boundary stay explicit --adapter (only seed + live-listen bare) — confirmed, won't expand surface. Wiring now: psyche_init→base, delete [profiles.live], min_spt_core→0.9.0, live.md bare-listen, seed branch bare, setup `spt adapter use claude-spt`, int tests to base/bare, traceable + PARITY updated. Then gates → update local spt 0.8.2→0.9.0 → VERIFY /sptc:live end-to-end → ship v0.3.0 + cplugs republish. Executing; report wired+validated.
EOF

ACK doyle Option A lock


➜ SENT:doyle


Write

# PREP-4 — legacy-parity bringup (Option A) → v0.3.0 — JIT plan

> **Trigger:** spt-core **v0.9.0 SHIPPED** (2026-06-17, doyle). Adapter-agnostic seed + bind-time
> resolution by `host_binaries` + `spt adapter use` + optional `--adapter`. Activates the PREP-4
> staging from v0.2.1. Closes the legacy-parity arc.

## Decision: OPTION A (doyle-ruled + code-confirmed, livehost.rs:282)
`:live` differs from base ONLY by `[session.psyche_init]` → fold psyche_init INTO base claude-spt,
DELETE `[profiles.live]`. ready-vs-live is the COMMAND, not the profile:
- `/sptc:live`  → bare `spt api listen <id>` → base has psyche_init → perch state=live_agent → daemon
  hosts the Psyche. LIVE.
- `/sptc:ready` → bare `spt ready/poll <id>` → perch state=ready_agent → livehost.rs:282 SKIPS psyche
  regardless of base psyche_init. READY.
Zero `--adapter` on the default path. `:ccs`/`:deep` stay as sanctioned override profiles (genuine
deltas: command=ccs, digest window) — selected with explicit `--adapter claude-spt:<p>`.

Compat gate = (a): hard-bump `min_spt_core_version` 0.7.0 → 0.9.0 (no <0.9.0 users; compat gate
refuses older spt-core). Bind/boundary hooks KEEP explicit `--adapter claude-spt` (sanctioned
override, valid on 0.9.0; only seed + live-listen go bare — doyle scope).

## Tasks (wire)
1. **adapter/claude-spt.toml**: add `[session.psyche_init]` to base (move the 4-key command from
   `[profiles.live.session.psyche_init]`); DELETE the `[profiles.live...]` block; `min_spt_core_version
   = "0.9.0"`; rewrite the stale base=ReadyAgent/:live-activates comments to the Option-A model.
2. **adapter/strings/skills/live.md**: bringup → single persistent Monitor task running bare
   `spt api listen <id>` (NO seed-chain, NO `--adapter`, NO `--manifest`). SessionStart already seeded
   the perch agnostically; listen resolves claude-spt by pid→host_binaries and the live path stamps
   state=live_agent. Strip the PREP-4 interim note + all `:live` composite refs. Keep OUTPUT
   DISCIPLINE + canonical LIVE block.
3. **plugin/sptc/hooks/session-start.sh**: `seed)` branch → bare `spt api seed --pid "$PPID"
   --session-id "$sid"` (drop `--adapter "$ADAPTER"`). bind/boundary branches UNCHANGED.
4. **adapter/strings/skills/setup.md**: add a step calling `spt adapter use claude-spt` (durable
   active-profile pointer → claude-spt for the `claude` host binary); fix the line-49 spot-check
   (`get-string claude-spt:live` → a live profile that still exists, e.g. `:deep`). ccs `:ccs` refs
   stay valid (override profile).
5. **plugin/sptc/skills/live/SKILL.md**: stub text — drop the "activates claude-spt:live profile"
   claim; base is now live-capable, the listen command actualizes.
6. **ci/manifest/registration-int.sh**: drop `:live`-profile assertions (profile deleted); keep the
   `skills.live` file-string check (that's the skill KEY, not a profile).
7. **ci/psyche/live-relay-int.sh**: `A=claude-spt:live` → `claude-spt`; seed/listen drop `--adapter`
   composite (bare or base). Asserts psyche hosts off the BASE manifest + live listen path.
8. **traceable-reqs.toml**: rewrite REQ-SKILL-LIVE title/notes to the Option-A model (base
   psyche_init + command-decides, no :live profile). Fix line-180 ":deep/:live" comment.
9. **docs/PARITY.md**: lines 18,27 — ":live profile" → base psyche_init / command-decides.
10. **tools/claude-spt-psyche/src/main.rs + Cargo.toml**: doc-comment accuracy (base now declares
    psyche_init; the runner itself is unchanged).
11. Lower-priority doc accuracy (not gate-blocking, may defer): docs/adr/0002 wording,
    SPT_HARNESS_ADAPTER_TIPS.md (general :live-overlay pattern still valid as a technique), historical
    PLAN docs + SPT-CORE-FINDINGS.md = dated snapshots, leave.

## Gate
`sh ci/run-gates.sh` green + `traceable-reqs check` green + manifest validates + both .spt repack &
validate. Then **VERIFY on live spt 0.9.0**: update local spt 0.8.2→0.9.0 (`spt update fetch &&
apply`, restart daemon); run `/sptc:live` end-to-end with NO `--adapter` anywhere; confirm the perch
stamps adapter=claude-spt + state=live_agent + online + Psyche hosted, and the user sees only the
clean LIVE block. Empirically reconfirms poll-stays-ready on a psyche_init base.

## Release (v0.3.0)
Bump adapter version → 0.3.0 (min_spt_core + structural change). CHANGELOG [0.3.0] (user-facing:
going live is now one bare step, full legacy parity; requires spt-core 0.9.0+). Repack both per-OS
.spt (binaries unchanged), tag, GH release. **cplugs republish** this time (session-start.sh is a
structural skeleton change) → bump plugin.json 0.1.2→0.1.3, copy skeleton subset, push cplugs.
Sequence: per-OS-asset release FIRST, then cplugs skeleton bump (runbook coupling).

## Closes
The whole legacy-parity arc (doyle). PREP-4 fully activated; staging from v0.2.1 retired.


File created successfully at: C:\Users\decid\Documents\projects\spt-claude-code\PREP4-PLAN.md (file state is current in your context — no need to Read it back)

# PREP-4 — legacy-parity bringup (Option A) → v0.3.0 — JIT plan

> **Trigger:** spt-core **v0.9.0 SHIPPED** (2026-06-17, doyle). Adapter-agnostic seed + bind-time
> resolution by `host_binaries` + `spt adapter use` + optional `--adapter`. Activates the PREP-4
> staging from v0.2.1. Closes the legacy-parity arc.

## Decision: OPTION A (doyle-ruled + code-confirmed, livehost.rs:282)
`:live` differs from base ONLY by `[session.psyche_init]` → fold psyche_init INTO base claude-spt,
DELETE `[profiles.live]`. ready-vs-live is the COMMAND, not the profile:
- `/sptc:live`  → bare `spt api listen <id>` → base has psyche_init → perch state=live_agent → daemon
  hosts the Psyche. LIVE.
- `/sptc:ready` → bare `spt ready/poll <id>` → perch state=ready_agent → livehost.rs:282 SKIPS psyche
  regardless of base psyche_init. READY.
Zero `--adapter` on the default path. `:ccs`/`:deep` stay as sanctioned override profiles (genuine
deltas: command=ccs, digest window) — selected with explicit `--adapter claude-spt:<p>`.

Compat gate = (a): hard-bump `min_spt_core_version` 0.7.0 → 0.9.0 (no <0.9.0 users; compat gate
refuses older spt-core). Bind/boundary hooks KEEP explicit `--adapter claude-spt` (sanctioned
override, valid on 0.9.0; only seed + live-listen go bare — doyle scope).

## Tasks (wire)
1. **adapter/claude-spt.toml**: add `[session.psyche_init]` to base (move the 4-key command from
   `[profiles.live.session.psyche_init]`); DELETE the `[profiles.live...]` block; `min_spt_core_version
   = "0.9.0"`; rewrite the stale base=ReadyAgent/:live-activates comments to the Option-A model.
2. **adapter/strings/skills/live.md**: bringup → single persistent Monitor task running bare
   `spt api listen <id>` (NO seed-chain, NO `--adapter`, NO `--manifest`). SessionStart already seeded
   the perch agnostically; listen resolves claude-spt by pid→host_binaries and the live path stamps
   state=live_agent. Strip the PREP-4 interim note + all `:live` composite refs. Keep OUTPUT
   DISCIPLINE + canonical LIVE block.
3. **plugin/sptc/hooks/session-start.sh**: `seed)` branch → bare `spt api seed --pid "$PPID"
   --session-id "$sid"` (drop `--adapter "$ADAPTER"`). bind/boundary branches UNCHANGED.
4. **adapter/strings/skills/setup.md**: add a step calling `spt adapter use claude-spt` (durable
   active-profile pointer → claude-spt for the `claude` host binary); fix the line-49 spot-check
   (`get-string claude-spt:live` → a live profile that still exists, e.g. `:deep`). ccs `:ccs` refs
   stay valid (override profile).
5. **plugin/sptc/skills/live/SKILL.md**: stub text — drop the "activates claude-spt:live profile"
   claim; base is now live-capable, the listen command actualizes.
6. **ci/manifest/registration-int.sh**: drop `:live`-profile assertions (profile deleted); keep the
   `skills.live` file-string check (that's the skill KEY, not a profile).
7. **ci/psyche/live-relay-int.sh**: `A=claude-spt:live` → `claude-spt`; seed/listen drop `--adapter`
   composite (bare or base). Asserts psyche hosts off the BASE manifest + live listen path.
8. **traceable-reqs.toml**: rewrite REQ-SKILL-LIVE title/notes to the Option-A model (base
   psyche_init + command-decides, no :live profile). Fix line-180 ":deep/:live" comment.
9. **docs/PARITY.md**: lines 18,27 — ":live profile" → base psyche_init / command-decides.
10. **tools/claude-spt-psyche/src/main.rs + Cargo.toml**: doc-comment accuracy (base now declares
    psyche_init; the runner itself is unchanged).
11. Lower-priority doc accuracy (not gate-blocking, may defer): docs/adr/0002 wording,
    SPT_HARNESS_ADAPTER_TIPS.md (general :live-overlay pattern still valid as a technique), historical
    PLAN docs + SPT-CORE-FINDINGS.md = dated snapshots, leave.

## Gate
`sh ci/run-gates.sh` green + `traceable-reqs check` green + manifest validates + both .spt repack &
validate. Then **VERIFY on live spt 0.9.0**: update local spt 0.8.2→0.9.0 (`spt update fetch &&
apply`, restart daemon); run `/sptc:live` end-to-end with NO `--adapter` anywhere; confirm the perch
stamps adapter=claude-spt + state=live_agent + online + Psyche hosted, and the user sees only the
clean LIVE block. Empirically reconfirms poll-stays-ready on a psyche_init base.

## Release (v0.3.0)
Bump adapter version → 0.3.0 (min_spt_core + structural change). CHANGELOG [0.3.0] (user-facing:
going live is now one bare step, full legacy parity; requires spt-core 0.9.0+). Repack both per-OS
.spt (binaries unchanged), tag, GH release. **cplugs republish** this time (session-start.sh is a
structural skeleton change) → bump plugin.json 0.1.2→0.1.3, copy skeleton subset, push cplugs.
Sequence: per-OS-asset release FIRST, then cplugs skeleton bump (runbook coupling).

## Closes
The whole legacy-parity arc (doyle). PREP-4 fully activated; staging from v0.2.1 retired.


Plan written. Execute — start with the keystone manifest. Bump min_spt_core, then move psyche_init to base + delete `[profiles.live]`.


Edit

# Local spt binary is 0.6.0; the published contract we build to is v0.7.0 (manifest.schema.json,
# shortcut_basename). 0.7.0 is the floor this manifest's fields require.
min_spt_core_version = "0.7.0"

# Floor BUMPED to 0.9.0 (2026-06-17, PREP-4): the legacy-parity bringup requires spt-core's
# adapter-agnostic seed + bind-time resolution by [adapter] host_binaries (shipped v0.9.0, ADR-0021).
# On <0.9.0 `api seed`/`listen` still mandate --adapter, so the bare flow this manifest now wires
# would break — the compat gate refuses the adapter on older spt-core rather than misbehave.
min_spt_core_version = "0.9.0"


The file C:\Users\decid\Documents\projects\spt-claude-code\adapter\claude-spt.toml has been updated successfully. (file state is current in your context — no need to Read it back)

# Local spt binary is 0.6.0; the published contract we build to is v0.7.0 (manifest.schema.json,
# shortcut_basename). 0.7.0 is the floor this manifest's fields require.
min_spt_core_version = "0.7.0"

# Floor BUMPED to 0.9.0 (2026-06-17, PREP-4): the legacy-parity bringup requires spt-core's
# adapter-agnostic seed + bind-time resolution by [adapter] host_binaries (shipped v0.9.0, ADR-0021).
# On <0.9.0 `api seed`/`listen` still mandate --adapter, so the bare flow this manifest now wires
# would break — the compat gate refuses the adapter on older spt-core rather than misbehave.
min_spt_core_version = "0.9.0"

-# Local spt binary is 0.6.0; the published contract we build to is v0.7.0 (manifest.schema.json,

-# shortcut_basename). 0.7.0 is the floor this manifest's fields require.

+# Floor BUMPED to 0.9.0 (2026-06-17, PREP-4): the legacy-parity bringup requires spt-core's

+# adapter-agnostic seed + bind-time resolution by [adapter] host_binaries (shipped v0.9.0, ADR-0021).

+# On <0.9.0 `api seed`/`listen` still mandate --adapter, so the bare flow this manifest now wires

+# would break — the compat gate refuses the adapter on older spt-core rather than misbehave.

hostable_types = ["LiveAgent", "ReadyAgent", "Worker"]

# host_binaries — STAGED ahead of spt-core v0.9.0 (doyle 2026-06-16). The pid→binary match-key for

# adapter-agnostic resolution: `spt api listen <id>` resolves the owning adapter/profile by walking


Now move `psyche_init` into base `[session]` and update its comment block. First the base `[session]`:


[session]
commune_dir = ".claude"
signoff_dir = ".claude"

[session]
commune_dir = ".claude"
signoff_dir = ".claude"

# ── [session.psyche_init] — base is LIVE-CAPABLE (Option A, doyle-ruled + code-confirmed 2026-06-17) ─
# The Psyche-spawn seam lives in the BASE manifest, not a `:live` profile overlay. ready-vs-live is the
# COMMAND, not the profile: `/sptc:live` brings the perch up via `spt api listen` → the live path stamps
# the perch state=live_agent → the daemon livehost hosts the Psyche off that online status (it checks
# psyche_init on the resolved manifest). `/sptc:ready` brings it up as state=ready_agent → livehost
# SKIPS the Psyche regardless of base psyche_init (livehost.rs:282 gate — doyle). So declaring
# psyche_init in base is harmless for ready agents and removes the need for a separate `:live` profile
# (legacy parity: zero --adapter on the default path; the daemon resolves claude-spt by host_binaries).
#
# Psyche keys the daemon fills are EXACTLY {id, session_id, psyche_dir, psyche_prompt} — NOT
# {session_name} (a [session.self] fill) and NOT {psyche_context} (the resume-preload seam, not first
# spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached (Stdio::null,
# unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at session end.
# The runner (claude-spt-psyche, tools/) is a resident wrapper — `claude -p` is one-shot, so it seeds
# the Psyche `claude` session once from {psyche_prompt} then drives one `claude --continue` turn per
# daemon pulse (NOT a bare `claude -p`), the claude authoring commune drops. spt-core fills each {key}
# as ONE argv element (tokenize-then-fill, v0.8.2+); the runner parses --prompt greedily as defense.
# [impl->REQ-SKILL-LIVE]
[session.psyche_init]
command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"
cwd = "{psyche_dir}"
detach = true
keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]


[session]
commune_dir = ".claude"
signoff_dir = ".claude"

# ── [session.psyche_init] — base is LIVE-CAPABLE (Option A, doyle-ruled + code-confirmed 2026-06-17) ─
# The Psyche-spawn seam lives in the BASE manifest, not a `:live` profile overlay. ready-vs-live is the
# COMMAND, not the profile: `/sptc:live` brings the perch up via `spt api listen` → the live path stamps
# the perch state=live_agent → the daemon livehost hosts the Psyche off that online status (it checks
# psyche_init on the resolved manifest). `/sptc:ready` brings it up as state=ready_agent → livehost
# SKIPS the Psyche regardless of base psyche_init (livehost.rs:282 gate — doyle). So declaring
# psyche_init in base is harmless for ready agents and removes the need for a separate `:live` profile
# (legacy parity: zero --adapter on the default path; the daemon resolves claude-spt by host_binaries).
#
# Psyche keys the daemon fills are EXACTLY {id, session_id, psyche_dir, psyche_prompt} — NOT
# {session_name} (a [session.self] fill) and NOT {psyche_context} (the resume-preload seam, not first
# spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached (Stdio::null,
# unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at session end.
# The runner (claude-spt-psyche, tools/) is a resident wrapper — `claude -p` is one-shot, so it seeds
# the Psyche `claude` session once from {psyche_prompt} then drives one `claude --continue` turn per
# daemon pulse (NOT a bare `claude -p`), the claude authoring commune drops. spt-core fills each {key}
# as ONE argv element (tokenize-then-fill, v0.8.2+); the runner parses --prompt greedily as defense.
# [impl->REQ-SKILL-LIVE]
[session.psyche_init]
command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"
cwd = "{psyche_dir}"
detach = true
keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]

+# ── [session.psyche_init] — base is LIVE-CAPABLE (Option A, doyle-ruled + code-confirmed 2026-06-17) ─

+# The Psyche-spawn seam lives in the BASE manifest, not a `:live` profile overlay. ready-vs-live is the

+# COMMAND, not the profile: `/sptc:live` brings the perch up via `spt api listen` → the live path stamps

+# the perch state=live_agent → the daemon livehost hosts the Psyche off that online status (it checks

+# psyche_init on the resolved manifest). `/sptc:ready` brings it up as state=ready_agent → livehost

+# SKIPS the Psyche regardless of base psyche_init (livehost.rs:282 gate — doyle). So declaring

+# psyche_init in base is harmless for ready agents and removes the need for a separate `:live` profile

+# (legacy parity: zero --adapter on the default path; the daemon resolves claude-spt by host_binaries).

+# Psyche keys the daemon fills are EXACTLY {id, session_id, psyche_dir, psyche_prompt} — NOT

+# {session_name} (a [session.self] fill) and NOT {psyche_context} (the resume-preload seam, not first

+# spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached (Stdio::null,

+# unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at session end.

+# The runner (claude-spt-psyche, tools/) is a resident wrapper — `claude -p` is one-shot, so it seeds

+# the Psyche `claude` session once from {psyche_prompt} then drives one `claude --continue` turn per

+# daemon pulse (NOT a bare `claude -p`), the claude authoring commune drops. spt-core fills each {key}

+# as ONE argv element (tokenize-then-fill, v0.8.2+); the runner parses --prompt greedily as defense.

+command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"

+keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]

# ── [session.self] ─────────────────────────────────────────────────────────────────────────

# The spt-hosted bringup template (publish target #2's launcher seam). `spt endpoint run --adapter

# claude-spt --id <id>` spawns this command into a broker-held PTY; CC self-registers its perch


Now delete the old `[profiles.live]` block + its comment. Read the current region precisely.


196	source = "~/.claude/projects"
197	window_turns = 6
198	arg_truncation = 240
199	sprint_collapse = true
200
201	# ── [profiles.<name>] ────────────────────────────────────────────────────────────────────────
202	# Shipped profile overlays (sparse leaf-replace; selected as the composite `claude-spt:<name>`).
203	# `deep` widens the digest window for long-context review sessions without forking the base, and
204	# overrides a [strings] leaf so the overlay is observable via `spt adapter get-string` (base vs
205	# `:deep` differ). (Node-local user profiles are created with `spt adapter create-profile` and
206	# live in the registry, never in this manifest.)
207	[profiles.deep.digest]
208	window_turns = 20
209
210	[profiles.deep.strings]
211	adapter_label = "Claude Code (spt, deep)"
212
213	# `live` — the LiveAgent overlay. Selected as `claude-spt:live`; /sptc:live activates this profile so
214	# the daemon resolves the MERGED manifest, sees [session.psyche_init], and spawns the endpoint's
215	# Psyche companion (base claude-spt declares no psyche_init = ReadyAgent — doyle 2026-06-15, traced:
216	# api seed stores the composite → info.json adapter → daemon resolve_option_in applies the overlay →
217	# BrainLifecycle checks psyche_init on the merged view). Psyche keys the daemon fills are EXACTLY
218	# {id, session_id, psyche_dir, psyche_prompt} — NOT {session_name} (a [session.self] fill; the
219	# published example's use of it is a bug doyle fixed) and NOT {psyche_context} (the resume-preload
220	# seam, not first spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached
221	# (Stdio::null, unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at
222	# session end. The runner (claude-spt-psyche, tools/) is BUILT: a resident wrapper — `claude -p` is
223	# one-shot, so it seeds the Psyche `claude` session once from {psyche_prompt} then drives one
224	# `claude --continue` turn per daemon pulse (NOT a bare `claude -p`), the claude authoring commune
225	# drops. Activation: /sptc:live brings the perch up under the `claude-spt:live` composite (the overlay
226	# the daemon resolves to see psyche_init) + runs the delivery relay via Monitor (skills/live.md).
227	# [impl->REQ-SKILL-LIVE]
228	[profiles.live.session.psyche_init]
229	command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"
230	cwd = "{psyche_dir}"
231	detach = true
232	keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]
233
234	# `ccs` — the LOCKED-ADD ccs profile template. Selected as `claude-spt:ccs`. ccs is the model/billing
235	# multiplexer (kaitranntt/ccs): structurally Claude Code, only the launch binary + backend differ —

source = "~/.claude/projects"
window_turns = 6
arg_truncation = 240
sprint_collapse = true

# ── [profiles.<name>] ────────────────────────────────────────────────────────────────────────
# Shipped profile overlays (sparse leaf-replace; selected as the composite `claude-spt:<name>`).
# `deep` widens the digest window for long-context review sessions without forking the base, and
# overrides a [strings] leaf so the overlay is observable via `spt adapter get-string` (base vs
# `:deep` differ). (Node-local user profiles are created with `spt adapter create-profile` and
# live in the registry, never in this manifest.)
[profiles.deep.digest]
window_turns = 20

[profiles.deep.strings]
adapter_label = "Claude Code (spt, deep)"

# `live` — the LiveAgent overlay. Selected as `claude-spt:live`; /sptc:live activates this profile so
# the daemon resolves the MERGED manifest, sees [session.psyche_init], and spawns the endpoint's
# Psyche companion (base claude-spt declares no psyche_init = ReadyAgent — doyle 2026-06-15, traced:
# api seed stores the composite → info.json adapter → daemon resolve_option_in applies the overlay →
# BrainLifecycle checks psyche_init on the merged view). Psyche keys the daemon fills are EXACTLY
# {id, session_id, psyche_dir, psyche_prompt} — NOT {session_name} (a [session.self] fill; the
# published example's use of it is a bug doyle fixed) and NOT {psyche_context} (the resume-preload
# seam, not first spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached
# (Stdio::null, unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at
# session end. The runner (claude-spt-psyche, tools/) is BUILT: a resident wrapper — `claude -p` is
# one-shot, so it seeds the Psyche `claude` session once from {psyche_prompt} then drives one
# `claude --continue` turn per daemon pulse (NOT a bare `claude -p`), the claude authoring commune
# drops. Activation: /sptc:live brings the perch up under the `claude-spt:live` composite (the overlay
# the daemon resolves to see psyche_init) + runs the delivery relay via Monitor (skills/live.md).
# [impl->REQ-SKILL-LIVE]
[profiles.live.session.psyche_init]
command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"
cwd = "{psyche_dir}"
detach = true
keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]

# `ccs` — the LOCKED-ADD ccs profile template. Selected as `claude-spt:ccs`. ccs is the model/billing
# multiplexer (kaitranntt/ccs): structurally Claude Code, only the launch binary + backend differ —


# `live` — the LiveAgent overlay. Selected as `claude-spt:live`; /sptc:live activates this profile so
# the daemon resolves the MERGED manifest, sees [session.psyche_init], and spawns the endpoint's
# Psyche companion (base claude-spt declares no psyche_init = ReadyAgent — doyle 2026-06-15, traced:
# api seed stores the composite → info.json adapter → daemon resolve_option_in applies the overlay →
# BrainLifecycle checks psyche_init on the merged view). Psyche keys the daemon fills are EXACTLY
# {id, session_id, psyche_dir, psyche_prompt} — NOT {session_name} (a [session.self] fill; the
# published example's use of it is a bug doyle fixed) and NOT {psyche_context} (the resume-preload
# seam, not first spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached
# (Stdio::null, unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at
# session end. The runner (claude-spt-psyche, tools/) is BUILT: a resident wrapper — `claude -p` is
# one-shot, so it seeds the Psyche `claude` session once from {psyche_prompt} then drives one
# `claude --continue` turn per daemon pulse (NOT a bare `claude -p`), the claude authoring commune
# drops. Activation: /sptc:live brings the perch up under the `claude-spt:live` composite (the overlay
# the daemon resolves to see psyche_init) + runs the delivery relay via Monitor (skills/live.md).
# [impl->REQ-SKILL-LIVE]
[profiles.live.session.psyche_init]
command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"
cwd = "{psyche_dir}"
detach = true
keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]

# `ccs` — the LOCKED-ADD ccs profile template.

# NOTE: there is no `:live` profile (removed 2026-06-17, PREP-4 Option A). The LiveAgent seam
# `[session.psyche_init]` now lives in the BASE manifest above — base is live-capable, and ready-vs-live
# is the command (`api listen` vs `api poll`), not a profile. A live agent needs no composite.
#
# `ccs` — the LOCKED-ADD ccs profile template.


# `live` — the LiveAgent overlay. Selected as `claude-spt:live`; /sptc:live activates this profile so
# the daemon resolves the MERGED manifest, sees [session.psyche_init], and spawns the endpoint's
# Psyche companion (base claude-spt declares no psyche_init = ReadyAgent — doyle 2026-06-15, traced:
# api seed stores the composite → info.json adapter → daemon resolve_option_in applies the overlay →
# BrainLifecycle checks psyche_init on the merged view). Psyche keys the daemon fills are EXACTLY
# {id, session_id, psyche_dir, psyche_prompt} — NOT {session_name} (a [session.self] fill; the
# published example's use of it is a bug doyle fixed) and NOT {psyche_context} (the resume-preload
# seam, not first spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached
# (Stdio::null, unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at
# session end. The runner (claude-spt-psyche, tools/) is BUILT: a resident wrapper — `claude -p` is
# one-shot, so it seeds the Psyche `claude` session once from {psyche_prompt} then drives one
# `claude --continue` turn per daemon pulse (NOT a bare `claude -p`), the claude authoring commune
# drops. Activation: /sptc:live brings the perch up under the `claude-spt:live` composite (the overlay
# the daemon resolves to see psyche_init) + runs the delivery relay via Monitor (skills/live.md).
# [impl->REQ-SKILL-LIVE]
[profiles.live.session.psyche_init]
command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"
cwd = "{psyche_dir}"
detach = true
keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]

# `ccs` — the LOCKED-ADD ccs profile template.

# NOTE: there is no `:live` profile (removed 2026-06-17, PREP-4 Option A). The LiveAgent seam
# `[session.psyche_init]` now lives in the BASE manifest above — base is live-capable, and ready-vs-live
# is the command (`api listen` vs `api poll`), not a profile. A live agent needs no composite.
#
# `ccs` — the LOCKED-ADD ccs profile template.

adapter_label = "Claude Code (spt, deep)"

-# `live` — the LiveAgent overlay. Selected as `claude-spt:live`; /sptc:live activates this profile so

-# the daemon resolves the MERGED manifest, sees [session.psyche_init], and spawns the endpoint's

-# Psyche companion (base claude-spt declares no psyche_init = ReadyAgent — doyle 2026-06-15, traced:

-# api seed stores the composite → info.json adapter → daemon resolve_option_in applies the overlay →

-# BrainLifecycle checks psyche_init on the merged view). Psyche keys the daemon fills are EXACTLY

-# {id, session_id, psyche_dir, psyche_prompt} — NOT {session_name} (a [session.self] fill; the

-# published example's use of it is a bug doyle fixed) and NOT {psyche_context} (the resume-preload

-# seam, not first spawn). `detach=true` + cwd={psyche_dir}: the Psyche is fire-and-forget detached

-# (Stdio::null, unsupervised), owns its `<parent>-psyche` perch, authors commune-drops only, exits at

-# session end. The runner (claude-spt-psyche, tools/) is BUILT: a resident wrapper — `claude -p` is

-# one-shot, so it seeds the Psyche `claude` session once from {psyche_prompt} then drives one

-# `claude --continue` turn per daemon pulse (NOT a bare `claude -p`), the claude authoring commune

-# drops. Activation: /sptc:live brings the perch up under the `claude-spt:live` composite (the overlay

-# the daemon resolves to see psyche_init) + runs the delivery relay via Monitor (skills/live.md).

-command = "claude-spt-psyche --id {id} --session-id {session_id} --prompt {psyche_prompt}"

-keys = ["id", "session_id", "psyche_dir", "psyche_prompt"]

+# NOTE: there is no `:live` profile (removed 2026-06-17, PREP-4 Option A). The LiveAgent seam

+# `[session.psyche_init]` now lives in the BASE manifest above — base is live-capable, and ready-vs-live

+# is the command (`api listen` vs `api poll`), not a profile. A live agent needs no composite.

# `ccs` — the LOCKED-ADD ccs profile template. Selected as `claude-spt:ccs`. ccs is the model/billing

# multiplexer (kaitranntt/ccs): structurally Claude Code, only the launch binary + backend differ —

# exactly the profile seam (SCOPE §ccs, LOCKED). This overlay leaf-replaces ONE leaf: the spawn


Update the `[strings.skills].live` comment that still references the `:live` composite.


# `live` -> upgrade THIS session to a LiveAgent via the `claude-spt:live` profile (overlays
# [session.psyche_init] -> daemon spawns the Psyche) + a resident Monitor relay as the delivery pipe.
# Profile propagation CONFIRMED in spt-core code (doyle 2026-06-15: api seed stores the composite ->
# info.json adapter -> daemon resolve_option_in applies the overlay -> BrainLifecycle checks
# psyche_init on the merged view). Body authored; runner (claude-spt-psyche) built (tools/).
live = { file = "skills/live.md" }             # -> activate claude-spt:live + Monitor relay (LiveAgent)

# `live` -> upgrade THIS session to a LiveAgent (Option A, 2026-06-17): base claude-spt is live-capable
# ([session.psyche_init] in base), so going live is just the COMMAND — bare `spt api listen <id>` under
# a Monitor relay. The live listen path stamps the perch state=live_agent → the daemon livehost hosts
# the Psyche (livehost.rs:282 gates on live_agent state, doyle). No `:live` composite, no --adapter.
live = { file = "skills/live.md" }             # -> bare `spt api listen <id>` + Monitor relay (LiveAgent)


# `live` -> upgrade THIS session to a LiveAgent via the `claude-spt:live` profile (overlays
# [session.psyche_init] -> daemon spawns the Psyche) + a resident Monitor relay as the delivery pipe.
# Profile propagation CONFIRMED in spt-core code (doyle 2026-06-15: api seed stores the composite ->
# info.json adapter -> daemon resolve_option_in applies the overlay -> BrainLifecycle checks
# psyche_init on the merged view). Body authored; runner (claude-spt-psyche) built (tools/).
live = { file = "skills/live.md" }             # -> activate claude-spt:live + Monitor relay (LiveAgent)

# `live` -> upgrade THIS session to a LiveAgent (Option A, 2026-06-17): base claude-spt is live-capable
# ([session.psyche_init] in base), so going live is just the COMMAND — bare `spt api listen <id>` under
# a Monitor relay. The live listen path stamps the perch state=live_agent → the daemon livehost hosts
# the Psyche (livehost.rs:282 gates on live_agent state, doyle). No `:live` composite, no --adapter.
live = { file = "skills/live.md" }             # -> bare `spt api listen <id>` + Monitor relay (LiveAgent)

commune = { file = "skills/commune.md" }       # -> file-drop <id>-commune.md into [session].commune_dir

force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)

subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine); REQ-SKILL-SUBNET

-# `live` -> upgrade THIS session to a LiveAgent via the `claude-spt:live` profile (overlays

-# [session.psyche_init] -> daemon spawns the Psyche) + a resident Monitor relay as the delivery pipe.

-# Profile propagation CONFIRMED in spt-core code (doyle 2026-06-15: api seed stores the composite ->

-# info.json adapter -> daemon resolve_option_in applies the overlay -> BrainLifecycle checks

-# psyche_init on the merged view). Body authored; runner (claude-spt-psyche) built (tools/).

-live = { file = "skills/live.md" }             # -> activate claude-spt:live + Monitor relay (LiveAgent)

+# `live` -> upgrade THIS session to a LiveAgent (Option A, 2026-06-17): base claude-spt is live-capable

+# ([session.psyche_init] in base), so going live is just the COMMAND — bare `spt api listen <id>` under

+# a Monitor relay. The live listen path stamps the perch state=live_agent → the daemon livehost hosts

+# the Psyche (livehost.rs:282 gates on live_agent state, doyle). No `:live` composite, no --adapter.

+live = { file = "skills/live.md" }             # -> bare `spt api listen <id>` + Monitor relay (LiveAgent)

# `new-alarm` -> EXPLICIT OUT OF SCOPE for v1: no core `spt` alarm/timed-pulse primitive exists

# (legacy $LIVE TIMED PULSE is owl/Psyche-layer, not lifted into core; adapters don't add core

# features). doyle carries a parity-gap finding to mint a core deferred-pulse primitive; the skill


Manifest restructured. Quick validate before continuing.


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && sh ci/manifest/check-manifest.sh 2>&1 | tail -8; echo "=== psyche_init now in base? ==="; grep -n '\[session.psyche_init\]\|\[profiles.live\|min_spt_core' adapter/claude-spt.toml

Validate manifest parses + confirm psyche_init moved to base


ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json
=== psyche_init now in base? ===
36:min_spt_core_version = "0.9.0"
123:# ── [session.psyche_init] — base is LIVE-CAPABLE (Option A, doyle-ruled + code-confirmed 2026-06-17) ─
141:[session.psyche_init]
214:# `[session.psyche_init]` now lives in the BASE manifest above — base is live-capable, and ready-vs-live
272:# ([session.psyche_init] in base), so going live is just the COMMAND — bare `spt api listen <id>` under


Manifest valid (psyche_init in base, `[profiles.live]` gone, floor 0.9.0). Now rewrite live.md to the bare flow.


Write

# /sptc:live — operative instructions

> Delivered file-backed via the `claude-spt` adapter `[strings]`
> (`[strings.skills].live = { file = "skills/live.md" }`), resolved at injection time. The cplugs
> SKILL.md stays a thin skeleton (ADR-0001). **This injection is self-contained** — everything you
> need to go live is below; you never need to run a `how-to` topic or any external lookup.

**Goal:** upgrade THIS Claude Code session to a **LiveAgent** — a reachable agent whose context is
backed by a **Psyche** (the daemon-managed companion that briefs your resume across `/clear`/compact
via commune deltas). This is NOT spawning a separate or nested session; it is making the session you
are in right now live.

**How a session becomes live (legacy parity — the COMMAND decides):** base `claude-spt` is
live-capable (its manifest declares `[session.psyche_init]`). Whether you are a **ReadyAgent** or a
**LiveAgent** is decided by *how the perch comes up*, not by any profile or `--adapter`:

- `/sptc:ready` brings the perch up via the ready path (`spt ready`/`api poll`) → perch state
  `ready_agent` → the daemon does **not** host a Psyche.
- `/sptc:live` brings the perch up via the **live listen** path (`spt api listen`) → perch state
  `live_agent` → the daemon livehost **hosts the Psyche** off that online status.

So there is **no go-live verb and no `:live` profile** — running the live `listen` command IS going
live. spt-core (v0.9.0+) resolves the owning adapter (`claude-spt`) automatically from the perch's
seed parent-pid via the manifest's `[adapter] host_binaries = ["claude"]`, so **no `--adapter` is
needed anywhere**. The Psyche is **daemon-managed by contract** — do NOT start, poll, or tear it down
yourself; the daemon owns its whole lifecycle off the perch's online status (it survives a relay
restart).

---

## Bringup — do this

1. **Ensure `spt` is available.** If `spt` is not on `PATH`, run `/sptc:setup` first, then continue.
   (Setup also sets the durable active-profile pointer `spt adapter use claude-spt`, so bare resolution
   lands on this adapter.)

2. **Pick the perch id.** Use a short lowercase id, or reuse this session's id from `spt whoami`. Call
   it `<id>`. The SessionStart hook already seeded this session's perch (adapter-agnostically), so you
   do not seed again here.

3. **Run the live relay as a single PERSISTENT background task** — Claude Code's **Monitor** tool (a
   long-running listener that keeps the process alive and re-fires per event WITHOUT terminating).
   This one resident process IS the live delivery pipe. The task body is a single bare command:

   ```sh
   spt api listen <id>
   ```

   - **No `--adapter`, no `--manifest`, no seed step, no pid anchor.** `spt api listen` resolves the
     owning adapter from the seed's parent pid (`host_binaries` match) and the live path stamps the
     perch `state=live_agent`, which is what tells the daemon to host the Psyche.
   - The command **blocks for the session's life**: the spooled backlog drains first, then each
     delivery streams to stdout as it arrives.
   - Do **not** use `--once` — that drains the backlog and forwards a single delivery, then exits (the
     degenerate path for a harness with no long-running listener). It never sustains a live session.

4. **Bind-before-send / readiness.** Watch the relay's output for the bringup markers (see OUTPUT
   DISCIPLINE): `BOUND:<id>` (perch established) then `READY:<id>` (the relay is streaming). Only once
   `READY:<id>` appears is the live pipe up — do not announce LIVE before then.

5. **Poll/relay reconcile (avoids double-delivery).** While the Monitor relay is up it is the *single*
   delivery pipe — treat it as authoritative. The `UserPromptSubmit` `api poll` hook must NOT also
   drain; the per-prompt poll no-ops while the relay is live and resumes only if the relay goes down.

6. **Delivery + reply shape.** Inbound arrives on the relay stream as the
   `<EVENT type="msg" from="<sender>">body</EVENT>` envelope (body HTML-escaped, newlines as `<br>`).
   To reply, pipe the body to `spt send --reply-to <sender>`.

7. **Across boundaries.** As a live agent you now have a Psyche — use `/sptc:commune` to push a
   context delta before a `/clear` or compact so your resume is briefed. On a session boundary,
   re-fire the same relay (`spt api listen <id>`) — the seed bridges the new session to the same perch
   and the relay resumes. To go offline gracefully (tears the Psyche down with the perch), use
   `/sptc:force-stop` (`spt endpoint shutdown <id>`).

---

## OUTPUT DISCIPLINE — what the user sees

The relay emits machine markers on stdout/stderr: `BOUND:<id>`, `READY:<id>`, and any other raw
token / step lines. **These are an internal parse contract — you read them to drive bringup; you
NEVER echo them to the user.** Do not surface raw spt-core plumbing.

**The ONLY user-facing surface for going live is the canonical LIVE block below.** Emit it verbatim
(substituting `<id>`) once `READY:<id>` is observed — and nothing else from the bringup:

```
**LIVE.** Session `<id>` is now a LiveAgent (Psyche-backed).
- Perch `<id>` — status online; Psyche companion hosted by the daemon.
- Reachable — other agents reach you with `/sptc:send <id>` (or `spt send <id>`).
- Inbound — streams into this session live; the Monitor relay is your delivery pipe.
- Reply — pipe a reply body to `spt send --reply-to <sender>`.
- Across resets — `/sptc:commune` before a `/clear`/compact; `/sptc:force-stop` to go offline (tears down the Psyche).
```

If bringup fails (no `READY:<id>` within ~25s), do NOT show the LIVE block — report a short
plain-language failure ("could not bring the live perch up") and the most likely cause (run
`/sptc:setup`, or confirm spt-core is >= 0.9.0), still without dumping the raw markers.

> The Psyche runner (`claude-spt-psyche`, declared in the base manifest's `[session.psyche_init]`) is
> the resident headless-`claude` companion the daemon launches detached — it keeps a Psyche `claude`
> session alive (one `--continue` turn per daemon pulse) and authors commune drops. You never invoke
> it directly; the daemon does.
<!-- [doc->REQ-SKILL-LIVE] -->


The file C:\Users\decid\Documents\projects\spt-claude-code\adapter\strings\skills\live.md has been updated successfully. (file state is current in your context — no need to Read it back)

# /sptc:live — operative instructions

> Delivered file-backed via the `claude-spt` adapter `[strings]`
> (`[strings.skills].live = { file = "skills/live.md" }`), resolved at injection time. The cplugs
> SKILL.md stays a thin skeleton (ADR-0001). **This injection is self-contained** — everything you
> need to go live is below; you never need to run a `how-to` topic or any external lookup.

**Goal:** upgrade THIS Claude Code session to a **LiveAgent** — a reachable agent whose context is
backed by a **Psyche** (the daemon-managed companion that briefs your resume across `/clear`/compact
via commune deltas). This is NOT spawning a separate or nested session; it is making the session you
are in right now live.

**How a session becomes live (legacy parity — the COMMAND decides):** base `claude-spt` is
live-capable (its manifest declares `[session.psyche_init]`). Whether you are a **ReadyAgent** or a
**LiveAgent** is decided by *how the perch comes up*, not by any profile or `--adapter`:

- `/sptc:ready` brings the perch up via the ready path (`spt ready`/`api poll`) → perch state
  `ready_agent` → the daemon does **not** host a Psyche.
- `/sptc:live` brings the perch up via the **live listen** path (`spt api listen`) → perch state
  `live_agent` → the daemon livehost **hosts the Psyche** off that online status.

So there is **no go-live verb and no `:live` profile** — running the live `listen` command IS going
live. spt-core (v0.9.0+) resolves the owning adapter (`claude-spt`) automatically from the perch's
seed parent-pid via the manifest's `[adapter] host_binaries = ["claude"]`, so **no `--adapter` is
needed anywhere**. The Psyche is **daemon-managed by contract** — do NOT start, poll, or tear it down
yourself; the daemon owns its whole lifecycle off the perch's online status (it survives a relay
restart).

---

## Bringup — do this

1. **Ensure `spt` is available.** If `spt` is not on `PATH`, run `/sptc:setup` first, then continue.
   (Setup also sets the durable active-profile pointer `spt adapter use claude-spt`, so bare resolution
   lands on this adapter.)

2. **Pick the perch id.** Use a short lowercase id, or reuse this session's id from `spt whoami`. Call
   it `<id>`. The SessionStart hook already seeded this session's perch (adapter-agnostically), so you
   do not seed again here.

3. **Run the live relay as a single PERSISTENT background task** — Claude Code's **Monitor** tool (a
   long-running listener that keeps the process alive and re-fires per event WITHOUT terminating).
   This one resident process IS the live delivery pipe. The task body is a single bare command:

   ```sh
   spt api listen <id>
   ```

   - **No `--adapter`, no `--manifest`, no seed step, no pid anchor.** `spt api listen` resolves the
     owning adapter from the seed's parent pid (`host_binaries` match) and the live path stamps the
     perch `state=live_agent`, which is what tells the daemon to host the Psyche.
   - The command **blocks for the session's life**: the spooled backlog drains first, then each
     delivery streams to stdout as it arrives.
   - Do **not** use `--once` — that drains the backlog and forwards a single delivery, then exits (the
     degenerate path for a harness with no long-running listener). It never sustains a live session.

4. **Bind-before-send / readiness.** Watch the relay's output for the bringup markers (see OUTPUT
   DISCIPLINE): `BOUND:<id>` (perch established) then `READY:<id>` (the relay is streaming). Only once
   `READY:<id>` appears is the live pipe up — do not announce LIVE before then.

5. **Poll/relay reconcile (avoids double-delivery).** While the Monitor relay is up it is the *single*
   delivery pipe — treat it as authoritative. The `UserPromptSubmit` `api poll` hook must NOT also
   drain; the per-prompt poll no-ops while the relay is live and resumes only if the relay goes down.

6. **Delivery + reply shape.** Inbound arrives on the relay stream as the
   `<EVENT type="msg" from="<sender>">body</EVENT>` envelope (body HTML-escaped, newlines as `<br>`).
   To reply, pipe the body to `spt send --reply-to <sender>`.

7. **Across boundaries.** As a live agent you now have a Psyche — use `/sptc:commune` to push a
   context delta before a `/clear` or compact so your resume is briefed. On a session boundary,
   re-fire the same relay (`spt api listen <id>`) — the seed bridges the new session to the same perch
   and the relay resumes. To go offline gracefully (tears the Psyche down with the perch), use
   `/sptc:force-stop` (`spt endpoint shutdown <id>`).

---

## OUTPUT DISCIPLINE — what the user sees

The relay emits machine markers on stdout/stderr: `BOUND:<id>`, `READY:<id>`, and any other raw
token / step lines. **These are an internal parse contract — you read them to drive bringup; you
NEVER echo them to the user.** Do not surface raw spt-core plumbing.

**The ONLY user-facing surface for going live is the canonical LIVE block below.** Emit it verbatim
(substituting `<id>`) once `READY:<id>` is observed — and nothing else from the bringup:

```
**LIVE.** Session `<id>` is now a LiveAgent (Psyche-backed).
- Perch `<id>` — status online; Psyche companion hosted by the daemon.
- Reachable — other agents reach you with `/sptc:send <id>` (or `spt send <id>`).
- Inbound — streams into this session live; the Monitor relay is your delivery pipe.
- Reply — pipe a reply body to `spt send --reply-to <sender>`.
- Across resets — `/sptc:commune` before a `/clear`/compact; `/sptc:force-stop` to go offline (tears down the Psyche).
```

If bringup fails (no `READY:<id>` within ~25s), do NOT show the LIVE block — report a short
plain-language failure ("could not bring the live perch up") and the most likely cause (run
`/sptc:setup`, or confirm spt-core is >= 0.9.0), still without dumping the raw markers.

> The Psyche runner (`claude-spt-psyche`, declared in the base manifest's `[session.psyche_init]`) is
> the resident headless-`claude` companion the daemon launches detached — it keeps a Psyche `claude`
> session alive (one `--continue` turn per daemon pulse) and authors commune drops. You never invoke
> it directly; the daemon does.
<!-- [doc->REQ-SKILL-LIVE] -->

-**How a session becomes live (no "go-live" verb):** LiveAgent vs ReadyAgent is gated entirely by the

-resolved manifest declaring `[session.psyche_init]`. Base `claude-spt` declares none (= ReadyAgent,

-like `/sptc:ready`); the `claude-spt:live` profile overlays it (= LiveAgent). Bringing the perch up

-under the `:live` composite is what tells the spt-core daemon to host this endpoint's Psyche. The

-Psyche is **daemon-managed by contract** — do NOT try to start, poll, or tear down the Psyche

-yourself; the daemon owns its whole lifecycle off the perch's online status (so the companion

+**How a session becomes live (legacy parity — the COMMAND decides):** base `claude-spt` is

+live-capable (its manifest declares `[session.psyche_init]`). Whether you are a **ReadyAgent** or a

+**LiveAgent** is decided by *how the perch comes up*, not by any profile or `--adapter`:

+- `/sptc:ready` brings the perch up via the ready path (`spt ready`/`api poll`) → perch state

+  `ready_agent` → the daemon does **not** host a Psyche.

+- `/sptc:live` brings the perch up via the **live listen** path (`spt api listen`) → perch state

+  `live_agent` → the daemon livehost **hosts the Psyche** off that online status.

+So there is **no go-live verb and no `:live` profile** — running the live `listen` command IS going

+live. spt-core (v0.9.0+) resolves the owning adapter (`claude-spt`) automatically from the perch's

+seed parent-pid via the manifest's `[adapter] host_binaries = ["claude"]`, so **no `--adapter` is

+needed anywhere**. The Psyche is **daemon-managed by contract** — do NOT start, poll, or tear it down

+yourself; the daemon owns its whole lifecycle off the perch's online status (it survives a relay

+   (Setup also sets the durable active-profile pointer `spt adapter use claude-spt`, so bare resolution

-   it `<id>`. Use this session's id from `spt whoami` for `<sid>` (the session-id).

+   it `<id>`. The SessionStart hook already seeded this session's perch (adapter-agnostically), so you

-   This one resident process IS the live delivery pipe. It seeds the `:live` composite and binds the

-   relay in the SAME process so both share one parent-pid anchor (see the INTERIM note below). The

+   This one resident process IS the live delivery pipe. The task body is a single bare command:

-   # Anchor pid: spt probes WINDOWS pids. From git-bash, $$ is the MSYS pid (reads as a dead anchor);

-   # use the WINPID column from `ps` (col 4) when present, else $$ is already the real pid.

-   ANCHOR=$(ps -p $$ 2>/dev/null | awk 'NR==2{print $4}'); case "$ANCHOR" in ''|*[!0-9]*) ANCHOR=$$;; esac

-   spt api --adapter claude-spt:live seed   --pid        "$ANCHOR" --session-id "<sid>"

-   spt api --adapter claude-spt:live listen --parent-pid "$ANCHOR" "<id>"

-   - The `listen` line **blocks for the session's life**: the spooled backlog drains first, then each

+   - **No `--adapter`, no `--manifest`, no seed step, no pid anchor.** `spt api listen` resolves the

+     owning adapter from the seed's parent pid (`host_binaries` match) and the live path stamps the

+     perch `state=live_agent`, which is what tells the daemon to host the Psyche.

+   - The command **blocks for the session's life**: the spooled backlog drains first, then each

delivery streams to stdout as it arrives.

-   - On a registered adapter, `--adapter claude-spt:live` resolves the registered manifest, so no

-   re-fire the same relay (step 3) — the seed bridges the new session to the same perch and the relay

-   resumes. To go offline gracefully (tears the Psyche down with the perch), use `/sptc:force-stop`

+   re-fire the same relay (`spt api listen <id>`) — the seed bridges the new session to the same perch

+   and the relay resumes. To go offline gracefully (tears the Psyche down with the perch), use

+   `/sptc:force-stop` (`spt endpoint shutdown <id>`).

-> **INTERIM (PREP-4 / spt-core v0.9.0).** The seed+listen-chained-in-one-process step exists only

-> because today's seed and listen must share a parent-pid anchor. Once spt-core ships the

-> adapter-agnostic seed (SessionStart seeds by pid; `api listen` resolves the owning adapter/profile

-> by pid), this collapses to a bare `spt api listen <id>` under Monitor — no shared-pid chaining and

-> no `--adapter`. Do not pre-build that path; it activates on doyle's v0.9.0 ship.

## OUTPUT DISCIPLINE — what the user sees

-The relay and seed emit machine markers on stdout/stderr: `SEEDED:<pid>`, `NO_SEED:<pid>`,

-`STALE_SEED:<pid>`, `BOUND:<id>`, `READY:<id>`, plus any raw token / step lines. **These are an

-internal parse contract — you read them to drive bringup; you NEVER echo them to the user.** Do not

+The relay emits machine markers on stdout/stderr: `BOUND:<id>`, `READY:<id>`, and any other raw

+token / step lines. **These are an internal parse contract — you read them to drive bringup; you

+NEVER echo them to the user.** Do not surface raw spt-core plumbing.

(substituting `<id>`) once `READY:<id>` is observed — and nothing else from the bringup:

-If bringup fails (e.g. `NO_SEED`/`STALE_SEED` and no `READY:<id>` within ~25s), do NOT show the LIVE

-block — report a short plain-language failure ("could not bring the live perch up") and the most

-likely cause (stale pid anchor, or run `/sptc:setup`), still without dumping the raw markers.

+If bringup fails (no `READY:<id>` within ~25s), do NOT show the LIVE block — report a short

+plain-language failure ("could not bring the live perch up") and the most likely cause (run

+`/sptc:setup`, or confirm spt-core is >= 0.9.0), still without dumping the raw markers.

-> The Psyche runner (`claude-spt-psyche`, declared in `[profiles.live.session.psyche_init]`) is the

-> resident headless-`claude` companion the daemon launches detached — it keeps a Psyche `claude`

+> The Psyche runner (`claude-spt-psyche`, declared in the base manifest's `[session.psyche_init]`) is

+> the resident headless-`claude` companion the daemon launches detached — it keeps a Psyche `claude`

# /sptc:live — operative instructions

> Delivered file-backed via the `claude-spt` adapter `[strings]`
> (`[strings.skills].live = { file = "skills/live.md" }`), resolved at injection time. The cplugs
> SKILL.md stays a thin skeleton (ADR-0001). **This injection is self-contained** — everything you
> need to go live is below; you never need to run a `how-to` topic or any external lookup.

**Goal:** upgrade THIS Claude Code session to a **LiveAgent** — a reachable agent whose context is
backed by a **Psyche** (the daemon-managed companion that briefs your resume across `/clear`/compact
via commune deltas). This is NOT spawning a separate or nested session; it is making the session you
are in right now live.

**How a session becomes live (no "go-live" verb):** LiveAgent vs ReadyAgent is gated entirely by the
resolved manifest declaring `[session.psyche_init]`. Base `claude-spt` declares none (= ReadyAgent,
like `/sptc:ready`); the `claude-spt:live` profile overlays it (= LiveAgent). Bringing the perch up
under the `:live` composite is what tells the spt-core daemon to host this endpoint's Psyche. The
Psyche is **daemon-managed by contract** — do NOT try to start, poll, or tear down the Psyche
yourself; the daemon owns its whole lifecycle off the perch's online status (so the companion
survives a relay restart).

---

## Bringup — do this

1. **Ensure `spt` is available.** If `spt` is not on `PATH`, run `/sptc:setup` first, then continue.

2. **Pick the perch id.** Use a short lowercase id, or reuse this session's id from `spt whoami`. Call
   it `<id>`. Use this session's id from `spt whoami` for `<sid>` (the session-id).

3. **Run the live relay as a single PERSISTENT background task** — Claude Code's **Monitor** tool (a
   long-running listener that keeps the process alive and re-fires per event WITHOUT terminating).
   This one resident process IS the live delivery pipe. It seeds the `:live` composite and binds the
   relay in the SAME process so both share one parent-pid anchor (see the INTERIM note below). The
   task body:

   ```sh
   # Anchor pid: spt probes WINDOWS pids. From git-bash, $$ is the MSYS pid (reads as a dead anchor);
   # use the WINPID column from `ps` (col 4) when present, else $$ is already the real pid.
   ANCHOR=$(ps -p $$ 2>/dev/null | awk 'NR==2{print $4}'); case "$ANCHOR" in ''|*[!0-9]*) ANCHOR=$$;; esac
   spt api --adapter claude-spt:live seed   --pid        "$ANCHOR" --session-id "<sid>"
   spt api --adapter claude-spt:live listen --parent-pid "$ANCHOR" "<id>"
   ```

   - The `listen` line **blocks for the session's life**: the spooled backlog drains first, then each
     delivery streams to stdout as it arrives.
   - Do **not** use `--once` — that drains the backlog and forwards a single delivery, then exits (the
     degenerate path for a harness with no long-running listener). It never sustains a live session.
   - On a registered adapter, `--adapter claude-spt:live` resolves the registered manifest, so no
     `--manifest` is needed.

4. **Bind-before-send / readiness.** Watch the relay's output for the bringup markers (see OUTPUT
   DISCIPLINE): `BOUND:<id>` (perch established) then `READY:<id>` (the relay is streaming). Only once
   `READY:<id>` appears is the live pipe up — do not announce LIVE before then.

5. **Poll/relay reconcile (avoids double-delivery).** While the Monitor relay is up it is the *single*
   delivery pipe — treat it as authoritative. The `UserPromptSubmit` `api poll` hook must NOT also
   drain; the per-prompt poll no-ops while the relay is live and resumes only if the relay goes down.

6. **Delivery + reply shape.** Inbound arrives on the relay stream as the
   `<EVENT type="msg" from="<sender>">body</EVENT>` envelope (body HTML-escaped, newlines as `<br>`).
   To reply, pipe the body to `spt send --reply-to <sender>`.

7. **Across boundaries.** As a live agent you now have a Psyche — use `/sptc:commune` to push a
   context delta before a `/clear` or compact so your resume is briefed. On a session boundary,
   re-fire the same relay (step 3) — the seed bridges the new session to the same perch and the relay
   resumes. To go offline gracefully (tears the Psyche down with the perch), use `/sptc:force-stop`
   (`spt endpoint shutdown <id>`).

> **INTERIM (PREP-4 / spt-core v0.9.0).** The seed+listen-chained-in-one-process step exists only
> because today's seed and listen must share a parent-pid anchor. Once spt-core ships the
> adapter-agnostic seed (SessionStart seeds by pid; `api listen` resolves the owning adapter/profile
> by pid), this collapses to a bare `spt api listen <id>` under Monitor — no shared-pid chaining and
> no `--adapter`. Do not pre-build that path; it activates on doyle's v0.9.0 ship.

---

## OUTPUT DISCIPLINE — what the user sees

The relay and seed emit machine markers on stdout/stderr: `SEEDED:<pid>`, `NO_SEED:<pid>`,
`STALE_SEED:<pid>`, `BOUND:<id>`, `READY:<id>`, plus any raw token / step lines. **These are an
internal parse contract — you read them to drive bringup; you NEVER echo them to the user.** Do not
surface raw spt-core plumbing.

**The ONLY user-facing surface for going live is the canonical LIVE block below.** Emit it verbatim
(substituting `<id>`) once `READY:<id>` is observed — and nothing else from the bringup:

```
**LIVE.** Session `<id>` is now a LiveAgent (Psyche-backed).
- Perch `<id>` — status online; Psyche companion hosted by the daemon.
- Reachable — other agents reach you with `/sptc:send <id>` (or `spt send <id>`).
- Inbound — streams into this session live; the Monitor relay is your delivery pipe.
- Reply — pipe a reply body to `spt send --reply-to <sender>`.
- Across resets — `/sptc:commune` before a `/clear`/compact; `/sptc:force-stop` to go offline (tears down the Psyche).
```

If bringup fails (e.g. `NO_SEED`/`STALE_SEED` and no `READY:<id>` within ~25s), do NOT show the LIVE
block — report a short plain-language failure ("could not bring the live perch up") and the most
likely cause (stale pid anchor, or run `/sptc:setup`), still without dumping the raw markers.

> The Psyche runner (`claude-spt-psyche`, declared in `[profiles.live.session.psyche_init]`) is the
> resident headless-`claude` companion the daemon launches detached — it keeps a Psyche `claude`
> session alive (one `--continue` turn per daemon pulse) and authors commune drops. You never invoke
> it directly; the daemon does.
<!-- [doc->REQ-SKILL-LIVE] -->


Now the seed branch → bare (bind/boundary unchanged per doyle).


Edit

  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    "$SPT" api --adapter "$ADAPTER" seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;

  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    # ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter — `api listen`/`poll` resolve the
    # owning adapter from this seed's parent pid via [adapter] host_binaries = ["claude"] (the legacy-
    # parity bare flow). $PPID is the CC host process (basename `claude`), the host_binaries match-key.
    # (bind/boundary below keep their explicit --adapter override — sanctioned, valid on 0.9.0.)
    "$SPT" api seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;


<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.


30	    "$SPT" api --adapter "$ADAPTER" bind "$SPT_ENDPOINT_ID" \
31	      --set-session-id "$sid" >/dev/null 2>&1 || true
32	    ;;
33	  seed)
34	    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
35	    "$SPT" api --adapter "$ADAPTER" seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
36	    ;;
37	esac
38
39	# Persist for the rest of the session (whoami + per-prompt hooks read these).
40	if [ -n "$CLAUDE_ENV_FILE" ]; then
41	  {

    "$SPT" api --adapter "$ADAPTER" bind "$SPT_ENDPOINT_ID" \
      --set-session-id "$sid" >/dev/null 2>&1 || true
    ;;
  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    "$SPT" api --adapter "$ADAPTER" seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;
esac

# Persist for the rest of the session (whoami + per-prompt hooks read these).
if [ -n "$CLAUDE_ENV_FILE" ]; then
  {


  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    "$SPT" api --adapter "$ADAPTER" seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;

  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    # ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter — `api listen`/`poll` resolve the
    # owning adapter from this seed's parent pid via [adapter] host_binaries = ["claude"] (the legacy-
    # parity bare flow). $PPID is the CC host process (basename `claude`), the host_binaries match-key.
    # (bind/boundary above keep their explicit --adapter override — sanctioned, valid on 0.9.0.)
    "$SPT" api seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;


The file C:\Users\decid\Documents\projects\spt-claude-code\plugin\sptc\hooks\session-start.sh has been updated successfully. (file state is current in your context — no need to Read it back)

  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    # ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter — `api listen`/`poll` resolve the
    # owning adapter from this seed's parent pid via [adapter] host_binaries = ["claude"] (the legacy-
    # parity bare flow). $PPID is the CC host process (basename `claude`), the host_binaries match-key.
    # (bind/boundary above keep their explicit --adapter override — sanctioned, valid on 0.9.0.)
    "$SPT" api seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;

#!/bin/sh
# SessionStart: ensure spt-core installed, then register the perch — bind (spt-hosted, broker
# spawned us via [session.self] + injected $SPT_ENDPOINT_ID), seed (harness-hosted, user-launched
# CC), or boundary-rebind (clear/compact) — and persist session env so whoami + per-prompt hooks
# resolve. Non-blocking (never `listen`).
# [impl->REQ-DIST-HOOKS-API]
. "$CLAUDE_PLUGIN_ROOT/hooks/_common.sh"

input=$(cat)
sid=$(json_str "$input" session_id)
src=$(json_str "$input" source)

# Invisible-installer: bootstrap spt-core if absent (no-op when present).
sh "$CLAUDE_PLUGIN_ROOT/bootstrap.sh" >/dev/null 2>&1 || true
SPT=$(spt_bin)

case "$(sptc_register_verb "$src")" in
  boundary)
    id=$(sptc_self_id "$sid")
    [ -n "$id" ] && "$SPT" api --adapter "$ADAPTER" boundary "$src" "$id" \
      --to-session-id "$sid" >/dev/null 2>&1 || true
    ;;
  bind)
    # spt-hosted: `spt endpoint run` spawned us into the broker PTY and injected the endpoint id via
    # [env.SPT_ENDPOINT_ID]. The perch already exists — self-register post-spawn by binding our
    # discovered CC session id, NOT seed (no seed file in this path). `bind` is an ESTABLISHING call:
    # the broker parentage IS the credential (auth intrinsic), so --set-session-id alone, no proof
    # token — confirmed by doyle 2026-06-15 (harness-contract/api.md "Auth is intrinsic"). Later
    # mutating calls prove association with --session-id "$sid" against the record bind wrote.
    "$SPT" api --adapter "$ADAPTER" bind "$SPT_ENDPOINT_ID" \
      --set-session-id "$sid" >/dev/null 2>&1 || true
    ;;
  seed)
    # harness-hosted (seed→listen path): user-launched CC. Record the seed for /sptc:ready|live.
    "$SPT" api --adapter "$ADAPTER" seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true
    ;;
esac

# Persist for the rest of the session (whoami + per-prompt hooks read these).
if [ -n "$CLAUDE_ENV_FILE" ]; then
  {
    printf 'OWL_SESSION_ID=%s\n' "$sid"
    printf 'SPT_ADAPTER=%s\n' "$ADAPTER"
  } >> "$CLAUDE_ENV_FILE"
fi
exit 0

-    "$SPT" api --adapter "$ADAPTER" seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true

+    # ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter — `api listen`/`poll` resolve the

+    # owning adapter from this seed's parent pid via [adapter] host_binaries = ["claude"] (the legacy-

+    # parity bare flow). $PPID is the CC host process (basename `claude`), the host_binaries match-key.

+    # (bind/boundary above keep their explicit --adapter override — sanctioned, valid on 0.9.0.)

+    "$SPT" api seed --pid "$PPID" --session-id "$sid" >/dev/null 2>&1 || true


Update the manifest `[hooks.SessionStart].fires` declaration to mirror the bare seed.


[hooks.SessionStart]
# Seed the endpoint. {parent_pid} + {adapter_name} are spt-filled keys (confirmed against the
# published mock-adapter manifest); session_id is a CC stdin field. NOT the blocking poll/listen
# loop — that is /sptc:ready|live, never SessionStart.
fires = "api seed --session-id {session_id} --pid {parent_pid} --adapter {adapter_name}"
reads = ["session_id", "parent_pid"]
can_inject = true

[hooks.SessionStart]
# Seed the endpoint — ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter. The owning
# adapter resolves at bind time from this seed's {parent_pid} via [adapter] host_binaries (the
# legacy-parity bare flow). {parent_pid} is a spt-filled key; session_id is a CC stdin field. NOT the
# blocking poll/listen loop — that is /sptc:ready|live, never SessionStart. (Mirrors the hand-written
# plugin hook session-start.sh, whose bind/boundary branches keep an explicit --adapter override.)
fires = "api seed --session-id {session_id} --pid {parent_pid}"
reads = ["session_id", "parent_pid"]
can_inject = true


[hooks.SessionStart]
# Seed the endpoint. {parent_pid} + {adapter_name} are spt-filled keys (confirmed against the
# published mock-adapter manifest); session_id is a CC stdin field. NOT the blocking poll/listen
# loop — that is /sptc:ready|live, never SessionStart.
fires = "api seed --session-id {session_id} --pid {parent_pid} --adapter {adapter_name}"
reads = ["session_id", "parent_pid"]
can_inject = true

[hooks.SessionStart]
# Seed the endpoint — ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter. The owning
# adapter resolves at bind time from this seed's {parent_pid} via [adapter] host_binaries (the
# legacy-parity bare flow). {parent_pid} is a spt-filled key; session_id is a CC stdin field. NOT the
# blocking poll/listen loop — that is /sptc:ready|live, never SessionStart. (Mirrors the hand-written
# plugin hook session-start.sh, whose bind/boundary branches keep an explicit --adapter override.)
fires = "api seed --session-id {session_id} --pid {parent_pid}"
reads = ["session_id", "parent_pid"]
can_inject = true

# fields we map; `can_inject` = whether the event can surface additionalContext back to the agent.

-# Seed the endpoint. {parent_pid} + {adapter_name} are spt-filled keys (confirmed against the

-# published mock-adapter manifest); session_id is a CC stdin field. NOT the blocking poll/listen

-# loop — that is /sptc:ready|live, never SessionStart.

-fires = "api seed --session-id {session_id} --pid {parent_pid} --adapter {adapter_name}"

+# Seed the endpoint — ADAPTER-AGNOSTIC since spt-core v0.9.0 (PREP-4): NO --adapter. The owning

+# adapter resolves at bind time from this seed's {parent_pid} via [adapter] host_binaries (the

+# legacy-parity bare flow). {parent_pid} is a spt-filled key; session_id is a CC stdin field. NOT the

+# blocking poll/listen loop — that is /sptc:ready|live, never SessionStart. (Mirrors the hand-written

+# plugin hook session-start.sh, whose bind/boundary branches keep an explicit --adapter override.)

+fires = "api seed --session-id {session_id} --pid {parent_pid}"


Now setup.md — fix the `:live` spot-check + add the `spt adapter use claude-spt` pointer step. Read the relevant region.


38	       then `spt adapter add --release SaberMage/spt-claude-code --asset adapter-$os-$arch.spt` —
39	       fetches the per-OS `adapter-<os>-<arch>.spt` release asset (a tar whose **root** holds
40	       `manifest.toml` + `strings/` + the native tool binaries) from the repo's GitHub release,
41	       extracts it to the durable adapter home, and registers it. Add `--tag <ver>` to pin a version
42	       (omit for latest). Re-running with a newer `--tag` is a manual re-acquire. `adapter add` is
43	       manifest-first (an invalid manifest registers nothing). (v1 ships **windows + linux** assets.
44	       Recommended path — straight from the monorepo, no dedicated repo. Needs the spt release that
45	       carries `--release`; the older `--github <root-manifest-repo>` is the alternative.)
46	       <!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
47
48	3. **Verify activation.** Re-run `spt adapter list`: `claude-spt` must now appear **active** (no
49	   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:live adapter_label`
50	   (any shipped profile). Report the active state.
51
52	4. **Tool binaries — resolved from the install dir (REQ-INSTALL-11; no PATH copy).** The `[digest]`
53	   extractor (`claude-spt-digest`) and the Psyche runner (`claude-spt-psyche`) are invoked by **bare
54	   name**. spt-core resolves them **from the adapter install dir** — the `from …` path in
55	   `spt adapter list` (e.g. `…/adapters/_github/<safe>/`), where `--release` activation already
56	   extracted them beside the manifest. **Nothing to do here:** step 2 placed them; no PATH copy is
57	   needed. Verified on spt **v0.8.1** — session-digest and the daemon-hosted Psyche both resolve the

       then `spt adapter add --release SaberMage/spt-claude-code --asset adapter-$os-$arch.spt` —
       fetches the per-OS `adapter-<os>-<arch>.spt` release asset (a tar whose **root** holds
       `manifest.toml` + `strings/` + the native tool binaries) from the repo's GitHub release,
       extracts it to the durable adapter home, and registers it. Add `--tag <ver>` to pin a version
       (omit for latest). Re-running with a newer `--tag` is a manual re-acquire. `adapter add` is
       manifest-first (an invalid manifest registers nothing). (v1 ships **windows + linux** assets.
       Recommended path — straight from the monorepo, no dedicated repo. Needs the spt release that
       carries `--release`; the older `--github <root-manifest-repo>` is the alternative.)
       <!-- [doc->REQ-DIST-ADAPTER-PEROS] -->

3. **Verify activation.** Re-run `spt adapter list`: `claude-spt` must now appear **active** (no
   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:live adapter_label`
   (any shipped profile). Report the active state.

4. **Tool binaries — resolved from the install dir (REQ-INSTALL-11; no PATH copy).** The `[digest]`
   extractor (`claude-spt-digest`) and the Psyche runner (`claude-spt-psyche`) are invoked by **bare
   name**. spt-core resolves them **from the adapter install dir** — the `from …` path in
   `spt adapter list` (e.g. `…/adapters/_github/<safe>/`), where `--release` activation already
   extracted them beside the manifest. **Nothing to do here:** step 2 placed them; no PATH copy is
   needed. Verified on spt **v0.8.1** — session-digest and the daemon-hosted Psyche both resolve the


3. **Verify activation.** Re-run `spt adapter list`: `claude-spt` must now appear **active** (no
   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:live adapter_label`
   (any shipped profile). Report the active state.

3. **Verify activation.** Re-run `spt adapter list`: `claude-spt` must now appear **active** (no
   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:deep adapter_label`
   (any shipped profile — `:deep` or `:ccs`). Report the active state.

   Then **set the active-profile pointer** so bare resolution lands on this adapter for the `claude`
   host binary (the legacy-parity bare flow — `/sptc:live` and `/sptc:ready` need no `--adapter`):
   `spt adapter use claude-spt`. (With a single claude-hosting adapter registered this is the implicit
   default, but setting it is explicit + durable. There is **no `:live` profile** — base claude-spt is
   live-capable and the command decides ready vs live; reach for `--adapter claude-spt:ccs`/`:deep`
   only to select one of those override profiles.)


The file C:\Users\decid\Documents\projects\spt-claude-code\adapter\strings\skills\setup.md has been updated successfully. (file state is current in your context — no need to Read it back)

3. **Verify activation.** Re-run `spt adapter list`: `claude-spt` must now appear **active** (no
   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:deep adapter_label`
   (any shipped profile — `:deep` or `:ccs`). Report the active state.

   Then **set the active-profile pointer** so bare resolution lands on this adapter for the `claude`
   host binary (the legacy-parity bare flow — `/sptc:live` and `/sptc:ready` need no `--adapter`):
   `spt adapter use claude-spt`. (With a single claude-hosting adapter registered this is the implicit
   default, but setting it is explicit + durable. There is **no `:live` profile** — base claude-spt is
   live-capable and the command decides ready vs live; reach for `--adapter claude-spt:ccs`/`:deep`
   only to select one of those override profiles.)

# /sptc:setup — operative instructions

> Delivered file-backed via the `claude-spt` adapter `[strings]`
> (`[strings.skills].setup = { file = "skills/setup.md" }`), resolved at injection time. The cplugs
> SKILL.md stays a thin skeleton (ADR-0001).

**Goal:** install or repair spt-core for this session **and ACTIVATE the claude-spt adapter** — the
mid-session installer that covers the gap when no SessionStart bootstrap has fired (a user who
installs the plugin mid-session). Installing the binary is only half the job: a present binary with
a **deregistered** adapter has no profiles/strings/hints/`[digest]`, so `/sptc:*` injection has
nothing to source. Setup must leave the adapter **active**.

<!-- [doc->REQ-SETUP-ACTIVATE] -->

**Do this:**

1. **Binary.** Check whether spt-core is present: `command -v spt && spt --version`.
   - If absent, run the published install-on-demand bootstrap (verbatim against the spt-releases
     contract, `harness-contract/install-on-demand.md`):
     - **POSIX:** `curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh`
     - **Windows (PowerShell):** `irm https://sabermage.github.io/spt-releases/install.ps1 | iex`
   - `PATH` is not reloaded in the current shell after a fresh install — verify with the absolute
     path for the first call: `"$HOME/.local/bin/spt" --version` (POSIX). Report the version.

2. **Adapter activation (the step a present binary still needs).** Run `spt adapter list` and look
   for `claude-spt`:
   - If `claude-spt` is listed and **not** marked `deregistered` → the adapter is **active**; report
     "adapter active" and go to step 4.
   - If `claude-spt` is **missing or `deregistered`** → **activate** it. Pick the source:
     - **Local dev / dogfooding from a repo checkout** (an `adapter/claude-spt.toml` is present near
       cwd): `spt adapter add ./adapter/claude-spt.toml` (the file-form takes any path + filename).
     - **End-user (plugin only, no repo checkout):** the `.spt` asset carries native binaries, so it
       ships **per-OS** — select the one matching this host. Detect os + arch:
       ```sh
       os=$(uname -s);  case "$os" in MINGW*|MSYS*|CYGWIN*|Windows*) os=windows;; Linux) os=linux;; Darwin) os=macos;; esac
       arch=$(uname -m); case "$arch" in x86_64|amd64) arch=x86_64;; arm64|aarch64) arch=aarch64;; esac
       ```
       then `spt adapter add --release SaberMage/spt-claude-code --asset adapter-$os-$arch.spt` —
       fetches the per-OS `adapter-<os>-<arch>.spt` release asset (a tar whose **root** holds
       `manifest.toml` + `strings/` + the native tool binaries) from the repo's GitHub release,
       extracts it to the durable adapter home, and registers it. Add `--tag <ver>` to pin a version
       (omit for latest). Re-running with a newer `--tag` is a manual re-acquire. `adapter add` is
       manifest-first (an invalid manifest registers nothing). (v1 ships **windows + linux** assets.
       Recommended path — straight from the monorepo, no dedicated repo. Needs the spt release that
       carries `--release`; the older `--github <root-manifest-repo>` is the alternative.)
       <!-- [doc->REQ-DIST-ADAPTER-PEROS] -->

3. **Verify activation.** Re-run `spt adapter list`: `claude-spt` must now appear **active** (no
   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:live adapter_label`
   (any shipped profile). Report the active state.

4. **Tool binaries — resolved from the install dir (REQ-INSTALL-11; no PATH copy).** The `[digest]`
   extractor (`claude-spt-digest`) and the Psyche runner (`claude-spt-psyche`) are invoked by **bare
   name**. spt-core resolves them **from the adapter install dir** — the `from …` path in
   `spt adapter list` (e.g. `…/adapters/_github/<safe>/`), where `--release` activation already
   extracted them beside the manifest. **Nothing to do here:** step 2 placed them; no PATH copy is
   needed. Verified on spt **v0.8.1** — session-digest and the daemon-hosted Psyche both resolve the
   binary straight from the install dir with nothing on PATH.
   - *(The legacy F-006 interim PATH-copy step is **retired**: spt-core's install-dir resolution —
     REQ-INSTALL-11, Feature B, landed in spt v0.8.0 — supersedes it. If session-digest or the Psyche
     ever fails to start, confirm the two `.exe`s are present in the install dir above; their absence
     is a packaging defect, not a PATH problem.)*

5. **ccs wiring (optional — SCOPE setup #7).** Detect `~/.ccs`:
   - **Present** → ccs is installed. The shipped **`claude-spt:ccs`** profile leaf-replaces the live
     session command with `ccs` (a drop-in for `claude`), so live/ready agents can run on ccs's
     configured backends (glm/kimi/custom from `~/.ccs/config.json`) instead of `claude`. Use it by
     selecting the `:ccs` composite: e.g. `/sptc:live` / `/sptc:ready` under `--adapter claude-spt:ccs`,
     or `spt endpoint run --adapter claude-spt:ccs`. Sanity-check `command -v ccs`; if `~/.ccs` exists
     but `ccs` is not on PATH, point the user at their ccs install's bin dir. (No action needed if
     they don't want ccs — base `claude-spt` is unaffected.)
   - **Absent** → ccs is an optional CLI router that lets your spt sessions drive alternate model
     backends (glm/kimi/custom) in place of `claude`. To enable: install ccs (see its docs), then
     re-run `/sptc:setup`. Skip if not wanted.

   <!-- [doc->REQ-SETUP-CCS] -->

6. **Subnet onboarding (optional — SCOPE setup #3/#4).** A **subnet** is the private group of paired
   machines that makes `/sptc:send`, `/sptc:ready`, and live agents work **cross-machine** (local
   single-machine use needs none). Check membership: `spt subnet status` (or bare `spt subnet`).
   - **Already in a subnet** → report it. To invite another machine: `spt subnet show-code` (prints
     the current 6-digit code + `otpauth://` URI + a terminal QR). On the joining machine run
     `spt subnet join <name> --code <code>`.
   - **Not in a subnet** → offer either: **create** the first one — `spt subnet create <name>` (this
     node becomes the seed-holder; prints code/URI/QR) — or **join** an existing one —
     `spt subnet join <name> --code <code>`. Skip if single-machine use is fine.
   - Full verb guidance (status/create/show-code/join + lifecycle) lives in **/sptc:subnet** — route
     there rather than re-explaining.
   - **Elevation:** `create`/`join`/`show-code` are **OS-elevation-gated** (seed-reveal /
     machine-enrollment). Run where elevation can be granted: **Windows** → an elevated (UAC) shell;
     **Linux desktop** → pkexec/polkit or a sudo-capable terminal; **Linux TTY** → inline `sudo`;
     **headless / no-TTY** → print the exact command for the user to run elevated. (Automated
     context-aware elevation is a deeper wave; today, surface the requirement + the exact command.)

   <!-- [doc->REQ-SETUP-SUBNET] -->

After this initial bootstrap + activation, `spt update` handles signed self-updates automatically —
the user does not run setup again. The whole flow is idempotent and safe to re-run.

-   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:live adapter_label`

-   (any shipped profile). Report the active state.

+   `deregistered`). Spot-check a profile resolves: `spt adapter get-string claude-spt:deep adapter_label`

+   (any shipped profile — `:deep` or `:ccs`). Report the active state.

+   Then **set the active-profile pointer** so bare resolution lands on this adapter for the `claude`

+   host binary (the legacy-parity bare flow — `/sptc:live` and `/sptc:ready` need no `--adapter`):

+   `spt adapter use claude-spt`. (With a single claude-hosting adapter registered this is the implicit

+   default, but setting it is explicit + durable. There is **no `:live` profile** — base claude-spt is

+   live-capable and the command decides ready vs live; reach for `--adapter claude-spt:ccs`/`:deep`

+   only to select one of those override profiles.)


Now the live SKILL.md stub (remove the `:live` profile claim).


19
20	Upgrades THIS session to a LiveAgent (Psyche-backed) — distinct from `spt endpoint run` (which
21	spawns a separate broker-PTY session). Activates the `claude-spt:live` profile (overlays
22	`[session.psyche_init]` → the daemon spawns the Psyche) and runs a resident Monitor relay as the
23	delivery pipe.
24

Upgrades THIS session to a LiveAgent (Psyche-backed) — distinct from `spt endpoint run` (which
spawns a separate broker-PTY session). Activates the `claude-spt:live` profile (overlays
`[session.psyche_init]` → the daemon spawns the Psyche) and runs a resident Monitor relay as the
delivery pipe.


Upgrades THIS session to a LiveAgent (Psyche-backed) — distinct from `spt endpoint run` (which
spawns a separate broker-PTY session). Activates the `claude-spt:live` profile (overlays
`[session.psyche_init]` → the daemon spawns the Psyche) and runs a resident Monitor relay as the
delivery pipe.

Upgrades THIS session to a LiveAgent (Psyche-backed) — distinct from `spt endpoint run` (which
spawns a separate broker-PTY session). Base `claude-spt` is live-capable (`[session.psyche_init]` in
the base manifest); going live is just the command — a resident Monitor relay running bare
`spt api listen <id>` (no `--adapter`). The live listen path stamps the perch `state=live_agent`, so
the daemon hosts the Psyche; `/sptc:ready` (poll) stays Psyche-less.


The file C:\Users\decid\Documents\projects\spt-claude-code\plugin\sptc\skills\live\SKILL.md has been updated successfully. (file state is current in your context — no need to Read it back)

Upgrades THIS session to a LiveAgent (Psyche-backed) — distinct from `spt endpoint run` (which
spawns a separate broker-PTY session). Base `claude-spt` is live-capable (`[session.psyche_init]` in
the base manifest); going live is just the command — a resident Monitor relay running bare
`spt api listen <id>` (no `--adapter`). The live listen path stamps the perch `state=live_agent`, so
the daemon hosts the Psyche; `/sptc:ready` (poll) stays Psyche-less.

---
name: live
description: |
  Run a live agent session (Self + Psyche companion). For past sessions, restores a summarized context.
argument-hint: "[<id>] [--auto]"
allowed-tools: [Bash]
---

# /sptc:live

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
>
> **Operative.** The UPS hook detects `/sptc:live` and injects the body from
> `adapter/strings/skills/live.md` (UPS-fires-on-slash confirmed, ADR-0002; file-backed `[strings]`
> shipped, F-003). If injection ever no-ops (spt absent / adapter unregistered), this stub is the
> floor.

Upgrades THIS session to a LiveAgent (Psyche-backed) — distinct from `spt endpoint run` (which
spawns a separate broker-PTY session). Activates the `claude-spt:live` profile (overlays
`[session.psyche_init]` → the daemon spawns the Psyche) and runs a resident Monitor relay as the
delivery pipe.

-spawns a separate broker-PTY session). Activates the `claude-spt:live` profile (overlays

-`[session.psyche_init]` → the daemon spawns the Psyche) and runs a resident Monitor relay as the

+spawns a separate broker-PTY session). Base `claude-spt` is live-capable (`[session.psyche_init]` in

+the base manifest); going live is just the command — a resident Monitor relay running bare

+`spt api listen <id>` (no `--adapter`). The live listen path stamps the perch `state=live_agent`, so

+the daemon hosts the Psyche; `/sptc:ready` (poll) stays Psyche-less.


Now traceable-reqs REQ-SKILL-LIVE — rewrite title + int notes to Option A.


id = "REQ-SKILL-LIVE"
title = "/sptc:live upgrades THIS CC session to a LiveAgent: the claude-spt:live profile overlays [session.psyche_init] (daemon spawns the Psyche), the claude-spt-psyche runner keeps the Psyche claude alive (one --continue turn per pulse, NOT one-shot `claude -p`), and the live body activates the :live composite + a resident Monitor relay as the single delivery pipe"

id = "REQ-SKILL-LIVE"
title = "/sptc:live upgrades THIS CC session to a LiveAgent: base claude-spt is live-capable ([session.psyche_init] in the BASE manifest, Option A) so ready-vs-live is the COMMAND not a profile — bare `spt api listen <id>` stamps the perch state=live_agent and the daemon hosts the Psyche (poll stays ready, livehost.rs:282 gate); the claude-spt-psyche runner keeps the Psyche claude alive (one --continue turn per pulse, NOT one-shot `claude -p`); a resident Monitor relay is the single delivery pipe; zero --adapter (host_binaries resolution, spt-core v0.9.0+)"