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

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+)"

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

+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+)"

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


Edit

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

required_stages = ["doc", "impl", "unit", "int"]  # doc: adapter/strings/skills/live.md (bare-listen bringup + poll/relay reconcile + Psyche-is-daemon-managed; Option A, no :live profile). impl: [session.psyche_init] in the BASE adapter/claude-spt.toml (the LiveAgent seam, 4 daemon keys, base-not-overlay) + min_spt_core_version=0.9.0 + the bare SessionStart seed (session-start.sh) + 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 a persistent CHILD `spt api ... listen <id>`, NOT --once) under a disposable id and asserts on real spt >=0.9.0: PSYCHE-SPAWN (the daemon livehost hosts the Psyche off the perch's live_agent online status — resident claude-spt-psyche runner + nested {id}-psyche perch dir; livehost.rs:282 gates on live_agent state) + RELAY (BOUND/READY + a probe relayed as the escaped <EVENT> envelope + live_agent kind). PSYCHE hosts off the BASE manifest (Option A — psyche_init in base; the live listen path stamps live_agent which actualizes it, poll stays ready). Gotchas baked in (doyle): anchor on the WINDOWS pid not git-bash `$$` (else STALE_SEED); `bind` before `send` (else NO_PERCH, no queue). The psyche binary resolves by bare name from the adapter install dir (REQ-INSTALL-11, Feature B v0.8.0+). Bare-flow (no --adapter; host_binaries resolution) is the spt-core v0.9.0 legacy-parity path — F-007 (how-to redirect) retired, the body is self-contained.


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+)"

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

+required_stages = ["doc", "impl", "unit", "int"]  # doc: adapter/strings/skills/live.md (bare-listen bringup + poll/relay reconcile + Psyche-is-daemon-managed; Option A, no :live profile). impl: [session.psyche_init] in the BASE adapter/claude-spt.toml (the LiveAgent seam, 4 daemon keys, base-not-overlay) + min_spt_core_version=0.9.0 + the bare SessionStart seed (session-start.sh) + 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 a persistent CHILD `spt api ... listen <id>`, NOT --once) under a disposable id and asserts on real spt >=0.9.0: PSYCHE-SPAWN (the daemon livehost hosts the Psyche off the perch's live_agent online status — resident claude-spt-psyche runner + nested {id}-psyche perch dir; livehost.rs:282 gates on live_agent state) + RELAY (BOUND/READY + a probe relayed as the escaped <EVENT> envelope + live_agent kind). PSYCHE hosts off the BASE manifest (Option A — psyche_init in base; the live listen path stamps live_agent which actualizes it, poll stays ready). Gotchas baked in (doyle): anchor on the WINDOWS pid not git-bash `$$` (else STALE_SEED); `bind` before `send` (else NO_PERCH, no queue). The psyche binary resolves by bare name from the adapter install dir (REQ-INSTALL-11, Feature B v0.8.0+). Bare-flow (no --adapter; host_binaries resolution) is the spt-core v0.9.0 legacy-parity path — F-007 (how-to redirect) retired, the body is self-contained.


178	# profile leaf does NOT register as impl evidence; the real impl is the env-aware extractor).
179	# doc = manifest [profiles.ccs] overlay + docs/PARITY.md. int = registration-int profile-resolves
180	# assertion (claude-spt:ccs resolves + adapter_label overlay observable; mirrors :deep/:live).
181	required_stages = ["doc", "impl", "unit", "int"]

# profile leaf does NOT register as impl evidence; the real impl is the env-aware extractor).
# doc = manifest [profiles.ccs] overlay + docs/PARITY.md. int = registration-int profile-resolves
# assertion (claude-spt:ccs resolves + adapter_label overlay observable; mirrors :deep/:live).
required_stages = ["doc", "impl", "unit", "int"]


# assertion (claude-spt:ccs resolves + adapter_label overlay observable; mirrors :deep/:live).

# assertion (claude-spt:ccs resolves + adapter_label overlay observable; mirrors :deep).


# impl/unit = the CLAUDE_CONFIG_DIR-aware projects-root resolver in claude-spt-digest (a .toml

# profile leaf does NOT register as impl evidence; the real impl is the env-aware extractor).

# doc = manifest [profiles.ccs] overlay + docs/PARITY.md. int = registration-int profile-resolves

-# assertion (claude-spt:ccs resolves + adapter_label overlay observable; mirrors :deep/:live).

+# assertion (claude-spt:ccs resolves + adapter_label overlay observable; mirrors :deep).

required_stages = ["doc", "impl", "unit", "int"]


Now docs/PARITY.md (lines 18, 27).


14	## Disposition matrix
15
16	| Surface | SCOPE bucket | Adapter today | Status |
17	|---|---|---|---|
18	| `/live` | KEEP | operative — `skills/live.md` (`:live` profile + Monitor relay) | ✅ parity |
19	| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
20	| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
21	| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
22	| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
23	| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |
24	| `/force-stop` | KEEP (topology-aware) | operative — `skills/force-stop.md` | ✅ parity |
25	| `/spt:setup` | ADD | operative — `skills/setup.md` | ✅ parity |
26	| `/spt:version` | ADD | operative — `skills/version.md` | ✅ parity |
27	| profiles + strings + hints | ADD | wired (`:deep`, `:live`, `[strings]`, `[[hints]]`) | ✅ parity |
28	| subnet skill (status/create/show-code/join) | ADD (LOCKED) | operative — `skills/subnet.md` (wraps `spt subnet`) | ✅ parity (REQ-SKILL-SUBNET) |
29	| ccs profile (`claude-spt:ccs`) | ADD (LOCKED) | operative — `[profiles.ccs]` overlay + CLAUDE_CONFIG_DIR-aware extractor | ✅ parity (REQ-CCS-PROFILES) |
30	| `/whoami` skill | **DROP** (core `spt whoami` stays → M12 `endpoint list` alias) | removed (was carry-over) | ✅ dropped |
31	| `/new-alarm` | accepted gap | dead stub, no manifest entry | ⏸ DEFER + remove stub |

## Disposition matrix

| Surface | SCOPE bucket | Adapter today | Status |
|---|---|---|---|
| `/live` | KEEP | operative — `skills/live.md` (`:live` profile + Monitor relay) | ✅ parity |
| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |
| `/force-stop` | KEEP (topology-aware) | operative — `skills/force-stop.md` | ✅ parity |
| `/spt:setup` | ADD | operative — `skills/setup.md` | ✅ parity |
| `/spt:version` | ADD | operative — `skills/version.md` | ✅ parity |
| profiles + strings + hints | ADD | wired (`:deep`, `:live`, `[strings]`, `[[hints]]`) | ✅ parity |
| subnet skill (status/create/show-code/join) | ADD (LOCKED) | operative — `skills/subnet.md` (wraps `spt subnet`) | ✅ parity (REQ-SKILL-SUBNET) |
| ccs profile (`claude-spt:ccs`) | ADD (LOCKED) | operative — `[profiles.ccs]` overlay + CLAUDE_CONFIG_DIR-aware extractor | ✅ parity (REQ-CCS-PROFILES) |
| `/whoami` skill | **DROP** (core `spt whoami` stays → M12 `endpoint list` alias) | removed (was carry-over) | ✅ dropped |
| `/new-alarm` | accepted gap | dead stub, no manifest entry | ⏸ DEFER + remove stub |


| `/live` | KEEP | operative — `skills/live.md` (`:live` profile + Monitor relay) | ✅ parity |

| `/live` | KEEP | operative — `skills/live.md` (base-manifest `[session.psyche_init]` + bare `api listen` + Monitor relay; Option A, no `:live` profile) | ✅ parity |


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

# Parity audit — claude-spt vs the legacy owl surface

> The reconciliation record: the adapter's operative surface measured against the **LOCKED
> parity-trim** in `SCOPE.md` (KEEP / ADD / DROP / TRANSFORM). This is the **"proven parity"**
> gate the `sptc`→`spt` succession flip depends on (see `docs/RELEASE-RUNBOOK.md` +
> `docs/KNOWN-HAZARDS.md`): the name flip happens only once this audit shows no unresolved
> divergence AND legacy owl is retired. Audited 2026-06-15 against published spt v0.7.2.
>
> Public-surface-only binds (AGENTS.md): a capability missing from the published `spt` surface is
> a **finding**, never a reason to reach into spt-core source.

<!-- [doc->REQ-PARITY-AUDIT] -->

## Disposition matrix

| Surface | SCOPE bucket | Adapter today | Status |
|---|---|---|---|
| `/live` | KEEP | operative — `skills/live.md` (`:live` profile + Monitor relay) | ✅ parity |
| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |
| `/force-stop` | KEEP (topology-aware) | operative — `skills/force-stop.md` | ✅ parity |
| `/spt:setup` | ADD | operative — `skills/setup.md` | ✅ parity |
| `/spt:version` | ADD | operative — `skills/version.md` | ✅ parity |
| profiles + strings + hints | ADD | wired (`:deep`, `:live`, `[strings]`, `[[hints]]`) | ✅ parity |
| subnet skill (status/create/show-code/join) | ADD (LOCKED) | operative — `skills/subnet.md` (wraps `spt subnet`) | ✅ parity (REQ-SKILL-SUBNET) |
| ccs profile (`claude-spt:ccs`) | ADD (LOCKED) | operative — `[profiles.ccs]` overlay + CLAUDE_CONFIG_DIR-aware extractor | ✅ parity (REQ-CCS-PROFILES) |
| `/whoami` skill | **DROP** (core `spt whoami` stays → M12 `endpoint list` alias) | removed (was carry-over) | ✅ dropped |
| `/new-alarm` | accepted gap | dead stub, no manifest entry | ⏸ DEFER + remove stub |
| `/fork` | DROP | absent | ✅ confirmed absent |
| `/amend-signoff` | DROP | absent | ✅ confirmed absent |
| `/revive` | DROP (daemon owns liveness) | absent | ✅ confirmed absent |
| `/clear-psyche` | DROP (= delete+recreate endpoint) | absent | ✅ confirmed absent |

TRANSFORM bucket (already realised, not skills): capsule→spt-hosted+`cc` (`[session.self]` +
`shortcut_basename="cc"`); echo-commune→`[digest]` extractor (`claude-spt-digest`); psyche-sync→
`/spt:setup`; doctor→setup-verify; working-perches→`api worker-start/stop` hooks. ✅

## Open divergences and dispositions

### subnet skills — LOCKED-ADD gap → BUILD
The published surface carries the verbs (`spt subnet {create,join,show-code,status,…}`), and v1
topology scope is **mandatory** (operator vetoed harness-hosted-only — cross-subnet/PTY proof is
spt-core's central value prop, SCOPE). So this is a real gap to fill, not defer. → `REQ-SKILL-SUBNET`.

### ccs profile — LOCKED-ADD gap → BUILT ✅
<!-- [doc->REQ-CCS-PROFILES] -->
SCOPE §ccs (LOCKED): ccs ships as a **profile template** under the adapter that retargets the spawn
command template through `ccs`, with ccs's relocated log dir honored. Templates only — the user
supplies their own ccs config/keys. **Operator ruling (2026-06-15):** the shipped profile is
`claude-spt:ccs` invoking **bare `ccs`** (the account set via `ccs auth default <name>`), NOT
`glm`/`kimi` — those were SCOPE *examples*; this profile IS the worked example of adapter-profile
authoring **and** the operator's own SPT-ecosystem hook. Built + validated against the known-good
sister project **claude_skill_owl**:
- **Spawn seam.** `[profiles.ccs.session.self].command = "ccs"` leaf-replaces the base `claude`.
  owl proves ccs is a drop-in for the `claude` binary on the same argv (`live/wrapper/claude.rs`
  Tier-2 PULSE recovery latches `cli_binary = "ccs"`); `SPT_ENDPOINT_ID` rides inherited env through
  the wrapper unchanged.
- **Log-dir seam → in the extractor, not a manifest leaf.** ccs relocates CC's whole state tree
  (incl. `projects/`) via the **`CLAUDE_CONFIG_DIR`** env var (`~/.ccs/instances/<account>/.claude`),
  a per-account runtime value with no static catalog path. SCOPE's "per-profile `~/.ccs` log dir"
  is therefore honored **in `claude-spt-digest`** (dir-locate branch prefers `$CLAUDE_CONFIG_DIR/
  projects` over the `--in` root) — the owl-validated `owlery::claude_projects_root` pattern. The
  base `[digest]` config thus serves base **and** ccs sessions transparently; no `[profiles.ccs.
  digest]` leaf. This env-aware resolver is REQ-CCS-PROFILES's `impl`/`unit` evidence (a `.toml`
  profile leaf alone does not register as `impl`).
- **Finding (refines SCOPE's model).** SCOPE conflated ccs *profiles* (glm/kimi = API-provider
  env-swap, NO transcript relocation) with ccs *accounts* (isolated instances that DO relocate via
  `CLAUDE_CONFIG_DIR`). The operator runs the account/default path, which relocates — so the digest
  seam is real, but its mechanism is `CLAUDE_CONFIG_DIR`, not a fixed `~/.ccs` path. → `REQ-CCS-PROFILES`.

### `/whoami` skill — DROP (honoring LOCKED)
SCOPE DROPs the whoami **skill**; core `spt whoami` stays in the hot path (and becomes the
`endpoint list` alias in M12). The adapter currently still ships a whoami skill (a carry-over from
an early slice). Operator confirmed the drop 2026-06-15. Action: remove `[strings.skills].whoami` +
the file body + the plugin stub; redirect the "who am i" hint to core `spt whoami`. No REQ rides
whoami evidence (the file-backed-string + UPS-injection int examples repoint to `skills.ready`;
the hook-parser unit examples repoint to a surviving skill name). Core `spt whoami` calls in the
hooks (`session-start.sh`, `_common.sh`) are the CLI, not the skill — they STAY.

### `/new-alarm` — ACCEPTED GAP (deferred-pending-arrangement-system)
**Not missing — deliberately deferred.** doyle triage (2026-06-15): spt-core carries only the
alarm *event shape* (spt-proto) + relay handling (psyrelay.rs) + a test fixture; the durable
"fire-at-target-time" timer still lives in legacy owl in-memory, and an in-daemon durable scheduler
is a deliberate deferral (DEFERRED.md:43, ADR-0018 Q4/V3 — deferred because the daemon had no
one-shot consumer; building pre-consumer = untested dead code). The **operator has a superseding
"arrangement" system** in mind that would replace the alarm port. Disposition: **do not build an
adapter-side stopgap timer** (throwaway vs the coming model); **no REQ minted**; **remove the dead
`/sptc:new-alarm` stub** from the published surface (deferred ≠ shipping a no-op skill). doyle loops
me when the arrangement design surfaces.

## Parity verdict
**PROVEN (adapter surface).** All three open divergences are closed this slice: subnet skill added
(`skills/subnet.md`, REQ-SKILL-SUBNET), ccs profile added (`[profiles.ccs]` + CLAUDE_CONFIG_DIR-aware
extractor, REQ-CCS-PROFILES), whoami skill dropped. The full KEEP/ADD set is ✅ and the DROP set is
confirmed absent. `/new-alarm` is an accepted, documented gap (deferred-pending-arrangement-system)
and does **not** block the verdict.

The `sptc`→`spt` succession flip remains separately gated (NOT unblocked by this verdict alone):
it also requires **legacy owl retired** (KNOWN-HAZARDS / RELEASE-RUNBOOK) and rides the live-relay
int held for spt-core M11 counter-15. This audit clears only the parity precondition.

-| `/live` | KEEP | operative — `skills/live.md` (`:live` profile + Monitor relay) | ✅ parity |

+| `/live` | KEEP | operative — `skills/live.md` (base-manifest `[session.psyche_init]` + bare `api listen` + Monitor relay; Option A, no `:live` profile) | ✅ parity |


| profiles + strings + hints | ADD | wired (`:deep`, `:live`, `[strings]`, `[[hints]]`) | ✅ parity |

| profiles + strings + hints | ADD | wired (`:deep`, `:ccs`, `[strings]`, `[[hints]]`; live-capability is in base, not a profile) | ✅ parity |


# Parity audit — claude-spt vs the legacy owl surface

> The reconciliation record: the adapter's operative surface measured against the **LOCKED
> parity-trim** in `SCOPE.md` (KEEP / ADD / DROP / TRANSFORM). This is the **"proven parity"**
> gate the `sptc`→`spt` succession flip depends on (see `docs/RELEASE-RUNBOOK.md` +
> `docs/KNOWN-HAZARDS.md`): the name flip happens only once this audit shows no unresolved
> divergence AND legacy owl is retired. Audited 2026-06-15 against published spt v0.7.2.
>
> Public-surface-only binds (AGENTS.md): a capability missing from the published `spt` surface is
> a **finding**, never a reason to reach into spt-core source.

<!-- [doc->REQ-PARITY-AUDIT] -->

## Disposition matrix

| Surface | SCOPE bucket | Adapter today | Status |
|---|---|---|---|
| `/live` | KEEP | operative — `skills/live.md` (base-manifest `[session.psyche_init]` + bare `api listen` + Monitor relay; Option A, no `:live` profile) | ✅ parity |
| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |
| `/force-stop` | KEEP (topology-aware) | operative — `skills/force-stop.md` | ✅ parity |
| `/spt:setup` | ADD | operative — `skills/setup.md` | ✅ parity |
| `/spt:version` | ADD | operative — `skills/version.md` | ✅ parity |
| profiles + strings + hints | ADD | wired (`:deep`, `:live`, `[strings]`, `[[hints]]`) | ✅ parity |
| subnet skill (status/create/show-code/join) | ADD (LOCKED) | operative — `skills/subnet.md` (wraps `spt subnet`) | ✅ parity (REQ-SKILL-SUBNET) |
| ccs profile (`claude-spt:ccs`) | ADD (LOCKED) | operative — `[profiles.ccs]` overlay + CLAUDE_CONFIG_DIR-aware extractor | ✅ parity (REQ-CCS-PROFILES) |
| `/whoami` skill | **DROP** (core `spt whoami` stays → M12 `endpoint list` alias) | removed (was carry-over) | ✅ dropped |
| `/new-alarm` | accepted gap | dead stub, no manifest entry | ⏸ DEFER + remove stub |
| `/fork` | DROP | absent | ✅ confirmed absent |
| `/amend-signoff` | DROP | absent | ✅ confirmed absent |
| `/revive` | DROP (daemon owns liveness) | absent | ✅ confirmed absent |
| `/clear-psyche` | DROP (= delete+recreate endpoint) | absent | ✅ confirmed absent |

TRANSFORM bucket (already realised, not skills): capsule→spt-hosted+`cc` (`[session.self]` +
`shortcut_basename="cc"`); echo-commune→`[digest]` extractor (`claude-spt-digest`); psyche-sync→
`/spt:setup`; doctor→setup-verify; working-perches→`api worker-start/stop` hooks. ✅

## Open divergences and dispositions

### subnet skills — LOCKED-ADD gap → BUILD
The published surface carries the verbs (`spt subnet {create,join,show-code,status,…}`), and v1
topology scope is **mandatory** (operator vetoed harness-hosted-only — cross-subnet/PTY proof is
spt-core's central value prop, SCOPE). So this is a real gap to fill, not defer. → `REQ-SKILL-SUBNET`.

### ccs profile — LOCKED-ADD gap → BUILT ✅
<!-- [doc->REQ-CCS-PROFILES] -->
SCOPE §ccs (LOCKED): ccs ships as a **profile template** under the adapter that retargets the spawn
command template through `ccs`, with ccs's relocated log dir honored. Templates only — the user
supplies their own ccs config/keys. **Operator ruling (2026-06-15):** the shipped profile is
`claude-spt:ccs` invoking **bare `ccs`** (the account set via `ccs auth default <name>`), NOT
`glm`/`kimi` — those were SCOPE *examples*; this profile IS the worked example of adapter-profile
authoring **and** the operator's own SPT-ecosystem hook. Built + validated against the known-good
sister project **claude_skill_owl**:
- **Spawn seam.** `[profiles.ccs.session.self].command = "ccs"` leaf-replaces the base `claude`.
  owl proves ccs is a drop-in for the `claude` binary on the same argv (`live/wrapper/claude.rs`
  Tier-2 PULSE recovery latches `cli_binary = "ccs"`); `SPT_ENDPOINT_ID` rides inherited env through
  the wrapper unchanged.
- **Log-dir seam → in the extractor, not a manifest leaf.** ccs relocates CC's whole state tree
  (incl. `projects/`) via the **`CLAUDE_CONFIG_DIR`** env var (`~/.ccs/instances/<account>/.claude`),
  a per-account runtime value with no static catalog path. SCOPE's "per-profile `~/.ccs` log dir"
  is therefore honored **in `claude-spt-digest`** (dir-locate branch prefers `$CLAUDE_CONFIG_DIR/
  projects` over the `--in` root) — the owl-validated `owlery::claude_projects_root` pattern. The
  base `[digest]` config thus serves base **and** ccs sessions transparently; no `[profiles.ccs.
  digest]` leaf. This env-aware resolver is REQ-CCS-PROFILES's `impl`/`unit` evidence (a `.toml`
  profile leaf alone does not register as `impl`).
- **Finding (refines SCOPE's model).** SCOPE conflated ccs *profiles* (glm/kimi = API-provider
  env-swap, NO transcript relocation) with ccs *accounts* (isolated instances that DO relocate via
  `CLAUDE_CONFIG_DIR`). The operator runs the account/default path, which relocates — so the digest
  seam is real, but its mechanism is `CLAUDE_CONFIG_DIR`, not a fixed `~/.ccs` path. → `REQ-CCS-PROFILES`.

### `/whoami` skill — DROP (honoring LOCKED)
SCOPE DROPs the whoami **skill**; core `spt whoami` stays in the hot path (and becomes the
`endpoint list` alias in M12). The adapter currently still ships a whoami skill (a carry-over from
an early slice). Operator confirmed the drop 2026-06-15. Action: remove `[strings.skills].whoami` +
the file body + the plugin stub; redirect the "who am i" hint to core `spt whoami`. No REQ rides
whoami evidence (the file-backed-string + UPS-injection int examples repoint to `skills.ready`;
the hook-parser unit examples repoint to a surviving skill name). Core `spt whoami` calls in the
hooks (`session-start.sh`, `_common.sh`) are the CLI, not the skill — they STAY.

### `/new-alarm` — ACCEPTED GAP (deferred-pending-arrangement-system)
**Not missing — deliberately deferred.** doyle triage (2026-06-15): spt-core carries only the
alarm *event shape* (spt-proto) + relay handling (psyrelay.rs) + a test fixture; the durable
"fire-at-target-time" timer still lives in legacy owl in-memory, and an in-daemon durable scheduler
is a deliberate deferral (DEFERRED.md:43, ADR-0018 Q4/V3 — deferred because the daemon had no
one-shot consumer; building pre-consumer = untested dead code). The **operator has a superseding
"arrangement" system** in mind that would replace the alarm port. Disposition: **do not build an
adapter-side stopgap timer** (throwaway vs the coming model); **no REQ minted**; **remove the dead
`/sptc:new-alarm` stub** from the published surface (deferred ≠ shipping a no-op skill). doyle loops
me when the arrangement design surfaces.

## Parity verdict
**PROVEN (adapter surface).** All three open divergences are closed this slice: subnet skill added
(`skills/subnet.md`, REQ-SKILL-SUBNET), ccs profile added (`[profiles.ccs]` + CLAUDE_CONFIG_DIR-aware
extractor, REQ-CCS-PROFILES), whoami skill dropped. The full KEEP/ADD set is ✅ and the DROP set is
confirmed absent. `/new-alarm` is an accepted, documented gap (deferred-pending-arrangement-system)
and does **not** block the verdict.

The `sptc`→`spt` succession flip remains separately gated (NOT unblocked by this verdict alone):
it also requires **legacy owl retired** (KNOWN-HAZARDS / RELEASE-RUNBOOK) and rides the live-relay
int held for spt-core M11 counter-15. This audit clears only the parity precondition.

-| profiles + strings + hints | ADD | wired (`:deep`, `:live`, `[strings]`, `[[hints]]`) | ✅ parity |

+| profiles + strings + hints | ADD | wired (`:deep`, `:ccs`, `[strings]`, `[[hints]]`; live-capability is in base, not a profile) | ✅ parity |


Now the int tests. Read registration-int.sh to update the `:live` profile assertions (profile no longer exists).


1	#!/bin/sh
2	# Integration proof for the claude-spt adapter manifest against a REAL spt-core (>= v0.7.0): the
3	# 2nd validation layer beyond JSON Schema — spt-core's `spt adapter add` cross-field registration —
4	# accepts the manifest, the shipped profile resolves, [strings] read through the registry, and the
5	# profile overlay is observable (base vs :deep differ). This is the v1 acceptance proof for the
6	# adapter manifest (LLM never in the loop; pure CLI assertions). [int->REQ-DIST-MANIFEST-SCHEMA]
7	#
8	# Mutates the node-local adapter registry (add + soft-remove), so it is gated behind SPTC_ACCEPTANCE=1
9	# and a present spt>=0.7.0 — never runs in the default `tests/*.sh` unit sweep. Idempotent: removes
10	# what it adds. Run: SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh   (exit 0 = pass).
11	set -u
12	ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
13	MANIFEST="$ROOT/adapter/claude-spt.toml"
14
15	if [ "${SPTC_ACCEPTANCE:-0}" != "1" ]; then echo "SKIP: set SPTC_ACCEPTANCE=1 to run (mutates the adapter registry)"; exit 0; fi
16	command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }
17	ver=$(spt --version 2>/dev/null | awk '{print $NF}')
18	case "$ver" in
19	  0.6.*|0.5.*|0.4.*|0.3.*|0.2.*|0.1.*|0.0.*) echo "SKIP: spt $ver < 0.7.0 (shortcut_basename + adapter add are v0.7.0)"; exit 0 ;;
20	esac
21
22	fail=0
23	ok()  { echo "ok   $1"; }
24	bad() { echo "FAIL $1"; fail=1; }
25	# Clean up registry residue however we exit.
26	trap 'spt adapter remove claude-spt >/dev/null 2>&1 || true' EXIT INT TERM
27
28	# 1. Registration cross-field validation accepts the manifest.
29	out=$(spt adapter add "$MANIFEST" 2>&1)
30	case "$out" in *registered*) ok "spt adapter add: registered" ;; *) bad "adapter add rejected: $out" ;; esac
31
32	# 2. Listed, with the shipped profile resolved as a composite option.
33	list=$(spt adapter list 2>&1)
34	case "$list" in *claude-spt*) ok "listed: claude-spt active" ;; *) bad "claude-spt not listed" ;; esac
35	case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
36	# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay
37	# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).
38	case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac
39	# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —
40	# the LOCKED-ADD ccs profile template (REQ-CCS-PROFILES; validated vs sister project claude_skill_owl).
41	case "$list" in *claude-spt:ccs*) ok "shipped profile resolves: claude-spt:ccs (ccs overlay)" ;; *) bad "ccs profile not resolved" ;; esac
42
43	# 3. [strings] read through the registry (base value).
44	base=$(spt adapter get-string claude-spt adapter_label 2>&1)
45	[ "$base" = "Claude Code (spt)" ] && ok "strings resolve: adapter_label (base)" || bad "base adapter_label='$base'"
46
47	# 4. Profile overlay is observable: :deep leaf-replaces the base string.
48	deep=$(spt adapter get-string claude-spt:deep adapter_label 2>&1)
49	[ "$deep" = "Claude Code (spt, deep)" ] && ok "overlay observable: :deep adapter_label differs" || bad "deep adapter_label='$deep'"
50	# :ccs overlay observable the same way (leaf-replaced adapter_label). [int->REQ-CCS-PROFILES]
51	ccs=$(spt adapter get-string claude-spt:ccs adapter_label 2>&1)
52	[ "$ccs" = "Claude Code (spt, ccs)" ] && ok "overlay observable: :ccs adapter_label differs" || bad "ccs adapter_label='$ccs'"
53
54	# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
55	#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
56	body=$(spt adapter get-string claude-spt skills.ready 2>&1)
57	case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
58	# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
59	cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
60	case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
61	fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
62	case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
63	# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
64	# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).
65	lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
66	case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
67	# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
68	sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
69	case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac
70
71	# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped
72	#     operative body via get-string (REQ-UPS-INJECTION impl, on the registered adapter).
73	( . "$ROOT/plugin/sptc/hooks/_common.sh"
74	  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")
75	  case "$inj" in
76	    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;
77	    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
78	  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"
79
80	# 4d. The spt-hosted bringup blocks ([session.self] + [env.SPT_ENDPOINT_ID]) cross-field-validate:
81	#     `adapter add` (step 1) is manifest-first ("an invalid manifest registers nothing"), so the
82	#     manifest carrying these blocks registering at all proves spt-core accepted their shape. Confirm
83	#     the registered adapter advertises a hostable harness — the thing `spt endpoint run` spawns via
84	#     [session.self] (the M12 cc-launcher target). [int->REQ-DIST-MANIFEST-SCHEMA]
85	cap=$(spt api --adapter claude-spt --manifest "$MANIFEST" capability 2>&1)
86	case "$cap" in *LiveAgent*) ok "bringup blocks accepted; capability reports hostable harness" ;; *) bad "capability missing LiveAgent: $cap" ;; esac
87
88	# 5. Soft-deregister cleanly.
89	rm=$(spt adapter remove claude-spt 2>&1)
90	list2=$(spt adapter list 2>&1)
91	case "$list2" in *"claude-spt"*"active"*) bad "claude-spt still active after remove" ;; *) ok "removed (soft-deregistered)" ;; esac
92
93	[ "$fail" -eq 0 ] && { echo "MANIFEST-REGISTRATION-INT OK"; exit 0; } || { echo "MANIFEST-REGISTRATION-INT FAIL"; exit 1; }
94

#!/bin/sh
# Integration proof for the claude-spt adapter manifest against a REAL spt-core (>= v0.7.0): the
# 2nd validation layer beyond JSON Schema — spt-core's `spt adapter add` cross-field registration —
# accepts the manifest, the shipped profile resolves, [strings] read through the registry, and the
# profile overlay is observable (base vs :deep differ). This is the v1 acceptance proof for the
# adapter manifest (LLM never in the loop; pure CLI assertions). [int->REQ-DIST-MANIFEST-SCHEMA]
#
# Mutates the node-local adapter registry (add + soft-remove), so it is gated behind SPTC_ACCEPTANCE=1
# and a present spt>=0.7.0 — never runs in the default `tests/*.sh` unit sweep. Idempotent: removes
# what it adds. Run: SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh   (exit 0 = pass).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
MANIFEST="$ROOT/adapter/claude-spt.toml"

if [ "${SPTC_ACCEPTANCE:-0}" != "1" ]; then echo "SKIP: set SPTC_ACCEPTANCE=1 to run (mutates the adapter registry)"; exit 0; fi
command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }
ver=$(spt --version 2>/dev/null | awk '{print $NF}')
case "$ver" in
  0.6.*|0.5.*|0.4.*|0.3.*|0.2.*|0.1.*|0.0.*) echo "SKIP: spt $ver < 0.7.0 (shortcut_basename + adapter add are v0.7.0)"; exit 0 ;;
esac

fail=0
ok()  { echo "ok   $1"; }
bad() { echo "FAIL $1"; fail=1; }
# Clean up registry residue however we exit.
trap 'spt adapter remove claude-spt >/dev/null 2>&1 || true' EXIT INT TERM

# 1. Registration cross-field validation accepts the manifest.
out=$(spt adapter add "$MANIFEST" 2>&1)
case "$out" in *registered*) ok "spt adapter add: registered" ;; *) bad "adapter add rejected: $out" ;; esac

# 2. Listed, with the shipped profile resolved as a composite option.
list=$(spt adapter list 2>&1)
case "$list" in *claude-spt*) ok "listed: claude-spt active" ;; *) bad "claude-spt not listed" ;; esac
case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay
# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).
case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —
# the LOCKED-ADD ccs profile template (REQ-CCS-PROFILES; validated vs sister project claude_skill_owl).
case "$list" in *claude-spt:ccs*) ok "shipped profile resolves: claude-spt:ccs (ccs overlay)" ;; *) bad "ccs profile not resolved" ;; esac

# 3. [strings] read through the registry (base value).
base=$(spt adapter get-string claude-spt adapter_label 2>&1)
[ "$base" = "Claude Code (spt)" ] && ok "strings resolve: adapter_label (base)" || bad "base adapter_label='$base'"

# 4. Profile overlay is observable: :deep leaf-replaces the base string.
deep=$(spt adapter get-string claude-spt:deep adapter_label 2>&1)
[ "$deep" = "Claude Code (spt, deep)" ] && ok "overlay observable: :deep adapter_label differs" || bad "deep adapter_label='$deep'"
# :ccs overlay observable the same way (leaf-replaced adapter_label). [int->REQ-CCS-PROFILES]
ccs=$(spt adapter get-string claude-spt:ccs adapter_label 2>&1)
[ "$ccs" = "Claude Code (spt, ccs)" ] && ok "overlay observable: :ccs adapter_label differs" || bad "ccs adapter_label='$ccs'"

# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac

# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped
#     operative body via get-string (REQ-UPS-INJECTION impl, on the registered adapter).
( . "$ROOT/plugin/sptc/hooks/_common.sh"
  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")
  case "$inj" in
    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;
    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"

# 4d. The spt-hosted bringup blocks ([session.self] + [env.SPT_ENDPOINT_ID]) cross-field-validate:
#     `adapter add` (step 1) is manifest-first ("an invalid manifest registers nothing"), so the
#     manifest carrying these blocks registering at all proves spt-core accepted their shape. Confirm
#     the registered adapter advertises a hostable harness — the thing `spt endpoint run` spawns via
#     [session.self] (the M12 cc-launcher target). [int->REQ-DIST-MANIFEST-SCHEMA]
cap=$(spt api --adapter claude-spt --manifest "$MANIFEST" capability 2>&1)
case "$cap" in *LiveAgent*) ok "bringup blocks accepted; capability reports hostable harness" ;; *) bad "capability missing LiveAgent: $cap" ;; esac

# 5. Soft-deregister cleanly.
rm=$(spt adapter remove claude-spt 2>&1)
list2=$(spt adapter list 2>&1)
case "$list2" in *"claude-spt"*"active"*) bad "claude-spt still active after remove" ;; *) ok "removed (soft-deregistered)" ;; esac

[ "$fail" -eq 0 ] && { echo "MANIFEST-REGISTRATION-INT OK"; exit 0; } || { echo "MANIFEST-REGISTRATION-INT FAIL"; exit 1; }


`claude-spt:live` profile assertion (lines 36-38) must go — profile deleted; base live-capability is proven by the `capability` check at line 86. Edit:


case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay
# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).
case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —

case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# NOTE: there is no `:live` profile (Option A, PREP-4) — base claude-spt is live-capable
# ([session.psyche_init] in base); the LiveAgent capability is asserted via `spt api capability` at
# step 4d below, not a composite resolve.
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —


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

case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# NOTE: there is no `:live` profile (Option A, PREP-4) — base claude-spt is live-capable
# ([session.psyche_init] in base); the LiveAgent capability is asserted via `spt api capability` at
# step 4d below, not a composite resolve.
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —

#!/bin/sh
# Integration proof for the claude-spt adapter manifest against a REAL spt-core (>= v0.7.0): the
# 2nd validation layer beyond JSON Schema — spt-core's `spt adapter add` cross-field registration —
# accepts the manifest, the shipped profile resolves, [strings] read through the registry, and the
# profile overlay is observable (base vs :deep differ). This is the v1 acceptance proof for the
# adapter manifest (LLM never in the loop; pure CLI assertions). [int->REQ-DIST-MANIFEST-SCHEMA]
#
# Mutates the node-local adapter registry (add + soft-remove), so it is gated behind SPTC_ACCEPTANCE=1
# and a present spt>=0.7.0 — never runs in the default `tests/*.sh` unit sweep. Idempotent: removes
# what it adds. Run: SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh   (exit 0 = pass).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
MANIFEST="$ROOT/adapter/claude-spt.toml"

if [ "${SPTC_ACCEPTANCE:-0}" != "1" ]; then echo "SKIP: set SPTC_ACCEPTANCE=1 to run (mutates the adapter registry)"; exit 0; fi
command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }
ver=$(spt --version 2>/dev/null | awk '{print $NF}')
case "$ver" in
  0.6.*|0.5.*|0.4.*|0.3.*|0.2.*|0.1.*|0.0.*) echo "SKIP: spt $ver < 0.7.0 (shortcut_basename + adapter add are v0.7.0)"; exit 0 ;;
esac

fail=0
ok()  { echo "ok   $1"; }
bad() { echo "FAIL $1"; fail=1; }
# Clean up registry residue however we exit.
trap 'spt adapter remove claude-spt >/dev/null 2>&1 || true' EXIT INT TERM

# 1. Registration cross-field validation accepts the manifest.
out=$(spt adapter add "$MANIFEST" 2>&1)
case "$out" in *registered*) ok "spt adapter add: registered" ;; *) bad "adapter add rejected: $out" ;; esac

# 2. Listed, with the shipped profile resolved as a composite option.
list=$(spt adapter list 2>&1)
case "$list" in *claude-spt*) ok "listed: claude-spt active" ;; *) bad "claude-spt not listed" ;; esac
case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay
# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).
case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —
# the LOCKED-ADD ccs profile template (REQ-CCS-PROFILES; validated vs sister project claude_skill_owl).
case "$list" in *claude-spt:ccs*) ok "shipped profile resolves: claude-spt:ccs (ccs overlay)" ;; *) bad "ccs profile not resolved" ;; esac

# 3. [strings] read through the registry (base value).
base=$(spt adapter get-string claude-spt adapter_label 2>&1)
[ "$base" = "Claude Code (spt)" ] && ok "strings resolve: adapter_label (base)" || bad "base adapter_label='$base'"

# 4. Profile overlay is observable: :deep leaf-replaces the base string.
deep=$(spt adapter get-string claude-spt:deep adapter_label 2>&1)
[ "$deep" = "Claude Code (spt, deep)" ] && ok "overlay observable: :deep adapter_label differs" || bad "deep adapter_label='$deep'"
# :ccs overlay observable the same way (leaf-replaced adapter_label). [int->REQ-CCS-PROFILES]
ccs=$(spt adapter get-string claude-spt:ccs adapter_label 2>&1)
[ "$ccs" = "Claude Code (spt, ccs)" ] && ok "overlay observable: :ccs adapter_label differs" || bad "ccs adapter_label='$ccs'"

# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac

# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped
#     operative body via get-string (REQ-UPS-INJECTION impl, on the registered adapter).
( . "$ROOT/plugin/sptc/hooks/_common.sh"
  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")
  case "$inj" in
    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;
    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"

# 4d. The spt-hosted bringup blocks ([session.self] + [env.SPT_ENDPOINT_ID]) cross-field-validate:
#     `adapter add` (step 1) is manifest-first ("an invalid manifest registers nothing"), so the
#     manifest carrying these blocks registering at all proves spt-core accepted their shape. Confirm
#     the registered adapter advertises a hostable harness — the thing `spt endpoint run` spawns via
#     [session.self] (the M12 cc-launcher target). [int->REQ-DIST-MANIFEST-SCHEMA]
cap=$(spt api --adapter claude-spt --manifest "$MANIFEST" capability 2>&1)
case "$cap" in *LiveAgent*) ok "bringup blocks accepted; capability reports hostable harness" ;; *) bad "capability missing LiveAgent: $cap" ;; esac

# 5. Soft-deregister cleanly.
rm=$(spt adapter remove claude-spt 2>&1)
list2=$(spt adapter list 2>&1)
case "$list2" in *"claude-spt"*"active"*) bad "claude-spt still active after remove" ;; *) ok "removed (soft-deregistered)" ;; esac

[ "$fail" -eq 0 ] && { echo "MANIFEST-REGISTRATION-INT OK"; exit 0; } || { echo "MANIFEST-REGISTRATION-INT FAIL"; exit 1; }

-# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay

-# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).

-case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac

+# NOTE: there is no `:live` profile (Option A, PREP-4) — base claude-spt is live-capable

+# ([session.psyche_init] in base); the LiveAgent capability is asserted via `spt api capability` at

+# step 4d below, not a composite resolve.


# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).

# `live` is file-backed too (the LiveAgent bringup body — base [session.psyche_init] + bare
# `api listen`; REQ-SKILL-LIVE).


# `live` is file-backed too (the LiveAgent bringup body — base [session.psyche_init] + bare
# `api listen`; REQ-SKILL-LIVE).

#!/bin/sh
# Integration proof for the claude-spt adapter manifest against a REAL spt-core (>= v0.7.0): the
# 2nd validation layer beyond JSON Schema — spt-core's `spt adapter add` cross-field registration —
# accepts the manifest, the shipped profile resolves, [strings] read through the registry, and the
# profile overlay is observable (base vs :deep differ). This is the v1 acceptance proof for the
# adapter manifest (LLM never in the loop; pure CLI assertions). [int->REQ-DIST-MANIFEST-SCHEMA]
#
# Mutates the node-local adapter registry (add + soft-remove), so it is gated behind SPTC_ACCEPTANCE=1
# and a present spt>=0.7.0 — never runs in the default `tests/*.sh` unit sweep. Idempotent: removes
# what it adds. Run: SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh   (exit 0 = pass).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
MANIFEST="$ROOT/adapter/claude-spt.toml"

if [ "${SPTC_ACCEPTANCE:-0}" != "1" ]; then echo "SKIP: set SPTC_ACCEPTANCE=1 to run (mutates the adapter registry)"; exit 0; fi
command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }
ver=$(spt --version 2>/dev/null | awk '{print $NF}')
case "$ver" in
  0.6.*|0.5.*|0.4.*|0.3.*|0.2.*|0.1.*|0.0.*) echo "SKIP: spt $ver < 0.7.0 (shortcut_basename + adapter add are v0.7.0)"; exit 0 ;;
esac

fail=0
ok()  { echo "ok   $1"; }
bad() { echo "FAIL $1"; fail=1; }
# Clean up registry residue however we exit.
trap 'spt adapter remove claude-spt >/dev/null 2>&1 || true' EXIT INT TERM

# 1. Registration cross-field validation accepts the manifest.
out=$(spt adapter add "$MANIFEST" 2>&1)
case "$out" in *registered*) ok "spt adapter add: registered" ;; *) bad "adapter add rejected: $out" ;; esac

# 2. Listed, with the shipped profile resolved as a composite option.
list=$(spt adapter list 2>&1)
case "$list" in *claude-spt*) ok "listed: claude-spt active" ;; *) bad "claude-spt not listed" ;; esac
case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# NOTE: there is no `:live` profile (Option A, PREP-4) — base claude-spt is live-capable
# ([session.psyche_init] in base); the LiveAgent capability is asserted via `spt api capability` at
# step 4d below, not a composite resolve.
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —
# the LOCKED-ADD ccs profile template (REQ-CCS-PROFILES; validated vs sister project claude_skill_owl).
case "$list" in *claude-spt:ccs*) ok "shipped profile resolves: claude-spt:ccs (ccs overlay)" ;; *) bad "ccs profile not resolved" ;; esac

# 3. [strings] read through the registry (base value).
base=$(spt adapter get-string claude-spt adapter_label 2>&1)
[ "$base" = "Claude Code (spt)" ] && ok "strings resolve: adapter_label (base)" || bad "base adapter_label='$base'"

# 4. Profile overlay is observable: :deep leaf-replaces the base string.
deep=$(spt adapter get-string claude-spt:deep adapter_label 2>&1)
[ "$deep" = "Claude Code (spt, deep)" ] && ok "overlay observable: :deep adapter_label differs" || bad "deep adapter_label='$deep'"
# :ccs overlay observable the same way (leaf-replaced adapter_label). [int->REQ-CCS-PROFILES]
ccs=$(spt adapter get-string claude-spt:ccs adapter_label 2>&1)
[ "$ccs" = "Claude Code (spt, ccs)" ] && ok "overlay observable: :ccs adapter_label differs" || bad "ccs adapter_label='$ccs'"

# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac

# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped
#     operative body via get-string (REQ-UPS-INJECTION impl, on the registered adapter).
( . "$ROOT/plugin/sptc/hooks/_common.sh"
  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")
  case "$inj" in
    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;
    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"

# 4d. The spt-hosted bringup blocks ([session.self] + [env.SPT_ENDPOINT_ID]) cross-field-validate:
#     `adapter add` (step 1) is manifest-first ("an invalid manifest registers nothing"), so the
#     manifest carrying these blocks registering at all proves spt-core accepted their shape. Confirm
#     the registered adapter advertises a hostable harness — the thing `spt endpoint run` spawns via
#     [session.self] (the M12 cc-launcher target). [int->REQ-DIST-MANIFEST-SCHEMA]
cap=$(spt api --adapter claude-spt --manifest "$MANIFEST" capability 2>&1)
case "$cap" in *LiveAgent*) ok "bringup blocks accepted; capability reports hostable harness" ;; *) bad "capability missing LiveAgent: $cap" ;; esac

# 5. Soft-deregister cleanly.
rm=$(spt adapter remove claude-spt 2>&1)
list2=$(spt adapter list 2>&1)
case "$list2" in *"claude-spt"*"active"*) bad "claude-spt still active after remove" ;; *) ok "removed (soft-deregistered)" ;; esac

[ "$fail" -eq 0 ] && { echo "MANIFEST-REGISTRATION-INT OK"; exit 0; } || { echo "MANIFEST-REGISTRATION-INT FAIL"; exit 1; }

-# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the

-# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).

+# `live` is file-backed too (the LiveAgent bringup body — base [session.psyche_init] + bare


The big int (live-relay-int.sh): CI has no real `claude` parent, so by-pid host_binaries resolution can't fire there — it keeps the explicit `--adapter` override (valid on 0.9.0), but now points at **base** `claude-spt` (psyche_init in base = the Option-A assertion). Real bare-by-pid is the live VERIFY step. Edit the composite + comments.


# Option A (PREP-4): [session.psyche_init] is in the BASE manifest — NO `:live` profile. The adapter
# is BASE claude-spt; the live `api listen` COMMAND (not a composite) is what stamps state=live_agent
# and actualizes the Psyche. We still pass an explicit `--adapter claude-spt --manifest <man>` here:
# CI has no real `claude` parent process, so spt-core's bare-by-pid host_binaries resolution cannot
# fire (the anchor is `sh`/`timeout`, not `claude`). The explicit override remains valid on 0.9.0 and
# loads the same base manifest the bare flow would resolve. The TRUE bare-flow (no --adapter, by-pid
# resolution under a real claude host) is covered by the live /sptc:live VERIFY, not this int.
RUN=$$
ID=sptc-ci-liverelay-$RUN
SID=sptc-ci-liverelay-$RUN-sess


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

# Option A (PREP-4): [session.psyche_init] is in the BASE manifest — NO `:live` profile. The adapter
# is BASE claude-spt; the live `api listen` COMMAND (not a composite) is what stamps state=live_agent
# and actualizes the Psyche. We still pass an explicit `--adapter claude-spt --manifest <man>` here:
# CI has no real `claude` parent process, so spt-core's bare-by-pid host_binaries resolution cannot
# fire (the anchor is `sh`/`timeout`, not `claude`). The explicit override remains valid on 0.9.0 and
# loads the same base manifest the bare flow would resolve. The TRUE bare-flow (no --adapter, by-pid
# resolution under a real claude host) is covered by the live /sptc:live VERIFY, not this int.
RUN=$$
ID=sptc-ci-liverelay-$RUN
SID=sptc-ci-liverelay-$RUN-sess

# env vars; pin OWL_SESSION_ID for the auth-gated seed/send/listen surfaces. PER-RUN UNIQUE ($$ suffix):

# the daemon hosts a Psyche at most ONCE per session_id, so a FIXED id/session would not re-host on a

# rerun (the first run's hosted-session memory suppresses it) — each CI run must be a fresh session.

+# Option A (PREP-4): [session.psyche_init] is in the BASE manifest — NO `:live` profile. The adapter

+# is BASE claude-spt; the live `api listen` COMMAND (not a composite) is what stamps state=live_agent

+# and actualizes the Psyche. We still pass an explicit `--adapter claude-spt --manifest <man>` here:

+# CI has no real `claude` parent process, so spt-core's bare-by-pid host_binaries resolution cannot

+# fire (the anchor is `sh`/`timeout`, not `claude`). The explicit override remains valid on 0.9.0 and

+# loads the same base manifest the bare flow would resolve. The TRUE bare-flow (no --adapter, by-pid

+# resolution under a real claude host) is covered by the live /sptc:live VERIFY, not this int.


# Run: SPTC_ACCEPTANCE=1 sh ci/psyche/live-relay-int.sh   (exit 0 = pass).

ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)

# Disposable perch id — NEVER a live agent's id (REQ-HAZARD-PERCH-COLLISION). Override BOTH identity


Update the psyche runner doc-comment (base now declares psyche_init).


1	//! claude-spt-psyche — the `[session.psyche_init]` Psyche runner for the claude-spt LiveAgent.
2	//!
3	//! WHAT A PSYCHE IS (doyle 2026-06-15, traced spt-core): a LiveAgent's detached companion. The
4	//! spt-core daemon's BrainLifecycle spawns it ONCE per live session — only when the resolved
5	//! manifest declares `[session.psyche_init]` (the `claude-spt:live` overlay; base claude-spt has
6	//! none = ReadyAgent). The Psyche owns its OWN perch (`<parent>-psyche`), receives daemon PULSES
7	//! on that perch, and on each pulse authors a COMMUNE delta (a context brief for the parent's
8	//! resume across `/clear`/compact) — it NEVER replies/notifies (the echo-commune is a distinct,
9	//! cheaper actor). It exits at session end. The Psyche is daemon-MANAGED by contract (decoupled
10	//! from message delivery); this runner does NOT orchestrate its own lifecycle — it is the dumb
11	//! resident wrapper the daemon launches and the daemon tears down (perch close = our exit signal).
12	//!

//! claude-spt-psyche — the `[session.psyche_init]` Psyche runner for the claude-spt LiveAgent.
//!
//! WHAT A PSYCHE IS (doyle 2026-06-15, traced spt-core): a LiveAgent's detached companion. The
//! spt-core daemon's BrainLifecycle spawns it ONCE per live session — only when the resolved
//! manifest declares `[session.psyche_init]` (the `claude-spt:live` overlay; base claude-spt has
//! none = ReadyAgent). The Psyche owns its OWN perch (`<parent>-psyche`), receives daemon PULSES
//! on that perch, and on each pulse authors a COMMUNE delta (a context brief for the parent's
//! resume across `/clear`/compact) — it NEVER replies/notifies (the echo-commune is a distinct,
//! cheaper actor). It exits at session end. The Psyche is daemon-MANAGED by contract (decoupled
//! from message delivery); this runner does NOT orchestrate its own lifecycle — it is the dumb
//! resident wrapper the daemon launches and the daemon tears down (perch close = our exit signal).
//!


//! spt-core daemon's BrainLifecycle spawns it ONCE per live session — only when the resolved
//! manifest declares `[session.psyche_init]` (the `claude-spt:live` overlay; base claude-spt has
//! none = ReadyAgent). The Psyche owns its OWN perch (`<parent>-psyche`), receives daemon PULSES

//! spt-core daemon's livehost hosts it — only when the resolved manifest declares
//! `[session.psyche_init]` AND the perch came up as state=live_agent (the live `api listen` path;
//! Option A 2026-06-17 — psyche_init lives in the BASE claude-spt manifest, no `:live` profile; a
//! ready/poll perch is skipped regardless, livehost.rs:282). The Psyche owns its OWN perch
//! (`<parent>-psyche`), receives daemon PULSES


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

//! spt-core daemon's BrainLifecycle spawns it ONCE per live session — only when the resolved
//! manifest declares `[session.psyche_init]` (the `claude-spt:live` overlay; base claude-spt has
//! none = ReadyAgent). The Psyche owns its OWN perch (`<parent>-psyche`), receives daemon PULSES

//! spt-core daemon's livehost hosts it — only when the resolved manifest declares
//! `[session.psyche_init]` AND the perch came up as state=live_agent (the live `api listen` path;
//! Option A 2026-06-17 — psyche_init lives in the BASE claude-spt manifest, no `:live` profile; a
//! ready/poll perch is skipped regardless, livehost.rs:282). The Psyche owns its OWN perch
//! (`<parent>-psyche`), receives daemon PULSES

//! claude-spt-psyche — the `[session.psyche_init]` Psyche runner for the claude-spt LiveAgent.

//! WHAT A PSYCHE IS (doyle 2026-06-15, traced spt-core): a LiveAgent's detached companion. The

-//! spt-core daemon's BrainLifecycle spawns it ONCE per live session — only when the resolved

-//! manifest declares `[session.psyche_init]` (the `claude-spt:live` overlay; base claude-spt has

-//! none = ReadyAgent). The Psyche owns its OWN perch (`<parent>-psyche`), receives daemon PULSES

+//! spt-core daemon's livehost hosts it — only when the resolved manifest declares

+//! `[session.psyche_init]` AND the perch came up as state=live_agent (the live `api listen` path;

+//! Option A 2026-06-17 — psyche_init lives in the BASE claude-spt manifest, no `:live` profile; a

+//! ready/poll perch is skipped regardless, livehost.rs:282). The Psyche owns its OWN perch

+//! (`<parent>-psyche`), receives daemon PULSES

//! on that perch, and on each pulse authors a COMMUNE delta (a context brief for the parent's

//! resume across `/clear`/compact) — it NEVER replies/notifies (the echo-commune is a distinct,

//! cheaper actor). It exits at session end. The Psyche is daemon-MANAGED by contract (decoupled


Core functional + gated surface done. Run the deterministic gates to catch breakage before the live verify.


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && sh ci/run-gates.sh 2>&1 | tail -35; echo "GATES_EXIT=${PIPESTATUS[0]}"

Run full deterministic gate suite


ok  tests/manifest-shortcut.sh
ok  tests/msys-hazard.sh
ok  tests/skeleton-validate.sh

=== GATE: digest-extractor (cargo build + test) ===

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

ok  claude-spt-digest: cargo test + release build

=== GATE: psyche-runner (cargo build + test) ===

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

ok  claude-spt-psyche: cargo test + release build

=== GATE: traceable-reqs check (requirement coverage) ===
ok  coverage green

=== GATE: skeleton-validate (cplugs installability) ===
ok  skeleton installable

=== GATE: manifest-schema ===
ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json

=== GATE: docs-drift ===
ok   mdbook build
ok   llms.txt in sync

=== RESULT: PASS ===
GATES_EXIT=0


All deterministic gates PASS (traceable green — base psyche_init satisfies REQ-SKILL-LIVE impl; docs in sync). Commit the wiring before the disruptive spt-update + live verify.


cd C:/Users/decid/Documents/projects/spt-claude-code && git add adapter/claude-spt.toml adapter/strings/skills/live.md adapter/strings/skills/setup.md plugin/sptc/hooks/session-start.sh plugin/sptc/skills/live/SKILL.md traceable-reqs.toml docs/PARITY.md ci/manifest/registration-int.sh ci/psyche/live-relay-int.sh tools/claude-spt-psyche/src/main.rs PREP4-PLAN.md && git status --short

Stage PREP-4 wiring changes


A  PREP4-PLAN.md
M  adapter/claude-spt.toml
M  adapter/strings/skills/live.md
M  adapter/strings/skills/setup.md
M  ci/manifest/registration-int.sh
M  ci/psyche/live-relay-int.sh
M  docs/PARITY.md
M  plugin/sptc/hooks/session-start.sh
M  plugin/sptc/skills/live/SKILL.md
M  tools/claude-spt-psyche/src/main.rs
M  traceable-reqs.toml


cd C:/Users/decid/Documents/projects/spt-claude-code && git commit -q -F - <<'EOF'
feat(live): PREP-4 Option A — base-manifest live-capability, bare flow, no :live profile

spt-core v0.9.0 shipped the adapter-agnostic harness-resolution feature set
(ADR-0021: bare seed, bind-time resolution by [adapter] host_binaries,
`spt adapter use`, optional --adapter). Activate the PREP-4 staging → full
legacy-parity bringup.

Design = Option A (doyle-ruled + code-confirmed, livehost.rs:282 gates psyche on
state==live_agent): our :live profile differed from base ONLY by
[session.psyche_init], so fold psyche_init INTO base claude-spt and DELETE the
:live profile. ready-vs-live is the COMMAND, not a profile:
- /sptc:live  -> bare `spt api listen <id>` -> base psyche_init + live_agent state
  -> daemon hosts the Psyche.
- /sptc:ready -> bare poll -> ready_agent state -> psyche skipped regardless.
Zero --adapter on the default path (host_binaries resolves claude-spt by the
seed's parent pid). :ccs / :deep remain as sanctioned override profiles (genuine
deltas) selected with explicit --adapter.

Wiring:
- adapter/claude-spt.toml: [session.psyche_init] moved to BASE; [profiles.live]
  deleted; min_spt_core_version 0.7.0 -> 0.9.0 (compat gate (a) — hard floor, no
  <0.9.0 users); SessionStart [hooks].fires drops --adapter; comments rewritten.
- plugin/sptc/hooks/session-start.sh: seed branch -> bare `spt api seed`
  (no --adapter). bind/boundary branches keep their explicit --adapter override
  (sanctioned, valid on 0.9.0 — doyle scope).
- adapter/strings/skills/live.md: bringup -> single persistent Monitor task running
  bare `spt api listen <id>` (no seed-chain, no --adapter, no pid anchor); interim
  PREP-4 note stripped; OUTPUT DISCIPLINE + canonical LIVE block kept.
- adapter/strings/skills/setup.md: add `spt adapter use claude-spt` (durable
  active-profile pointer); fix the :live spot-check -> :deep.
- plugin live SKILL.md stub, tools/claude-spt-psyche doc-comment: Option-A model.
- ci/manifest/registration-int.sh: drop the :live composite-resolve assertion
  (base live-capability proven via `api capability`); ci/psyche/live-relay-int.sh:
  A=claude-spt:live -> claude-spt (psyche hosts off BASE manifest; explicit
  --adapter override retained because CI has no real claude parent for by-pid
  resolution — the true bare flow is the live VERIFY).
- traceable-reqs REQ-SKILL-LIVE + docs/PARITY.md: Option-A model.

Deterministic gates PASS (traceable green, manifest validates, docs in sync).
Live VERIFY on spt 0.9.0 + v0.3.0 release follow.

Co-authored by: perri
EOF
echo "EXIT=$?" && git log --oneline -1

Commit PREP-4 wiring


EXIT=0
3665fc5 feat(live): PREP-4 Option A — base-manifest live-capability, bare flow, no :live profile


Wiring committed (3665fc5). Now VERIFY against live 0.9.0 — but first probe the update path (read-only) and assess infra-disruption risk before pulling the trigger.


cd C:/Users/decid/Documents/projects/spt-claude-code && echo "=== spt update --help ==="; spt update --help 2>&1 | head -25; echo "=== current adapters/endpoints ==="; spt adapter list 2>&1 | head -15

Probe spt update subcommand + current adapter/endpoint state


=== spt update --help ===
Self-update operations.

`apply` is the explicit ack named by the update-consent notification; it re-verifies the staged
release before touching the live daemon.

Usage: spt.exe update <COMMAND>

Commands:
  apply  Apply the staged, verified self-update now
  fetch  Fetch the latest signed release from the GitHub origin and stage it (then `spt update
         apply`). Bootstraps a node with no peer to pull from
  help   Print this message or the help of the given subcommand(s)

Options:
  -h, --help
          Print help (see a summary with '-h')
=== current adapters/endpoints ===
claude-spt: Harness Pointer active (from C:\Users\decid\AppData\Local\spt-core\adapters\_github\SaberMage-spt-claude-code)
  claude-spt:ccs (shipped)
  claude-spt:deep (shipped)
  claude-spt:live (shipped)
claude-spt-hbprobe: Harness Copy deregistered (from C:/Users/decid/AppData/Local/Temp/tmp.q4739pmxMf)
  claude-spt-hbprobe:ccs (shipped)
  claude-spt-hbprobe:deep (shipped)
  claude-spt-hbprobe:live (shipped)
sptc-probe-f003: Harness Copy deregistered (from C:/Users/decid/AppData/Local/Temp/sptc-probe-f003)


Key finding: the **registered** claude-spt is still v0.2.0 (no `host_binaries`, psyche_init in `:live`) — and the daemon hosts the psyche from the *registered* install dir, ignoring `--manifest`. So a faithful bare-flow verify needs: (1) spt 0.8.2→0.9.0 self-update + daemon restart, AND (2) re-registering claude-spt with the new base-psyche_init manifest — both churn the operator's live dogfood machine (daemon restart, install-dir contention, the documented 20-40 psyche-storm hazard). That's infra-disruptive on a machine the operator is actively using.

Responsible move: report wiring+gates done, flag the verify choreography, confirm timing with doyle before yanking the daemon under the operator.


cd C:/Users/decid/Documents/projects/spt-claude-code && "$OWL" send doyle perri <<'EOF'
perri — PREP-4 Option A WIRED + committed (3665fc5). Deterministic gates GREEN (traceable, manifest validates, docs in sync). Changed: manifest (psyche_init→base, [profiles.live] deleted, min_spt_core→0.9.0, SessionStart fires bare), session-start.sh seed→bare, live.md bare-listen, setup.md `spt adapter use claude-spt`, int tests + traceable + PARITY + psyche-runner doc-comment to Option A.

VERIFY LOGISTICS — need your call before I churn the operator's live machine:
The faithful "no --adapter end-to-end" verify needs BOTH (a) spt self-update 0.8.2→0.9.0 + daemon restart, AND (b) re-register claude-spt with the new base-psyche_init manifest. Why (b): the registered adapter is still v0.2.0 (psyche_init in :live, no host_binaries), and the daemon hosts the Psyche from the REGISTERED install dir — it ignores --manifest for binary resolution — so a local --manifest verify can't exercise base-psyche_init; I must register the new manifest. Both hit known hazards on THIS machine: daemon restart, install-dir contention ("Device or resource busy"), and the 20-40 psyche STORM on 0.8.x daemon start with stale owlery dirs (needs daemon-stop + psyche-kill-to-0 + stale sptc-* owlery delete choreography).

The operator is actively dogfooding here. Options:
1. I proceed NOW: spt update→0.9.0, careful daemon teardown, register v0.3.0 manifest, run live-relay-int.sh (Option-A base-psyche_init proof) + a bare-by-pid resolution check anchored on a real claude pid, report. Risk: disrupts any live session the operator has running on this daemon.
2. Ship v0.3.0 on the code-confirmed design (you confirmed livehost.rs:282 + the contract), and the operator dogfoods /sptc:live on 0.9.0 as the live confirmation (they're already on the machine, lowest disruption).
3. You/operator pick a window for me to do the full update+verify.

I lean 1 IF the operator is clear to have the daemon bounced now; else 2. Which? Release artifacts (v0.3.0 repack + CHANGELOG + cplugs republish) are staged-ready either way.
EOF

Report wiring done + ask doyle verify logistics


➜ SENT:doyle


While doyle weighs verify logistics, prep the non-disruptive release artifacts (version bump + CHANGELOG) so release is one step from ready regardless of which verify path he picks.


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)

# 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


Edit

## [0.3.0] - 2026-06-17

### Changed
- **Going live is now a single, seamless step.** A live agent comes up directly — no behind-the-scenes
  adapter selection, no chained setup commands. `/sptc:live` just brings your session up live, and
  `/sptc:ready` brings it up reachable-but-light; the difference is simply which one you run. This is
  full parity with the original live-agent experience.
- **Requires spt-core 0.9.0 or newer.** This release uses spt-core's newer automatic
  harness-resolution, so the adapter now needs spt-core **0.9.0+**. If you keep spt-core up to date
  (it self-updates), there's nothing to do; on an older spt-core the adapter will decline to install
  until you update.

### Fixed
- **Setup pins this adapter as your default.** `/sptc:setup` now records `claude-spt` as the active
  adapter for Claude Code, so going live or ready needs no extra flags.

## [0.2.1] - 2026-06-16


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

## [0.3.0] - 2026-06-17

### Changed
- **Going live is now a single, seamless step.** A live agent comes up directly — no behind-the-scenes
  adapter selection, no chained setup commands. `/sptc:live` just brings your session up live, and
  `/sptc:ready` brings it up reachable-but-light; the difference is simply which one you run. This is
  full parity with the original live-agent experience.
- **Requires spt-core 0.9.0 or newer.** This release uses spt-core's newer automatic
  harness-resolution, so the adapter now needs spt-core **0.9.0+**. If you keep spt-core up to date
  (it self-updates), there's nothing to do; on an older spt-core the adapter will decline to install
  until you update.

### Fixed
- **Setup pins this adapter as your default.** `/sptc:setup` now records `claude-spt` as the active
  adapter for Claude Code, so going live or ready needs no extra flags.

## [0.2.1] - 2026-06-16

# Changelog

All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
public release body verbatim.

> Each release below is keyed to the **adapter version of truth** (the version you see in
> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
> moves on its own slower schedule and may differ.

## [0.2.1] - 2026-06-16

### Fixed
- **Going live shows a clean status, not raw machine output.** Starting a live agent previously
  leaked internal setup markers and tokens into the chat. `/sptc:live` now reports a single tidy
  summary — your agent id, online status, how other agents reach you, and how to reply — and
  nothing else.

### Changed
- **Self-contained live setup.** Bringing a session live no longer bounces you to a separate help
  topic; `/sptc:live` carries the full bringup itself, so going live works in one step.

## [0.2.0] - 2026-06-16

### Fixed
- **Live agents now reliably start their background companion.** Previously a live agent could come
  up marked "online" while its persistent companion silently failed to start, leaving it with no
  running context. The companion now launches and stays resident.

### Changed
- **Simpler setup — no manual PATH step.** The adapter's helper tools are now found automatically
  from where the adapter is installed; `/sptc:setup` no longer asks you to copy anything onto your
  PATH.
- **Automatic adapter updates.** The adapter now updates itself from this project's GitHub releases
  (`spt adapter update`) — logic and instruction changes reach you without a reinstall.

### Added
- **Linux support.** The adapter ships native **Windows and Linux** builds; `/sptc:setup` detects
  your OS and installs the right one.
- **ccs integration.** If you use [ccs](https://github.com/kaitranntt/ccs), `/sptc:setup` wires the
  shipped `claude-spt:ccs` profile so live/ready agents can run on your ccs backends instead of
  `claude`.
- **Private-network onboarding.** `/sptc:setup` now offers to create or join a private network
  (subnet) so you can pair machines and reach agents across them.

## [0.1.0] - 2026-06-15

First public release.

### Added
- **One-step setup — `/sptc:setup`.** Sets the plugin up and installs the Spacetime (`spt`)
  engine for you if it isn't already present, so messaging and live agents just work. Also offers
  to create or join a private network and to register the always-on background service.
- **Agent messaging.** `/sptc:ready` turns on your inbox so other agents can reach this session;
  `/sptc:send` messages another agent; `/sptc:list-agents` shows who is currently active.
- **Live agents.** `/sptc:live` upgrades the current session into a live agent with a persistent
  companion that keeps its own running context. `/sptc:commune` pushes a context update to that
  companion, and `/sptc:signoff` ends a live session cleanly with a final summary.
- **`/sptc:force-stop`** immediately tears down a listening or live agent when you need it gone.
- **Cross-machine networks — `/sptc:subnet`.** Pair machines into a private network (create,
  show the join code, or join an existing one) so your agents can reach each other across nodes.
- **`/sptc:version`** reports the running engine version.
- **Alternate model backends (advanced).** Ships a ready-made `ccs` profile template you can
  select to launch sessions through [ccs](https://github.com/kaitranntt/ccs) — useful for routing
  an agent to a different model or billing backend. You supply your own ccs configuration.
- **Invisible engine install.** Starting a session automatically installs the `spt` engine the
  first time if it is missing — no separate install step.

+- **Going live is now a single, seamless step.** A live agent comes up directly — no behind-the-scenes

+  adapter selection, no chained setup commands. `/sptc:live` just brings your session up live, and

+  `/sptc:ready` brings it up reachable-but-light; the difference is simply which one you run. This is

+  full parity with the original live-agent experience.

+- **Requires spt-core 0.9.0 or newer.** This release uses spt-core's newer automatic

+  harness-resolution, so the adapter now needs spt-core **0.9.0+**. If you keep spt-core up to date

+  (it self-updates), there's nothing to do; on an older spt-core the adapter will decline to install

+- **Setup pins this adapter as your default.** `/sptc:setup` now records `claude-spt` as the active

+  adapter for Claude Code, so going live or ready needs no extra flags.


Non-disruptive prep done. Holding for doyle's verify-logistics call before any infra churn.

## Status — PREP-4 Option A wired, awaiting verify decision

**Done + committed (3665fc5), gates green:**
- Manifest: `psyche_init`→base, `[profiles.live]` deleted, `min_spt_core`→0.9.0, SessionStart `fires` bare
- `session-start.sh`: seed branch → bare `spt api seed` (bind/boundary keep explicit `--adapter` per doyle)
- `live.md`: single bare `spt api listen <id>` Monitor task — no seed-chain, no `--adapter`, no anchor; interim note stripped
- `setup.md`: `spt adapter use claude-spt`; int tests + traceable + PARITY + psyche doc-comment → Option A

**Design:** Option A, doyle code-confirmed (livehost.rs:282 gates psyche on `state==live_agent` → listen=live, poll=ready; base psyche_init harmless for ready). Zero `--adapter` on default path.

**Staged (uncommitted):** v0.3.0 version bump + CHANGELOG.

**Blocked on doyle:** the faithful "no --adapter end-to-end" verify needs spt 0.8.2→0.9.0 self-update + re-registering the new manifest — both churn the operator's live dogfood machine (daemon restart, install-dir contention, documented psyche-storm). Asked doyle whether to bounce the daemon now (option 1), ship on the code-confirmed design + operator dogfoods (option 2), or schedule a window (option 3).


<task-notification>
<task-id>bje35dsqf</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/4" id="8a7ffe38" type="msg" from="doyle">GO — operator cleared the daemon bounce. Proceed with the full live verify now (your option 1):
1. spt self-update 0.8.2→0.9.0 + daemon restart.
2. Careful teardown choreography: daemon-stop → psyche-kill-to-0 → delete stale sptc-* owlery dirs (avoid the 20-40 psyche storm).
3. Register the v0.3.0 base</EVENT-PART>
<EVENT-PART seq="2/4" id="8a7ffe38">-psyche_init manifest.
4. Run live-relay-int.sh (Option-A base-psyche_init proof) + the bare-by-pid resolution check on a real claude pid.
5. On verify-green → ship v0.3.0 + cplugs republish.

SAFETY (you're on the shared box too): scope every reap by PATH or PID, NEVER machine-wide (Get-Process spt | Stop-Process would tear down</EVENT-PART>
<EVENT-PART seq="3/4" id="8a7ffe38"> live agents incl this perch). Reap psyches by their nested perch pids / the sptc-* owlery scope only. install-dir contention (&quot;Device or resource busy&quot;) → retry after the psyche-kill settles, don't force.

Report wired+VALIDATED (the empirical poll-stays-ready confirm + the bare /sptc:live end-to-end). That closes the legacy</EVENT-PART>
<EVENT-PART seq="4/4" id="8a7ffe38">-parity arc. Go.
-- doyle</EVENT-PART></event>
</task-notification>


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


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


doyle: **GO** — operator cleared the daemon bounce. Proceeding option 1 with the safety constraints: scope every reap by PID/PATH/`sptc-*` owlery only — **never** machine-wide `Stop-Process spt` (would tear down this perch + live agents). ACK + start the self-update fetch.


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && "$OWL" send --reply-to doyle doyle perri <<'EOF'
perri — GO confirmed, operator cleared. Proceeding option 1 with your safety rails: every psyche reap scoped by nested-perch PID or sptc-* owlery dir ONLY — NEVER machine-wide (no Get-Process spt | Stop-Process; that'd kill this perch + live agents). Contention → wait for psyche-kill to settle + retry, no force. Sequence: update→0.9.0 → careful teardown → register v0.3.0 manifest → live-relay-int + bare-by-pid check on a real claude pid → report VALIDATED. Starting the self-update now.
EOF
echo "=== spt update fetch (stage only) ==="
spt update fetch 2>&1 | tail -20

ACK doyle + fetch/stage the 0.9.0 self-update


➜ REPLIED:doyle
=== spt update fetch (stage only) ===
Fetching update set from SaberMage/spt-releases…
  fetching spt-x86_64-windows.exe (x86_64-pc-windows-msvc)…
  fetching spt-x86_64-linux (x86_64-unknown-linux-gnu)…
UPDATE_STAGED:21 (set, 2 platforms, from SaberMage/spt-releases) — run `spt update apply` to apply


Staged (counter 21). Before applying (which restarts the daemon — storm risk from stale `sptc-*` owlery dirs), inspect current psyche/owlery state read-only so I clean stale scope FIRST.


cd C:/Users/decid/Documents/projects/spt-claude-code
echo "=== running claude-spt-psyche procs ==="; tasklist 2>/dev/null | grep -i 'claude-spt-psyche' | head -20; echo "count: $(tasklist 2>/dev/null | grep -ci 'claude-spt-psyche')"
echo "=== owlery dirs (sptc-* / stale CI) ==="; OWLERY="${SPT_HOME:-$HOME/AppData/Local/spt-core}/owlery"; echo "owlery=$OWLERY"; ls -1 "$OWLERY" 2>/dev/null | grep -i 'sptc-\|liverelay\|liveprobe' | head -40
echo "=== total owlery entries ==="; ls -1 "$OWLERY" 2>/dev/null | wc -l
echo "=== current live endpoints (mine!) ==="; spt endpoint list 2>&1 | head -20

Inspect psyche procs, stale sptc owlery dirs, live endpoints before apply


=== running claude-spt-psyche procs ===
claude-spt-psyche.exe       115876 Console                    1      4,560 K
claude-spt-psyche.exe        25700 Console                    1      4,248 K
count: 2
=== owlery dirs (sptc-* / stale CI) ===
owlery=/c/Users/decid/AppData/Local/spt-core/owlery
=== total owlery entries ===
4
=== current live endpoints (mine!) ===
SUBNET SPT_DEV
  X	14efb80c…	Dormant
  help	14efb80c…	Dormant
ENDPOINTS:2


State: 2 orphan `claude-spt-psyche.exe` (pids 115876, 25700), **no** stale `sptc-*` owlery dirs (storm source absent), and **no** live sptc endpoint (operator isn't running one — `endpoint list` shows only non-sptc X/help). The psyches are orphans. `claude-spt-psyche` is the sptc-only runner (perri is legacy, untouched). Reap by exact PID (tightest scope), confirm →0, then apply.


cd C:/Users/decid/Documents/projects/spt-claude-code
echo "=== reap the 2 orphan sptc psyches by exact PID ==="
for pid in 115876 25700; do
  taskkill //PID "$pid" //T //F 2>&1 | head -2
done
sleep 1
echo "=== confirm claude-spt-psyche count now ==="
tasklist 2>/dev/null | grep -ci 'claude-spt-psyche'
echo "=== sanity: perri perch + legacy psyche untouched ==="
"$OWL" list 2>&1 | grep -i 'perri' | head -5

Reap orphan sptc psyches by exact PID, confirm scope safety


=== reap the 2 orphan sptc psyches by exact PID ===
SUCCESS: The process with PID 86140 (child process of PID 115876) has been terminated.
SUCCESS: The process with PID 128460 (child process of PID 115876) has been terminated.
SUCCESS: The process with PID 87060 (child process of PID 25700) has been terminated.
SUCCESS: The process with PID 165452 (child process of PID 25700) has been terminated.
=== confirm claude-spt-psyche count now ===
0
=== sanity: perri perch + legacy psyche untouched ===
✓ ACTIVE:perri
✓   - ACTIVE:perri-psyche


Orphans reaped (count 0); perri + perri-psyche still ACTIVE (legacy, untouched). Apply the update.


cd C:/Users/decid/Documents/projects/spt-claude-code && echo "=== spt update apply ==="; spt update apply 2>&1 | tail -25; echo "=== new version ==="; sleep 2; spt --version 2>&1

Apply staged 0.9.0 update + restart daemon, confirm version


=== spt update apply ===
Updated spt-core to v0.9.0.
Changelog: https://github.com/SaberMage/spt-releases/releases
=== new version ===
spt 0.9.0


spt now **0.9.0**. Verify daemon health + perri survived the restart before registering.


cd C:/Users/decid/Documents/projects/spt-claude-code
echo "=== daemon status ==="; spt daemon status 2>&1 | head -12
echo "=== perri perch alive post-restart? ==="; "$OWL" list 2>&1 | grep -i 'perri' | head -5
echo "=== psyche count (should be ~0, no storm) ==="; tasklist 2>/dev/null | grep -ci 'claude-spt-psyche'

Verify daemon health, perri survival, no psyche storm post-0.9.0


=== daemon status ===
daemon: running (pid 140172)
peer pump: live (last tick 33s ago)
managed-by: manual — at-logon task 'spt-core daemon' registered (auto-starts at logon)
subnets:
  SPT_DEV  nodes=3 endpoints=3
local endpoints:
  X                            ready_agent    ready=false alive=false
  help                         ready_agent    ready=false alive=false
  wall-a                       live_agent     ready=false alive=true
=== perri perch alive post-restart? ===
✓ ACTIVE:perri
✓   - ACTIVE:perri-psyche
=== psyche count (should be ~0, no storm) ===
2


Daemon healthy on 0.9.0 (peer pump LIVE — the 0.8.2 stall is gone), perri + perri-psyche survived. Note: `wall-a` shows as `live_agent alive=true` — that's the operator's live agent (the 2 psyches are wall-a's, re-hosted, not orphans). I'll leave wall-a untouched and use a disposable id for verify.

Registration needs `host_binaries` (registered v0.2.0 lacks it) → must register the v0.3.0 manifest. Repack v0.3.0 windows .spt, extract, register from a separate temp dir (Copy → new location, avoids contention with wall-a's running psyche in the `_github` dir).


cd C:/Users/decid/Documents/projects/spt-claude-code
echo "=== repack v0.3.0 windows .spt ==="
sh ci/publish/package-adapter.sh --apply 2>&1 | grep -E 'WROTE|version|asset|validate|ok' | head
echo "=== extract to temp ==="
TMP=$(mktemp -d); tar -xf dist/adapter-windows-x86_64.spt -C "$TMP"
echo "extracted:"; ls "$TMP"; echo "manifest version: $(grep -m1 '^version' "$TMP/manifest.toml")"; echo "host_binaries: $(grep -m1 host_binaries "$TMP/manifest.toml")"; echo "base psyche_init: $(grep -c '^\[session.psyche_init\]' "$TMP/manifest.toml") ; profiles.live: $(grep -c 'profiles.live' "$TMP/manifest.toml")"
echo "$TMP" > /tmp/sptc-v030-regdir
echo "=== register v0.3.0 (Copy from temp; watch for contention) ==="
spt adapter add "$TMP" 2>&1 | head -6

Repack v0.3.0, extract, register from temp dir


=== repack v0.3.0 windows .spt ===
== validate manifest ==
ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-digest/target/release/claude-spt-digest.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-psyche/target/release/claude-spt-psyche.exe
asset    : /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter-windows-x86_64.spt  (-> adapter-windows-x86_64.spt)
      Ship one asset per OS on the same release; /sptc:setup picks the host's via --asset. Build
== validate archive ==
ok   manifest.toml at archive root
WROTE /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter-windows-x86_64.spt. Next (operator): attach to a GitHub release on SaberMage/spt-claude-code (alongside
=== extract to temp ===
extracted:
claude-spt-digest.exe
claude-spt-psyche.exe
manifest.toml
strings
manifest version: version = "0.3.0"
host_binaries: # adapter-agnostic seed + bind-time resolution by [adapter] host_binaries (shipped v0.9.0, ADR-0021).
base psyche_init: 1 ; profiles.live: 0
=== register v0.3.0 (Copy from temp; watch for contention) ===
ADAPTER_ADD:claude-spt:Harness:Pointer (registered)
ADAPTER_INSTALL_DEFERRED: GhReleaseManaged (rides the update engine)


Registered as Pointer/GhReleaseManaged (the `[update]` avenue drives mode). Verify the **v0.3.0 base-psyche_init** manifest is what's now active (no `:live` composite, base live-capable).