# 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 /spt:* skill stubs + hooks.json + plugin.json, no embedded logic"
required_stages = []

[[requirements]]
id = "REQ-DIST-BOOTSTRAP-INSTALL"
title = "SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)"
required_stages = []

[[requirements]]
id = "REQ-DIST-HOOKS-API"
title = "hooks.json delegates to `spt api` rather than carrying adapter logic"
required_stages = []

[[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 /spt: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 = []

[[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 = []

# 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 = ["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).

+title = "Marketplace artifact on cplugs is a thin skeleton: namespaced /spt:* skill stubs + hooks.json + plugin.json, no embedded logic"

+title = "SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)"

+title = "hooks.json delegates to `spt api` rather than carrying adapter logic"

+title = "CC adapter manifest validates against spt-core's published manifest.schema.json (from spt-releases)"

+title = "UserPromptSubmit hook detects /spt:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands"

+title = "/spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)"


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && traceable-reqs check 2>&1; echo "EXIT=$?"

Re-run traceability gate after fix


6 requirements: 6 complete, 0 incomplete, 6 findings
  [OK] REQ-DIST-BOOTSTRAP-INSTALL  required: []  stages: -doc -impl -unit -int
       SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)
  [OK] REQ-DIST-HOOKS-API  required: []  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: []  stages: -doc -impl -unit -int
       Marketplace artifact on cplugs is a thin skeleton: namespaced /spt:* skill stubs + hooks.json + plugin.json, no embedded logic
  [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: []  stages: -doc -impl -unit -int
       UserPromptSubmit hook detects /spt:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands

Findings:
  [must] parse_error docs/formats/adr-format.md:37 — Tag-shaped token does not match [stage->REQ-ID]
  [must] parse_error docs/formats/context-format.md:35 — Tag-shaped token does not match [stage->REQ-ID]
  [must] undeclared_id REQ-EXAMPLE-REL docs/RELEASE-RUNBOOK.md:8 — Tag references an ID not declared in the manifest
  [must] undeclared_id REQ-FOO AGENTS.md:36 — Tag references an ID not declared in the manifest
  [must] undeclared_id REQ-FOO AGENTS.md:36 — Tag references an ID not declared in the manifest
  [must] undeclared_id REQ-FOO AGENTS.md:36 — Tag references an ID not declared in the manifest
EXIT=1


Gate advanced: `manifest_error` resolved, all 6 REQs OK. Remaining EXIT=1 from 6 findings — all **template-scaffold tag leakage** (experimplate shipped illustrative `[stage->REQ-ID]` tokens that the scanner reads as live evidence). Fix: apply the repo's own escaping convention (angle-bracket the stage, per `docs/TRACEABILITY.md`). Inspecting the offenders.


30	- **Consequences** — what becomes true now, good *and* bad: breaking changes, new obligations
31	  (e.g. "new `REQ-*` ids to mint", "the integration checklist must be updated"), and what this
32	  forecloses or forces later.
33
34	## Tagging
35
36	If an ADR section is the `doc`-stage evidence for a requirement, tag it in place:
37	`<!-- [doc->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).
38

- **Consequences** — what becomes true now, good *and* bad: breaking changes, new obligations
  (e.g. "new `REQ-*` ids to mint", "the integration checklist must be updated"), and what this
  forecloses or forces later.

## Tagging

If an ADR section is the `doc`-stage evidence for a requirement, tag it in place:
`<!-- [doc->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).


28	## Maintenance discipline
29
30	- When a grilling session or an ADR changes what a term *means*, edit the entry **in the same
31	  change** — a stale definition is worse than none, because this file is authoritative.
32	- New domain term introduced in a plan or PR → add it here before (or with) the code that uses
33	  it, so reviewers and dev-agents share one vocabulary.
34	- If an entry is the `doc`-stage evidence for a requirement, tag it in place:
35	  `<!-- [doc->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.
36

## Maintenance discipline

- When a grilling session or an ADR changes what a term *means*, edit the entry **in the same
  change** — a stale definition is worse than none, because this file is authoritative.
- New domain term introduced in a plan or PR → add it here before (or with) the code that uses
  it, so reviewers and dev-agents share one vocabulary.
- If an entry is the `doc`-stage evidence for a requirement, tag it in place:
  `<!-- [doc->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.


1	# Release runbook
2
3	> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
4	> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
5	> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
6	> thin adapter; the heavy signed-release machinery lives in spt-core, not here.
7
8	<!-- [doc->REQ-EXAMPLE-REL] -->
9
10	## Publish targets
11
12	An spt-claude-code release lands in **two places**, by volatility:
13
14	1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced

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

## Publish targets

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

1. **The `SaberMage/cplugs` marketplace** — the thin **skeleton plugin** (namespaced


Confirmed — all four are illustrative tags that didn't follow `TRACEABILITY.md`'s own angle-bracket-the-stage convention. Fixing all to illustration form.


Edit

`<!-- [doc->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).

`<!-- [<doc>->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).


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

# How to write an ADR

> ADRs (`docs/adr/`) record **decisions** — what we chose and why — so they aren't
> re-litigated. They are the companion to `CONTEXT.md` (which records **meaning**). The
> *grill-with-docs* loop writes and supersedes ADRs inline as decisions crystallize. Start from
> `docs/adr/0000-template.md`.

## Conventions

- **One file per decision**, numbered sequentially: `0001-<slug>.md`, `0002-<slug>.md`, …
  Never renumber a published ADR — it's a stable anchor.
- **Title = the decision as its outcome**, not the question. "Sessions are pinned at first
  bind" — not "How should sessions bind?". A reader skimming filenames should see what was
  decided.
- **Four sections, always, in order:** `## Status`, `## Context`, `## Decision`,
  `## Consequences`. Add an optional `## Alternatives considered` when the rejected options carry
  real weight.

## The sections

- **Status** — `proposed` | `accepted` | `superseded`, plus a date. If it changes an earlier
  decision, link it: `supersedes [ADR-0003](0003-….md)`. When a later ADR overrides this one,
  come back and mark this one `superseded by …`. Be precise about *what* is superseded — a later
  ADR may reverse one mechanism while keeping another's framing.
- **Context** — the forces that made the decision necessary: constraints, the real problems
  (number them if several), what a reader who wasn't in the room needs to understand the *why*.
  Refer to terms by their canonical `CONTEXT.md` names.
- **Decision** — what was decided, stated plainly and **actively**. Include the specifics of what
  gets built / changed, and name the tempting alternative you rejected so it stays rejected.
- **Consequences** — what becomes true now, good *and* bad: breaking changes, new obligations
  (e.g. "new `REQ-*` ids to mint", "the integration checklist must be updated"), and what this
  forecloses or forces later.

## Tagging

If an ADR section is the `doc`-stage evidence for a requirement, tag it in place:
`<!-- [doc->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).

-`<!-- [doc->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).

+`<!-- [<doc>->REQ-…] -->` immediately above that section (see `docs/TRACEABILITY.md`).


`<!-- [doc->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.

`<!-- [<doc>->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.


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

# How to write & maintain CONTEXT.md

> `CONTEXT.md` is the project's glossary and domain model, and it is **authoritative for
> meaning**: when code or prose disagrees with a definition, the definition wins and the code is
> the bug. The *grill-with-docs* loop reads it to sharpen terminology and **updates it inline**
> as decisions land — it must not lag the code.

## What belongs here (and what doesn't)

- **Here:** the *meaning* of a term — what it is, why it exists, the invariants baked into the
  word, how it differs from neighbouring terms.
- **Not here:** *decisions* (those are ADRs in `docs/adr/`) and *requirements* (those are
  `REQ-*` ids in `traceable-reqs.toml`). CONTEXT defines the vocabulary the ADRs and reqs are
  written in.

## Entry conventions

- **Canonical name.** The term as written here is *the* name — use it verbatim in code, docs,
  commits, and plans. If two names exist, pick one, define it, and note the other as "(formerly
  X)".
- **Format:** `**Term** — definition in one or two sentences.` Lead with what it *is*, then any
  constraint that is part of its meaning.
- **Cross-link** related terms in `**bold**`, and draw the **boundary** between terms that are
  easy to conflate ("distinct from **X** in that …").
- **Short.** A glossary entry is a definition, not an essay. If it needs a rationale, that
  rationale is probably an ADR — link to it.

## Maintenance discipline

- When a grilling session or an ADR changes what a term *means*, edit the entry **in the same
  change** — a stale definition is worse than none, because this file is authoritative.
- New domain term introduced in a plan or PR → add it here before (or with) the code that uses
  it, so reviewers and dev-agents share one vocabulary.
- If an entry is the `doc`-stage evidence for a requirement, tag it in place:
  `<!-- [doc->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.

-  `<!-- [doc->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.

+  `<!-- [<doc>->REQ-…] -->` (see `docs/TRACEABILITY.md`). `CONTEXT.md` is a valid `[scan].root`.


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


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)

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

## Publish targets

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

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

## One-time setup

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

## Per release

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

## Update path (dual, kept in sync)

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

## Notes

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

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


34	1. **Tag evidence in the same change.** When you write a function/test/doc-section that
35	   satisfies a requirement stage, add its tag in that same commit:
36	   `// [impl->REQ-FOO]` · `// [unit->REQ-FOO]` · `<!-- [doc->REQ-FOO] -->`
37	   Stages: `doc` / `impl` / `unit` / `int`. Tag *on or immediately above* the real
38	   evidence — never at file tops to satisfy coverage.

1. **Tag evidence in the same change.** When you write a function/test/doc-section that
   satisfies a requirement stage, add its tag in that same commit:
   `// [impl->REQ-FOO]` · `// [unit->REQ-FOO]` · `<!-- [doc->REQ-FOO] -->`
   Stages: `doc` / `impl` / `unit` / `int`. Tag *on or immediately above* the real
   evidence — never at file tops to satisfy coverage.


   `// [<impl>->REQ-FOO]` · `// [<unit>->REQ-FOO]` · `<!-- [<doc>->REQ-FOO] -->`
   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,
   not live evidence — write a bare stage word like `[impl->REQ-FOO]` in real code)


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

   `// [<impl>->REQ-FOO]` · `// [<unit>->REQ-FOO]` · `<!-- [<doc>->REQ-FOO] -->`
   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,
   not live evidence — write a bare stage word like `[impl->REQ-FOO]` in real code)

# spt-claude-code — agent working rules

> **Canonical source of truth for how agents work in this repo.** Harness-agnostic:
> Claude Code reads it via the one-line `CLAUDE.md` (`@AGENTS.md`); other harnesses read
> this file directly. Edit working rules HERE, never in `CLAUDE.md`.

## Public-surface-only constraint (read first)

You build `spt-claude-code` against **only the published public surface of spt-core**:
`SaberMage/spt-releases` (the `spt`/`spt.exe` binary, install scripts, `mock-adapter`
source, `manifest.schema.json`) and the GitHub Pages docs at
`https://sabermage.github.io/spt-releases` (mental model, quickstarts, harness-contract +
CLI reference, rustdoc, `llms.txt` / `llms-full.txt`). **Never read the `spt-core` source
tree.** Proving the adapter contract is buildable from the public surface alone is the entire
point of this project. If a capability you need is missing from the public docs/releases,
that is a **finding to report** (spt-core's published contract has a gap) — not a reason to
reach into spt-core source to work around it. (See `HANDOFF.md` §1.)

`spt-claude-code` is the rebuilt Claude Code harness adapter (`claude-spt`) — spt-core's v1
acceptance proof, its first casual-end-user entrypoint (a CC plugin), and an invisible
spt-core installer.

**Orientation — read before working:** `SCOPE.md` (decision ledger, authoritative for
what/why), `HANDOFF.md` (build orientation + the hard public-surface-only constraint),
`CONTEXT.md` (glossary/model, authoritative for meaning), `docs/adr/` (decisions),
`docs/KNOWN-HAZARDS.md` (invariants we must not re-break), `docs/{DOCS-STRATEGY,TRACEABILITY}.md`.

## Requirement traceability (binding)

This project uses [`traceable-reqs`](https://github.com/BigscreenVR/traceable-reqs)
(`traceable-reqs.toml` = the authoritative `REQ-*` registry). The full contract is
`docs/TRACEABILITY.md`. The rules you must follow:

1. **Tag evidence in the same change.** When you write a function/test/doc-section that
   satisfies a requirement stage, add its tag in that same commit:
   `// [impl->REQ-FOO]` · `// [unit->REQ-FOO]` · `<!-- [doc->REQ-FOO] -->`
   Stages: `doc` / `impl` / `unit` / `int`. Tag *on or immediately above* the real
   evidence — never at file tops to satisfy coverage.
2. **Run `traceable-reqs check` before declaring work done.** Exit-1 means missing/invalid
   evidence — fix it, don't ship it.
3. **New requirement → add it to `traceable-reqs.toml` first** (with a `REQ-*` id), then
   satisfy it. No untracked work; no untagged evidence.
4. **KNOWN-HAZARDS are `REQ-HAZARD-*` requirements** — each needs a test before it's
   "covered." Treat the hazard list as a conformance checklist you must satisfy, not advice.
5. **Activate, don't pre-fail.** Requirements you aren't yet working stay
   `required_stages = []`. Activate (set real stages) only when starting the milestone that
   delivers them.

## Other conventions

- Match surrounding code style. spt-claude-code consumes spt-core's **published** manifest
  contract (`manifest.schema.json` from `spt-releases`) — validate your manifest against it.
  Do **not** copy spt-core internals: build against the public surface only.
- Honor every `docs/KNOWN-HAZARDS.md` invariant — these are real bugs already paid for once
  (or hazards you have committed to never introduce). Each is a `REQ-HAZARD-*` with a test.
- Docs are dual-audience (human + AI dev-agent) per `docs/DOCS-STRATEGY.md`; doc generation
  is CI-gated against drift.
- Commit messages end with the project's co-author trailer:
  `Co-authored by: <your live agent name>` (e.g. `Co-authored by: perri`).

## Plans and context hygiene

- **JIT plans.** Plan the next immediate body of work just-in-time (a short `*-PLAN.md`),
  not the whole project up front. A plan names scope, open design questions, tasks, and the
  gate (build + `traceable-reqs check` green).
- If you finish a significant body of work without need for user intervention, or if your
  context gets too high, you can clear your own context and keep moving:
  1. Create a JIT plan for the next immediate body of work, if it isn't already planned.
  2. If you are a live agent: commune immediate next steps + a broad project-status/end-goal
     summary, then clear.

+   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,

+   not live evidence — write a bare stage word like `[impl->REQ-FOO]` in real code)


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && traceable-reqs check 2>&1; echo "EXIT=$?"

Re-run traceability gate


6 requirements: 6 complete, 0 incomplete, 1 findings
  [OK] REQ-DIST-BOOTSTRAP-INSTALL  required: []  stages: -doc -impl -unit -int
       SessionStart bootstrap installs spt-core when absent (invisible-installer pattern)
  [OK] REQ-DIST-HOOKS-API  required: []  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: []  stages: -doc -impl -unit -int
       Marketplace artifact on cplugs is a thin skeleton: namespaced /spt:* skill stubs + hooks.json + plugin.json, no embedded logic
  [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: []  stages: -doc -impl -unit -int
       UserPromptSubmit hook detects /spt:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands

Findings:
  [must] undeclared_id REQ-FOO AGENTS.md:38 — Tag references an ID not declared in the manifest
EXIT=1


My clarifying note introduced a literal bare `[impl->REQ-FOO]`. Reword to avoid the tag shape.


Edit

   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,
   not live evidence — write a bare stage word like `[impl->REQ-FOO]` in real code)

   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,
   not live evidence — in real code write a bare stage word: `impl`, not `<impl>`)


   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,
   not live evidence — in real code write a bare stage word: `impl`, not `<impl>`)

# spt-claude-code — agent working rules

> **Canonical source of truth for how agents work in this repo.** Harness-agnostic:
> Claude Code reads it via the one-line `CLAUDE.md` (`@AGENTS.md`); other harnesses read
> this file directly. Edit working rules HERE, never in `CLAUDE.md`.

## Public-surface-only constraint (read first)

You build `spt-claude-code` against **only the published public surface of spt-core**:
`SaberMage/spt-releases` (the `spt`/`spt.exe` binary, install scripts, `mock-adapter`
source, `manifest.schema.json`) and the GitHub Pages docs at
`https://sabermage.github.io/spt-releases` (mental model, quickstarts, harness-contract +
CLI reference, rustdoc, `llms.txt` / `llms-full.txt`). **Never read the `spt-core` source
tree.** Proving the adapter contract is buildable from the public surface alone is the entire
point of this project. If a capability you need is missing from the public docs/releases,
that is a **finding to report** (spt-core's published contract has a gap) — not a reason to
reach into spt-core source to work around it. (See `HANDOFF.md` §1.)

`spt-claude-code` is the rebuilt Claude Code harness adapter (`claude-spt`) — spt-core's v1
acceptance proof, its first casual-end-user entrypoint (a CC plugin), and an invisible
spt-core installer.

**Orientation — read before working:** `SCOPE.md` (decision ledger, authoritative for
what/why), `HANDOFF.md` (build orientation + the hard public-surface-only constraint),
`CONTEXT.md` (glossary/model, authoritative for meaning), `docs/adr/` (decisions),
`docs/KNOWN-HAZARDS.md` (invariants we must not re-break), `docs/{DOCS-STRATEGY,TRACEABILITY}.md`.

## Requirement traceability (binding)

This project uses [`traceable-reqs`](https://github.com/BigscreenVR/traceable-reqs)
(`traceable-reqs.toml` = the authoritative `REQ-*` registry). The full contract is
`docs/TRACEABILITY.md`. The rules you must follow:

1. **Tag evidence in the same change.** When you write a function/test/doc-section that
   satisfies a requirement stage, add its tag in that same commit:
   `// [<impl>->REQ-FOO]` · `// [<unit>->REQ-FOO]` · `<!-- [<doc>->REQ-FOO] -->`
   (the stage is angle-bracketed *here only* so the scanner treats these as illustrations,
   not live evidence — write a bare stage word like `[impl->REQ-FOO]` in real code)
   Stages: `doc` / `impl` / `unit` / `int`. Tag *on or immediately above* the real
   evidence — never at file tops to satisfy coverage.
2. **Run `traceable-reqs check` before declaring work done.** Exit-1 means missing/invalid
   evidence — fix it, don't ship it.
3. **New requirement → add it to `traceable-reqs.toml` first** (with a `REQ-*` id), then
   satisfy it. No untracked work; no untagged evidence.
4. **KNOWN-HAZARDS are `REQ-HAZARD-*` requirements** — each needs a test before it's
   "covered." Treat the hazard list as a conformance checklist you must satisfy, not advice.
5. **Activate, don't pre-fail.** Requirements you aren't yet working stay
   `required_stages = []`. Activate (set real stages) only when starting the milestone that
   delivers them.

## Other conventions

- Match surrounding code style. spt-claude-code consumes spt-core's **published** manifest
  contract (`manifest.schema.json` from `spt-releases`) — validate your manifest against it.
  Do **not** copy spt-core internals: build against the public surface only.
- Honor every `docs/KNOWN-HAZARDS.md` invariant — these are real bugs already paid for once
  (or hazards you have committed to never introduce). Each is a `REQ-HAZARD-*` with a test.
- Docs are dual-audience (human + AI dev-agent) per `docs/DOCS-STRATEGY.md`; doc generation
  is CI-gated against drift.
- Commit messages end with the project's co-author trailer:
  `Co-authored by: <your live agent name>` (e.g. `Co-authored by: perri`).

## Plans and context hygiene

- **JIT plans.** Plan the next immediate body of work just-in-time (a short `*-PLAN.md`),
  not the whole project up front. A plan names scope, open design questions, tasks, and the
  gate (build + `traceable-reqs check` green).
- If you finish a significant body of work without need for user intervention, or if your
  context gets too high, you can clear your own context and keep moving:
  1. Create a JIT plan for the next immediate body of work, if it isn't already planned.
  2. If you are a live agent: commune immediate next steps + a broad project-status/end-goal
     summary, then clear.

-   not live evidence — write a bare stage word like `[impl->REQ-FOO]` in real code)

+   not live evidence — in real code write a bare stage word: `impl`, not `<impl>`)


Bash

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

Confirm traceability gate green


<owl_messages>
<owl_message from="doyle" priority="high">cplugs publish — you have the WHAT/WHEN (your docs/RELEASE-RUNBOOK.md: two targets, sequence, signing-delegated-to-spt) but NOT the concrete HOW. The concrete cplugs marketplace mechanics live in the sister project: claude_skill_owl/docs/DEPLOY.md (path: C:\Users\decid\Documents\projects\claude_skill_owl\docs\DEPLOY.md). Read it for the real steps.

IMPORTANT FRAMING — take only the SKELETON SUBSET. owl's DEPLOY.md ships a FULL binary plugin (owl.exe + skills + hooks + seamless-handoff + cache-prune machinery). YOUR cplugs target is the THIN SKELETON ONLY — skill skeletons, hooks.json, SessionStart bootstrap, plugin.json. NO binary in cplugs (binary+manifest ride the spt-core adapter registry, spt-conducted — that's your other publish target, NOT cplugs). So:

USE from DEPLOY.md:
- Marketplace setup (one-time): clone SaberMage/cplugs -> ~/.claude/plugins/marketplaces/cplugs, register in known_marketplaces.json (source: github, repo SaberMage/cplugs).
- Per skeleton bump: bump plugin.json version (plugin manager skips update if version matches cache), copy skeleton files to $MARKET/plugins/<your-plugin>, commit+push the cplugs repo.
- Pointer flip: `claude plugin install <your-plugin>@cplugs` (un-orphans + rewrites installed_plugins.json atomically), then /reload-plugins.

SKIP (owl-specific, not yours): the owl.exe binary sync, the cache targeted-prune + seamless-handoff logic (Phase 18.4/18.5), DEPLOY.ps1's keep-PREVIOUS-version machinery — all that exists because owl ships a live self-migrating binary. Your skeleton has no binary -> none of it applies.

GOTCHAS that DO apply (DEPLOY.md Troubleshooting):
- Do NOT hand-patch installed_plugins.json (jq/sed brittle, CC may reject) — use the CLI.
- A legacy manual-install dir (~/.claude/plugins/<name>/) alongside the marketplace install makes /plugin report "not installed" — keep the two exclusive.
- Restart the CC session after install so the SessionStart hook repopulates env.

Reminder: plugin (marketplace) version and adapter manifest/binary version are DISTINCT numbers on independent schedules (your RELEASE-RUNBOOK Notes). Don't conflate.

Pull the relevant subset into your own RELEASE-RUNBOOK so it's captured in-repo. Questions -> me.</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.


       /spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)
  [OK] REQ-UPS-INJECTION  required: []  stages: -doc -impl -unit -int
       UserPromptSubmit hook detects /spt:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands
EXIT=0


Gate green (EXIT=0). Acknowledging doyle's high-priority message, then capturing the cplugs subset.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && $OWL send --reply-to doyle <<'EOF'
ACK cplugs-HOW. Underway. Status: foundation body green — traceable-reqs check EXIT=0 (fixed seed manifest_error: scan roots trimmed to existing dirs; fixed 4 template-scaffold tag-leak findings from experimplate's own example docs — flagging that as experimplate template feedback). Minted skeleton-milestone REQs (DIST-PLUGIN-SKELETON, BOOTSTRAP-INSTALL, HOOKS-API, MANIFEST-SCHEMA, UPS-INJECTION, SKILL-VERSION), all inactive per activate-don't-pre-fail.

Reading claude_skill_owl/docs/DEPLOY.md now, taking the SKELETON SUBSET only (skill stubs + hooks.json + bootstrap + plugin.json; NO binary in cplugs — binary+manifest ride spt-core adapter registry). Folding the marketplace-setup / per-bump / pointer-flip steps + the 3 gotchas into RELEASE-RUNBOOK.md, keeping plugin-version vs manifest-version distinct. Will ping when captured.
EOF
echo "sent=$?"

Reply ack to doyle


error: the following required arguments were not provided:
  <TARGET>

Usage: owl.exe send --reply-to <REPLY_TO> <TARGET> [FROM]

For more information, try '--help'.
sent=2


1	# Deploying Spacetime (spt) Plugin
2
3	## Automated Deploy (DEPLOY.ps1)
4
5	For the common case — binary + skills + hooks + marketplace + cache refresh for the version already set in `plugin/spt/.claude-plugin/plugin.json` — use the PowerShell script:
6
7	```powershell
8	# From repo root, after bumping plugin.json version:
9	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1
10
11	# Or let the script bump the version for you:
12	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
13	```
14
15	The script automates the build-through-cache-sync sequence and the state refresh (by shelling out to `claude plugin install spt@cplugs`). It **stops before `/reload-plugins`** — you must run that yourself inside Claude Code.
16
17	### What it does (in order)
18
19	1. Reads the authoritative version from `plugin/spt/.claude-plugin/plugin.json`. If `-Bump <patch|minor|major>` is passed, runs the bump sub-sequence: (a) abort gate — refuses to proceed if `CHANGELOG.md` is missing OR still contains a `TODO: changelog entry` marker from a prior bump; (b) rewrites `plugin.json` + `Cargo.toml` to the new version; (c) appends a `TODO: changelog entry` stub H2 to `CHANGELOG.md` for the new version; (d) stages `plugin.json` + `Cargo.toml` + `CHANGELOG.md` and commits as `chore: bump spt plugin to vX.Y.Z` in the spt repo (no push). The bump commit ships with the TODO stub; the user is expected to author a curated `CHANGELOG.md` entry for the new version in a follow-up commit before the next `-Bump`. See "Two-phase changelog authoring" below.
20	2. `cargo build --release` in the repo root.
21	3. Syncs `owl.exe`, `skills/`, `hooks/`, and `plugin.json` to the cplugs marketplace clone.
22	4. Commits + pushes the marketplace (unless skipped).
23	5. Syncs the new version into `~/.claude/plugins/cache/cplugs/spt/<Version>/`, then targeted-prunes older semver directories. The script keeps the NEW version dir, the PREVIOUS version dir (running `owl.exe` processes are still exec'ing from there until they self-migrate via handoff), and the `installPath` leaf dir recorded in `installed_plugins.json` (captured before step 7 rewrites it). Non-semver directories and directories in the keep-set are never touched. Sharing-violation residue (a dir that can't be removed because `owl.exe` still has the `.exe` mapped) is logged as a warning and retried on the next deploy — it does **not** fail the deploy. First-deploy case (no prior `spt@cplugs` entry) skips the prune entirely.
24	6. Cleans up the legacy manual-install dir at `~/.claude/plugins/spt/` if it exists as a regular directory. A manual install coexisting with the marketplace install makes Claude Code report the plugin as **not installed** in `/plugin` (see Troubleshooting below). Symlinked dirs are preserved for the dev-symlink workflow.
25	7. Runs `claude plugin install spt@cplugs` for cache-warm-up and `.orphaned_at` marker cleanup. **This is no longer the version-pointer flip** — the CLI is a no-op when `spt@cplugs` is already installed at any version (it only touches `lastUpdated`, not `version` / `installPath` / `gitCommitSha`). That silent no-op was the failure mode in the 1.9.8 → 1.9.9 deploy that left the pointer pinned to 1.9.8 (see `.planning/debug/26-06-live-skill-regression.md`). If `claude` is not in `PATH`, the script warns and tells you to run the command manually.
26	8. **Explicitly patches `~/.claude/plugins/installed_plugins.json`** to set the `spt@cplugs` entry's `version`, `installPath`, `gitCommitSha`, and `lastUpdated`. Atomic write via temp file + `Move-Item -Force`. This is the authoritative pointer flip — the step 7 CLI call is kept only for its cache-warm-up side effects. Uses `ConvertFrom-Json` / `ConvertTo-Json -Depth 10` (no `JavaScriptSerializer` — its earlier hand-rolled patch was removed for circular-reference crashes; the new patch round-trips through the PSObject graph safely).
27	9. **Re-reads `installed_plugins.json` and asserts `spt@cplugs.version == <Version>`** (and `installPath` matches the new cache dir). Any mismatch writes `ASSERTION FAILED: ...` to stderr and exits the script with status 1; the green "Deploy complete" banner never prints on drift.
28	10. Prints a reminder to run `/reload-plugins` inside Claude Code.
29
30	> The `Write-Step "Step N: ..."` labels in the script body still use their historical numbers (Step 3 for build, Step 4 for marketplace sync, etc. — Step 2 is intentionally absent) so log-grep tooling keeps matching prior runs. The numbered list above is the user-facing sequence; the in-script labels are the log contract.
31
32	### What it does NOT do
33
34	- Bump the plugin version in `plugin.json` unless `-Bump <patch|minor|major>` is passed.
35	- Push the spt repo. Even with `-Bump`, the bump commit lands locally and you push it yourself.
36	- Commit other changes in the spt repo (that's your job).
37	- Edit `~/.claude/settings.json`.
38	- Run `/reload-plugins` (that's a Claude Code slash command).
39	- Kill running `owl.exe` processes. Phase 18.4/18.5 shipped seamless binary handoff — old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json` once the new version is staged and `installed_plugins.json` flips (step 7 above). Killing them aborts the handoff and strands their session state.
40	- Wipe the entire cache tree. The old "blanket `rm -rf` on `cache/cplugs/spt`" behavior was correct before handoff shipped, but now it yanks the `.exe` out from under running processes mid-migration. The new targeted prune (step 5 above) keeps the PREVIOUS version dir on disk so in-flight handoffs complete.
41
42	### Two-phase changelog authoring (Phase 34 D-08 round 2)
43
44	Under the locked D-08 round 2 design (realigned in quick-260517-n4b post-UAT clarification), `-Bump` enforces a curation gate so each version-change Stop-hook surface ships with a real CHANGELOG entry inside the deployed bundle. The first `-Bump` for any new version aborts and seeds a TODO stub; the second `-Bump` (after the user fills it in and commits) proceeds.
45
46	**First `-Bump` run (no curated entry yet):**
47
48	1. **Abort gate** — refuses to proceed if `CHANGELOG.md` is missing OR contains a `TODO: changelog entry` marker from a prior bump.
49	2. **Curation gate** — checks for `## [<NewVersion>]` H2 header in `CHANGELOG.md`:
50	   - **Absent:** appends `## [<NewVersion>] - YYYY-MM-DD\n\n- TODO: changelog entry` to the working tree (uncommitted), then **EXIT 1** with instructions to fill and commit, then re-run. The current deploy does **not** succeed; no `plugin.json` / `Cargo.toml` mutation occurs.
51	   - **Present + TODO marker:** caught by abort gate above → EXIT 1.
52	   - **Present + curated body:** proceed past the gate.
53
54	**User action between runs:** Edit `CHANGELOG.md`, replace `- TODO: changelog entry` under the newly-stubbed `## [<NewVersion>]` H2 with real bullets in Keep-a-Changelog 1.1.0 categories (Added / Changed / Fixed / Removed / Deprecated / Security). Commit as e.g. `docs(NN): fill <NewVersion> CHANGELOG entry`.
55
56	**Second `-Bump` run (curated entry committed):**
57
58	1. Abort gate passes — no `TODO: changelog entry` marker remains.
59	2. Curation gate passes — `## [<NewVersion>]` H2 present with curated body.
60	3. Rewrite `plugin/spt/.claude-plugin/plugin.json` + `Cargo.toml` to the new version.
61	4. Stage + commit `plugin.json` + `Cargo.toml` (two files) as `chore: bump spt plugin to vX.Y.Z`. CHANGELOG.md is **not** in the bump commit — it landed in the prior curated-entry commit.
62	5. Build, sync to marketplace + cache (including curated `CHANGELOG.md`), refresh `installed_plugins.json`.
63
64	**Pre-authoring shortcut:** A caller who knows the next semver upfront can pre-author and commit the `## [<NewVersion>]` H2 with curated body before running `-Bump`. The curation gate sees the H2 present + curated and proceeds in one shot.
65
66	**Backstop:** If a stub somehow slips into a bump commit (manual flow, prior implementation), the next `-Bump`'s abort gate fires on the `TODO: changelog entry` marker and forces the user to curate before any further deploy.
67
68	### Bucket discipline for stub fill-in (quick-260520-uc2)
69
70	The version-change Stop hook surfaces curated CHANGELOG bullets to the user
71	on the next session boundary after a plugin upgrade. The user sees only the
72	release-facing buckets — implementation noise is filtered out at hook time.
73	The deploying agent (human or LLM) filling the `- TODO: changelog entry`
74	stub MUST follow these rules:
75
76	**Section vocabulary per H2 (`## [vX.Y.Z] - YYYY-MM-DD`):**
77
78	| Section | Use for | Surfaced to user? |
79	|---------|---------|---------------------|
80	| `### Added` | New user-facing capability (a flag, command, message, prompt, hook trigger they will notice) | YES |
81	| `### Changed` | Modified user-facing behavior (different message, different default, different timing) | YES |
82	| `### Fixed` | Bug fix the user actually observed or could observe | YES |
83	| `### Removed` | A user-facing capability that no longer exists | YES |
84	| `### Deprecated` | A capability the user should stop relying on | YES |
85	| `### Security` | Anything with user-relevant security impact | YES |
86	| `### Reverted` | A user-visible behavior was rolled back to a prior shape | YES |
87	| `### BTS` | Anything else: internal refactors, repo plumbing, deploy script changes, doc-string drift, test-only changes, code reverts that don't change user-visible behavior, commit SHAs, file-path callouts | **NO** — filtered out at hook time |
88
89	**UX-language rule for Added / Changed / Fixed (and the other surfaced
90	buckets):**
91	- Phrase as what the user sees, does, or runs. "`/spt:live` now offers an
92	  Other option" — not "added `AuqOption::Other` to `live::auto_pick`".
93	- Do NOT mention: code paths (`src/...`), function or struct names, prompt
94	  XML element names (`<spt-foo>`), commit SHAs, planning doc references
95	  (`Phase 34 D-08`, `quick-260517-n4b`), Rust types, file paths inside
96	  `plugin/spt/`, internal env-var names that are not part of the
97	  documented user surface.
98	- Keep bullets terse. One sentence each is the target; two short sentences
99	  is the ceiling. If you need more, you are probably writing BTS content.
100	- The Stop-hook "Yes, highlights only" path picks 2-5 bullets across the
101	  whole transition; bullets that are themselves short and UX-focused give
102	  it the right material to work with.
103
104	**BTS rule (anything off the UX path):**
105	- Repo-management changes (Cargo manifest tweaks unless they change runtime
106	  behavior, CI config, .gitignore, planning docs).
107	- Deploy-sequence changes (DEPLOY.ps1, DEPLOY.md, marketplace cache logic)
108	  unless the user's deploy workflow changes.
109	- Commit reverts where the reverted code never reached the user's machine.
110	- Doc-string / comment drift fixes.
111	- Test-only additions or framework changes.
112	- Internal refactors with no observable behavior change.
113	- Naming, struct-field, or fn-signature changes inside `src/`.
114
115	**When in doubt, ask: "Would a user typing `/spt:live` notice the
116	difference?"** If no, it's BTS.
117
118	**Examples of correct bucketing** (from real prior bullets, reframed):
119
120	- v1.10.20 "version-change owl message routed via TCP-first deliver_body" →
121	  `### Fixed`: "Version-change announcements arrive in-line with other
122	  high-priority messages instead of waiting for the next poll tick."
123	  (The file-path / fn-name detail moves to `### BTS` as: "Routed
124	  `version_changelog` payload through `deliver_body` like every other owl
125	  message.")
126	- v1.10.20 "WORKING_PERCH_NOTICE TCP-first flip reverted" — no user-visible
127	  symptom changed, so it belongs entirely in `### BTS`, not `### Reverted`.
128	- v1.10.18 the entire "260520-r2f commune-reference write-path fix" — the
129	  user-visible bullet is `### Fixed`: "Fresh `/spt:live` start now delivers
130	  first-commune context to the live agent reliably." Every file/line
131	  citation in the existing entry moves to `### BTS`.
132	- v1.10.17 chunking work — `### Fixed`: "Long poll-listener events no
133	  longer get truncated by the Claude Code Monitor at ~500 characters."
134	  Internal const names and chunk-boundary detail → `### BTS`.
135
136	**Backward compatibility:** Pre-v1.10.21 entries that pre-date this
137	discipline are left as-is. The hook's "skip `### BTS`" filter is a no-op
138	on them (they have no `### BTS` section) and the full-changelog path will
139	surface them verbatim per the existing behavior. The new discipline
140	applies prospectively starting with the next bump.
141
142	### Parameters
143
144	| Parameter | Default | Purpose |
145	|-----------|---------|---------|
146	| `-RepoRoot` | script's parent-of-parent dir | Path to the spt repo root |
147	| `-Marketplace` | `~/.claude/plugins/marketplaces/cplugs` | Local cplugs marketplace clone |
148	| `-PluginDir` | `~/.claude/plugins/spt` | Legacy manual-install dir. Removed by step 6 if present as a regular dir (it would otherwise conflict with the marketplace install). Symlinks at this path are preserved. |
149	| `-Bump` | _(unset)_ | Increment semver: `patch`/`minor`/`major`. Rewrites `plugin.json` and commits in the spt repo (no push). Aborts if `plugin.json` has uncommitted changes. |
150	| `-SkipPush` | off | Commit marketplace locally, don't push. Does not affect the `-Bump` commit (never pushed). |
151	| `-SkipMarketplaceCommit` | off | Skip marketplace commit+push entirely |
152	| `-DryRun` | off | Print every step with `[DRY-RUN]` prefix; no mutations |
153
154	### Example: dev loop without pushing
155
156	```powershell
157	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -SkipPush
158	```
159
160	### Example: dry-run to preview
161
162	```powershell
163	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -DryRun
164	```
165
166	### Example: bump patch + full deploy
167
168	```powershell
169	powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
170	```
171
172	### Troubleshooting: plugin shows as "not installed" in `/plugin`
173
174	**Symptom:** `/plugin` reports `spt` as not installed (and refuses to update), even though `installed_plugins.json` has `spt@cplugs` and the plugin clearly works. Commands like `/spt:*` may still function via the marketplace install while the UI misreports state.
175
176	**Cause:** A legacy manual-install directory at `~/.claude/plugins/spt/` (with its own `.claude-plugin/plugin.json`) living alongside the marketplace install at `~/.claude/plugins/cache/cplugs/spt/<version>/`. Claude Code enumerates both, and the unmanaged one — not tracked in `installed_plugins.json` — causes the conflict. Usually a leftover from a pre-marketplace deploy or from running the old "Deploy as Plugin (manual)" flow on top of a marketplace install.
177
178	**Fix:**
179
180	```bash
181	rm -rf ~/.claude/plugins/spt/
182	```
183
184	Then restart Claude Code (or run `/reload-plugins`). DEPLOY.ps1 step 6 now auto-cleans this directory on every marketplace deploy, so re-running the deploy script also resolves it.
185
186	> The manual steps documented in the sections below remain the authoritative spec. The script automates them; if the two ever diverge, the manual steps are correct and the script must be updated to match.
187
188	## Files Overview
189
190	| File | Purpose |
191	|------|---------|
192	| `owl.exe` | Native binary (compiled from `src/`) -- handles all owl and live commands |
193	| `plugin/spt/` | Plugin directory (deployed via marketplace or manually) |
194	| `plugin/spt/.claude-plugin/plugin.json` | Plugin manifest (name: spt) |
195	| `plugin/spt/skills/*/SKILL.md` | Per-command skill files (one directory per /spt: command) |
196	| `plugin/spt/skills/commune/commune.md` | Commune protocol for Self agents |
197	| `plugin/spt/hooks/hooks.json` | SessionStart + SessionEnd hook registrations |
198	| `psyche.md` | Psyche agent prompt template (embedded in binary via `include_str!`) |
199
200	## Deploy via Marketplace (recommended)
201
202	The cplugs private marketplace is the primary way to install spt. It bundles skill files, hooks, and the native binary.
203
204	### Marketplace Setup (one-time)
205
206	Register the cplugs marketplace if not already registered:
207
208	```bash
209	# Clone the marketplace repo
210	git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
211
212	# Register in ~/.claude/plugins/known_marketplaces.json
213	# Add a "cplugs" entry with source: { source: "github", repo: "SaberMage/cplugs" }
214	```
215
216	### Install from Marketplace
217
218	```
219	/plugin install spt@cplugs
220	```
221
222	### Update Marketplace Plugin Files
223
224	When skill files, hooks, or the binary change in this repo:
225
226	1. **Bump the version** in `plugin/spt/.claude-plugin/plugin.json` (e.g., `1.5.0` → `1.5.1`). The plugin manager compares this version to the cached copy — if they match, it skips the update entirely.
227	2. Build: `cargo build --release`
228	3. **Copy to marketplace** — binary, skills, hooks, and plugin manifest:
229	   ```bash
230	   MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/spt
231	   cp target/release/owl.exe "$MARKET/"
232	   cp -r plugin/spt/skills/* "$MARKET/skills/"
233	   cp -r plugin/spt/hooks/* "$MARKET/hooks/"
234	   cp plugin/spt/.claude-plugin/plugin.json "$MARKET/.claude-plugin/"
235	   ```
236	4. **Commit and push** the marketplace repo:
237	   ```bash
238	   cd ~/.claude/plugins/marketplaces/cplugs
239	   git add plugins/spt/ && git commit -m "spt: <describe change>" && git push
240	   ```
241	5. **Sync the new version into the cache, then (optionally) targeted-prune older versions.** `/plugin install` may not refresh the cache if the plugin is already enabled, so the new version directory must be created explicitly. **Do NOT stop live agents first** — Phase 18.4/18.5 shipped seamless binary handoff, and the handoff fires when `installed_plugins.json` flips in step 6 below. Old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json`. Killing `owl.exe` aborts that handoff.
242
243	   **Do NOT wipe the entire cache tree.** Running `owl.exe` processes are exec'ing from the PREVIOUS version directory until they migrate. `rm -rf ~/.claude/plugins/cache/cplugs/spt` yanks the `.exe` out from under them mid-handoff. At minimum, keep the PREVIOUS version dir on disk.
244
245	   **Sync all files** (not just the ones you changed) — partial syncs cause subtle mismatches between the binary and skill definitions:
246
247	   ```bash
248	   # Read the version from the plugin manifest — this is the authoritative version number
249	   VERSION=$(node -p "require('./plugin/spt/.claude-plugin/plugin.json').version")
250
251	   # Sync the new version into the cache (do NOT rm -rf the whole spt/ cache tree).
252	   CACHE=~/.claude/plugins/cache/cplugs/spt/$VERSION
253	   mkdir -p "$CACHE/.claude-plugin" "$CACHE/skills" "$CACHE/hooks"
254	   cp target/release/owl.exe "$CACHE/"
255	   cp -r plugin/spt/skills/* "$CACHE/skills/"
256	   cp -r plugin/spt/hooks/* "$CACHE/hooks/"
257	   cp plugin/spt/.claude-plugin/plugin.json "$CACHE/.claude-plugin/"
258
259	   # (Optional cleanup) After the next session start confirms no owl.exe has the
260	   # old .exe mapped, you can remove stale older-version dirs individually:
261	   #   rm -rf ~/.claude/plugins/cache/cplugs/spt/<old-version>
262	   # Keep the PREVIOUS version and the current $VERSION in place. PowerShell
263	   # users should prefer docs/DEPLOY.ps1, which handles this keep-set logic
264	   # automatically and tolerates sharing-violation residue.
265	   ```
266	6. **Refresh plugin state via the Claude Code CLI.** After the manual cache sync in step 5, `installed_plugins.json` still holds the `gitCommitSha` from the previous deploy; on the next session start Claude Code sees a mismatch against cplugs marketplace HEAD, writes `.orphaned_at` into the freshly-deployed cache dir, and refuses to source the SessionStart hook's `$OWL`/`$LIVE` exports — every `/spt:*` skill silently breaks. This is also the trigger for the seamless handoff: flipping `installPath` / `version` in `installed_plugins.json` is what old `owl.exe` listeners detect to initiate their self-migration to the new cache dir. See `.planning/debug/spt-env-vars-empty.md` for the full env-var evidence trail.
267
268	   The one-liner — canonical path, handles `installed_plugins.json` rewrite and orphan-marker cleanup atomically:
269
270	   ```bash
271	   claude plugin install spt@cplugs
272	   ```
273
274	   Equivalent to `/plugin install spt@cplugs` from inside Claude Code. Works identically from a script or CI. Restart the Claude Code session afterwards so the un-orphaned plugin's SessionStart hook runs cleanly and `$OWL`/`$LIVE` populate in Bash subprocesses.
275
276	   **Do not hand-patch `installed_plugins.json`.** Earlier revisions of this doc showed `jq` / `sed` fallbacks; they're brittle (an older DEPLOY.ps1 version crashed on a PowerShell `JavaScriptSerializer` circular-reference error here) and Claude Code may silently reject the result. If the CLI is unavailable, install it — don't edit the manifest by hand.
277	7. Run `/reload-plugins` in Claude Code to pick up skill file changes
278
279	### Enable the Plugin
280
281	Add to `~/.claude/settings.json`:
282
283	```json
284	{
285	  "enabledPlugins": {
286	    "spt@cplugs": true
287	  }
288	}
289	```
290
291	## Build
292
293	```bash
294	cargo build --release
295	```
296
297	The binary is at `target/release/owl.exe` (Windows) or `target/release/owl` (Linux/macOS). It handles both owl and live commands -- no separate binaries needed.
298
299	## Deploy as Plugin (manual, without marketplace)
300
301	> **Warning — do not combine with the marketplace install.** If you have `spt@cplugs` in `installed_plugins.json` (i.e. you installed via `/plugin install spt@cplugs`), do **not** also create `~/.claude/plugins/spt/`. Claude Code will enumerate both and report the marketplace install as **not installed** in `/plugin`, refusing to update it. The two flows are exclusive. See "Troubleshooting: plugin shows as 'not installed'" above.
302
303	If you don't have marketplace access, you can deploy the plugin manually. This replaces the old `~/.claude/skills/owl/` and `~/.claude/skills/live/` directories.
304
305	```bash
306	# Build
307	cargo build --release
308
309	# Deploy plugin directory
310	mkdir -p ~/.claude/plugins/spt
311	cp -r plugin/spt/.claude-plugin ~/.claude/plugins/spt/
312	cp -r plugin/spt/skills ~/.claude/plugins/spt/
313	cp -r plugin/spt/hooks ~/.claude/plugins/spt/
314	# Deploy binary into plugin directory
315	# IMPORTANT: hooks.json uses 'owl.exe' as the canonical binary name on ALL platforms.
316	# Windows:
317	cp target/release/owl.exe ~/.claude/plugins/spt/
318	# Linux/macOS -- copy as owl.exe so hooks.json works without platform detection:
319	cp target/release/owl ~/.claude/plugins/spt/owl.exe
320
321	# Remove old skill directories (clean break per D-08)
322	rm -rf ~/.claude/skills/owl
323	rm -rf ~/.claude/skills/live
324	```
325
326	**Cross-platform note:** The plugin's `hooks.json` references `$CLAUDE_PLUGIN_ROOT/owl.exe` for all hooks (`CLAUDE_PLUGIN_ROOT` is auto-provided by Claude Code's plugin system, pointing to the active plugin version directory). Using `owl.exe` as the canonical binary name on all platforms avoids needing platform-detection logic. On Unix, the binary is simply copied/renamed to `owl.exe` during deployment. Unix has no restriction on `.exe` extensions -- the file runs fine.
327
328	After deploying:
329
330	- The plugin's SessionStart hook auto-injects `$OWL` and `$LIVE` env vars via CLAUDE_ENV_FILE
331	- The plugin's SessionEnd hook runs cleanup-session
332	- No manual settings.json editing needed
333	- `$OWL` and `$LIVE` are set dynamically via CLAUDE_ENV_FILE on each session start, always pointing to the current plugin version's binary
334
335	## Remove Old Skill Directories
336
337	When switching to the plugin (via marketplace or manual), you **must** remove the old skill directories to avoid confusion:
338
339	```bash
340	# Required: remove old skill directories
341	rm -rf ~/.claude/skills/owl
342	rm -rf ~/.claude/skills/live
343	```
344
345	The plugin replaces both old skill directories. Old `/owl` and `/live` skill commands will no longer work. All commands are now under `/spt:` (e.g., `/spt:ready`, `/spt:live`). The binary CLI (`$OWL`, `$LIVE`) is unchanged.
346
347	## Env Var Setup
348
349	The plugin's SessionStart hook automatically writes `$OWL` and `$LIVE` env vars via `CLAUDE_ENV_FILE` on each session start. These point to the current plugin version's binary — no manual configuration needed.
350
351	**If env vars are missing** (e.g., `$OWL: command not found`), restart the Claude Code session — the SessionStart hook will re-run and populate `$OWL` / `$LIVE` from the current active plugin version via `CLAUDE_ENV_FILE`. There is no in-session force-write command; that used to exist as `/spt:env-setup` but was removed because its `settings.json` fallback pinned binary paths and broke upgrades. If restarting doesn't fix it, run `$OWL doctor` to diagnose — something is wrong with the plugin install or the hook.
352
353	**Stale env vars in settings.json:** If you have `OWL`/`LIVE` entries in `~/.claude/settings.json` from a pre-plugin install, remove them. They pin to a specific version and will override the plugin's dynamic env var injection, causing hooks to call the wrong binary after upgrades.
354
355	## Legacy / Standalone Deployment (removed)
356
357	Standalone deployment via `~/.claude/skills/owl/` and `~/.claude/skills/live/` is no longer supported. The root-level `SKILL.md`, `LIVE-SKILL.md`, and `commune.md` files have been removed. All skill definitions live under `plugin/spt/skills/`. Use the plugin system (marketplace or manual) for deployment.
358
359	## Development Workflow
360
361	For plugin development, use `--plugin-dir` to test the plugin from the repo without deploying:
362
363	```bash
364	# Build first
365	cargo build --release
366
367	# Copy binary into plugin dir
368	# Windows:
369	cp target/release/owl.exe plugin/spt/
370	# Linux/macOS:
371	cp target/release/owl plugin/spt/owl.exe
372
373	# Run Claude with the local plugin directory
374	claude --plugin-dir ./plugin/spt
375
376	# Or symlink for persistent development:
377	# ln -s $(pwd)/plugin/spt ~/.claude/plugins/spt
378	```
379
380	Note: When using `--plugin-dir`, the binary must already be built and present in the plugin directory as `owl.exe`.
381
382	## Bundle for Sharing
383
384	```bash
385	cargo build --release
386	# Windows:
387	cp target/release/owl.exe plugin/spt/
388	# Linux/macOS:
389	cp target/release/owl plugin/spt/owl.exe
390	tar czf spt-plugin.tar.gz -C plugin spt/
391	```
392
393	Install from bundle:
394
395	```bash
396	mkdir -p ~/.claude/plugins
397	tar xzf spt-plugin.tar.gz -C ~/.claude/plugins/
398	rm -rf ~/.claude/skills/owl ~/.claude/skills/live
399	```
400
401	Then start a new Claude Code session. The plugin's SessionStart hook will auto-inject `$OWL` and `$LIVE`.
402
403	## Commit Changes
404
405	```bash
406	git add plugin/ src/ psyche.md docs/ Cargo.toml
407	git commit -m "owl: <describe change>"
408	```
409

# Deploying Spacetime (spt) Plugin

## Automated Deploy (DEPLOY.ps1)

For the common case — binary + skills + hooks + marketplace + cache refresh for the version already set in `plugin/spt/.claude-plugin/plugin.json` — use the PowerShell script:

```powershell
# From repo root, after bumping plugin.json version:
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1

# Or let the script bump the version for you:
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
```

The script automates the build-through-cache-sync sequence and the state refresh (by shelling out to `claude plugin install spt@cplugs`). It **stops before `/reload-plugins`** — you must run that yourself inside Claude Code.

### What it does (in order)

1. Reads the authoritative version from `plugin/spt/.claude-plugin/plugin.json`. If `-Bump <patch|minor|major>` is passed, runs the bump sub-sequence: (a) abort gate — refuses to proceed if `CHANGELOG.md` is missing OR still contains a `TODO: changelog entry` marker from a prior bump; (b) rewrites `plugin.json` + `Cargo.toml` to the new version; (c) appends a `TODO: changelog entry` stub H2 to `CHANGELOG.md` for the new version; (d) stages `plugin.json` + `Cargo.toml` + `CHANGELOG.md` and commits as `chore: bump spt plugin to vX.Y.Z` in the spt repo (no push). The bump commit ships with the TODO stub; the user is expected to author a curated `CHANGELOG.md` entry for the new version in a follow-up commit before the next `-Bump`. See "Two-phase changelog authoring" below.
2. `cargo build --release` in the repo root.
3. Syncs `owl.exe`, `skills/`, `hooks/`, and `plugin.json` to the cplugs marketplace clone.
4. Commits + pushes the marketplace (unless skipped).
5. Syncs the new version into `~/.claude/plugins/cache/cplugs/spt/<Version>/`, then targeted-prunes older semver directories. The script keeps the NEW version dir, the PREVIOUS version dir (running `owl.exe` processes are still exec'ing from there until they self-migrate via handoff), and the `installPath` leaf dir recorded in `installed_plugins.json` (captured before step 7 rewrites it). Non-semver directories and directories in the keep-set are never touched. Sharing-violation residue (a dir that can't be removed because `owl.exe` still has the `.exe` mapped) is logged as a warning and retried on the next deploy — it does **not** fail the deploy. First-deploy case (no prior `spt@cplugs` entry) skips the prune entirely.
6. Cleans up the legacy manual-install dir at `~/.claude/plugins/spt/` if it exists as a regular directory. A manual install coexisting with the marketplace install makes Claude Code report the plugin as **not installed** in `/plugin` (see Troubleshooting below). Symlinked dirs are preserved for the dev-symlink workflow.
7. Runs `claude plugin install spt@cplugs` for cache-warm-up and `.orphaned_at` marker cleanup. **This is no longer the version-pointer flip** — the CLI is a no-op when `spt@cplugs` is already installed at any version (it only touches `lastUpdated`, not `version` / `installPath` / `gitCommitSha`). That silent no-op was the failure mode in the 1.9.8 → 1.9.9 deploy that left the pointer pinned to 1.9.8 (see `.planning/debug/26-06-live-skill-regression.md`). If `claude` is not in `PATH`, the script warns and tells you to run the command manually.
8. **Explicitly patches `~/.claude/plugins/installed_plugins.json`** to set the `spt@cplugs` entry's `version`, `installPath`, `gitCommitSha`, and `lastUpdated`. Atomic write via temp file + `Move-Item -Force`. This is the authoritative pointer flip — the step 7 CLI call is kept only for its cache-warm-up side effects. Uses `ConvertFrom-Json` / `ConvertTo-Json -Depth 10` (no `JavaScriptSerializer` — its earlier hand-rolled patch was removed for circular-reference crashes; the new patch round-trips through the PSObject graph safely).
9. **Re-reads `installed_plugins.json` and asserts `spt@cplugs.version == <Version>`** (and `installPath` matches the new cache dir). Any mismatch writes `ASSERTION FAILED: ...` to stderr and exits the script with status 1; the green "Deploy complete" banner never prints on drift.
10. Prints a reminder to run `/reload-plugins` inside Claude Code.

> The `Write-Step "Step N: ..."` labels in the script body still use their historical numbers (Step 3 for build, Step 4 for marketplace sync, etc. — Step 2 is intentionally absent) so log-grep tooling keeps matching prior runs. The numbered list above is the user-facing sequence; the in-script labels are the log contract.

### What it does NOT do

- Bump the plugin version in `plugin.json` unless `-Bump <patch|minor|major>` is passed.
- Push the spt repo. Even with `-Bump`, the bump commit lands locally and you push it yourself.
- Commit other changes in the spt repo (that's your job).
- Edit `~/.claude/settings.json`.
- Run `/reload-plugins` (that's a Claude Code slash command).
- Kill running `owl.exe` processes. Phase 18.4/18.5 shipped seamless binary handoff — old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json` once the new version is staged and `installed_plugins.json` flips (step 7 above). Killing them aborts the handoff and strands their session state.
- Wipe the entire cache tree. The old "blanket `rm -rf` on `cache/cplugs/spt`" behavior was correct before handoff shipped, but now it yanks the `.exe` out from under running processes mid-migration. The new targeted prune (step 5 above) keeps the PREVIOUS version dir on disk so in-flight handoffs complete.

### Two-phase changelog authoring (Phase 34 D-08 round 2)

Under the locked D-08 round 2 design (realigned in quick-260517-n4b post-UAT clarification), `-Bump` enforces a curation gate so each version-change Stop-hook surface ships with a real CHANGELOG entry inside the deployed bundle. The first `-Bump` for any new version aborts and seeds a TODO stub; the second `-Bump` (after the user fills it in and commits) proceeds.

**First `-Bump` run (no curated entry yet):**

1. **Abort gate** — refuses to proceed if `CHANGELOG.md` is missing OR contains a `TODO: changelog entry` marker from a prior bump.
2. **Curation gate** — checks for `## [<NewVersion>]` H2 header in `CHANGELOG.md`:
   - **Absent:** appends `## [<NewVersion>] - YYYY-MM-DD\n\n- TODO: changelog entry` to the working tree (uncommitted), then **EXIT 1** with instructions to fill and commit, then re-run. The current deploy does **not** succeed; no `plugin.json` / `Cargo.toml` mutation occurs.
   - **Present + TODO marker:** caught by abort gate above → EXIT 1.
   - **Present + curated body:** proceed past the gate.

**User action between runs:** Edit `CHANGELOG.md`, replace `- TODO: changelog entry` under the newly-stubbed `## [<NewVersion>]` H2 with real bullets in Keep-a-Changelog 1.1.0 categories (Added / Changed / Fixed / Removed / Deprecated / Security). Commit as e.g. `docs(NN): fill <NewVersion> CHANGELOG entry`.

**Second `-Bump` run (curated entry committed):**

1. Abort gate passes — no `TODO: changelog entry` marker remains.
2. Curation gate passes — `## [<NewVersion>]` H2 present with curated body.
3. Rewrite `plugin/spt/.claude-plugin/plugin.json` + `Cargo.toml` to the new version.
4. Stage + commit `plugin.json` + `Cargo.toml` (two files) as `chore: bump spt plugin to vX.Y.Z`. CHANGELOG.md is **not** in the bump commit — it landed in the prior curated-entry commit.
5. Build, sync to marketplace + cache (including curated `CHANGELOG.md`), refresh `installed_plugins.json`.

**Pre-authoring shortcut:** A caller who knows the next semver upfront can pre-author and commit the `## [<NewVersion>]` H2 with curated body before running `-Bump`. The curation gate sees the H2 present + curated and proceeds in one shot.

**Backstop:** If a stub somehow slips into a bump commit (manual flow, prior implementation), the next `-Bump`'s abort gate fires on the `TODO: changelog entry` marker and forces the user to curate before any further deploy.

### Bucket discipline for stub fill-in (quick-260520-uc2)

The version-change Stop hook surfaces curated CHANGELOG bullets to the user
on the next session boundary after a plugin upgrade. The user sees only the
release-facing buckets — implementation noise is filtered out at hook time.
The deploying agent (human or LLM) filling the `- TODO: changelog entry`
stub MUST follow these rules:

**Section vocabulary per H2 (`## [vX.Y.Z] - YYYY-MM-DD`):**

| Section | Use for | Surfaced to user? |
|---------|---------|---------------------|
| `### Added` | New user-facing capability (a flag, command, message, prompt, hook trigger they will notice) | YES |
| `### Changed` | Modified user-facing behavior (different message, different default, different timing) | YES |
| `### Fixed` | Bug fix the user actually observed or could observe | YES |
| `### Removed` | A user-facing capability that no longer exists | YES |
| `### Deprecated` | A capability the user should stop relying on | YES |
| `### Security` | Anything with user-relevant security impact | YES |
| `### Reverted` | A user-visible behavior was rolled back to a prior shape | YES |
| `### BTS` | Anything else: internal refactors, repo plumbing, deploy script changes, doc-string drift, test-only changes, code reverts that don't change user-visible behavior, commit SHAs, file-path callouts | **NO** — filtered out at hook time |

**UX-language rule for Added / Changed / Fixed (and the other surfaced
buckets):**
- Phrase as what the user sees, does, or runs. "`/spt:live` now offers an
  Other option" — not "added `AuqOption::Other` to `live::auto_pick`".
- Do NOT mention: code paths (`src/...`), function or struct names, prompt
  XML element names (`<spt-foo>`), commit SHAs, planning doc references
  (`Phase 34 D-08`, `quick-260517-n4b`), Rust types, file paths inside
  `plugin/spt/`, internal env-var names that are not part of the
  documented user surface.
- Keep bullets terse. One sentence each is the target; two short sentences
  is the ceiling. If you need more, you are probably writing BTS content.
- The Stop-hook "Yes, highlights only" path picks 2-5 bullets across the
  whole transition; bullets that are themselves short and UX-focused give
  it the right material to work with.

**BTS rule (anything off the UX path):**
- Repo-management changes (Cargo manifest tweaks unless they change runtime
  behavior, CI config, .gitignore, planning docs).
- Deploy-sequence changes (DEPLOY.ps1, DEPLOY.md, marketplace cache logic)
  unless the user's deploy workflow changes.
- Commit reverts where the reverted code never reached the user's machine.
- Doc-string / comment drift fixes.
- Test-only additions or framework changes.
- Internal refactors with no observable behavior change.
- Naming, struct-field, or fn-signature changes inside `src/`.

**When in doubt, ask: "Would a user typing `/spt:live` notice the
difference?"** If no, it's BTS.

**Examples of correct bucketing** (from real prior bullets, reframed):

- v1.10.20 "version-change owl message routed via TCP-first deliver_body" →
  `### Fixed`: "Version-change announcements arrive in-line with other
  high-priority messages instead of waiting for the next poll tick."
  (The file-path / fn-name detail moves to `### BTS` as: "Routed
  `version_changelog` payload through `deliver_body` like every other owl
  message.")
- v1.10.20 "WORKING_PERCH_NOTICE TCP-first flip reverted" — no user-visible
  symptom changed, so it belongs entirely in `### BTS`, not `### Reverted`.
- v1.10.18 the entire "260520-r2f commune-reference write-path fix" — the
  user-visible bullet is `### Fixed`: "Fresh `/spt:live` start now delivers
  first-commune context to the live agent reliably." Every file/line
  citation in the existing entry moves to `### BTS`.
- v1.10.17 chunking work — `### Fixed`: "Long poll-listener events no
  longer get truncated by the Claude Code Monitor at ~500 characters."
  Internal const names and chunk-boundary detail → `### BTS`.

**Backward compatibility:** Pre-v1.10.21 entries that pre-date this
discipline are left as-is. The hook's "skip `### BTS`" filter is a no-op
on them (they have no `### BTS` section) and the full-changelog path will
surface them verbatim per the existing behavior. The new discipline
applies prospectively starting with the next bump.

### Parameters

| Parameter | Default | Purpose |
|-----------|---------|---------|
| `-RepoRoot` | script's parent-of-parent dir | Path to the spt repo root |
| `-Marketplace` | `~/.claude/plugins/marketplaces/cplugs` | Local cplugs marketplace clone |
| `-PluginDir` | `~/.claude/plugins/spt` | Legacy manual-install dir. Removed by step 6 if present as a regular dir (it would otherwise conflict with the marketplace install). Symlinks at this path are preserved. |
| `-Bump` | _(unset)_ | Increment semver: `patch`/`minor`/`major`. Rewrites `plugin.json` and commits in the spt repo (no push). Aborts if `plugin.json` has uncommitted changes. |
| `-SkipPush` | off | Commit marketplace locally, don't push. Does not affect the `-Bump` commit (never pushed). |
| `-SkipMarketplaceCommit` | off | Skip marketplace commit+push entirely |
| `-DryRun` | off | Print every step with `[DRY-RUN]` prefix; no mutations |

### Example: dev loop without pushing

```powershell
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -SkipPush
```

### Example: dry-run to preview

```powershell
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -DryRun
```

### Example: bump patch + full deploy

```powershell
powershell -ExecutionPolicy Bypass -File docs/DEPLOY.ps1 -Bump patch
```

### Troubleshooting: plugin shows as "not installed" in `/plugin`

**Symptom:** `/plugin` reports `spt` as not installed (and refuses to update), even though `installed_plugins.json` has `spt@cplugs` and the plugin clearly works. Commands like `/spt:*` may still function via the marketplace install while the UI misreports state.

**Cause:** A legacy manual-install directory at `~/.claude/plugins/spt/` (with its own `.claude-plugin/plugin.json`) living alongside the marketplace install at `~/.claude/plugins/cache/cplugs/spt/<version>/`. Claude Code enumerates both, and the unmanaged one — not tracked in `installed_plugins.json` — causes the conflict. Usually a leftover from a pre-marketplace deploy or from running the old "Deploy as Plugin (manual)" flow on top of a marketplace install.

**Fix:**

```bash
rm -rf ~/.claude/plugins/spt/
```

Then restart Claude Code (or run `/reload-plugins`). DEPLOY.ps1 step 6 now auto-cleans this directory on every marketplace deploy, so re-running the deploy script also resolves it.

> The manual steps documented in the sections below remain the authoritative spec. The script automates them; if the two ever diverge, the manual steps are correct and the script must be updated to match.

## Files Overview

| File | Purpose |
|------|---------|
| `owl.exe` | Native binary (compiled from `src/`) -- handles all owl and live commands |
| `plugin/spt/` | Plugin directory (deployed via marketplace or manually) |
| `plugin/spt/.claude-plugin/plugin.json` | Plugin manifest (name: spt) |
| `plugin/spt/skills/*/SKILL.md` | Per-command skill files (one directory per /spt: command) |
| `plugin/spt/skills/commune/commune.md` | Commune protocol for Self agents |
| `plugin/spt/hooks/hooks.json` | SessionStart + SessionEnd hook registrations |
| `psyche.md` | Psyche agent prompt template (embedded in binary via `include_str!`) |

## Deploy via Marketplace (recommended)

The cplugs private marketplace is the primary way to install spt. It bundles skill files, hooks, and the native binary.

### Marketplace Setup (one-time)

Register the cplugs marketplace if not already registered:

```bash
# Clone the marketplace repo
git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs

# Register in ~/.claude/plugins/known_marketplaces.json
# Add a "cplugs" entry with source: { source: "github", repo: "SaberMage/cplugs" }
```

### Install from Marketplace

```
/plugin install spt@cplugs
```

### Update Marketplace Plugin Files

When skill files, hooks, or the binary change in this repo:

1. **Bump the version** in `plugin/spt/.claude-plugin/plugin.json` (e.g., `1.5.0` → `1.5.1`). The plugin manager compares this version to the cached copy — if they match, it skips the update entirely.
2. Build: `cargo build --release`
3. **Copy to marketplace** — binary, skills, hooks, and plugin manifest:
   ```bash
   MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/spt
   cp target/release/owl.exe "$MARKET/"
   cp -r plugin/spt/skills/* "$MARKET/skills/"
   cp -r plugin/spt/hooks/* "$MARKET/hooks/"
   cp plugin/spt/.claude-plugin/plugin.json "$MARKET/.claude-plugin/"
   ```
4. **Commit and push** the marketplace repo:
   ```bash
   cd ~/.claude/plugins/marketplaces/cplugs
   git add plugins/spt/ && git commit -m "spt: <describe change>" && git push
   ```
5. **Sync the new version into the cache, then (optionally) targeted-prune older versions.** `/plugin install` may not refresh the cache if the plugin is already enabled, so the new version directory must be created explicitly. **Do NOT stop live agents first** — Phase 18.4/18.5 shipped seamless binary handoff, and the handoff fires when `installed_plugins.json` flips in step 6 below. Old listeners self-migrate via stdio-relay and Psyche wrappers detach + rehydrate from `wrapper-state.json`. Killing `owl.exe` aborts that handoff.

   **Do NOT wipe the entire cache tree.** Running `owl.exe` processes are exec'ing from the PREVIOUS version directory until they migrate. `rm -rf ~/.claude/plugins/cache/cplugs/spt` yanks the `.exe` out from under them mid-handoff. At minimum, keep the PREVIOUS version dir on disk.

   **Sync all files** (not just the ones you changed) — partial syncs cause subtle mismatches between the binary and skill definitions:

   ```bash
   # Read the version from the plugin manifest — this is the authoritative version number
   VERSION=$(node -p "require('./plugin/spt/.claude-plugin/plugin.json').version")

   # Sync the new version into the cache (do NOT rm -rf the whole spt/ cache tree).
   CACHE=~/.claude/plugins/cache/cplugs/spt/$VERSION
   mkdir -p "$CACHE/.claude-plugin" "$CACHE/skills" "$CACHE/hooks"
   cp target/release/owl.exe "$CACHE/"
   cp -r plugin/spt/skills/* "$CACHE/skills/"
   cp -r plugin/spt/hooks/* "$CACHE/hooks/"
   cp plugin/spt/.claude-plugin/plugin.json "$CACHE/.claude-plugin/"

   # (Optional cleanup) After the next session start confirms no owl.exe has the
   # old .exe mapped, you can remove stale older-version dirs individually:
   #   rm -rf ~/.claude/plugins/cache/cplugs/spt/<old-version>
   # Keep the PREVIOUS version and the current $VERSION in place. PowerShell
   # users should prefer docs/DEPLOY.ps1, which handles this keep-set logic
   # automatically and tolerates sharing-violation residue.
   ```
6. **Refresh plugin state via the Claude Code CLI.** After the manual cache sync in step 5, `installed_plugins.json` still holds the `gitCommitSha` from the previous deploy; on the next session start Claude Code sees a mismatch against cplugs marketplace HEAD, writes `.orphaned_at` into the freshly-deployed cache dir, and refuses to source the SessionStart hook's `$OWL`/`$LIVE` exports — every `/spt:*` skill silently breaks. This is also the trigger for the seamless handoff: flipping `installPath` / `version` in `installed_plugins.json` is what old `owl.exe` listeners detect to initiate their self-migration to the new cache dir. See `.planning/debug/spt-env-vars-empty.md` for the full env-var evidence trail.

   The one-liner — canonical path, handles `installed_plugins.json` rewrite and orphan-marker cleanup atomically:

   ```bash
   claude plugin install spt@cplugs
   ```

   Equivalent to `/plugin install spt@cplugs` from inside Claude Code. Works identically from a script or CI. Restart the Claude Code session afterwards so the un-orphaned plugin's SessionStart hook runs cleanly and `$OWL`/`$LIVE` populate in Bash subprocesses.

   **Do not hand-patch `installed_plugins.json`.** Earlier revisions of this doc showed `jq` / `sed` fallbacks; they're brittle (an older DEPLOY.ps1 version crashed on a PowerShell `JavaScriptSerializer` circular-reference error here) and Claude Code may silently reject the result. If the CLI is unavailable, install it — don't edit the manifest by hand.
7. Run `/reload-plugins` in Claude Code to pick up skill file changes

### Enable the Plugin

Add to `~/.claude/settings.json`:

```json
{
  "enabledPlugins": {
    "spt@cplugs": true
  }
}
```

## Build

```bash
cargo build --release
```

The binary is at `target/release/owl.exe` (Windows) or `target/release/owl` (Linux/macOS). It handles both owl and live commands -- no separate binaries needed.

## Deploy as Plugin (manual, without marketplace)

> **Warning — do not combine with the marketplace install.** If you have `spt@cplugs` in `installed_plugins.json` (i.e. you installed via `/plugin install spt@cplugs`), do **not** also create `~/.claude/plugins/spt/`. Claude Code will enumerate both and report the marketplace install as **not installed** in `/plugin`, refusing to update it. The two flows are exclusive. See "Troubleshooting: plugin shows as 'not installed'" above.

If you don't have marketplace access, you can deploy the plugin manually. This replaces the old `~/.claude/skills/owl/` and `~/.claude/skills/live/` directories.

```bash
# Build
cargo build --release

# Deploy plugin directory
mkdir -p ~/.claude/plugins/spt
cp -r plugin/spt/.claude-plugin ~/.claude/plugins/spt/
cp -r plugin/spt/skills ~/.claude/plugins/spt/
cp -r plugin/spt/hooks ~/.claude/plugins/spt/
# Deploy binary into plugin directory
# IMPORTANT: hooks.json uses 'owl.exe' as the canonical binary name on ALL platforms.
# Windows:
cp target/release/owl.exe ~/.claude/plugins/spt/
# Linux/macOS -- copy as owl.exe so hooks.json works without platform detection:
cp target/release/owl ~/.claude/plugins/spt/owl.exe

# Remove old skill directories (clean break per D-08)
rm -rf ~/.claude/skills/owl
rm -rf ~/.claude/skills/live
```

**Cross-platform note:** The plugin's `hooks.json` references `$CLAUDE_PLUGIN_ROOT/owl.exe` for all hooks (`CLAUDE_PLUGIN_ROOT` is auto-provided by Claude Code's plugin system, pointing to the active plugin version directory). Using `owl.exe` as the canonical binary name on all platforms avoids needing platform-detection logic. On Unix, the binary is simply copied/renamed to `owl.exe` during deployment. Unix has no restriction on `.exe` extensions -- the file runs fine.

After deploying:

- The plugin's SessionStart hook auto-injects `$OWL` and `$LIVE` env vars via CLAUDE_ENV_FILE
- The plugin's SessionEnd hook runs cleanup-session
- No manual settings.json editing needed
- `$OWL` and `$LIVE` are set dynamically via CLAUDE_ENV_FILE on each session start, always pointing to the current plugin version's binary

## Remove Old Skill Directories

When switching to the plugin (via marketplace or manual), you **must** remove the old skill directories to avoid confusion:

```bash
# Required: remove old skill directories
rm -rf ~/.claude/skills/owl
rm -rf ~/.claude/skills/live
```

The plugin replaces both old skill directories. Old `/owl` and `/live` skill commands will no longer work. All commands are now under `/spt:` (e.g., `/spt:ready`, `/spt:live`). The binary CLI (`$OWL`, `$LIVE`) is unchanged.

## Env Var Setup

The plugin's SessionStart hook automatically writes `$OWL` and `$LIVE` env vars via `CLAUDE_ENV_FILE` on each session start. These point to the current plugin version's binary — no manual configuration needed.

**If env vars are missing** (e.g., `$OWL: command not found`), restart the Claude Code session — the SessionStart hook will re-run and populate `$OWL` / `$LIVE` from the current active plugin version via `CLAUDE_ENV_FILE`. There is no in-session force-write command; that used to exist as `/spt:env-setup` but was removed because its `settings.json` fallback pinned binary paths and broke upgrades. If restarting doesn't fix it, run `$OWL doctor` to diagnose — something is wrong with the plugin install or the hook.

**Stale env vars in settings.json:** If you have `OWL`/`LIVE` entries in `~/.claude/settings.json` from a pre-plugin install, remove them. They pin to a specific version and will override the plugin's dynamic env var injection, causing hooks to call the wrong binary after upgrades.

## Legacy / Standalone Deployment (removed)

Standalone deployment via `~/.claude/skills/owl/` and `~/.claude/skills/live/` is no longer supported. The root-level `SKILL.md`, `LIVE-SKILL.md`, and `commune.md` files have been removed. All skill definitions live under `plugin/spt/skills/`. Use the plugin system (marketplace or manual) for deployment.

## Development Workflow

For plugin development, use `--plugin-dir` to test the plugin from the repo without deploying:

```bash
# Build first
cargo build --release

# Copy binary into plugin dir
# Windows:
cp target/release/owl.exe plugin/spt/
# Linux/macOS:
cp target/release/owl plugin/spt/owl.exe

# Run Claude with the local plugin directory
claude --plugin-dir ./plugin/spt

# Or symlink for persistent development:
# ln -s $(pwd)/plugin/spt ~/.claude/plugins/spt
```

Note: When using `--plugin-dir`, the binary must already be built and present in the plugin directory as `owl.exe`.

## Bundle for Sharing

```bash
cargo build --release
# Windows:
cp target/release/owl.exe plugin/spt/
# Linux/macOS:
cp target/release/owl plugin/spt/owl.exe
tar czf spt-plugin.tar.gz -C plugin spt/
```

Install from bundle:

```bash
mkdir -p ~/.claude/plugins
tar xzf spt-plugin.tar.gz -C ~/.claude/plugins/
rm -rf ~/.claude/skills/owl ~/.claude/skills/live
```

Then start a new Claude Code session. The plugin's SessionStart hook will auto-inject `$OWL` and `$LIVE`.

## Commit Changes

```bash
git add plugin/ src/ psyche.md docs/ Cargo.toml
git commit -m "owl: <describe change>"
```


`send` needs explicit IDs (auto-detect failed). Resending with explicit target+from, and reading current RELEASE-RUNBOOK to find the insert point.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && $OWL send doyle perri <<'EOF'
ACK cplugs-HOW. Underway. Foundation body green: traceable-reqs check EXIT=0 (fixed seed manifest_error via scan-root trim; fixed 4 tag-leak findings in experimplate's own example docs — flagging as experimplate template feedback). Minted skeleton-milestone REQs (DIST-PLUGIN-SKELETON, BOOTSTRAP-INSTALL, HOOKS-API, MANIFEST-SCHEMA, UPS-INJECTION, SKILL-VERSION), all inactive.

Read claude_skill_owl/docs/DEPLOY.md. Taking SKELETON SUBSET only (marketplace one-time setup, per-bump copy+commit+push, pointer-flip via `claude plugin install <plugin>@cplugs` + /reload-plugins; 3 gotchas: no hand-patch installed_plugins.json, no legacy manual-install dir alongside marketplace, restart session after install). Skipping owl.exe binary sync + targeted-prune/seamless-handoff/keep-PREVIOUS machinery (binary-specific, N/A to thin skeleton). Folding into RELEASE-RUNBOOK.md now, plugin-version vs manifest-version kept distinct. Ping when committed.
EOF
echo "sent=$?"

Resend ack to doyle with explicit ids


➜ SENT:doyle
sent=0