1	# Release runbook
2
3	> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
4	> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
5	> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
6	> thin adapter; the heavy signed-release machinery lives in spt-core, not here.
7
8	<!-- [<doc>->REQ-EXAMPLE-REL] (illustration; escaped per docs/TRACEABILITY.md) -->
9
10	## Publish targets
11
12	An spt-claude-code release lands in **two places**, by volatility:
13
14	1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced
15	   `/spt:*` skill skeletons, `hooks.json`, the SessionStart bootstrap, `plugin.json`). This
16	   is low-churn: it only changes on **structural** plugin changes, not on logic/instruction
17	   updates.
18	2. **The spt-core adapter registry** — the volatile bulk: the CC adapter **manifest**
19	   (`[digest]` extractor, profiles, strings, hints) **+ binary**. This publish is
20	   **spt-core-conducted** (the same framework that conducts adapter updates), not a plugin
21	   file push.
22
23	## One-time setup
24
25	No signing-key ceremony in this project. **Signing is delegated to the `spt` binary**, which
26	handles adapter manifest/binary integrity through spt-core's own signed-update framework. Keep
27	no release keys here. The only setup is marketplace/registry publish credentials for the two
28	targets above (a `cplugs` push token + whatever the spt-core adapter-registry publish step
29	requires).
30
31	## Per release
32
33	1. **Bump** the version (the plugin `plugin.json` version and/or the adapter manifest version —
34	   note these are distinct numbers; see Notes) if needed; **regenerate generated docs**
35	   (the mdBook `docs-site/` build + `llms.txt` / schema / CLI-help exports); land everything
36	   green.
37	2. **Write the user-facing changelog** — add a `## [<version>] - <date>` section at the top of
38	   `CHANGELOG.md` with **Added / Changed / Fixed** subsections. This section becomes the public
39	   release body verbatim, so it is the changelog every user reads. Rules:
40	   - **User-facing UX only.** What a person using the product notices or does differently. Name
41	     the actual commands / flags / surfaces they touch (`/spt:*` skills, `cc`, `/spt:setup`).
42	   - **No internal lingo** — no requirement ids, internal module names, commit hashes, or
43	     milestone / hazard codes. A reader who has never seen the source must understand every line.
44	   - **Flag breaking changes** prominently under Changed.
45	   - The release **fails loudly** if the tagged version has no `## [<version>]` section — the
46	     changelog is not optional.
47	3. **Tag**: `git tag <vX.Y.Z> && git push origin <vX.Y.Z>`. This triggers:
48	   - the **docs-publish workflow** — build `docs-site/` and publish to GitHub Pages (drift-gated;
49	     see `docs/DOCS-STRATEGY.md`).
50	4. **Publish** (light — signing delegated to `spt`):
51	   - **Skeleton plugin** → push to the `SaberMage/cplugs` marketplace **only when the plugin
52	     skeleton structurally changed** (new/removed skill skeleton, hook wiring, bootstrap, or
53	     `plugin.json`). Most releases do not bump the marketplace.
54	   - **Manifest + binary** → publish through the **spt-core adapter registry**; the `spt`
55	     binary performs signing/integrity as part of spt-core's adapter-update framework. You do
56	     **not** run a separate signing step.
57
58	## Update path (dual, kept in sync)
59
60	Users receive updates two ways, and both must stay consistent:
61	- **File-pull (the real channel)** — spt-core conducts manifest + binary updates seamlessly;
62	  this is how logic and instruction churn reaches users. Version-of-truth = the
63	  manifest/binary version spt-core tracks (`spt adapter list` / `/spt:version`).
64	- **`claude plugin update` (cautionary, skeleton sync)** — only resyncs the low-churn
65	  marketplace skeleton on the rare structural bump. It is NOT the version-of-truth.
66
67	## Notes
68
69	- **Distinct numbers — never conflate them:** the **plugin (marketplace) version** (in
70	  `plugin.json`, bumps rarely) and the **adapter manifest/binary version** (spt-core-tracked,
71	  the user-visible version-of-truth). They move on independent schedules — the plugin can sit
72	  static across many manifest updates.
73	- **Trust model:** adapter manifest/binary integrity is the `spt` binary's responsibility via
74	  spt-core's signed-update framework — spt-claude-code defines no separate trust ceremony.
75	  (Claude Code has no plugin-file integrity check, which is why the skeleton plugin is safe to
76	  in-place-edit; only old version dirs orphan on update and are GC'd after 7 days.)
77

# Release runbook

> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
> thin adapter; the heavy signed-release machinery lives in spt-core, not here.

<!-- [<doc>->REQ-EXAMPLE-REL] (illustration; escaped per docs/TRACEABILITY.md) -->

## Publish targets

An spt-claude-code release lands in **two places**, by volatility:

1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced
   `/spt:*` skill skeletons, `hooks.json`, the SessionStart bootstrap, `plugin.json`). This
   is low-churn: it only changes on **structural** plugin changes, not on logic/instruction
   updates.
2. **The spt-core adapter registry** — the volatile bulk: the CC adapter **manifest**
   (`[digest]` extractor, profiles, strings, hints) **+ binary**. This publish is
   **spt-core-conducted** (the same framework that conducts adapter updates), not a plugin
   file push.

## One-time setup

No signing-key ceremony in this project. **Signing is delegated to the `spt` binary**, which
handles adapter manifest/binary integrity through spt-core's own signed-update framework. Keep
no release keys here. The only setup is marketplace/registry publish credentials for the two
targets above (a `cplugs` push token + whatever the spt-core adapter-registry publish step
requires).

## Per release

1. **Bump** the version (the plugin `plugin.json` version and/or the adapter manifest version —
   note these are distinct numbers; see Notes) if needed; **regenerate generated docs**
   (the mdBook `docs-site/` build + `llms.txt` / schema / CLI-help exports); land everything
   green.
2. **Write the user-facing changelog** — add a `## [<version>] - <date>` section at the top of
   `CHANGELOG.md` with **Added / Changed / Fixed** subsections. This section becomes the public
   release body verbatim, so it is the changelog every user reads. Rules:
   - **User-facing UX only.** What a person using the product notices or does differently. Name
     the actual commands / flags / surfaces they touch (`/spt:*` skills, `cc`, `/spt:setup`).
   - **No internal lingo** — no requirement ids, internal module names, commit hashes, or
     milestone / hazard codes. A reader who has never seen the source must understand every line.
   - **Flag breaking changes** prominently under Changed.
   - The release **fails loudly** if the tagged version has no `## [<version>]` section — the
     changelog is not optional.
3. **Tag**: `git tag <vX.Y.Z> && git push origin <vX.Y.Z>`. This triggers:
   - the **docs-publish workflow** — build `docs-site/` and publish to GitHub Pages (drift-gated;
     see `docs/DOCS-STRATEGY.md`).
4. **Publish** (light — signing delegated to `spt`):
   - **Skeleton plugin** → push to the `SaberMage/cplugs` marketplace **only when the plugin
     skeleton structurally changed** (new/removed skill skeleton, hook wiring, bootstrap, or
     `plugin.json`). Most releases do not bump the marketplace.
   - **Manifest + binary** → publish through the **spt-core adapter registry**; the `spt`
     binary performs signing/integrity as part of spt-core's adapter-update framework. You do
     **not** run a separate signing step.

## Update path (dual, kept in sync)

Users receive updates two ways, and both must stay consistent:
- **File-pull (the real channel)** — spt-core conducts manifest + binary updates seamlessly;
  this is how logic and instruction churn reaches users. Version-of-truth = the
  manifest/binary version spt-core tracks (`spt adapter list` / `/spt:version`).
- **`claude plugin update` (cautionary, skeleton sync)** — only resyncs the low-churn
  marketplace skeleton on the rare structural bump. It is NOT the version-of-truth.

## Notes

- **Distinct numbers — never conflate them:** the **plugin (marketplace) version** (in
  `plugin.json`, bumps rarely) and the **adapter manifest/binary version** (spt-core-tracked,
  the user-visible version-of-truth). They move on independent schedules — the plugin can sit
  static across many manifest updates.
- **Trust model:** adapter manifest/binary integrity is the `spt` binary's responsibility via
  spt-core's signed-update framework — spt-claude-code defines no separate trust ceremony.
  (Claude Code has no plugin-file integrity check, which is why the skeleton plugin is safe to
  in-place-edit; only old version dirs orphan on update and are GC'd after 7 days.)


Ack delivered. Folding the cplugs skeleton-subset mechanics into RELEASE-RUNBOOK.md, and tagging it as `doc` evidence for REQ-DIST-PLUGIN-SKELETON (first req activation — demonstrates the trace working).


Edit

## cplugs skeleton publish — concrete mechanics

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
The concrete cplugs marketplace steps, captured from the sister project's
`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs
target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart
bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest
ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
none of owl's binary-lifecycle machinery applies here.

**One-time marketplace setup:**

```bash
# Clone the marketplace repo
git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
# Register a "cplugs" entry in ~/.claude/plugins/known_marketplaces.json
#   source: { source: "github", repo: "SaberMage/cplugs" }
```

**Per skeleton bump** (only when the skeleton *structurally* changes — see "Per release"):

```bash
# 1. Bump plugin.json version. The plugin manager compares this to the cached copy and
#    SKIPS the update entirely if they match — a stale version = a silent no-update.
# 2. Copy skeleton files (NO binary) into the marketplace clone:
MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/<plugin>
cp -r skills/*  "$MARKET/skills/"
cp -r hooks/*   "$MARKET/hooks/"
cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
# 3. Commit + push the cplugs repo:
cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/<plugin>/ \
  && git commit -m "<plugin>: <change>" && git push
```

**Pointer flip** (the authoritative install-state update):

```bash
claude plugin install <plugin>@cplugs   # un-orphans + rewrites installed_plugins.json atomically
# then, inside Claude Code:
/reload-plugins
```

**Gotchas (these DO apply to the skeleton):**
- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).
  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.
- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`
  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin
  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.
- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates
  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.

**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so
skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·
the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.

> **OPEN — cplugs plugin name.** Legacy already occupies the `spt` plugin name on cplugs; the
> skeleton needs a **distinct** marketplace plugin name (candidate: `spt-claude-code` /
> `claude-spt`) while still exposing the `/spt:` *skill* namespace (plugin name ≠ skill-namespace
> prefix). Resolve before first cplugs push; replace `<plugin>` above.

## Update path (dual, kept in sync)


<owl_messages>
<owl_message from="doyle" priority="high">ACK — good foundation. Skeleton subset read right (you correctly dropped the binary-sync / prune / handoff machinery — that's owl's live-binary problem, not yours). REQs minted inactive = correct (activate-don't-pre-fail).

Logged your experimplate template feedback (4 tag-leaks + seed manifest_error in the template's own example docs; you fixed in-instance via scan-root trim). That's a TEMPLATE defect every instantiation inherits — I'll carry the fix upstream into experimplate (scope the scan-root to exclude the example/formats guides, or make example tags inert) so the next adapter starts clean. Your in-instance fix is the right move for now.

No spt-core surface issues from you yet (expected — you're on v0.6.0-supported scaffolding). Keep going. Ping when RELEASE-RUNBOOK is committed. M12 publish still imminent — I ping you the moment its surface is live so you can sequence the strings / shortcut_basename / endpoint-run pieces.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


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

## cplugs skeleton publish — concrete mechanics

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
The concrete cplugs marketplace steps, captured from the sister project's
`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs
target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart
bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest
ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
none of owl's binary-lifecycle machinery applies here.

**One-time marketplace setup:**

```bash
# Clone the marketplace repo
git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
# Register a "cplugs" entry in ~/.claude/plugins/known_marketplaces.json
#   source: { source: "github", repo: "SaberMage/cplugs" }
```

**Per skeleton bump** (only when the skeleton *structurally* changes — see "Per release"):

```bash
# 1. Bump plugin.json version. The plugin manager compares this to the cached copy and
#    SKIPS the update entirely if they match — a stale version = a silent no-update.
# 2. Copy skeleton files (NO binary) into the marketplace clone:
MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/<plugin>
cp -r skills/*  "$MARKET/skills/"
cp -r hooks/*   "$MARKET/hooks/"
cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
# 3. Commit + push the cplugs repo:
cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/<plugin>/ \
  && git commit -m "<plugin>: <change>" && git push
```

**Pointer flip** (the authoritative install-state update):

```bash
claude plugin install <plugin>@cplugs   # un-orphans + rewrites installed_plugins.json atomically
# then, inside Claude Code:
/reload-plugins
```

**Gotchas (these DO apply to the skeleton):**
- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).
  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.
- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`
  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin
  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.
- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates
  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.

**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so
skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·
the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.

> **OPEN — cplugs plugin name.** Legacy already occupies the `spt` plugin name on cplugs; the
> skeleton needs a **distinct** marketplace plugin name (candidate: `spt-claude-code` /
> `claude-spt`) while still exposing the `/spt:` *skill* namespace (plugin name ≠ skill-namespace
> prefix). Resolve before first cplugs push; replace `<plugin>` above.

## Update path (dual, kept in sync)

# Release runbook

> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
> thin adapter; the heavy signed-release machinery lives in spt-core, not here.

<!-- [<doc>->REQ-EXAMPLE-REL] (illustration; escaped per docs/TRACEABILITY.md) -->

## Publish targets

An spt-claude-code release lands in **two places**, by volatility:

1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced
   `/spt:*` skill skeletons, `hooks.json`, the SessionStart bootstrap, `plugin.json`). This
   is low-churn: it only changes on **structural** plugin changes, not on logic/instruction
   updates.
2. **The spt-core adapter registry** — the volatile bulk: the CC adapter **manifest**
   (`[digest]` extractor, profiles, strings, hints) **+ binary**. This publish is
   **spt-core-conducted** (the same framework that conducts adapter updates), not a plugin
   file push.

## One-time setup

No signing-key ceremony in this project. **Signing is delegated to the `spt` binary**, which
handles adapter manifest/binary integrity through spt-core's own signed-update framework. Keep
no release keys here. The only setup is marketplace/registry publish credentials for the two
targets above (a `cplugs` push token + whatever the spt-core adapter-registry publish step
requires).

## Per release

1. **Bump** the version (the plugin `plugin.json` version and/or the adapter manifest version —
   note these are distinct numbers; see Notes) if needed; **regenerate generated docs**
   (the mdBook `docs-site/` build + `llms.txt` / schema / CLI-help exports); land everything
   green.
2. **Write the user-facing changelog** — add a `## [<version>] - <date>` section at the top of
   `CHANGELOG.md` with **Added / Changed / Fixed** subsections. This section becomes the public
   release body verbatim, so it is the changelog every user reads. Rules:
   - **User-facing UX only.** What a person using the product notices or does differently. Name
     the actual commands / flags / surfaces they touch (`/spt:*` skills, `cc`, `/spt:setup`).
   - **No internal lingo** — no requirement ids, internal module names, commit hashes, or
     milestone / hazard codes. A reader who has never seen the source must understand every line.
   - **Flag breaking changes** prominently under Changed.
   - The release **fails loudly** if the tagged version has no `## [<version>]` section — the
     changelog is not optional.
3. **Tag**: `git tag <vX.Y.Z> && git push origin <vX.Y.Z>`. This triggers:
   - the **docs-publish workflow** — build `docs-site/` and publish to GitHub Pages (drift-gated;
     see `docs/DOCS-STRATEGY.md`).
4. **Publish** (light — signing delegated to `spt`):
   - **Skeleton plugin** → push to the `SaberMage/cplugs` marketplace **only when the plugin
     skeleton structurally changed** (new/removed skill skeleton, hook wiring, bootstrap, or
     `plugin.json`). Most releases do not bump the marketplace.
   - **Manifest + binary** → publish through the **spt-core adapter registry**; the `spt`
     binary performs signing/integrity as part of spt-core's adapter-update framework. You do
     **not** run a separate signing step.

## Update path (dual, kept in sync)

Users receive updates two ways, and both must stay consistent:
- **File-pull (the real channel)** — spt-core conducts manifest + binary updates seamlessly;
  this is how logic and instruction churn reaches users. Version-of-truth = the
  manifest/binary version spt-core tracks (`spt adapter list` / `/spt:version`).
- **`claude plugin update` (cautionary, skeleton sync)** — only resyncs the low-churn
  marketplace skeleton on the rare structural bump. It is NOT the version-of-truth.

## Notes

- **Distinct numbers — never conflate them:** the **plugin (marketplace) version** (in
  `plugin.json`, bumps rarely) and the **adapter manifest/binary version** (spt-core-tracked,
  the user-visible version-of-truth). They move on independent schedules — the plugin can sit
  static across many manifest updates.
- **Trust model:** adapter manifest/binary integrity is the `spt` binary's responsibility via
  spt-core's signed-update framework — spt-claude-code defines no separate trust ceremony.
  (Claude Code has no plugin-file integrity check, which is why the skeleton plugin is safe to
  in-place-edit; only old version dirs orphan on update and are GC'd after 7 days.)

+## cplugs skeleton publish — concrete mechanics

+The concrete cplugs marketplace steps, captured from the sister project's

+`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs

+target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart

+bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest

+ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so

+none of owl's binary-lifecycle machinery applies here.

+git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs

+# Register a "cplugs" entry in ~/.claude/plugins/known_marketplaces.json

+#   source: { source: "github", repo: "SaberMage/cplugs" }

+**Per skeleton bump** (only when the skeleton *structurally* changes — see "Per release"):

+# 1. Bump plugin.json version. The plugin manager compares this to the cached copy and

+#    SKIPS the update entirely if they match — a stale version = a silent no-update.

+# 2. Copy skeleton files (NO binary) into the marketplace clone:

+cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/<plugin>/ \

+  && git commit -m "<plugin>: <change>" && git push

+**Pointer flip** (the authoritative install-state update):

+claude plugin install <plugin>@cplugs   # un-orphans + rewrites installed_plugins.json atomically

+**Gotchas (these DO apply to the skeleton):**

+- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).

+  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.

+- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`

+  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin

+  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.

+- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates

+  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.

+**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so

+skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·

+the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.

+> **OPEN — cplugs plugin name.** Legacy already occupies the `spt` plugin name on cplugs; the

+> skeleton needs a **distinct** marketplace plugin name (candidate: `spt-claude-code` /

+> `claude-spt`) while still exposing the `/spt:` *skill* namespace (plugin name ≠ skill-namespace

+> prefix). Resolve before first cplugs push; replace `<plugin>` above.


doyle ACK'd — he's carrying the experimplate scan-root/example-tag fix upstream; my in-instance fix stands. No reply needed; I'll ping after commit. Writing the JIT plan, then committing.


Write

# SKELETON milestone — JIT plan

> First body of work for `spt-claude-code`, on the **v0.6.0 (M10) public surface**. JIT: this
> plans the skeleton milestone only — M12-dependent pieces are explicitly out of scope until
> M12 publishes (doyle pings when its surface is live). Authoritative why/what: `SCOPE.md`.

## Goal

Stand up the **thin cplugs skeleton plugin** — the low-churn marketplace artifact — and the
traceability + release foundation around it. No spt-core logic lives here; the plugin is
skill skeletons + `hooks.json` (→ `spt api`) + a SessionStart bootstrap + `plugin.json`.

## Scope (v0.6.0-supported, startable now)

In:
- Traceability gate green + skeleton-milestone REQs seeded (DONE this body — see below).
- cplugs publish mechanics captured in `docs/RELEASE-RUNBOOK.md` (DONE this body).
- The plugin skeleton itself: `plugin.json`, `hooks.json` (delegating to `spt api`),
  SessionStart bootstrap that installs spt-core when absent, namespaced `/spt:*` skill
  skeletons for the KEEP set, `/spt:version`, the UPS-injection hook.
- ADR-0001 (distribution split: skeleton plugin vs spt-conducted manifest+binary).
- Empirical confirmation: does `UserPromptSubmit` fire on `/spt:X` slash-commands? (gates the
  UPS-injection design — REQ-UPS-INJECTION). Fallback = in-SKILL.md instructions if it doesn't.

Out (deferred to post-M12 — sequence after doyle's M12-live ping):
- File-backed `[strings]`, `adapter:profile` fallback, `[adapter] shortcut_basename`,
  `spt endpoint run` bringup picker, `spt rc` cross-node attach, `cc` launcher, `/spt:setup`,
  whoami→alias. (M12 public-surface deps — `SCOPE.md` §"spt-core upstream deps".)
- CI fleet wiring (own milestone) and docs-site mdBook build (own milestone) — both startable
  on v0.6.0 but not part of THIS skeleton body; planned next.

## Open design questions

- **cplugs plugin name** — legacy occupies `spt`; skeleton needs a distinct marketplace name
  (`spt-claude-code` / `claude-spt`?) while keeping the `/spt:` *skill* namespace. Resolve
  before any cplugs push. (`docs/RELEASE-RUNBOOK.md` OPEN note.)
- **UPS-on-slash-command** — design assumes UPS fires; confirm empirically (above).
- `/spt:setup` #9 verify fold-inline vs deferred (`SCOPE.md` §8 open item) — not skeleton-blocking.

## Tasks

- [x] Green `traceable-reqs check` (fix seed scan-root manifest_error + 4 template tag-leaks).
- [x] Seed skeleton-milestone REQs (inactive): DIST-PLUGIN-SKELETON, DIST-BOOTSTRAP-INSTALL,
      DIST-HOOKS-API, DIST-MANIFEST-SCHEMA, UPS-INJECTION, SKILL-VERSION.
- [x] Capture cplugs skeleton-publish mechanics in `docs/RELEASE-RUNBOOK.md`.
- [ ] Write ADR-0001 (distribution split) → `doc` evidence; activate reqs' `doc` stage.
- [ ] Scaffold `plugin/` (plugin.json), `hooks/` (hooks.json → `spt api`; UPS-injection hook),
      `skills/` (KEEP skeletons + `/spt:version`). Add these as `[scan].roots` when created.
- [ ] Empirically test UPS-fires-on-slash-command; record result in ADR / KNOWN-HAZARDS.
- [ ] Validate `plugin.json` + adapter manifest shape against published `manifest.schema.json`
      from `SaberMage/spt-releases`.
- [ ] Activate `impl`/`unit` stages + tag as each surface lands; `traceable-reqs check` EXIT=0.

## Gate

`traceable-reqs check` EXIT=0 with the skeleton milestone's reqs activated and covered, the
skeleton plugin installable from a local marketplace clone, and the manifest validating against
the published schema. Build/CI wiring is a separate milestone.


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

# SKELETON milestone — JIT plan

> First body of work for `spt-claude-code`, on the **v0.6.0 (M10) public surface**. JIT: this
> plans the skeleton milestone only — M12-dependent pieces are explicitly out of scope until
> M12 publishes (doyle pings when its surface is live). Authoritative why/what: `SCOPE.md`.

## Goal

Stand up the **thin cplugs skeleton plugin** — the low-churn marketplace artifact — and the
traceability + release foundation around it. No spt-core logic lives here; the plugin is
skill skeletons + `hooks.json` (→ `spt api`) + a SessionStart bootstrap + `plugin.json`.

## Scope (v0.6.0-supported, startable now)

In:
- Traceability gate green + skeleton-milestone REQs seeded (DONE this body — see below).
- cplugs publish mechanics captured in `docs/RELEASE-RUNBOOK.md` (DONE this body).
- The plugin skeleton itself: `plugin.json`, `hooks.json` (delegating to `spt api`),
  SessionStart bootstrap that installs spt-core when absent, namespaced `/spt:*` skill
  skeletons for the KEEP set, `/spt:version`, the UPS-injection hook.
- ADR-0001 (distribution split: skeleton plugin vs spt-conducted manifest+binary).
- Empirical confirmation: does `UserPromptSubmit` fire on `/spt:X` slash-commands? (gates the
  UPS-injection design — REQ-UPS-INJECTION). Fallback = in-SKILL.md instructions if it doesn't.

Out (deferred to post-M12 — sequence after doyle's M12-live ping):
- File-backed `[strings]`, `adapter:profile` fallback, `[adapter] shortcut_basename`,
  `spt endpoint run` bringup picker, `spt rc` cross-node attach, `cc` launcher, `/spt:setup`,
  whoami→alias. (M12 public-surface deps — `SCOPE.md` §"spt-core upstream deps".)
- CI fleet wiring (own milestone) and docs-site mdBook build (own milestone) — both startable
  on v0.6.0 but not part of THIS skeleton body; planned next.

## Open design questions

- **cplugs plugin name** — legacy occupies `spt`; skeleton needs a distinct marketplace name
  (`spt-claude-code` / `claude-spt`?) while keeping the `/spt:` *skill* namespace. Resolve
  before any cplugs push. (`docs/RELEASE-RUNBOOK.md` OPEN note.)
- **UPS-on-slash-command** — design assumes UPS fires; confirm empirically (above).
- `/spt:setup` #9 verify fold-inline vs deferred (`SCOPE.md` §8 open item) — not skeleton-blocking.

## Tasks

- [x] Green `traceable-reqs check` (fix seed scan-root manifest_error + 4 template tag-leaks).
- [x] Seed skeleton-milestone REQs (inactive): DIST-PLUGIN-SKELETON, DIST-BOOTSTRAP-INSTALL,
      DIST-HOOKS-API, DIST-MANIFEST-SCHEMA, UPS-INJECTION, SKILL-VERSION.
- [x] Capture cplugs skeleton-publish mechanics in `docs/RELEASE-RUNBOOK.md`.
- [ ] Write ADR-0001 (distribution split) → `doc` evidence; activate reqs' `doc` stage.
- [ ] Scaffold `plugin/` (plugin.json), `hooks/` (hooks.json → `spt api`; UPS-injection hook),
      `skills/` (KEEP skeletons + `/spt:version`). Add these as `[scan].roots` when created.
- [ ] Empirically test UPS-fires-on-slash-command; record result in ADR / KNOWN-HAZARDS.
- [ ] Validate `plugin.json` + adapter manifest shape against published `manifest.schema.json`
      from `SaberMage/spt-releases`.
- [ ] Activate `impl`/`unit` stages + tag as each surface lands; `traceable-reqs check` EXIT=0.

## Gate

`traceable-reqs check` EXIT=0 with the skeleton milestone's reqs activated and covered, the
skeleton plugin installable from a local marketplace clone, and the manifest validating against
the published schema. Build/CI wiring is a separate milestone.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && traceable-reqs check 2>&1 | tail -2; echo "EXIT=${PIPESTATUS[0]}" && echo "--- status ---" && git status --short

Confirm gate green and review working tree


  [OK] REQ-UPS-INJECTION  required: []  stages: -doc -impl -unit -int
       UserPromptSubmit hook detects /spt:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands
EXIT=0
--- status ---
 M AGENTS.md
 M docs/RELEASE-RUNBOOK.md
 M docs/formats/adr-format.md
 M docs/formats/context-format.md
 M traceable-reqs.toml
?? SKELETON-PLAN.md


Gate green. Committing the skeleton-milestone kickoff (project trailer per AGENTS.md).


cd "C:/Users/decid/Documents/projects/spt-claude-code" && git add -A && git commit -q -F - <<'EOF'
chore(skeleton): kickoff — green traceability gate, seed REQs, capture cplugs mechanics

Foundation for the SKELETON milestone (thin cplugs plugin) on the v0.6.0 public surface.

- traceable-reqs: fix seed manifest_error (scan roots trimmed to dirs that exist now;
  plugin/skills/hooks/tests added back as those dirs land); fix 4 template tag-leaks in
  experimplate's own example docs (adr-format, context-format, RELEASE-RUNBOOK, AGENTS.md)
  by escaping the illustrative stage per docs/TRACEABILITY.md convention. check EXIT=0.
- Seed skeleton-milestone REQs, all inactive (activate-don't-pre-fail): DIST-PLUGIN-SKELETON,
  DIST-BOOTSTRAP-INSTALL, DIST-HOOKS-API, DIST-MANIFEST-SCHEMA, UPS-INJECTION, SKILL-VERSION.
- docs/RELEASE-RUNBOOK.md: capture concrete cplugs skeleton-publish mechanics (one-time
  marketplace setup, per-bump copy+commit+push, pointer-flip via `claude plugin install`,
  3 gotchas) — skeleton subset only, owl binary/prune/handoff machinery excluded. Flags the
  OPEN cplugs plugin-name collision with legacy `spt`.
- SKELETON-PLAN.md: JIT plan for the milestone; M12-dependent pieces explicitly deferred.

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

Commit skeleton-milestone kickoff foundation


warning: in the working copy of 'AGENTS.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/RELEASE-RUNBOOK.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/formats/adr-format.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'docs/formats/context-format.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'traceable-reqs.toml', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'SKELETON-PLAN.md', LF will be replaced by CRLF the next time Git touches it
commit=0
d089941 chore(skeleton): kickoff — green traceability gate, seed REQs, capture cplugs mechanics


Committed `d089941`, gate green. Pinging doyle.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && $OWL send doyle perri <<'EOF'
COMMITTED d089941 — "chore(skeleton): kickoff". RELEASE-RUNBOOK cplugs mechanics captured (skeleton subset; owl binary/prune/handoff excluded as you confirmed). Also in commit: green traceable-reqs gate, 6 inactive skeleton REQs, SKELETON-PLAN.md (JIT).

One OPEN flagged in RELEASE-RUNBOOK + plan, your call if you have context: cplugs plugin NAME — legacy holds `spt`; the skeleton needs a distinct marketplace plugin name (spt-claude-code? claude-spt?) while keeping the /spt: skill namespace (plugin name != skill-namespace prefix). Not blocking until first cplugs push; resolving before then.

Next, on v0.6.0: scaffold the actual plugin skeleton (plugin.json, hooks.json->spt api, SessionStart bootstrap, KEEP skill skeletons + /spt:version, UPS-injection hook) + ADR-0001 (distribution split), and empirically test whether UPS fires on /spt:X slash-commands (gates the UPS-injection design). Proceeding unless you redirect. Standing by for your M12-live ping to sequence the strings/shortcut_basename/endpoint-run pieces.
EOF
echo "sent=$?"

Ping doyle: committed + next steps + open question


➜ SENT:doyle
sent=0


Body #1 done. Status:

**perri live, building spt-claude-code on v0.6.0 baseline.**

Shipped (commit `d089941`):
- **Traceability gate green** (`traceable-reqs check` EXIT=0). Fixed two seed defects inherited from experimplate template — scan-root `manifest_error` + 4 illustrative-tag leaks in the template's own example docs. doyle carrying the fix upstream so next adapter starts clean.
- **6 skeleton-milestone REQs seeded**, all inactive (activate-don't-pre-fail).
- **cplugs publish mechanics** captured in RELEASE-RUNBOOK.md — skeleton subset only (skill stubs + hooks.json + bootstrap + plugin.json; no binary, no owl prune/handoff machinery).
- **SKELETON-PLAN.md** JIT plan; M12-deps deferred until doyle's M12-live ping.

Flagged 1 open to doyle: cplugs **plugin-name collision** — legacy holds `spt`, skeleton needs distinct marketplace name while keeping `/spt:` skill namespace. Non-blocking until first push.

Next body (proceeding unless doyle redirects): scaffold the real plugin skeleton + ADR-0001 + empirically test whether UPS fires on `/spt:X` (gates UPS-injection design).

No spt-core public-surface bugs hit yet. Idle on perch — doyle pings when M12 publishes.


<task-notification>
<task-id>bq54alupr</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="5a7934e8" type="msg" from="doyle">Commit d089941 noted — RELEASE-RUNBOOK cplugs subset + 6 inactive REQs + SKELETON-PLAN, clean.

cplugs plugin NAME — taking it. SCOPE confirms the locked identities (adapter_name=claude-spt, repo=spt-claude-code, skills=/spt:* parity, legacy owl plugin=spt → collision real) but does NOT pin the marketplace p</EVENT-PART>
<EVENT-PART seq="2/6" id="5a7934e8">lugin name. Before I rule, a flag on YOUR stated assumption &quot;plugin name != skill-namespace prefix&quot;:

In Claude Code the skill-namespace prefix appears to TRACK the plugin name — every plugin skill I can see is invoked /&lt;plugin&gt;:&lt;skill&gt; (spt:send, caveman:caveman, gsd:*). If that holds, a plugin named `claude-spt`</EVENT-PART>
<EVENT-PART seq="3/6" id="5a7934e8"> makes skills /claude-spt:* — it would NOT keep /spt:. That directly contradicts your decouple assumption and changes the call. So this is exactly your &quot;empirically test the slash-command behavior&quot; territory.

I've got a claude-code-guide check running to confirm definitively whether a plugin can declare a skill prefix distin</EVENT-PART>
<EVENT-PART seq="4/6" id="5a7934e8">ct from plugin.json name. HOLD the final name pick until that lands (you confirmed it's non-blocking till first push — scaffold plugin.json with a placeholder, rename pre-push). Two branches:
- If prefix CAN be decoupled → plugin name `claude-spt` (matches adapter identity), skills stay /spt:. Clean.
- If prefix is hard-tied to plugi</EVENT-PART>
<EVENT-PART seq="5/6" id="5a7934e8">n name → genuine conflict: /spt: parity requires plugin name `spt` (collides w/ legacy) OR you accept /claude-spt: skills. That's a branding/parity tradeoff I'll put to the operator.

Proceed with the rest (skeleton scaffold, ADR-0001 split, UPS-fires-on-/spt:X empirical test — that test is adjacent, may answer the namespace question</EVENT-PART>
<EVENT-PART seq="6/6" id="5a7934e8"> too). I ping you with the name ruling + the M12-live sequence ping. Keep moving.</EVENT-PART></event>
</task-notification>


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


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


doyle correct — my decouple assumption is wrong. In CC the skill prefix tracks plugin name (`/spt:live` ⇒ plugin named `spt`, `caveman:*`, etc.). So `claude-spt` plugin ⇒ `/claude-spt:*`, not `/spt:`. Conceding; scaffolding with placeholder name, holding for doyle's ruling. Ack + inspect legacy plugin layout to mirror.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && $OWL send doyle perri <<'EOF'
You're right — my "plugin name != skill prefix" assumption is wrong. CC invokes /<plugin>:<skill> (my own loaded skills confirm: spt:*, caveman:*). So `claude-spt` plugin => /claude-spt:* skills, NOT /spt:. Concede; corrected my mental model.

HOLDING name pick per your call. Scaffolding plugin.json with a placeholder name + a single PLUGIN_NAME marker so the pre-push rename is one substitution. Proceeding with the name-independent work: skeleton structure (hooks.json->spt api, SessionStart bootstrap, KEEP skill skeletons + version), ADR-0001 (distribution split), and the UPS-fires-on-slash-command empirical test — agreed it's adjacent and may settle the namespace branch. Will report findings + await your name ruling + M12-live ping.
EOF
echo "sent=$?"

Ack doyle, concede assumption, state plan


cd "C:/Users/decid/Documents/projects/claude_skill_owl" && echo "=== plugin tree ===" && find plugin -maxdepth 4 -type f 2>/dev/null | head -60

List legacy plugin file layout


=== plugin tree ===
plugin/spt/.claude-plugin/plugin.json
plugin/spt/hooks/hooks.json
plugin/spt/owl.exe
plugin/spt/skills/clear-psyche/SKILL.md
plugin/spt/skills/commune/commune.md
plugin/spt/skills/commune/SKILL.md
plugin/spt/skills/force-stop/SKILL.md
plugin/spt/skills/list-agents/SKILL.md
plugin/spt/skills/live/SKILL.md
plugin/spt/skills/new-alarm/SKILL.md
plugin/spt/skills/psyche-sync-setup/SKILL.md
plugin/spt/skills/ready/SKILL.md
plugin/spt/skills/revive/SKILL.md
plugin/spt/skills/send/SKILL.md
plugin/spt/skills/signoff/SKILL.md
plugin/spt/skills/whoami/SKILL.md
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-claude-code


1	{
2	  "name": "spt",
3	  "version": "1.11.25",
4	  "description": "Spacetime agent messaging and live agent system for Claude Code",
5	  "author": {
6	    "name": "todlando"
7	  },
8	  "keywords": ["messaging", "agents", "live", "owl"]
9	}
10


1	{
2	  "hooks": {
3	    "SessionStart": [
4	      {
5	        "hooks": [
6	          {
7	            "type": "command",
8	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe plugin-session-start"
9	          }
10	        ]
11	      }
12	    ],
13	    "SessionEnd": [
14	      {
15	        "hooks": [
16	          {
17	            "type": "command",
18	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe cleanup-session"
19	          }
20	        ]
21	      }
22	    ],
23	    "PreToolUse": [
24	      {
25	        "hooks": [
26	          {
27	            "type": "command",
28	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe hook-check"
29	          }
30	        ]
31	      }
32	    ],
33	    "Stop": [
34	      {
35	        "hooks": [
36	          {
37	            "type": "command",
38	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe hook-idle"
39	          }
40	        ]
41	      }
42	    ],
43	    "UserPromptSubmit": [
44	      {
45	        "hooks": [
46	          {
47	            "type": "command",
48	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe hook-prompt"
49	          }
50	        ]
51	      }
52	    ],
53	    "SubagentStart": [
54	      {
55	        "hooks": [
56	          {
57	            "type": "command",
58	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe hook-subagent-start"
59	          }
60	        ]
61	      }
62	    ],
63	    "SubagentStop": [
64	      {
65	        "hooks": [
66	          {
67	            "type": "command",
68	            "command": "$CLAUDE_PLUGIN_ROOT/owl.exe hook-subagent-stop"
69	          }
70	        ]
71	      }
72	    ]
73	  }
74	}
75


1	---
2	name: send
3	description: |
4	  Send a message to another SPT agent. Use when the user says "send to",
5	  "message", "tell <agent>", or wants to communicate with another agent.
6	argument-hint: "<target> [--block]"
7	allowed-tools: [Bash, Read]
8	---
9
10	# /spt:send
11
12	All commands use `$OWL` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.
13
14	> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.
15
16	## Messaging Command Reference
17
18	Both commands read the message body from **stdin** (pipe or heredoc).
19
20	```bash
21	# send -- fire-and-forget to a target (use when you have your own perch)
22	$OWL send <target> <<'EOF'
23	message body
24	EOF
25
26	# send --reply-to -- respond to whoever messaged you
27	$OWL send --reply-to <sender> [your-id] <<'EOF'
28	response body
29	EOF
30
31	# ask -- deliver + create ephemeral reply perch + poll for reply (use when you have NO perch)
32	$OWL ask <target> [from] <<'EOF'
33	message body
34	EOF
35	```
36
37	If auto-detection fails, pass your ID explicitly: `$OWL send <target> <your-id>`, `$OWL send --reply-to <sender> <your-id>`, `$OWL ask <target> <impromptu-id>`.
38
39	**When to use which:**
40	- **`send`**: You already have a listener. Fire-and-forget, no reply perch created.
41	- **`send --reply-to`**: You received a message and want to respond. `from` is auto-set from the original sender.
42	- **`ask`**: You have no listener. Creates a temporary perch, delivers, then polls for the reply.
43
44	## Pre-checks
45
46	- If `<target>` matches this agent's own listener ID, refuse. Self-sends create loops.
47	- All send/reply-to/ask commands verify the target is alive automatically.
48
49	## If you already have a listener
50
51	Just send -- no reply perch needed. Run **not** in background:
52
53	```bash
54	$OWL send <target> <<'EOF'
55	<msg>
56	EOF
57	```
58
59	## If you have no listener
60
61	The `ask` subcommand creates an ephemeral reply perch, delivers, and polls for the reply. Run with `run_in_background: true` (or `false` for `--block`):
62
63	```bash
64	$OWL ask <target> <<'EOF'
65	<msg>
66	EOF
67	```
68
69	## On response
70
71	**Important:** If you have an active listener perch, always launch Agent tool calls with `run_in_background: true`. Foreground agents block your poll loop -- no messages (including replies to your send) can be delivered until the agent finishes.
72
73	When the response arrives, present it to the user. Responses arrive via one of three paths:
74	- **Background interject** -- the `$OWL ask` background task completes with the reply.
75	- **Inline** -- if you used `--block`, the reply prints directly.
76	- **Hook-injected XML** -- if you are mid-tool-call, the reply appears as `<owl_messages>` XML in your tool call context. The `from` attribute is the sender.
77

---
name: send
description: |
  Send a message to another SPT agent. Use when the user says "send to",
  "message", "tell <agent>", or wants to communicate with another agent.
argument-hint: "<target> [--block]"
allowed-tools: [Bash, Read]
---

# /spt:send

All commands use `$OWL` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.

> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.

## Messaging Command Reference

Both commands read the message body from **stdin** (pipe or heredoc).

```bash
# send -- fire-and-forget to a target (use when you have your own perch)
$OWL send <target> <<'EOF'
message body
EOF

# send --reply-to -- respond to whoever messaged you
$OWL send --reply-to <sender> [your-id] <<'EOF'
response body
EOF

# ask -- deliver + create ephemeral reply perch + poll for reply (use when you have NO perch)
$OWL ask <target> [from] <<'EOF'
message body
EOF
```

If auto-detection fails, pass your ID explicitly: `$OWL send <target> <your-id>`, `$OWL send --reply-to <sender> <your-id>`, `$OWL ask <target> <impromptu-id>`.

**When to use which:**
- **`send`**: You already have a listener. Fire-and-forget, no reply perch created.
- **`send --reply-to`**: You received a message and want to respond. `from` is auto-set from the original sender.
- **`ask`**: You have no listener. Creates a temporary perch, delivers, then polls for the reply.

## Pre-checks

- If `<target>` matches this agent's own listener ID, refuse. Self-sends create loops.
- All send/reply-to/ask commands verify the target is alive automatically.

## If you already have a listener

Just send -- no reply perch needed. Run **not** in background:

```bash
$OWL send <target> <<'EOF'
<msg>
EOF
```

## If you have no listener

The `ask` subcommand creates an ephemeral reply perch, delivers, and polls for the reply. Run with `run_in_background: true` (or `false` for `--block`):

```bash
$OWL ask <target> <<'EOF'
<msg>
EOF
```

## On response

**Important:** If you have an active listener perch, always launch Agent tool calls with `run_in_background: true`. Foreground agents block your poll loop -- no messages (including replies to your send) can be delivered until the agent finishes.

When the response arrives, present it to the user. Responses arrive via one of three paths:
- **Background interject** -- the `$OWL ask` background task completes with the reply.
- **Inline** -- if you used `--block`, the reply prints directly.
- **Hook-injected XML** -- if you are mid-tool-call, the reply appears as `<owl_messages>` XML in your tool call context. The `from` attribute is the sender.


1	---
2	name: new-alarm
3	description: |
4	  Schedule a one-shot timed alarm delivered as TIMED PULSE. Use when the user
5	  says "remind me", "check back in", "set an alarm", "wake me when", or wants a
6	  delayed notification.
7	argument-hint: <time_spec> -- <message>
8	allowed-tools: [Bash]
9	---
10
11	# /spt:new-alarm
12
13	All commands use `$OWL` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.
14
15	> **Renamed 2026-04 (Phase 18.7):** previously `/spt:timed-pulse` /
16	> `$LIVE timed-pulse`. All existing pulse files under `pulses/<id>.json`
17	> continue to work — only the CLI + skill surface renamed. The message
18	> body delivered to Self is still formatted as `TIMED PULSE ({timestamp}): ...`.
19
20	> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.
21
22	## Quick form
23
24	```bash
25	$OWL new-alarm <time_spec> -- <message>
26	```
27
28	If auto-detection fails: `$OWL new-alarm <your-id> <time_spec> -- <message>`
29
30	Schedule a one-shot timed alarm. The message is delivered to Self as a `TIMED PULSE` when the specified time arrives.
31
32	**Time formats:**
33
34	- `+30m` -- 30 minutes from now
35	- `+2h` -- 2 hours from now
36	- `+1h30m` -- 1 hour 30 minutes from now
37	- ISO-8601 absolute -- e.g., `2026-03-30T15:00:00`
38
39	Example:
40
41	```bash
42	$OWL new-alarm +30m -- Check if CI pipeline finished
43	```
44
45	## Messages containing slash-prefixed commands
46
47	On Windows under Git Bash / MSYS, any argument starting with `/` is silently rewritten to an absolute Windows path before the binary sees it. This corrupts alarm bodies like `/gsd-plan-phase 19`. The scheduler detects the shapes caused by this rewriting and rejects them with guidance, but use one of the safe transports below to avoid the error entirely.
48
49	### Option A (recommended): stdin
50
51	```bash
52	echo "/gsd-plan-phase 19 --research --auto" | $OWL new-alarm +1h
53	```
54
55	When no positional message is provided, `new-alarm` reads the body from stdin. Immune to shell path conversion.
56
57	### Option B: --message-file
58
59	```bash
60	printf '%s' "/gsd-plan-phase 19 --research --auto" > /tmp/alarm.txt
61	$OWL new-alarm +1h --message-file /tmp/alarm.txt
62	```
63
64	Reads the file verbatim. Trailing newlines are trimmed.
65
66	### Option C: MSYS escape
67
68	If you must use the positional form in a MSYS shell:
69
70	```bash
71	MSYS_NO_PATHCONV=1 $OWL new-alarm +1h -- /gsd-plan-phase 19 --research --auto
72	```
73
74	Or double the leading slash, which MSYS leaves alone:
75
76	```bash
77	$OWL new-alarm +1h -- //gsd-plan-phase 19 --research --auto
78	```
79
80	(The receiver can strip the extra leading slash when interpreting the reminder.)
81
82	## Persistence
83
84	Alarms are persisted to `%LOCALAPPDATA%\spt\pulses\<id>.json` (Windows)
85	or `~/.spt/pulses/<id>.json` (Unix). They survive listener restarts and
86	binary handoffs: on listener startup any expired entries fire to Self
87	inline (no stale-age filter — every expired alarm fires when a Self
88	listener next runs). Pending entries are re-read each poll iteration
89	when the pulse file's mtime changes. File schema is unchanged from the
90	prior `timed-pulse` surface, so any entries scheduled under the old
91	CLI continue to work after the rename.
92
93	Firing: when a Self listener is running (`$OWL poll <id> listen --live`
94	or `$LIVE start <id>` which spawns one), alarms fire within one blocking-
95	wait slice of the target epoch (sub-second in practice). When no listener
96	is running the alarm sits in the file and fires the next time a Self
97	listener starts.
98
99	The message body delivered to Self is formatted as
100	`TIMED PULSE ({timestamp}): {message}` — this internal body string is
101	unchanged from the prior surface (only the CLI + skill rename applies).
102

---
name: new-alarm
description: |
  Schedule a one-shot timed alarm delivered as TIMED PULSE. Use when the user
  says "remind me", "check back in", "set an alarm", "wake me when", or wants a
  delayed notification.
argument-hint: <time_spec> -- <message>
allowed-tools: [Bash]
---

# /spt:new-alarm

All commands use `$OWL` env var, auto-injected by the plugin's SessionStart hook. If commands fail with "command not found", restart the Claude Code session so the SessionStart hook re-runs.

> **Renamed 2026-04 (Phase 18.7):** previously `/spt:timed-pulse` /
> `$LIVE timed-pulse`. All existing pulse files under `pulses/<id>.json`
> continue to work — only the CLI + skill surface renamed. The message
> body delivered to Self is still formatted as `TIMED PULSE ({timestamp}): ...`.

> **Identity auto-detection:** Your identity is auto-detected from your session. Pass your ID explicitly only if auto-detection fails.

## Quick form

```bash
$OWL new-alarm <time_spec> -- <message>
```

If auto-detection fails: `$OWL new-alarm <your-id> <time_spec> -- <message>`

Schedule a one-shot timed alarm. The message is delivered to Self as a `TIMED PULSE` when the specified time arrives.

**Time formats:**

- `+30m` -- 30 minutes from now
- `+2h` -- 2 hours from now
- `+1h30m` -- 1 hour 30 minutes from now
- ISO-8601 absolute -- e.g., `2026-03-30T15:00:00`

Example:

```bash
$OWL new-alarm +30m -- Check if CI pipeline finished
```

## Messages containing slash-prefixed commands

On Windows under Git Bash / MSYS, any argument starting with `/` is silently rewritten to an absolute Windows path before the binary sees it. This corrupts alarm bodies like `/gsd-plan-phase 19`. The scheduler detects the shapes caused by this rewriting and rejects them with guidance, but use one of the safe transports below to avoid the error entirely.

### Option A (recommended): stdin

```bash
echo "/gsd-plan-phase 19 --research --auto" | $OWL new-alarm +1h
```

When no positional message is provided, `new-alarm` reads the body from stdin. Immune to shell path conversion.

### Option B: --message-file

```bash
printf '%s' "/gsd-plan-phase 19 --research --auto" > /tmp/alarm.txt
$OWL new-alarm +1h --message-file /tmp/alarm.txt
```

Reads the file verbatim. Trailing newlines are trimmed.

### Option C: MSYS escape

If you must use the positional form in a MSYS shell:

```bash
MSYS_NO_PATHCONV=1 $OWL new-alarm +1h -- /gsd-plan-phase 19 --research --auto
```

Or double the leading slash, which MSYS leaves alone:

```bash
$OWL new-alarm +1h -- //gsd-plan-phase 19 --research --auto
```

(The receiver can strip the extra leading slash when interpreting the reminder.)

## Persistence

Alarms are persisted to `%LOCALAPPDATA%\spt\pulses\<id>.json` (Windows)
or `~/.spt/pulses/<id>.json` (Unix). They survive listener restarts and
binary handoffs: on listener startup any expired entries fire to Self
inline (no stale-age filter — every expired alarm fires when a Self
listener next runs). Pending entries are re-read each poll iteration
when the pulse file's mtime changes. File schema is unchanged from the
prior `timed-pulse` surface, so any entries scheduled under the old
CLI continue to work after the rename.

Firing: when a Self listener is running (`$OWL poll <id> listen --live`
or `$LIVE start <id>` which spawns one), alarms fire within one blocking-
wait slice of the target epoch (sub-second in practice). When no listener
is running the alarm sits in the file and fires the next time a Self
listener starts.

The message body delivered to Self is formatted as
`TIMED PULSE ({timestamp}): {message}` — this internal body string is
unchanged from the prior surface (only the CLI + skill rename applies).


Legacy structure understood. Key deltas for the thin skeleton: `hooks.json` → `spt api` (not `owl.exe`), no binary shipped, SKILL.md bodies become skeletons (real instructions via UPS-injection from `[strings]`, M12-dep).

Next body: **ADR-0001 (distribution split)** + **plugin skeleton scaffold**. ADR is pure design from SCOPE (name-independent, high value, gives doc-stage evidence). Reading ADR template.


1	# <Title — the decision stated as its outcome>
2
3	<!-- Title states the decision, not the question. e.g. "Sessions are pinned at first bind",
4	     not "How should sessions bind?". Number files sequentially: 0001-, 0002-, … -->
5
6	## Status
7
8	<!-- proposed | accepted | superseded — plus a date, and links if it supersedes / is superseded. -->
9	proposed (`<YYYY-MM-DD>`)
10	<!-- e.g. accepted (2026-01-15) · supersedes [ADR-0003](0003-old-decision.md) -->
11
12	## Context
13
14	<!-- The forces at play: what made this decision necessary, the constraints, the real
15	     problems (numbered if several). Enough that a reader who wasn't there understands WHY.
16	     Reference CONTEXT.md terms by their canonical names. -->
17
18	## Decision
19
20	<!-- What we decided, stated plainly and actively. The specifics: what is built / changed,
21	     and the rejected-but-tempting alternative noted so it isn't re-litigated. -->
22
23	## Consequences
24
25	<!-- What becomes true now — good and bad. Breaking changes, new obligations (e.g. new
26	     REQ-* ids to mint, docs to update), and anything this forecloses or forces later. -->
27

# <Title — the decision stated as its outcome>

<!-- Title states the decision, not the question. e.g. "Sessions are pinned at first bind",
     not "How should sessions bind?". Number files sequentially: 0001-, 0002-, … -->

## Status

<!-- proposed | accepted | superseded — plus a date, and links if it supersedes / is superseded. -->
proposed (`<YYYY-MM-DD>`)
<!-- e.g. accepted (2026-01-15) · supersedes [ADR-0003](0003-old-decision.md) -->

## Context

<!-- The forces at play: what made this decision necessary, the constraints, the real
     problems (numbered if several). Enough that a reader who wasn't there understands WHY.
     Reference CONTEXT.md terms by their canonical names. -->

## Decision

<!-- What we decided, stated plainly and actively. The specifics: what is built / changed,
     and the rejected-but-tempting alternative noted so it isn't re-litigated. -->

## Consequences

<!-- What becomes true now — good and bad. Breaking changes, new obligations (e.g. new
     REQ-* ids to mint, docs to update), and anything this forecloses or forces later. -->


1	# Documentation strategy
2
3	> A **designed-in commitment**, not an afterthought — grounded in research on
4	> critically-acclaimed developer docs (Stripe, Twilio, the Rust Book, FastAPI, Cloudflare,
5	> Anthropic, Diátaxis, `llms.txt`, the Google developer style guide). Governs the **shipped
6	> product docs** (authored as code lands). The planning docs (CONTEXT / ADRs / design docs) are
7	> internal and separate.
8	>
9	> **Where the docs live (the reframe):** docs live in **this same repo** under `docs-site/`.
10	> CI builds them with **mdBook** and **publishes to GitHub Pages from this repo**, gated against
11	> drift. (No separate releases/publish repo — source of truth and published site are one repo.)
12
13	## The defining constraint: a dual audience
14
15	These docs serve **two readers at once** — human developers *and* the **AI dev-agents that
16	build on or integrate with this project**. This is the design constraint: every artifact is
17	authored **once in clean markdown** and served in **two depths** (human-rendered + agent-export).
18	The agent layer is **first-class, not optional** — make it so good that a dev-agent integrates
19	correctly on the first try.
20
21	## Principles (top techniques, prioritized)
22
23	1. **Sub-10-minute killer quickstart** — runnable, deterministic, whole-working-thing-first, no
24	   placeholders. Time-to-first-hello-world is the single most-cited conversion lever.
25	2. **Diátaxis four-mode separation** — tutorial / how-to / reference / explanation, never mixed
26	   (mixing is the most-cited cause of confusing docs).
27	3. **Deterministic, real, copy-pasteable examples everywhere** — no `<YOUR_VALUE_HERE>`
28	   placeholders; real values that run. Serves humans *and* agents.
29	4. **Dual-depth agent exports** — `llms.txt` (slim curated index) + `llms-full.txt` (full
30	   concatenated export), auto-generated in CI, plus markdown content negotiation (a `.md` suffix
31	   alongside each `.html`, or `Accept: text/markdown`) which cuts agent token use ~90% vs HTML.
32	5. **One canonical way to do X** — explicitly mark deprecated / alternate paths. Non-determinism
33	   is fatal for agents.
34	6. **Complete reference, auto-generated, all error variants** — generate API reference from the
35	   code for your public surface, plus any machine-readable contract/schema. Generic placeholders
36	   in reference are a failure mode.
37	7. **Consistent conversational voice** — adopt the Google developer style guide: second person
38	   ("you"), active voice, knowledgeable-friend tone.
39	8. **Explain *why*, not just *what*** — conceptual docs + diagrams for the project's core model
40	   and state machines.
41	9. **Stable, never-renamed anchors / URLs** — agents cache links.
42	10. **Docs-as-product, gated in CI** — generation (API reference, `llms.txt`, schema, CLI help)
43	    is part of the build so docs can never drift from code. Drift is the #1 most-cited docs
44	    failure; this kills it structurally.
45
46	## Information architecture — by capability vertical
47
48	Organize by capability, each vertical carrying the same four Diátaxis modes internally (the
49	Cloudflare pattern):
50
51	**The `/spt:*` skills · `/spt:setup` · the `cc` launcher · ccs profiles · subnet setup · the
52	`[digest]` extractor.**
53
54	Global sequence (Rust Book logic): early runnable project → dependency-ordered concepts →
55	capstone last. Per-vertical internal template (Django labels × Cloudflare ordering):
56	`Overview (why + diagram) · Quickstart/Tutorial · How-to guides · Reference (generated) ·
57	llms.txt`.
58
59	## Killer quickstart targets
60
61	Define one per audience — e.g. a **human dev** quickstart (the core primitive end-to-end in one
62	command + minimal config, < 10 min, whole-thing-first then decomposed) and, if you have
63	integrators, a **dev-agent integrator** quickstart (the minimal "build against this" hello-world
64	for `<your integration surface>`). Zero placeholders; every value runs.
65
66	## Agent-consumable docs
67
68	- **`llms.txt` / `llms-full.txt`** — auto-emitted in CI; the slim index answers quick questions,
69	  the full export feeds deep ingestion. Two-level (Cloudflare pattern): a curated root index that
70	  fans out to per-vertical `llms.txt`; `llms-full.txt` is CI-concatenated page bodies
71	  (generation-only, never hand-authored).
72	- **Markdown content negotiation** + deterministic include/exclude tags so agent exports carry
73	  the canonical path and drop human-only narrative.
74	- **Machine-readable contract/schema** (e.g. JSON Schema) at a stable, discoverable path, if your
75	  project has one — the schema *is* documentation.
76	- **CLI help as first-class agent docs**, if you ship a CLI — `<cmd> --help` complete, exampled,
77	  and ideally machine-readable (a `--json` / structured help mode). The CLI surface is a
78	  documentation surface.
79
80	## Site generator: mdBook + custom theme CSS
81
82	**mdBook** is the generator. The **shared theme** lives in `docs-site/theme/` (a
83	Starlight-inspired skin reused across consumer projects — re-point the accent to rebrand);
84	**Astro Starlight is the styling north star** (copy its look / feel in the theme CSS, not its
85	toolchain). Raw `.md` is published alongside each rendered page (`/x.html` ↔ `/x.md`) for the
86	agent-export convention; `llms.txt` / `llms-full.txt` / any schema are static assets at site root.
87
88	## CI commitments
89
90	Generated reference, the schema, `llms.txt` / `llms-full.txt`, and CLI help exports are
91	**generated and checked in CI** — a doc-drift gate. The mdBook build + GitHub Pages publish run
92	in the same pipeline. Doc quality lives on the same footing as tests.
93
94	## Anti-patterns to design against (most-cited failures)
95
96	Doc / code drift (#1 — solved by CI gating); *what* without *why*; too much setup before first
97	success; generic placeholders in reference; mixed Diátaxis modes; poor search / navigation;
98	multiple non-canonical ways to do X (fatal for agents).
99

# Documentation strategy

> A **designed-in commitment**, not an afterthought — grounded in research on
> critically-acclaimed developer docs (Stripe, Twilio, the Rust Book, FastAPI, Cloudflare,
> Anthropic, Diátaxis, `llms.txt`, the Google developer style guide). Governs the **shipped
> product docs** (authored as code lands). The planning docs (CONTEXT / ADRs / design docs) are
> internal and separate.
>
> **Where the docs live (the reframe):** docs live in **this same repo** under `docs-site/`.
> CI builds them with **mdBook** and **publishes to GitHub Pages from this repo**, gated against
> drift. (No separate releases/publish repo — source of truth and published site are one repo.)

## The defining constraint: a dual audience

These docs serve **two readers at once** — human developers *and* the **AI dev-agents that
build on or integrate with this project**. This is the design constraint: every artifact is
authored **once in clean markdown** and served in **two depths** (human-rendered + agent-export).
The agent layer is **first-class, not optional** — make it so good that a dev-agent integrates
correctly on the first try.

## Principles (top techniques, prioritized)

1. **Sub-10-minute killer quickstart** — runnable, deterministic, whole-working-thing-first, no
   placeholders. Time-to-first-hello-world is the single most-cited conversion lever.
2. **Diátaxis four-mode separation** — tutorial / how-to / reference / explanation, never mixed
   (mixing is the most-cited cause of confusing docs).
3. **Deterministic, real, copy-pasteable examples everywhere** — no `<YOUR_VALUE_HERE>`
   placeholders; real values that run. Serves humans *and* agents.
4. **Dual-depth agent exports** — `llms.txt` (slim curated index) + `llms-full.txt` (full
   concatenated export), auto-generated in CI, plus markdown content negotiation (a `.md` suffix
   alongside each `.html`, or `Accept: text/markdown`) which cuts agent token use ~90% vs HTML.
5. **One canonical way to do X** — explicitly mark deprecated / alternate paths. Non-determinism
   is fatal for agents.
6. **Complete reference, auto-generated, all error variants** — generate API reference from the
   code for your public surface, plus any machine-readable contract/schema. Generic placeholders
   in reference are a failure mode.
7. **Consistent conversational voice** — adopt the Google developer style guide: second person
   ("you"), active voice, knowledgeable-friend tone.
8. **Explain *why*, not just *what*** — conceptual docs + diagrams for the project's core model
   and state machines.
9. **Stable, never-renamed anchors / URLs** — agents cache links.
10. **Docs-as-product, gated in CI** — generation (API reference, `llms.txt`, schema, CLI help)
    is part of the build so docs can never drift from code. Drift is the #1 most-cited docs
    failure; this kills it structurally.

## Information architecture — by capability vertical

Organize by capability, each vertical carrying the same four Diátaxis modes internally (the
Cloudflare pattern):

**The `/spt:*` skills · `/spt:setup` · the `cc` launcher · ccs profiles · subnet setup · the
`[digest]` extractor.**

Global sequence (Rust Book logic): early runnable project → dependency-ordered concepts →
capstone last. Per-vertical internal template (Django labels × Cloudflare ordering):
`Overview (why + diagram) · Quickstart/Tutorial · How-to guides · Reference (generated) ·
llms.txt`.

## Killer quickstart targets

Define one per audience — e.g. a **human dev** quickstart (the core primitive end-to-end in one
command + minimal config, < 10 min, whole-thing-first then decomposed) and, if you have
integrators, a **dev-agent integrator** quickstart (the minimal "build against this" hello-world
for `<your integration surface>`). Zero placeholders; every value runs.

## Agent-consumable docs

- **`llms.txt` / `llms-full.txt`** — auto-emitted in CI; the slim index answers quick questions,
  the full export feeds deep ingestion. Two-level (Cloudflare pattern): a curated root index that
  fans out to per-vertical `llms.txt`; `llms-full.txt` is CI-concatenated page bodies
  (generation-only, never hand-authored).
- **Markdown content negotiation** + deterministic include/exclude tags so agent exports carry
  the canonical path and drop human-only narrative.
- **Machine-readable contract/schema** (e.g. JSON Schema) at a stable, discoverable path, if your
  project has one — the schema *is* documentation.
- **CLI help as first-class agent docs**, if you ship a CLI — `<cmd> --help` complete, exampled,
  and ideally machine-readable (a `--json` / structured help mode). The CLI surface is a
  documentation surface.

## Site generator: mdBook + custom theme CSS

**mdBook** is the generator. The **shared theme** lives in `docs-site/theme/` (a
Starlight-inspired skin reused across consumer projects — re-point the accent to rebrand);
**Astro Starlight is the styling north star** (copy its look / feel in the theme CSS, not its
toolchain). Raw `.md` is published alongside each rendered page (`/x.html` ↔ `/x.md`) for the
agent-export convention; `llms.txt` / `llms-full.txt` / any schema are static assets at site root.

## CI commitments

Generated reference, the schema, `llms.txt` / `llms-full.txt`, and CLI help exports are
**generated and checked in CI** — a doc-drift gate. The mdBook build + GitHub Pages publish run
in the same pipeline. Doc quality lives on the same footing as tests.

## Anti-patterns to design against (most-cited failures)

Doc / code drift (#1 — solved by CI gating); *what* without *why*; too much setup before first
success; generic placeholders in reference; mixed Diátaxis modes; poor search / navigation;
multiple non-canonical ways to do X (fatal for agents).


ADR template + docs-strategy clear. Writing ADR-0001 (distribution split) — doc evidence for 4 skeleton REQs.


Write

<!-- [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 is an empirical assumption.** The design assumes
  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)
- **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.

## Consequences

- 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. 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).
- Forces the cplugs **plugin-name** question: because CC invokes skills as `/<plugin>:<skill>`,
  the plugin name determines the skill prefix. Keeping `/spt:` parity vs. matching the adapter
  identity (`claude-spt`) is an open branding/parity call (held; see `SKELETON-PLAN.md`).
- 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`.


<owl_messages>
<owl_message from="doyle" priority="high">NAME RULING (operator): plugin name = `sptc` for now. Skills → /sptc:*. SUCCESSION PLAN: once spt-claude-code proves parity, flip the switch to TAKE OVER the `spt` plugin slot (plugin.json name sptc→spt, skills become /spt:*) — at which point legacy owl's cplugs `spt` plugin gets retired/renamed (coordinate that; can't have two `spt`).

Your single PLUGIN_NAME marker = exactly the right seam. Set it to `sptc` now; the later sptc→spt flip is the same one-substitution. Good instinct holding + parameterizing.

And ACK your concession — yes, CC invokes /<plugin>:<skill>, prefix hard-tied to plugin.json name, no override (confirmed it via claude-code-guide against the official plugin docs). Your corrected model is right.

Proceed: sptc-named skeleton scaffold, ADR-0001 split, UPS-fires-on-/sptc:X empirical test (note: test against /sptc: now, since that's the live prefix until the flip). Report the UPS finding — it gates the UPS-injection design. I ping you on M12-live for the strings/shortcut_basename/endpoint-run sequence.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


File created successfully at: C:\Users\decid\Documents\projects\spt-claude-code\docs\adr\0001-distribution-splits-by-volatility.md (file state is current in your context — no need to Read it back)

<!-- [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 is an empirical assumption.** The design assumes
  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)
- **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.

## Consequences

- 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. 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).
- Forces the cplugs **plugin-name** question: because CC invokes skills as `/<plugin>:<skill>`,
  the plugin name determines the skill prefix. Keeping `/spt:` parity vs. matching the adapter
  identity (`claude-spt`) is an open branding/parity call (held; see `SKELETON-PLAN.md`).
- 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`.


Name ruling received: plugin = **`sptc`** (skills `/sptc:*`), succession plan flip `sptc`→`spt` once parity proven. CC prefix confirmed hard-tied to plugin.json name (no override). Acknowledging, then applying the name across ADR/runbook/plan and scaffolding.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && $OWL send doyle perri <<'EOF'
ACK name ruling: plugin=`sptc`, skills /sptc:*, succession sptc->spt on parity-proof (coordinate legacy `spt` retirement at flip). Recording the ruling + succession in ADR-0001, RELEASE-RUNBOOK, SKELETON-PLAN. sptc is now the single-substitution seam (plugin.json name + dir + skill prefixes); the later flip = s/sptc/spt/.

Also ACK your claude-code-guide confirmation (prefix hard-tied to plugin.json name, no override) — matches my corrected model.

Scaffolding sptc skeleton now: plugin.json (name sptc), KEEP skill skeletons + /sptc:version, hooks.json->spt api. One accuracy gate: hooks.json needs the PUBLISHED `spt api` hook-subcommand contract (legacy called owl.exe subcommands directly; the public spt api naming may differ). Pulling it from the spt-releases GH Pages harness-contract; if the api hook surface isn't in the public docs, that's a FINDING and I send it to you rather than guess. UPS-fires-on-/sptc:X empirical test queued next (testing /sptc: per your note) — will report the finding since it gates UPS-injection.
EOF
echo "sent=$?"

Ack name ruling to doyle


➜ SENT:doyle
sent=0


Now recording the ruling in ADR-0001.


Edit

- Forces the cplugs **plugin-name** question: because CC invokes skills as `/<plugin>:<skill>`,
  the plugin name determines the skill prefix. Keeping `/spt:` parity vs. matching the adapter
  identity (`claude-spt`) is an open branding/parity call (held; see `SKELETON-PLAN.md`).

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


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

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

<!-- [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 is an empirical assumption.** The design assumes
  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)
- **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.

## Consequences

- 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. 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).
- Forces the cplugs **plugin-name** question: because CC invokes skills as `/<plugin>:<skill>`,
  the plugin name determines the skill prefix. Keeping `/spt:` parity vs. matching the adapter
  identity (`claude-spt`) is an open branding/parity call (held; see `SKELETON-PLAN.md`).
- 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`.

-- Forces the cplugs **plugin-name** question: because CC invokes skills as `/<plugin>:<skill>`,

-  the plugin name determines the skill prefix. Keeping `/spt:` parity vs. matching the adapter

-  identity (`claude-spt`) is an open branding/parity call (held; see `SKELETON-PLAN.md`).

+- 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/`.


Now RELEASE-RUNBOOK: resolve `<plugin>` → `sptc` and convert the OPEN note.


# 2. Copy skeleton files (NO binary) into the marketplace clone:
MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/<plugin>
cp -r skills/*  "$MARKET/skills/"
cp -r hooks/*   "$MARKET/hooks/"
cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
# 3. Commit + push the cplugs repo:
cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/<plugin>/ \
  && git commit -m "<plugin>: <change>" && git push
```

**Pointer flip** (the authoritative install-state update):

```bash
claude plugin install <plugin>@cplugs   # un-orphans + rewrites installed_plugins.json atomically
# then, inside Claude Code:
/reload-plugins
```

# 2. Copy skeleton files (NO binary) into the marketplace clone:
MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/sptc
cp -r skills/*  "$MARKET/skills/"
cp -r hooks/*   "$MARKET/hooks/"
cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
# 3. Commit + push the cplugs repo:
cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/sptc/ \
  && git commit -m "sptc: <change>" && git push
```

**Pointer flip** (the authoritative install-state update):

```bash
claude plugin install sptc@cplugs   # un-orphans + rewrites installed_plugins.json atomically
# then, inside Claude Code:
/reload-plugins
```