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

---
name: checkpoint-commune-wave-built
description: "Checkpoint-commune wave SHIPPED — adapter v0.7.0 released + cplugs sptc 0.1.6 published (multi-line idle, checkpoint communes via --json-payload, psyche-download resume); only new-plugin-install dogfood E2Es remain."
metadata:
  node_type: memory
  type: project
  originSessionId: ddb3691d-6635-4e91-bb00-3365d2fec908
---

Checkpoint-commune wave (operator-requested) — SHIPPED 2026-06-24 (perri). BUILT + VALIDATED on published spt-core v0.15.0 (counter 34) + doyle GATE-PASS + PUBLISHED both targets. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter v0.7.0, min_spt_core 0.15.0, cplugs sptc 0.1.6.

**PUBLISHED (both ADR-0001 targets):**
- Adapter `.spt` -> GitHub release **v0.7.0** on SaberMage/spt-claude-code (asset `adapter.spt`, Latest, not draft). Fat archive both triples (floor 0.15.0), dogfood-proven pre-upload (extract+flatten+translate-proof fired normal framing AND checkpoint macro). Build: Windows native + Linux cross (cargo-zigbuild + zig 0.14.1 at `~/.sptc-zig/zig-x86_64-windows-0.14.1`, add to PATH). Pack: `sh ci/publish/package-adapter.sh --apply` -> dist/adapter.spt. Release: `gh release create v0.7.0 dist/adapter.spt#adapter.spt`.
- cplugs skeleton **0.1.6** -> SaberMage/cplugs main @ b14bb19 (structural bump: new PostToolUse hook). Stage: `sh ci/publish/package-skeleton.sh --apply` -> marketplace clone `~/.claude/plugins/marketplaces/cplugs/plugins/sptc` -> commit+push via `git -C`.
- NOTE: no `.github/workflows` in repo -> tag triggers NO docs-publish action (pre-existing gap; docs in-repo + llms.txt in sync). Pointer-flip (`claude plugin install sptc@cplugs` + session restart) is a per-node operator step, NOT auto-run (would disrupt the active session).

Commits on main: `211a5cc` doc-capture, `befa592` T1, `e27e0d3` T2, `38044e2` T3, `a9aa714` v0.15.0-publish, `5a1d744` watch-item, `50eac11` release-prep. Tag `v0.7.0`.

**What shipped (don't re-derive):**
- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). CC soft-newlines a bare `\n` (empirically gated).
- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) -> PostToolUse(Write) hook `post-tool-use.sh` -> `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":...}'`. Binary parses the `json="..."` envelope ATTR -> xml-attr-unescape (`&` LAST) -> serde -> `checkpoint=="v1"` -> clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint`.
- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` in `session-start.sh` bind|boundary brief -> injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

**VALIDATED on-node (real spt 0.15.0):** T2 LIVE LOOPBACK (real --json-payload self-send drove a spt-hosted CC through /clear + wake turn, transcript-proven); T3 psyche-download 4-way contract (verb/NO-CONTEXT/AUTH/POSITIVE) via deterministic CI `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch, no real CC; 8-poll window straddles the ~5s ingest pulse per doyle's watch-item).

**STILL OPEN — the two harness-glue dogfood E2Es** (doyle gate ruling: logged, NOT CI; CHECKPOINT-COMMUNE-PLAN.md §Deferred validations). Now UNBLOCKED by the publish: a node that installs sptc 0.1.6 + adapter v0.7.0 and spawns a fresh spt-hosted CC runs the NEW hooks -> dogfood (a) T2 PostToolUse write-detect (real `!!checkpoint!!` Write -> self-send), (b) T3 SessionStart injection (hook consuming psyche-download into live CC). Run once a node lands the new plugin.

**Recipe:** comms = legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends (PowerShell has no printf). Validation: swap installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id in `owlery/<id>/info.json` (the CC post_spawn uuid, NOT the endpoint-run session), transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`. Teardown: `spt endpoint purge <id> --force --yes` + restore binary.

-description: "Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain."

+description: "Checkpoint-commune wave SHIPPED — adapter v0.7.0 released + cplugs sptc 0.1.6 published (multi-line idle, checkpoint communes via --json-payload, psyche-download resume); only new-plugin-install dogfood E2Es remain."

-Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

+Checkpoint-commune wave (operator-requested) — SHIPPED 2026-06-24 (perri). BUILT + VALIDATED on published spt-core v0.15.0 (counter 34) + doyle GATE-PASS + PUBLISHED both targets. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter v0.7.0, min_spt_core 0.15.0, cplugs sptc 0.1.6.

-Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

+- Adapter `.spt` -> GitHub release **v0.7.0** on SaberMage/spt-claude-code (asset `adapter.spt`, Latest, not draft). Fat archive both triples (floor 0.15.0), dogfood-proven pre-upload (extract+flatten+translate-proof fired normal framing AND checkpoint macro). Build: Windows native + Linux cross (cargo-zigbuild + zig 0.14.1 at `~/.sptc-zig/zig-x86_64-windows-0.14.1`, add to PATH). Pack: `sh ci/publish/package-adapter.sh --apply` -> dist/adapter.spt. Release: `gh release create v0.7.0 dist/adapter.spt#adapter.spt`.

+- cplugs skeleton **0.1.6** -> SaberMage/cplugs main @ b14bb19 (structural bump: new PostToolUse hook). Stage: `sh ci/publish/package-skeleton.sh --apply` -> marketplace clone `~/.claude/plugins/marketplaces/cplugs/plugins/sptc` -> commit+push via `git -C`.

+- NOTE: no `.github/workflows` in repo -> tag triggers NO docs-publish action (pre-existing gap; docs in-repo + llms.txt in sync). Pointer-flip (`claude plugin install sptc@cplugs` + session restart) is a per-node operator step, NOT auto-run (would disrupt the active session).

-- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits the `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). Empirical gate proved CC soft-newlines a bare `\n`.

-- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) → PostToolUse(Write) hook `post-tool-use.sh` → `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":…}'`. Binary: parse `json="…"` envelope ATTR → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint` SKILL.md + `[[hints]]`.

-- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

+Commits on main: `211a5cc` doc-capture, `befa592` T1, `e27e0d3` T2, `38044e2` T3, `a9aa714` v0.15.0-publish, `5a1d744` watch-item, `50eac11` release-prep. Tag `v0.7.0`.

-**VALIDATED on-node (real spt 0.15.0, spt-hosted CC endpoint ckptval):**

-- **T2 LIVE LOOPBACK** — real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` fired the macro E2E. Transcript: user turn `/clear`, next turn = the wake.

-- **T3 psyche-download** — all 4 contract behaviors. Deterministic CI: `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch via `api bind <id> --set-session-id <sid> --subnet <discovered-from-`spt endpoint list`>`, NO real CC; purge `--force --yes` on exit). Auth needs `--session-id` matching info.json (the CC post_spawn uuid, NOT the endpoint-run session).

-- Send axes (--idle-only/--active-only=renamed-deferred/--prefer-native) + Windows no-window-flash fix confirmed.

+- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). CC soft-newlines a bare `\n` (empirically gated).

+- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) -> PostToolUse(Write) hook `post-tool-use.sh` -> `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":...}'`. Binary parses the `json="..."` envelope ATTR -> xml-attr-unescape (`&` LAST) -> serde -> `checkpoint=="v1"` -> clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint`.

+- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` in `session-start.sh` bind|boundary brief -> injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

-**STILL DEFERRED (dogfood-after-plugin-land, LOGGED not CI — doyle gate ruling):** the two harness-glue E2Es need the NEW v0.7.0 plugin installed + real CC (the spt-hosted CC currently runs the OLD `cplugs sptc 0.1.4` hooks): (a) T2 PostToolUse write-detect (`post-tool-use.sh` detecting `!!checkpoint!!` in a Write + self-sending); (b) T3 SessionStart injection (`session-start.sh` consuming the pull + injecting into live CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`. Light up on a v0.7.0 publish (operator call) — the remaining open item.

+**VALIDATED on-node (real spt 0.15.0):** T2 LIVE LOOPBACK (real --json-payload self-send drove a spt-hosted CC through /clear + wake turn, transcript-proven); T3 psyche-download 4-way contract (verb/NO-CONTEXT/AUTH/POSITIVE) via deterministic CI `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch, no real CC; 8-poll window straddles the ~5s ingest pulse per doyle's watch-item).

-**Probe/validation recipe:** swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`) with a dev binary (back up first, restore after), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id is in `owlery/<id>/info.json`, transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`, grep a unique token. Teardown: `spt endpoint shutdown <id>` + reap the binary + restore. Comms: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends.

+**STILL OPEN — the two harness-glue dogfood E2Es** (doyle gate ruling: logged, NOT CI; CHECKPOINT-COMMUNE-PLAN.md §Deferred validations). Now UNBLOCKED by the publish: a node that installs sptc 0.1.6 + adapter v0.7.0 and spawns a fresh spt-hosted CC runs the NEW hooks -> dogfood (a) T2 PostToolUse write-detect (real `!!checkpoint!!` Write -> self-send), (b) T3 SessionStart injection (hook consuming psyche-download into live CC). Run once a node lands the new plugin.

+**Recipe:** comms = legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends (PowerShell has no printf). Validation: swap installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id in `owlery/<id>/info.json` (the CC post_spawn uuid, NOT the endpoint-run session), transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`. Teardown: `spt endpoint purge <id> --force --yes` + restore binary.

---
name: checkpoint-commune-wave-built
description: "Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain."
metadata:
  node_type: memory
  type: project
  originSessionId: ddb3691d-6635-4e91-bb00-3365d2fec908
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

**What got built (don't re-derive):**
- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits the `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). Empirical gate proved CC soft-newlines a bare `\n`.
- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) → PostToolUse(Write) hook `post-tool-use.sh` → `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":…}'`. Binary: parse `json="…"` envelope ATTR → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint` SKILL.md + `[[hints]]`.
- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

**VALIDATED on-node (real spt 0.15.0, spt-hosted CC endpoint ckptval):**
- **T2 LIVE LOOPBACK** — real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` fired the macro E2E. Transcript: user turn `/clear`, next turn = the wake.
- **T3 psyche-download** — all 4 contract behaviors. Deterministic CI: `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch via `api bind <id> --set-session-id <sid> --subnet <discovered-from-`spt endpoint list`>`, NO real CC; purge `--force --yes` on exit). Auth needs `--session-id` matching info.json (the CC post_spawn uuid, NOT the endpoint-run session).
- Send axes (--idle-only/--active-only=renamed-deferred/--prefer-native) + Windows no-window-flash fix confirmed.

**STILL DEFERRED (dogfood-after-plugin-land, LOGGED not CI — doyle gate ruling):** the two harness-glue E2Es need the NEW v0.7.0 plugin installed + real CC (the spt-hosted CC currently runs the OLD `cplugs sptc 0.1.4` hooks): (a) T2 PostToolUse write-detect (`post-tool-use.sh` detecting `!!checkpoint!!` in a Write + self-sending); (b) T3 SessionStart injection (`session-start.sh` consuming the pull + injecting into live CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`. Light up on a v0.7.0 publish (operator call) — the remaining open item.

**Probe/validation recipe:** swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`) with a dev binary (back up first, restore after), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id is in `owlery/<id>/info.json`, transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`, grep a unique token. Teardown: `spt endpoint shutdown <id>` + reap the binary + restore. Comms: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends.


doyle verified — **wave SHIPPED end-to-end.** ACK + finish memory index.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
$OWL send doyle perri <<'EOF'
ACK — confirmed shipped. Both targets verified your end (v0.7.0 Latest fat .spt floor 0.15.0, cplugs sptc 0.1.6 @b14bb19). Only thread left = the two dogfood E2Es (PostToolUse write-detect + SessionStart injection) on the first node that lands sptc 0.1.6 + v0.7.0 — mine to run on next install, will ping outcomes. Checkpoint-commune wave closed. Good collaborating, doyle.
EOF
echo ack=$?


➜ SENT:doyle
ack=0


Edit

- [Checkpoint-commune wave BUILT (T1/T2/T3)](checkpoint-commune-wave-built.md) — 2026-06-24 (perri): T1 multi-line idle envelope (empirical \n-soft-newline gate PASSED), T2 checkpoint communes (`!!checkpoint!!` write-trigger + `spt send --json-payload` json-attr delivery → binary clear+wake macro; one-shot via core-side strip), T3 psyche-download resume wiring (closes F-020). 4 commits on main (211a5cc/befa592/e27e0d3/38044e2), all gates GREEN. **BLOCKED ON spt-core v0.15.0/counter-34 publish** → then bump min_spt_core 0.13.2→0.15.0 + light 2 deferred ints (json-payload self-send loopback, psyche-download SessionStart). EMIT-halves proven via translate-proof.

- [Checkpoint-commune wave SHIPPED (T1/T2/T3)](checkpoint-commune-wave-built.md) — 2026-06-24 (perri): T1 multi-line idle envelope, T2 checkpoint communes (`!!checkpoint!!` + `spt send --json-payload` json-attr → binary clear+wake macro), T3 psyche-download resume (closes F-020). VALIDATED on spt-core v0.15.0 + doyle GATE-PASS + **PUBLISHED both targets** (adapter **v0.7.0** GitHub release `adapter.spt` Latest; cplugs **sptc 0.1.6** @b14bb19). Only open thread = the 2 harness-glue dogfood E2Es (write-detect + SessionStart injection) on the first node that installs sptc 0.1.6 + v0.7.0.


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

# Memory index — spt-claude-code

- [Live-agent ints DONE](live-int-gated-m11-counter15.md) — v0.7.3; setup+release ints (350ecea) AND live relay int (29e69bc) all GREEN; F-007 resolved to docs-gap; live-bringup recipe = persistent-child `api listen --manifest` (WINPID anchor, bind-before-send).
- [v0.8.1 dogfood → v0.2.0 → v0.2.1 SHIPPED](v074-follow-ups.md) — psyche blocker fixed (ddf1965 greedy --prompt); F-006 retired; v0.2.0 (win+linux .spt) + auto-update proven; cplugs sptc 0.1.2; F-009/F-010 re-validated v0.8.2. **v0.2.1 SHIPPED** (doyle live-bringup feedback): self-contained /sptc:live injection (killed how-to redirect) + humanized output (OUTPUT DISCIPLINE + canonical LIVE block) + staged host_binaries=["claude"]. **PREP-4 HELD** for spt-core v0.9.0 ping (agnostic-seed/by-pid resolution, ADR-0021): bare SessionStart seed, drop --adapter + seed/listen chain, `spt adapter use` in setup.
- [PREP-4 → v0.3.0 → PARITY ARC CLOSED](prep4-v030-parity-closed.md) — **v0.3.0 SHIPPED** (Option A: psyche_init in BASE, NO :live profile, bare flow zero --adapter, min_spt_core 0.9.0; ready/live = the command). cplugs sptc 0.1.3. Verified+dogfooded on spt 0.9.1 (F1 broker-bounce, F2b stem-matcher for CC's renamed-in-use exe, bare live→psyche, poll-stays-ready). F-011 deferred-Pointer-manifest silent-drop = real spt-core gap (doyle minting). F-012 legacy-owl poll death = not-spt-core.
- [Per-OS adapter.spt + setup waves](per-os-and-setup-waves.md) — per-OS .spt (windows+linux, linux cross-built via cargo-zigbuild) DONE; setup {1..7}: #7 ccs + #3/#4 subnet DONE, #6 installer-handled, #1 gated-M12, #5 deferred-by-design (F-008).
- [Succession flip gated](succession-flip-gated.md) — publish as sptc; flip to spt only after proven parity AND legacy owl retired.
- [Psyche sandbox + ADR-leak → v0.4.0 SHIPPED](psyche-sandbox-and-adr-leak.md) — 2026-06-18: psyche sandboxed to legacy-owl parity (ADR-0003, REQ-HAZARD-PSYCHE-PERMS-DEADLOCK); skip-perms on bringup; `:deep` removed; **only ADR-0001/0002 are real here — 0003..0021 were foreign-leak, wiped**; **SHIPPED v0.4.0** (both per-OS .spt, no cplugs bump, min_spt_core stays 0.9.0); spt-core 0.12.0 out but not required; experimplate clean.
- [F-019 idle-submit root cause + v0.6.2 + v0.14.1](f019-idle-submit-and-v0142.md) — 2026-06-23 (perri): idle msgs never submitted = **relative translation-binary path not resolved against install_dir** → spawn fails → silent raw-inject fallback → stacks-unsent. Binary NEVER ran (all byte/encoding/paste/focus theories were red herrings). Fix = spt-core **v0.14.2** (resolve_program_in_dir + loud spawn-fail log; doyle @1941eb8, todlando building). Adapter unchanged; **v0.6.2 shipped** (moot). Interim abs-path hack on this node. **v0.14.1 out** (F-018 adapter-safety). perri verifies v0.14.2 on doyle's ping.
- [Checkpoint-commune wave BUILT (T1/T2/T3)](checkpoint-commune-wave-built.md) — 2026-06-24 (perri): T1 multi-line idle envelope (empirical \n-soft-newline gate PASSED), T2 checkpoint communes (`!!checkpoint!!` write-trigger + `spt send --json-payload` json-attr delivery → binary clear+wake macro; one-shot via core-side strip), T3 psyche-download resume wiring (closes F-020). 4 commits on main (211a5cc/befa592/e27e0d3/38044e2), all gates GREEN. **BLOCKED ON spt-core v0.15.0/counter-34 publish** → then bump min_spt_core 0.13.2→0.15.0 + light 2 deferred ints (json-payload self-send loopback, psyche-download SessionStart). EMIT-halves proven via translate-proof.
- [W3 validated + F-017 seed + v0.6.1 fat packaging](v06x-w3-f017-v061.md) — 2026-06-22 (perri): spt-core v0.13.2. **W3 psyche-lock VALIDATED** (F-010×F-015 closed both sides, `.live-bin` own-copy). **F-017 multi-subnet seed shipped** (ci/subnet, =REQ-RUN-MULTISUBNET-HOME, doyle v0.14.0). **v0.6.1 SHIPPED — F-014 DEAD**: single multi-platform fat adapter.spt (per-triple subdirs, 2 recognized triples, min_spt_core→0.13.2), /sptc:setup bare `adapter add --release`, cplugs 0.1.5, proof-ints→`--dir/--manifest`; real-extract proven on-node. Findings: F-018 (add --github footgun), multi-platform layout was doc-gap (doyle PR#31). **NEXT: v0.14.0 publish validation on doyle's ping.**

-- [Checkpoint-commune wave BUILT (T1/T2/T3)](checkpoint-commune-wave-built.md) — 2026-06-24 (perri): T1 multi-line idle envelope (empirical \n-soft-newline gate PASSED), T2 checkpoint communes (`!!checkpoint!!` write-trigger + `spt send --json-payload` json-attr delivery → binary clear+wake macro; one-shot via core-side strip), T3 psyche-download resume wiring (closes F-020). 4 commits on main (211a5cc/befa592/e27e0d3/38044e2), all gates GREEN. **BLOCKED ON spt-core v0.15.0/counter-34 publish** → then bump min_spt_core 0.13.2→0.15.0 + light 2 deferred ints (json-payload self-send loopback, psyche-download SessionStart). EMIT-halves proven via translate-proof.

+- [Checkpoint-commune wave SHIPPED (T1/T2/T3)](checkpoint-commune-wave-built.md) — 2026-06-24 (perri): T1 multi-line idle envelope, T2 checkpoint communes (`!!checkpoint!!` + `spt send --json-payload` json-attr → binary clear+wake macro), T3 psyche-download resume (closes F-020). VALIDATED on spt-core v0.15.0 + doyle GATE-PASS + **PUBLISHED both targets** (adapter **v0.7.0** GitHub release `adapter.spt` Latest; cplugs **sptc 0.1.6** @b14bb19). Only open thread = the 2 harness-glue dogfood E2Es (write-detect + SessionStart injection) on the first node that installs sptc 0.1.6 + v0.7.0.


**v0.7.0 PUBLISHED — wave shipped end-to-end. doyle verified.**

Both ADR-0001 publish targets live:

| Target | Result |
|--------|--------|
| **Adapter `.spt`** | GitHub release **v0.7.0** (SaberMage/spt-claude-code), asset `adapter.spt` (1.05MB fat archive, both triples, floor 0.15.0), Latest. **Dogfood-proven pre-upload** — extracted+flattened, translate-proof fired both normal framing AND the checkpoint macro from the released binary. |
| **cplugs skeleton** | **sptc 0.1.6** pushed to SaberMage/cplugs `@b14bb19` (new PostToolUse hook + post-tool-use.sh + resume/checkpoint helpers + full-fat commune skill). |

Build: Windows native + Linux cross (cargo-zigbuild/zig 0.14.1), all 3 tool crates. Tag `v0.7.0` pushed. CHANGELOG [0.7.0] user-facing. `traceable-reqs` GREEN throughout. Project repo clean + in sync.

Notes:
- No `.github/workflows` in repo → tag triggers no docs-publish action (pre-existing gap; docs in-repo + llms.txt in sync).
- **Pointer-flip** (`claude plugin install sptc@cplugs` + session restart) is a per-node operator step — did NOT auto-run (would disrupt this active session). Do it on a node when you want it to consume 0.1.6.

**One thread left** (doyle-gated, now unblocked by the publish): the two harness-glue dogfood E2Es — PostToolUse write-detect + SessionStart injection — run on the first node that installs sptc 0.1.6 + v0.7.0 and spawns a fresh spt-hosted CC. Mine to run on next install; I'll ping doyle the outcomes.

Checkpoint-commune wave: **closed.**


<command-message>grill-with-docs</command-message>
<command-name>/grill-with-docs</command-name>
<command-args>-i want to make it so the user can keep all of `spt-claude-code` updated using only `spt adapter update claude-spt`. how would you suggest we achieve this? -this adapter has too many names. it's `spt-claude-code` at the repo, `sptc` at the plugin, and `claude-spt` for the adapter. we need to come up with a unification plan.</command-args>


Base directory for this skill: C:\Users\decid\.ccs\instances\bigscreen\skills\grill-with-docs

<what-to-do>

Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.

Ask the questions one at a time, waiting for feedback on each question before continuing.

If a question can be answered by exploring the codebase, explore the codebase instead.

</what-to-do>

<supporting-info>

## Domain awareness

During codebase exploration, also look for existing documentation:

### File structure

Most repos have a single context:

```
/
├── CONTEXT.md
├── docs/
│   └── adr/
│       ├── 0001-event-sourced-orders.md
│       └── 0002-postgres-for-write-model.md
└── src/
```

If a `CONTEXT-MAP.md` exists at the root, the repo has multiple contexts. The map points to where each one lives:

```
/
├── CONTEXT-MAP.md
├── docs/
│   └── adr/                          ← system-wide decisions
├── src/
│   ├── ordering/
│   │   ├── CONTEXT.md
│   │   └── docs/adr/                 ← context-specific decisions
│   └── billing/
│       ├── CONTEXT.md
│       └── docs/adr/
```

Create files lazily — only when you have something to write. If no `CONTEXT.md` exists, create one when the first term is resolved. If no `docs/adr/` exists, create it when the first ADR is needed.

## During the session

### Challenge against the glossary

When the user uses a term that conflicts with the existing language in `CONTEXT.md`, call it out immediately. "Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?"

### Sharpen fuzzy language

When the user uses vague or overloaded terms, propose a precise canonical term. "You're saying 'account' — do you mean the Customer or the User? Those are different things."

### Discuss concrete scenarios

When domain relationships are being discussed, stress-test them with specific scenarios. Invent scenarios that probe edge cases and force the user to be precise about the boundaries between concepts.

### Cross-reference with code

When the user states how something works, check whether the code agrees. If you find a contradiction, surface it: "Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?"

### Update CONTEXT.md inline

When a term is resolved, update `CONTEXT.md` right there. Don't batch these up — capture them as they happen. Use the format in [CONTEXT-FORMAT.md](./CONTEXT-FORMAT.md).

`CONTEXT.md` should be totally devoid of implementation details. Do not treat `CONTEXT.md` as a spec, a scratch pad, or a repository for implementation decisions. It is a glossary and nothing else.

### Offer ADRs sparingly

Only offer to create an ADR when all three are true:

1. **Hard to reverse** — the cost of changing your mind later is meaningful
2. **Surprising without context** — a future reader will wonder "why did they do it this way?"
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons

If any of the three is missing, skip the ADR. Use the format in [ADR-FORMAT.md](./ADR-FORMAT.md).

</supporting-info>

ARGUMENTS: -i want to make it so the user can keep all of `spt-claude-code` updated using only `spt adapter update claude-spt`. how would you suggest we achieve this? -this adapter has too many names. it's `spt-claude-code` at the repo, `sptc` at the plugin, and `claude-spt` for the adapter. we need to come up with a unification plan.


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


/grill-with-docs -i want to make it so the user can keep all of `spt-claude-code` updated using only `spt adapter update claude-spt`. how would you suggest we achieve this? -this adapter has too many…


Two big topics: (1) one-command update for the whole thing, (2) name unification. Both touch the distribution model. Let me ground in the existing decisions before grilling — reading ADR-0001 + naming/succession docs.


1	<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
2	<!-- [doc->REQ-DIST-HOOKS-API] -->
3	<!-- [doc->REQ-DIST-BOOTSTRAP-INSTALL] -->
4	<!-- [doc->REQ-UPS-INJECTION] -->
5	# Distribution splits by volatility: a thin skeleton plugin, an spt-conducted manifest + binary
6
7	## Status
8
9	accepted (2026-06-14)
10
11	## Context
12
13	`spt-claude-code` is the `claude-spt` adapter — simultaneously spt-core's v1 acceptance proof,
14	its first casual-end-user entrypoint (a Claude Code plugin on `SaberMage/cplugs`), and an
15	invisible spt-core installer. It must ship as a CC plugin, yet almost all real logic now lives
16	inside the `spt` binary and the CC adapter manifest, both **conducted by spt-core**, not by the
17	plugin author.
18
19	Forces:
20
21	1. **Two very different churn rates.** Skill *instructions*, the `[digest]` extractor, profiles,
22	   strings, and hints change often. The plugin's *structure* (which skills exist, hook wiring,
23	   bootstrap) changes rarely. Coupling them to one release cadence means either over-bumping the
24	   marketplace or letting logic rot.
25	2. **Claude Code has no plugin-file integrity check.** In-place edits to an installed plugin do
26	   not orphan it; only *old version directories* orphan on a version bump (GC'd after 7 days).
27	   So low-churn structure is safe to ship via the marketplace, and high-churn logic does *not*
28	   need to ride the marketplace at all.
29	3. **The skill `/spt:` namespace must be preserved** (legacy parity). User-scope skills under
30	   `~/.claude/skills` cannot namespace (they surface as bare `/live`, collision-prone), so skills
31	   must stay **plugin-provided**.
32	4. **Users install mid-session**, so the plugin cannot assume a SessionStart fire has installed
33	   spt-core; the bootstrap and a `/spt:setup` skill must cover the gap.
34
35	## Decision
36
37	Split the adapter across **three layers by volatility** (see `SCOPE.md` §"Distribution
38	architecture"):
39
40	| Layer | Contents | Home | Churn |
41	|---|---|---|---|
42	| **Skeleton plugin** | namespaced `/spt:*` skill *skeletons*, `hooks.json`, SessionStart bootstrap, `plugin.json` | `SaberMage/cplugs` | low |
43	| **spt binary** | all logic | spt-core domain (signed, peer-propagated) | high |
44	| **CC adapter manifest** | `[digest]` extractor, profiles, strings, hints | spt-core adapter registry (**not** plugin files) | medium |
45
46	Concretely:
47
48	- **The marketplace plugin is a true thin skeleton.** It carries skill *skeletons* (frontmatter
49	  + a stub body), `hooks.json`, a SessionStart bootstrap, and `plugin.json` — **no binary, no
50	  manifest, no embedded logic.** [`REQ-DIST-PLUGIN-SKELETON`]
51	- **`hooks.json` delegates to `spt api`** rather than carrying adapter behavior. The hook commands
52	  invoke the spt binary's published `api` surface; the plugin ships no binary of its own (contrast
53	  legacy, which shipped `owl.exe` and called it directly). [`REQ-DIST-HOOKS-API`]
54	- **A SessionStart bootstrap installs spt-core when absent** — the invisible-installer pattern.
55	  A user who installs the plugin gets spt-core for free. [`REQ-DIST-BOOTSTRAP-INSTALL`]
56	- **Skill instructions are delivered by UPS-injection.** A `UserPromptSubmit` hook detects `/spt:X`
57	  in the prompt and injects X's real instructions as `additionalContext`, sourced from the adapter
58	  `[strings]` (file-backed, so the manifest doesn't bloat). SKILL.md files stay skeletons; the
59	  volatile instruction text lives in spt-core-conducted strings. [`REQ-UPS-INJECTION`]
60	- **The "bulk" install/update is manifest + binary**, conducted by spt-core's own framework (the
61	  file-pull channel), not a marketplace bump. The marketplace plugin rarely needs a version bump
62	  and never orphans an active install.
63
64	### Rejected alternatives
65
66	- **Global user-scope skills** (`~/.claude/skills`) — lose the `/spt:` namespace (bare `/live`,
67	  collision-prone). Rejected.
68	- **fetch-stub skills** (SKILL.md fetches its real body at runtime) — VETOED by the operator:
69	  adds terminal noise, latency, and an extra tool call per invocation. UPS-injection chosen
70	  instead. (`SCOPE.md` §"Skill-instruction delivery".)
71	- **A fat plugin shipping the binary** (legacy `owl.exe` model) — couples high-churn logic to the
72	  marketplace cadence and forces the binary/handoff/cache-prune machinery the thin skeleton has no
73	  need for. Rejected; logic rides the spt binary + adapter registry.
74
75	### Open / to-confirm
76
77	- **UPS-fires-on-slash-command — CONFIRMED (2026-06-15, ADR-0002 validation).** `UserPromptSubmit`
78	  fires on a `/sptc:X` slash-command with the token intact (`prompt:"/sptc:send doyle"` literal, hook
79	  fired, skill ran, on CC 2.1.177). The injection branch is built (`user-prompt-submit.sh`
80	  `sptc_skill_key` + `sptc_inject_skill`); the in-SKILL.md fallback is unneeded for injection (but
81	  `setup` stays self-contained — it runs when spt may be absent). (`REQ-UPS-INJECTION`.)
82	- **File-backed `[strings]` is an M12 spt-core dependency** — until M12 publishes, instruction
83	  bodies cannot be externalized; skeleton SKILL.md files may carry interim inline instructions.
84	  **Update 2026-06-15 — SATISFIED.** M12 (`spt 0.7.0`) ships file-backed `[strings]` as a
85	  value-position table pointer: `key = { file = "rel" }` over the per-adapter aux dir
86	  `adapters/<adapter>/strings/`, resolved lazily at `get-string` (containment-checked; copied on
87	  `adapter add`). Confirmed by live byte-test + spt-core ruling (`REQ-MANIFEST-5`, M12-W3). The
88	  dependency is met — skill bodies are externalized via this pointer (no manifest bloat); the
89	  interim-inline fallback is retired. The pointer syntax was merely **undocumented** on the published
90	  surface (now finding **F-003**, docs-only, doyle publishing). See `docs/SPT-CORE-FINDINGS.md`.
91
92	## Consequences
93
94	<!-- [doc->REQ-DIST-MANIFEST-SCHEMA] -->
95	- The marketplace plugin can sit static across many manifest/binary updates → the
96	  version-of-truth is the spt-core-tracked manifest/binary version (`spt adapter list` /
97	  `/spt:version`), **not** the marketplace `plugin.json` version. The CC adapter manifest
98	  (`adapter/claude-spt.toml`) is authored against spt-core's **published** `manifest.schema.json`
99	  (vendored at `adapter/manifest.schema.json`) and CI-gated against it (`ci/manifest/`). These are distinct numbers on
100	  independent schedules (see `docs/RELEASE-RUNBOOK.md`).
101	- New obligation: a dual update path kept in sync — file-pull (real) + a cautionary
102	  `claude plugin update` (skeleton sync only).
103	<!-- [doc->REQ-DIST-SHORTCUT-BASENAME] -->
104	- The manifest brands the `spt endpoint run` launcher shortcut **`cc-<id>`** via
105	  `shortcut_basename = "cc"` (the M12 `cc` launcher; the harness-agnostic default would be
106	  `spt-<id>`, and it matches the schema's own `spt-claude-code → cc` example). This launcher brand
107	  is **decoupled** from the `sptc` plugin name — the `s/sptc/spt/` succession (next bullet) renames
108	  the plugin, not this shortcut. (Operator-ruled 2026-06-15.)
109	- Forces the cplugs **plugin-name** question, now **ruled (operator, 2026-06-14)**: because CC
110	  invokes skills as `/<plugin>:<skill>` (the skill prefix is hard-tied to the `plugin.json` name —
111	  no override; confirmed against the official plugin docs), the marketplace plugin is named
112	  **`sptc`** and skills surface as **`/sptc:*`** for now. **Succession plan:** once `spt-claude-code`
113	  proves parity, flip `plugin.json` name `sptc`→`spt` (skills become `/spt:*`) and retire/rename
114	  legacy owl's cplugs `spt` plugin in the same coordinated move (two plugins cannot share the
115	  `spt` name). `sptc` is the single-substitution seam — the flip is `s/sptc/spt/`.
116	- Mints no new REQs beyond those already seeded for the skeleton milestone; this ADR is the
117	  `doc`-stage evidence for `REQ-DIST-PLUGIN-SKELETON`, `REQ-DIST-HOOKS-API`,
118	  `REQ-DIST-BOOTSTRAP-INSTALL`, and `REQ-UPS-INJECTION`.
119

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
<!-- [doc->REQ-DIST-HOOKS-API] -->
<!-- [doc->REQ-DIST-BOOTSTRAP-INSTALL] -->
<!-- [doc->REQ-UPS-INJECTION] -->
# Distribution splits by volatility: a thin skeleton plugin, an spt-conducted manifest + binary

## Status

accepted (2026-06-14)

## Context

`spt-claude-code` is the `claude-spt` adapter — simultaneously spt-core's v1 acceptance proof,
its first casual-end-user entrypoint (a Claude Code plugin on `SaberMage/cplugs`), and an
invisible spt-core installer. It must ship as a CC plugin, yet almost all real logic now lives
inside the `spt` binary and the CC adapter manifest, both **conducted by spt-core**, not by the
plugin author.

Forces:

1. **Two very different churn rates.** Skill *instructions*, the `[digest]` extractor, profiles,
   strings, and hints change often. The plugin's *structure* (which skills exist, hook wiring,
   bootstrap) changes rarely. Coupling them to one release cadence means either over-bumping the
   marketplace or letting logic rot.
2. **Claude Code has no plugin-file integrity check.** In-place edits to an installed plugin do
   not orphan it; only *old version directories* orphan on a version bump (GC'd after 7 days).
   So low-churn structure is safe to ship via the marketplace, and high-churn logic does *not*
   need to ride the marketplace at all.
3. **The skill `/spt:` namespace must be preserved** (legacy parity). User-scope skills under
   `~/.claude/skills` cannot namespace (they surface as bare `/live`, collision-prone), so skills
   must stay **plugin-provided**.
4. **Users install mid-session**, so the plugin cannot assume a SessionStart fire has installed
   spt-core; the bootstrap and a `/spt:setup` skill must cover the gap.

## Decision

Split the adapter across **three layers by volatility** (see `SCOPE.md` §"Distribution
architecture"):

| Layer | Contents | Home | Churn |
|---|---|---|---|
| **Skeleton plugin** | namespaced `/spt:*` skill *skeletons*, `hooks.json`, SessionStart bootstrap, `plugin.json` | `SaberMage/cplugs` | low |
| **spt binary** | all logic | spt-core domain (signed, peer-propagated) | high |
| **CC adapter manifest** | `[digest]` extractor, profiles, strings, hints | spt-core adapter registry (**not** plugin files) | medium |

Concretely:

- **The marketplace plugin is a true thin skeleton.** It carries skill *skeletons* (frontmatter
  + a stub body), `hooks.json`, a SessionStart bootstrap, and `plugin.json` — **no binary, no
  manifest, no embedded logic.** [`REQ-DIST-PLUGIN-SKELETON`]
- **`hooks.json` delegates to `spt api`** rather than carrying adapter behavior. The hook commands
  invoke the spt binary's published `api` surface; the plugin ships no binary of its own (contrast
  legacy, which shipped `owl.exe` and called it directly). [`REQ-DIST-HOOKS-API`]
- **A SessionStart bootstrap installs spt-core when absent** — the invisible-installer pattern.
  A user who installs the plugin gets spt-core for free. [`REQ-DIST-BOOTSTRAP-INSTALL`]
- **Skill instructions are delivered by UPS-injection.** A `UserPromptSubmit` hook detects `/spt:X`
  in the prompt and injects X's real instructions as `additionalContext`, sourced from the adapter
  `[strings]` (file-backed, so the manifest doesn't bloat). SKILL.md files stay skeletons; the
  volatile instruction text lives in spt-core-conducted strings. [`REQ-UPS-INJECTION`]
- **The "bulk" install/update is manifest + binary**, conducted by spt-core's own framework (the
  file-pull channel), not a marketplace bump. The marketplace plugin rarely needs a version bump
  and never orphans an active install.

### Rejected alternatives

- **Global user-scope skills** (`~/.claude/skills`) — lose the `/spt:` namespace (bare `/live`,
  collision-prone). Rejected.
- **fetch-stub skills** (SKILL.md fetches its real body at runtime) — VETOED by the operator:
  adds terminal noise, latency, and an extra tool call per invocation. UPS-injection chosen
  instead. (`SCOPE.md` §"Skill-instruction delivery".)
- **A fat plugin shipping the binary** (legacy `owl.exe` model) — couples high-churn logic to the
  marketplace cadence and forces the binary/handoff/cache-prune machinery the thin skeleton has no
  need for. Rejected; logic rides the spt binary + adapter registry.

### Open / to-confirm

- **UPS-fires-on-slash-command — CONFIRMED (2026-06-15, ADR-0002 validation).** `UserPromptSubmit`
  fires on a `/sptc:X` slash-command with the token intact (`prompt:"/sptc:send doyle"` literal, hook
  fired, skill ran, on CC 2.1.177). The injection branch is built (`user-prompt-submit.sh`
  `sptc_skill_key` + `sptc_inject_skill`); the in-SKILL.md fallback is unneeded for injection (but
  `setup` stays self-contained — it runs when spt may be absent). (`REQ-UPS-INJECTION`.)
- **File-backed `[strings]` is an M12 spt-core dependency** — until M12 publishes, instruction
  bodies cannot be externalized; skeleton SKILL.md files may carry interim inline instructions.
  **Update 2026-06-15 — SATISFIED.** M12 (`spt 0.7.0`) ships file-backed `[strings]` as a
  value-position table pointer: `key = { file = "rel" }` over the per-adapter aux dir
  `adapters/<adapter>/strings/`, resolved lazily at `get-string` (containment-checked; copied on
  `adapter add`). Confirmed by live byte-test + spt-core ruling (`REQ-MANIFEST-5`, M12-W3). The
  dependency is met — skill bodies are externalized via this pointer (no manifest bloat); the
  interim-inline fallback is retired. The pointer syntax was merely **undocumented** on the published
  surface (now finding **F-003**, docs-only, doyle publishing). See `docs/SPT-CORE-FINDINGS.md`.

## Consequences

<!-- [doc->REQ-DIST-MANIFEST-SCHEMA] -->
- The marketplace plugin can sit static across many manifest/binary updates → the
  version-of-truth is the spt-core-tracked manifest/binary version (`spt adapter list` /
  `/spt:version`), **not** the marketplace `plugin.json` version. The CC adapter manifest
  (`adapter/claude-spt.toml`) is authored against spt-core's **published** `manifest.schema.json`
  (vendored at `adapter/manifest.schema.json`) and CI-gated against it (`ci/manifest/`). These are distinct numbers on
  independent schedules (see `docs/RELEASE-RUNBOOK.md`).
- New obligation: a dual update path kept in sync — file-pull (real) + a cautionary
  `claude plugin update` (skeleton sync only).
<!-- [doc->REQ-DIST-SHORTCUT-BASENAME] -->
- The manifest brands the `spt endpoint run` launcher shortcut **`cc-<id>`** via
  `shortcut_basename = "cc"` (the M12 `cc` launcher; the harness-agnostic default would be
  `spt-<id>`, and it matches the schema's own `spt-claude-code → cc` example). This launcher brand
  is **decoupled** from the `sptc` plugin name — the `s/sptc/spt/` succession (next bullet) renames
  the plugin, not this shortcut. (Operator-ruled 2026-06-15.)
- Forces the cplugs **plugin-name** question, now **ruled (operator, 2026-06-14)**: because CC
  invokes skills as `/<plugin>:<skill>` (the skill prefix is hard-tied to the `plugin.json` name —
  no override; confirmed against the official plugin docs), the marketplace plugin is named
  **`sptc`** and skills surface as **`/sptc:*`** for now. **Succession plan:** once `spt-claude-code`
  proves parity, flip `plugin.json` name `sptc`→`spt` (skills become `/spt:*`) and retire/rename
  legacy owl's cplugs `spt` plugin in the same coordinated move (two plugins cannot share the
  `spt` name). `sptc` is the single-substitution seam — the flip is `s/sptc/spt/`.
- Mints no new REQs beyond those already seeded for the skeleton milestone; this ADR is the
  `doc`-stage evidence for `REQ-DIST-PLUGIN-SKELETON`, `REQ-DIST-HOOKS-API`,
  `REQ-DIST-BOOTSTRAP-INSTALL`, and `REQ-UPS-INJECTION`.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== CONTEXT.md name/succession terms ==="; grep -niE 'sptc|claude-spt|spt-claude-code|succession|shortcut|launcher|cc\b' CONTEXT.md | head -30
echo "=== SCOPE.md naming/succession ==="; grep -niE 'succession|sptc|claude-spt|name|flip' SCOPE.md | head -30


=== CONTEXT.md name/succession terms ===
1:# spt-claude-code — glossary
6:**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
8:parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
11:**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
12:invocation + the manifest). The CC adapter's identity inside spt-core.
14:**legacy spt / `claude_skill_owl`** — the sister project; today's shipped CC integration
41:**ccs profile** — a profile under spt-claude-code (`claude-spt:glm`, `claude-spt:kimi`)
45:**`cc` launcher (capsule-style)** — a generated `cc`/`cc <id>` script at project root that
46:spawns-or-attaches a CC endpoint via spt-core's spt-hosted topology (broker PTY + inject +
51:(`/sptc:live`), spt-core's daemon hosts a Psyche alongside it: the Psyche owns its own
55:profile. (Realized here by the `claude-spt-psyche` runner.)
77:adapter/shell *developer* who is spt-core's nominal target. spt-claude-code serves casual
81:extracted from spt-core's working style. spt-claude-code is its first consumer. Carries
=== SCOPE.md naming/succession ===
11:`claude-spt` adapter) — simultaneously:
36:| Plugin (marketplace) | namespaced `/spt:*` skill *skeletons*, `hooks.json` (call `spt api`), SessionStart bootstrap (installs spt-core if absent), `plugin.json` | `SaberMage/cplugs` | `claude plugin update` (rare structural changes only) | low |
40:- Skills stay **plugin-provided** → keep `/spt:` namespace (user-scope skills can't
41:  namespace: `~/.claude/skills` → bare `/live`, collision-prone). Rejected global-skills.
70:**LOCKED.** ccs = **profile(s) under spt-claude-code** (`claude-spt:glm`,
71:`claude-spt:kimi`, …), NOT its own adapter. It's structurally CC — only launch command +
77:not only bare adapter_name (profile model already supports composite addressing).
100:`spt endpoint run` defaulted to claude-spt. _Operator "9. agree" then "except 9" — deferred 9;
195:- **`claude-spt:deep` profile REMOVED — LOCKED.** It was a placeholder (digest `window_turns=20`
204:  `claude-spt-psyche` turn (seed + each pulse): `--tools Read,Edit,Write --disable-slash-commands


Grounded. The gap is precise: everything except the **cplugs skeleton** already rides `spt adapter update` (manifest, binaries, and skill bodies via file-backed `[strings]`). Only the plugin skeleton — `hooks.json`, wrapper `.sh`, SKILL skeletons, `plugin.json`, bootstrap — updates via the separate `claude plugin update` channel. That's the wall.

Let me verify one load-bearing fact before the first question: does spt-core actually *execute* hooks from the manifest `[hooks]` table, or is it declaration-only (the plugin's hooks.json is the real thing)?


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== spt adapter verbs (hook-run / hook-path?) ==="; spt adapter --help 2>&1 | sed -n '/Commands:/,/Options:/p'
echo "=== any hook-dispatch verb? ==="; spt adapter 2>&1 | grep -iE 'hook' || echo "no hook verb in adapter group"
echo "=== F-001 / hooks-execution finding ==="; grep -niE 'hooks table|declaration-only|conducts? hook|execute.*hook|hooks.json mirror' docs/SPT-CORE-FINDINGS.md adapter/claude-spt.toml | head


=== spt adapter verbs (hook-run / hook-path?) ===
Commands:
  add              Register an adapter from a local path (a dir holding manifest.toml, or the
                   manifest file itself) or from GitHub (--github user/repo, cloned under
                   adapters/_github/). Manifest-first: an invalid manifest registers nothing.
                   Install is the first update — the declared [update] avenue is conducted once
                   after recording
  remove           Soft-deregister: hidden from new-creation/discovery; existing and live instances
                   keep running. The manifest's optional uninstall template is conducted only with
                   --force until quiesce detection lands
  list             List registered adapters (active and soft-deregistered), each followed by its
                   shipped + local profiles as composite options
  version          Print a registered adapter's declared version — the [adapter].version from its
                   manifest. Resolves the option's merged view like the other adapter commands; exit
                   1 if the adapter is not registered
  create-profile   Create (or overwrite) a local profile — a node-local sparse overlay
                   registered beside the adapter that survives adapter add re-registration. The
                   overlay TOML is read from --from <file> or piped stdin (empty = a placeholder
                   profile to populate later with set-string). Refuses a name shadowing a shipped
                   profile, an invalid name, or an overlay that loosens a consent floor — nothing is
                   written unless every check passes
  delete-profile   Delete a local profile. Refuses a shipped profile name (adapter-owned,
                   immutable) and errors if no local file exists
  get-string       Read a [strings] dot-path from an adapter option's merged view
                   (<adapter>[:profile] <key.path>). Resolves through the profile overlay like
                   every other consumer; prints the value (strings raw, else JSON). Exit 1 if the
                   key is unset. Strings are data — never executed
  digest-proof     Prove an adapter's [digest] extractor against a real log sample. Runs the
                   declared extractor over --sample <log> (or the declared source) and prints the
                   parsed contract records, the rendered digest, and every dropped line with its
                   reason — the author-time answer to "spt endpoint digest returns nothing" (no
                   silent empty). Exit 1 if any line drops or nothing parses
  translate-proof  Prove an adapter's [message-idle-translation-binary] against an inbound event.
                   Spawns and feeds the declared translation binary exactly as the daemon does at
                   idle-delivery — sends the init line then the --event envelope and reads back
                   the emitted keystroke-command stream ({key}/{text}/{delay_ms}/{commit}),
                   printed author-readable. This is the EMIT half ONLY: it proves the binary's
                   spawn-feed-emit contract; it does NOT exercise the daemon's atomic PTY apply or
                   controller buffering. Fills {id} and {session_id} into the envelope the same
                   way the daemon does (use --session to pin the session id). Exit 1 if the binary
                   fails to spawn, emits nothing, emits no commit, or emits an unparseable line
  set-string       Set a [strings] dot-path on a local profile (<adapter>:<profile>). Sugar
                   over editing the overlay file; refuses a shipped profile and a bare option (a
                   local target is required — create-profile first)
  update           Update registered adapters that ship from their own GitHub releases: compare each
                   [update] avenue = "gh_release" adapter's latest release version against the
                   installed one and, when newer, fetch the release archive, verify it against the
                   declared signing key if any (else trusting HTTPS + GitHub), and re-register. With
                   no name, sweeps every gh_release adapter; with a name, updates just that one
  use              Set or clear the active-profile pointer — the default <adapter>[:profile] a
                   harness session binds to when no --adapter is given. spt adapter use
                   <adapter>[:profile] points every host binary the adapter declares at it (run
                   once per host binary you support); --clear <adapter|binary> drops the pointer
                   (resolution falls back to the freshest-registered adapter). Never changed by
                   install or update
  help             Print this message or the help of the given subcommand(s)

Options:
=== any hook-dispatch verb? ===
no hook verb in adapter group
=== F-001 / hooks-execution finding ===
adapter/claude-spt.toml:234:# Satisfies REQ-DIST-SESSION-RESUME (declaration-only seam; .toml is unscanned, so the formal


Confirmed the load-bearing fact: **spt-core does not execute hooks** — no hook-dispatch verb exists; the manifest `[hooks]` table is declaration-only, and the plugin's `hooks.json` + wrapper `.sh` (the real hook *logic*) live in the CC plugin dir, which `spt adapter update` cannot touch.

That reframes topic 1. Here's the wall, precisely:

The skeleton's content splits by update-reachability:
- **Skill bodies** (`strings/`) — already ride the adapter `.spt`. ✓ covered by `spt adapter update`.
- **Hook *logic*** (`post-tool-use.sh`, `session-start.sh`, `_common.sh`, …) — real code, lives in the CC plugin dir. ✗ only `claude plugin update`. **This is the churn that bit us this wave.**
- **Hook *wiring* + shells** (`hooks.json`, `plugin.json`, SKILL skeletons, `bootstrap.sh`) — rare churn. ✗ only `claude plugin update`.

So "one command updates everything" is **not reachable purely inside spt-claude-code** — hook logic must physically live where CC loads it (the plugin dir), outside the adapter channel. Closing the gap *requires a new spt-core capability* → a finding to doyle (public-surface-only: we ask, we don't reach in).

That gives the first fork.


AskUserQuestion

Question: What's the target end-state for the skeleton — how 'static' do we want the CC plugin to become? (All three need an spt-core capability we don't have yet = a finding to doyle.)

Choices:
Move the hook wrapper .sh INTO the adapter .spt; plugin hooks.json delegates each event to `sh "$(spt adapter dir claude-spt)/hooks/<event>.sh"`. spt-core ask is SMALL (a verb to print the adapter install dir). `spt adapter update` then covers all hook LOGIC + skill bodies + manifest + binaries. Residual: adding a NEW hook EVENT still edits hooks.json (rare plugin bump).
PLUGIN (static-ish, cplugs)
  hooks.json   -> each event: sh "$(spt adapter dir claude-spt)/hooks/$EV.sh"
  plugin.json, SKILL skeletons, bootstrap.sh
ADAPTER .spt (spt adapter update)
  manifest + 3 binaries + strings/
  hooks/*.sh   <-- REAL logic moves here

spt-core ask: `spt adapter dir <name>`  (print install path)
New hook EVENT -> still a (rare) hooks.json bump
Pre-wire ALL ~27 CC hook events in hooks.json to ONE generic stub that calls a NEW `spt run-hook claude-spt <event>`; spt-core conducts the adapter's handler from the manifest. Plugin becomes truly static-forever — even ADDING a new hook event needs zero plugin republish (it's already wired). Bigger spt-core ask (real hook execution).
PLUGIN (static-FOREVER, cplugs)
  hooks.json   -> all 27 events: spt run-hook claude-spt $EV
  plugin.json, SKILL skeletons, bootstrap.sh
ADAPTER (spt adapter update)
  manifest [hooks.*] = the real handlers spt executes
  + binaries + strings/

spt-core ask: `spt run-hook <name> <event>` (execute hook)
New hook EVENT -> NO plugin bump (already wired)
Keep hook logic in the plugin; don't pursue one-command. Just minimize plugin churn and document the dual channel (`spt adapter update` + occasional `claude plugin update`). No spt-core ask. Rejects the stated goal — listed for completeness.
PLUGIN (cplugs) -- still churns on logic changes
  hooks.json + hooks/*.sh (logic) + plugin.json + skills
ADAPTER (spt adapter update)
  manifest + binaries + strings/

Two commands stay:
  spt adapter update claude-spt   (manifest/bin/strings)
  claude plugin update            (hook logic + structure)


Your questions have been answered: "What's the target end-state for the skeleton — how 'static' do we want the CC plugin to become? (All three need an spt-core capability we don't have yet = a finding to doyle.)"=(no option selected) notes: i like the generic dispatch proposal. it'd likely be more like `spt api run-hook`. i can discuss this separately with doyle. --- is it possible to bake `[claude|ccs] plugin update claude-spt` into this adapter's manifest's update sequence, or have it be something one of the attached binaries runs?. You can now continue with these answers in mind.

Question: What's the target end-state for the skeleton — how 'static' do we want the CC plugin to become? (All three need an spt-core capability we don't have yet = a finding to doyle.)

Choices:
Move the hook wrapper .sh INTO the adapter .spt; plugin hooks.json delegates each event to `sh "$(spt adapter dir claude-spt)/hooks/<event>.sh"`. spt-core ask is SMALL (a verb to print the adapter install dir). `spt adapter update` then covers all hook LOGIC + skill bodies + manifest + binaries. Residual: adding a NEW hook EVENT still edits hooks.json (rare plugin bump).
PLUGIN (static-ish, cplugs)
  hooks.json   -> each event: sh "$(spt adapter dir claude-spt)/hooks/$EV.sh"
  plugin.json, SKILL skeletons, bootstrap.sh
ADAPTER .spt (spt adapter update)
  manifest + 3 binaries + strings/
  hooks/*.sh   <-- REAL logic moves here

spt-core ask: `spt adapter dir <name>`  (print install path)
New hook EVENT -> still a (rare) hooks.json bump
Pre-wire ALL ~27 CC hook events in hooks.json to ONE generic stub that calls a NEW `spt run-hook claude-spt <event>`; spt-core conducts the adapter's handler from the manifest. Plugin becomes truly static-forever — even ADDING a new hook event needs zero plugin republish (it's already wired). Bigger spt-core ask (real hook execution).
PLUGIN (static-FOREVER, cplugs)
  hooks.json   -> all 27 events: spt run-hook claude-spt $EV
  plugin.json, SKILL skeletons, bootstrap.sh
ADAPTER (spt adapter update)
  manifest [hooks.*] = the real handlers spt executes
  + binaries + strings/

spt-core ask: `spt run-hook <name> <event>` (execute hook)
New hook EVENT -> NO plugin bump (already wired)
Keep hook logic in the plugin; don't pursue one-command. Just minimize plugin churn and document the dual channel (`spt adapter update` + occasional `claude plugin update`). No spt-core ask. Rejects the stated goal — listed for completeness.
PLUGIN (cplugs) -- still churns on logic changes
  hooks.json + hooks/*.sh (logic) + plugin.json + skills
ADAPTER (spt adapter update)
  manifest + binaries + strings/

Two commands stay:
  spt adapter update claude-spt   (manifest/bin/strings)
  claude plugin update            (hook logic + structure)


Good — generic dispatch via `spt api run-hook` (you'll take that to doyle). Now your chaining question: can `spt adapter update` also run `claude|ccs plugin update`?

Let me check whether the manifest `[update]` block already supports any post-step.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== schema [update]/Update def ==="; grep -niE 'update|post_|avenue|gh_release|command|after' adapter/manifest.schema.json | head -30


=== schema [update]/Update def ===
4:  "description": "Per-adapter runtime manifest for the spt-core harness contract. Authored as TOML (this schema describes the equivalent data model). A manifest declares only what varies per harness/shell; command templates are opaque strings spt-core never parses. Cross-field invariants (kind<->[shell] agreement, strategy/avenue field requirements) are enforced by spt-core's validate step beyond this schema.",
69:    "update": {
72:          "$ref": "#/$defs/Update"
96:      "description": "`[strings]` — an adapter-authored KV tree of **opaque data** (spt-core\nnever executes a string; command templates live in their own sections\nbehind registration). Dot-path-readable via `spt adapter get-string`, and\nit rides the same leaf-replace profile overlay as the rest of the manifest\n(a shipped or local profile may override base strings). Node-local; no\ncross-node sync. (CONTEXT.md §adapter strings.)",
113:      "description": "`[adapter]` — the manifest header, readable before any update (compat gate).",
160:      "description": "`[hooks.<event>]` — one harness event → the `api` command it fires, the\nstdin fields it maps, and whether it can surface context to the agent.",
164:          "description": "Opaque `api …` command line the harness invokes for this event.",
184:      "description": "`[session]` — the watched-dir keys (`commune_dir`/`signoff_dir`) co-located\nwith the fixed set of `[session.<role>]` command templates.",
250:          "description": "`[session.notif]` — the endpoint-native notification render\n(the `notif_command` seam, REQ-NOTIF-2): an OS toast, a\nGameRobot `alert-symbol`, anything the adapter can run. Spawned\ndetached when a notif surfaces at this endpoint, combinable with the\nagent-surface delivery. Keys spt-core fills: `{notif_id}`,\n`{notif_from}`, `{notif_subnet}`, `{notif_body}`.",
263:      "description": "`[session.<role>]` — one opaque outbound command template plus its spawn\ncontext. Model/tools/flags all live inside `command`, never as fields\n(MANIFEST §session roles). No nested tables here (keeps TOML round-trip\nemission scalar-before-table clean).",
266:        "command": {
267:          "description": "Opaque command line, with `{key}` substitution placeholders.",
303:        "command"
360:        "normalize_command": {
361:          "description": "`locate_normalize` strategy: command normalizing the raw transcript.",
392:      "description": "`[digest]` — the session-digest extractor seam. Reverses M9's\n\"no manifest seam\" stance: the digest gets its **own** adapter-declared\nextractor, distinct from `[history]` (which stays opaque + single-session and\nfeeds the echo-commune verbatim). The extractor maps the harness's **native**\nlog → the published `{role, text, tool, ts}` digest-record contract\n([`spt_term::DigestRecord`]).\n\n**Imperative, not a DSL** (by design): real harness logs are nested\n(one line → many entries, mixed block lists, types to filter) — a flat\ndeclarative map cannot express them, and a map powerful enough is a reinvented\nlanguage. So the extractor is an opaque command spt-core never parses, exactly\nlike every other manifest template.\n\n**Source.** By default the extractor reads the **same files as `[history]`**\n(the `locate_template`; DRY). An adapter may override with `source` (the\nown-source escape hatch). `api digest-entry` push remains the always-available\nfallback for a log-less adapter (which declares no `[digest]` at all).\n\n**Presentation.** `window_turns`, `arg_truncation`, and `sprint_collapse` are\nadapter-declared **defaults** any consumer may override at pull/subscribe;\nspt-core ships fallback defaults ([`spt_term::DigestConfig`]) when absent. The\nfixed \"~3 turns\" is no longer an spt-core requirement.",
396:          "description": "Opaque extractor command: native harness log → the `{role,text,tool,ts}`\ncontract (one JSON record per output line). `{key}` substitution applies\n(e.g. `{session_id}`, and `{source}` for the resolved log path).",
485:          "description": "Discovered after spawn (process-tree / wrapper handoff).",
487:          "const": "post_spawn"
496:    "Update": {
497:      "description": "`[update]` — adapter self-update directive (parsed in M2a; conducted in M3).",
500:        "avenue": {
501:          "$ref": "#/$defs/UpdateAvenue"
503:        "command": {
504:          "description": "`delegated` avenue: the command spt-core delegates to.",
511:          "description": "`file_pull` / `gh_release` avenue: source repo. For `gh_release` this is\nthe `user/repo` whose **GitHub releases** the adapter ships updates from.",
518:          "description": "`file_pull` avenue: path selector.",
525:          "description": "`gh_release` avenue: the release **asset** name to fetch (the adapter\n`.spt` archive). Absent ⇒ the default `adapter.spt`, matching the\n`spt adapter add --release` acquisition primitive (REQ-INSTALL-9). Not\napplicable to `delegated` / `file_pull`.",
532:          "description": "The adapter's Ed25519 **content-signing public key** (64 hex chars / 32\nbytes). spt-core verifies a pulled payload against this per-adapter key\nbefore applying it (REQ-UPD-5 adapter content signing) — the\nadapter author signs their own releases; spt-core's release key stays\nscoped to spt-core. **Required for `file_pull`** (there are bytes to\nverify); **optional for `gh_release`** (absent ⇒ HTTPS+GitHub\nfirst-acquisition trust, the same trust `spt adapter add --release` and\nthe installer first-fetch place; present ⇒ the fetched `.spt` is verified\nfail-closed against this key, REQ-UPD-9); not applicable to `delegated`\n(opaque updater).",
539:          "description": "`delegated` avenue: the adapter attests its own updater verifies the\ncontent it installs (e.g. `claude.exe plugin update` checks its own\nsignatures). spt-core cannot see a delegated updater's bytes, so it\ndelegates the trust **only** when this is set; an unattested delegated\nupdate is skipped as unverifiable (REQ-UPD-5).",


Big find — spt-core **already** has the exact seam you're describing. The schema's `[update]` supports a **`delegated`** avenue, and its canonical example *is* `claude.exe plugin update` (schema line 539: *"the adapter attests its own updater verifies the content it installs, e.g. `claude.exe plugin update` checks its own signatures"*). There's an `attest_delegated` flag so spt-core trusts the delegated updater fail-closed.

So the answer to your question: **yes — via the manifest, not a binary.** A binary *could* shell `claude plugin update`, but it's the wrong seam (binaries have scoped lifecycle roles; spt-core wouldn't invoke one at update time). The manifest `[update]` delegated command is the sanctioned, spt-conducted hook.

**The catch** (and the second doyle ask): `[update]` takes **one** `avenue` — `gh_release` **XOR** `delegated`. Today we need `gh_release` (it pulls the adapter `.spt` = manifest + binaries + strings). `delegated` would instead run `claude plugin update sptc` — which refreshes the *cplugs skeleton*, a **different payload**. To get **both** in one `spt adapter update`, you'd ask doyle for a **composite**: `gh_release` + a `post_update` delegated command. That's a clean, agnostic ask (spt-core runs an opaque command the manifest declares; it never knows it's CC).

Two more caveats on the chained `claude plugin update`:
- It updates the plugin **cache**; the running session needs a **restart** to load new hooks/skills. Fine for rare structural changes, not instant.
- It needs the right CLI (`claude` vs `ccs`) on PATH — the command must detect which manages the install.

Here's the thing though — **with generic dispatch (your pick), the skeleton goes nearly static**, so `claude plugin update` becomes a ~never operation. That shrinks what the composite even buys. The one thing generic dispatch does **not** eliminate:


AskUserQuestion

Question: After generic hook-dispatch lands, the ONE remaining thing that still forces a plugin (cplugs) bump is adding a NEW SKILL — CC surfaces `/spt:<skill>` only if a SKILL.md for it physically exists in the plugin dir (it enumerates files at load; bodies already ride adapter strings). How do we want to handle that residual?

Choices:
Ship SKILL.md skeletons NOW for every planned skill (current + foreseeable). Adding a skill becomes rare; bodies still ride adapter strings via UPS. Combined with generic dispatch, the skeleton is effectively static-forever WITHOUT needing the composite-update ask. Simplest; the composite chaining becomes optional polish.
PLUGIN (static-forever, install once)
  hooks.json   -> generic dispatch (all events)
  skills/{ready,live,send,commune,signoff,setup,
          subnet,list-agents,version,force-stop,
          ...all planned...}/SKILL.md  (skeletons)
  plugin.json, bootstrap.sh

New skill = rare; pre-declared headroom avoids it
`spt adapter update` covers ALL real churn
Composite plugin-update: NOT needed
Also pursue the doyle ask for a composite `[update]` (gh_release + delegated `claude plugin update sptc`), so even adding a skill / structural change is picked up by the one `spt adapter update` command (after a session restart). Belt-and-suspenders; covers everything but adds a second spt-core dependency + the restart caveat.
manifest [update]
  avenue = gh_release        (pull .spt: manifest/bin/strings)
  post_update = delegated    (NEW ask)
    command = "<claude|ccs> plugin update spt"
    attest_delegated = true

spt adapter update claude-spt:
  1. pull + re-register adapter .spt
  2. run plugin-update (skeleton)  -> live after restart

Needs doyle ask #2 (composite update)
Pre-declare a generous skill set NOW (immediate, no dependency) AND file the composite-update ask with doyle as the long-term guarantee. Static skeleton today; one-command-covers-truly-everything once the composite lands.
NOW:  generous skill skeleton set + generic dispatch
      -> skeleton static, one-command for all churn
LATER: composite [update] (doyle ask #2)
      -> even new skills/structural via one command

Layered: ship the static skeleton first,
add the composite guarantee when doyle delivers


Your questions have been answered: "After generic hook-dispatch lands, the ONE remaining thing that still forces a plugin (cplugs) bump is adding a NEW SKILL — CC surfaces `/spt:<skill>` only if a SKILL.md for it physically exists in the plugin dir (it enumerates files at load; bodies already ride adapter strings). How do we want to handle that residual?"=(no option selected) notes: -plugins can be reloaded without restarting via /reload-plugin -disagree that the skeleton goes nearly static, as there might be need for refinements to skill docs over time. --- i want to consider whether the "claude code marketplace plugin" is the right format at all. maybe the claude interface for claude-spt would be better served as raw skills, similar to how https://github.com/mattpocock/skills or https://github.com/gsd-build/get-shit-done does it. which of those two is the more eloquent example to use as a baseline? --- want to also discuss a zero-friction installation path. i.e. the spt-claude-code README.md provides users a copy-paste prompt for them to pass onto a claude code agent for claude-spt installation. agent takes care of `spt adapter add`, `claude plugin update`, and potentially `ccs plugin update` all in one sequenced Bash tool call. --- the installation method **and** the update method should feel harmonious, such that the user wouldn't feel like they need to search for the update lever after installing. at the same time, update should be possible without the user needing to be inside an endpoint session terminal (leads us back to CLI). thoughts/suggestions?. You can now continue with these answers in mind.

Question: After generic hook-dispatch lands, the ONE remaining thing that still forces a plugin (cplugs) bump is adding a NEW SKILL — CC surfaces `/spt:<skill>` only if a SKILL.md for it physically exists in the plugin dir (it enumerates files at load; bodies already ride adapter strings). How do we want to handle that residual?

Choices:
Ship SKILL.md skeletons NOW for every planned skill (current + foreseeable). Adding a skill becomes rare; bodies still ride adapter strings via UPS. Combined with generic dispatch, the skeleton is effectively static-forever WITHOUT needing the composite-update ask. Simplest; the composite chaining becomes optional polish.
PLUGIN (static-forever, install once)
  hooks.json   -> generic dispatch (all events)
  skills/{ready,live,send,commune,signoff,setup,
          subnet,list-agents,version,force-stop,
          ...all planned...}/SKILL.md  (skeletons)
  plugin.json, bootstrap.sh

New skill = rare; pre-declared headroom avoids it
`spt adapter update` covers ALL real churn
Composite plugin-update: NOT needed
Also pursue the doyle ask for a composite `[update]` (gh_release + delegated `claude plugin update sptc`), so even adding a skill / structural change is picked up by the one `spt adapter update` command (after a session restart). Belt-and-suspenders; covers everything but adds a second spt-core dependency + the restart caveat.
manifest [update]
  avenue = gh_release        (pull .spt: manifest/bin/strings)
  post_update = delegated    (NEW ask)
    command = "<claude|ccs> plugin update spt"
    attest_delegated = true

spt adapter update claude-spt:
  1. pull + re-register adapter .spt
  2. run plugin-update (skeleton)  -> live after restart

Needs doyle ask #2 (composite update)
Pre-declare a generous skill set NOW (immediate, no dependency) AND file the composite-update ask with doyle as the long-term guarantee. Static skeleton today; one-command-covers-truly-everything once the composite lands.
NOW:  generous skill skeleton set + generic dispatch
      -> skeleton static, one-command for all churn
LATER: composite [update] (doyle ask #2)
      -> even new skills/structural via one command

Layered: ship the static skeleton first,
add the composite guarantee when doyle delivers


Two corrections accepted — and one sharpens the picture:

1. `/reload-plugin` avoids a full restart — noted, the chained-update caveat softens.
2. You're **right** that the skeleton doesn't go static — and it's worse than "skill docs refine over time": this very wave made `commune`/`send`/`signoff` SKILL.md **full-fat** (real body in the plugin, *not* injected from adapter strings — they're deliberately omitted from `[strings.skills]`). So those bodies churn *in the plugin*, on the `claude plugin update` channel. My "bodies ride strings" claim only holds for the injected skills (setup/ready/version/etc.). That inconsistency is itself a smell — and it feeds straight into your format question.

Before I compare the two baselines, the mandatory flag: **ADR-0001 already rejected raw user-scope skills** — explicitly, because they "lose the `/spt:` namespace (bare `/live`, collision-prone)." Reopening the format means reopening that. Let me check how each baseline actually handles invocation/namespacing before I weigh in.


ToolSearch

Question: select:WebFetch,WebSearch


Question: select:WebFetch,WebSearch


WebFetch

Prompt: How are skills structured and installed in this repo? Specifically: are these Claude Code "skills" (the SKILL.md / Skill tool kind) or slash commands or a plugin? How does a user install them — copy into ~/.claude/skills, a plugin marketplace, or something else? How are skills invoked (by the model via the Skill tool, or as /namespace:command slash commands)? Is there any namespacing/prefix? Quote the install instructions and the directory structure.


Prompt: How are skills/commands structured and installed in this repo? Are these Claude Code "skills" (SKILL.md / Skill tool kind), slash commands, or a plugin/marketplace? How does a user install them (copy into ~/.claude, a plugin marketplace add, an installer script)? How are they invoked — by the model via the Skill tool, or as /namespace:command slash commands? Is there namespacing (e.g. /gsd:...)? How does updating work? Quote the install/update instructions and the directory structure.


# Skills Structure and Installation

## Installation Method

Users install these skills via the **skills.sh installer**, a command-line tool:

> "npx skills@latest add mattpocock/skills"

This is not a manual file copy or plugin marketplace approach—it's an automated installation system that guides users through setup.

## Skill Type and Invocation

These are **Claude Code "skills"** that function as **slash commands**. The repository references them with forward slashes (e.g., `/grill-me`, `/tdd`, `/ask-matt`), indicating they're invoked as commands within Claude Code rather than through a generic Skill tool interface.

## Directory Structure

The repo organizes skills by category:

```
skills/
├── engineering/
│   ├── ask-matt/
│   ├── grill-with-docs/
│   ├── tdd/
│   └── [other engineering skills]/
├── productivity/
│   ├── grill-me/
│   ├── handoff/
│   └── [other productivity skills]/
└── misc/
    └── [miscellaneous skills]/
```

Each skill contains a `SKILL.md` file documenting its purpose and behavior.

## Namespacing

Skills use **category-based organization** (engineering, productivity, misc) but appear to be invoked with simple slash commands rather than namespaced prefixes like `/engineering:grill-me`.


I cannot provide a comprehensive answer based on the webpage content provided. The page shows a GitHub repository overview for "get-shit-done," but it displays multiple error messages ("Uh oh! There was an error while loading") that prevent access to critical information.

The visible content confirms:
- The project is a "meta-prompting, context engineering and spec-driven development system for Claude Code"
- It uses JavaScript (73.1%) and TypeScript (26.5%)
- The repository structure includes folders like `agents/`, `commands/gsd/`, `sdk/`, and `docs/`

However, the actual documentation explaining installation, invocation methods, and command structure is inaccessible due to loading errors. To answer your questions about skill installation patterns, namespacing conventions, and update mechanisms, I would need access to files like README.md, AGENTS.md, or CONTRIBUTING.md—which aren't fully rendered in this screenshot.

**Recommendation:** Visit the repository's documentation directly or check the referenced migration notice pointing to https://github.com/open-gsd/gsd-core for current implementation details.


`commands/gsd/` in GSD is the key signal — CC slash **commands** in a subdir, not a marketplace plugin. That's the linchpin: ADR-0001 rejected user-scope **skills** for losing the namespace, but it may not have weighed **commands** (`commands/<ns>/`), which is how GSD gets `/gsd:*`. Let me verify that namespacing mechanism precisely — it decides whether raw-format can keep `/spt:*` at all.


Prompt: How are the /gsd:* slash commands delivered and installed? Is it a Claude Code plugin (with plugin.json + a marketplace) or raw slash command files under .claude/commands/gsd/? How does the user install and update (installer script, npx, plugin marketplace add)? Does it also ship hooks, and if so where do the hooks live (plugin hooks.json or settings.json)? How is the /gsd: namespace achieved? Quote install/update commands and the directory layout.


WebSearch

Question: Claude Code slash commands subdirectory namespacing .claude/commands/sub/command invoke syntax


# GSD Core: Command Installation & Architecture

Based on the GitHub repository content, here's how GSD Core delivers its slash commands:

## Installation Method

The primary installation uses **npx**:

```
npx @opengsd/gsd-core@latest
```

The repository states: "The installer prompts for your runtime (Claude Code, OpenCode, Gemini CLI, Kimi CLI, Kilo, Codex, Copilot, Cursor, Windsurf, and more) and whether to install globally or locally. The installer is required for cross-runtime compatibility — do not copy files from `agents/` or `commands/` directly."

## Directory Structure

The codebase includes these relevant directories:
- `commands/gsd/` — contains the slash command definitions
- `.claude-plugin/` — appears to support plugin configuration
- `hooks/` — contains hook implementations
- `.githooks/` — git hooks directory

## Key Limitation

The documentation explicitly warns against manual installation: users **must use the installer script** rather than copying files directly. This suggests the installer performs runtime-specific transformations to achieve `/gsd:` namespace compatibility across different AI coding environments.

The repository does not publicly document the exact mechanism for creating the `/gsd:` namespace prefix or whether it uses Claude's native plugin.json marketplace versus custom command registration per runtime.


Web search results for query: "Claude Code slash commands subdirectory namespacing .claude/commands/sub/command invoke syntax"

Links: [{"title":"Slash Commands in the SDK - Claude Code Docs","url":"https://code.claude.com/docs/en/agent-sdk/slash-commands"},{"title":"# [BUG] Slash command namespacing with subdirectories not working as documented · Issue #2422 · anthropics/claude-code","url":"https://github.com/anthropics/claude-code/issues/2422"},{"title":"Custom Slash Commands - Augment","url":"https://docs.augmentcode.com/cli/custom-commands"},{"title":"Claude Code Commands | Developing with AI Tools | Steve Kinney","url":"https://stevekinney.com/courses/ai-development/claude-code-commands"},{"title":"Thought Eddies | Claude Code Custom Slash Commands Hierarchy","url":"https://www.danielcorin.com/til/anthropic/custom-slash-commands-hierarchy/"},{"title":"Slash Commands in the SDK - Claude API Docs","url":"https://platform.claude.com/docs/en/agent-sdk/slash-commands"},{"title":"Claude Code Slash Commands (2026): How to Create Custom Commands That Stick | Claude Directory","url":"https://www.claudedirectory.org/blog/claude-code-slash-commands-guide"},{"title":"How to Add Custom Slash Commands in Claude Code","url":"https://aiengineerguide.com/til/claude-code-custom-command/"},{"title":"Slash Commands - .claude Directory","url":"https://dotclaude.com/commands"},{"title":"Claude Code custom slash commands: the /commands directory you're probably not using - DEV Community","url":"https://dev.to/subprime2010/claude-code-custom-slash-commands-the-commands-directory-youre-probably-not-using-5a18"}]

Based on the search results, here's what I found about Claude Code slash commands subdirectory namespacing and invoke syntax:

## Documented Namespacing Syntax

Subdirectories become namespaces—for example, .claude/commands/release/notes.md becomes /release:notes. Project-scoped commands can be accessed using the /project:command_name or /project:category:command_name syntax.

The documented structure for namespaced commands is: a file at .claude/commands/frontend/component.md creates the command /project:frontend:component.

## Important Note: Implementation Issues

However, there's a significant caveat: According to the official documentation, this namespacing feature should work in practice, but commands placed in subdirectories are not accessible using the documented namespace syntax. The subdirectory appears in the command description but doesn't affect the command name itself.

## Current Behavior

The subdirectory appears in the command description but doesn't affect the command name itself, suggesting that while subdirectories are supported for organizational purposes, they don't currently create true namespaced command invocation as documented.

This appears to be a known issue where the documented namespacing syntax (`/project:namespace:command`) isn't fully functional in practice, despite being officially documented.

REMINDER: You MUST include the sources above in your response to the user using markdown hyperlinks.

Question: Claude Code slash commands subdirectory namespacing .claude/commands/sub/command invoke syntax

Custom Slash Commands - Augment

Slash Commands - .claude Directory


Research settles the format question — and it cuts against the raw-skills instinct. Key findings:

- **Raw command namespacing is broken.** CC docs say `commands/release/notes.md` → `/release:notes`, but there's an open bug ([anthropics/claude-code#2422](https://github.com/anthropics/claude-code/issues/2422)): subdirectories show in the *description* but **don't actually namespace the invocation**. So raw commands can't reliably give `/spt:*`.
- **mattpocock/skills** = flat skills, **no namespace** (`/grill-me`, not `/eng:grill-me`), installed via `npx skills@latest add`. No hooks. Too thin for a hook-heavy adapter.
- **gsd-core is itself a plugin** — it has `.claude-plugin/` **and** `hooks/` **and** `commands/gsd/`, delivered by a cross-runtime installer (`npx @opengsd/gsd-core`, *"do not copy files directly"*). It does **not** prove raw-files-get-namespace; it proves the opposite — it wraps a plugin in a smart installer.

**Verdict on your question:** **gsd-core is the more eloquent baseline** — but its eloquence is the **installer + cross-runtime onboarding**, *not* going raw. mattpocock is too simple (no namespace, no hooks).

**So the format conclusion (challenging the raw-skills reopen):** keep the **plugin**. It's still the only reliable `/spt:*` + the only clean hooks bundle (claude-spt is hook-heavy — SessionStart/UPS/Stop/PostToolUse/Subagent\*; raw skills carry none). ADR-0001's rejection of raw skills holds, and #2422 *strengthens* it. What we **borrow from GSD** is the installer/onboarding UX — which is exactly your threads #4 (zero-friction install) and #5 (harmonious update).

One correction that bites here: the full-fat skills (`commune`/`send`/`signoff`) are full-fat **on purpose** — they're invoked *reactively* (the agent communes without you typing `/sptc:commune`), so UPS-injection (which fires only on the typed slash-command) can't deliver them; their bodies must be always-loaded. That's a real reason the plugin keeps churning bodies — unless we move reactive bodies to SessionStart-injection from adapter strings. (Flagging; not deciding yet.)

Now the install/update harmony fork:


AskUserQuestion

Question: Given we keep the plugin: what's the shape of the install + update levers so they feel harmonious AND update works outside an endpoint session (pure CLI)?

Choices:
Two symmetric CLI verbs are the real levers: install = `spt adapter add --release ...`, update = `spt adapter update claude-spt` — BOTH conduct the plugin side via the delegated [update] avenue (the doyle composite ask). The README copy-paste prompt is just a friendly agent-skin over the SAME install command. Update is pure CLI (works outside any session); install/update mirror each other.
INSTALL (either path, same effect):
  CLI:    spt adapter add --release SaberMage/spt-claude-code
  PROMPT: README -> agent runs the CLI in one Bash call
UPDATE (pure CLI, outside any session):
  spt adapter update claude-spt
    -> pulls .spt (manifest/bin/strings)
    -> delegated: <claude|ccs> plugin update spt + reload

Harmony: add <-> update, same verb family; prompt is a skin
The README copy-paste prompt is THE primary path for both install and update: one prompt installs (adapter add + plugin install + ccs variant, sequenced Bash), a second prompt updates (adapter update + plugin update + reload). Most friendly for casual users, but update needs a CC agent/session — conflicts with your 'update outside a session' requirement unless paired with a CLI fallback.
INSTALL: paste prompt -> agent:
  spt adapter add --release ... && <cli> plugin install ...
UPDATE: paste prompt -> agent:
  spt adapter update claude-spt && <cli> plugin update ... && /reload-plugin

(+ CLI fallback documented for outside-session update)
Friendliest; update-in-session is the default modality
Drop the agent-prompt idea; document plain CLI install + update commands (with the delegated composite so one command does both sides). Simplest to reason about, most 'developer'-shaped — but loses the zero-friction casual-user onboarding you wanted in thread #4.
INSTALL: spt adapter add --release SaberMage/spt-claude-code
        (+ first-run SessionStart bootstrap covers spt-core)
UPDATE: spt adapter update claude-spt (delegated does both)

No README prompt; pure CLI both ways
Minimal, but no casual-user agent onboarding


Your questions have been answered: "Given we keep the plugin: what's the shape of the install + update levers so they feel harmonious AND update works outside an endpoint session (pure CLI)?"=(no option selected) notes: symmetric CLI + prompt skin is good. IIRC, after a plugin is first installed, spt-core also runs its update flow. --- we need to keep in mind that claude-spt might be the first avenue for users to use spt-core. so the install path on the readme might need to be long platform-specific command chains (so windows command prompt, windows powershell, and linux bash) which involve: 1. checking if spt-core is installed 2. installing it if not 3. running `spt adapter add`. --> from there, the adapter update command(s) need to 1. check if the cplugs marketplace is installed 1a. install it if not 2. check if sptc is installed, 2a. if not, run `claude plugin add sptc@cplugs`, 2b. if sptc is installed, run `claude plugin update sptc` 3. use the `[update]` message (an adapter manifest field) to inform the user how to proceed, i.e. "**claude-spt** is installed! To make the most of it, run the `spt` command from any project folder to **create a live agent endpoint**. You can also `/reload-plugins` from any running Claude Code session, then spin up a more basic endpoint using the `/sptc:live` skill." --- quick and simple side question: what tool did you use to prompt me about "Install/update lever" while providing examples for each point? it doesn't seem like normal old `AskUserQuestion`.. You can now continue with these answers in mind.

Question: Given we keep the plugin: what's the shape of the install + update levers so they feel harmonious AND update works outside an endpoint session (pure CLI)?

Choices:
Two symmetric CLI verbs are the real levers: install = `spt adapter add --release ...`, update = `spt adapter update claude-spt` — BOTH conduct the plugin side via the delegated [update] avenue (the doyle composite ask). The README copy-paste prompt is just a friendly agent-skin over the SAME install command. Update is pure CLI (works outside any session); install/update mirror each other.
INSTALL (either path, same effect):
  CLI:    spt adapter add --release SaberMage/spt-claude-code
  PROMPT: README -> agent runs the CLI in one Bash call
UPDATE (pure CLI, outside any session):
  spt adapter update claude-spt
    -> pulls .spt (manifest/bin/strings)
    -> delegated: <claude|ccs> plugin update spt + reload

Harmony: add <-> update, same verb family; prompt is a skin
The README copy-paste prompt is THE primary path for both install and update: one prompt installs (adapter add + plugin install + ccs variant, sequenced Bash), a second prompt updates (adapter update + plugin update + reload). Most friendly for casual users, but update needs a CC agent/session — conflicts with your 'update outside a session' requirement unless paired with a CLI fallback.
INSTALL: paste prompt -> agent:
  spt adapter add --release ... && <cli> plugin install ...
UPDATE: paste prompt -> agent:
  spt adapter update claude-spt && <cli> plugin update ... && /reload-plugin

(+ CLI fallback documented for outside-session update)
Friendliest; update-in-session is the default modality
Drop the agent-prompt idea; document plain CLI install + update commands (with the delegated composite so one command does both sides). Simplest to reason about, most 'developer'-shaped — but loses the zero-friction casual-user onboarding you wanted in thread #4.
INSTALL: spt adapter add --release SaberMage/spt-claude-code
        (+ first-run SessionStart bootstrap covers spt-core)
UPDATE: spt adapter update claude-spt (delegated does both)

No README prompt; pure CLI both ways
Minimal, but no casual-user agent onboarding


Quick side answer first: that was **`AskUserQuestion`** — the richer rendering comes from an optional **`preview`** field on each option. When any option has a `preview`, the UI switches to a side-by-side layout (vertical option list left, monospace preview box right). Same tool, just the preview field populated with the ASCII mockups.

**Topic 1 — wrapping the install/update flow** (your detailed sequence is right; three recommendations to lock it):

1. **The post-install/update message → `[strings.notice]`, not a new `[update]` field.** That table already exists (`[strings.notice].not_installed`). Add `installed` / `updated` keys; the prompt-skin and the plugin-sync step surface them via `spt adapter get-string`. Bonus: the message text then rides the `.spt` (adapter-updatable) and needs **no doyle ask** — keeps the schema-change surface minimal.
2. **The delegated plugin-lifecycle logic ships *inside* the `.spt`** as a script (e.g. `plugin-sync.sh`): check cplugs marketplace → add if missing → `claude plugin add sptc@cplugs` or `update sptc` → `/reload-plugins` → print the notice. The delegated `[update]` command just runs `sh "$(adapter-dir)/plugin-sync.sh"`. Avoids chicken-and-egg (the script arrives with the `.spt`, before the plugin exists) and stays adapter-updatable.
3. **The one firm doyle ask = composite `[update]`**: `gh_release` (pull the `.spt`) **+** a delegated post-step (run plugin-sync). Today `[update]` is one avenue (gh_release XOR delegated). Since *"install is the first update"*, a composite means **`spt adapter add` itself bootstraps the plugin** — exactly the harmony you want. (The platform-specific README chains — cmd/pwsh/bash, check-install-spt-core then `adapter add` — sit *above* this; spt-releases ships per-platform install scripts the chains call.)

That ties off topic 1. Pivoting to **topic 2 (naming)** — and it hits a hard wall worth stating before we choose:

**The adapter can *not* be named `spt`** — that's spt-core's own CLI/identity (`spt adapter update spt` would read as "spt updates spt"). And the plugin *wants* `/spt:*` (your succession plan, gated on retiring legacy owl). So **true one-token unification is impossible** — the user-facing skill prefix wants `spt`, the adapter id can't be `spt`. The realistic target is **3 names → 2**, with a crisp domain rule.


Question: Given the adapter can't be `spt` but the skills want `/spt:*` — what's the unification rule? (cc launcher shortcut stays `cc` either way.)

Choices:
spt-core side (repo + adapter) = `claude-spt`; CC-plugin side (plugin + skills) = `spt` via the planned succession (gated on retiring owl; `sptc` until then). Rename the repo spt-claude-code -> claude-spt so `--release SaberMage/claude-spt` matches `adapter update claude-spt`. End state: 2 names, each with a clear domain.
spt-core side (one token: claude-spt)
  repo    : SaberMage/claude-spt   (renamed)
  adapter : claude-spt            (unchanged)
  install : spt adapter add --release SaberMage/claude-spt
  update  : spt adapter update claude-spt
CC side (one token: spt, post-succession)
  plugin  : spt   (sptc until owl retires)
  skills  : /spt:commune, /spt:live, ...
launcher : cc-<id>

COST: repo rename changes the install dir
  (adapters/_github/SaberMage-claude-spt) -> re-add needed
Full unification on `claude-spt` EVERYWHERE — repo, adapter, plugin, and skills (`/claude-spt:commune`). Abandons the /spt:* succession goal. Maximally consistent, zero ambiguity, no owl-retirement dependency — but the most-typed surface (skill prefix) becomes long.
ONE token everywhere: claude-spt
  repo    : SaberMage/claude-spt
  adapter : claude-spt
  plugin  : claude-spt
  skills  : /claude-spt:commune, /claude-spt:live
  install : spt adapter add --release SaberMage/claude-spt
  update  : spt adapter update claude-spt
launcher : cc-<id>

No succession, no owl dependency
CON: /claude-spt:* is verbose (most-typed surface)
Unify on the short `sptc` everywhere — repo, adapter, plugin, skills (`/sptc:*`, already current). Shortest, no owl dependency. But `sptc` is cryptic to a newcomer, and it abandons the cleaner /spt:* succession the project already planned.
ONE token everywhere: sptc
  repo    : SaberMage/sptc
  adapter : sptc
  plugin  : sptc
  skills  : /sptc:commune, /sptc:live   (unchanged)
  install : spt adapter add --release SaberMage/sptc
  update  : spt adapter update sptc
launcher : cc-<id>

Shortest; no owl dependency
CON: 'sptc' cryptic; drops the planned /spt:* succession