Your questions have been answered: "Given the adapter can't be `spt` but the skills want `/spt:*` — what's the unification rule? (cc launcher shortcut stays `cc` either way.)"=(no option selected) notes: 1. no like, the `[update]` message field really exists. see the screenshotted public docs excerpt: C:\Users\decid\Documents\ShareX\Screenshots\2026-06\chrome_nek0tgEBHt.png -but you have a fair point that the output could be more composable using `spt adapter get-string` 2. note that `/reload-plugins` is something the user needs to do in the claude code tui. -we need to ensure that spt-core gracefully works with arbitrary files like scripts being packed into adapter archives. it's also worth noting that, unless spt-core has its own bash interpreter, `.sh` won't work for every OS. 3. agree composite `[update]`. might want to confirm with public docs and/or doyle that the "install is the first update" is a functional truth. --- topic 2: agree with option (1) and renaming the gh repo. not an issue as there's only one end user today.. You can now continue with these answers in mind.

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

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

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

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

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


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


Let me look at that screenshot — I was working from the vendored schema, which may lag the published docs on the `[update]` message field.


Good — I was reading the stale vendored schema; the screenshot is the live docs and you're right. Correcting:

**`[update].message` exists** — purpose-built for exactly this: *"a plain human notice `spt adapter update` prints to stdout, markdown-rendered, **only when a new version is actually applied** (never on a no-op)… Use it to tell the operator what to do after updating — e.g. `Run /reload-plugins in any ongoing sessions.` for spt-claude-code."* No `{key}` substitution (static string). And the header confirms your other point: *"How spt-core updates (and first installs — **install is the first update**)"* — so that's **docs-confirmed, not a doyle ask.**

That reshapes the update design — cleaner and lower-dependency than I had:

- **avenue = `gh_release`** pulls the `.spt` (manifest + binaries + strings — all the high-churn). Ship now.
- **`message`** = the static post-update notice telling the user the residual manual steps: `claude plugin update spt` + `/reload-plugins`. Your correction lands here — **`/reload-plugins` is a TUI action**, so it *can't* be automated; the `message` field is precisely the right home for instructing it. The notice prints only on a real version bump.
- So **as-shipped, it's one CLI command + a printed next-step the user follows** — honest, since `/reload-plugins` already needs the user in the loop.
- **The composite (`gh_release` + a delegated post-step) stays the doyle ask** — but now only to *automate* the `claude plugin update` half (shrinking the message to just "/reload-plugins"). Nice-to-have, not blocking.

On your cross-platform flag: if we do pursue the composite plugin-sync, it should be a **compiled binary** (a 4th Rust tool in the triple dirs), **not a `.sh`** — sidesteps both the no-bash-on-Windows problem *and* the "arbitrary files in the archive" concern (it's just another binary in the slots spt-core already handles; the existing tools' Cargo comment even cites "no interpreter-on-PATH hazard" as the reason they're Rust). It still can't run `/reload-plugins` — that stays the user's TUI step.

**Doyle asks, total: 2** — (1) `spt api run-hook` (generic dispatch, yours to raise), (2) composite `[update]` (optional, automates the plugin half). Everything else rides the published surface.

**Topic 2 locked:** option 1 — `claude-spt` for repo+adapter (rename `spt-claude-code` → `claude-spt`), `spt` for plugin+skills via the succession; repo-rename cost is a non-issue (one user today).

We've resolved the big forks. Before I capture, two residual threads remain:


AskUserQuestion

Question: Residual A — the full-fat reactive skills (commune/send/signoff) still churn IN the plugin. Their reactive guidance is ALREADY delivered by the SessionStart brief (adapter strings — the spacetime-reorientation block lists send/commune/signoff usage). Thin the plugin SKILL.md to stubs and rely on the brief?

Choices:
Move the reactive-skill bodies fully to adapter strings (the SessionStart brief already carries this guidance); plugin SKILL.md becomes frontmatter-only stubs. Then ALL skill bodies ride `spt adapter update` — the plugin truly stops carrying churning prose. Cost: the brief grows slightly (already injects messaging guidance).
PLUGIN skills/ (all stubs, frontmatter only)
  commune/SKILL.md  send/SKILL.md  signoff/SKILL.md
ADAPTER strings (spt adapter update)
  briefs/ already inject send/commune/signoff usage
  -> reactive bodies live here, one source

ALL skill prose now rides the adapter channel
Plugin = hooks.json(generic) + stubs + plugin.json + bootstrap
Leave commune/send/signoff full-fat in the plugin (status quo). They're rare-churn after this wave; the SessionStart brief is belt-and-suspenders. Simpler now, but accepts that these specific bodies update on the plugin channel, not `spt adapter update`.
PLUGIN skills/
  commune/SKILL.md  send/SKILL.md  signoff/SKILL.md
    (full bodies, model-loadable for reactive use)
ADAPTER briefs/ also describe them (redundant)

These 3 bodies churn on the plugin channel
Accepted as rare; no change needed

Question: Residual B — how do you want to capture all this? We've decided: keep-plugin (reaffirms ADR-0001 + #2422), the naming unification (option 1), and the update architecture (gh_release + message now, generic-dispatch + composite as doyle asks).

Choices:
Write ADR for the naming unification (claude-spt / spt rule, repo rename, succession) and ADR for the update architecture (gh_release + [update].message, the 2 doyle asks, plugin-sync-as-binary). Add a naming cross-ref to CONTEXT.md. Then a short *-PLAN.md for execution sequencing.
docs/adr/0005-naming-unification.md
  claude-spt (repo+adapter) + spt (plugin+skills)
docs/adr/0006-one-command-update.md
  gh_release + [update].message; run-hook + composite asks
CONTEXT.md: cross-ref the naming terms
NAMING-AND-UPDATE-PLAN.md: execution order

(write on your go)
Not done — keep interviewing on the open execution questions (sequencing the run-hook ask vs the rename vs generic-dispatch; what ships first; whether the composite is worth doing at all) before any capture.
Continue the interview:
 - execution order (rename / run-hook / dispatch / sync)
 - does generic-dispatch block the thin-plugin, or ship
   independently?
 - is the composite worth the doyle ask vs message-only?
 - README platform-chain specifics (cmd/pwsh/bash)
Then capture.


Your questions have been answered: "Residual A — the full-fat reactive skills (commune/send/signoff) still churn IN the plugin. Their reactive guidance is ALREADY delivered by the SessionStart brief (adapter strings — the spacetime-reorientation block lists send/commune/signoff usage). Thin the plugin SKILL.md to stubs and rely on the brief?"=(no option selected) notes: you pointed at `claude plugin update spt` as a residual manual step, but no... that's something our update script would take care of. the only residual is `/reload-plugins`. -the update message should still mention the `spt` (it's an alias for `spt endpoint run`) CLI command route as the more powerful way to create an endpoint. --- cross-platform: can we consolidate to one binary export per OS + subcommands for each function (history/psyche/translation/post-update)? does the adapter manifest support that? if not, it should probably be another doyle ask. -additional note: to cover for plugin-version-updates-but-not-adapter-version, our ask should mandate that the post-update step runs regardless of whether there was a new adapter version to install. and maybe mandate that a special return value of the post-update command can flag "updated-true"-->`spt adapter update` still prints the `[update] message` even though the adapter itself didn't update. --- residual A: does the brief fire on SessionStart for a harness-hosted config (no perch yet)? because if it doesn't, then new harness-hosted sessions would be missing that information. otherwise yes, it *would* make sense to thin the SKILL.md files again. we'd need to ensure `spacetime-reorientation` includes instruction for the new `--checkpoint` version of `/commune`., "Residual B — how do you want to capture all this? We've decided: keep-plugin (reaffirms ADR-0001 + #2422), the naming unification (option 1), and the update architecture (gh_release + message now, generic-dispatch + composite as doyle asks)."=(no option selected) notes: two adrs + context, then share with me a detailed grill prompt for me to take our feature requests to doyle.. You can now continue with these answers in mind.

Question: Residual A — the full-fat reactive skills (commune/send/signoff) still churn IN the plugin. Their reactive guidance is ALREADY delivered by the SessionStart brief (adapter strings — the spacetime-reorientation block lists send/commune/signoff usage). Thin the plugin SKILL.md to stubs and rely on the brief?

Choices:
Move the reactive-skill bodies fully to adapter strings (the SessionStart brief already carries this guidance); plugin SKILL.md becomes frontmatter-only stubs. Then ALL skill bodies ride `spt adapter update` — the plugin truly stops carrying churning prose. Cost: the brief grows slightly (already injects messaging guidance).
PLUGIN skills/ (all stubs, frontmatter only)
  commune/SKILL.md  send/SKILL.md  signoff/SKILL.md
ADAPTER strings (spt adapter update)
  briefs/ already inject send/commune/signoff usage
  -> reactive bodies live here, one source

ALL skill prose now rides the adapter channel
Plugin = hooks.json(generic) + stubs + plugin.json + bootstrap
Leave commune/send/signoff full-fat in the plugin (status quo). They're rare-churn after this wave; the SessionStart brief is belt-and-suspenders. Simpler now, but accepts that these specific bodies update on the plugin channel, not `spt adapter update`.
PLUGIN skills/
  commune/SKILL.md  send/SKILL.md  signoff/SKILL.md
    (full bodies, model-loadable for reactive use)
ADAPTER briefs/ also describe them (redundant)

These 3 bodies churn on the plugin channel
Accepted as rare; no change needed

Question: Residual B — how do you want to capture all this? We've decided: keep-plugin (reaffirms ADR-0001 + #2422), the naming unification (option 1), and the update architecture (gh_release + message now, generic-dispatch + composite as doyle asks).

Choices:
Write ADR for the naming unification (claude-spt / spt rule, repo rename, succession) and ADR for the update architecture (gh_release + [update].message, the 2 doyle asks, plugin-sync-as-binary). Add a naming cross-ref to CONTEXT.md. Then a short *-PLAN.md for execution sequencing.
docs/adr/0005-naming-unification.md
  claude-spt (repo+adapter) + spt (plugin+skills)
docs/adr/0006-one-command-update.md
  gh_release + [update].message; run-hook + composite asks
CONTEXT.md: cross-ref the naming terms
NAMING-AND-UPDATE-PLAN.md: execution order

(write on your go)
Not done — keep interviewing on the open execution questions (sequencing the run-hook ask vs the rename vs generic-dispatch; what ships first; whether the composite is worth doing at all) before any capture.
Continue the interview:
 - execution order (rename / run-hook / dispatch / sync)
 - does generic-dispatch block the thin-plugin, or ship
   independently?
 - is the composite worth the doyle ask vs message-only?
 - README platform-chain specifics (cmd/pwsh/bash)
Then capture.


Dense feedback — parsing it into the design. Key corrections absorbed:

- **Composite is IN, not optional** — the update script automates `claude plugin update`; the *only* manual residual is `/reload-plugins`. So `message` shrinks to just that (plus mentioning the `spt` CLI / `spt endpoint run` as the more powerful endpoint route).
- **Two strong new asks** for doyle: (a) the post-update step must run **unconditionally** (plugin can change when the adapter didn't), and (b) a return value can flag "changed" so `spt adapter update` **still prints `message`** even on an adapter no-op.
- **Binary consolidation** (one binary/OS + subcommands: `digest`/`psyche`/`translate`/`post-update`) — great, but I need to check the one seam that takes a *bare path*, not a command.

Let me verify two load-bearing facts before I capture: (1) the translation-binary seam's `path` field shape (the consolidation blocker), and (2) whether the perched brief actually carries commune/signoff so thinning is safe.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== schema: message-idle-translation-binary seam shape ==="
grep -niE 'message-idle-translation|translation_binary|MessageIdle|path' adapter/manifest.schema.json | head
echo "=== brief: messaging-perch.md (does it cover commune/signoff/checkpoint?) ==="
cat adapter/strings/briefs/messaging-perch.md
echo "=== brief: identity.md ==="
cat adapter/strings/briefs/identity.md


=== schema: message-idle-translation-binary seam shape ===
96:      "description": "`[strings]` — an adapter-authored KV tree of **opaque data** (spt-core\nnever executes a string; command templates live in their own sections\nbehind registration). Dot-path-readable via `spt adapter get-string`, and\nit rides the same leaf-replace profile overlay as the rest of the manifest\n(a shipped or local profile may override base strings). Node-local; no\ncross-node sync. (CONTEXT.md §adapter strings.)",
385:          "description": "Adapter pushes via `api history-log`; spt-core stores (Path-B).",
396:          "description": "Opaque extractor command: native harness log → the `{role,text,tool,ts}`\ncontract (one JSON record per output line). `{key}` substitution applies\n(e.g. `{session_id}`, and `{source}` for the resolved log path).",
517:        "path_regex": {
518:          "description": "`file_pull` avenue: path selector.",
=== brief: messaging-perch.md (does it cover commune/signoff/checkpoint?) ===
Reach another agent (the body is read from stdin):

- Send: `printf '%s' "<body>" | spt send <target>`. `SENT` = delivered live; `QUEUED` = target offline, spooled for its next listen — QUEUED is success, do not retry.
- Reply: `printf '%s' "<body>" | spt send <sender>` (sender = the `from` on the EVENT you received).
=== brief: identity.md ===
You are spt agent "{id}". This Claude Code session already owns a live perch — you are reachable right now.

- Messages arrive on your Monitor EVENT stream (`<EVENT type="msg" from="<sender>">body</EVENT>`) or, mid-tool-call, as `<sptc_messages>` the hook injects. Process them and reply.
- Do NOT run /sptc:ready or /sptc:live — your perch is already up. Re-arming returns COLLISION (that is proof it is alive, not a stale perch).


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== translation-binary schema def (search the property + neighbors) ==="
grep -niE 'idle|translat|binary' adapter/manifest.schema.json
echo "--- show the property block around it ---"
python -c "
import json
s=json.load(open('adapter/manifest.schema.json'))
props=s.get('properties',{})
for k,v in props.items():
    if 'transl' in k.lower() or 'idle' in k.lower():
        print('TOPLEVEL KEY:',k); print(json.dumps(v,indent=1)[:600])
# also scan defs
for dk,dv in s.get('\$defs',{}).items():
    blob=json.dumps(dv).lower()
    if 'translat' in blob or 'idle-deliver' in blob or 'keystroke' in blob:
        print('DEF:',dk); print(json.dumps(dv,indent=1)[:800])
"


=== translation-binary schema def (search the property + neighbors) ===
347:          "description": "`fetcher` strategy: adapter binary emitting normalized history.",
375:          "description": "spt-core asks the adapter (pull, adapter binary emits normalized).",
446:        "idle": {
614:          "description": "Instruction sent to the binary on link-break.",
--- show the property block around it ---


btw, we never grilled the whole "consolidate the 4 binary exports into 1 with subcommands" suggestion. please do.


Confirmed the two facts:
- **`messaging-perch` brief carries only messaging + liveness** — *not* commune/signoff/checkpoint. So thinning the reactive skills isn't free: their bodies would need authoring into a new perched-brief string, plus the `/sptc:live` UPS body to cover the harness-hosted go-live moment (the perched brief fires on bind|boundary, not the instant a seed session goes live). **Viable, but real work + a delivery-timing wrinkle** → I'll log it as a plan item, not block on it.
- **Translation-binary seam is a bare `path`** (no args), unlike `[digest]`/`[session.*]`/`[update]` which take command strings. So one-binary-with-subcommands works for digest/psyche/post-update but **the translation seam is the blocker** → a doyle ask/question.

Converging now per your ask: two ADRs + CONTEXT, then the doyle grill prompt. Writing the captures first.


1	# ADR Format
2
3	ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
4
5	Create the `docs/adr/` directory lazily — only when the first ADR is needed.
6
7	## Template
8
9	```md
10	# {Short title of the decision}
11
12	{1-3 sentences: what's the context, what did we decide, and why.}
13	```
14
15	That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections.
16
17	## Optional sections
18
19	Only include these when they add genuine value. Most ADRs won't need them.
20
21	- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
22	- **Considered Options** — only when the rejected alternatives are worth remembering
23	- **Consequences** — only when non-obvious downstream effects need to be called out
24
25	## Numbering
26
27	Scan `docs/adr/` for the highest existing number and increment by one.
28
29	## When to offer an ADR
30
31	All three of these must be true:
32
33	1. **Hard to reverse** — the cost of changing your mind later is meaningful
34	2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
35	3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
36
37	If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
38
39	### What qualifies
40
41	- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
42	- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
43	- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
44	- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
45	- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
46	- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
47	- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.
48

# ADR Format

ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.

Create the `docs/adr/` directory lazily — only when the first ADR is needed.

## Template

```md
# {Short title of the decision}

{1-3 sentences: what's the context, what did we decide, and why.}
```

That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why* — not in filling out sections.

## Optional sections

Only include these when they add genuine value. Most ADRs won't need them.

- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
- **Considered Options** — only when the rejected alternatives are worth remembering
- **Consequences** — only when non-obvious downstream effects need to be called out

## Numbering

Scan `docs/adr/` for the highest existing number and increment by one.

## When to offer an ADR

All three of these must be true:

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

If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."

### What qualifies

- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.


Prompt: btw, we never grilled the whole "consolidate the 4 binary exports into 1 with subcommands" suggestion. please do.


Good catch — let me grill consolidation properly before capturing; it shapes ADR-0006.

The current + proposed binaries:

| Binary | Seam | Seam shape | Lifecycle |
|--------|------|-----------|-----------|
| `claude-spt-digest` | `[digest].extractor` | command string | one-shot |
| `claude-spt-psyche` | `[session.psyche_init].command` | command string | **resident** |
| `cc-spt-idle-translate` | `[message-idle-translation-binary].path` | **bare path** | **resident** (stdin filter) |
| *(proposed)* post-update | `[update].command` (delegated) | command string | one-shot |

The case **for** consolidating to one `claude-spt <subcommand>`:
- **Naming** — folds straight into topic 2 (`claude-spt` binary matches the adapter); also kills the odd-one-out `cc-spt-idle-translate` (it's `cc-spt-*`, not `claude-spt-*`).
- **Packaging** — the fat `.spt` drops from 3 binaries × 2 triples = 6 down to 1 × 2 = 2; one cargo build, one test crate, shared deps deduped (likely *smaller* total).
- **Extensible** — if `spt api run-hook` lands, the hook handlers become subcommands too (`claude-spt hook session-start`) — *one* artifact for everything the adapter executes.

The case **against** / the costs:
- **Blast radius** — one binary, all seams: a build break or bad bump takes down every seam at once (mild — they already ship in one `.spt`).
- **The translate blocker** — three seams take a *command* (trivially `claude-spt digest …`), but `[message-idle-translation-binary]` takes a **bare path**. `path = "claude-spt"` would spawn it with no args → it can't tell it's in translate mode. So full consolidation needs doyle to let that seam take a command/subcommand (or a default-to-translate-on-no-args convention).


AskUserQuestion

Question: Consolidate the adapter binaries into one `claude-spt <subcommand>`? And if so, how to handle the translate seam being a bare-path (not a command)?

Choices:
Consolidate the three command-seams (digest/psyche/post-update) into one `claude-spt` binary NOW — no doyle dependency. Keep cc-spt-idle-translate separate until doyle lets the translation seam take a command, then fold it in (and rename). Most benefit immediately, incremental, nothing blocked.
NOW (no doyle dep):
  claude-spt digest ...      [digest].extractor
  claude-spt psyche ...      [session.psyche_init]
  claude-spt post-update     [update].command
  cc-spt-idle-translate      (stays, bare-path seam)
LATER (after doyle seam-takes-command):
  claude-spt translate  <- fold in + rename
  + claude-spt hook <event>  (if run-hook lands)
Artifacts/triple: 2 now -> 1 later
Do it all at once: one `claude-spt` binary for digest/psyche/translate/post-update (+ future hook handlers), shipped when doyle adds command support to the translation-binary seam. Cleanest single end-state, one cutover — but waits on the doyle ask before any consolidation lands.
WHEN doyle ships seam-takes-command:
  claude-spt digest|psyche|translate|post-update
  (+ claude-spt hook <event> if run-hook lands)
ONE binary/triple, one cutover, no half-state

Blocks ALL consolidation on doyle ask
(meanwhile: status quo 3 binaries)
Keep separate binaries. Each seam stays its own crate/artifact. No refactor, max failure isolation — but keeps 6 artifacts in the fat .spt, the cc-spt-* naming inconsistency, and duplicated deps. Rejects the suggestion; listed for completeness.
STATUS QUO:
  claude-spt-digest
  claude-spt-psyche
  cc-spt-idle-translate   (naming outlier)
  + new post-update binary (#4)
6-8 artifacts in the fat .spt
No doyle dep, max isolation, no refactor


Your questions have been answered: "Consolidate the adapter binaries into one `claude-spt <subcommand>`? And if so, how to handle the translate seam being a bare-path (not a command)?"="Partial now, fold translate later" selected preview:
NOW (no doyle dep):
  claude-spt digest ...      [digest].extractor
  claude-spt psyche ...      [session.psyche_init]
  claude-spt post-update     [update].command
  cc-spt-idle-translate      (stays, bare-path seam)
LATER (after doyle seam-takes-command):
  claude-spt translate  <- fold in + rename
  + claude-spt hook <event>  (if run-hook lands)
Artifacts/triple: 2 now -> 1 later. You can now continue with these answers in mind.

Question: Consolidate the adapter binaries into one `claude-spt <subcommand>`? And if so, how to handle the translate seam being a bare-path (not a command)?

Choices:
Consolidate the three command-seams (digest/psyche/post-update) into one `claude-spt` binary NOW — no doyle dependency. Keep cc-spt-idle-translate separate until doyle lets the translation seam take a command, then fold it in (and rename). Most benefit immediately, incremental, nothing blocked.
NOW (no doyle dep):
  claude-spt digest ...      [digest].extractor
  claude-spt psyche ...      [session.psyche_init]
  claude-spt post-update     [update].command
  cc-spt-idle-translate      (stays, bare-path seam)
LATER (after doyle seam-takes-command):
  claude-spt translate  <- fold in + rename
  + claude-spt hook <event>  (if run-hook lands)
Artifacts/triple: 2 now -> 1 later
Do it all at once: one `claude-spt` binary for digest/psyche/translate/post-update (+ future hook handlers), shipped when doyle adds command support to the translation-binary seam. Cleanest single end-state, one cutover — but waits on the doyle ask before any consolidation lands.
WHEN doyle ships seam-takes-command:
  claude-spt digest|psyche|translate|post-update
  (+ claude-spt hook <event> if run-hook lands)
ONE binary/triple, one cutover, no half-state

Blocks ALL consolidation on doyle ask
(meanwhile: status quo 3 binaries)
Keep separate binaries. Each seam stays its own crate/artifact. No refactor, max failure isolation — but keeps 6 artifacts in the fat .spt, the cc-spt-* naming inconsistency, and duplicated deps. Rejects the suggestion; listed for completeness.
STATUS QUO:
  claude-spt-digest
  claude-spt-psyche
  cc-spt-idle-translate   (naming outlier)
  + new post-update binary (#4)
6-8 artifacts in the fat .spt
No doyle dep, max isolation, no refactor


All decisions resolved. Capturing now — two ADRs, a CONTEXT cross-ref, then the doyle grill prompt.


Write

# Name unification: `claude-spt` (spt-core side) + `spt` (Claude Code side)

## Status

accepted (2026-06-24)

## Context

The project carried **three** names for one thing, plus a launcher shortcut:

| Identifier | Value | Where it shows |
|---|---|---|
| GitHub repo | `spt-claude-code` | `spt adapter add --release SaberMage/spt-claude-code`, `[update].repo`, install dir derivation |
| spt-core adapter | `claude-spt` | every `api --adapter claude-spt`, `[adapter].name`, `spt adapter update claude-spt` |
| cplugs plugin | `sptc` | `/sptc:*` skill prefix, `claude plugin … sptc@cplugs`, marketplace path |
| launcher shortcut | `cc` | `cc-<id>` (`spt endpoint run` brand) |

Three names for one adapter is friction — a user reads `--release SaberMage/spt-claude-code` to install but `spt adapter update claude-spt` to update, and `/sptc:*` to invoke. The hard constraint that prevents collapsing to **one** token: the adapter **cannot** be named `spt` (that is spt-core's own CLI/identity — `spt adapter update spt` reads as "spt updates spt"), yet the user-facing skill prefix **wants** `/spt:*` (the legacy-parity goal, already planned as the `sptc`→`spt` succession). The skill prefix and the adapter id genuinely want different tokens.

## Decision

Collapse three names to **two**, by domain:

- **spt-core-facing identity = `claude-spt`** — the **repo** (rename `spt-claude-code` → `claude-spt`), the **adapter** (unchanged), and the **consolidated binary** (see ADR-0006). After the rename, install and update both read `claude-spt`: `spt adapter add --release SaberMage/claude-spt` ↔ `spt adapter update claude-spt`.
- **Claude-Code-facing identity = `spt`** — the **plugin** and its **skills** (`/spt:*`), reached via the already-planned `sptc`→`spt` succession (a single `s/sptc/spt/` substitution), gated on retiring legacy owl's cplugs `spt` plugin (two plugins cannot share the name). Until owl retires, the plugin stays `sptc` / `/sptc:*`.
- **launcher shortcut stays `cc`** (`cc-<id>`) — decoupled from the plugin name, unchanged (per ADR-0001).

End state: `claude-spt` everywhere spt-core sees it; `spt` everywhere the Claude Code user types a skill; `cc` for the spawned-endpoint launcher.

## Considered and rejected

- **One token everywhere = `claude-spt`** (incl. `/claude-spt:*` skills). Maximally consistent and dependency-free, but makes the most-typed surface (the skill prefix) long. Rejected — the skill prefix should head to `/spt:*`.
- **One token everywhere = `sptc`** (`/sptc:*` unchanged). Shortest and dependency-free, but `sptc` is cryptic to a newcomer and abandons the cleaner `/spt:*` succession the project already committed to. Rejected.

## Consequences

- **The repo rename changes the adapter install dir** (`adapters/_github/SaberMage-spt-claude-code` → `…SaberMage-claude-spt`, derived from the repo path), so existing `--release` installs must be re-added. Acceptable: one end user today. GitHub redirects old links; `[update].repo`, the README install chains, CI, and the package scripts must be updated in the same move.
- Two names is the floor, not a failure to unify — it reflects a real constraint (adapter ≠ `spt`), recorded here so a future reader does not "simplify" the adapter to `spt` and collide with spt-core.
- The `spt` plugin succession keeps its existing gate (owl retirement); this ADR does not change that timing, only commits the target token split.


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

# Name unification: `claude-spt` (spt-core side) + `spt` (Claude Code side)

## Status

accepted (2026-06-24)

## Context

The project carried **three** names for one thing, plus a launcher shortcut:

| Identifier | Value | Where it shows |
|---|---|---|
| GitHub repo | `spt-claude-code` | `spt adapter add --release SaberMage/spt-claude-code`, `[update].repo`, install dir derivation |
| spt-core adapter | `claude-spt` | every `api --adapter claude-spt`, `[adapter].name`, `spt adapter update claude-spt` |
| cplugs plugin | `sptc` | `/sptc:*` skill prefix, `claude plugin … sptc@cplugs`, marketplace path |
| launcher shortcut | `cc` | `cc-<id>` (`spt endpoint run` brand) |

Three names for one adapter is friction — a user reads `--release SaberMage/spt-claude-code` to install but `spt adapter update claude-spt` to update, and `/sptc:*` to invoke. The hard constraint that prevents collapsing to **one** token: the adapter **cannot** be named `spt` (that is spt-core's own CLI/identity — `spt adapter update spt` reads as "spt updates spt"), yet the user-facing skill prefix **wants** `/spt:*` (the legacy-parity goal, already planned as the `sptc`→`spt` succession). The skill prefix and the adapter id genuinely want different tokens.

## Decision

Collapse three names to **two**, by domain:

- **spt-core-facing identity = `claude-spt`** — the **repo** (rename `spt-claude-code` → `claude-spt`), the **adapter** (unchanged), and the **consolidated binary** (see ADR-0006). After the rename, install and update both read `claude-spt`: `spt adapter add --release SaberMage/claude-spt` ↔ `spt adapter update claude-spt`.
- **Claude-Code-facing identity = `spt`** — the **plugin** and its **skills** (`/spt:*`), reached via the already-planned `sptc`→`spt` succession (a single `s/sptc/spt/` substitution), gated on retiring legacy owl's cplugs `spt` plugin (two plugins cannot share the name). Until owl retires, the plugin stays `sptc` / `/sptc:*`.
- **launcher shortcut stays `cc`** (`cc-<id>`) — decoupled from the plugin name, unchanged (per ADR-0001).

End state: `claude-spt` everywhere spt-core sees it; `spt` everywhere the Claude Code user types a skill; `cc` for the spawned-endpoint launcher.

## Considered and rejected

- **One token everywhere = `claude-spt`** (incl. `/claude-spt:*` skills). Maximally consistent and dependency-free, but makes the most-typed surface (the skill prefix) long. Rejected — the skill prefix should head to `/spt:*`.
- **One token everywhere = `sptc`** (`/sptc:*` unchanged). Shortest and dependency-free, but `sptc` is cryptic to a newcomer and abandons the cleaner `/spt:*` succession the project already committed to. Rejected.

## Consequences

- **The repo rename changes the adapter install dir** (`adapters/_github/SaberMage-spt-claude-code` → `…SaberMage-claude-spt`, derived from the repo path), so existing `--release` installs must be re-added. Acceptable: one end user today. GitHub redirects old links; `[update].repo`, the README install chains, CI, and the package scripts must be updated in the same move.
- Two names is the floor, not a failure to unify — it reflects a real constraint (adapter ≠ `spt`), recorded here so a future reader does not "simplify" the adapter to `spt` and collide with spt-core.
- The `spt` plugin succession keeps its existing gate (owl retirement); this ADR does not change that timing, only commits the target token split.


# One-command-ish update + a single consolidated adapter binary

## Status

accepted (2026-06-24) — extends ADR-0001 (distribution splits by volatility). Some pieces depend on spt-core capabilities not yet shipped (the "doyle asks" below); those parts are decided in shape but gated on delivery.

## Context

ADR-0001 split distribution into a low-churn **cplugs skeleton** (hooks, skill skeletons, `plugin.json`, bootstrap — updated by `claude plugin update`) and an spt-conducted **adapter** (manifest + binaries + strings — updated by `spt adapter update`). The operator wants the whole thing kept current with effectively **one lever**, and wants install and update to feel **harmonious** (no hunting for the update command after install) and to work from the **CLI** (not only inside an endpoint session).

Two facts bound the design:

1. **Hook *logic* must live in the CC plugin dir** (CC loads hooks from there), outside `spt adapter update`'s reach. So "one command updates everything" is unreachable purely inside this repo — it needs spt-core capabilities → findings, not workarounds (public-surface-only).
2. **Raw skills/commands can't reliably replace the plugin.** Reopened and re-rejected: user-scope skills are flat (no namespace), and CC slash-command subdirectory namespacing is broken ([anthropics/claude-code#2422] — subdirs show in the description but don't namespace the invocation). The plugin is the only reliable `/spt:*` + the only clean hooks bundle. The eloquent thing to borrow from suite installers (e.g. gsd-core) is their **installer/onboarding UX**, not going raw.

## Decision

**Keep the plugin** (reaffirms ADR-0001). Make the update path one-lever-shaped and consolidate the adapter's binaries.

### Update mechanics

- **`avenue = "gh_release"`** pulls the adapter `.spt` (manifest + binaries + strings — all the high-churn). Ships against the published surface today.
- **`[update].message`** carries the residual manual step. This is a real, docs-confirmed field: a markdown-rendered notice `spt adapter update` prints to stdout **only when a new version is actually applied** (never on a no-op), no `{key}` substitution. It tells the user to run **`/reload-plugins`** — which is a **Claude Code TUI action and cannot be automated** — and mentions the `spt` CLI (alias for `spt endpoint run`) as the more powerful way to create an endpoint, alongside `/spt:live`.
- **"Install is the first update"** is docs-confirmed, so `spt adapter add` conducts the same `[update]` flow — one acquisition bootstraps the adapter.
- **Symmetric levers:** install = `spt adapter add --release SaberMage/claude-spt`, update = `spt adapter update claude-spt` (the verbs mirror). A README **copy-paste agent prompt** is an optional skin over the install command (the casual-user onboarding), with **platform-specific chains** (cmd / PowerShell / bash) that check-for / install spt-core (claude-spt may be a user's first exposure to spt-core) then `adapter add`.

### Binary consolidation

Collapse the adapter's executables into **one `claude-spt <subcommand>`** binary (name per ADR-0005), **partially now, fully later**:

- **Now** (no spt-core dependency — these seams take a command string): `claude-spt digest` (`[digest].extractor`), `claude-spt psyche` (`[session.psyche_init]`), `claude-spt post-update` (the delegated update step). Drops the fat `.spt` from 3 binaries/triple toward 1, dedupes deps, and retires the odd-one-out `cc-spt-idle-translate` name.
- **Later** (gated on doyle ask #3): fold in `claude-spt translate` once `[message-idle-translation-binary]` accepts a command/subcommand rather than a bare `path`; and `claude-spt hook <event>` if generic hook dispatch (ask #1) lands.

The **post-update step is cross-platform** because it is a compiled subcommand of this binary, **not** a `.sh` (Windows has no bash; a stray script would also stress spt-core's archive handling). It runs `claude plugin add|update <plugin>` + prints the notice; it cannot run `/reload-plugins` (TUI).

### The spt-core asks (findings to doyle — decided in shape, gated on delivery)

1. **Generic hook dispatch** — `spt api run-hook <adapter> <event>`: spt-core executes the adapter's handler for a hook event (from the manifest), so the plugin's `hooks.json` can pre-wire all events to one generic stub and hook *logic* rides `spt adapter update`. Eliminates the plugin-channel churn that adding this wave's PostToolUse hook forced.
2. **Composite `[update]`** — `gh_release` (pull the `.spt`) **plus** a delegated post-step in the same `spt adapter update`, so the plugin update is automated (the only manual residual becomes `/reload-plugins`). Two sub-requirements: the post-step must run **unconditionally** (the plugin can change when the adapter version did not), and its return value must be able to flag **"changed"** so `spt adapter update` still prints `[update].message` even on an adapter no-op.
3. **Translation seam takes a command** — `[message-idle-translation-binary]` accept a command/subcommand (or a default-on-no-args convention) instead of a bare `path`, so `translate` can fold into the consolidated binary.

## Consequences

- The thin-skeleton goal advances even before the asks land: skill bodies already ride strings; the **reactive skills** (`commune`/`send`/`signoff`, full-fat in the plugin because they are invoked without a typed slash-command) can be thinned to stubs by moving their bodies into the perched SessionStart brief (adapter strings) plus the `/spt:live` UPS body — with a known **delivery-timing wrinkle** (the perched brief fires on bind|boundary, not the instant a seed session goes live), tracked as a plan item, not blocking.
- Until asks #1/#3 land, hook logic and the translate binary stay on their current channels; the design degrades gracefully (the `message` field bridges the manual steps).
- One artifact per OS simplifies packaging but couples all seams' build/release — acceptable, they already ship in one `.spt`.


File created successfully at: C:\Users\decid\Documents\projects\spt-claude-code\docs\adr\0006-one-command-update-and-consolidated-binary.md (file state is current in your context — no need to Read it back)

# One-command-ish update + a single consolidated adapter binary

## Status

accepted (2026-06-24) — extends ADR-0001 (distribution splits by volatility). Some pieces depend on spt-core capabilities not yet shipped (the "doyle asks" below); those parts are decided in shape but gated on delivery.

## Context

ADR-0001 split distribution into a low-churn **cplugs skeleton** (hooks, skill skeletons, `plugin.json`, bootstrap — updated by `claude plugin update`) and an spt-conducted **adapter** (manifest + binaries + strings — updated by `spt adapter update`). The operator wants the whole thing kept current with effectively **one lever**, and wants install and update to feel **harmonious** (no hunting for the update command after install) and to work from the **CLI** (not only inside an endpoint session).

Two facts bound the design:

1. **Hook *logic* must live in the CC plugin dir** (CC loads hooks from there), outside `spt adapter update`'s reach. So "one command updates everything" is unreachable purely inside this repo — it needs spt-core capabilities → findings, not workarounds (public-surface-only).
2. **Raw skills/commands can't reliably replace the plugin.** Reopened and re-rejected: user-scope skills are flat (no namespace), and CC slash-command subdirectory namespacing is broken ([anthropics/claude-code#2422] — subdirs show in the description but don't namespace the invocation). The plugin is the only reliable `/spt:*` + the only clean hooks bundle. The eloquent thing to borrow from suite installers (e.g. gsd-core) is their **installer/onboarding UX**, not going raw.

## Decision

**Keep the plugin** (reaffirms ADR-0001). Make the update path one-lever-shaped and consolidate the adapter's binaries.

### Update mechanics

- **`avenue = "gh_release"`** pulls the adapter `.spt` (manifest + binaries + strings — all the high-churn). Ships against the published surface today.
- **`[update].message`** carries the residual manual step. This is a real, docs-confirmed field: a markdown-rendered notice `spt adapter update` prints to stdout **only when a new version is actually applied** (never on a no-op), no `{key}` substitution. It tells the user to run **`/reload-plugins`** — which is a **Claude Code TUI action and cannot be automated** — and mentions the `spt` CLI (alias for `spt endpoint run`) as the more powerful way to create an endpoint, alongside `/spt:live`.
- **"Install is the first update"** is docs-confirmed, so `spt adapter add` conducts the same `[update]` flow — one acquisition bootstraps the adapter.
- **Symmetric levers:** install = `spt adapter add --release SaberMage/claude-spt`, update = `spt adapter update claude-spt` (the verbs mirror). A README **copy-paste agent prompt** is an optional skin over the install command (the casual-user onboarding), with **platform-specific chains** (cmd / PowerShell / bash) that check-for / install spt-core (claude-spt may be a user's first exposure to spt-core) then `adapter add`.

### Binary consolidation

Collapse the adapter's executables into **one `claude-spt <subcommand>`** binary (name per ADR-0005), **partially now, fully later**:

- **Now** (no spt-core dependency — these seams take a command string): `claude-spt digest` (`[digest].extractor`), `claude-spt psyche` (`[session.psyche_init]`), `claude-spt post-update` (the delegated update step). Drops the fat `.spt` from 3 binaries/triple toward 1, dedupes deps, and retires the odd-one-out `cc-spt-idle-translate` name.
- **Later** (gated on doyle ask #3): fold in `claude-spt translate` once `[message-idle-translation-binary]` accepts a command/subcommand rather than a bare `path`; and `claude-spt hook <event>` if generic hook dispatch (ask #1) lands.

The **post-update step is cross-platform** because it is a compiled subcommand of this binary, **not** a `.sh` (Windows has no bash; a stray script would also stress spt-core's archive handling). It runs `claude plugin add|update <plugin>` + prints the notice; it cannot run `/reload-plugins` (TUI).

### The spt-core asks (findings to doyle — decided in shape, gated on delivery)

1. **Generic hook dispatch** — `spt api run-hook <adapter> <event>`: spt-core executes the adapter's handler for a hook event (from the manifest), so the plugin's `hooks.json` can pre-wire all events to one generic stub and hook *logic* rides `spt adapter update`. Eliminates the plugin-channel churn that adding this wave's PostToolUse hook forced.
2. **Composite `[update]`** — `gh_release` (pull the `.spt`) **plus** a delegated post-step in the same `spt adapter update`, so the plugin update is automated (the only manual residual becomes `/reload-plugins`). Two sub-requirements: the post-step must run **unconditionally** (the plugin can change when the adapter version did not), and its return value must be able to flag **"changed"** so `spt adapter update` still prints `[update].message` even on an adapter no-op.
3. **Translation seam takes a command** — `[message-idle-translation-binary]` accept a command/subcommand (or a default-on-no-args convention) instead of a bare `path`, so `translate` can fold into the consolidated binary.

## Consequences

- The thin-skeleton goal advances even before the asks land: skill bodies already ride strings; the **reactive skills** (`commune`/`send`/`signoff`, full-fat in the plugin because they are invoked without a typed slash-command) can be thinned to stubs by moving their bodies into the perched SessionStart brief (adapter strings) plus the `/spt:live` UPS body — with a known **delivery-timing wrinkle** (the perched brief fires on bind|boundary, not the instant a seed session goes live), tracked as a plan item, not blocking.
- Until asks #1/#3 land, hook logic and the translate binary stay on their current channels; the design degrades gracefully (the `message` field bridges the manual steps).
- One artifact per OS simplifies packaging but couples all seams' build/release — acceptable, they already ship in one `.spt`.


Now the CONTEXT.md cross-ref. Let me read the naming terms to edit precisely.


1	# spt-claude-code — glossary
2
3	> Authoritative for meaning (grill-with-docs convention). Glossary only — no
4	> implementation detail. Decisions/rationale live in `SCOPE.md` and `docs/adr/`.
5
6	**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
7	adapter in spt-core terms. Simultaneously spt-core's v1 acceptance proof (legacy
8	parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
9	installer. Built by maintainer perri from the public spt-releases surface only.
10
11	**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
12	invocation + the manifest). The CC adapter's identity inside spt-core.
13
14	**legacy spt / `claude_skill_owl`** — the sister project; today's shipped CC integration
15	(owl.exe + `spt` plugin, v1.11.25). The parity *target* (user-facing features), not a

# spt-claude-code — glossary

> Authoritative for meaning (grill-with-docs convention). Glossary only — no
> implementation detail. Decisions/rationale live in `SCOPE.md` and `docs/adr/`.

**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
adapter in spt-core terms. Simultaneously spt-core's v1 acceptance proof (legacy
parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
installer. Built by maintainer perri from the public spt-releases surface only.

**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
invocation + the manifest). The CC adapter's identity inside spt-core.

**legacy spt / `claude_skill_owl`** — the sister project; today's shipped CC integration
(owl.exe + `spt` plugin, v1.11.25). The parity *target* (user-facing features), not a


Edit

**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
adapter in spt-core terms. Simultaneously spt-core's v1 acceptance proof (legacy
parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
installer. Built by maintainer perri from the public spt-releases surface only.

**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
invocation + the manifest). The CC adapter's identity inside spt-core.

**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
adapter in spt-core terms. Simultaneously spt-core's v1 acceptance proof (legacy
parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
installer. Built by maintainer perri from the public spt-releases surface only.
(Repo being renamed `spt-claude-code` → `claude-spt` per the naming-domain rule; see
[[naming-domains]].)

**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
invocation + the manifest). The CC adapter's identity inside spt-core. The
**spt-core-facing** token (repo + adapter + consolidated binary). See [[naming-domains]].

**naming-domains** — the two-token naming rule (ADR-0005): **`claude-spt`** is the
spt-core-facing identity (repo, adapter, binary — `spt adapter … claude-spt`), and
**`spt`** is the Claude-Code-facing identity (plugin + skills, `/spt:*`, via the
`sptc`→`spt` succession). One token can't cover both because the adapter cannot be named
`spt` (spt-core's own identity) while the skill prefix wants `/spt:*`. The `cc` launcher
shortcut (`cc-<id>`) is decoupled from both.


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

**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
adapter in spt-core terms. Simultaneously spt-core's v1 acceptance proof (legacy
parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
installer. Built by maintainer perri from the public spt-releases surface only.
(Repo being renamed `spt-claude-code` → `claude-spt` per the naming-domain rule; see
[[naming-domains]].)

**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
invocation + the manifest). The CC adapter's identity inside spt-core. The
**spt-core-facing** token (repo + adapter + consolidated binary). See [[naming-domains]].

**naming-domains** — the two-token naming rule (ADR-0005): **`claude-spt`** is the
spt-core-facing identity (repo, adapter, binary — `spt adapter … claude-spt`), and
**`spt`** is the Claude-Code-facing identity (plugin + skills, `/spt:*`, via the
`sptc`→`spt` succession). One token can't cover both because the adapter cannot be named
`spt` (spt-core's own identity) while the skill prefix wants `/spt:*`. The `cc` launcher
shortcut (`cc-<id>`) is decoupled from both.

# spt-claude-code — glossary

> Authoritative for meaning (grill-with-docs convention). Glossary only — no
> implementation detail. Decisions/rationale live in `SCOPE.md` and `docs/adr/`.

**spt-claude-code** — the rebuilt Claude Code harness adapter. The `claude-spt`
adapter in spt-core terms. Simultaneously spt-core's v1 acceptance proof (legacy
parity), its first casual-end-user entrypoint (a CC plugin), and an invisible spt-core
installer. Built by maintainer perri from the public spt-releases surface only.

**claude-spt** — the adapter_name spt-claude-code registers under (rides every `api`
invocation + the manifest). The CC adapter's identity inside spt-core.

**legacy spt / `claude_skill_owl`** — the sister project; today's shipped CC integration
(owl.exe + `spt` plugin, v1.11.25). The parity *target* (user-facing features), not a
1:1 port — most of its machinery now lives in spt.exe.

**skeleton plugin** — the thin marketplace artifact on `SaberMage/cplugs`: namespaced
`/spt:*` skill stubs + hooks + a SessionStart bootstrap that installs spt-core. Low-churn;
the volatile bulk (binary + manifest) is spt-core-conducted, not in the plugin.

**fetch-stub skill** — a `/spt:X` SKILL.md that is a 2-line stub fetching its real
instructions at runtime from the adapter `[strings]` (`spt adapter get-string`/`skill-help`).
Keeps skill files static while instructions update via spt-core. Distinct from a `[hints]`
entry (proactive, keyword-triggered, UPS-hook-delivered).

**identity brief** — the agent-facing text the adapter injects at SessionStart into a session
that already owns a perch (the `bind` + `boundary` topologies). Tells the agent who it is, that
its perch is already live (so it must not re-arm), and how to message (send + reply + the endpoint
roster). Adapter-string-backed (`[strings.briefs]`), composed from an `{id}`-templated identity
piece plus shared messaging pieces. Liveness-agnostic: it does not distinguish live-vs-ready (that
flavor is deferred until spt-core publishes a machine-readable liveness query). Distinct from the
**ring brief** (the no-perch sibling).

**ring brief** — the SessionStart counterpart for a session with **no** perch (the `seed`
topology): a node-local agent that hasn't readied still learns how to reach other agents via
`spt ring` (the no-id messaging path). Peer-gated — emitted only when the node actually
participates in a subnet (has reachable peers), so a solo casual end user is never told how to
ring agents that don't exist.

**ccs profile** — a profile under spt-claude-code (`claude-spt:glm`, `claude-spt:kimi`)
that leaf-replaces the launch command + history/digest log dir to use the `ccs` backend.
NOT its own adapter — ccs is structurally Claude Code.

**`cc` launcher (capsule-style)** — a generated `cc`/`cc <id>` script at project root that
spawns-or-attaches a CC endpoint via spt-core's spt-hosted topology (broker PTY + inject +
attach). The spt-core realization of legacy's unbuilt "Capsule" milestone. _Avoid_: equating
it with psmux/sendkeys — spt-core's broker is the terminal host.

**Psyche** — a LiveAgent's detached *companion* process. When a session goes live
(`/sptc:live`), spt-core's daemon hosts a Psyche alongside it: the Psyche owns its own
perch (`<parent>-psyche`), is woken by daemon *pulses*, and on each pulse authors a
*commune*. It never replies or notifies (that is the echo-commune, a different actor) and
exits at session end. A *ready* agent has no Psyche — live-vs-ready is the command, not a
profile. (Realized here by the `claude-spt-psyche` runner.)

**commune** — the context-delta a Psyche writes on each pulse: a brief that lets the
parent agent resume coherently after a context wipe (`/clear` / compact). A file-drop the
daemon ingests, not an `api` verb.

**checkpoint** — a live agent's *self-initiated* context reset: the agent flags a commune to
request that its own working context be wiped and rebuilt from that latest commune, so it
continues coherently without the operator manually running `/clear`. The agent-driven sibling
of the operator's manual clear; only meaningful for a live agent (which has a Psyche + a
commune to rebuild from). See [[commune]].

**checkpoint trigger** — the marker a live agent embeds in a commune to request a
[[checkpoint]] (the request signal; distinct from the wire payload that carries it to the
delivery layer, which is an implementation concern).

**psyche sandbox** — the constrained surface a Psyche's `claude` turns run under:
Read/Edit/Write tools only, slash-commands disabled, permissions auto-approved, cheap
pinned model. Deliberately narrower than the parent agent (which is unconstrained). Mirrors
legacy owl's psyche box; see `docs/adr/0003-*`.

**casual end user** — a user who *uses* an spt-powered system mostly-invisibly, vs the
adapter/shell *developer* who is spt-core's nominal target. spt-claude-code serves casual
users: install the plugin, get spt-core for free.

**experimplate** — the standalone reusable project-workflow template (its own sibling repo)
extracted from spt-core's working style. spt-claude-code is its first consumer. Carries
traceable-reqs gating, JIT plans, grill-with-docs scaffolding, release/changelog, and
same-repo published-docs. Defined in its own folder, not here.

+(Repo being renamed `spt-claude-code` → `claude-spt` per the naming-domain rule; see

-invocation + the manifest). The CC adapter's identity inside spt-core.

+invocation + the manifest). The CC adapter's identity inside spt-core. The

+**spt-core-facing** token (repo + adapter + consolidated binary). See [[naming-domains]].

+**naming-domains** — the two-token naming rule (ADR-0005): **`claude-spt`** is the

+spt-core-facing identity (repo, adapter, binary — `spt adapter … claude-spt`), and

+**`spt`** is the Claude-Code-facing identity (plugin + skills, `/spt:*`, via the

+`sptc`→`spt` succession). One token can't cover both because the adapter cannot be named

+`spt` (spt-core's own identity) while the skill prefix wants `/spt:*`. The `cc` launcher

+shortcut (`cc-<id>`) is decoupled from both.


Now the deliverable — the doyle grill prompt enumerating the three asks, with the contract shape and the open questions for him to rule on.


Write

# spt-core asks — one-command update, binary consolidation, name unification

> A grill prompt to take to doyle. Three capability asks + one heads-up, each framed as a
> proposal with the contract shape we want and the open questions for doyle to rule on. Context:
> ADR-0005 (name unification) and ADR-0006 (one-command update + consolidated binary) in this
> repo. Goal: a casual user keeps **all** of the adapter current with `spt adapter update claude-spt`
> (the only manual residual being `/reload-plugins`, which is an unavoidable Claude Code TUI action).
>
> Public-surface-only stance: these are capabilities we believe are missing from the published
> surface; we're asking, not reaching into spt-core. Each should land as an F-0xx finding once shaped.

## Framing for doyle

The adapter ships in two homes (ADR-0001): the spt-conducted **adapter** (`spt adapter update` — manifest
+ binaries + strings) and the low-churn **cplugs plugin skeleton** (`claude plugin update` — hooks.json,
hook wrapper logic, skill skeletons, `plugin.json`, bootstrap). The operator wants one lever. The wall:
**hook *logic* must physically live in the CC plugin dir** (CC loads hooks there), outside `spt adapter
update`'s reach. We re-checked raw-skills/commands as an escape and re-rejected them (CC subdir
command-namespacing is broken — anthropics/claude-code#2422; user-scope skills are flat). So the plugin
stays, and closing the gap needs the asks below.

---

## Ask 1 — generic hook dispatch: `spt api run-hook <adapter> <event>`

**Problem.** Every hook the adapter adds (this cycle's `PostToolUse` checkpoint detector) forces a cplugs
republish + `claude plugin update` + `/reload-plugins`, because the wrapper *logic* lives in the plugin.
The manifest `[hooks.<event>]` table is declaration-only today — the plugin's `hooks.json` + wrapper `.sh`
are what actually run.

**Proposal.** spt-core executes the adapter's handler for a hook event. The plugin's `hooks.json` pre-wires
**all** CC hook events to one generic stub:

```
# hooks.json (static-forever — every event, one line each)
<event> -> spt api run-hook claude-spt <event>      # stdin = the CC hook payload, verbatim
```

spt-core resolves the adapter's handler for that event and runs it, passing the hook stdin through and
surfacing any `additionalContext` back. The handler is adapter-conducted — ideally a subcommand of our
consolidated binary (`claude-spt hook <event>`, see Ask 3 / ADR-0006), so hook *logic* then rides
`spt adapter update` and `hooks.json` never changes again.

**Open questions for doyle:**
- Where does the handler live — the manifest `[hooks.<event>].fires` command (made executable, not just
  declarative), or a declared binary/subcommand spt-core invokes? Our preference: a binary subcommand.
- What's passed in (the raw CC hook JSON on stdin?) and how does `additionalContext` / the `can_inject`
  contract flow back out through `api run-hook`?
- Auth/session threading — same shape as the other id-scoped `api` calls (`--session-id`)?
- Does spt-core stay harness-agnostic here (it runs an opaque adapter-declared handler; it needn't know
  the event semantics)?

---

## Ask 2 — composite `[update]`: `gh_release` pull **+** a delegated post-step

**Problem.** `[update]` takes one `avenue` (`gh_release` XOR `delegated`). We need `gh_release` (to pull the
`.spt` = manifest + binaries + strings) **and** a delegated step (to run our cross-platform plugin-sync:
`claude plugin add|update <plugin>`) in the **same** `spt adapter update`. Today they're mutually exclusive.

**Proposal.** Let `gh_release` carry a post-update delegated command spt-core runs after the pull/re-register,
in the same invocation. Two sub-requirements:

- **2a — runs unconditionally.** The post-step must run **even when the adapter `.spt` version did not
  change** — the cplugs plugin can have a new version when the adapter didn't, and the user still wants one
  command to reconcile it.
- **2b — a "changed" return signals the message.** The post-step's return value can flag *applied/changed*
  so `spt adapter update` still prints `[update].message` (the `/reload-plugins` notice) even on an adapter
  no-op. (`message` today prints only when a new adapter version is applied — we need the post-step able to
  trigger it too.)

**Already confirmed (no ask):** `[update].message` exists (markdown, prints only on a real apply, no `{key}`
subst) and *"install is the first update"* — so this same flow makes `spt adapter add` bootstrap the plugin.

**Open questions for doyle:**
- Field shape — a `post_update` command on `gh_release`? a `[update.post]` sub-table? an ordered avenue list?
- The "changed" return convention — exit code? a sentinel on stdout?
- No-op suppression — if *neither* the adapter nor the post-step changed, `message` still suppresses?
- Trust — does the post-step inherit the `self_verifies`/attest model the `delegated` avenue already uses?

---

## Ask 3 — `[message-idle-translation-binary]` accepts a command/subcommand (not a bare `path`)

**Problem.** `[digest]`, `[session.*]`, and `[update]` take **command strings** (so they consolidate trivially
into `claude-spt <subcommand>`). `[message-idle-translation-binary]` takes a bare **`path`** — `path =
"claude-spt"` would spawn the binary with no args, so it can't tell it's in translate mode. This is the one
seam blocking us from folding the translation filter into the single consolidated binary (ADR-0006).

**Proposal.** Let the seam take a command (with args / `{key}` substitution) like the other seams — e.g.
`command = "claude-spt translate"` — keeping the existing stdin JSON-lines protocol unchanged. (A documented
"invoked with no args ⇒ translate mode" convention would also unblock us, but an explicit command is cleaner
and consistent with the other seams.)

**Open questions for doyle:**
- Preferred shape — promote `path` to an optional `command`, or add a sibling `command` field?
- Backward-compat for existing bare-`path` adapters (keep `path` working)?
- Does the spawn/stdin-protocol contract change at all, or is it purely how the executable is located?

---

## Heads-up (not an ask) — name unification

We're collapsing three names to two (ADR-0005): **`claude-spt`** = the spt-core-facing identity (we're
renaming the GitHub repo `spt-claude-code` → `claude-spt`, so `--release SaberMage/claude-spt` matches
`spt adapter update claude-spt`, and the consolidated binary is `claude-spt`); **`spt`** = the Claude-Code
plugin + skills (`/spt:*`, via the planned `sptc`→`spt` succession). The repo rename changes the derived
install dir (`adapters/_github/SaberMage-claude-spt`), so the `[update].repo` and any path assumptions on
your side should expect the new repo slug. One end user today, so the re-add cost is a non-issue.

---

## Dependency map (what unblocks what)

- **Ask 1** → hook logic + new handlers ride `spt adapter update`; `hooks.json` goes static-forever.
- **Ask 2** → the plugin update is automated; the only manual residual is `/reload-plugins`.
- **Ask 3** → `translate` folds into the one `claude-spt` binary (1 artifact/triple).
- Ships **without** any ask: `gh_release` + `[update].message` (the message instructs the manual steps),
  partial binary consolidation (digest/psyche/post-update), and the name unification.


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

# spt-core asks — one-command update, binary consolidation, name unification

> A grill prompt to take to doyle. Three capability asks + one heads-up, each framed as a
> proposal with the contract shape we want and the open questions for doyle to rule on. Context:
> ADR-0005 (name unification) and ADR-0006 (one-command update + consolidated binary) in this
> repo. Goal: a casual user keeps **all** of the adapter current with `spt adapter update claude-spt`
> (the only manual residual being `/reload-plugins`, which is an unavoidable Claude Code TUI action).
>
> Public-surface-only stance: these are capabilities we believe are missing from the published
> surface; we're asking, not reaching into spt-core. Each should land as an F-0xx finding once shaped.

## Framing for doyle

The adapter ships in two homes (ADR-0001): the spt-conducted **adapter** (`spt adapter update` — manifest
+ binaries + strings) and the low-churn **cplugs plugin skeleton** (`claude plugin update` — hooks.json,
hook wrapper logic, skill skeletons, `plugin.json`, bootstrap). The operator wants one lever. The wall:
**hook *logic* must physically live in the CC plugin dir** (CC loads hooks there), outside `spt adapter
update`'s reach. We re-checked raw-skills/commands as an escape and re-rejected them (CC subdir
command-namespacing is broken — anthropics/claude-code#2422; user-scope skills are flat). So the plugin
stays, and closing the gap needs the asks below.

---

## Ask 1 — generic hook dispatch: `spt api run-hook <adapter> <event>`

**Problem.** Every hook the adapter adds (this cycle's `PostToolUse` checkpoint detector) forces a cplugs
republish + `claude plugin update` + `/reload-plugins`, because the wrapper *logic* lives in the plugin.
The manifest `[hooks.<event>]` table is declaration-only today — the plugin's `hooks.json` + wrapper `.sh`
are what actually run.

**Proposal.** spt-core executes the adapter's handler for a hook event. The plugin's `hooks.json` pre-wires
**all** CC hook events to one generic stub:

```
# hooks.json (static-forever — every event, one line each)
<event> -> spt api run-hook claude-spt <event>      # stdin = the CC hook payload, verbatim
```

spt-core resolves the adapter's handler for that event and runs it, passing the hook stdin through and
surfacing any `additionalContext` back. The handler is adapter-conducted — ideally a subcommand of our
consolidated binary (`claude-spt hook <event>`, see Ask 3 / ADR-0006), so hook *logic* then rides
`spt adapter update` and `hooks.json` never changes again.

**Open questions for doyle:**
- Where does the handler live — the manifest `[hooks.<event>].fires` command (made executable, not just
  declarative), or a declared binary/subcommand spt-core invokes? Our preference: a binary subcommand.
- What's passed in (the raw CC hook JSON on stdin?) and how does `additionalContext` / the `can_inject`
  contract flow back out through `api run-hook`?
- Auth/session threading — same shape as the other id-scoped `api` calls (`--session-id`)?
- Does spt-core stay harness-agnostic here (it runs an opaque adapter-declared handler; it needn't know
  the event semantics)?

---

## Ask 2 — composite `[update]`: `gh_release` pull **+** a delegated post-step

**Problem.** `[update]` takes one `avenue` (`gh_release` XOR `delegated`). We need `gh_release` (to pull the
`.spt` = manifest + binaries + strings) **and** a delegated step (to run our cross-platform plugin-sync:
`claude plugin add|update <plugin>`) in the **same** `spt adapter update`. Today they're mutually exclusive.

**Proposal.** Let `gh_release` carry a post-update delegated command spt-core runs after the pull/re-register,
in the same invocation. Two sub-requirements:

- **2a — runs unconditionally.** The post-step must run **even when the adapter `.spt` version did not
  change** — the cplugs plugin can have a new version when the adapter didn't, and the user still wants one
  command to reconcile it.
- **2b — a "changed" return signals the message.** The post-step's return value can flag *applied/changed*
  so `spt adapter update` still prints `[update].message` (the `/reload-plugins` notice) even on an adapter
  no-op. (`message` today prints only when a new adapter version is applied — we need the post-step able to
  trigger it too.)

**Already confirmed (no ask):** `[update].message` exists (markdown, prints only on a real apply, no `{key}`
subst) and *"install is the first update"* — so this same flow makes `spt adapter add` bootstrap the plugin.

**Open questions for doyle:**
- Field shape — a `post_update` command on `gh_release`? a `[update.post]` sub-table? an ordered avenue list?
- The "changed" return convention — exit code? a sentinel on stdout?
- No-op suppression — if *neither* the adapter nor the post-step changed, `message` still suppresses?
- Trust — does the post-step inherit the `self_verifies`/attest model the `delegated` avenue already uses?

---

## Ask 3 — `[message-idle-translation-binary]` accepts a command/subcommand (not a bare `path`)

**Problem.** `[digest]`, `[session.*]`, and `[update]` take **command strings** (so they consolidate trivially
into `claude-spt <subcommand>`). `[message-idle-translation-binary]` takes a bare **`path`** — `path =
"claude-spt"` would spawn the binary with no args, so it can't tell it's in translate mode. This is the one
seam blocking us from folding the translation filter into the single consolidated binary (ADR-0006).

**Proposal.** Let the seam take a command (with args / `{key}` substitution) like the other seams — e.g.
`command = "claude-spt translate"` — keeping the existing stdin JSON-lines protocol unchanged. (A documented
"invoked with no args ⇒ translate mode" convention would also unblock us, but an explicit command is cleaner
and consistent with the other seams.)

**Open questions for doyle:**
- Preferred shape — promote `path` to an optional `command`, or add a sibling `command` field?
- Backward-compat for existing bare-`path` adapters (keep `path` working)?
- Does the spawn/stdin-protocol contract change at all, or is it purely how the executable is located?

---

## Heads-up (not an ask) — name unification

We're collapsing three names to two (ADR-0005): **`claude-spt`** = the spt-core-facing identity (we're
renaming the GitHub repo `spt-claude-code` → `claude-spt`, so `--release SaberMage/claude-spt` matches
`spt adapter update claude-spt`, and the consolidated binary is `claude-spt`); **`spt`** = the Claude-Code
plugin + skills (`/spt:*`, via the planned `sptc`→`spt` succession). The repo rename changes the derived
install dir (`adapters/_github/SaberMage-claude-spt`), so the `[update].repo` and any path assumptions on
your side should expect the new repo slug. One end user today, so the re-add cost is a non-issue.

---

## Dependency map (what unblocks what)

- **Ask 1** → hook logic + new handlers ride `spt adapter update`; `hooks.json` goes static-forever.
- **Ask 2** → the plugin update is automated; the only manual residual is `/reload-plugins`.
- **Ask 3** → `translate` folds into the one `claude-spt` binary (1 artifact/triple).
- Ships **without** any ask: `gh_release` + `[update].message` (the message instructs the manual steps),
  partial binary consolidation (digest/psyche/post-update), and the name unification.


Captured. Let me confirm nothing broke the traceability gate.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
traceable-reqs check >/dev/null 2>&1 && echo "traceable GREEN" || echo "traceable RED (investigate)"
echo "=== new/edited artifacts ==="; ls docs/adr/0005-name-unification.md docs/adr/0006-one-command-update-and-consolidated-binary.md UPDATE-NAMING-DOYLE-ASKS.md 2>&1
git status --short


traceable GREEN
=== new/edited artifacts ===
UPDATE-NAMING-DOYLE-ASKS.md
docs/adr/0005-name-unification.md
docs/adr/0006-one-command-update-and-consolidated-binary.md
 M CONTEXT.md
?? UPDATE-NAMING-DOYLE-ASKS.md
?? docs/adr/0005-name-unification.md
?? docs/adr/0006-one-command-update-and-consolidated-binary.md


Grill converged + captured. `traceable` green. Caveman summary:

**Decided:**

| Topic | Decision |
|-------|----------|
| **One-command update** | Keep the plugin (raw skills re-rejected — CC #2422 breaks command namespacing). `gh_release` + `[update].message` ships **now** (message instructs `/reload-plugins` — unavoidable TUI step — + points at the `spt` CLI). Full one-lever needs the 3 doyle asks. |
| **Naming** | 2-token rule (ADR-0005): **`claude-spt`** = repo+adapter+binary (rename repo `spt-claude-code`→`claude-spt`); **`spt`** = plugin+skills (`/spt:*` via succession). `cc` launcher unchanged. |
| **Binary consolidation** | One `claude-spt <subcommand>`. **Partial now** (digest/psyche/post-update — command-seams, no dep). **translate folds in later** (needs ask #3). |
| **Reactive skills** | Thin commune/send/signoff → stubs, bodies into perched brief + `/spt:live` UPS body. Plan item — delivery-timing wrinkle flagged (brief fires bind\|boundary, not the instant a seed goes live). |

**3 doyle asks** (the deliverable, in `UPDATE-NAMING-DOYLE-ASKS.md`):
1. **`spt api run-hook <adapter> <event>`** — generic hook dispatch → hook logic rides the adapter, `hooks.json` static-forever.
2. **Composite `[update]`** — `gh_release` + delegated post-step; must run **unconditionally** + a **"changed" return** prints `message` on an adapter no-op.
3. **`[message-idle-translation-binary]` takes a command** (not bare `path`) → fold `translate` into the one binary.

**Artifacts written** (uncommitted):
- `docs/adr/0005-name-unification.md`
- `docs/adr/0006-one-command-update-and-consolidated-binary.md`
- `CONTEXT.md` — new `naming-domains` term + cross-refs
- `UPDATE-NAMING-DOYLE-ASKS.md` — the doyle grill prompt

Two things: want me to **commit** these? And is the grill **done**, or keep pushing a branch (e.g. the README platform-chain specifics, or execution sequencing)?


please write a JIT plan for this next milestone (what can be done before doyle implements). i'll have doyle rolling the spt-core build alongside you. then /commune with immediate next steps and broad summary of the project's status + end goal. we'll resume in the next session


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


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


Writing the JIT plan for the doyle-independent prep, then committing the planning artifacts, then communing.


Write

# UNIFY-CONSOLIDATE-PLAN.md — JIT plan (doyle-independent prep)

> The next milestone: everything in ADR-0005 (name unification) + ADR-0006 (one-command update +
> consolidated binary) that ships **without** the three spt-core asks. doyle rolls the spt-core build
> (the asks) in parallel; this plan is the work we own meanwhile. Authored 2026-06-24 (perri).
> Companions: `docs/adr/0005-name-unification.md`, `docs/adr/0006-one-command-update-and-consolidated-binary.md`,
> `UPDATE-NAMING-DOYLE-ASKS.md`. Target release: **adapter v0.8.0**.

## Scope

Five doyle-independent tasks (U1–U5). They make the adapter one-lever-*shaped* and consolidate its
binaries, so that when the asks land the remaining wiring (D1–D3) is small. The plugin `sptc`→`spt`
succession (D4) stays gated on **owl retirement**, not doyle — out of this milestone.

## Tasks (doyle-independent)

### U1 — `[update].message` (quick win, ship first) · REQ-DIST-UPDATE-MESSAGE (mint)
- Add `message` to manifest `[update]` (the field is docs-confirmed; prints markdown on a real apply only).
- Copy: tell the user to run **`/reload-plugins`** (unavoidable TUI step) and point at the **`spt`** CLI
  (`spt endpoint run`) as the more powerful endpoint route, alongside `/spt:live`. (Use the operator's
  example phrasing.)
- Gate: `spt adapter update` prints it on a version apply; schema-validates.

### U2 — Binary consolidation, partial · REQ-DIST-BINARY-CONSOLIDATE (mint)
- Merge `claude-spt-digest` + `claude-spt-psyche` into **one `claude-spt` crate** with clap subcommands
  `digest` / `psyche`; add a `post-update` subcommand = the **plugin-sync** logic (detect `claude` vs `ccs`
  CLI → check cplugs marketplace, add if missing → `claude plugin add|update <plugin>` → print the notice;
  it does NOT run `/reload-plugins`). Standalone-runnable + unit-tested NOW even though `[update]` can't
  invoke it until ask #2 lands.
- Keep `cc-spt-idle-translate` separate (folds in at D3, after ask #3).
- Update manifest `[digest].extractor` → `claude-spt digest …`, `[session.psyche_init].command` →
  `claude-spt psyche …`.
- Update `ci/publish/package-adapter.sh` (binary set per triple: `claude-spt` + `cc-spt-idle-translate`),
  `ci/{digest,psyche}/build.sh` → one build, and the proof-ints.
- Gate: all subcommand tests green; digest-proof + (existing) translate-proof green; archive packs the 2
  binaries per triple.

### U3 — Name unification: repo rename + references · REQ-DIST-NAME-UNIFY (mint)
- Rename GitHub repo `spt-claude-code` → `claude-spt`. Update `[update].repo`, README, CI, package scripts,
  every `SaberMage/spt-claude-code` reference, and the install-dir assumptions in tests
  (`adapters/_github/SaberMage-claude-spt`).
- Adapter name `claude-spt` UNCHANGED. Plugin `sptc` UNCHANGED this milestone (succession = D4, owl-gated).
- Gate: a clean `spt adapter add --release SaberMage/claude-spt` re-acquires + runs on-node; `--release`
  + `update` both read `claude-spt`.

### U4 — Thin the reactive skills · REQ-DIST-SKELETON-THIN (mint)
- Move `commune`/`send`/`signoff` bodies OUT of the plugin SKILL.md (stubs: frontmatter + "live agents
  only — `/spt:live` first") and INTO adapter strings: a new **perched SessionStart brief** string
  (commune incl. **`--checkpoint`**, signoff) + the existing `/spt:live` UPS body for the go-live moment.
- Solve the delivery-timing wrinkle: perched brief fires on bind|boundary; `/spt:live` UPS body covers a
  seed→live transition (confirm whether going live re-surfaces a brief; if not, the UPS body is the path).
- Gate: a perched session's SessionStart additionalContext carries commune/checkpoint/signoff guidance;
  reactive-skill prose now rides `spt adapter update`.

### U5 — README install UX + agent-prompt skin · REQ-DIST-INSTALL-UX (mint)
- Platform-specific install chains (cmd / PowerShell / bash): check-for / install spt-core (claude-spt may
  be a user's first spt-core exposure → call the spt-releases per-platform install script), then
  `spt adapter add --release SaberMage/claude-spt`.
- A copy-paste **agent prompt** (the casual-user skin) that runs the install chain in one sequenced Bash
  call. Symmetric with the update lever (`spt adapter update claude-spt`).
- Gate: docs build / llms.txt in sync; the chains are copy-paste-correct per OS.

## Deferred (doyle-gated — land when the asks ship; NOT this milestone)
- **D1** generic `hooks.json` + `claude-spt hook <event>` subcommand ← ask #1 (`spt api run-hook`).
- **D2** wire `[update]` composite to invoke `claude-spt post-update` (the subcommand U2 prepped) ← ask #2.
- **D3** fold `translate` into `claude-spt translate` + retire `cc-spt-idle-translate` ← ask #3.
- **D4** plugin `sptc`→`spt` succession (`/spt:*`) ← owl retirement gate (separate from doyle).

## Sequencing
- U1 first (isolated, quick). Then U2 (the big refactor; preps the post-update subcommand for D2).
- U3 after U2 (both touch `package-adapter.sh`); batch them into the v0.8.0 cut. Rename the repo as part
  of cutting v0.8.0 so the new release lands on `claude-spt` (v0.7.0 stays on the redirected old slug).
- U4 / U5 parallel-able with U2/U3.
- Cut **v0.8.0** once U1–U5 green (bump adapter version; cplugs skeleton bump for the thinned stubs/new
  hooks.json only if structural).

## Open questions
- Repo rename vs the v0.8.0 release ordering (rename-then-cut, confirmed above — watch the install-dir
  break for the one existing install: a re-add covers it).
- `post-update` plugin-sync: exact `claude` vs `ccs` detection + idempotency + what it prints.
- Reactive-brief split: does `/spt:live` re-fire a SessionStart brief, or is the UPS body the only go-live
  delivery? (Empirical check.)
- Whether the cplugs skeleton needs a bump this milestone at all (U4 thins SKILL.md = structural → likely
  one bump; U1–U3 are adapter-side).

## Validation gate (per AGENTS.md)
- `cargo build` + all crate tests green (consolidated binary); `sh ci/run-gates.sh` green.
- `traceable-reqs check` green — REQs minted in `traceable-reqs.toml` FIRST, evidence tagged in-commit.
- On-node dogfood: `spt adapter add --release SaberMage/claude-spt` re-acquires + digest/translate-proof
  the consolidated binary; `spt adapter update` prints `[update].message`.
- v0.8.0 release cut (adapter `.spt` + cplugs skeleton if structural).


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

# UNIFY-CONSOLIDATE-PLAN.md — JIT plan (doyle-independent prep)

> The next milestone: everything in ADR-0005 (name unification) + ADR-0006 (one-command update +
> consolidated binary) that ships **without** the three spt-core asks. doyle rolls the spt-core build
> (the asks) in parallel; this plan is the work we own meanwhile. Authored 2026-06-24 (perri).
> Companions: `docs/adr/0005-name-unification.md`, `docs/adr/0006-one-command-update-and-consolidated-binary.md`,
> `UPDATE-NAMING-DOYLE-ASKS.md`. Target release: **adapter v0.8.0**.

## Scope

Five doyle-independent tasks (U1–U5). They make the adapter one-lever-*shaped* and consolidate its
binaries, so that when the asks land the remaining wiring (D1–D3) is small. The plugin `sptc`→`spt`
succession (D4) stays gated on **owl retirement**, not doyle — out of this milestone.

## Tasks (doyle-independent)

### U1 — `[update].message` (quick win, ship first) · REQ-DIST-UPDATE-MESSAGE (mint)
- Add `message` to manifest `[update]` (the field is docs-confirmed; prints markdown on a real apply only).
- Copy: tell the user to run **`/reload-plugins`** (unavoidable TUI step) and point at the **`spt`** CLI
  (`spt endpoint run`) as the more powerful endpoint route, alongside `/spt:live`. (Use the operator's
  example phrasing.)
- Gate: `spt adapter update` prints it on a version apply; schema-validates.

### U2 — Binary consolidation, partial · REQ-DIST-BINARY-CONSOLIDATE (mint)
- Merge `claude-spt-digest` + `claude-spt-psyche` into **one `claude-spt` crate** with clap subcommands
  `digest` / `psyche`; add a `post-update` subcommand = the **plugin-sync** logic (detect `claude` vs `ccs`
  CLI → check cplugs marketplace, add if missing → `claude plugin add|update <plugin>` → print the notice;
  it does NOT run `/reload-plugins`). Standalone-runnable + unit-tested NOW even though `[update]` can't
  invoke it until ask #2 lands.
- Keep `cc-spt-idle-translate` separate (folds in at D3, after ask #3).
- Update manifest `[digest].extractor` → `claude-spt digest …`, `[session.psyche_init].command` →
  `claude-spt psyche …`.
- Update `ci/publish/package-adapter.sh` (binary set per triple: `claude-spt` + `cc-spt-idle-translate`),
  `ci/{digest,psyche}/build.sh` → one build, and the proof-ints.
- Gate: all subcommand tests green; digest-proof + (existing) translate-proof green; archive packs the 2
  binaries per triple.

### U3 — Name unification: repo rename + references · REQ-DIST-NAME-UNIFY (mint)
- Rename GitHub repo `spt-claude-code` → `claude-spt`. Update `[update].repo`, README, CI, package scripts,
  every `SaberMage/spt-claude-code` reference, and the install-dir assumptions in tests
  (`adapters/_github/SaberMage-claude-spt`).
- Adapter name `claude-spt` UNCHANGED. Plugin `sptc` UNCHANGED this milestone (succession = D4, owl-gated).
- Gate: a clean `spt adapter add --release SaberMage/claude-spt` re-acquires + runs on-node; `--release`
  + `update` both read `claude-spt`.

### U4 — Thin the reactive skills · REQ-DIST-SKELETON-THIN (mint)
- Move `commune`/`send`/`signoff` bodies OUT of the plugin SKILL.md (stubs: frontmatter + "live agents
  only — `/spt:live` first") and INTO adapter strings: a new **perched SessionStart brief** string
  (commune incl. **`--checkpoint`**, signoff) + the existing `/spt:live` UPS body for the go-live moment.
- Solve the delivery-timing wrinkle: perched brief fires on bind|boundary; `/spt:live` UPS body covers a
  seed→live transition (confirm whether going live re-surfaces a brief; if not, the UPS body is the path).
- Gate: a perched session's SessionStart additionalContext carries commune/checkpoint/signoff guidance;
  reactive-skill prose now rides `spt adapter update`.

### U5 — README install UX + agent-prompt skin · REQ-DIST-INSTALL-UX (mint)
- Platform-specific install chains (cmd / PowerShell / bash): check-for / install spt-core (claude-spt may
  be a user's first spt-core exposure → call the spt-releases per-platform install script), then
  `spt adapter add --release SaberMage/claude-spt`.
- A copy-paste **agent prompt** (the casual-user skin) that runs the install chain in one sequenced Bash
  call. Symmetric with the update lever (`spt adapter update claude-spt`).
- Gate: docs build / llms.txt in sync; the chains are copy-paste-correct per OS.

## Deferred (doyle-gated — land when the asks ship; NOT this milestone)
- **D1** generic `hooks.json` + `claude-spt hook <event>` subcommand ← ask #1 (`spt api run-hook`).
- **D2** wire `[update]` composite to invoke `claude-spt post-update` (the subcommand U2 prepped) ← ask #2.
- **D3** fold `translate` into `claude-spt translate` + retire `cc-spt-idle-translate` ← ask #3.
- **D4** plugin `sptc`→`spt` succession (`/spt:*`) ← owl retirement gate (separate from doyle).

## Sequencing
- U1 first (isolated, quick). Then U2 (the big refactor; preps the post-update subcommand for D2).
- U3 after U2 (both touch `package-adapter.sh`); batch them into the v0.8.0 cut. Rename the repo as part
  of cutting v0.8.0 so the new release lands on `claude-spt` (v0.7.0 stays on the redirected old slug).
- U4 / U5 parallel-able with U2/U3.
- Cut **v0.8.0** once U1–U5 green (bump adapter version; cplugs skeleton bump for the thinned stubs/new
  hooks.json only if structural).

## Open questions
- Repo rename vs the v0.8.0 release ordering (rename-then-cut, confirmed above — watch the install-dir
  break for the one existing install: a re-add covers it).
- `post-update` plugin-sync: exact `claude` vs `ccs` detection + idempotency + what it prints.
- Reactive-brief split: does `/spt:live` re-fire a SessionStart brief, or is the UPS body the only go-live
  delivery? (Empirical check.)
- Whether the cplugs skeleton needs a bump this milestone at all (U4 thins SKILL.md = structural → likely
  one bump; U1–U3 are adapter-side).

## Validation gate (per AGENTS.md)
- `cargo build` + all crate tests green (consolidated binary); `sh ci/run-gates.sh` green.
- `traceable-reqs check` green — REQs minted in `traceable-reqs.toml` FIRST, evidence tagged in-commit.
- On-node dogfood: `spt adapter add --release SaberMage/claude-spt` re-acquires + digest/translate-proof
  the consolidated binary; `spt adapter update` prints `[update].message`.
- v0.8.0 release cut (adapter `.spt` + cplugs skeleton if structural).


please write a JIT plan for this next milestone (what can be done before doyle implements). i'll have doyle rolling the spt-core build alongside you. then /commune with immediate next steps and broad…


Now commit the planning artifacts (grill output + JIT plan):


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
git add docs/adr/0005-name-unification.md docs/adr/0006-one-command-update-and-consolidated-binary.md CONTEXT.md UPDATE-NAMING-DOYLE-ASKS.md UNIFY-CONSOLIDATE-PLAN.md
git commit -q -F - <<'EOF'
docs(plan): name unification + one-command-update arc — ADRs, doyle asks, JIT plan

Grill-with-docs output for the next milestone (post-v0.7.0):

- docs/adr/0005-name-unification.md: collapse 3 names -> 2 — `claude-spt`
  (repo+adapter+binary; rename spt-claude-code -> claude-spt) and `spt`
  (plugin+skills via the owl-gated succession). Adapter can't be `spt`
  (spt-core's own id) while skills want `/spt:*`, so two tokens is the floor.
- docs/adr/0006-one-command-update-and-consolidated-binary.md: keep the plugin
  (raw skills re-rejected — CC #2422 breaks command namespacing); gh_release +
  [update].message ships now; one `claude-spt <subcommand>` binary (partial
  now: digest/psyche/post-update; translate later). Records the 3 spt-core asks.
- CONTEXT.md: new `naming-domains` term + cross-refs.
- UPDATE-NAMING-DOYLE-ASKS.md: the doyle grill prompt — (1) `spt api run-hook`
  generic dispatch, (2) composite [update] (unconditional post-step + changed
  flag prints message), (3) translation seam takes a command not a bare path.
- UNIFY-CONSOLIDATE-PLAN.md: JIT plan for the doyle-INDEPENDENT prep (U1 update
  message, U2 binary consolidation, U3 repo rename, U4 thin reactive skills,
  U5 README install UX) toward adapter v0.8.0; D1-D4 deferred to the asks/owl.

No code yet — planning only. traceable GREEN.

Co-authored by: perri
EOF
echo "commit=$?"; git log -1 --format='%h %s'


commit=0
e814cad docs(plan): name unification + one-command-update arc — ADRs, doyle asks, JIT plan


Push (doyle needs the asks; clean resume), then commune.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
git push origin main 2>&1 | tail -3
echo "=== ping doyle the asks doc ==="
$OWL send doyle perri <<'EOF'
Grill-with-docs wrapped (operator + me). Pushed e814cad to origin/main. THE DELIVERABLE FOR YOU: `UPDATE-NAMING-DOYLE-ASKS.md` at repo root — 3 spt-core asks, each with proposed contract shape + open questions for you to rule on:

1. `spt api run-hook <adapter> <event>` — generic hook dispatch (hook logic rides `spt adapter update`; hooks.json static-forever). Handler ideally a binary subcommand.
2. Composite `[update]` — gh_release pull + a delegated post-step in the SAME `spt adapter update`. Two sub-reqs: post-step runs UNCONDITIONALLY (plugin can change w/o adapter version bump), and a "changed" return makes `spt adapter update` print `[update].message` even on an adapter no-op.
3. `[message-idle-translation-binary]` take a command/subcommand (not a bare `path`) so `translate` folds into our consolidated `claude-spt` binary.

Design context: ADR-0005 (name unify: repo+adapter+binary = claude-spt; plugin+skills = spt) + ADR-0006 (the update arc). Operator says you're rolling the spt-core build alongside my doyle-INDEPENDENT prep (UNIFY-CONSOLIDATE-PLAN.md: [update].message, binary consolidation digest/psyche/post-update, repo rename, thin reactive skills, README install UX → v0.8.0). I'm signing off this session; resuming next session. Pull the asks doc + grill me back on any contract shape.
EOF
echo "sent=$?"


To https://github.com/SaberMage/spt-claude-code.git
   50eac11..e814cad  main -> main
=== ping doyle the asks doc ===
➜ SENT:doyle
sent=0


Pushed (e814cad), doyle has the asks. Now the commune — the cross-session resume brief.