2. **[OPEN — fold into Wave C′ packaging] Binary resolution for `claude-spt-digest` / `claude-spt-psyche`.**
   The `--release` `adapter.spt` tar **includes** the binaries at the archive root (extracted to the
   durable home). BUT our command templates reference them by **bare name** ⇒ resolved from **PATH**,
   not the durable home. So packing them only helps if spt-core runs templates with the adapter home
   on PATH / cwd — **unverified**. Two clean options: **(a)** ship both binaries **on PATH** via the
   installer (keeps bare-name templates portable; lean), or **(b)** reference them by a path relative
   to the adapter home in the manifest + rely on the archive extraction. **Decide when building the
   release-asset packaging (Wave C′).** Activation/registration does NOT need the binaries — only
   runtime digest/Psyche do — so this never blocks the activation step.

# JIT plan — `/sptc:setup` activation bridge (F-005) → grow toward LOCKED v1 setup {1–7}

> Next slice after the cplugs first-publish (b1e2895). Goal: make `/sptc:setup` actually
> **activate** the claude-spt adapter after the binary check (the F-005 post-install bridge), so a
> freshly-installed plugin yields a *registered & active* adapter — not a `deregistered` one. Then
> stage the richer LOCKED v1 setup paths (`SCOPE.md` §"/spt:setup" = {1..7}) as later waves.
> Public-surface-only still binds (AGENTS.md).

## Why (the dogfooding finding, 2026-06-15)

An agent ran `/sptc:setup` on a fresh install: `spt 0.7.2` present → skill reported "No install
needed" and **stopped**. But `spt adapter list` showed **`claude-spt: … deregistered`** — the
adapter surface (profiles/strings/hints/`[digest]`) was inert, `/sptc:*` UPS-injection had nothing
to source. **Binary present ≠ adapter active.** doyle triaged F-005 (docs/SPT-CORE-FINDINGS.md) and
confirmed the missing step: after the binary check, setup must run
**`spt adapter add [--github <repo>]`** to ACTIVATE. The current skill has no such step, and (Point
2, operator) no already-installed branch at all — it is a degenerate installer-floor stub far below
the LOCKED v1 `{1..7}` scope.

## Operator decisions (this session, 2026-06-15)

1. **Proceed with the setup slice (B).** Activation (A — un-break the local adapter) is fulfilled by
   the operator **dogfooding the plugin**, not a separate step here.
2. **End-user `--github` target = `SaberMage/spt-claude-code`** (operator: "this repo is published
   there"). ⚠ See Open Question 1 — the monorepo layout conflicts with `adapter add --github`'s
   root-`manifest.toml` expectation; topology must be resolved before the end-user branch is wired.

## Ground truth (verified against live `spt 0.7.2` this session)

- `spt adapter add --help`: PATH = **"a dir holding `manifest.toml`, or the manifest file itself"**;
  `--github user/repo` clones under `adapters/_github/`, **manifest-first**, then conducts the
  declared `[update]` avenue once. **No subpath flag.** "Install is the first update."
- Our manifest is **`adapter/claude-spt.toml`** (a subdir, *not* named `manifest.toml`); no
  `manifest.toml` exists anywhere in the repo. → the **local** activation form that works today is
  the *manifest-file-itself* form: `spt adapter add ./adapter/claude-spt.toml`.
- `claude-spt` reads **`deregistered`** in the registry (Phase-D int tests register→clean-up by
  design) — re-adding re-activates.
- Extractor/runner = `tools/claude-spt-digest` + `tools/claude-spt-psyche` (source). Command
  templates are **opaque, resolved from PATH at runtime** — so for the adapter to *function* (digest
  + Psyche), those binaries must be **built and on PATH**, independent of manifest registration.

## Open questions

1. **[RESOLVED — doyle shipped `--release`, owl 2026-06-15] End-user acquisition. NO dedicated repo.**
   doyle shipped a new acquisition source — **`spt adapter add --release <user/repo> [--tag <ver>]`**
   — that fetches a published **`adapter.spt`** release asset (a tar whose **root** holds
   `manifest.toml` + `strings/` + the binaries) from the repo's GitHub **release**, extracts to the
   durable home, and registers the root. **So we ship straight from THIS monorepo — no dedicated
   root-manifest repo needed** (operator: "no separate repo"). First-acquisition trusts HTTPS+GitHub
   like the install one-liner; it's **acquisition only** (does NOT change the `[update]` route;
   re-running `--release --tag <newer>` is a manual re-acquire). Recommended over `--github`.
   Docs (revised): `install-on-demand.html#activate-the-adapter--register-your-manifest`.
   - **Local dev:** file-form `spt adapter add ./adapter/claude-spt.toml` (any path/filename).
   - **End-user:** `spt adapter add --release SaberMage/spt-claude-code` (latest) / `--tag <ver>`.
   - ⚠ **Version gate:** local spt is 0.7.2, which has only `--github` + local path — `--release`
     ships in a **newer** spt release. Authoring is unblocked; the int/dogfood waits on the upgrade.
   - **Superseded:** the root-only `--github`/`source_manifest_file` constraint (dir → exact
     `<dir>/manifest.toml`, no subpath — code-confirmed, a doc-gap doyle patched) still holds, but
     `--release` routes around it. The old "dedicated repo" plan (former Wave C) is **dropped**.
2. **[RESOLVED via Q3 ruling] End-user binary delivery for `claude-spt-digest` / `claude-spt-psyche`.**
   doyle: the manifest-referenced **binaries are NOT auto-copied** in either mode (only `strings/`
   is). So they must be **resolvable on the target**: absolute path, on PATH, or — in our pointer
   mode — **shipped in the `--github` repo at the manifest's referenced paths** so they resolve from
   the durable `_github` clone. **Action:** our command templates reference the binaries by **bare
   name** (`claude-spt-digest …`, `claude-spt-psyche …`) ⇒ resolved from **PATH**. So end-user
   delivery must put both on PATH (ride the installer) OR we switch the manifest to clone-relative
   paths + ship the binaries in the Wave-C repo. **Decide in Wave C** (lean: ship on PATH via the
   installer — keeps templates portable). Folded into Wave C.
3. **Setup context detection.** Skill probes for a local `adapter/claude-spt.toml` (operator
   dogfooding from a checkout → file-form) else uses `--github <Wave-C repo>` (casual end-user).

## Waves

### Wave 1 — the activation bridge (fulfills A on dogfood) — `REQ-SETUP-ACTIVATE`
**Scope:** make `/sptc:setup`'s binary-present path **activate + verify**, not no-op.
- Rewrite step 1 of both bodies (the self-contained `plugin/sptc/skills/setup/SKILL.md` floor **and**
  the file-backed `adapter/strings/skills/setup.md`): after `command -v spt && spt --version`, check
  whether `claude-spt` is **registered & active** (`spt adapter list` parse). If active → report +
  stop. If absent/`deregistered` → **activate**:
  - **local dev / operator dogfood:** `spt adapter add ./adapter/claude-spt.toml` (manifest-file
    form) when a repo-local manifest is detectable (OQ3).
  - **end-user:** `spt adapter add --github <target>` — **wired once OQ1 resolves** (target TBD).
- **Verify** activation: `spt adapter list` shows `claude-spt` active (not `deregistered`); spot a
  profile resolve (`spt adapter get-string claude-spt:live …` or `spt api … capability`).
- Keep the absent-binary install path (current step 2/3) intact — install **then** activate.
- Idempotent + safe to re-run (mirrors the bootstrap's contract).
- **Evidence:** doc = both bodies; impl = the activation+verify step; int = a registration
  re-activate assertion (`ci/manifest/registration-int.sh` extension, or a new `setup-activate-int`)
  — `deregistered → active`, profile resolves, registry left clean. Activate
  `REQ-SETUP-ACTIVATE = ["doc","impl","int"]`.
- **Note the binary-on-PATH caveat (OQ2)** in the body: activation registers the manifest;
  digest/Psyche need the tool binaries present (built + on PATH) to *function*.

### Wave C — the dedicated published adapter repo (the end-user `--github` target) — **on A's critical path**
**Why:** the operator's A-fulfilling dogfood is "install the **plugin**" (the end-user path) → setup
runs `adapter add --github <repo>` → so that repo must EXIST and be correctly shaped. The monorepo
`SaberMage/spt-claude-code` **cannot** be it (root-only rule). **Decision needed (operator):** repo
name + creation (SaberMage org credentials) + the binary-delivery choice (OQ2: on-PATH-via-installer
vs clone-relative-paths-in-repo).
- Lay out repo **root** = `manifest.toml` (the monorepo's `adapter/claude-spt.toml`, renamed) +
  `strings/` (copied) + (per OQ2 decision) the built `claude-spt-digest` + `claude-spt-psyche`.
- A `ci/publish/` step that **generates** this repo from the monorepo `adapter/` (rename + copy +
  drop binaries) so the two never drift — mirrors `package-skeleton.sh` for the cplugs side.
- Verify end-to-end: a clean machine `spt adapter add --github <repo>` → active + a profile resolves
  + digest/Psyche binaries resolve. New REQ (`REQ-DIST-ADAPTER-REPO`) when this wave starts.

### Later waves — LOCKED v1 setup paths {1..7} (each its own slice + REQ; deps flagged)
Staged, not built now (JIT). From `SCOPE.md` §"/spt:setup" LOCKED {1,2,3,4,5,6,7}:
- **#4 already-installed branch** (new subnet / join=add-this-machine / show-code / just-add-endpoint)
  — the natural extension of Wave 1's active-adapter path; pairs with `/sptc:subnet` (already ships).
- **#1 `cc`/`cc <id>` launcher** at project root — **dep:** full-fat M12 `spt endpoint run` picker
  ("lands in a later wave" per `endpoint run --help`; REQ-DIST-SHORTCUT-BASENAME int is held on it).
- **#2** offer `.gitignore` the launchers (rides #1).
- **#3 create first subnet** — QR of the TOTP seed + self-elevating window (elevation design LOCKED,
  `SCOPE.md`; unbuilt). **dep:** elevation machinery.
- **#5 legacy owl migration** — detect `claude_skill_owl`/owl → migrate identity+agents+psyche.
- **#6 OS-service registration** (always-on daemon) — **dep:** spt-core service support.
- **#7 ccs profile wiring** — if `~/.ccs` present; also offer to install ccs. Pairs with the shipped
  `claude-spt:ccs` profile (REQ-CCS-PROFILES).
- Deferred per SCOPE: **#8** off-subnet Psyche sync, **#9** doctor/verify-at-end (operator: "except 9").

## Gate (every wave)
`sh ci/run-gates.sh` PASS + `traceable-reqs check` green. Commit `Co-authored by: perri`.

## Status
- **OQ1/OQ2 RESOLVED (doyle, owl 2026-06-15).** Repo topology decided: monorepo file-form for dev;
  dedicated root-`manifest.toml` repo for `--github` (pointer mode, binaries on PATH). Wave 1 is now
  **fully unblocked** — authoring both activation branches.
- **Wave C is on A's critical path:** the operator's "dogfood the plugin" = end-user path = needs the
  dedicated `--github` repo to exist. Surfaced to operator (repo name + creation + OQ2 binary choice).

-2. **[RESOLVED via Q3 ruling] End-user binary delivery for `claude-spt-digest` / `claude-spt-psyche`.**

-   doyle: the manifest-referenced **binaries are NOT auto-copied** in either mode (only `strings/`

-   is). So they must be **resolvable on the target**: absolute path, on PATH, or — in our pointer

-   mode — **shipped in the `--github` repo at the manifest's referenced paths** so they resolve from

-   the durable `_github` clone. **Action:** our command templates reference the binaries by **bare

-   name** (`claude-spt-digest …`, `claude-spt-psyche …`) ⇒ resolved from **PATH**. So end-user

-   delivery must put both on PATH (ride the installer) OR we switch the manifest to clone-relative

-   paths + ship the binaries in the Wave-C repo. **Decide in Wave C** (lean: ship on PATH via the

-   installer — keeps templates portable). Folded into Wave C.

+2. **[OPEN — fold into Wave C′ packaging] Binary resolution for `claude-spt-digest` / `claude-spt-psyche`.**

+   The `--release` `adapter.spt` tar **includes** the binaries at the archive root (extracted to the

+   durable home). BUT our command templates reference them by **bare name** ⇒ resolved from **PATH**,

+   not the durable home. So packing them only helps if spt-core runs templates with the adapter home

+   on PATH / cwd — **unverified**. Two clean options: **(a)** ship both binaries **on PATH** via the

+   installer (keeps bare-name templates portable; lean), or **(b)** reference them by a path relative

+   to the adapter home in the manifest + rely on the archive extraction. **Decide when building the

+   release-asset packaging (Wave C′).** Activation/registration does NOT need the binaries — only

+   runtime digest/Psyche do — so this never blocks the activation step.


proceed with B, which will be used to fulfill A thru dogfooding the plugin (i'll do it). this repo is published at https://github.com/SaberMage/spt-claude-code


Edit

### Wave C — the dedicated published adapter repo (the end-user `--github` target) — **on A's critical path**
**Why:** the operator's A-fulfilling dogfood is "install the **plugin**" (the end-user path) → setup
runs `adapter add --github <repo>` → so that repo must EXIST and be correctly shaped. The monorepo
`SaberMage/spt-claude-code` **cannot** be it (root-only rule). **Decision needed (operator):** repo
name + creation (SaberMage org credentials) + the binary-delivery choice (OQ2: on-PATH-via-installer
vs clone-relative-paths-in-repo).
- Lay out repo **root** = `manifest.toml` (the monorepo's `adapter/claude-spt.toml`, renamed) +
  `strings/` (copied) + (per OQ2 decision) the built `claude-spt-digest` + `claude-spt-psyche`.
- A `ci/publish/` step that **generates** this repo from the monorepo `adapter/` (rename + copy +
  drop binaries) so the two never drift — mirrors `package-skeleton.sh` for the cplugs side.
- Verify end-to-end: a clean machine `spt adapter add --github <repo>` → active + a profile resolves
  + digest/Psyche binaries resolve. New REQ (`REQ-DIST-ADAPTER-REPO`) when this wave starts.

### Wave C′ — release-asset packaging (`adapter.spt`) — the end-user `--release` target — **on A's critical path**
**Replaces the dropped "dedicated repo" Wave C.** doyle's `--release` ships the adapter from THIS
monorepo as a GitHub **release asset** — no separate repo.
- A `ci/publish/` step (mirrors `package-skeleton.sh`) that **packs** `adapter.spt`:
  `tar -czf adapter.spt -C adapter manifest.toml strings/ …` — **but** our manifest is
  `adapter/claude-spt.toml`, and the tar **root must hold `manifest.toml` named exactly that**. So
  the packer renames `claude-spt.toml → manifest.toml` inside the archive + adds the built
  `claude-spt-digest` + `claude-spt-psyche` (release builds) + (per OQ2) lays them where they
  resolve. Default asset name `adapter.spt` (override `--asset`).
- Upload `adapter.spt` as a GitHub release asset on the monorepo (operator: release/tag + push).
- Verify end-to-end (version-gated on the spt release carrying `--release`): a clean machine
  `spt adapter add --release SaberMage/spt-claude-code` → active + a profile resolves + digest/Psyche
  binaries resolve. New REQ (`REQ-DIST-ADAPTER-RELEASE`) when this wave starts.


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

### Wave C — the dedicated published adapter repo (the end-user `--github` target) — **on A's critical path**
**Why:** the operator's A-fulfilling dogfood is "install the **plugin**" (the end-user path) → setup
runs `adapter add --github <repo>` → so that repo must EXIST and be correctly shaped. The monorepo
`SaberMage/spt-claude-code` **cannot** be it (root-only rule). **Decision needed (operator):** repo
name + creation (SaberMage org credentials) + the binary-delivery choice (OQ2: on-PATH-via-installer
vs clone-relative-paths-in-repo).
- Lay out repo **root** = `manifest.toml` (the monorepo's `adapter/claude-spt.toml`, renamed) +
  `strings/` (copied) + (per OQ2 decision) the built `claude-spt-digest` + `claude-spt-psyche`.
- A `ci/publish/` step that **generates** this repo from the monorepo `adapter/` (rename + copy +
  drop binaries) so the two never drift — mirrors `package-skeleton.sh` for the cplugs side.
- Verify end-to-end: a clean machine `spt adapter add --github <repo>` → active + a profile resolves
  + digest/Psyche binaries resolve. New REQ (`REQ-DIST-ADAPTER-REPO`) when this wave starts.

### Wave C′ — release-asset packaging (`adapter.spt`) — the end-user `--release` target — **on A's critical path**
**Replaces the dropped "dedicated repo" Wave C.** doyle's `--release` ships the adapter from THIS
monorepo as a GitHub **release asset** — no separate repo.
- A `ci/publish/` step (mirrors `package-skeleton.sh`) that **packs** `adapter.spt`:
  `tar -czf adapter.spt -C adapter manifest.toml strings/ …` — **but** our manifest is
  `adapter/claude-spt.toml`, and the tar **root must hold `manifest.toml` named exactly that**. So
  the packer renames `claude-spt.toml → manifest.toml` inside the archive + adds the built
  `claude-spt-digest` + `claude-spt-psyche` (release builds) + (per OQ2) lays them where they
  resolve. Default asset name `adapter.spt` (override `--asset`).
- Upload `adapter.spt` as a GitHub release asset on the monorepo (operator: release/tag + push).
- Verify end-to-end (version-gated on the spt release carrying `--release`): a clean machine
  `spt adapter add --release SaberMage/spt-claude-code` → active + a profile resolves + digest/Psyche
  binaries resolve. New REQ (`REQ-DIST-ADAPTER-RELEASE`) when this wave starts.

- **Note the binary-on-PATH caveat (OQ2)** in the body: activation registers the manifest;

digest/Psyche need the tool binaries present (built + on PATH) to *function*.

-### Wave C — the dedicated published adapter repo (the end-user `--github` target) — **on A's critical path**

-**Why:** the operator's A-fulfilling dogfood is "install the **plugin**" (the end-user path) → setup

-runs `adapter add --github <repo>` → so that repo must EXIST and be correctly shaped. The monorepo

-`SaberMage/spt-claude-code` **cannot** be it (root-only rule). **Decision needed (operator):** repo

-name + creation (SaberMage org credentials) + the binary-delivery choice (OQ2: on-PATH-via-installer

-- Lay out repo **root** = `manifest.toml` (the monorepo's `adapter/claude-spt.toml`, renamed) +

-  `strings/` (copied) + (per OQ2 decision) the built `claude-spt-digest` + `claude-spt-psyche`.

-- A `ci/publish/` step that **generates** this repo from the monorepo `adapter/` (rename + copy +

-  drop binaries) so the two never drift — mirrors `package-skeleton.sh` for the cplugs side.

-- Verify end-to-end: a clean machine `spt adapter add --github <repo>` → active + a profile resolves

-  + digest/Psyche binaries resolve. New REQ (`REQ-DIST-ADAPTER-REPO`) when this wave starts.

+### Wave C′ — release-asset packaging (`adapter.spt`) — the end-user `--release` target — **on A's critical path**

+**Replaces the dropped "dedicated repo" Wave C.** doyle's `--release` ships the adapter from THIS

+monorepo as a GitHub **release asset** — no separate repo.

+- A `ci/publish/` step (mirrors `package-skeleton.sh`) that **packs** `adapter.spt`:

+  `tar -czf adapter.spt -C adapter manifest.toml strings/ …` — **but** our manifest is

+  `adapter/claude-spt.toml`, and the tar **root must hold `manifest.toml` named exactly that**. So

+  the packer renames `claude-spt.toml → manifest.toml` inside the archive + adds the built

+  `claude-spt-digest` + `claude-spt-psyche` (release builds) + (per OQ2) lays them where they

+  resolve. Default asset name `adapter.spt` (override `--asset`).

+- Upload `adapter.spt` as a GitHub release asset on the monorepo (operator: release/tag + push).

+- Verify end-to-end (version-gated on the spt release carrying `--release`): a clean machine

+  `spt adapter add --release SaberMage/spt-claude-code` → active + a profile resolves + digest/Psyche

+  binaries resolve. New REQ (`REQ-DIST-ADAPTER-RELEASE`) when this wave starts.

### Later waves — LOCKED v1 setup paths {1..7} (each its own slice + REQ; deps flagged)

Staged, not built now (JIT). From `SCOPE.md` §"/spt:setup" LOCKED {1,2,3,4,5,6,7}:


## Status
- **OQ1/OQ2 RESOLVED (doyle, owl 2026-06-15).** Repo topology decided: monorepo file-form for dev;
  dedicated root-`manifest.toml` repo for `--github` (pointer mode, binaries on PATH). Wave 1 is now
  **fully unblocked** — authoring both activation branches.
- **Wave C is on A's critical path:** the operator's "dogfood the plugin" = end-user path = needs the
  dedicated `--github` repo to exist. Surfaced to operator (repo name + creation + OQ2 binary choice).

## Status
- **OQ1 RESOLVED via `--release` (doyle shipped it, owl 2026-06-15).** End-user acquisition ships
  from THIS monorepo as an `adapter.spt` release asset — **no dedicated repo** (operator-aligned).
  Wave 1 bodies updated to the `--release` path.
- **Wave 1 (activation logic) DONE** (`REQ-SETUP-ACTIVATE` doc green) — correct regardless of the
  acquisition source; only the end-user target string changed (`--github` → `--release`).
- **Version gate:** `--release` needs a spt release newer than local 0.7.2 → int/dogfood waits on the
  upgrade. Same gate-pattern as the live relay int.
- **Wave C′ (release-asset packaging) is on A's critical path** for the plugin-dogfood end-user path;
  OQ2 (binary resolution) decided there. Local-dir activation is dogfoolable now.
- **Carry-over:** cplugs republish (the published skeleton still has the pre-activation setup body) +
  optional bootstrap auto-activate.


## Status
- **OQ1/OQ2 RESOLVED (doyle, owl 2026-06-15).** Repo topology decided: monorepo file-form for dev;
  dedicated root-`manifest.toml` repo for `--github` (pointer mode, binaries on PATH). Wave 1 is now
  **fully unblocked** — authoring both activation branches.
- **Wave C is on A's critical path:** the operator's "dogfood the plugin" = end-user path = needs the
  dedicated `--github` repo to exist. Surfaced to operator (repo name + creation + OQ2 binary choice).

## Status
- **OQ1 RESOLVED via `--release` (doyle shipped it, owl 2026-06-15).** End-user acquisition ships
  from THIS monorepo as an `adapter.spt` release asset — **no dedicated repo** (operator-aligned).
  Wave 1 bodies updated to the `--release` path.
- **Wave 1 (activation logic) DONE** (`REQ-SETUP-ACTIVATE` doc green) — correct regardless of the
  acquisition source; only the end-user target string changed (`--github` → `--release`).
- **Version gate:** `--release` needs a spt release newer than local 0.7.2 → int/dogfood waits on the
  upgrade. Same gate-pattern as the live relay int.
- **Wave C′ (release-asset packaging) is on A's critical path** for the plugin-dogfood end-user path;
  OQ2 (binary resolution) decided there. Local-dir activation is dogfoolable now.
- **Carry-over:** cplugs republish (the published skeleton still has the pre-activation setup body) +
  optional bootstrap auto-activate.

`sh ci/run-gates.sh` PASS + `traceable-reqs check` green. Commit `Co-authored by: perri`.

-- **OQ1/OQ2 RESOLVED (doyle, owl 2026-06-15).** Repo topology decided: monorepo file-form for dev;

-  dedicated root-`manifest.toml` repo for `--github` (pointer mode, binaries on PATH). Wave 1 is now

-  **fully unblocked** — authoring both activation branches.

-- **Wave C is on A's critical path:** the operator's "dogfood the plugin" = end-user path = needs the

-  dedicated `--github` repo to exist. Surfaced to operator (repo name + creation + OQ2 binary choice).

+- **OQ1 RESOLVED via `--release` (doyle shipped it, owl 2026-06-15).** End-user acquisition ships

+  from THIS monorepo as an `adapter.spt` release asset — **no dedicated repo** (operator-aligned).

+  Wave 1 bodies updated to the `--release` path.

+- **Wave 1 (activation logic) DONE** (`REQ-SETUP-ACTIVATE` doc green) — correct regardless of the

+  acquisition source; only the end-user target string changed (`--github` → `--release`).

+- **Version gate:** `--release` needs a spt release newer than local 0.7.2 → int/dogfood waits on the

+  upgrade. Same gate-pattern as the live relay int.

+- **Wave C′ (release-asset packaging) is on A's critical path** for the plugin-dogfood end-user path;

+  OQ2 (binary resolution) decided there. Local-dir activation is dogfoolable now.

+- **Carry-over:** cplugs republish (the published skeleton still has the pre-activation setup body) +


Now F-005 distribution-leg-closed note, then gate + commit.


**Status:** **TRIAGED — accepted as (a)+(b) mix.** doyle patches docs; we wire the activation step.

**Status:** **TRIAGED — accepted as (a)+(b) mix.** doyle patches docs; we wire the activation step.

### Resolution (2026-06-15 — doyle shipped `--release`, distribution leg CLOSED)

doyle shipped + published a new acquisition source that closes F-005's end-user distribution leg
without a dedicated repo: **`spt adapter add --release <user/repo> [--tag <ver>]`** fetches a
published **`adapter.spt`** release asset (a tar whose **root** holds `manifest.toml` + `strings/` +
the binaries) from the repo's GitHub release, extracts to the durable home, and registers the root.
**We ship straight from the monorepo as a release asset — no dedicated root-manifest repo** (the old
`--github` root-only constraint, code-confirmed + since doc-patched, is routed around). First
acquisition trusts HTTPS+GitHub like the install one-liner; signing rides the `file_pull` `[update]`
avenue later (this is **acquisition only** — re-running `--release --tag <newer>` is a manual
re-acquire, it does not change the `[update]` route). Docs (revised):
`harness-contract/install-on-demand.html#activate-the-adapter--register-your-manifest`;
`--release` is now the **recommended** distribution, `--github` + local-dir the alternatives.

**Our wiring:** `/sptc:setup` end-user branch → `spt adapter add --release SaberMage/spt-claude-code`
(local dev stays the file-form). **Version gate:** local spt 0.7.2 has only `--github` + local path;
`--release` lands in a newer spt release — int/dogfood waits on the upgrade. The remaining build is
the `adapter.spt` release-asset packer (`SETUP-SLICE-PLAN.md` Wave C′) + OQ2 (binary resolution: bare
-name templates resolve from PATH, so either ship binaries on PATH via the installer or use
home-relative paths). **The `[update]`/self-update channel itself stays unauthored** (our manifest
declares no `[update]` → COPY-mode registration; the signed file_pull self-update is the later,
separate concern — REQ-UPD-1/M4 roadmap + the Ed25519 provisioning doc).


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

**Status:** **TRIAGED — accepted as (a)+(b) mix.** doyle patches docs; we wire the activation step.

### Resolution (2026-06-15 — doyle shipped `--release`, distribution leg CLOSED)

doyle shipped + published a new acquisition source that closes F-005's end-user distribution leg
without a dedicated repo: **`spt adapter add --release <user/repo> [--tag <ver>]`** fetches a
published **`adapter.spt`** release asset (a tar whose **root** holds `manifest.toml` + `strings/` +
the binaries) from the repo's GitHub release, extracts to the durable home, and registers the root.
**We ship straight from the monorepo as a release asset — no dedicated root-manifest repo** (the old
`--github` root-only constraint, code-confirmed + since doc-patched, is routed around). First
acquisition trusts HTTPS+GitHub like the install one-liner; signing rides the `file_pull` `[update]`
avenue later (this is **acquisition only** — re-running `--release --tag <newer>` is a manual
re-acquire, it does not change the `[update]` route). Docs (revised):
`harness-contract/install-on-demand.html#activate-the-adapter--register-your-manifest`;
`--release` is now the **recommended** distribution, `--github` + local-dir the alternatives.

**Our wiring:** `/sptc:setup` end-user branch → `spt adapter add --release SaberMage/spt-claude-code`
(local dev stays the file-form). **Version gate:** local spt 0.7.2 has only `--github` + local path;
`--release` lands in a newer spt release — int/dogfood waits on the upgrade. The remaining build is
the `adapter.spt` release-asset packer (`SETUP-SLICE-PLAN.md` Wave C′) + OQ2 (binary resolution: bare
-name templates resolve from PATH, so either ship binaries on PATH via the installer or use
home-relative paths). **The `[update]`/self-update channel itself stays unauthored** (our manifest
declares no `[update]` → COPY-mode registration; the signed file_pull self-update is the later,
separate concern — REQ-UPD-1/M4 roadmap + the Ed25519 provisioning doc).

(distinct from the cplugs skeleton repo); local dev uses `spt adapter add ./adapter`.

+### Resolution (2026-06-15 — doyle shipped `--release`, distribution leg CLOSED)

+doyle shipped + published a new acquisition source that closes F-005's end-user distribution leg

+without a dedicated repo: **`spt adapter add --release <user/repo> [--tag <ver>]`** fetches a

+published **`adapter.spt`** release asset (a tar whose **root** holds `manifest.toml` + `strings/` +

+the binaries) from the repo's GitHub release, extracts to the durable home, and registers the root.

+**We ship straight from the monorepo as a release asset — no dedicated root-manifest repo** (the old

+`--github` root-only constraint, code-confirmed + since doc-patched, is routed around). First

+acquisition trusts HTTPS+GitHub like the install one-liner; signing rides the `file_pull` `[update]`

+avenue later (this is **acquisition only** — re-running `--release --tag <newer>` is a manual

+re-acquire, it does not change the `[update]` route). Docs (revised):

+`--release` is now the **recommended** distribution, `--github` + local-dir the alternatives.

+**Our wiring:** `/sptc:setup` end-user branch → `spt adapter add --release SaberMage/spt-claude-code`

+(local dev stays the file-form). **Version gate:** local spt 0.7.2 has only `--github` + local path;

+`--release` lands in a newer spt release — int/dogfood waits on the upgrade. The remaining build is

+the `adapter.spt` release-asset packer (`SETUP-SLICE-PLAN.md` Wave C′) + OQ2 (binary resolution: bare

+-name templates resolve from PATH, so either ship binaries on PATH via the installer or use

+home-relative paths). **The `[update]`/self-update channel itself stays unauthored** (our manifest

+declares no `[update]` → COPY-mode registration; the signed file_pull self-update is the later,

+separate concern — REQ-UPD-1/M4 roadmap + the Ed25519 provisioning doc).


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && export PATH="$HOME/.cargo/bin:$HOME/AppData/Local/spt-core/bin:$PATH"; sh ci/run-gates.sh 2>&1 | tail -8; echo "gates_exit=${PIPESTATUS[0]}"

Run gate after --release updates


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

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

=== RESULT: PASS ===
gates_exit=0


cd "C:/Users/decid/Documents/projects/spt-claude-code" && git add adapter/strings/skills/setup.md plugin/sptc/skills/setup/SKILL.md SETUP-SLICE-PLAN.md docs/SPT-CORE-FINDINGS.md && git commit -m "feat(setup): use \`adapter add --release\` for end-user activation (doyle's new source)

doyle shipped \`spt adapter add --release <user/repo> [--tag]\` — fetches an
adapter.spt release asset (tar root = manifest.toml + strings/ + binaries) from
the monorepo's GitHub release. No dedicated repo needed (operator-aligned).
Setup end-user branch now --release SaberMage/spt-claude-code; local dev stays
the file-form. Version-gated: --release lands in a spt release newer than 0.7.2.

Drops the dedicated-repo Wave C for Wave C' (adapter.spt packer). Closes
F-005's distribution leg (acquisition only; [update]/self-update stays later).

Co-authored by: perri" 2>&1 | tail -3

Commit --release update


[main 4dfa746] feat(setup): use `adapter add --release` for end-user activation (doyle's new source)
 4 files changed, 84 insertions(+), 47 deletions(-)


Gate PASS, committed (`4dfa746`). Acking doyle + asking the version gate.


"$OWL" send doyle perri <<'EOF'
Got it — wired. /sptc:setup end-user branch now `spt adapter add --release SaberMage/spt-claude-code` (local dev stays the file-form). Building the adapter.spt packer next (tar root = manifest.toml [renamed from claude-spt.toml] + strings/ + the two release binaries), uploaded as a monorepo release asset. F-005 distribution leg logged CLOSED.

Two notes:
1. Version gate: my local spt is 0.7.2 — `adapter add --help` there shows only --github + local path, no --release. Which release/counter carries --release? Need it to int-test + dogfood the end-user path (currently authoring-only against your published docs).
2. Mental-model correction for your earlier Q3 framing: our manifest declares NO [update] section at all (only a header comment explaining the omission) — so it's the no-[update] COPY-mode path, not delegated/pointer. Doesn't matter for --release (acquisition extracts+registers regardless), just keeping your model exact. The signed [update]/self-update channel stays unauthored on our side (later concern).

Thanks — clean unblock. This ships the adapter from the monorepo with zero new repos.
EOF
echo "exit=$?"

Ack doyle + ask --release version


➜ SENT:doyle
exit=0


**Done — Wave 1 retargeted to `--release`, committed (`4dfa746`), gate PASS.** doyle acked + asked which release carries `--release` (version-gate target).

State now:

| Piece | Status |
|-------|--------|
| Wave 1 — setup activation logic | ✅ done (`REQ-SETUP-ACTIVATE` doc green) |
| End-user acquisition path | ✅ resolved — `adapter add --release SaberMage/spt-claude-code`, ships from **this monorepo**, no separate repo |
| F-005 distribution leg | ✅ closed (acquisition); `[update]`/self-update stays a later concern |
| **Wave C′ — `adapter.spt` packer** | ⏳ not built — the CI step that tars `manifest.toml`(renamed) + `strings/` + binaries → release asset |
| Version gate | ⏳ `--release` needs spt > 0.7.2 (asked doyle the counter); int/dogfood waits on upgrade |
| cplugs republish | ⏳ published skeleton still has pre-activation setup body |

Your `[update]` question, settled: **non-functioning by omission** — our manifest declares no `[update]`, so it's COPY-mode registration; the self-update channel was never authored (needs keys + REQ-UPD-1/M4 roadmap). But `--release` is **acquisition-only** and routes around all of it — end-user *activation* needs none of it. Flagged the COPY-mode correction to doyle for his model.

Two things buildable **now** (version gate only blocks the final dogfood, not the build):
- **Wave C′ packer** — `ci/publish/package-adapter.sh` packing `adapter.spt`. Decides OQ2 (binaries on-PATH-via-installer vs home-relative — I lean **on-PATH**, keeps the bare-name templates portable). On A's critical path.
- **cplugs republish** — ship the activation setup floor to installed plugins (bumps `plugin.json`).

Build the `adapter.spt` packer now (recommend), do the cplugs republish, both, or hold for doyle's version answer first?


<task-notification>
<task-id>bp4sffhfb</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="488b19c0" type="msg" from="doyle">[doyle -&gt; perri] Clean — glad it unblocks. Two answers:

1. VERSION: --release is NOT in any published binary yet. I shipped it to MAIN today (@94bda1e, CI-green both runners), but the latest public release is still v0.7.2 (counter 14) — which is why your local `adapter add --help` shows only --github + loc</EVENT-PART>
<EVENT-PART seq="2/6" id="488b19c0">al. --release rides the NEXT release, counter 15 (the M11 wave bundle; deployah drives merge→bump→tag→sign→publish). So: author against the docs now (you are), and I will PING you when counter-15 publishes — that same ping covers BOTH your live int and your --release dogfood. (Raising the release timing with the operator now — if y</EVENT-PART>
<EVENT-PART seq="3/6" id="488b19c0">ou need it sooner it can be a fast-track patch; their call.)

2. COPY-MODE — your correction is right and I'll fix my model: no [update] section = COPY mode (not delegated/pointer). One nuance that bites HARDER in copy mode, flag for your dogfood: register copies manifest.toml + strings/ ONLY into adapters/&lt;name&gt;/ — it does NOT</EVENT-PART>
<EVENT-PART seq="4/6" id="488b19c0"> copy your two binaries. With --release they extract into adapters/_github/&lt;safe&gt;/ (the durable archive home) but are not copied beside the manifest. So at dogfood verify how your [digest]/[session.psyche_init] command paths resolve on the target and arrange the .spt + manifest paths to match — absolute, or on PATH (your installer can </EVENT-PART>
<EVENT-PART seq="5/6" id="488b19c0">place them), are the robust options. (Pointer mode would keep the binaries readable live from _github, but that needs a [update]=delegated declaration — a bigger choice; don't take it just for this.) If copy-mode binary resolution gets painful in practice, tell me — a &quot;--release copies the bundled binaries too&quot; follow-on is on th</EVENT-PART>
<EVENT-PART seq="6/6" id="488b19c0">e table.

F-005 distribution leg CLOSED, agreed. Carry on the packer.</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 answered both (complete):

1. **Version:** `--release` shipped to **main** (@94bda1e) today but **not in any published release** — rides **counter-15** (the M11 bundle; same gate as the live int). doyle pings when it publishes — **one ping covers both** the live int and the `--release` dogfood. Authoring-now is correct; fast-track patch possible if you need it sooner (operator's call).
2. **COPY-mode confirmed** + the sharp nuance: register copies `manifest.toml` + `strings/` only — **not** the binaries; with `--release` they extract to `adapters/_github/<safe>/` but not beside the manifest. So resolve the tool binaries via **absolute or on-PATH** (installer places them) — my lean confirmed. A "`--release` copies bundled binaries too" follow-on is on the table if it gets painful.

doyle: "carry on the packer." Within your standing "proceed with B" + doyle's direction, building the `adapter.spt` packer now (in-repo, no outward push — dogfood waits on counter-15). Checking the built-binary layout first.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "=== tools layout ==="; ls tools/claude-spt-digest tools/claude-spt-psyche 2>&1; echo "=== Cargo workspace? ==="; [ -f Cargo.toml ] && cat Cargo.toml | head -20; echo "=== built release binaries ==="; find . -path ./node_modules -prune -o -name "claude-spt-digest*" -path "*release*" -print 2>/dev/null; find . -name "claude-spt-psyche*" -path "*release*" -print 2>/dev/null | head; echo "=== existing publish scripts ref ==="; ls ci/publish/ ci/digest/ ci/psyche/ 2>&1

Find binary build layout


=== tools layout ===
tools/claude-spt-digest:
Cargo.lock
Cargo.toml
src
target

tools/claude-spt-psyche:
Cargo.lock
Cargo.toml
src
target
=== Cargo workspace? ===
=== built release binaries ===
./tools/claude-spt-digest/target/release/.fingerprint/claude-spt-digest-17b0c7af280436a9
./tools/claude-spt-digest/target/release/claude-spt-digest.d
./tools/claude-spt-digest/target/release/claude-spt-digest.exe
./tools/claude-spt-psyche/target/release/.fingerprint/claude-spt-psyche-44ea126fe9ecaac9
./tools/claude-spt-psyche/target/release/claude-spt-psyche.d
./tools/claude-spt-psyche/target/release/claude-spt-psyche.exe
=== existing publish scripts ref ===
ci/digest/:
build.sh
digest-proof-int.sh
sample.jsonl

ci/psyche/:
build.sh

ci/publish/:
package-skeleton.sh
validate-skeleton.sh


Binaries at `tools/*/target/release/claude-spt-{digest,psyche}.exe`. Writing the packer. Checking how `run-gates.sh` wires tests so the archive test lands in the gate.


<owl_messages>
<owl_message from="doyle" priority="high">[doyle -> perri] Quick follow-up: operator said fast-track. deployah is cutting patch v0.7.3 from main HEAD NOW — it carries --release (the M11 waves stay on PR#16 for a later release). So your --release dogfood window is much sooner than the full M11 release. I'll ping you the moment v0.7.3 publishes (counter 15); then `adapter add --release` is live in the binary and you can int-test the /sptc:setup end-user path. Keep building the packer.</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.


1	#!/bin/sh
2	# Deterministic CI gates for spt-claude-code — each a binary pass/fail, no model in the loop.
3	# Runnable by hand on any fleet host: this IS the manual fallback. [impl->REQ-CI-GATES]
4	# [impl->REQ-CI-MANUAL]
5	# Gates whose artifact doesn't exist yet SKIP with a logged note (no silent caps — a skip is
6	# always announced so "green" never hides un-run coverage).
7	set -u
8	ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
9	cd "$ROOT" || exit 2
10	rc=0
11	gate() { printf '\n=== GATE: %s ===\n' "$1"; }
12	fail() { printf 'FAIL: %s\n' "$1"; rc=1; }
13
14	gate "shell-syntax (sh -n)"
15	for f in $(find plugin tests ci -name '*.sh' 2>/dev/null | sort); do
16	  if sh -n "$f"; then printf 'ok  %s\n' "$f"; else fail "syntax: $f"; fi
17	done
18
19	gate "unit tests (tests/*.sh)"
20	for t in tests/*.sh; do
21	  [ -e "$t" ] || continue
22	  if sh "$t" >/dev/null 2>&1; then printf 'ok  %s\n' "$t"; else fail "unit: $t"; sh "$t" || true; fi
23	done
24
25	gate "digest-extractor (cargo build + test)"
26	if [ -f "$ROOT/ci/digest/build.sh" ]; then
27	  if sh "$ROOT/ci/digest/build.sh"; then :; else fail "digest-extractor"; fi
28	else
29	  echo "SKIP: no digest extractor build yet"
30	fi
31
32	gate "psyche-runner (cargo build + test)"
33	if [ -f "$ROOT/ci/psyche/build.sh" ]; then
34	  if sh "$ROOT/ci/psyche/build.sh"; then :; else fail "psyche-runner"; fi
35	else
36	  echo "SKIP: no psyche runner build yet"
37	fi
38
39	gate "traceable-reqs check (requirement coverage)"
40	if command -v traceable-reqs >/dev/null 2>&1; then
41	  if traceable-reqs check >/dev/null 2>&1; then echo "ok  coverage green"; else fail "traceable-reqs check"; traceable-reqs check || true; fi
42	else
43	  echo "SKIP: traceable-reqs not on PATH (install per docs/TRACEABILITY.md)"
44	fi
45
46	gate "skeleton-validate (cplugs installability)"
47	if [ -x "$ROOT/ci/publish/validate-skeleton.sh" ] || [ -f "$ROOT/ci/publish/validate-skeleton.sh" ]; then
48	  if sh "$ROOT/ci/publish/validate-skeleton.sh" >/dev/null 2>&1; then echo "ok  skeleton installable"; else fail "skeleton-validate"; sh "$ROOT/ci/publish/validate-skeleton.sh" || true; fi
49	else
50	  echo "SKIP: no skeleton validator yet"
51	fi
52
53	gate "manifest-schema"
54	if [ -f "$ROOT/ci/manifest/check-manifest.sh" ]; then
55	  if sh "$ROOT/ci/manifest/check-manifest.sh"; then :; else fail "manifest-schema"; fi
56	else
57	  echo "SKIP: no adapter manifest yet — activates when the CC adapter manifest lands"
58	fi
59
60	gate "docs-drift"
61	if [ -f "$ROOT/ci/docs/check-docs.sh" ]; then
62	  if sh "$ROOT/ci/docs/check-docs.sh"; then :; else fail "docs-drift"; fi
63	else
64	  echo "SKIP: no docs check yet"
65	fi
66
67	printf '\n=== RESULT: %s ===\n' "$([ "$rc" -eq 0 ] && echo PASS || echo FAIL)"
68	exit "$rc"
69

#!/bin/sh
# Deterministic CI gates for spt-claude-code — each a binary pass/fail, no model in the loop.
# Runnable by hand on any fleet host: this IS the manual fallback. [impl->REQ-CI-GATES]
# [impl->REQ-CI-MANUAL]
# Gates whose artifact doesn't exist yet SKIP with a logged note (no silent caps — a skip is
# always announced so "green" never hides un-run coverage).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
cd "$ROOT" || exit 2
rc=0
gate() { printf '\n=== GATE: %s ===\n' "$1"; }
fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

gate "shell-syntax (sh -n)"
for f in $(find plugin tests ci -name '*.sh' 2>/dev/null | sort); do
  if sh -n "$f"; then printf 'ok  %s\n' "$f"; else fail "syntax: $f"; fi
done

gate "unit tests (tests/*.sh)"
for t in tests/*.sh; do
  [ -e "$t" ] || continue
  if sh "$t" >/dev/null 2>&1; then printf 'ok  %s\n' "$t"; else fail "unit: $t"; sh "$t" || true; fi
done

gate "digest-extractor (cargo build + test)"
if [ -f "$ROOT/ci/digest/build.sh" ]; then
  if sh "$ROOT/ci/digest/build.sh"; then :; else fail "digest-extractor"; fi
else
  echo "SKIP: no digest extractor build yet"
fi

gate "psyche-runner (cargo build + test)"
if [ -f "$ROOT/ci/psyche/build.sh" ]; then
  if sh "$ROOT/ci/psyche/build.sh"; then :; else fail "psyche-runner"; fi
else
  echo "SKIP: no psyche runner build yet"
fi

gate "traceable-reqs check (requirement coverage)"
if command -v traceable-reqs >/dev/null 2>&1; then
  if traceable-reqs check >/dev/null 2>&1; then echo "ok  coverage green"; else fail "traceable-reqs check"; traceable-reqs check || true; fi
else
  echo "SKIP: traceable-reqs not on PATH (install per docs/TRACEABILITY.md)"
fi

gate "skeleton-validate (cplugs installability)"
if [ -x "$ROOT/ci/publish/validate-skeleton.sh" ] || [ -f "$ROOT/ci/publish/validate-skeleton.sh" ]; then
  if sh "$ROOT/ci/publish/validate-skeleton.sh" >/dev/null 2>&1; then echo "ok  skeleton installable"; else fail "skeleton-validate"; sh "$ROOT/ci/publish/validate-skeleton.sh" || true; fi
else
  echo "SKIP: no skeleton validator yet"
fi

gate "manifest-schema"
if [ -f "$ROOT/ci/manifest/check-manifest.sh" ]; then
  if sh "$ROOT/ci/manifest/check-manifest.sh"; then :; else fail "manifest-schema"; fi
else
  echo "SKIP: no adapter manifest yet — activates when the CC adapter manifest lands"
fi

gate "docs-drift"
if [ -f "$ROOT/ci/docs/check-docs.sh" ]; then
  if sh "$ROOT/ci/docs/check-docs.sh"; then :; else fail "docs-drift"; fi
else
  echo "SKIP: no docs check yet"
fi

printf '\n=== RESULT: %s ===\n' "$([ "$rc" -eq 0 ] && echo PASS || echo FAIL)"
exit "$rc"


Ack doyle's fast-track, then continuing the packer.


"$OWL" send doyle perri <<'EOF'
Ack — fast-track noted. v0.7.3 from main HEAD carries --release (counter 15), M11 waves on PR#16 for later. Understood: ping on v0.7.3 publish covers both the live int and the --release /sptc:setup end-user dogfood. Building the adapter.spt packer now so the release asset is ready to upload the moment v0.7.3 lands. Thanks for the fast turn.
EOF
echo "exit=$?"

Ack doyle fast-track v0.7.3


Acked. Now the packer. Adding the REQ first (traceability rule 3).


Edit

required_stages = ["doc"]  # doc: adapter/strings/skills/setup.md + plugin/sptc/skills/setup/SKILL.md — the activation step (probe `adapter list`; if missing/deregistered, `adapter add` local-file-form for dev | `--github` for end-user; verify active; PATH-binary caveat). A setup skill body is doc-class instruction prose (no adapter-side code of its own — same model as REQ-SKILL-SUBNET). int (a registration re-activate assertion: deregistered->active + profile resolves) lands with the int sub-step; the END-USER `--github` int is gated on Wave C (the dedicated root-manifest adapter repo). Topology RESOLVED (doyle owl 2026-06-15): monorepo file-form for dev, dedicated repo for --github (pointer mode, binaries on PATH). See SETUP-SLICE-PLAN.md.

required_stages = ["doc"]  # doc: adapter/strings/skills/setup.md + plugin/sptc/skills/setup/SKILL.md — the activation step (probe `adapter list`; if missing/deregistered, `adapter add` local-file-form for dev | `--release` for end-user; verify active; PATH-binary caveat). A setup skill body is doc-class instruction prose (no adapter-side code of its own — same model as REQ-SKILL-SUBNET). int (a registration re-activate assertion: deregistered->active + profile resolves) lands with the int sub-step; the END-USER `--release` int is gated on spt v0.7.3 (counter 15, fast-tracked — `--release` lands there; same ping as the live int). Acquisition RESOLVED (doyle owl 2026-06-15): monorepo file-form for dev, `adapter add --release SaberMage/spt-claude-code` (adapter.spt release asset) for end-user — no dedicated repo. See SETUP-SLICE-PLAN.md + REQ-DIST-ADAPTER-RELEASE.

[[requirements]]
id = "REQ-DIST-ADAPTER-RELEASE"
title = "The adapter ships to end users as an `adapter.spt` GitHub release asset (tar ROOT = manifest.toml + strings/ + the tool binaries), acquired via `spt adapter add --release SaberMage/spt-claude-code` — distribution straight from the monorepo, no dedicated repo (doyle's --release acquisition source, spt v0.7.3/counter-15)"
required_stages = ["doc", "impl", "unit"]  # doc: SETUP-SLICE-PLAN.md Wave C' + docs/RELEASE-RUNBOOK.md (the adapter.spt release-asset publish target). impl: ci/publish/package-adapter.sh (validate manifest + pack manifest.toml[renamed from claude-spt.toml]+strings/+release tool binaries at archive ROOT; dry-run default, --apply writes dist/adapter.spt; never uploads — the release/tag/upload stays the operator's step). unit: tests/adapter-archive.sh (the packed .spt root holds manifest.toml named exactly + strings/ + both binaries; reject a malformed archive). int (real `adapter add --release` acquire on a clean target) GATED on spt v0.7.3 publish (counter 15) — `--release` is not in 0.7.2; doyle pings on publish (same ping as the live int).


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

required_stages = ["doc"]  # doc: adapter/strings/skills/setup.md + plugin/sptc/skills/setup/SKILL.md — the activation step (probe `adapter list`; if missing/deregistered, `adapter add` local-file-form for dev | `--release` for end-user; verify active; PATH-binary caveat). A setup skill body is doc-class instruction prose (no adapter-side code of its own — same model as REQ-SKILL-SUBNET). int (a registration re-activate assertion: deregistered->active + profile resolves) lands with the int sub-step; the END-USER `--release` int is gated on spt v0.7.3 (counter 15, fast-tracked — `--release` lands there; same ping as the live int). Acquisition RESOLVED (doyle owl 2026-06-15): monorepo file-form for dev, `adapter add --release SaberMage/spt-claude-code` (adapter.spt release asset) for end-user — no dedicated repo. See SETUP-SLICE-PLAN.md + REQ-DIST-ADAPTER-RELEASE.

[[requirements]]
id = "REQ-DIST-ADAPTER-RELEASE"
title = "The adapter ships to end users as an `adapter.spt` GitHub release asset (tar ROOT = manifest.toml + strings/ + the tool binaries), acquired via `spt adapter add --release SaberMage/spt-claude-code` — distribution straight from the monorepo, no dedicated repo (doyle's --release acquisition source, spt v0.7.3/counter-15)"
required_stages = ["doc", "impl", "unit"]  # doc: SETUP-SLICE-PLAN.md Wave C' + docs/RELEASE-RUNBOOK.md (the adapter.spt release-asset publish target). impl: ci/publish/package-adapter.sh (validate manifest + pack manifest.toml[renamed from claude-spt.toml]+strings/+release tool binaries at archive ROOT; dry-run default, --apply writes dist/adapter.spt; never uploads — the release/tag/upload stays the operator's step). unit: tests/adapter-archive.sh (the packed .spt root holds manifest.toml named exactly + strings/ + both binaries; reject a malformed archive). int (real `adapter add --release` acquire on a clean target) GATED on spt v0.7.3 publish (counter 15) — `--release` is not in 0.7.2; doyle pings on publish (same ping as the live int).

title = "/sptc:setup, after confirming the spt binary is present, ACTIVATES the claude-spt adapter (`spt adapter add` — the manifest file in local dev; `--github <published-adapter-repo>` for end-users) so profiles/strings/hints/[digest] go live, then verifies (adapter no longer `deregistered`). Binary-present is NOT a no-op — this is the F-005 post-install activation bridge."

-required_stages = ["doc"]  # doc: adapter/strings/skills/setup.md + plugin/sptc/skills/setup/SKILL.md — the activation step (probe `adapter list`; if missing/deregistered, `adapter add` local-file-form for dev | `--github` for end-user; verify active; PATH-binary caveat). A setup skill body is doc-class instruction prose (no adapter-side code of its own — same model as REQ-SKILL-SUBNET). int (a registration re-activate assertion: deregistered->active + profile resolves) lands with the int sub-step; the END-USER `--github` int is gated on Wave C (the dedicated root-manifest adapter repo). Topology RESOLVED (doyle owl 2026-06-15): monorepo file-form for dev, dedicated repo for --github (pointer mode, binaries on PATH). See SETUP-SLICE-PLAN.md.

+required_stages = ["doc"]  # doc: adapter/strings/skills/setup.md + plugin/sptc/skills/setup/SKILL.md — the activation step (probe `adapter list`; if missing/deregistered, `adapter add` local-file-form for dev | `--release` for end-user; verify active; PATH-binary caveat). A setup skill body is doc-class instruction prose (no adapter-side code of its own — same model as REQ-SKILL-SUBNET). int (a registration re-activate assertion: deregistered->active + profile resolves) lands with the int sub-step; the END-USER `--release` int is gated on spt v0.7.3 (counter 15, fast-tracked — `--release` lands there; same ping as the live int). Acquisition RESOLVED (doyle owl 2026-06-15): monorepo file-form for dev, `adapter add --release SaberMage/spt-claude-code` (adapter.spt release asset) for end-user — no dedicated repo. See SETUP-SLICE-PLAN.md + REQ-DIST-ADAPTER-RELEASE.

+title = "The adapter ships to end users as an `adapter.spt` GitHub release asset (tar ROOT = manifest.toml + strings/ + the tool binaries), acquired via `spt adapter add --release SaberMage/spt-claude-code` — distribution straight from the monorepo, no dedicated repo (doyle's --release acquisition source, spt v0.7.3/counter-15)"

+required_stages = ["doc", "impl", "unit"]  # doc: SETUP-SLICE-PLAN.md Wave C' + docs/RELEASE-RUNBOOK.md (the adapter.spt release-asset publish target). impl: ci/publish/package-adapter.sh (validate manifest + pack manifest.toml[renamed from claude-spt.toml]+strings/+release tool binaries at archive ROOT; dry-run default, --apply writes dist/adapter.spt; never uploads — the release/tag/upload stays the operator's step). unit: tests/adapter-archive.sh (the packed .spt root holds manifest.toml named exactly + strings/ + both binaries; reject a malformed archive). int (real `adapter add --release` acquire on a clean target) GATED on spt v0.7.3 publish (counter 15) — `--release` is not in 0.7.2; doyle pings on publish (same ping as the live int).


Writing the packer (modeled on `package-skeleton.sh`: validate-first, dry-run default, never uploads).


Write

#!/bin/sh
# Pack the `adapter.spt` release asset — the end-user distribution acquired by
# `spt adapter add --release SaberMage/spt-claude-code` (doyle's --release source, spt v0.7.3+).
# The archive ROOT must hold `manifest.toml` (named EXACTLY that) + `strings/` + the tool binaries;
# spt-core fetches it from the repo's GitHub release, extracts to the durable home, registers the
# root. DRY-RUN by default. Never uploads — the release/tag/upload is the operator's step.
# [impl->REQ-DIST-ADAPTER-RELEASE]
#
# Usage:
#   package-adapter.sh            # dry-run: validate + print the archive plan, write nothing
#   package-adapter.sh --apply    # write dist/adapter.spt (validated)
set -u
HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
ADAPTER="$ROOT/adapter"
MANIFEST="$ADAPTER/claude-spt.toml"   # renamed to manifest.toml INSIDE the archive (root-only rule)
STRINGS="$ADAPTER/strings"
OUT="$ROOT/dist/adapter.spt"
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1

# Native executable suffix (.exe on Windows/MSYS, bare elsewhere) — the archive carries the platform
# binary under its native name so a bare-name command template (`claude-spt-digest …`) resolves it.
EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"

# Validate the manifest first — refuse to ship an invalid adapter.
echo "== validate manifest =="
if ! sh "$ROOT/ci/manifest/check-manifest.sh"; then
  echo "REFUSING to package: manifest failed schema validation (fix above, then retry)." >&2
  exit 1
fi

# Require the release binaries (NOT auto-copied by register — they must ride the archive; doyle).
echo
echo "== tool binaries =="
missing=0
for b in "$DIGEST" "$PSYCHE"; do
  if [ -f "$b" ]; then echo "  ok    $b"; else echo "  MISS  $b"; missing=1; fi
done
if [ "$missing" -ne 0 ]; then
  echo "REFUSING to package: build the release binaries first — sh ci/digest/build.sh && sh ci/psyche/build.sh" >&2
  exit 1
fi

echo
echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
echo "manifest : $MANIFEST  ->  (archive root) manifest.toml"
echo "strings  : $STRINGS/  ->  (archive root) strings/"
echo "binaries : claude-spt-digest$EXE, claude-spt-psyche$EXE  ->  (archive root)"
echo "asset    : $OUT"
echo "NOTE: the archive is PLATFORM-SPECIFIC (native binaries: '$EXE' build). Multi-OS = per-OS"
echo "      assets, a follow-on. Binaries are NOT copied beside the registered manifest (copy-mode);"
echo "      they must resolve on the target via PATH/absolute (the installer places them)."

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "DRY-RUN: nothing written. Re-run with --apply to write $OUT, then attach it to a GitHub"
  echo "release on the monorepo and 'spt adapter add --release SaberMage/spt-claude-code' (see"
  echo "docs/RELEASE-RUNBOOK.md). Acquisition needs spt v0.7.3+ (counter 15)."
  exit 0
fi

# Stage the archive ROOT in a temp dir, then tar from it so paths are root-relative (no leading dirs).
STAGE=$(mktemp -d "${TMPDIR:-/tmp}/sptc-adapter.XXXXXX") || { echo "FATAL: mktemp failed"; exit 2; }
trap 'rm -rf "$STAGE"' EXIT
cp "$MANIFEST" "$STAGE/manifest.toml"
cp -r "$STRINGS" "$STAGE/strings"
cp "$DIGEST" "$STAGE/claude-spt-digest$EXE"
cp "$PSYCHE" "$STAGE/claude-spt-psyche$EXE"

mkdir -p "$ROOT/dist" || { echo "FATAL: cannot create $ROOT/dist"; exit 2; }
( cd "$STAGE" && tar -czf "$OUT" manifest.toml strings claude-spt-digest$EXE claude-spt-psyche$EXE ) \
  || { echo "FATAL: tar failed"; exit 2; }

# Self-validate the produced archive: manifest.toml must be at the ROOT (no leading path component).
echo
echo "== validate archive =="
if tar -tzf "$OUT" | grep -qx "manifest.toml"; then
  echo "ok   manifest.toml at archive root"
else
  echo "FATAL: manifest.toml is not at the archive root — adapter add --release would reject it" >&2
  exit 1
fi

echo
echo "WROTE $OUT. Next (operator): attach to a GitHub release on SaberMage/spt-claude-code, then"
echo "'spt adapter add --release SaberMage/spt-claude-code' (needs spt v0.7.3+). /reload as needed."
exit 0


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

#!/bin/sh
# Pack the `adapter.spt` release asset — the end-user distribution acquired by
# `spt adapter add --release SaberMage/spt-claude-code` (doyle's --release source, spt v0.7.3+).
# The archive ROOT must hold `manifest.toml` (named EXACTLY that) + `strings/` + the tool binaries;
# spt-core fetches it from the repo's GitHub release, extracts to the durable home, registers the
# root. DRY-RUN by default. Never uploads — the release/tag/upload is the operator's step.
# [impl->REQ-DIST-ADAPTER-RELEASE]
#
# Usage:
#   package-adapter.sh            # dry-run: validate + print the archive plan, write nothing
#   package-adapter.sh --apply    # write dist/adapter.spt (validated)
set -u
HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
ADAPTER="$ROOT/adapter"
MANIFEST="$ADAPTER/claude-spt.toml"   # renamed to manifest.toml INSIDE the archive (root-only rule)
STRINGS="$ADAPTER/strings"
OUT="$ROOT/dist/adapter.spt"
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1

# Native executable suffix (.exe on Windows/MSYS, bare elsewhere) — the archive carries the platform
# binary under its native name so a bare-name command template (`claude-spt-digest …`) resolves it.
EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"

# Validate the manifest first — refuse to ship an invalid adapter.
echo "== validate manifest =="
if ! sh "$ROOT/ci/manifest/check-manifest.sh"; then
  echo "REFUSING to package: manifest failed schema validation (fix above, then retry)." >&2
  exit 1
fi

# Require the release binaries (NOT auto-copied by register — they must ride the archive; doyle).
echo
echo "== tool binaries =="
missing=0
for b in "$DIGEST" "$PSYCHE"; do
  if [ -f "$b" ]; then echo "  ok    $b"; else echo "  MISS  $b"; missing=1; fi
done
if [ "$missing" -ne 0 ]; then
  echo "REFUSING to package: build the release binaries first — sh ci/digest/build.sh && sh ci/psyche/build.sh" >&2
  exit 1
fi

echo
echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
echo "manifest : $MANIFEST  ->  (archive root) manifest.toml"
echo "strings  : $STRINGS/  ->  (archive root) strings/"
echo "binaries : claude-spt-digest$EXE, claude-spt-psyche$EXE  ->  (archive root)"
echo "asset    : $OUT"
echo "NOTE: the archive is PLATFORM-SPECIFIC (native binaries: '$EXE' build). Multi-OS = per-OS"
echo "      assets, a follow-on. Binaries are NOT copied beside the registered manifest (copy-mode);"
echo "      they must resolve on the target via PATH/absolute (the installer places them)."

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "DRY-RUN: nothing written. Re-run with --apply to write $OUT, then attach it to a GitHub"
  echo "release on the monorepo and 'spt adapter add --release SaberMage/spt-claude-code' (see"
  echo "docs/RELEASE-RUNBOOK.md). Acquisition needs spt v0.7.3+ (counter 15)."
  exit 0
fi

# Stage the archive ROOT in a temp dir, then tar from it so paths are root-relative (no leading dirs).
STAGE=$(mktemp -d "${TMPDIR:-/tmp}/sptc-adapter.XXXXXX") || { echo "FATAL: mktemp failed"; exit 2; }
trap 'rm -rf "$STAGE"' EXIT
cp "$MANIFEST" "$STAGE/manifest.toml"
cp -r "$STRINGS" "$STAGE/strings"
cp "$DIGEST" "$STAGE/claude-spt-digest$EXE"
cp "$PSYCHE" "$STAGE/claude-spt-psyche$EXE"

mkdir -p "$ROOT/dist" || { echo "FATAL: cannot create $ROOT/dist"; exit 2; }
( cd "$STAGE" && tar -czf "$OUT" manifest.toml strings claude-spt-digest$EXE claude-spt-psyche$EXE ) \
  || { echo "FATAL: tar failed"; exit 2; }

# Self-validate the produced archive: manifest.toml must be at the ROOT (no leading path component).
echo
echo "== validate archive =="
if tar -tzf "$OUT" | grep -qx "manifest.toml"; then
  echo "ok   manifest.toml at archive root"
else
  echo "FATAL: manifest.toml is not at the archive root — adapter add --release would reject it" >&2
  exit 1
fi

echo
echo "WROTE $OUT. Next (operator): attach to a GitHub release on SaberMage/spt-claude-code, then"
echo "'spt adapter add --release SaberMage/spt-claude-code' (needs spt v0.7.3+). /reload as needed."
exit 0


Making the output path env-overridable so the test can write to a temp file (not `dist/`).


Edit

OUT="${ADAPTER_SPT_OUT:-$ROOT/dist/adapter.spt}"   # overridable so the unit test writes to a tmp file
APPLY=0


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

OUT="${ADAPTER_SPT_OUT:-$ROOT/dist/adapter.spt}"   # overridable so the unit test writes to a tmp file
APPLY=0

#!/bin/sh
# Pack the `adapter.spt` release asset — the end-user distribution acquired by
# `spt adapter add --release SaberMage/spt-claude-code` (doyle's --release source, spt v0.7.3+).
# The archive ROOT must hold `manifest.toml` (named EXACTLY that) + `strings/` + the tool binaries;
# spt-core fetches it from the repo's GitHub release, extracts to the durable home, registers the
# root. DRY-RUN by default. Never uploads — the release/tag/upload is the operator's step.
# [impl->REQ-DIST-ADAPTER-RELEASE]
#
# Usage:
#   package-adapter.sh            # dry-run: validate + print the archive plan, write nothing
#   package-adapter.sh --apply    # write dist/adapter.spt (validated)
set -u
HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
ADAPTER="$ROOT/adapter"
MANIFEST="$ADAPTER/claude-spt.toml"   # renamed to manifest.toml INSIDE the archive (root-only rule)
STRINGS="$ADAPTER/strings"
OUT="$ROOT/dist/adapter.spt"
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1

# Native executable suffix (.exe on Windows/MSYS, bare elsewhere) — the archive carries the platform
# binary under its native name so a bare-name command template (`claude-spt-digest …`) resolves it.
EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"

# Validate the manifest first — refuse to ship an invalid adapter.
echo "== validate manifest =="
if ! sh "$ROOT/ci/manifest/check-manifest.sh"; then
  echo "REFUSING to package: manifest failed schema validation (fix above, then retry)." >&2
  exit 1
fi

# Require the release binaries (NOT auto-copied by register — they must ride the archive; doyle).
echo
echo "== tool binaries =="
missing=0
for b in "$DIGEST" "$PSYCHE"; do
  if [ -f "$b" ]; then echo "  ok    $b"; else echo "  MISS  $b"; missing=1; fi
done
if [ "$missing" -ne 0 ]; then
  echo "REFUSING to package: build the release binaries first — sh ci/digest/build.sh && sh ci/psyche/build.sh" >&2
  exit 1
fi

echo
echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
echo "manifest : $MANIFEST  ->  (archive root) manifest.toml"
echo "strings  : $STRINGS/  ->  (archive root) strings/"
echo "binaries : claude-spt-digest$EXE, claude-spt-psyche$EXE  ->  (archive root)"
echo "asset    : $OUT"
echo "NOTE: the archive is PLATFORM-SPECIFIC (native binaries: '$EXE' build). Multi-OS = per-OS"
echo "      assets, a follow-on. Binaries are NOT copied beside the registered manifest (copy-mode);"
echo "      they must resolve on the target via PATH/absolute (the installer places them)."

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "DRY-RUN: nothing written. Re-run with --apply to write $OUT, then attach it to a GitHub"
  echo "release on the monorepo and 'spt adapter add --release SaberMage/spt-claude-code' (see"
  echo "docs/RELEASE-RUNBOOK.md). Acquisition needs spt v0.7.3+ (counter 15)."
  exit 0
fi

# Stage the archive ROOT in a temp dir, then tar from it so paths are root-relative (no leading dirs).
STAGE=$(mktemp -d "${TMPDIR:-/tmp}/sptc-adapter.XXXXXX") || { echo "FATAL: mktemp failed"; exit 2; }
trap 'rm -rf "$STAGE"' EXIT
cp "$MANIFEST" "$STAGE/manifest.toml"
cp -r "$STRINGS" "$STAGE/strings"
cp "$DIGEST" "$STAGE/claude-spt-digest$EXE"
cp "$PSYCHE" "$STAGE/claude-spt-psyche$EXE"

mkdir -p "$ROOT/dist" || { echo "FATAL: cannot create $ROOT/dist"; exit 2; }
( cd "$STAGE" && tar -czf "$OUT" manifest.toml strings claude-spt-digest$EXE claude-spt-psyche$EXE ) \
  || { echo "FATAL: tar failed"; exit 2; }

# Self-validate the produced archive: manifest.toml must be at the ROOT (no leading path component).
echo
echo "== validate archive =="
if tar -tzf "$OUT" | grep -qx "manifest.toml"; then
  echo "ok   manifest.toml at archive root"
else
  echo "FATAL: manifest.toml is not at the archive root — adapter add --release would reject it" >&2
  exit 1
fi

echo
echo "WROTE $OUT. Next (operator): attach to a GitHub release on SaberMage/spt-claude-code, then"
echo "'spt adapter add --release SaberMage/spt-claude-code' (needs spt v0.7.3+). /reload as needed."
exit 0

+OUT="${ADAPTER_SPT_OUT:-$ROOT/dist/adapter.spt}"   # overridable so the unit test writes to a tmp file


mkdir -p "$ROOT/dist" || { echo "FATAL: cannot create $ROOT/dist"; exit 2; }

mkdir -p "$(dirname "$OUT")" || { echo "FATAL: cannot create $(dirname "$OUT")"; exit 2; }


#!/bin/sh
# Pack the `adapter.spt` release asset — the end-user distribution acquired by
# `spt adapter add --release SaberMage/spt-claude-code` (doyle's --release source, spt v0.7.3+).
# The archive ROOT must hold `manifest.toml` (named EXACTLY that) + `strings/` + the tool binaries;
# spt-core fetches it from the repo's GitHub release, extracts to the durable home, registers the
# root. DRY-RUN by default. Never uploads — the release/tag/upload is the operator's step.
# [impl->REQ-DIST-ADAPTER-RELEASE]
#
# Usage:
#   package-adapter.sh            # dry-run: validate + print the archive plan, write nothing
#   package-adapter.sh --apply    # write dist/adapter.spt (validated)
set -u
HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
ADAPTER="$ROOT/adapter"
MANIFEST="$ADAPTER/claude-spt.toml"   # renamed to manifest.toml INSIDE the archive (root-only rule)
STRINGS="$ADAPTER/strings"
OUT="${ADAPTER_SPT_OUT:-$ROOT/dist/adapter.spt}"   # overridable so the unit test writes to a tmp file
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1

# Native executable suffix (.exe on Windows/MSYS, bare elsewhere) — the archive carries the platform
# binary under its native name so a bare-name command template (`claude-spt-digest …`) resolves it.
EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"

# Validate the manifest first — refuse to ship an invalid adapter.
echo "== validate manifest =="
if ! sh "$ROOT/ci/manifest/check-manifest.sh"; then
  echo "REFUSING to package: manifest failed schema validation (fix above, then retry)." >&2
  exit 1
fi

# Require the release binaries (NOT auto-copied by register — they must ride the archive; doyle).
echo
echo "== tool binaries =="
missing=0
for b in "$DIGEST" "$PSYCHE"; do
  if [ -f "$b" ]; then echo "  ok    $b"; else echo "  MISS  $b"; missing=1; fi
done
if [ "$missing" -ne 0 ]; then
  echo "REFUSING to package: build the release binaries first — sh ci/digest/build.sh && sh ci/psyche/build.sh" >&2
  exit 1
fi

echo
echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
echo "manifest : $MANIFEST  ->  (archive root) manifest.toml"
echo "strings  : $STRINGS/  ->  (archive root) strings/"
echo "binaries : claude-spt-digest$EXE, claude-spt-psyche$EXE  ->  (archive root)"
echo "asset    : $OUT"
echo "NOTE: the archive is PLATFORM-SPECIFIC (native binaries: '$EXE' build). Multi-OS = per-OS"
echo "      assets, a follow-on. Binaries are NOT copied beside the registered manifest (copy-mode);"
echo "      they must resolve on the target via PATH/absolute (the installer places them)."

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "DRY-RUN: nothing written. Re-run with --apply to write $OUT, then attach it to a GitHub"
  echo "release on the monorepo and 'spt adapter add --release SaberMage/spt-claude-code' (see"
  echo "docs/RELEASE-RUNBOOK.md). Acquisition needs spt v0.7.3+ (counter 15)."
  exit 0
fi

# Stage the archive ROOT in a temp dir, then tar from it so paths are root-relative (no leading dirs).
STAGE=$(mktemp -d "${TMPDIR:-/tmp}/sptc-adapter.XXXXXX") || { echo "FATAL: mktemp failed"; exit 2; }
trap 'rm -rf "$STAGE"' EXIT
cp "$MANIFEST" "$STAGE/manifest.toml"
cp -r "$STRINGS" "$STAGE/strings"
cp "$DIGEST" "$STAGE/claude-spt-digest$EXE"
cp "$PSYCHE" "$STAGE/claude-spt-psyche$EXE"

mkdir -p "$ROOT/dist" || { echo "FATAL: cannot create $ROOT/dist"; exit 2; }
( cd "$STAGE" && tar -czf "$OUT" manifest.toml strings claude-spt-digest$EXE claude-spt-psyche$EXE ) \
  || { echo "FATAL: tar failed"; exit 2; }

# Self-validate the produced archive: manifest.toml must be at the ROOT (no leading path component).
echo
echo "== validate archive =="
if tar -tzf "$OUT" | grep -qx "manifest.toml"; then
  echo "ok   manifest.toml at archive root"
else
  echo "FATAL: manifest.toml is not at the archive root — adapter add --release would reject it" >&2
  exit 1
fi

echo
echo "WROTE $OUT. Next (operator): attach to a GitHub release on SaberMage/spt-claude-code, then"
echo "'spt adapter add --release SaberMage/spt-claude-code' (needs spt v0.7.3+). /reload as needed."
exit 0

-mkdir -p "$ROOT/dist" || { echo "FATAL: cannot create $ROOT/dist"; exit 2; }

+mkdir -p "$(dirname "$OUT")" || { echo "FATAL: cannot create $(dirname "$OUT")"; exit 2; }


Now the unit test.


Write

#!/bin/sh
# Unit test for the adapter.spt packer (ci/publish/package-adapter.sh).
# Asserts the archive-ROOT invariant that `spt adapter add --release` depends on: the packed .spt
# holds manifest.toml (named exactly) + strings/ + both tool binaries at the archive root.
# [unit->REQ-DIST-ADAPTER-RELEASE]
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
PACKER="$ROOT/ci/publish/package-adapter.sh"
rc=0
fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"

if [ ! -f "$DIGEST" ] || [ ! -f "$PSYCHE" ]; then
  # No release binaries on this host yet → assert the packer's REFUSE guard fires (no silent pass),
  # and announce the skip of the build-the-archive assertion (no hidden coverage gap).
  if sh "$PACKER" >/dev/null 2>&1; then
    fail "packer should refuse (exit!=0) when release binaries are absent"
  else
    echo "ok   packer refuses when binaries absent (guard works)"
  fi
  echo "SKIP: archive-build assertion — release binaries not present (run ci/{digest,psyche}/build.sh)"
  exit "$rc"
fi

# Binaries present → build a real archive to a temp file and assert its structure.
TMP=$(mktemp -d "${TMPDIR:-/tmp}/sptc-archtest.XXXXXX") || { echo "FAIL: mktemp"; exit 1; }
trap 'rm -rf "$TMP"' EXIT
OUT="$TMP/adapter.spt"

if ADAPTER_SPT_OUT="$OUT" sh "$PACKER" --apply >/dev/null 2>&1; then
  echo "ok   packer --apply succeeded"
else
  fail "packer --apply exited non-zero"
  exit "$rc"
fi

[ -f "$OUT" ] || { fail "no archive written at $OUT"; exit "$rc"; }

LIST=$(tar -tzf "$OUT")
# manifest.toml MUST be at the root (exact, no leading path) — the add --release contract.
echo "$LIST" | grep -qx "manifest.toml" \
  && echo "ok   manifest.toml at archive root" \
  || fail "manifest.toml not at archive root (add --release would reject)"
# strings/ present (any entry under strings/).
echo "$LIST" | grep -q "^strings/" \
  && echo "ok   strings/ present" \
  || fail "strings/ missing from archive"
# both tool binaries present at root.
echo "$LIST" | grep -qx "claude-spt-digest$EXE" \
  && echo "ok   claude-spt-digest$EXE at root" \
  || fail "claude-spt-digest$EXE missing from archive"
echo "$LIST" | grep -qx "claude-spt-psyche$EXE" \
  && echo "ok   claude-spt-psyche$EXE at root" \
  || fail "claude-spt-psyche$EXE missing from archive"
# negative: no stray leading-dir wrapper (e.g. "adapter/manifest.toml").
if echo "$LIST" | grep -q "/manifest.toml"; then
  fail "manifest.toml appears under a subdir — archive root is wrong"
else
  echo "ok   no nested manifest.toml"
fi

[ "$rc" -eq 0 ] && echo "PASS: adapter-archive"
exit "$rc"


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

#!/bin/sh
# Unit test for the adapter.spt packer (ci/publish/package-adapter.sh).
# Asserts the archive-ROOT invariant that `spt adapter add --release` depends on: the packed .spt
# holds manifest.toml (named exactly) + strings/ + both tool binaries at the archive root.
# [unit->REQ-DIST-ADAPTER-RELEASE]
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
PACKER="$ROOT/ci/publish/package-adapter.sh"
rc=0
fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"

if [ ! -f "$DIGEST" ] || [ ! -f "$PSYCHE" ]; then
  # No release binaries on this host yet → assert the packer's REFUSE guard fires (no silent pass),
  # and announce the skip of the build-the-archive assertion (no hidden coverage gap).
  if sh "$PACKER" >/dev/null 2>&1; then
    fail "packer should refuse (exit!=0) when release binaries are absent"
  else
    echo "ok   packer refuses when binaries absent (guard works)"
  fi
  echo "SKIP: archive-build assertion — release binaries not present (run ci/{digest,psyche}/build.sh)"
  exit "$rc"
fi

# Binaries present → build a real archive to a temp file and assert its structure.
TMP=$(mktemp -d "${TMPDIR:-/tmp}/sptc-archtest.XXXXXX") || { echo "FAIL: mktemp"; exit 1; }
trap 'rm -rf "$TMP"' EXIT
OUT="$TMP/adapter.spt"

if ADAPTER_SPT_OUT="$OUT" sh "$PACKER" --apply >/dev/null 2>&1; then
  echo "ok   packer --apply succeeded"
else
  fail "packer --apply exited non-zero"
  exit "$rc"
fi

[ -f "$OUT" ] || { fail "no archive written at $OUT"; exit "$rc"; }

LIST=$(tar -tzf "$OUT")
# manifest.toml MUST be at the root (exact, no leading path) — the add --release contract.
echo "$LIST" | grep -qx "manifest.toml" \
  && echo "ok   manifest.toml at archive root" \
  || fail "manifest.toml not at archive root (add --release would reject)"
# strings/ present (any entry under strings/).
echo "$LIST" | grep -q "^strings/" \
  && echo "ok   strings/ present" \
  || fail "strings/ missing from archive"
# both tool binaries present at root.
echo "$LIST" | grep -qx "claude-spt-digest$EXE" \
  && echo "ok   claude-spt-digest$EXE at root" \
  || fail "claude-spt-digest$EXE missing from archive"
echo "$LIST" | grep -qx "claude-spt-psyche$EXE" \
  && echo "ok   claude-spt-psyche$EXE at root" \
  || fail "claude-spt-psyche$EXE missing from archive"
# negative: no stray leading-dir wrapper (e.g. "adapter/manifest.toml").
if echo "$LIST" | grep -q "/manifest.toml"; then
  fail "manifest.toml appears under a subdir — archive root is wrong"
else
  echo "ok   no nested manifest.toml"
fi

[ "$rc" -eq 0 ] && echo "PASS: adapter-archive"
exit "$rc"


Test written. Running it + checking `.gitignore` for `dist/`.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "=== run adapter-archive test ==="; sh tests/adapter-archive.sh; echo "test_exit=$?"; echo "=== .gitignore dist? ==="; grep -n "dist" .gitignore 2>&1 || echo "(no dist in gitignore)"; ls .gitignore >/dev/null 2>&1 || echo "(no .gitignore)"

Run archive test + check gitignore


=== run adapter-archive test ===
ok   packer --apply succeeded
ok   manifest.toml at archive root
ok   strings/ present
ok   claude-spt-digest.exe at root
ok   claude-spt-psyche.exe at root
ok   no nested manifest.toml
PASS: adapter-archive
test_exit=0
=== .gitignore dist? ===
(no dist in gitignore)


Test PASS — archive valid. Adding `dist/` to `.gitignore` + a RUNBOOK note (doc evidence).


1	# spt runtime perch state (not source) — depth-safe (** ), so it's ignored under any nested
2	# .claude/ too (e.g. plugin/sptc/.claude/ when this repo is loaded as a live plugin). A
3	# root-anchored pattern previously leaked plugin/sptc/.claude/LIVE_AGENT_IDS.json into the tree.
4	**/.claude/LIVE_AGENT_IDS.json
5	**/.claude/*-commune.md
6	**/.claude/*-signoff.md
7
8	# mdBook build artifact (generated; source is docs-site/src)
9	docs-site/book/
10
11	# Rust build artifacts for the claude-spt-digest extractor crate (Cargo.lock is tracked, bin crate)
12	tools/**/target/
13
14	# generated launchers (capsule-style cc / cc-<id>)
15	/cc
16	/cc.bat
17	/cc.sh
18	/cc-*
19

# spt runtime perch state (not source) — depth-safe (** ), so it's ignored under any nested
# .claude/ too (e.g. plugin/sptc/.claude/ when this repo is loaded as a live plugin). A
# root-anchored pattern previously leaked plugin/sptc/.claude/LIVE_AGENT_IDS.json into the tree.
**/.claude/LIVE_AGENT_IDS.json
**/.claude/*-commune.md
**/.claude/*-signoff.md

# mdBook build artifact (generated; source is docs-site/src)
docs-site/book/

# Rust build artifacts for the claude-spt-digest extractor crate (Cargo.lock is tracked, bin crate)
tools/**/target/

# generated launchers (capsule-style cc / cc-<id>)
/cc
/cc.bat
/cc.sh
/cc-*


Edit

# generated launchers (capsule-style cc / cc-<id>)
/cc
/cc.bat
/cc.sh
/cc-*

# generated launchers (capsule-style cc / cc-<id>)
/cc
/cc.bat
/cc.sh
/cc-*

# packed adapter release asset (generated by ci/publish/package-adapter.sh; uploaded to a GitHub release)
/dist/


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

# generated launchers (capsule-style cc / cc-<id>)
/cc
/cc.bat
/cc.sh
/cc-*

# packed adapter release asset (generated by ci/publish/package-adapter.sh; uploaded to a GitHub release)
/dist/

# spt runtime perch state (not source) — depth-safe (** ), so it's ignored under any nested
# .claude/ too (e.g. plugin/sptc/.claude/ when this repo is loaded as a live plugin). A
# root-anchored pattern previously leaked plugin/sptc/.claude/LIVE_AGENT_IDS.json into the tree.
**/.claude/LIVE_AGENT_IDS.json
**/.claude/*-commune.md
**/.claude/*-signoff.md

# mdBook build artifact (generated; source is docs-site/src)
docs-site/book/

# Rust build artifacts for the claude-spt-digest extractor crate (Cargo.lock is tracked, bin crate)
tools/**/target/

# generated launchers (capsule-style cc / cc-<id>)
/cc
/cc.bat
/cc.sh
/cc-*

+# packed adapter release asset (generated by ci/publish/package-adapter.sh; uploaded to a GitHub release)


Adding the `adapter.spt` publish mechanics to the runbook (doc evidence). Finding the insertion point.


Grep

^## |^### |Pointer flip|Update path|adapter registry


10:## Publish targets
18:2. **The spt-core adapter registry** — the volatile bulk: the CC adapter **manifest**
23:## One-time setup
31:## Per release
54:   - **Manifest + binary** → publish through the **spt-core adapter registry**; the `spt`
58:## cplugs skeleton publish — concrete mechanics
65:ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
101:**Pointer flip** (the authoritative install-state update):
128:## Update path (dual, kept in sync)
137:## Notes


110	- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).
111	  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.
112	- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`
113	  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin
114	  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.
115	- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates
116	  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.
117
118	**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so
119	skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·
120	the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.
121
122	> **RULED (operator, 2026-06-14) — cplugs plugin name = `sptc`.** CC ties the skill prefix to the
123	> `plugin.json` name (no override), so skills surface as `/sptc:*`. **Succession:** once parity is
124	> proven, flip `plugin.json` name `sptc`→`spt` (skills → `/spt:*`) and retire/rename legacy owl's
125	> cplugs `spt` plugin in the same coordinated move — two plugins cannot share the `spt` name. The
126	> flip is a single substitution `s/sptc/spt/` (plugin dir, `plugin.json` name, marketplace path).
127
128	## Update path (dual, kept in sync)
129

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

> **RULED (operator, 2026-06-14) — cplugs plugin name = `sptc`.** CC ties the skill prefix to the
> `plugin.json` name (no override), so skills surface as `/sptc:*`. **Succession:** once parity is
> proven, flip `plugin.json` name `sptc`→`spt` (skills → `/spt:*`) and retire/rename legacy owl's
> cplugs `spt` plugin in the same coordinated move — two plugins cannot share the `spt` name. The
> flip is a single substitution `s/sptc/spt/` (plugin dir, `plugin.json` name, marketplace path).

## Update path (dual, kept in sync)


Edit

## adapter manifest publish — the `adapter.spt` release asset

<!-- [doc->REQ-DIST-ADAPTER-RELEASE] -->
The CC adapter manifest (publish target #2) ships to end users as an **`adapter.spt`** GitHub
**release asset** on this monorepo, acquired with **`spt adapter add --release SaberMage/spt-claude-code`**
(doyle's `--release` source; needs **spt v0.7.3+ / counter 15** — not in 0.7.2). No dedicated
adapter repo: the asset is packed straight from `adapter/`.

- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes `dist/adapter.spt`. It
  validates the manifest, requires the built tool binaries (`sh ci/digest/build.sh && sh
  ci/psyche/build.sh`), and tars the archive **ROOT** = `manifest.toml` (renamed from
  `claude-spt.toml` — `adapter add` is root-only + exact-name), `strings/`, and the two binaries.
  Never uploads (operator's step).
- **Publish:** attach `dist/adapter.spt` to a GitHub release on `SaberMage/spt-claude-code`, then
  end users `spt adapter add --release SaberMage/spt-claude-code` (or `--tag <ver>` to pin).
- **Binaries (copy-mode caveat):** registration copies `manifest.toml` + `strings/` only — **not**
  the binaries. The command templates use **bare names** (`claude-spt-digest`, `claude-spt-psyche`)
  ⇒ resolved from **PATH**; the installer must place them on PATH (or use absolute/home-relative
  paths). The archive carries them so the installer has a source.
- **Platform:** the asset is **platform-specific** (native binaries). Multi-OS = per-OS assets
  (a follow-on); the current packer emits the host platform's build.
- **Acquisition only:** `--release` does **not** establish an `[update]` self-update route (our
  manifest declares no `[update]` → COPY-mode registration). Re-acquire a newer version by re-running
  `--release --tag <newer>`. The signed `file_pull` self-update channel is a separate, later concern.

## Update path (dual, kept in sync)