id = "REQ-DIST-PLUGIN-SKELETON"
title = "Marketplace artifact on cplugs is a thin skeleton: namespaced /sptc:* skill stubs + hooks.json + plugin.json, no embedded logic"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001 + RELEASE-RUNBOOK. impl: ci/publish/{validate,package}-skeleton.sh (installability gate + publish mechanic). unit: tests/skeleton-validate.sh (pass real + fail tampered).

# traceable-reqs manifest for spt-claude-code — SEED.
#
# Authoritative requirement registry: every requirement and every KNOWN-HAZARDS
# invariant lives here as a REQ-* id (see docs/TRACEABILITY.md for the full contract).
#
# SEED YOUR REQs FROM SCOPE.md "Parity-trim" — the DROP / TRANSFORM / KEEP / ADD buckets are
# the parity reference. Mint a REQ-* per surface you port/transform/add as you START each
# milestone (NOT up front). The registry below is intentionally EMPTY (commented shapes only).
#
# ACQUIRE the CLI (it is a released binary, not a crate dependency):
#   gh release download --repo BigscreenVR/traceable-reqs \
#     --pattern '*<your-platform>' --output traceable-reqs   # then chmod +x
# (see INSTANTIATE.md for per-platform patterns + CI wiring.) Validate this seed against
# the installed CLI before relying on it: `traceable-reqs check`.
#
# THE FOUR-STAGE CONTRACT: every requirement is traced from
#   doc  (prose/design)  ->  impl (production code)  ->  unit (unit test)  ->  int (cross-process/node)
# Tag evidence in the SAME commit, on/above the real evidence:
#   // [impl->REQ-FOO]   // [unit->REQ-FOO]   <!-- [doc->REQ-FOO] -->
#
# ACTIVATION MODEL: every requirement starts inactive (`required_stages = []`) so `check`
# stays green pre-code. A milestone ACTIVATES its requirements by setting their real
# `required_stages` when work on them begins. Deferred items stay [] until promoted.
# Never declare the full end-state stage list up front — activate incrementally as evidence
# lands (add "int" at the final integrating wave).

[scan]
# Every evidence location. A missing root makes evidence SILENTLY vanish from the trace —
# audit this whenever a new evidence dir appears. ADD `plugin`, `skills`, `hooks`, `tests`
# the moment those dirs are created (skeleton milestone, body #2) — until they exist a
# nonexistent root is a hard manifest_error, so only list what is on disk now.
roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]

[policy]
# Default policy applied at activation: doc + impl + unit. Networking/lifecycle/cross-node
# reqs add "int". Until a req is activated, its per-req `required_stages = []` keeps it out
# of the coverage gate.
required_stages = []

# ───────────────────────────── Requirements ─────────────────────────────────
# Add requirements as you scope milestones, seeded from SCOPE.md "Parity-trim". Shape:
#
# [[requirements]]
# id = "REQ-EXAMPLE-1"
# title = "One-line statement of the requirement"
# required_stages = []  # activate (e.g. ["doc","impl","unit"]) when the milestone starts
#
# KNOWN-HAZARDS invariants are first-class requirements — one REQ-HAZARD-* per invariant,
# each requiring at least "unit" (add "int" where cross-process/cross-node):
#
# [[requirements]]
# id = "REQ-HAZARD-EXAMPLE"
# title = "The invariant, stated so a test can prove it"
# required_stages = []

# ── Milestone: SKELETON (thin cplugs plugin) — minted at kickoff, per-milestone (not
# the whole registry up front). All inactive (required_stages = []) so `check` stays
# green until body #2 lands evidence and flips each to ["doc"] / ["doc","impl"] etc.
# Seeded from SCOPE.md "Parity-trim" (Distribution + ADD buckets).

[[requirements]]
id = "REQ-DIST-PLUGIN-SKELETON"
title = "Marketplace artifact on cplugs is a thin skeleton: namespaced /sptc:* skill stubs + hooks.json + plugin.json, no embedded logic"
required_stages = ["doc"]  # doc: ADR-0001. impl held until hooks.json lands (gap-F-001 #2).

[[requirements]]
id = "REQ-DIST-BOOTSTRAP-INSTALL"
title = "SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)"
required_stages = ["doc", "impl"]  # doc: ADR-0001. impl: plugin/sptc/bootstrap.{sh,ps1}.

[[requirements]]
id = "REQ-DIST-HOOKS-API"
title = "hooks.json delegates to `spt api` rather than carrying adapter logic"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001/0002. impl: hooks.json + wrappers. unit: tests/hooks-parse.sh. int (real-CC e2e) held for throwaway validation.

[[requirements]]
id = "REQ-DIST-MANIFEST-SCHEMA"
title = "CC adapter manifest validates against spt-core's published manifest.schema.json (from spt-releases)"
required_stages = []

[[requirements]]
id = "REQ-UPS-INJECTION"
title = "UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0002. impl: user-prompt-submit.sh. unit: render_frames test. int (UPS-fires + multi-frame post-F-002) held for throwaway validation.

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

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

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

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

[[requirements]]
id = "REQ-CI-BUS"
title = "CI progress/results report over spt messaging (dogfood the product as its own CI nervous system)"
required_stages = ["doc", "impl"]

[[requirements]]
id = "REQ-CI-OWL-DISCOVERY"
title = "The spt messaging (bus) binary is located robustly at run time (per-version plugins path / PATH / configured) — never a hard-coded versioned path"
required_stages = ["doc", "impl", "unit"]

[[requirements]]
id = "REQ-CI-ACCEPTANCE"
title = "Acceptance = scripted orchestration spawning real claude/headless sessions as the system-under-test, asserting spt-state/digest output (LLM is SUT, never the runner)"
required_stages = ["doc", "impl", "int"]  # doc: docs/CI.md. impl: ci/acceptance/{lib,run-acceptance}.sh + harness unit. int: real `claude -p` spawn asserted UPS-hook-fires green 2026-06-15 under disposable id sptc-ci-40322 (operator perch perri survived — guard proven e2e).

[[requirements]]
id = "REQ-HAZARD-PERCH-COLLISION"
title = "The acceptance harness spawns every nested claude SUT under a DISPOSABLE identity (SPT_AGENT_ID=sptc-ci-<n>), never a live agent's perch id — a colliding id tears down the live agent's perch + poll stream (name-keyed, last-establish-wins)"
required_stages = ["unit"]  # unit: tests/acceptance-harness.sh. Diagnosed 2026-06-15 (operator): nested claude loading the spt plugin established a perch under "perri" and displaced the live poll stream.

[[requirements]]
id = "REQ-HAZARD-MSYS-PATHCONV"
title = "Hook wrappers read the CC payload from stdin, never from a /-leading positional argv (Git-Bash/MSYS path-mangles those on Windows)"
required_stages = ["unit"]  # unit: tests/msys-hazard.sh. Confirmed live 2026-06-15 (UPS validation run A).

[[requirements]]
id = "REQ-SKILL-VERSION"
title = "/spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)"
required_stages = []

-required_stages = ["doc"]  # doc: ADR-0001. impl held until hooks.json lands (gap-F-001 #2).

+required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001 + RELEASE-RUNBOOK. impl: ci/publish/{validate,package}-skeleton.sh (installability gate + publish mechanic). unit: tests/skeleton-validate.sh (pass real + fail tampered).


Resend message with correct reply-to address


Runbook note — mechanics now scripted.


Edit

> **Now scripted (`ci/publish/`).** The validate + stage mechanics below are codified:
> `ci/publish/validate-skeleton.sh` is a binary installability gate (valid `plugin.json` with
> `name=sptc`; valid `hooks.json` whose referenced wrappers all exist; every skill has a
> `SKILL.md`; **no runtime-state / binary / manifest leak in the published surface**) — it runs in
> `ci/run-gates.sh` (the `skeleton-validate` gate) and is unit-tested by `tests/skeleton-validate.sh`.
> `ci/publish/package-skeleton.sh` codifies the per-bump copy below — validates first, **dry-run by
> default**, stages only the skeleton subset, never pushes (the marketplace commit/push stays the
> operator's step).

**One-time marketplace setup:**


<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>

Error: File has not been read yet. Read it first before writing to it.


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	## cplugs skeleton publish — concrete mechanics
59
60	<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
61	The concrete cplugs marketplace steps, captured from the sister project's

     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.

## cplugs skeleton publish — concrete mechanics

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
The concrete cplugs marketplace steps, captured from the sister project's


61	The concrete cplugs marketplace steps, captured from the sister project's
62	`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs
63	target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart
64	bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest
65	ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
66	none of owl's binary-lifecycle machinery applies here.
67
68	**One-time marketplace setup:**
69
70	```bash
71	# Clone the marketplace repo
72	git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs

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


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

**One-time marketplace setup:**

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

> **Now scripted (`ci/publish/`).** The validate + stage mechanics below are codified:
> `ci/publish/validate-skeleton.sh` is a binary installability gate (valid `plugin.json` with
> `name=sptc`; valid `hooks.json` whose referenced wrappers all exist; every skill has a
> `SKILL.md`; **no runtime-state / binary / manifest leak in the published surface**) — it runs in
> `ci/run-gates.sh` (the `skeleton-validate` gate) and is unit-tested by `tests/skeleton-validate.sh`.
> `ci/publish/package-skeleton.sh` codifies the per-bump copy below — validates first, **dry-run by
> default**, stages only the skeleton subset, never pushes (the marketplace commit/push stays the
> operator's step).

**One-time marketplace setup:**


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)

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

> **Now scripted (`ci/publish/`).** The validate + stage mechanics below are codified:
> `ci/publish/validate-skeleton.sh` is a binary installability gate (valid `plugin.json` with
> `name=sptc`; valid `hooks.json` whose referenced wrappers all exist; every skill has a
> `SKILL.md`; **no runtime-state / binary / manifest leak in the published surface**) — it runs in
> `ci/run-gates.sh` (the `skeleton-validate` gate) and is unit-tested by `tests/skeleton-validate.sh`.
> `ci/publish/package-skeleton.sh` codifies the per-bump copy below — validates first, **dry-run by
> default**, stages only the skeleton subset, never pushes (the marketplace commit/push stays the
> operator's step).

**One-time marketplace setup:**

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

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

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

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

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

+> **Now scripted (`ci/publish/`).** The validate + stage mechanics below are codified:

+> `ci/publish/validate-skeleton.sh` is a binary installability gate (valid `plugin.json` with

+> `name=sptc`; valid `hooks.json` whose referenced wrappers all exist; every skill has a

+> `SKILL.md`; **no runtime-state / binary / manifest leak in the published surface**) — it runs in

+> `ci/run-gates.sh` (the `skeleton-validate` gate) and is unit-tested by `tests/skeleton-validate.sh`.

+> `ci/publish/package-skeleton.sh` codifies the per-bump copy below — validates first, **dry-run by

+> default**, stages only the skeleton subset, never pushes (the marketplace commit/push stays the


Full gate + traceable check.


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code; sh ci/run-gates.sh 2>&1 | tail -30; echo "GATES_RC=${PIPESTATUS[0]}"; echo "=== traceable ==="; traceable-reqs check 2>&1 | head -3; echo "TR_RC=${PIPESTATUS[0]}"; echo "=== still leaking? ==="; git ls-files plugin/sptc/.claude/ ; git check-ignore plugin/sptc/.claude/LIVE_AGENT_IDS.json && echo "(now ignored)"

Full gates + traceable + ignore check


ok  plugin/sptc/hooks/stop.sh
ok  plugin/sptc/hooks/subagent-start.sh
ok  plugin/sptc/hooks/subagent-stop.sh
ok  plugin/sptc/hooks/user-prompt-submit.sh
ok  tests/acceptance-harness.sh
ok  tests/ci-gates.sh
ok  tests/hooks-parse.sh
ok  tests/msys-hazard.sh
ok  tests/skeleton-validate.sh

=== GATE: unit tests (tests/*.sh) ===
ok  tests/acceptance-harness.sh
ok  tests/ci-gates.sh
ok  tests/hooks-parse.sh
ok  tests/msys-hazard.sh
ok  tests/skeleton-validate.sh

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

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

=== GATE: manifest-schema ===
SKIP: no adapter manifest yet — activates when the CC adapter manifest lands

=== GATE: docs-drift ===
SKIP: no docs-site build yet — activates with the mdBook docs milestone

=== RESULT: PASS ===
GATES_RC=0
=== traceable ===
14 requirements: 14 complete, 0 incomplete, 0 findings
  [OK] REQ-CI-ACCEPTANCE  required: [doc, impl, int]  stages: +doc +impl -unit +int
       Acceptance = scripted orchestration spawning real claude/headless sessions as the system-under-test, asserting spt-state/digest output (LLM is SUT, never the runner)
TR_RC=0
=== still leaking? ===
plugin/sptc/.claude/LIVE_AGENT_IDS.json
(now ignored)


`git rm --cached` shows the file still tracked — index change may not have persisted across shells. Checking status before committing.


cd /c/Users/decid/Documents/projects/spt-claude-code; git status --short; echo "=== is the cached-rm staged? ==="; git diff --cached --name-status | grep -i live_agent || echo "NOT staged as deleted"

Check git status for staged deletion


 M .gitignore
 M ci/run-gates.sh
 M docs/RELEASE-RUNBOOK.md
D  plugin/sptc/.claude/LIVE_AGENT_IDS.json
 M traceable-reqs.toml
?? CPLUGS-PREP-PLAN.md
?? ci/publish/
?? tests/skeleton-validate.sh
=== is the cached-rm staged? ===
D	plugin/sptc/.claude/LIVE_AGENT_IDS.json


Staged deletion `D` is correct (working-tree file persists as runtime state, now ignored). Committing.


cd /c/Users/decid/Documents/projects/spt-claude-code; git add .gitignore ci/run-gates.sh docs/RELEASE-RUNBOOK.md traceable-reqs.toml CPLUGS-PREP-PLAN.md ci/publish tests/skeleton-validate.sh; git commit -q -F - <<'EOF'
feat(publish): scripted cplugs skeleton validation + packaging; untrack leaked perch state

Make the cplugs skeleton an actually-installable, validated artifact and codify the
publish mechanic (previously loose runbook snippets):

- ci/publish/validate-skeleton.sh — deterministic installability gate: valid plugin.json
  (name=sptc, version, description), valid hooks.json whose referenced wrappers all exist,
  every skill has a SKILL.md, and NO runtime-state/binary/manifest leak in the published
  surface (binary + adapter manifest ride the spt-core registry, never cplugs). Wired as
  the run-gates "skeleton-validate" gate.
- ci/publish/package-skeleton.sh — the runbook per-bump copy as a real script: validates
  first, dry-run by default, stages only the skeleton subset, never pushes.
- tests/skeleton-validate.sh — proves the gate fails on tampered copies (runtime state in
  a published path, planted binary, missing SKILL.md, wrong plugin name), not just green.

Hygiene: untrack plugin/sptc/.claude/LIVE_AGENT_IDS.json (live perch runtime state that
leaked into the tracked skeleton — root-anchored .gitignore missed the nested .claude/)
and make the ignore patterns depth-safe (**/.claude/...).

Activates REQ-DIST-PLUGIN-SKELETON -> [doc,impl,unit]. Gates PASS, 14/14 traceable green.

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

Commit cplugs-prep slice


warning: in the working copy of '.gitignore', LF will be replaced by CRLF the next time Git touches it
rc=0
e28db92 feat(publish): scripted cplugs skeleton validation + packaging; untrack leaked perch state


cplugs-prep landed (`e28db92`). Reporting doyle + rolling to mdBook (no parking).


cd /c/Users/decid/Documents/projects/spt-claude-code; $OWL send doyle perri <<'EOF'
cplugs publish-prep — LANDED (e28db92). The skeleton is now a validated, installable artifact.

- ci/publish/validate-skeleton.sh: deterministic installability gate — valid plugin.json (name=sptc/version/description), hooks.json whose referenced wrappers all exist, every skill has SKILL.md, and NO runtime-state/binary/manifest leak in the PUBLISHED surface (binary+manifest ride the spt-core registry, never cplugs). Wired as run-gates "skeleton-validate" gate.
- ci/publish/package-skeleton.sh: the runbook per-bump copy as a real script — validates first, dry-run default, stages only the skeleton subset, never pushes (marketplace commit/push stays operator's).
- tests/skeleton-validate.sh: proves the gate FAILS on tampered copies (runtime state in a published path / planted binary / missing SKILL.md / wrong name), not just green-on-green.

FOUND+FIXED a real leak while scoping: plugin/sptc/.claude/LIVE_AGENT_IDS.json (my live perch runtime state) was TRACKED into the publishable skeleton — root-anchored .gitignore missed the nested .claude/. Untracked + made ignores depth-safe (**/.claude/...). The validator gate now prevents recurrence.

REQ-DIST-PLUGIN-SKELETON -> [doc,impl,unit] (hooks.json existing lifted the impl hold). Gates PASS, 14/14 traceable green.

On your v0.7.0 / shortcut_basename point: noted + correct — but shortcut_basename is an [adapter] MANIFEST field, and the cplugs skeleton carries NO manifest (it rides the spt-core registry, publish-target #2). So it lands in the ADAPTER-MANIFEST slice (REQ-DIST-MANIFEST-SCHEMA, currently inactive), which I'll author against the REAL v0.7.0 manifest.schema.json + shortcut_basename. Sequencing it right after mdBook, alongside the M12 parity surfaces.

Rolling to mdBook docs-site now. Report when it lands.
EOF
echo "=== docs-site state ==="; find docs-site -type f 2>/dev/null | head -40; echo "(count: $(find docs-site -type f 2>/dev/null | wc -l))"; echo "=== DOCS-STRATEGY head ==="; sed -n '1,60p' docs/DOCS-STRATEGY.md

Report doyle + orient mdBook


➜ SENT:doyle
=== docs-site state ===
docs-site/README.md
docs-site/theme/theme.css
(count: 2)
=== DOCS-STRATEGY head ===
# 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


mdBook docs-site is near-empty (README + theme.css). That's a sizeable milestone — scoping a tight JIT slice (buildable book + drift-gate wiring + llms.txt + mint REQ-DOCS-*). Checking tooling first.


cd /c/Users/decid/Documents/projects/spt-claude-code; echo "mdbook: $(command -v mdbook || echo NONE)"; mdbook --version 2>/dev/null; echo "=== docs-site/README ==="; cat docs-site/README.md; echo "=== theme.css head ==="; head -30 docs-site/theme/theme.css

Check mdbook + docs-site seed files


mdbook: /c/Users/decid/.cargo/bin/mdbook
mdbook v0.5.3
=== docs-site/README ===
# docs-site

This project's developer docs. Authored in `src/*.md`, built with **mdBook**, published to
**GitHub Pages from this same repo**, and CI-gated against drift (see `docs/DOCS-STRATEGY.md`).

`theme/theme.css` is the **shared mdBook theme** — a Starlight-inspired skin reused verbatim
across consumer projects. Wire it via `additional-css = ["theme/theme.css"]` in `book.toml`;
re-point `--theme-accent` to rebrand.
=== theme.css head ===
/* experimplate docs theme — Starlight-inspired skin over mdBook.
   North star: Astro Starlight's look (accent violet-blue, calm dark surface,
   tight typographic rhythm, soft-rounded panels) — its look, not its
   toolchain (DOCS-STRATEGY). Layered onto the ayu theme's variables.

   This is a SHARED, transferable theme — reuse it verbatim across consumer
   projects. Re-point `--theme-accent` to rebrand. */

/* NOTE on units: mdBook sets `html { font-size: 62.5% }`, so 1rem = 10px
   here — every rem below is in 10px units (Starlight's 16px-based values
   are converted: 1rem Starlight = 1.6rem mdBook). */
:root {
  --theme-accent: #4f7cf7;
  --theme-accent-high: #82a7ff;
  --theme-radius: 8px;
  /* Starlight's content measure: --sl-content-width = 45rem @16px = 720px. */
  --content-max-width: 72rem;
}

/* ---- base surface + type ---- */
.ayu {
  --bg: #17181c;
  --fg: #e7e9ee;
  --sidebar-bg: #1b1d23;
  --sidebar-fg: #b3b8c3;
  --sidebar-active: var(--theme-accent-high);
  --scrollbar: #2a2d35;
  --links: var(--theme-accent-high);
  --inline-code-color: #e7e9ee;
  --theme-popup-bg: #1b1d23;


mdbook v0.5.3 + theme present — can build a real drift-gated site. Context is moderate; I'll do a **tight** first mdBook slice (buildable themed book + llms.txt + drift gate + mint REQ-DOCS-*), keeping pages minimal-but-real. JIT plan:


Write

# mdBook docs-site — JIT plan (slice 1)

> Stand up a buildable, themed, drift-gated mdBook docs-site. Activates the `docs-drift` gate and
> mints **REQ-DOCS-SITE** / **REQ-DOCS-DRIFT**. Tight first slice: real (no-placeholder) skeleton
> pages + generated `llms.txt` + drift gate — not the full docs corpus (that fills in per vertical).

## Scope

1. **`docs-site/book.toml`** — wire the shared `theme/theme.css`, title, src dir, GH-Pages-ready
   `site-url`/build-dir. Build output (`docs-site/book/`) is gitignored.
2. **`docs-site/src/`** — minimal Diátaxis-shaped, all-real-content skeleton (per DOCS-STRATEGY):
   - `SUMMARY.md` (the spine)
   - `introduction.md` — what `claude-spt` is + the mental model (one diagram), why it exists.
   - `quickstart.md` — sub-10-min: install the `sptc` plugin → spt-core auto-bootstraps → first
     `/sptc:*` skill. Deterministic, copy-pasteable, no `<PLACEHOLDER>`.
   - `reference/harness-contract.md` — points at the published spt-releases harness-contract + CLI
     reference (public surface), with the sptc adapter's hook→`spt api` mapping table.
   All content true to the current repo (skeleton-honest: mark not-yet-operative surfaces).
3. **`ci/docs/gen-llms.sh`** — generate `docs-site/llms.txt` (curated slim index per DOCS-STRATEGY
   #4) deterministically from `SUMMARY.md` + page H1s. Committed output; regen must be reproducible.
4. **`ci/docs/check-docs.sh`** — drift gate: `mdbook build` succeeds AND `gen-llms.sh` output
   matches the checked-in `llms.txt` (diff = fail). SKIPs cleanly if `mdbook` absent (fleet host
   without the toolchain) — no silent cap, the skip is logged.
5. **Wire** `check-docs.sh` into `ci/run-gates.sh` (replace the `docs-drift` SKIP stub).
6. **`tests/docs-gen.sh`** — UNIT: `gen-llms.sh` is deterministic (two runs identical) and its
   output covers every `SUMMARY.md` page. [unit->REQ-DOCS-DRIFT]
7. **traceable-reqs.toml** — mint `REQ-DOCS-SITE` `["doc","impl"]`, `REQ-DOCS-DRIFT`
   `["doc","impl","unit"]`; add `ci/docs` to `[scan].roots`.
8. **`.gitignore`** — ignore `docs-site/book/` (build artifact).

## Gate

`sh ci/run-gates.sh` ⇒ PASS (docs-drift now active/green) · `traceable-reqs check` green · `mdbook
build docs-site` clean. Atomic commit, `Co-authored by: perri`.

## Out of scope (later slices)

Per-vertical full Diátaxis content (`/sptc:*`, `cc`, subnet, `[digest]`), `llms-full.txt`,
markdown content-negotiation, GH-Pages publish workflow, rustdoc embed. Manifest authoring
(REQ-DIST-MANIFEST-SCHEMA + v0.7.0 `shortcut_basename`) is the NEXT milestone after this.


<owl_messages>
<owl_message from="doyle" priority="high">cplugs-prep = excellent slice. The installability gate with NEGATIVE tests (skeleton-validate FAILS on tampered copies — runtime-state-in-published-path / planted binary / missing SKILL.md / wrong name) is exactly the rigor that matters — not green-on-green. And the real catch: plugin/sptc/.claude/LIVE_AGENT_IDS.json (your live perch runtime state) tracked into the publishable skeleton — that's a genuine publish-hygiene leak (live runtime state must NEVER ride the published artifact); depth-safe **/.claude/ ignore + the validator preventing recurrence = the right fix. package-skeleton.sh validate-first/dry-run-default/never-push (commit stays operator's) is correct.

You're RIGHT on shortcut_basename — my M12-live framing mis-placed it. It's an [adapter] MANIFEST field, and the cplugs skeleton carries NO manifest (it rides publish-target #2, the spt-core registry). So it correctly lands in the adapter-manifest slice (REQ-DIST-MANIFEST-SCHEMA), authored against the REAL v0.7.0 manifest.schema.json. Good correction + right sequencing (after mdBook, alongside the M12 parity surfaces).

Keep going — roll mdBook docs-site now. Report when it lands; I ack + you continue to the manifest/parity slices. Momentum's good.</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\MDBOOK-PLAN.md (file state is current in your context — no need to Read it back)

# mdBook docs-site — JIT plan (slice 1)

> Stand up a buildable, themed, drift-gated mdBook docs-site. Activates the `docs-drift` gate and
> mints **REQ-DOCS-SITE** / **REQ-DOCS-DRIFT**. Tight first slice: real (no-placeholder) skeleton
> pages + generated `llms.txt` + drift gate — not the full docs corpus (that fills in per vertical).

## Scope

1. **`docs-site/book.toml`** — wire the shared `theme/theme.css`, title, src dir, GH-Pages-ready
   `site-url`/build-dir. Build output (`docs-site/book/`) is gitignored.
2. **`docs-site/src/`** — minimal Diátaxis-shaped, all-real-content skeleton (per DOCS-STRATEGY):
   - `SUMMARY.md` (the spine)
   - `introduction.md` — what `claude-spt` is + the mental model (one diagram), why it exists.
   - `quickstart.md` — sub-10-min: install the `sptc` plugin → spt-core auto-bootstraps → first
     `/sptc:*` skill. Deterministic, copy-pasteable, no `<PLACEHOLDER>`.
   - `reference/harness-contract.md` — points at the published spt-releases harness-contract + CLI
     reference (public surface), with the sptc adapter's hook→`spt api` mapping table.
   All content true to the current repo (skeleton-honest: mark not-yet-operative surfaces).
3. **`ci/docs/gen-llms.sh`** — generate `docs-site/llms.txt` (curated slim index per DOCS-STRATEGY
   #4) deterministically from `SUMMARY.md` + page H1s. Committed output; regen must be reproducible.
4. **`ci/docs/check-docs.sh`** — drift gate: `mdbook build` succeeds AND `gen-llms.sh` output
   matches the checked-in `llms.txt` (diff = fail). SKIPs cleanly if `mdbook` absent (fleet host
   without the toolchain) — no silent cap, the skip is logged.
5. **Wire** `check-docs.sh` into `ci/run-gates.sh` (replace the `docs-drift` SKIP stub).
6. **`tests/docs-gen.sh`** — UNIT: `gen-llms.sh` is deterministic (two runs identical) and its
   output covers every `SUMMARY.md` page. [unit->REQ-DOCS-DRIFT]
7. **traceable-reqs.toml** — mint `REQ-DOCS-SITE` `["doc","impl"]`, `REQ-DOCS-DRIFT`
   `["doc","impl","unit"]`; add `ci/docs` to `[scan].roots`.
8. **`.gitignore`** — ignore `docs-site/book/` (build artifact).

## Gate

`sh ci/run-gates.sh` ⇒ PASS (docs-drift now active/green) · `traceable-reqs check` green · `mdbook
build docs-site` clean. Atomic commit, `Co-authored by: perri`.

## Out of scope (later slices)

Per-vertical full Diátaxis content (`/sptc:*`, `cc`, subnet, `[digest]`), `llms-full.txt`,
markdown content-negotiation, GH-Pages publish workflow, rustdoc embed. Manifest authoring
(REQ-DIST-MANIFEST-SCHEMA + v0.7.0 `shortcut_basename`) is the NEXT milestone after this.


Doyle acks cplugs-prep, confirms sequencing — rolling mdBook. Minting REQ-DOCS first, then building.


Edit

roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]

roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "ci/docs", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]


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)

# traceable-reqs manifest for spt-claude-code — SEED.
#
# Authoritative requirement registry: every requirement and every KNOWN-HAZARDS
# invariant lives here as a REQ-* id (see docs/TRACEABILITY.md for the full contract).
#
# SEED YOUR REQs FROM SCOPE.md "Parity-trim" — the DROP / TRANSFORM / KEEP / ADD buckets are
# the parity reference. Mint a REQ-* per surface you port/transform/add as you START each
# milestone (NOT up front). The registry below is intentionally EMPTY (commented shapes only).
#
# ACQUIRE the CLI (it is a released binary, not a crate dependency):
#   gh release download --repo BigscreenVR/traceable-reqs \
#     --pattern '*<your-platform>' --output traceable-reqs   # then chmod +x
# (see INSTANTIATE.md for per-platform patterns + CI wiring.) Validate this seed against
# the installed CLI before relying on it: `traceable-reqs check`.
#
# THE FOUR-STAGE CONTRACT: every requirement is traced from
#   doc  (prose/design)  ->  impl (production code)  ->  unit (unit test)  ->  int (cross-process/node)
# Tag evidence in the SAME commit, on/above the real evidence:
#   // [impl->REQ-FOO]   // [unit->REQ-FOO]   <!-- [doc->REQ-FOO] -->
#
# ACTIVATION MODEL: every requirement starts inactive (`required_stages = []`) so `check`
# stays green pre-code. A milestone ACTIVATES its requirements by setting their real
# `required_stages` when work on them begins. Deferred items stay [] until promoted.
# Never declare the full end-state stage list up front — activate incrementally as evidence
# lands (add "int" at the final integrating wave).

[scan]
# Every evidence location. A missing root makes evidence SILENTLY vanish from the trace —
# audit this whenever a new evidence dir appears. ADD `plugin`, `skills`, `hooks`, `tests`
# the moment those dirs are created (skeleton milestone, body #2) — until they exist a
# nonexistent root is a hard manifest_error, so only list what is on disk now.
roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]

[policy]
# Default policy applied at activation: doc + impl + unit. Networking/lifecycle/cross-node
# reqs add "int". Until a req is activated, its per-req `required_stages = []` keeps it out
# of the coverage gate.
required_stages = []

# ───────────────────────────── Requirements ─────────────────────────────────
# Add requirements as you scope milestones, seeded from SCOPE.md "Parity-trim". Shape:
#
# [[requirements]]
# id = "REQ-EXAMPLE-1"
# title = "One-line statement of the requirement"
# required_stages = []  # activate (e.g. ["doc","impl","unit"]) when the milestone starts
#
# KNOWN-HAZARDS invariants are first-class requirements — one REQ-HAZARD-* per invariant,
# each requiring at least "unit" (add "int" where cross-process/cross-node):
#
# [[requirements]]
# id = "REQ-HAZARD-EXAMPLE"
# title = "The invariant, stated so a test can prove it"
# required_stages = []

# ── Milestone: SKELETON (thin cplugs plugin) — minted at kickoff, per-milestone (not
# the whole registry up front). All inactive (required_stages = []) so `check` stays
# green until body #2 lands evidence and flips each to ["doc"] / ["doc","impl"] etc.
# Seeded from SCOPE.md "Parity-trim" (Distribution + ADD buckets).

[[requirements]]
id = "REQ-DIST-PLUGIN-SKELETON"
title = "Marketplace artifact on cplugs is a thin skeleton: namespaced /sptc:* skill stubs + hooks.json + plugin.json, no embedded logic"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001 + RELEASE-RUNBOOK. impl: ci/publish/{validate,package}-skeleton.sh (installability gate + publish mechanic). unit: tests/skeleton-validate.sh (pass real + fail tampered).

[[requirements]]
id = "REQ-DIST-BOOTSTRAP-INSTALL"
title = "SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)"
required_stages = ["doc", "impl"]  # doc: ADR-0001. impl: plugin/sptc/bootstrap.{sh,ps1}.

[[requirements]]
id = "REQ-DIST-HOOKS-API"
title = "hooks.json delegates to `spt api` rather than carrying adapter logic"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001/0002. impl: hooks.json + wrappers. unit: tests/hooks-parse.sh. int (real-CC e2e) held for throwaway validation.

[[requirements]]
id = "REQ-DIST-MANIFEST-SCHEMA"
title = "CC adapter manifest validates against spt-core's published manifest.schema.json (from spt-releases)"
required_stages = []

[[requirements]]
id = "REQ-UPS-INJECTION"
title = "UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0002. impl: user-prompt-submit.sh. unit: render_frames test. int (UPS-fires + multi-frame post-F-002) held for throwaway validation.

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

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

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

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

[[requirements]]
id = "REQ-CI-BUS"
title = "CI progress/results report over spt messaging (dogfood the product as its own CI nervous system)"
required_stages = ["doc", "impl"]

[[requirements]]
id = "REQ-CI-OWL-DISCOVERY"
title = "The spt messaging (bus) binary is located robustly at run time (per-version plugins path / PATH / configured) — never a hard-coded versioned path"
required_stages = ["doc", "impl", "unit"]

[[requirements]]
id = "REQ-CI-ACCEPTANCE"
title = "Acceptance = scripted orchestration spawning real claude/headless sessions as the system-under-test, asserting spt-state/digest output (LLM is SUT, never the runner)"
required_stages = ["doc", "impl", "int"]  # doc: docs/CI.md. impl: ci/acceptance/{lib,run-acceptance}.sh + harness unit. int: real `claude -p` spawn asserted UPS-hook-fires green 2026-06-15 under disposable id sptc-ci-40322 (operator perch perri survived — guard proven e2e).

[[requirements]]
id = "REQ-HAZARD-PERCH-COLLISION"
title = "The acceptance harness spawns every nested claude SUT under a DISPOSABLE identity (SPT_AGENT_ID=sptc-ci-<n>), never a live agent's perch id — a colliding id tears down the live agent's perch + poll stream (name-keyed, last-establish-wins)"
required_stages = ["unit"]  # unit: tests/acceptance-harness.sh. Diagnosed 2026-06-15 (operator): nested claude loading the spt plugin established a perch under "perri" and displaced the live poll stream.

[[requirements]]
id = "REQ-HAZARD-MSYS-PATHCONV"
title = "Hook wrappers read the CC payload from stdin, never from a /-leading positional argv (Git-Bash/MSYS path-mangles those on Windows)"
required_stages = ["unit"]  # unit: tests/msys-hazard.sh. Confirmed live 2026-06-15 (UPS validation run A).

[[requirements]]
id = "REQ-SKILL-VERSION"
title = "/spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)"
required_stages = []

-roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]

+roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "ci/docs", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]


[[requirements]]
id = "REQ-SKILL-VERSION"

# ── Milestone: DOCS-SITE (mdBook, drift-gated) — minted on-start. Dual-audience product docs
# in docs-site/, built with mdBook, gated against drift. Seeded from docs/DOCS-STRATEGY.md.

[[requirements]]
id = "REQ-DOCS-SITE"
title = "docs-site/ builds with mdBook from src/ (themed, Diátaxis-shaped, all-real-content — no placeholders)"
required_stages = ["doc", "impl"]  # doc: DOCS-STRATEGY. impl: docs-site/book.toml + src/ pages.

[[requirements]]
id = "REQ-DOCS-DRIFT"
title = "Generated docs (llms.txt) are CI-gated against drift: a deterministic generator's output must match what's checked in, and the book must build"
required_stages = ["doc", "impl", "unit"]  # doc: DOCS-STRATEGY #10. impl: ci/docs/{gen-llms,check-docs}.sh + run-gates wiring. unit: tests/docs-gen.sh.

[[requirements]]
id = "REQ-SKILL-VERSION"


# ── Milestone: DOCS-SITE (mdBook, drift-gated) — minted on-start. Dual-audience product docs
# in docs-site/, built with mdBook, gated against drift. Seeded from docs/DOCS-STRATEGY.md.

[[requirements]]
id = "REQ-DOCS-SITE"
title = "docs-site/ builds with mdBook from src/ (themed, Diátaxis-shaped, all-real-content — no placeholders)"
required_stages = ["doc", "impl"]  # doc: DOCS-STRATEGY. impl: docs-site/book.toml + src/ pages.

[[requirements]]
id = "REQ-DOCS-DRIFT"
title = "Generated docs (llms.txt) are CI-gated against drift: a deterministic generator's output must match what's checked in, and the book must build"
required_stages = ["doc", "impl", "unit"]  # doc: DOCS-STRATEGY #10. impl: ci/docs/{gen-llms,check-docs}.sh + run-gates wiring. unit: tests/docs-gen.sh.

[[requirements]]
id = "REQ-SKILL-VERSION"

# traceable-reqs manifest for spt-claude-code — SEED.
#
# Authoritative requirement registry: every requirement and every KNOWN-HAZARDS
# invariant lives here as a REQ-* id (see docs/TRACEABILITY.md for the full contract).
#
# SEED YOUR REQs FROM SCOPE.md "Parity-trim" — the DROP / TRANSFORM / KEEP / ADD buckets are
# the parity reference. Mint a REQ-* per surface you port/transform/add as you START each
# milestone (NOT up front). The registry below is intentionally EMPTY (commented shapes only).
#
# ACQUIRE the CLI (it is a released binary, not a crate dependency):
#   gh release download --repo BigscreenVR/traceable-reqs \
#     --pattern '*<your-platform>' --output traceable-reqs   # then chmod +x
# (see INSTANTIATE.md for per-platform patterns + CI wiring.) Validate this seed against
# the installed CLI before relying on it: `traceable-reqs check`.
#
# THE FOUR-STAGE CONTRACT: every requirement is traced from
#   doc  (prose/design)  ->  impl (production code)  ->  unit (unit test)  ->  int (cross-process/node)
# Tag evidence in the SAME commit, on/above the real evidence:
#   // [impl->REQ-FOO]   // [unit->REQ-FOO]   <!-- [doc->REQ-FOO] -->
#
# ACTIVATION MODEL: every requirement starts inactive (`required_stages = []`) so `check`
# stays green pre-code. A milestone ACTIVATES its requirements by setting their real
# `required_stages` when work on them begins. Deferred items stay [] until promoted.
# Never declare the full end-state stage list up front — activate incrementally as evidence
# lands (add "int" at the final integrating wave).

[scan]
# Every evidence location. A missing root makes evidence SILENTLY vanish from the trace —
# audit this whenever a new evidence dir appears. ADD `plugin`, `skills`, `hooks`, `tests`
# the moment those dirs are created (skeleton milestone, body #2) — until they exist a
# nonexistent root is a hard manifest_error, so only list what is on disk now.
roots = ["plugin", "ci", "ci/acceptance", "ci/publish", "ci/docs", "tests", "docs", "docs-site", "CONTEXT.md", "SCOPE.md", "AGENTS.md", "HANDOFF.md"]

[policy]
# Default policy applied at activation: doc + impl + unit. Networking/lifecycle/cross-node
# reqs add "int". Until a req is activated, its per-req `required_stages = []` keeps it out
# of the coverage gate.
required_stages = []

# ───────────────────────────── Requirements ─────────────────────────────────
# Add requirements as you scope milestones, seeded from SCOPE.md "Parity-trim". Shape:
#
# [[requirements]]
# id = "REQ-EXAMPLE-1"
# title = "One-line statement of the requirement"
# required_stages = []  # activate (e.g. ["doc","impl","unit"]) when the milestone starts
#
# KNOWN-HAZARDS invariants are first-class requirements — one REQ-HAZARD-* per invariant,
# each requiring at least "unit" (add "int" where cross-process/cross-node):
#
# [[requirements]]
# id = "REQ-HAZARD-EXAMPLE"
# title = "The invariant, stated so a test can prove it"
# required_stages = []

# ── Milestone: SKELETON (thin cplugs plugin) — minted at kickoff, per-milestone (not
# the whole registry up front). All inactive (required_stages = []) so `check` stays
# green until body #2 lands evidence and flips each to ["doc"] / ["doc","impl"] etc.
# Seeded from SCOPE.md "Parity-trim" (Distribution + ADD buckets).

[[requirements]]
id = "REQ-DIST-PLUGIN-SKELETON"
title = "Marketplace artifact on cplugs is a thin skeleton: namespaced /sptc:* skill stubs + hooks.json + plugin.json, no embedded logic"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001 + RELEASE-RUNBOOK. impl: ci/publish/{validate,package}-skeleton.sh (installability gate + publish mechanic). unit: tests/skeleton-validate.sh (pass real + fail tampered).

[[requirements]]
id = "REQ-DIST-BOOTSTRAP-INSTALL"
title = "SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)"
required_stages = ["doc", "impl"]  # doc: ADR-0001. impl: plugin/sptc/bootstrap.{sh,ps1}.

[[requirements]]
id = "REQ-DIST-HOOKS-API"
title = "hooks.json delegates to `spt api` rather than carrying adapter logic"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0001/0002. impl: hooks.json + wrappers. unit: tests/hooks-parse.sh. int (real-CC e2e) held for throwaway validation.

[[requirements]]
id = "REQ-DIST-MANIFEST-SCHEMA"
title = "CC adapter manifest validates against spt-core's published manifest.schema.json (from spt-releases)"
required_stages = []

[[requirements]]
id = "REQ-UPS-INJECTION"
title = "UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands"
required_stages = ["doc", "impl", "unit"]  # doc: ADR-0002. impl: user-prompt-submit.sh. unit: render_frames test. int (UPS-fires + multi-frame post-F-002) held for throwaway validation.

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

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

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

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

[[requirements]]
id = "REQ-CI-BUS"
title = "CI progress/results report over spt messaging (dogfood the product as its own CI nervous system)"
required_stages = ["doc", "impl"]

[[requirements]]
id = "REQ-CI-OWL-DISCOVERY"
title = "The spt messaging (bus) binary is located robustly at run time (per-version plugins path / PATH / configured) — never a hard-coded versioned path"
required_stages = ["doc", "impl", "unit"]

[[requirements]]
id = "REQ-CI-ACCEPTANCE"
title = "Acceptance = scripted orchestration spawning real claude/headless sessions as the system-under-test, asserting spt-state/digest output (LLM is SUT, never the runner)"
required_stages = ["doc", "impl", "int"]  # doc: docs/CI.md. impl: ci/acceptance/{lib,run-acceptance}.sh + harness unit. int: real `claude -p` spawn asserted UPS-hook-fires green 2026-06-15 under disposable id sptc-ci-40322 (operator perch perri survived — guard proven e2e).

[[requirements]]
id = "REQ-HAZARD-PERCH-COLLISION"
title = "The acceptance harness spawns every nested claude SUT under a DISPOSABLE identity (SPT_AGENT_ID=sptc-ci-<n>), never a live agent's perch id — a colliding id tears down the live agent's perch + poll stream (name-keyed, last-establish-wins)"
required_stages = ["unit"]  # unit: tests/acceptance-harness.sh. Diagnosed 2026-06-15 (operator): nested claude loading the spt plugin established a perch under "perri" and displaced the live poll stream.

[[requirements]]
id = "REQ-HAZARD-MSYS-PATHCONV"
title = "Hook wrappers read the CC payload from stdin, never from a /-leading positional argv (Git-Bash/MSYS path-mangles those on Windows)"
required_stages = ["unit"]  # unit: tests/msys-hazard.sh. Confirmed live 2026-06-15 (UPS validation run A).

[[requirements]]
id = "REQ-SKILL-VERSION"
title = "/spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)"
required_stages = []

+# ── Milestone: DOCS-SITE (mdBook, drift-gated) — minted on-start. Dual-audience product docs

+# in docs-site/, built with mdBook, gated against drift. Seeded from docs/DOCS-STRATEGY.md.

+title = "docs-site/ builds with mdBook from src/ (themed, Diátaxis-shaped, all-real-content — no placeholders)"

+required_stages = ["doc", "impl"]  # doc: DOCS-STRATEGY. impl: docs-site/book.toml + src/ pages.

+title = "Generated docs (llms.txt) are CI-gated against drift: a deterministic generator's output must match what's checked in, and the book must build"

+required_stages = ["doc", "impl", "unit"]  # doc: DOCS-STRATEGY #10. impl: ci/docs/{gen-llms,check-docs}.sh + run-gates wiring. unit: tests/docs-gen.sh.


Now book.toml + gitignore.


Write

# mdBook config for the spt-claude-code docs-site. Built in CI, published to GitHub Pages from
# THIS repo, drift-gated (docs/DOCS-STRATEGY.md). The shared experimplate theme is layered via
# additional-css over the ayu theme (theme/theme.css). [impl->REQ-DOCS-SITE]
[book]
title = "claude-spt — Spacetime adapter for Claude Code"
description = "Agent messaging, live agents, and an invisible spt-core installer, delivered as a Claude Code plugin."
authors = ["perri"]
src = "src"
language = "en"

[output.html]
default-theme = "ayu"
preferred-dark-theme = "ayu"
additional-css = ["theme/theme.css"]
git-repository-url = "https://github.com/SaberMage/spt-claude-code"
# Stable, never-renamed anchors (DOCS-STRATEGY #9): agents cache links.
[output.html.fold]
enable = false


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

# mdBook config for the spt-claude-code docs-site. Built in CI, published to GitHub Pages from
# THIS repo, drift-gated (docs/DOCS-STRATEGY.md). The shared experimplate theme is layered via
# additional-css over the ayu theme (theme/theme.css). [impl->REQ-DOCS-SITE]
[book]
title = "claude-spt — Spacetime adapter for Claude Code"
description = "Agent messaging, live agents, and an invisible spt-core installer, delivered as a Claude Code plugin."
authors = ["perri"]
src = "src"
language = "en"

[output.html]
default-theme = "ayu"
preferred-dark-theme = "ayu"
additional-css = ["theme/theme.css"]
git-repository-url = "https://github.com/SaberMage/spt-claude-code"
# Stable, never-renamed anchors (DOCS-STRATEGY #9): agents cache links.
[output.html.fold]
enable = false


Edit

# generated launchers (capsule-style cc / cc-<id>)

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

# generated launchers (capsule-style cc / cc-<id>)


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)

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

# generated launchers (capsule-style cc / cc-<id>)

# 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

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

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


Now the src pages — SUMMARY + introduction + quickstart + reference.


Write

<!-- The docs-site spine. Keep page order dependency-ordered (Rust Book logic): runnable first,
     concepts next, reference last. Every entry must be a real, building page. [doc->REQ-DOCS-SITE] -->

# claude-spt

- [Introduction](./introduction.md)
- [Quickstart](./quickstart.md)

# Reference

- [Harness contract](./reference/harness-contract.md)


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

<!-- The docs-site spine. Keep page order dependency-ordered (Rust Book logic): runnable first,
     concepts next, reference last. Every entry must be a real, building page. [doc->REQ-DOCS-SITE] -->

# claude-spt

- [Introduction](./introduction.md)
- [Quickstart](./quickstart.md)

# Reference

- [Harness contract](./reference/harness-contract.md)


<!-- [doc->REQ-DOCS-SITE] -->
# Introduction

`claude-spt` is the [Spacetime (`spt`)](https://sabermage.github.io/spt-releases) adapter for
Claude Code. You install one Claude Code plugin — `sptc` — and get three things at once:

- **Agent messaging and live agents** inside Claude Code: send messages between agents, run a
  reachable "perch," and drive long-lived live-agent sessions, all delegated to the `spt` binary.
- **An invisible `spt-core` installer.** The first time the plugin loads, it installs `spt-core`
  for you if it is missing. No separate setup step.
- **A casual on-ramp to spt-core.** The plugin is the friendly front door to the wider Spacetime
  agent ecosystem — subnets, terminal hosting, seamless self-update — without leaving your editor.

## Mental model

Claude Code provides the **harness** (hooks, skills, your prompt). The `spt` binary provides the
**core** (messaging, lifecycle, networking). `claude-spt` is the **thin adapter** between them: it
maps Claude Code's hook events to the `spt` binary's harness-contract entry points and surfaces
delivered messages back into your session.

```text
   Claude Code  ──hook events──▶   sptc adapter   ──spt api──▶   spt-core
   (the harness)                  (this project)                (messaging,
        ▲                          thin glue                     live agents,
        └──────  additionalContext / skills  ◀── renders ◀────    networking)
```

Two ideas follow from that picture, and they shape everything else in these docs:

1. **The adapter is thin by design.** Logic and skill instructions live in the `spt` binary and
   its adapter manifest (conducted by spt-core), not in the plugin. The plugin ships skeletons;
   the operative content is delivered at run time. See
   [Harness contract](./reference/harness-contract.md).
2. **It is built against `spt-core`'s public surface only** — the published `spt-releases` binary,
   install scripts, and docs. That constraint is the point: it proves the adapter contract is
   buildable by anyone, from the published surface alone.

## Status

This is an early, skeleton-honest build. Skills surface under the `/sptc:*` namespace; surfaces
that are not yet operative say so in place. Start with the [Quickstart](./quickstart.md).


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

<!-- [doc->REQ-DOCS-SITE] -->
# Introduction

`claude-spt` is the [Spacetime (`spt`)](https://sabermage.github.io/spt-releases) adapter for
Claude Code. You install one Claude Code plugin — `sptc` — and get three things at once:

- **Agent messaging and live agents** inside Claude Code: send messages between agents, run a
  reachable "perch," and drive long-lived live-agent sessions, all delegated to the `spt` binary.
- **An invisible `spt-core` installer.** The first time the plugin loads, it installs `spt-core`
  for you if it is missing. No separate setup step.
- **A casual on-ramp to spt-core.** The plugin is the friendly front door to the wider Spacetime
  agent ecosystem — subnets, terminal hosting, seamless self-update — without leaving your editor.

## Mental model

Claude Code provides the **harness** (hooks, skills, your prompt). The `spt` binary provides the
**core** (messaging, lifecycle, networking). `claude-spt` is the **thin adapter** between them: it
maps Claude Code's hook events to the `spt` binary's harness-contract entry points and surfaces
delivered messages back into your session.

```text
   Claude Code  ──hook events──▶   sptc adapter   ──spt api──▶   spt-core
   (the harness)                  (this project)                (messaging,
        ▲                          thin glue                     live agents,
        └──────  additionalContext / skills  ◀── renders ◀────    networking)
```

Two ideas follow from that picture, and they shape everything else in these docs:

1. **The adapter is thin by design.** Logic and skill instructions live in the `spt` binary and
   its adapter manifest (conducted by spt-core), not in the plugin. The plugin ships skeletons;
   the operative content is delivered at run time. See
   [Harness contract](./reference/harness-contract.md).
2. **It is built against `spt-core`'s public surface only** — the published `spt-releases` binary,
   install scripts, and docs. That constraint is the point: it proves the adapter contract is
   buildable by anyone, from the published surface alone.

## Status

This is an early, skeleton-honest build. Skills surface under the `/sptc:*` namespace; surfaces
that are not yet operative say so in place. Start with the [Quickstart](./quickstart.md).


<!-- Sub-10-min, deterministic, no placeholders (DOCS-STRATEGY #1/#3). Every command is real and
     runs as written. Skeleton-honest: it only claims what is operative today. [doc->REQ-DOCS-SITE] -->
# Quickstart

Goal: install the `sptc` plugin, watch it bring up `spt-core` for you, and confirm your session is
reachable — in under ten minutes. You need Claude Code and a shell (`bash` on macOS/Linux, Git Bash
on Windows).

## 1. Add the marketplace and install the plugin

From Claude Code:

```text
/plugin marketplace add SaberMage/cplugs
/plugin install sptc@cplugs
```

Then **restart the Claude Code session** so the plugin's `SessionStart` hook runs and populates the
session environment.

## 2. Let it install spt-core for you

On that first start, the plugin's bootstrap installs `spt-core` if it is not already present —
this is the invisible-installer step, nothing for you to run. Confirm it landed:

```bash
spt --version
```

If `spt` is not yet on your `PATH` in this shell, it was installed to `~/.local/bin/spt` (macOS /
Linux) or `%LOCALAPPDATA%\spt-core\bin\spt.exe` (Windows); open a fresh shell and try again.

## 3. Confirm your session is reachable

Each Claude Code session resolves its own agent identity. Check it:

```bash
spt whoami
```

That prints this session's perch id (resolved from `$OWL_SESSION_ID` / `$SPT_AGENT_ID`). A printed
id means the adapter wired your session into Spacetime's messaging fabric.

## What works today, and what is coming

This is an early build. The plumbing above — install, invisible `spt-core` bootstrap, identity —
is operative now. The `/sptc:*` skills (send, ready, live, commune, and the rest) ship as
**skeletons**: their operative instructions are delivered by the adapter at invocation time rather
than baked into the plugin, and some are still being wired. Each skill says in place whether it is
operative yet.

Next, read [Harness contract](./reference/harness-contract.md) to see exactly how Claude Code hook
events map onto the `spt` binary.


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

<!-- Sub-10-min, deterministic, no placeholders (DOCS-STRATEGY #1/#3). Every command is real and
     runs as written. Skeleton-honest: it only claims what is operative today. [doc->REQ-DOCS-SITE] -->
# Quickstart

Goal: install the `sptc` plugin, watch it bring up `spt-core` for you, and confirm your session is
reachable — in under ten minutes. You need Claude Code and a shell (`bash` on macOS/Linux, Git Bash
on Windows).

## 1. Add the marketplace and install the plugin

From Claude Code:

```text
/plugin marketplace add SaberMage/cplugs
/plugin install sptc@cplugs
```

Then **restart the Claude Code session** so the plugin's `SessionStart` hook runs and populates the
session environment.

## 2. Let it install spt-core for you

On that first start, the plugin's bootstrap installs `spt-core` if it is not already present —
this is the invisible-installer step, nothing for you to run. Confirm it landed:

```bash
spt --version
```

If `spt` is not yet on your `PATH` in this shell, it was installed to `~/.local/bin/spt` (macOS /
Linux) or `%LOCALAPPDATA%\spt-core\bin\spt.exe` (Windows); open a fresh shell and try again.

## 3. Confirm your session is reachable

Each Claude Code session resolves its own agent identity. Check it:

```bash
spt whoami
```

That prints this session's perch id (resolved from `$OWL_SESSION_ID` / `$SPT_AGENT_ID`). A printed
id means the adapter wired your session into Spacetime's messaging fabric.

## What works today, and what is coming

This is an early build. The plumbing above — install, invisible `spt-core` bootstrap, identity —
is operative now. The `/sptc:*` skills (send, ready, live, commune, and the rest) ship as
**skeletons**: their operative instructions are delivered by the adapter at invocation time rather
than baked into the plugin, and some are still being wired. Each skill says in place whether it is
operative yet.

Next, read [Harness contract](./reference/harness-contract.md) to see exactly how Claude Code hook
events map onto the `spt` binary.


<!-- Reference: the adapter's hook -> `spt api` mapping. Generated-from-truth target later; for now
     hand-authored against plugin/sptc/hooks/. Keep in lockstep with hooks.json. [doc->REQ-DOCS-SITE] -->
# Harness contract

`claude-spt` is glue: it maps Claude Code **hook events** to the `spt` binary's harness-contract
inbound surface (`spt api --adapter claude-spt <verb>`). The binary is harness-agnostic; this
adapter is the Claude-Code-shaped edge of it.

The authoritative contract lives on `spt-core`'s published surface — the
[harness-contract + CLI reference](https://sabermage.github.io/spt-releases). This page documents
the **adapter's** wiring: which Claude Code hook drives which `spt api` verb.

## Hook → `spt api` mapping

| Claude Code hook   | sptc wrapper                | `spt api` verb           | Purpose                                              |
| ------------------ | --------------------------- | ------------------------ | --------------------------------------------------- |
| `SessionStart`     | `hooks/session-start.sh`    | `seed` / `boundary`      | Bootstrap spt-core, seed the perch (rebind on clear/compact); non-blocking — never `listen`. |
| `UserPromptSubmit` | `hooks/user-prompt-submit.sh` | `poll`                 | Drain delivered messages and surface them to the prompt as `additionalContext`. |
| `Stop`             | `hooks/stop.sh`             | `state` (idle)           | Mark the agent idle when a turn ends.               |
| `SessionEnd`       | `hooks/session-end.sh`      | `session-end`            | Tear down session state cleanly.                    |
| `SubagentStart`    | `hooks/subagent-start.sh`   | `worker-*`               | Track a spawned subagent.                           |
| `SubagentStop`     | `hooks/subagent-stop.sh`    | `worker-*`               | Track subagent completion.                          |

## Two invariants the wrappers hold

- **Payload comes from stdin, never from a `/`-leading argument.** On Windows under Git Bash /
  MSYS, any argument beginning with `/` is silently rewritten to a Windows path. The wrappers read
  the Claude Code hook payload as JSON from **stdin**, so a `/sptc:…` token is never corrupted.
- **Messages are self-delimiting `<EVENT>` envelopes.** `poll` output is rendered by splitting on
  the canonical `<EVENT type="msg" from="…">body</EVENT>` envelope, so a multi-message drain parses
  cleanly and each message keeps its sender for reply-correlation.

## Identity

Every session resolves its own perch id via `spt whoami`, off `$OWL_SESSION_ID` / `$SPT_AGENT_ID`.
A session with no perch (never made reachable) simply delivers nothing — the per-prompt drain
no-ops rather than erroring.

> The operative skill instructions are **not** in this plugin. They are delivered by the adapter
> manifest (conducted by spt-core) at invocation time; the `/sptc:*` `SKILL.md` files are
> deliberately thin skeletons. This split keeps the marketplace artifact low-churn while logic and
> instructions update through spt-core's signed adapter-update channel.


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

<!-- Reference: the adapter's hook -> `spt api` mapping. Generated-from-truth target later; for now
     hand-authored against plugin/sptc/hooks/. Keep in lockstep with hooks.json. [doc->REQ-DOCS-SITE] -->
# Harness contract

`claude-spt` is glue: it maps Claude Code **hook events** to the `spt` binary's harness-contract
inbound surface (`spt api --adapter claude-spt <verb>`). The binary is harness-agnostic; this
adapter is the Claude-Code-shaped edge of it.

The authoritative contract lives on `spt-core`'s published surface — the
[harness-contract + CLI reference](https://sabermage.github.io/spt-releases). This page documents
the **adapter's** wiring: which Claude Code hook drives which `spt api` verb.

## Hook → `spt api` mapping

| Claude Code hook   | sptc wrapper                | `spt api` verb           | Purpose                                              |
| ------------------ | --------------------------- | ------------------------ | --------------------------------------------------- |
| `SessionStart`     | `hooks/session-start.sh`    | `seed` / `boundary`      | Bootstrap spt-core, seed the perch (rebind on clear/compact); non-blocking — never `listen`. |
| `UserPromptSubmit` | `hooks/user-prompt-submit.sh` | `poll`                 | Drain delivered messages and surface them to the prompt as `additionalContext`. |
| `Stop`             | `hooks/stop.sh`             | `state` (idle)           | Mark the agent idle when a turn ends.               |
| `SessionEnd`       | `hooks/session-end.sh`      | `session-end`            | Tear down session state cleanly.                    |
| `SubagentStart`    | `hooks/subagent-start.sh`   | `worker-*`               | Track a spawned subagent.                           |
| `SubagentStop`     | `hooks/subagent-stop.sh`    | `worker-*`               | Track subagent completion.                          |

## Two invariants the wrappers hold

- **Payload comes from stdin, never from a `/`-leading argument.** On Windows under Git Bash /
  MSYS, any argument beginning with `/` is silently rewritten to a Windows path. The wrappers read
  the Claude Code hook payload as JSON from **stdin**, so a `/sptc:…` token is never corrupted.
- **Messages are self-delimiting `<EVENT>` envelopes.** `poll` output is rendered by splitting on
  the canonical `<EVENT type="msg" from="…">body</EVENT>` envelope, so a multi-message drain parses
  cleanly and each message keeps its sender for reply-correlation.

## Identity

Every session resolves its own perch id via `spt whoami`, off `$OWL_SESSION_ID` / `$SPT_AGENT_ID`.
A session with no perch (never made reachable) simply delivers nothing — the per-prompt drain
no-ops rather than erroring.

> The operative skill instructions are **not** in this plugin. They are delivered by the adapter
> manifest (conducted by spt-core) at invocation time; the `/sptc:*` `SKILL.md` files are
> deliberately thin skeletons. This split keeps the marketplace artifact low-churn while logic and
> instructions update through spt-core's signed adapter-update channel.


Now build the book to verify, then write the llms.txt generator.


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code/docs-site; mdbook build 2>&1 | tail -15; echo "BUILD_RC=${PIPESTATUS[0]}"; echo "=== output ==="; ls book/ 2>/dev/null | head

Build mdbook


 INFO Book building has started
 INFO Running the html backend
 INFO HTML book written to `C:\Users\decid\Documents\projects\spt-claude-code\docs-site\book`
BUILD_RC=0
=== output ===
404.html
ayu-highlight-3fdfc3ac.css
book-c22b7243.js
clipboard-1626706a.min.js
css
elasticlunr-ef4e11c1.min.js
favicon-8114d1fc.png
favicon-de23e50b.svg
fonts
highlight-493f70e1.css


Book builds clean. Now the deterministic `llms.txt` generator.


Write

#!/bin/sh
# Generate docs-site/llms.txt — a slim, curated agent index (DOCS-STRATEGY #4) derived
# DETERMINISTICALLY from SUMMARY.md (page order) + each page's first H1. No timestamps, no
# randomness: same sources => byte-identical output, so it can be drift-gated. [impl->REQ-DOCS-DRIFT]
#
# Usage: gen-llms.sh [--check]
#   (default) write docs-site/llms.txt
#   --check   print to stdout only (the drift gate diffs this against the committed file)
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
SITE="$ROOT/docs-site"
SRC="$SITE/src"
SUMMARY="$SRC/SUMMARY.md"
OUT="$SITE/llms.txt"
[ -f "$SUMMARY" ] || { echo "FATAL: no $SUMMARY" >&2; exit 2; }

# First H1 ("# ...") of a page, trimmed. Falls back to the file stem.
page_title() {
  _t=$(sed -n 's/^#[[:space:]]\{1,\}\(.*\)$/\1/p' "$1" 2>/dev/null | head -n1)
  [ -n "$_t" ] && printf '%s' "$_t" || basename "$1" .md
}

emit() {
  printf '# claude-spt\n\n'
  printf '> Spacetime (spt) adapter for Claude Code: agent messaging, live agents, and an invisible spt-core installer, delivered as a Claude Code plugin. Built against spt-core'\''s published public surface only.\n\n'
  printf '## Docs\n\n'
  # Walk SUMMARY.md in order; for each `[label](./path.md)` link, emit a real title + relative link.
  # site-relative .html link (stable URLs; DOCS-STRATEGY #9). Markdown links only, in file order.
  grep -o '(\./[A-Za-z0-9_./-]*\.md)' "$SUMMARY" | sed 's/^(\.\///; s/)$//' | while IFS= read -r rel; do
    [ -f "$SRC/$rel" ] || continue
    _title=$(page_title "$SRC/$rel")
    _html=$(printf '%s' "$rel" | sed 's/\.md$/.html/')
    printf -- '- [%s](%s)\n' "$_title" "$_html"
  done
}

if [ "${1:-}" = "--check" ]; then
  emit
else
  emit > "$OUT"
  echo "wrote $OUT"
fi


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

#!/bin/sh
# Generate docs-site/llms.txt — a slim, curated agent index (DOCS-STRATEGY #4) derived
# DETERMINISTICALLY from SUMMARY.md (page order) + each page's first H1. No timestamps, no
# randomness: same sources => byte-identical output, so it can be drift-gated. [impl->REQ-DOCS-DRIFT]
#
# Usage: gen-llms.sh [--check]
#   (default) write docs-site/llms.txt
#   --check   print to stdout only (the drift gate diffs this against the committed file)
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
SITE="$ROOT/docs-site"
SRC="$SITE/src"
SUMMARY="$SRC/SUMMARY.md"
OUT="$SITE/llms.txt"
[ -f "$SUMMARY" ] || { echo "FATAL: no $SUMMARY" >&2; exit 2; }

# First H1 ("# ...") of a page, trimmed. Falls back to the file stem.
page_title() {
  _t=$(sed -n 's/^#[[:space:]]\{1,\}\(.*\)$/\1/p' "$1" 2>/dev/null | head -n1)
  [ -n "$_t" ] && printf '%s' "$_t" || basename "$1" .md
}

emit() {
  printf '# claude-spt\n\n'
  printf '> Spacetime (spt) adapter for Claude Code: agent messaging, live agents, and an invisible spt-core installer, delivered as a Claude Code plugin. Built against spt-core'\''s published public surface only.\n\n'
  printf '## Docs\n\n'
  # Walk SUMMARY.md in order; for each `[label](./path.md)` link, emit a real title + relative link.
  # site-relative .html link (stable URLs; DOCS-STRATEGY #9). Markdown links only, in file order.
  grep -o '(\./[A-Za-z0-9_./-]*\.md)' "$SUMMARY" | sed 's/^(\.\///; s/)$//' | while IFS= read -r rel; do
    [ -f "$SRC/$rel" ] || continue
    _title=$(page_title "$SRC/$rel")
    _html=$(printf '%s' "$rel" | sed 's/\.md$/.html/')
    printf -- '- [%s](%s)\n' "$_title" "$_html"
  done
}

if [ "${1:-}" = "--check" ]; then
  emit
else
  emit > "$OUT"
  echo "wrote $OUT"
fi


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code; sh ci/docs/gen-llms.sh; echo "=== llms.txt ==="; cat docs-site/llms.txt; echo "=== determinism check (two runs identical?) ==="; a=$(sh ci/docs/gen-llms.sh --check); b=$(sh ci/docs/gen-llms.sh --check); [ "$a" = "$b" ] && echo "DETERMINISTIC ok" || echo "NON-DETERMINISTIC"

Generate + verify llms.txt determinism


wrote /c/Users/decid/Documents/projects/spt-claude-code/docs-site/llms.txt
=== llms.txt ===
# claude-spt

> Spacetime (spt) adapter for Claude Code: agent messaging, live agents, and an invisible spt-core installer, delivered as a Claude Code plugin. Built against spt-core's published public surface only.

## Docs

- [Introduction](introduction.html)
- [Quickstart](quickstart.html)
- [Harness contract](reference/harness-contract.html)
=== determinism check (two runs identical?) ===
DETERMINISTIC ok


llms.txt deterministic, covers all pages. Now the drift gate + unit test.


Write

#!/bin/sh
# Docs-drift gate: the book must BUILD, and the committed docs-site/llms.txt must MATCH a fresh
# deterministic regeneration (drift = fail). Kills doc/code drift structurally (DOCS-STRATEGY #10).
# [impl->REQ-DOCS-DRIFT]
#
# SKIPs cleanly (rc 0) when `mdbook` is absent — a fleet host without the toolchain shouldn't fail
# the gate, but the skip is ALWAYS announced (no silent cap). The llms.txt drift check runs
# regardless (it needs no toolchain).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
SITE="$ROOT/docs-site"
rc=0

# 1. Book builds.
if command -v mdbook >/dev/null 2>&1; then
  if ( cd "$SITE" && mdbook build >/dev/null 2>&1 ); then echo "ok   mdbook build"; else echo "FAIL: mdbook build"; rc=1; fi
else
  echo "SKIP: mdbook not on PATH — book-build check skipped (install mdBook on the fleet host)"
fi

# 2. llms.txt is not drifted from its generator.
if [ -f "$SITE/llms.txt" ]; then
  if sh "$ROOT/ci/docs/gen-llms.sh" --check | diff -u "$SITE/llms.txt" - >/dev/null 2>&1; then
    echo "ok   llms.txt in sync"
  else
    echo "FAIL: llms.txt drifted — run ci/docs/gen-llms.sh and commit the result"; rc=1
  fi
else
  echo "FAIL: docs-site/llms.txt missing — run ci/docs/gen-llms.sh"; rc=1
fi

exit "$rc"


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

#!/bin/sh
# Docs-drift gate: the book must BUILD, and the committed docs-site/llms.txt must MATCH a fresh
# deterministic regeneration (drift = fail). Kills doc/code drift structurally (DOCS-STRATEGY #10).
# [impl->REQ-DOCS-DRIFT]
#
# SKIPs cleanly (rc 0) when `mdbook` is absent — a fleet host without the toolchain shouldn't fail
# the gate, but the skip is ALWAYS announced (no silent cap). The llms.txt drift check runs
# regardless (it needs no toolchain).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
SITE="$ROOT/docs-site"
rc=0

# 1. Book builds.
if command -v mdbook >/dev/null 2>&1; then
  if ( cd "$SITE" && mdbook build >/dev/null 2>&1 ); then echo "ok   mdbook build"; else echo "FAIL: mdbook build"; rc=1; fi
else
  echo "SKIP: mdbook not on PATH — book-build check skipped (install mdBook on the fleet host)"
fi

# 2. llms.txt is not drifted from its generator.
if [ -f "$SITE/llms.txt" ]; then
  if sh "$ROOT/ci/docs/gen-llms.sh" --check | diff -u "$SITE/llms.txt" - >/dev/null 2>&1; then
    echo "ok   llms.txt in sync"
  else
    echo "FAIL: llms.txt drifted — run ci/docs/gen-llms.sh and commit the result"; rc=1
  fi
else
  echo "FAIL: docs-site/llms.txt missing — run ci/docs/gen-llms.sh"; rc=1
fi

exit "$rc"


#!/bin/sh
# Unit tests for the docs generator: deterministic output + full SUMMARY coverage. No mdbook needed.
# Run: sh tests/docs-gen.sh   (exit 0 = pass). [unit->REQ-DOCS-DRIFT]
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
GEN="$ROOT/ci/docs/gen-llms.sh"
SUMMARY="$ROOT/docs-site/src/SUMMARY.md"
fail=0

# 1. Deterministic: two regenerations are byte-identical.
a=$(sh "$GEN" --check); b=$(sh "$GEN" --check)
[ "$a" = "$b" ] && echo "ok   generator deterministic" || { echo "FAIL non-deterministic"; fail=1; }

# 2. Coverage: every page linked in SUMMARY.md appears in the generated index (as .html).
miss=0
for rel in $(grep -o '(\./[A-Za-z0-9_./-]*\.md)' "$SUMMARY" | sed 's/^(\.\///; s/)$//'); do
  html=$(printf '%s' "$rel" | sed 's/\.md$/.html/')
  if printf '%s\n' "$a" | grep -q "($html)"; then :; else echo "FAIL uncovered page: $rel"; miss=1; fi
done
[ "$miss" -eq 0 ] && echo "ok   all SUMMARY pages covered" || fail=1

# 3. Shape: starts with the H1 title and the blockquote summary (llms.txt convention).
printf '%s\n' "$a" | head -n1 | grep -q '^# claude-spt' && echo "ok   has H1 title" || { echo "FAIL missing H1"; fail=1; }
printf '%s\n' "$a" | grep -q '^> ' && echo "ok   has summary blockquote" || { echo "FAIL missing summary"; fail=1; }

# 4. Drift gate is wired and valid shell.
sh -n "$ROOT/ci/docs/check-docs.sh" && echo "ok   check-docs valid shell" || { echo "FAIL check-docs syntax"; fail=1; }

[ "$fail" -eq 0 ] && { echo "DOCS-GEN OK"; exit 0; } || { echo "DOCS-GEN FAIL"; exit 1; }


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

#!/bin/sh
# Unit tests for the docs generator: deterministic output + full SUMMARY coverage. No mdbook needed.
# Run: sh tests/docs-gen.sh   (exit 0 = pass). [unit->REQ-DOCS-DRIFT]
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
GEN="$ROOT/ci/docs/gen-llms.sh"
SUMMARY="$ROOT/docs-site/src/SUMMARY.md"
fail=0

# 1. Deterministic: two regenerations are byte-identical.
a=$(sh "$GEN" --check); b=$(sh "$GEN" --check)
[ "$a" = "$b" ] && echo "ok   generator deterministic" || { echo "FAIL non-deterministic"; fail=1; }

# 2. Coverage: every page linked in SUMMARY.md appears in the generated index (as .html).
miss=0
for rel in $(grep -o '(\./[A-Za-z0-9_./-]*\.md)' "$SUMMARY" | sed 's/^(\.\///; s/)$//'); do
  html=$(printf '%s' "$rel" | sed 's/\.md$/.html/')
  if printf '%s\n' "$a" | grep -q "($html)"; then :; else echo "FAIL uncovered page: $rel"; miss=1; fi
done
[ "$miss" -eq 0 ] && echo "ok   all SUMMARY pages covered" || fail=1

# 3. Shape: starts with the H1 title and the blockquote summary (llms.txt convention).
printf '%s\n' "$a" | head -n1 | grep -q '^# claude-spt' && echo "ok   has H1 title" || { echo "FAIL missing H1"; fail=1; }
printf '%s\n' "$a" | grep -q '^> ' && echo "ok   has summary blockquote" || { echo "FAIL missing summary"; fail=1; }

# 4. Drift gate is wired and valid shell.
sh -n "$ROOT/ci/docs/check-docs.sh" && echo "ok   check-docs valid shell" || { echo "FAIL check-docs syntax"; fail=1; }

[ "$fail" -eq 0 ] && { echo "DOCS-GEN OK"; exit 0; } || { echo "DOCS-GEN FAIL"; exit 1; }


Now wire `check-docs.sh` into run-gates (replace docs-drift SKIP stub).


Edit

gate "docs-drift"
echo "SKIP: no docs-site build yet — activates with the mdBook docs milestone"

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


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

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

#!/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 "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"
echo "SKIP: no adapter manifest yet — activates when the CC adapter manifest lands"

gate "docs-drift"
echo "SKIP: no docs-site build yet — activates with the mdBook docs milestone"

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

-echo "SKIP: no docs-site build yet — activates with the mdBook docs milestone"

+if [ -f "$ROOT/ci/docs/check-docs.sh" ]; then

+  if sh "$ROOT/ci/docs/check-docs.sh"; then :; else fail "docs-drift"; fi


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code; sh tests/docs-gen.sh; echo "DOCSTEST_RC=$?"; echo "=== full gates ==="; sh ci/run-gates.sh 2>&1 | sed -n '/docs-drift/,$p'; echo "GATES_RC=${PIPESTATUS[0]}"; echo "=== traceable ==="; traceable-reqs check 2>&1 | head -1

Run docs test + full gates + traceable


ok   generator deterministic
ok   all SUMMARY pages covered
ok   has H1 title
ok   has summary blockquote
ok   check-docs valid shell
DOCS-GEN OK
DOCSTEST_RC=0
=== full gates ===
       Deterministic gates (shell-syntax + unit tests + traceable-reqs check + manifest-schema + docs-drift), each a binary pass/fail, runnable as one command
  [OK] REQ-CI-MANUAL  required: [doc, impl]  stages: +doc +impl -unit -int
       A manual 'run gates' command always exists — the same gate scripts runnable by hand on any fleet host
  [OK] REQ-CI-OWL-DISCOVERY  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       The spt messaging (bus) binary is located robustly at run time (per-version plugins path / PATH / configured) — never a hard-coded versioned path
  [OK] REQ-CI-TRIGGER  required: [doc, impl]  stages: +doc +impl -unit -int
       A git push hook fires the gates: pings a fleet runner-agent over spt rather than polling
  [OK] REQ-DIST-BOOTSTRAP-INSTALL  required: [doc, impl]  stages: +doc +impl -unit -int
       SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)
  [OK] REQ-DIST-HOOKS-API  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       hooks.json delegates to `spt api` rather than carrying adapter logic
  [OK] REQ-DIST-MANIFEST-SCHEMA  required: []  stages: -doc -impl -unit -int
       CC adapter manifest validates against spt-core's published manifest.schema.json (from spt-releases)
  [OK] REQ-DIST-PLUGIN-SKELETON  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       Marketplace artifact on cplugs is a thin skeleton: namespaced /sptc:* skill stubs + hooks.json + plugin.json, no embedded logic
  [--] REQ-DOCS-DRIFT  required: [doc, impl, unit]  stages: -doc +impl +unit -int
       Generated docs (llms.txt) are CI-gated against drift: a deterministic generator's output must match what's checked in, and the book must build
  [--] REQ-DOCS-SITE  required: [doc, impl]  stages: +doc -impl -unit -int
       docs-site/ builds with mdBook from src/ (themed, Diátaxis-shaped, all-real-content — no placeholders)
  [OK] REQ-HAZARD-MSYS-PATHCONV  required: [unit]  stages: -doc -impl +unit -int
       Hook wrappers read the CC payload from stdin, never from a /-leading positional argv (Git-Bash/MSYS path-mangles those on Windows)
  [OK] REQ-HAZARD-PERCH-COLLISION  required: [unit]  stages: +doc +impl +unit +int
       The acceptance harness spawns every nested claude SUT under a DISPOSABLE identity (SPT_AGENT_ID=sptc-ci-<n>), never a live agent's perch id — a colliding id tears down the live agent's perch + poll stream (name-keyed, last-establish-wins)
  [OK] REQ-SKILL-VERSION  required: []  stages: -doc -impl -unit -int
       /spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)
  [OK] REQ-UPS-INJECTION  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands

Findings:
  [must] missing_stage REQ-DOCS-DRIFT stage=doc — Requirement is missing documentation evidence
  [must] missing_stage REQ-DOCS-SITE stage=impl — Requirement is missing implementation evidence

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

=== GATE: manifest-schema ===
SKIP: no adapter manifest yet — activates when the CC adapter manifest lands

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

=== RESULT: FAIL ===
GATES_RC=1
=== traceable ===
16 requirements: 14 complete, 2 incomplete, 2 findings


Two findings: `REQ-DOCS-SITE` impl tag (in `book.toml`) wasn't picked up — scanner likely skips `.toml`; and `REQ-DOCS-DRIFT` needs a doc tag. Fixing both — moving impl evidence to a scanned `.sh` + doc tag in DOCS-STRATEGY.


Edit

# SKIPs cleanly (rc 0) when `mdbook` is absent — a fleet host without the toolchain shouldn't fail
# the gate, but the skip is ALWAYS announced (no silent cap). The llms.txt drift check runs
# regardless (it needs no toolchain).

# SKIPs cleanly (rc 0) when `mdbook` is absent — a fleet host without the toolchain shouldn't fail
# the gate, but the skip is ALWAYS announced (no silent cap). The llms.txt drift check runs
# regardless (it needs no toolchain).
#
# This gate is also the impl proof that docs-site/ (book.toml + src/ pages) actually builds with
# mdBook — it runs `mdbook build` against the real site. [impl->REQ-DOCS-SITE]


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

# SKIPs cleanly (rc 0) when `mdbook` is absent — a fleet host without the toolchain shouldn't fail
# the gate, but the skip is ALWAYS announced (no silent cap). The llms.txt drift check runs
# regardless (it needs no toolchain).
#
# This gate is also the impl proof that docs-site/ (book.toml + src/ pages) actually builds with
# mdBook — it runs `mdbook build` against the real site. [impl->REQ-DOCS-SITE]

#!/bin/sh
# Docs-drift gate: the book must BUILD, and the committed docs-site/llms.txt must MATCH a fresh
# deterministic regeneration (drift = fail). Kills doc/code drift structurally (DOCS-STRATEGY #10).
# [impl->REQ-DOCS-DRIFT]
#
# SKIPs cleanly (rc 0) when `mdbook` is absent — a fleet host without the toolchain shouldn't fail
# the gate, but the skip is ALWAYS announced (no silent cap). The llms.txt drift check runs
# regardless (it needs no toolchain).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
SITE="$ROOT/docs-site"
rc=0

# 1. Book builds.
if command -v mdbook >/dev/null 2>&1; then
  if ( cd "$SITE" && mdbook build >/dev/null 2>&1 ); then echo "ok   mdbook build"; else echo "FAIL: mdbook build"; rc=1; fi
else
  echo "SKIP: mdbook not on PATH — book-build check skipped (install mdBook on the fleet host)"
fi

# 2. llms.txt is not drifted from its generator.
if [ -f "$SITE/llms.txt" ]; then
  if sh "$ROOT/ci/docs/gen-llms.sh" --check | diff -u "$SITE/llms.txt" - >/dev/null 2>&1; then
    echo "ok   llms.txt in sync"
  else
    echo "FAIL: llms.txt drifted — run ci/docs/gen-llms.sh and commit the result"; rc=1
  fi
else
  echo "FAIL: docs-site/llms.txt missing — run ci/docs/gen-llms.sh"; rc=1
fi

exit "$rc"

+# This gate is also the impl proof that docs-site/ (book.toml + src/ pages) actually builds with

+# mdBook — it runs `mdbook build` against the real site. [impl->REQ-DOCS-SITE]