- **`spt endpoint run`** — the `spt` CLI route: spawn/attach endpoints, drive them over Remote Control, and bring up sessions headlessly. The more powerful lever once you're comfortable.
"""

# ── [hooks.<event>] ──────────────────────────────────────────────────────────────────────────

- **`spt endpoint run`** — the `spt` CLI route: spawn/attach endpoints, drive them over Remote Control, and bring up sessions headlessly. The more powerful lever once you're comfortable.
"""

# ── [update.post] — the delegated plugin-reconcile step (D2, spt-core v0.16.0) ──────────────────
# spt-core runs this AFTER the gh_release pull+re-register, UNCONDITIONALLY (even on an adapter no-op),
# completing the one lever `spt adapter update claude-spt` = pull the adapter .spt + reconcile the
# cplugs plugin. `command` resolves the consolidated binary from the install dir via {adapter_dir}
# (survives updates). spt-core pipes a one-line JSON context on the command's stdin
# ({adapter_applied, adapter_name, profile_name, version, previous_version, adapter_dir}; additive —
# the subcommand ignores unknown keys) and reads its STDOUT to arbitrate the notice: the reserved
# sentinel `!!update-message!!` (which post-update emits on a successful reconcile) FIRES the
# [update].message above; custom text would supersede it; empty prints nothing. Exit code is
# orthogonal and the step is FAIL-ISOLATED (a committed pull is never rolled back on our failure).
# `self_verifies = false`: attestation-only, gates nothing yet (no signing infra). The reconcile logic
# rides `spt adapter update` (it lives in the binary), so it stays current without a separate step.
# [impl->REQ-DIST-UPDATE-MESSAGE]
[update.post]
command = "{adapter_dir}/claude-spt post-update"
self_verifies = false

# ── [hooks.<event>] ──────────────────────────────────────────────────────────────────────────


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

- **`spt endpoint run`** — the `spt` CLI route: spawn/attach endpoints, drive them over Remote Control, and bring up sessions headlessly. The more powerful lever once you're comfortable.
"""

# ── [hooks.<event>] ──────────────────────────────────────────────────────────────────────────

- **`spt endpoint run`** — the `spt` CLI route: spawn/attach endpoints, drive them over Remote Control, and bring up sessions headlessly. The more powerful lever once you're comfortable.
"""

# ── [update.post] — the delegated plugin-reconcile step (D2, spt-core v0.16.0) ──────────────────
# spt-core runs this AFTER the gh_release pull+re-register, UNCONDITIONALLY (even on an adapter no-op),
# completing the one lever `spt adapter update claude-spt` = pull the adapter .spt + reconcile the
# cplugs plugin. `command` resolves the consolidated binary from the install dir via {adapter_dir}
# (survives updates). spt-core pipes a one-line JSON context on the command's stdin
# ({adapter_applied, adapter_name, profile_name, version, previous_version, adapter_dir}; additive —
# the subcommand ignores unknown keys) and reads its STDOUT to arbitrate the notice: the reserved
# sentinel `!!update-message!!` (which post-update emits on a successful reconcile) FIRES the
# [update].message above; custom text would supersede it; empty prints nothing. Exit code is
# orthogonal and the step is FAIL-ISOLATED (a committed pull is never rolled back on our failure).
# `self_verifies = false`: attestation-only, gates nothing yet (no signing infra). The reconcile logic
# rides `spt adapter update` (it lives in the binary), so it stays current without a separate step.
# [impl->REQ-DIST-UPDATE-MESSAGE]
[update.post]
command = "{adapter_dir}/claude-spt post-update"
self_verifies = false

# ── [hooks.<event>] ──────────────────────────────────────────────────────────────────────────

+# ── [update.post] — the delegated plugin-reconcile step (D2, spt-core v0.16.0) ──────────────────

+# spt-core runs this AFTER the gh_release pull+re-register, UNCONDITIONALLY (even on an adapter no-op),

+# completing the one lever `spt adapter update claude-spt` = pull the adapter .spt + reconcile the

+# cplugs plugin. `command` resolves the consolidated binary from the install dir via {adapter_dir}

+# (survives updates). spt-core pipes a one-line JSON context on the command's stdin

+# ({adapter_applied, adapter_name, profile_name, version, previous_version, adapter_dir}; additive —

+# the subcommand ignores unknown keys) and reads its STDOUT to arbitrate the notice: the reserved

+# sentinel `!!update-message!!` (which post-update emits on a successful reconcile) FIRES the

+# [update].message above; custom text would supersede it; empty prints nothing. Exit code is

+# orthogonal and the step is FAIL-ISOLATED (a committed pull is never rolled back on our failure).

+# `self_verifies = false`: attestation-only, gates nothing yet (no signing infra). The reconcile logic

+# rides `spt adapter update` (it lives in the binary), so it stays current without a separate step.

+command = "{adapter_dir}/claude-spt post-update"

# CC hook event → the opaque `api …` command it fires. These mirror the hand-written plugin

# hooks.json (plugin/sptc/hooks/hooks.json) one-for-one; the plugin shells `spt api`, this table


Add a manifest unit assertion for `[update.post]`, then build + gates:


Edit

if grep -q 'reload-plugins' "$MANIFEST"; then echo "ok   [update].message points at /reload-plugins"; else echo "FAIL [update].message lacks the /reload-plugins notice"; fail=1; fi

if grep -q 'reload-plugins' "$MANIFEST"; then echo "ok   [update].message points at /reload-plugins"; else echo "FAIL [update].message lacks the /reload-plugins notice"; fail=1; fi
# D2: [update.post] declares the delegated plugin-reconcile = {adapter_dir}/claude-spt post-update.
if grep -Eq '^[[:space:]]*command[[:space:]]*=[[:space:]]*"\{adapter_dir\}/claude-spt post-update"' "$MANIFEST"; then echo "ok   [update.post].command = \"{adapter_dir}/claude-spt post-update\""; else echo "FAIL [update.post].command missing/wrong"; fail=1; fi
if grep -Eq '^\[update\.post\]' "$MANIFEST"; then echo "ok   [update.post] table present"; else echo "FAIL no [update.post] table"; fail=1; fi


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

if grep -q 'reload-plugins' "$MANIFEST"; then echo "ok   [update].message points at /reload-plugins"; else echo "FAIL [update].message lacks the /reload-plugins notice"; fail=1; fi
# D2: [update.post] declares the delegated plugin-reconcile = {adapter_dir}/claude-spt post-update.
if grep -Eq '^[[:space:]]*command[[:space:]]*=[[:space:]]*"\{adapter_dir\}/claude-spt post-update"' "$MANIFEST"; then echo "ok   [update.post].command = \"{adapter_dir}/claude-spt post-update\""; else echo "FAIL [update.post].command missing/wrong"; fail=1; fi
if grep -Eq '^\[update\.post\]' "$MANIFEST"; then echo "ok   [update.post] table present"; else echo "FAIL no [update.post] table"; fail=1; fi

#!/bin/sh
# Brand-value regression guard for the `spt endpoint run` shortcut: the manifest MUST declare
# adapter.shortcut_basename = "cc" (→ cc-<id>, the M12 cc launcher). A drift to "sptc"/"spt"/default
# is caught here, distinct from the structural manifest-schema check. (The launcher brand is
# decoupled from the plugin name `sptc`; the s/sptc/spt/ succession renames the plugin, not this.)
# Run: sh tests/manifest-shortcut.sh (exit 0 = pass).
# [unit->REQ-DIST-SHORTCUT-BASENAME]
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
MANIFEST="$ROOT/adapter/claude-spt.toml"
fail=0

# The active assignment line (not a comment): a TOML key at column 0. grep -E, exclude `#`-leading.
line=$(grep -E '^[[:space:]]*shortcut_basename[[:space:]]*=' "$MANIFEST")
if [ -z "$line" ]; then echo "FAIL no shortcut_basename assignment in manifest"; exit 1; fi

case "$line" in
  *'"cc"'*) echo "ok   shortcut_basename = \"cc\" (cc-<id> brand intact)" ;;
  *) echo "FAIL shortcut_basename is not \"cc\": $line"; fail=1 ;;
esac

# Defensive: exactly one active assignment (a stray second would make the picker value ambiguous).
n=$(grep -Ec '^[[:space:]]*shortcut_basename[[:space:]]*=' "$MANIFEST")
if [ "$n" -eq 1 ]; then echo "ok   single shortcut_basename assignment"; else echo "FAIL $n shortcut_basename assignments (want 1)"; fail=1; fi

# [unit->REQ-HAZARD-PSYCHE-PERMS-DEADLOCK]
# Non-interactive bringup: ALL THREE CC spawn commands — the two [session.self] (base `claude`, the
# `ccs` profile) AND [session.resume] (`claude -r … --remote-control …`) — MUST carry
# --dangerously-skip-permissions. The broker spawns CC into a PTY with no operator, so an interactive
# permission prompt would deadlock the launch (docs/KNOWN-HAZARDS.md §2.2). Match the active
# `command =` lines whose value is a bare claude/ccs CC spawn — NOT `claude-spt psyche` (the runner;
# `claude-spt` has `-`/no-space after `claude`, so the (claude|ccs)(space|") match correctly excludes
# it — its turns are asserted in the tools/claude-spt crate's psyche unit tests).
spawn=$(grep -E '^[[:space:]]*command[[:space:]]*=[[:space:]]*"(claude|ccs)([[:space:]]|")' "$MANIFEST")
nspawn=$(printf '%s' "$spawn" | grep -c .)
if [ "$nspawn" -eq 3 ]; then echo "ok   3 CC spawn commands ([session.self] base claude + ccs, [session.resume])"; else echo "FAIL expected 3 CC spawn commands, found $nspawn"; fail=1; fi
nomiss=$(printf '%s\n' "$spawn" | grep -c -v -- '--dangerously-skip-permissions')
if [ "$nspawn" -gt 0 ] && [ "$nomiss" -eq 0 ]; then echo "ok   every CC spawn command carries --dangerously-skip-permissions"; else echo "FAIL a CC spawn command lacks --dangerously-skip-permissions ($nomiss without it)"; fail=1; fi

# [unit->REQ-DIST-SESSION-RESUME]
# [session.resume] declares CC's native-resume verb — `-r {session_id}` (reload the real transcript,
# else a resume re-runs [session.self] → blank) and `--remote-control {id}` (thread the endpoint id
# as the RC session name). A drift that drops either fill silently breaks native resume / RC drive.
resume=$(grep -E '^[[:space:]]*command[[:space:]]*=[[:space:]]*"claude -r ' "$MANIFEST")
if [ -z "$resume" ]; then echo 'FAIL no [session.resume] claude -r … command in manifest'; fail=1; else
  case "$resume" in
    *'-r {session_id}'*) echo "ok   [session.resume] reloads by {session_id} (-r)";;
    *) echo "FAIL [session.resume] missing -r {session_id}: $resume"; fail=1;;
  esac
  case "$resume" in
    *'--remote-control {id}'*) echo "ok   [session.resume] threads {id} as the remote-control name";;
    *) echo "FAIL [session.resume] missing --remote-control {id}: $resume"; fail=1;;
  esac
fi

# [unit->REQ-DIST-NAME-UNIFY]
# U3: the repo was renamed spt-claude-code -> claude-spt. [update].repo MUST read the new slug (else
# `spt adapter update` pulls from the redirected old slug), and NO owner-qualified old slug may linger
# in the manifest (regression guard for the ref-flip).
if grep -Eq '^[[:space:]]*repo[[:space:]]*=[[:space:]]*"SaberMage/claude-spt"' "$MANIFEST"; then echo 'ok   [update].repo = "SaberMage/claude-spt"'; else echo "FAIL [update].repo is not SaberMage/claude-spt"; fail=1; fi
if grep -q 'SaberMage/spt-claude-code' "$MANIFEST"; then echo "FAIL stale SaberMage/spt-claude-code ref lingers in manifest"; fail=1; else echo "ok   no stale spt-claude-code repo ref in manifest"; fi

# [unit->REQ-DIST-UPDATE-MESSAGE]
# U1: [update] carries a `message` field (markdown spt-core prints on a real apply). It MUST mention
# the unavoidable /reload-plugins manual residual — that is the field's whole reason to exist. Match
# the active `message =` assignment and the reload-plugins notice anywhere in the manifest body.
if grep -Eq '^[[:space:]]*message[[:space:]]*=' "$MANIFEST"; then echo "ok   [update].message present"; else echo "FAIL [update] has no message field"; fail=1; fi
if grep -q 'reload-plugins' "$MANIFEST"; then echo "ok   [update].message points at /reload-plugins"; else echo "FAIL [update].message lacks the /reload-plugins notice"; fail=1; fi

# [unit->REQ-DIST-RC-STARTUP]
# U6: {id} threads the DISPLAY name (`-n {id}`) on BOTH bringup paths and the REMOTE-CONTROL channel
# (`--remote-control {id}`) on BOTH, so a FRESH ([session.self]) and a RESUMED ([session.resume])
# endpoint are identified + RC-controlled identically. self currently `claude -n {id} …`, resume
# `claude -r {session_id} -n {id} …`. A drift that drops -n on either, or --remote-control on self,
# silently breaks the {id} display/RC parity. ({id}-only now; -> {id}@{node} when a {node} key lands.)
self_cmd=$(grep -E '^[[:space:]]*command[[:space:]]*=[[:space:]]*"claude -n ' "$MANIFEST")
if [ -z "$self_cmd" ]; then echo 'FAIL no [session.self] "claude -n {id} …" command in manifest'; fail=1; else
  case "$self_cmd" in
    *'-n {id}'*) echo "ok   [session.self] sets {id} as the display name (-n)";;
    *) echo "FAIL [session.self] missing -n {id}: $self_cmd"; fail=1;;
  esac
  case "$self_cmd" in
    *'--remote-control {id}'*) echo "ok   [session.self] threads {id} as the remote-control name";;
    *) echo "FAIL [session.self] missing --remote-control {id}: $self_cmd"; fail=1;;
  esac
fi
# resume carries -n {id} too (the U6 addition — parity with self; --remote-control {id} already
# asserted by the REQ-DIST-SESSION-RESUME block above, $resume reused).
case "$resume" in
  *'-n {id}'*) echo "ok   [session.resume] sets {id} as the display name (-n, U6 parity)";;
  *) echo "FAIL [session.resume] missing -n {id} (U6 display-name parity): $resume"; fail=1;;
esac

# [unit->REQ-DIST-IDLE-TRANSLATE]
# [message-idle-translation-binary] declares the idle-delivery filter via `command` (spt-core v0.16.0
# seam; `path` deprecated) = the `translate` subcommand of the consolidated binary, resolved from the
# install dir via {adapter_dir} (D3 fold). MUST be `command` (not the deprecated `path`) and name the
# claude-spt translate subcommand.
idle=$(grep -E '^[[:space:]]*command[[:space:]]*=[[:space:]]*"\{adapter_dir\}/claude-spt translate"' "$MANIFEST")
if [ -n "$idle" ]; then echo "ok   [message-idle-translation-binary] command = \"{adapter_dir}/claude-spt translate\""; else echo "FAIL [message-idle-translation-binary] command != \"{adapter_dir}/claude-spt translate\""; fail=1; fi
# Regression guard: the deprecated bare `path = "cc-spt-idle-translate"` must be GONE (exactly one of path/command).
if grep -Eq '^[[:space:]]*path[[:space:]]*=[[:space:]]*"cc-spt-idle-translate"' "$MANIFEST"; then echo "FAIL deprecated [message-idle-translation-binary].path still present (both-set is refused)"; fail=1; else echo "ok   no deprecated idle-translate path (command-only)"; fi

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

+# D2: [update.post] declares the delegated plugin-reconcile = {adapter_dir}/claude-spt post-update.

+if grep -Eq '^[[:space:]]*command[[:space:]]*=[[:space:]]*"\{adapter_dir\}/claude-spt post-update"' "$MANIFEST"; then echo "ok   [update.post].command = \"{adapter_dir}/claude-spt post-update\""; else echo "FAIL [update.post].command missing/wrong"; fail=1; fi

+if grep -Eq '^\[update\.post\]' "$MANIFEST"; then echo "ok   [update.post] table present"; else echo "FAIL no [update.post] table"; fail=1; fi


proceed with the wiring and gates. spt-core is updated. publish when everything is ready.


Verify PreToolUse message polling and injection


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt" && cargo test --quiet 2>&1 | tail -4; echo "=== dry-run smoke ==="; cargo build --release --quiet 2>&1 | grep -i warning || echo "no warnings"; printf '%s\n' '{"adapter_applied":true,"adapter_dir":"/x","unknown":1}' | ./target/release/claude-spt.exe post-update --dry-run 2>/dev/null | head -3

Build+test crate + post-update dry-run smoke


running 61 tests
.............................................................
test result: ok. 61 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

=== dry-run smoke ===
no warnings
would run: claude plugin update sptc@cplugs


61/61 pass, clean build, dry-run correct. Run full gates (schema validates `[update.post]` against the re-vendored schema):


cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "=== manifest-shortcut update.post asserts ==="; sh tests/manifest-shortcut.sh 2>&1 | grep -iE "update.post|update.message|FAIL|OK$"; echo "=== full gates ==="; sh ci/run-gates.sh 2>&1 | grep -E "RESULT|FAIL|coverage green|manifest validates"

Gates after D2


=== manifest-shortcut update.post asserts ===
ok   [update.post].command = "{adapter_dir}/claude-spt post-update"
ok   [update.post] table present
MANIFEST-SHORTCUT OK
=== full gates ===
ok  coverage green
ok   manifest validates against published manifest.schema.json
=== RESULT: PASS ===


D2 green. Promote REQ-DIST-UPDATE-MESSAGE (now has impl + unit from the post-update contract wiring):


Edit

required_stages = ["unit"]  # U1 (quick win, ship first) — ACTIVATED 2026-06-25. The deliverable is the manifest [update].message seam (adapter/claude-spt.toml), and the traceable-reqs scanner does NOT read .toml, so the in-repo scannable evidence is the unit assertion (same convention as REQ-DIST-SESSION-RESUME / REQ-DIST-RC-STARTUP). unit: tests/manifest-shortcut.sh (message present + carries the /reload-plugins notice) + tests/manifest-schema.sh (schema-validates). doc DEFERRED to the docs-site harness-contract/RELEASE-RUNBOOK slice (designed in docs/adr/0006 + UPDATE-NAMING-DOYLE-ASKS.md meanwhile). int DEFERRED: `spt adapter update` prints it on a real version apply (on-node dogfood at the v0.8.0 cut).

required_stages = ["impl", "unit"]  # U1 (the [update].message field) ACTIVATED 2026-06-25; D2 ([update.post] composite) PROMOTED to +impl 2026-06-26 (spt-core v0.16.0). impl: tools/claude-spt/src/post_update.rs — the [update.post] stdin/stdout contract (update_applied_in parses the additive JSON context; arbiter_line emits the `!!update-message!!` sentinel on a successful reconcile so the static [update].message fires; diagnostics→stderr, stdout=arbiter; fail-isolated; three modes incl --dry-run). The manifest seams [update].message + [update.post]{command="{adapter_dir}/claude-spt post-update", self_verifies=false} are .toml (unscanned) — evidence is the unit assertions. unit: tests/manifest-shortcut.sh (message present + /reload-plugins notice + [update.post] table/command) + the crate's cargo tests (update_applied_in additive parse, arbiter_line sentinel/empty) + tests/manifest-schema.sh (validates against the re-vendored v0.16.0 schema with update.post). doc DEFERRED to the docs-site slice (designed in docs/adr/0006 + UPDATE-NAMING-DOYLE-ASKS.md). int DEFERRED to the publish dogfood: real `spt adapter update claude-spt` pulls the .spt then fires [update.post] → post-update reconciles the plugin → sentinel → [update].message prints (needs the v0.8.0 release published).


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

title = "The adapter manifest [update] table carries a `message` markdown field, printed by spt-core ONLY on a real version apply: it tells the user to run /reload-plugins (the unavoidable TUI step) and points at the more-powerful `spt` CLI endpoint route (`spt endpoint run`) alongside /sptc:live. U1."

-required_stages = ["unit"]  # U1 (quick win, ship first) — ACTIVATED 2026-06-25. The deliverable is the manifest [update].message seam (adapter/claude-spt.toml), and the traceable-reqs scanner does NOT read .toml, so the in-repo scannable evidence is the unit assertion (same convention as REQ-DIST-SESSION-RESUME / REQ-DIST-RC-STARTUP). unit: tests/manifest-shortcut.sh (message present + carries the /reload-plugins notice) + tests/manifest-schema.sh (schema-validates). doc DEFERRED to the docs-site harness-contract/RELEASE-RUNBOOK slice (designed in docs/adr/0006 + UPDATE-NAMING-DOYLE-ASKS.md meanwhile). int DEFERRED: `spt adapter update` prints it on a real version apply (on-node dogfood at the v0.8.0 cut).

+required_stages = ["impl", "unit"]  # U1 (the [update].message field) ACTIVATED 2026-06-25; D2 ([update.post] composite) PROMOTED to +impl 2026-06-26 (spt-core v0.16.0). impl: tools/claude-spt/src/post_update.rs — the [update.post] stdin/stdout contract (update_applied_in parses the additive JSON context; arbiter_line emits the `!!update-message!!` sentinel on a successful reconcile so the static [update].message fires; diagnostics→stderr, stdout=arbiter; fail-isolated; three modes incl --dry-run). The manifest seams [update].message + [update.post]{command="{adapter_dir}/claude-spt post-update", self_verifies=false} are .toml (unscanned) — evidence is the unit assertions. unit: tests/manifest-shortcut.sh (message present + /reload-plugins notice + [update.post] table/command) + the crate's cargo tests (update_applied_in additive parse, arbiter_line sentinel/empty) + tests/manifest-schema.sh (validates against the re-vendored v0.16.0 schema with update.post). doc DEFERRED to the docs-site slice (designed in docs/adr/0006 + UPDATE-NAMING-DOYLE-ASKS.md). int DEFERRED to the publish dogfood: real `spt adapter update claude-spt` pulls the .spt then fires [update.post] → post-update reconciles the plugin → sentinel → [update].message prints (needs the v0.8.0 release published).


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && traceable-reqs check >/dev/null 2>&1 && echo "traceable green" || echo "traceable FAIL"; git add -A && git commit -q -F - <<'EOF'
feat(v0.8.0): D2 — wire [update.post] composite update (spt-core v0.16.0)

REQ-DIST-UPDATE-MESSAGE (+impl, was +unit). Completes the one lever
`spt adapter update claude-spt` = pull the adapter .spt + reconcile the cplugs plugin.

- manifest [update.post] = {command="{adapter_dir}/claude-spt post-update",
  self_verifies=false} — spt-core runs it after the gh_release pull, unconditionally.
- post_update.rs honors the contract: reads the additive stdin JSON context
  (update_applied_in — ignores unknown keys), and STDOUT arbitrates the notice via
  the reserved `!!update-message!!` sentinel (arbiter_line) so the user-facing copy
  lives once in [update].message. Diagnostics → stderr (stdout is the arbiter
  channel); fail-isolated; three modes (update.post via piped stdin / standalone TTY
  human-notice / --dry-run preview).

unit: crate tests (update_applied_in additive, arbiter_line sentinel/empty) +
manifest-shortcut ([update.post] table + command) + manifest-schema (re-vendored
v0.16.0 schema defines update.post). int (live update.post firing) deferred to the
publish dogfood (needs the v0.8.0 release).

Digest input-classification binding (doyle): the extractor already opens a
turn-boundary `input` per user-turn message (user_string_is_input) — binding honored,
no change needed.

Gates green; 61 crate tests; traceable green.

Co-authored by: perri
EOF
git log --oneline -1

Commit D2


traceable green
warning: in the working copy of 'tools/claude-spt/src/post_update.rs', LF will be replaced by CRLF the next time Git touches it
d7122ef feat(v0.8.0): D2 — wire [update.post] composite update (spt-core v0.16.0)


D2+D3 committed, traceable green. Now the version bumps + CHANGELOG + skeleton. Read the manifest version-note block:


42	# the [message-idle-translation-binary] choreography now ends with {key:enter} after the text. Binary
43	# rebuild only — manifest seams + floor unchanged.
44	# 0.7.0 (2026-06-24): the CHECKPOINT-COMMUNE WAVE — three operator-requested deliverables. (T1) the
45	# idle-translation binary now FRAMES each inbound <EVENT> across multiple lines (a raw \n after the
46	# opening tag and before </EVENT>) for visual distinction — CC soft-newlines a bare \n, empirically
47	# gated. (T2) AGENT-DRIVEN CHECKPOINT: a NEW [hooks.PostToolUse] (Write matcher) detects a `!!checkpoint!!`
48	# trigger in a live agent's own `.claude/<id>-commune.md` and self-sends `spt send --json-payload
49	# '{"checkpoint":"v1","wake":…}'`; the signal loops back through the endpoint's own translation binary,
50	# which parses the structured json envelope-attr and fires a /clear + wake macro — the agent clearing +
51	# rebuilding its own context from its freshest commune. (T3) RESUME parity: SessionStart now pulls
52	# `spt api psyche-download <id>` and injects the durable role/live/project + pending commune/signoff as
53	# additionalContext (closes F-020). These REQUIRE spt-core v0.15.0 (the `--json-payload` send axis + the
54	# `psyche-download` verb), so the floor BUMPS to 0.15.0 below.
55	version = "0.7.0"
56	# Floor BUMPED to 0.13.2 (2026-06-22, C / F-014 drop): the release packaging moved from the per-OS
57	# stopgap (`adapter.spt` = a windows-binary copy — F-014; broke on a linux host) to ONE fat .spt that
58	# bundles both x86_64 triples beside a shared manifest. spt-core extracts a fat archive (classify
59	# top-level triple dirs → flatten this node's triple into the install dir, preserving bare-name
60	# resolution) ONLY on >= 0.13.2 (doyle, extract_release_archive); an older node cannot read the asset
61	# at all, so declaring the floor is honest, not a needless block. (Prior floor 0.9.0 was the
62	# adapter-agnostic seed/listen-by-host_binaries requirement, PREP-4 — still satisfied, just superseded.)
63	# Floor BUMPED to 0.15.0 (2026-06-24, the checkpoint-commune wave / v0.7.0): T2's checkpoint self-send
64	# needs `spt send --json-payload` (the opaque-metadata send axis) and T3's resume pull needs the
65	# `spt api psyche-download <id>` verb — BOTH land in spt-core v0.15.0 (counter 34, Latest). The
66	# multi-line frame (T1) works on any node, but the wave ships as one unit, so the honest floor is the

# the [message-idle-translation-binary] choreography now ends with {key:enter} after the text. Binary
# rebuild only — manifest seams + floor unchanged.
# 0.7.0 (2026-06-24): the CHECKPOINT-COMMUNE WAVE — three operator-requested deliverables. (T1) the
# idle-translation binary now FRAMES each inbound <EVENT> across multiple lines (a raw \n after the
# opening tag and before </EVENT>) for visual distinction — CC soft-newlines a bare \n, empirically
# gated. (T2) AGENT-DRIVEN CHECKPOINT: a NEW [hooks.PostToolUse] (Write matcher) detects a `!!checkpoint!!`
# trigger in a live agent's own `.claude/<id>-commune.md` and self-sends `spt send --json-payload
# '{"checkpoint":"v1","wake":…}'`; the signal loops back through the endpoint's own translation binary,
# which parses the structured json envelope-attr and fires a /clear + wake macro — the agent clearing +
# rebuilding its own context from its freshest commune. (T3) RESUME parity: SessionStart now pulls
# `spt api psyche-download <id>` and injects the durable role/live/project + pending commune/signoff as
# additionalContext (closes F-020). These REQUIRE spt-core v0.15.0 (the `--json-payload` send axis + the
# `psyche-download` verb), so the floor BUMPS to 0.15.0 below.
version = "0.7.0"
# Floor BUMPED to 0.13.2 (2026-06-22, C / F-014 drop): the release packaging moved from the per-OS
# stopgap (`adapter.spt` = a windows-binary copy — F-014; broke on a linux host) to ONE fat .spt that
# bundles both x86_64 triples beside a shared manifest. spt-core extracts a fat archive (classify
# top-level triple dirs → flatten this node's triple into the install dir, preserving bare-name
# resolution) ONLY on >= 0.13.2 (doyle, extract_release_archive); an older node cannot read the asset
# at all, so declaring the floor is honest, not a needless block. (Prior floor 0.9.0 was the
# adapter-agnostic seed/listen-by-host_binaries requirement, PREP-4 — still satisfied, just superseded.)
# Floor BUMPED to 0.15.0 (2026-06-24, the checkpoint-commune wave / v0.7.0): T2's checkpoint self-send
# needs `spt send --json-payload` (the opaque-metadata send axis) and T3's resume pull needs the
# `spt api psyche-download <id>` verb — BOTH land in spt-core v0.15.0 (counter 34, Latest). The
# multi-line frame (T1) works on any node, but the wave ships as one unit, so the honest floor is the


Edit

# additionalContext (closes F-020). These REQUIRE spt-core v0.15.0 (the `--json-payload` send axis + the
# `psyche-download` verb), so the floor BUMPS to 0.15.0 below.
version = "0.7.0"

# additionalContext (closes F-020). These REQUIRE spt-core v0.15.0 (the `--json-payload` send axis + the
# `psyche-download` verb), so the floor BUMPS to 0.15.0 below.
# 0.8.0 (2026-06-26): the UNIFY-CONSOLIDATE cut — name unification + one-command update + binary
# consolidation (ADR-0005/0006), consuming spt-core v0.16.0 (counter 35). U1 [update].message + U6
# {id} display/RC parity on both bringup paths + U2 the consolidated claude-spt binary
# (digest/psyche/post-update). D2: [update.post]={command,self_verifies} composite update — one lever
# `spt adapter update claude-spt` pulls the .spt AND reconciles the cplugs plugin (post-update emits
# the `!!update-message!!` sentinel → [update].message). D3: the idle filter folded into
# `claude-spt translate` ([message-idle-translation-binary].command, path deprecated) → ONE tool binary
# per triple. Also F-021 reachability (PreToolUse mid-turn poll + honest busy/idle turn-state) + U4
# reactive-skill thinning (commune/signoff prose → the live-ops brief). REQUIRES spt-core v0.16.0 (the
# update.post + idle command + {adapter_dir} seams), so the floor BUMPS to 0.16.0 below.
version = "0.8.0"


# additionalContext (closes F-020). These REQUIRE spt-core v0.15.0 (the `--json-payload` send axis + the
# `psyche-download` verb), so the floor BUMPS to 0.15.0 below.
version = "0.7.0"

# additionalContext (closes F-020). These REQUIRE spt-core v0.15.0 (the `--json-payload` send axis + the
# `psyche-download` verb), so the floor BUMPS to 0.15.0 below.
# 0.8.0 (2026-06-26): the UNIFY-CONSOLIDATE cut — name unification + one-command update + binary
# consolidation (ADR-0005/0006), consuming spt-core v0.16.0 (counter 35). U1 [update].message + U6
# {id} display/RC parity on both bringup paths + U2 the consolidated claude-spt binary
# (digest/psyche/post-update). D2: [update.post]={command,self_verifies} composite update — one lever
# `spt adapter update claude-spt` pulls the .spt AND reconciles the cplugs plugin (post-update emits
# the `!!update-message!!` sentinel → [update].message). D3: the idle filter folded into
# `claude-spt translate` ([message-idle-translation-binary].command, path deprecated) → ONE tool binary
# per triple. Also F-021 reachability (PreToolUse mid-turn poll + honest busy/idle turn-state) + U4
# reactive-skill thinning (commune/signoff prose → the live-ops brief). REQUIRES spt-core v0.16.0 (the
# update.post + idle command + {adapter_dir} seams), so the floor BUMPS to 0.16.0 below.
version = "0.8.0"

# `spt api psyche-download <id>` and injects the durable role/live/project + pending commune/signoff as

+# 0.8.0 (2026-06-26): the UNIFY-CONSOLIDATE cut — name unification + one-command update + binary

+# consolidation (ADR-0005/0006), consuming spt-core v0.16.0 (counter 35). U1 [update].message + U6

+# {id} display/RC parity on both bringup paths + U2 the consolidated claude-spt binary

+# (digest/psyche/post-update). D2: [update.post]={command,self_verifies} composite update — one lever

+# `spt adapter update claude-spt` pulls the .spt AND reconciles the cplugs plugin (post-update emits

+# the `!!update-message!!` sentinel → [update].message). D3: the idle filter folded into

+# `claude-spt translate` ([message-idle-translation-binary].command, path deprecated) → ONE tool binary

+# per triple. Also F-021 reachability (PreToolUse mid-turn poll + honest busy/idle turn-state) + U4

+# reactive-skill thinning (commune/signoff prose → the live-ops brief). REQUIRES spt-core v0.16.0 (the

+# update.post + idle command + {adapter_dir} seams), so the floor BUMPS to 0.16.0 below.

# Floor BUMPED to 0.13.2 (2026-06-22, C / F-014 drop): the release packaging moved from the per-OS

# stopgap (`adapter.spt` = a windows-binary copy — F-014; broke on a linux host) to ONE fat .spt that

# bundles both x86_64 triples beside a shared manifest. spt-core extracts a fat archive (classify


73	# Floor BUMPED to 0.15.0 (2026-06-24, the checkpoint-commune wave / v0.7.0): T2's checkpoint self-send
74	# needs `spt send --json-payload` (the opaque-metadata send axis) and T3's resume pull needs the
75	# `spt api psyche-download <id>` verb — BOTH land in spt-core v0.15.0 (counter 34, Latest). The
76	# multi-line frame (T1) works on any node, but the wave ships as one unit, so the honest floor is the
77	# verb floor. An older node can't fire checkpoint or pull resume context, so declaring 0.15.0 is honest.
78	min_spt_core_version = "0.15.0"
79	hostable_types = ["LiveAgent", "ReadyAgent", "Worker"]
80	# host_binaries — STAGED ahead of spt-core v0.9.0 (doyle 2026-06-16). The pid→binary match-key for

# Floor BUMPED to 0.15.0 (2026-06-24, the checkpoint-commune wave / v0.7.0): T2's checkpoint self-send
# needs `spt send --json-payload` (the opaque-metadata send axis) and T3's resume pull needs the
# `spt api psyche-download <id>` verb — BOTH land in spt-core v0.15.0 (counter 34, Latest). The
# multi-line frame (T1) works on any node, but the wave ships as one unit, so the honest floor is the
# verb floor. An older node can't fire checkpoint or pull resume context, so declaring 0.15.0 is honest.
min_spt_core_version = "0.15.0"
hostable_types = ["LiveAgent", "ReadyAgent", "Worker"]
# host_binaries — STAGED ahead of spt-core v0.9.0 (doyle 2026-06-16). The pid→binary match-key for


# verb floor. An older node can't fire checkpoint or pull resume context, so declaring 0.15.0 is honest.
min_spt_core_version = "0.15.0"

# verb floor. An older node can't fire checkpoint or pull resume context, so declaring 0.15.0 is honest.
# Floor BUMPED to 0.16.0 (2026-06-26, the UNIFY-CONSOLIDATE cut / v0.8.0): D2's [update.post] composite,
# D3's [message-idle-translation-binary].command, and the {adapter_dir}/{adapter_name} substitution +
# lazy [strings] subst ALL land in spt-core v0.16.0 (counter 35, Latest; ADR-0029). An older node can't
# read update.post or the command seam, so declaring 0.16.0 is the honest floor.
min_spt_core_version = "0.16.0"


# verb floor. An older node can't fire checkpoint or pull resume context, so declaring 0.15.0 is honest.
min_spt_core_version = "0.15.0"

# verb floor. An older node can't fire checkpoint or pull resume context, so declaring 0.15.0 is honest.
# Floor BUMPED to 0.16.0 (2026-06-26, the UNIFY-CONSOLIDATE cut / v0.8.0): D2's [update.post] composite,
# D3's [message-idle-translation-binary].command, and the {adapter_dir}/{adapter_name} substitution +
# lazy [strings] subst ALL land in spt-core v0.16.0 (counter 35, Latest; ADR-0029). An older node can't
# read update.post or the command seam, so declaring 0.16.0 is the honest floor.
min_spt_core_version = "0.16.0"

# `spt api psyche-download <id>` verb — BOTH land in spt-core v0.15.0 (counter 34, Latest). The

# multi-line frame (T1) works on any node, but the wave ships as one unit, so the honest floor is the

+# Floor BUMPED to 0.16.0 (2026-06-26, the UNIFY-CONSOLIDATE cut / v0.8.0): D2's [update.post] composite,

+# D3's [message-idle-translation-binary].command, and the {adapter_dir}/{adapter_name} substitution +

+# lazy [strings] subst ALL land in spt-core v0.16.0 (counter 35, Latest; ADR-0029). An older node can't

+# read update.post or the command seam, so declaring 0.16.0 is the honest floor.

hostable_types = ["LiveAgent", "ReadyAgent", "Worker"]

# host_binaries — STAGED ahead of spt-core v0.9.0 (doyle 2026-06-16). The pid→binary match-key for

# adapter-agnostic resolution: `spt api listen <id>` resolves the owning adapter/profile by walking


Now the CHANGELOG. Read the top:


1	# Changelog
2
3	All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
4	The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
5	public release body verbatim.
6
7	> Each release below is keyed to the **adapter version of truth** (the version you see in
8	> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
9	> moves on its own slower schedule and may differ.
10
11	## [0.7.0] - 2026-06-24
12
13	> Requires spt-core **v0.15.0 or newer** (was v0.13.2). This release adds self-checkpointing for
14	> live agents, automatic resume context, and clearer multi-line message delivery — all built on
15	> features that arrive in spt-core v0.15.0.
16
17	### Added
18	- **Self-checkpointing for live agents — `/sptc:commune --checkpoint`.** A live agent can now reset
19	  and rebuild its own context without you running `/clear`. Write a commune containing the marker
20	  `!!checkpoint!!` and the session clears itself, then wakes back up from that very commune and keeps
21	  going. A single `!!checkpoint!!` wakes with a default "Proceed with next steps"; a **pair** of
22	  markers wakes with whatever instruction you write between them. The marker is one-shot — it never
23	  lingers in your rebuilt context.
24	- **Automatic resume context.** When a live agent's session starts (or clears), it now pulls its
25	  durable role, working context, and latest unsynced commune and re-injects them automatically — so
26	  it resumes where it left off instead of coming back blank.
27
28	### Changed
29	- **Incoming messages are easier to read.** A message delivered from another agent now renders across
30	  multiple lines — the envelope opening, the body, and the close each on their own line — instead of

# Changelog

All notable user-facing changes to the **sptc** plugin (Spacetime adapter for Claude Code).
The format follows [Keep a Changelog](https://keepachangelog.com/); each release section is the
public release body verbatim.

> Each release below is keyed to the **adapter version of truth** (the version you see in
> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)
> moves on its own slower schedule and may differ.

## [0.7.0] - 2026-06-24

> Requires spt-core **v0.15.0 or newer** (was v0.13.2). This release adds self-checkpointing for
> live agents, automatic resume context, and clearer multi-line message delivery — all built on
> features that arrive in spt-core v0.15.0.

### Added
- **Self-checkpointing for live agents — `/sptc:commune --checkpoint`.** A live agent can now reset
  and rebuild its own context without you running `/clear`. Write a commune containing the marker
  `!!checkpoint!!` and the session clears itself, then wakes back up from that very commune and keeps
  going. A single `!!checkpoint!!` wakes with a default "Proceed with next steps"; a **pair** of
  markers wakes with whatever instruction you write between them. The marker is one-shot — it never
  lingers in your rebuilt context.
- **Automatic resume context.** When a live agent's session starts (or clears), it now pulls its
  durable role, working context, and latest unsynced commune and re-injects them automatically — so
  it resumes where it left off instead of coming back blank.

### Changed
- **Incoming messages are easier to read.** A message delivered from another agent now renders across
  multiple lines — the envelope opening, the body, and the close each on their own line — instead of


## [0.8.0] - 2026-06-26

> Requires spt-core **v0.16.0 or newer** (was v0.15.0). This release unifies the project naming,
> turns updating into a single command, and consolidates the adapter to one tool binary — built on
> the update-arc + CLI features that arrive in spt-core v0.16.0.

### Added
- **One-command update — `spt adapter update claude-spt`.** A single command now keeps *everything*
  current: it pulls the new adapter (manifest + binary + strings) **and** reconciles the Claude Code
  plugin in the same step. The only manual residual is `/reload-plugins` (an unavoidable Claude Code
  TUI action), which the update prints a reminder to run.
- **Mid-turn reachability for live agents.** A live agent can now receive a message *while it is
  working* (not only between turns) — incoming messages surface mid-turn, with the endpoint honestly
  marked busy during a turn and idle when it finishes.

### Changed
- **Name unification.** The project/repo and adapter are now **`claude-spt`** everywhere spt-core
  sees them (the repo was renamed `spt-claude-code` → `claude-spt`; install and update both read
  `claude-spt`). The Claude Code plugin stays `sptc` / `/sptc:*` this release.
- **One tool binary.** The separate digest, psyche, and idle-translation binaries are consolidated
  into a single `claude-spt` binary (subcommands) — one artifact per platform in the release.
- **Session display name + remote control.** A spawned/resumed endpoint now shows its `{id}` as the
  session display name and is remote-control-attachable under that id, on both bringup paths.
- **Leaner skills.** `commune` / `send` / `signoff` guidance is now delivered by the adapter (so it
  updates with `spt adapter update`) rather than baked into the plugin.

### Requires
- spt-core **v0.16.0+** (the composite `[update.post]`, the idle-translation `command` seam, and the
  `{adapter_dir}` substitution all land in v0.16.0).

## [0.7.0] - 2026-06-24


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

## [0.8.0] - 2026-06-26

> Requires spt-core **v0.16.0 or newer** (was v0.15.0). This release unifies the project naming,
> turns updating into a single command, and consolidates the adapter to one tool binary — built on
> the update-arc + CLI features that arrive in spt-core v0.16.0.

### Added
- **One-command update — `spt adapter update claude-spt`.** A single command now keeps *everything*
  current: it pulls the new adapter (manifest + binary + strings) **and** reconciles the Claude Code
  plugin in the same step. The only manual residual is `/reload-plugins` (an unavoidable Claude Code
  TUI action), which the update prints a reminder to run.
- **Mid-turn reachability for live agents.** A live agent can now receive a message *while it is
  working* (not only between turns) — incoming messages surface mid-turn, with the endpoint honestly
  marked busy during a turn and idle when it finishes.

### Changed
- **Name unification.** The project/repo and adapter are now **`claude-spt`** everywhere spt-core
  sees them (the repo was renamed `spt-claude-code` → `claude-spt`; install and update both read
  `claude-spt`). The Claude Code plugin stays `sptc` / `/sptc:*` this release.
- **One tool binary.** The separate digest, psyche, and idle-translation binaries are consolidated
  into a single `claude-spt` binary (subcommands) — one artifact per platform in the release.
- **Session display name + remote control.** A spawned/resumed endpoint now shows its `{id}` as the
  session display name and is remote-control-attachable under that id, on both bringup paths.
- **Leaner skills.** `commune` / `send` / `signoff` guidance is now delivered by the adapter (so it
  updates with `spt adapter update`) rather than baked into the plugin.

### Requires
- spt-core **v0.16.0+** (the composite `[update.post]`, the idle-translation `command` seam, and the
  `{adapter_dir}` substitution all land in v0.16.0).

## [0.7.0] - 2026-06-24

> `/sptc:version` and the GitHub release tag). The cplugs *plugin skeleton* version (`plugin.json`)

> moves on its own slower schedule and may differ.

+> Requires spt-core **v0.16.0 or newer** (was v0.15.0). This release unifies the project naming,

+> turns updating into a single command, and consolidates the adapter to one tool binary — built on

+> the update-arc + CLI features that arrive in spt-core v0.16.0.

+- **One-command update — `spt adapter update claude-spt`.** A single command now keeps *everything*

+  current: it pulls the new adapter (manifest + binary + strings) **and** reconciles the Claude Code

+  plugin in the same step. The only manual residual is `/reload-plugins` (an unavoidable Claude Code

+  TUI action), which the update prints a reminder to run.

+- **Mid-turn reachability for live agents.** A live agent can now receive a message *while it is

+  working* (not only between turns) — incoming messages surface mid-turn, with the endpoint honestly

+  marked busy during a turn and idle when it finishes.

+- **Name unification.** The project/repo and adapter are now **`claude-spt`** everywhere spt-core

+  sees them (the repo was renamed `spt-claude-code` → `claude-spt`; install and update both read

+  `claude-spt`). The Claude Code plugin stays `sptc` / `/sptc:*` this release.

+- **One tool binary.** The separate digest, psyche, and idle-translation binaries are consolidated

+  into a single `claude-spt` binary (subcommands) — one artifact per platform in the release.

+- **Session display name + remote control.** A spawned/resumed endpoint now shows its `{id}` as the

+  session display name and is remote-control-attachable under that id, on both bringup paths.

+- **Leaner skills.** `commune` / `send` / `signoff` guidance is now delivered by the adapter (so it

+  updates with `spt adapter update`) rather than baked into the plugin.

+- spt-core **v0.16.0+** (the composite `[update.post]`, the idle-translation `command` seam, and the

+  `{adapter_dir}` substitution all land in v0.16.0).

> Requires spt-core **v0.15.0 or newer** (was v0.13.2). This release adds self-checkpointing for


cplugs skeleton bump (structural: U4 stubs + PreToolUse hooks.json). Bump plugin.json:


1	{
2	  "name": "sptc",
3	  "version": "0.1.6",
4	  "description": "Spacetime (spt) adapter for Claude Code — agent messaging, live agents, and spt-core bootstrap. Thin skeleton; logic rides the spt binary + adapter manifest (spt-core-conducted).",
5	  "author": {
6	    "name": "perri"
7	  },
8	  "keywords": ["spt", "spacetime", "messaging", "agents", "live", "claude-spt"]
9	}
10

sptc

spt spacetime messaging agents live claude-spt


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

"description": "Spacetime (spt) adapter for Claude Code — agent messaging, live agents, and spt-core bootstrap. Thin skeleton; logic rides the spt binary + adapter manifest (spt-core-conducted).",


Run full gates after the bumps:


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && sh ci/run-gates.sh 2>&1 | grep -E "GATE|FAIL|RESULT|coverage green|manifest validates|skeleton installable|llms" ; echo "=== version sanity ==="; grep -E '^version|^min_spt_core' adapter/claude-spt.toml; grep '"version"' plugin/sptc/.claude-plugin/plugin.json

Full gates + version sanity after bumps


=== GATE: shell-syntax (sh -n) ===
ok  ci/docs/gen-llms.sh
=== GATE: unit tests (tests/*.sh) ===
=== GATE: digest-extractor (cargo build + test) ===
=== GATE: psyche-runner (cargo build + test) ===
=== GATE: idle-translate (cargo build + test) ===
=== GATE: traceable-reqs check (requirement coverage) ===
ok  coverage green
=== GATE: skeleton-validate (cplugs installability) ===
ok  skeleton installable
=== GATE: manifest-schema ===
ok   manifest validates against published manifest.schema.json
=== GATE: docs-drift ===
ok   llms.txt in sync
=== RESULT: PASS ===
=== version sanity ===
version = "0.8.0"
min_spt_core_version = "0.16.0"
version = { file = "skills/version.md" }
  "version": "0.1.7",


All green: v0.8.0, min_spt_core 0.16.0, skeleton 0.1.7. Dogfood the consolidated binary on the installed 0.16.0 (no-mutation `--dir` proofs):


cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "=== digest-proof (0.16.0) ==="; sh ci/digest/digest-proof-int.sh 2>&1 | tail -3; echo "=== translate-proof (0.16.0) ==="; SPTC_ACCEPTANCE=1 sh ci/idle-translate/translate-proof-int.sh 2>&1 | tail -4

Proof-ints on spt-core 0.16.0


=== digest-proof (0.16.0) ===
    parsed 5 record(s), dropped 0
    --- parsed records ---
DIGEST-PROOF-INT OK
=== translate-proof (0.16.0) ===
ok  checkpoint macro terminates with the mandatory {commit}
ok  checkpoint without a wake uses the default
ok  normal delivery never fires the /clear macro
TRANSLATE-PROOF-INT OK


Both proof-ints GREEN on 0.16.0 against the consolidated binary. Commit the version bumps:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && git add -A && git commit -q -F - <<'EOF'
chore(release): v0.8.0 — bump version + min_spt_core 0.16.0 + CHANGELOG + skeleton 0.1.7

The UNIFY-CONSOLIDATE cut. adapter version 0.7.0 -> 0.8.0; min_spt_core_version
0.15.0 -> 0.16.0 (the update.post + idle command + {adapter_dir} seams land in
spt-core v0.16.0). CHANGELOG v0.8.0 entry (one-command update, mid-turn reachability,
name unification, one tool binary, {id} display/RC, leaner skills). cplugs skeleton
plugin.json 0.1.6 -> 0.1.7 (structural: U4 thinned SKILL.md + PreToolUse hooks.json).

Dogfooded on-node against spt-core 0.16.0: digest-proof (5/0) + translate-proof
GREEN on the consolidated binary. All gates green; traceable green; 61 crate tests.

Co-authored by: perri
EOF
git push origin main 2>&1 | tail -2

Commit + push v0.8.0 bumps


To https://github.com/SaberMage/claude-spt.git
   3129c08..2f9de99  main -> main


Pushed. Now the publish. First check prerequisites — package-adapter needs both win + linux binaries. Dry-run to see what's present:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "=== binaries present? ==="; ls -la tools/claude-spt/target/release/claude-spt.exe 2>/dev/null && echo "win: yes" || echo "win: MISSING"; ls -la tools/claude-spt/target/x86_64-unknown-linux-gnu/release/claude-spt 2>/dev/null && echo "linux: yes" || echo "linux: MISSING"; echo "=== cross toolchain ==="; command -v cargo-zigbuild >/dev/null 2>&1 && echo "cargo-zigbuild: yes" || echo "cargo-zigbuild: NO"; command -v zig >/dev/null 2>&1 && echo "zig: yes" || echo "zig: NO ($([ -n "$(ls $HOME/.sptc-zig/*/zig.exe 2>/dev/null)" ] && echo 'present in ~/.sptc-zig' || echo 'not in ~/.sptc-zig'))"; echo "=== package-adapter dry-run ==="; sh ci/publish/package-adapter.sh 2>&1 | tail -12

Check publish prerequisites


=== binaries present? ===
-rwxr-xr-x 2 decid 197609 423424 Jun 26 03:07 tools/claude-spt/target/release/claude-spt.exe
win: yes
linux: MISSING
=== cross toolchain ===
cargo-zigbuild: yes
zig: NO (present in ~/.sptc-zig)
=== package-adapter dry-run ===
== validate manifest ==
ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json

== tool binaries ==
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt/target/release/claude-spt.exe
  MISS  /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt/target/x86_64-unknown-linux-gnu/release/claude-spt
REFUSING to package: build BOTH platforms first — Windows: sh ci/digest/build.sh;
Linux (cross): cargo-zigbuild --release --target x86_64-unknown-linux-gnu for each tool crate (see docs/RELEASE-RUNBOOK.md).


Win binary present; linux needs cross-build. `cargo-zigbuild` available, zig in `~/.sptc-zig`. Build the linux binary:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && ZIGDIR=$(dirname "$(ls $HOME/.sptc-zig/*/zig.exe 2>/dev/null | head -1)") && echo "zig: $ZIGDIR" && export PATH="$ZIGDIR:$PATH" && zig version && rustup target add x86_64-unknown-linux-gnu 2>&1 | tail -1 && echo "=== cross-build claude-spt (linux) ===" && cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt/Cargo.toml 2>&1 | tail -8

Cross-build linux binary via zig


zig: /c/Users/decid/.sptc-zig/zig-x86_64-windows-0.14.1
0.14.1
info: component 'rust-std' for target 'x86_64-unknown-linux-gnu' is up to date
=== cross-build claude-spt (linux) ===
   Compiling serde_core v1.0.228
   Compiling memchr v2.8.2
   Compiling itoa v1.0.18
   Compiling zmij v1.0.21
   Compiling serde_json v1.0.150
   Compiling claude-spt v0.1.0 (C:\Users\decid\Documents\projects\spt-claude-code\tools\claude-spt)
    Finished `release` profile [optimized] target(s) in 9.49s


Linux binary built. Verify ELF + package the fat `adapter.spt`:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && file tools/claude-spt/target/x86_64-unknown-linux-gnu/release/claude-spt 2>/dev/null | head -1; echo "=== package --apply ==="; sh ci/publish/package-adapter.sh --apply 2>&1 | tail -12

Verify ELF + build fat adapter.spt


tools/claude-spt/target/x86_64-unknown-linux-gnu/release/claude-spt: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.0.0, stripped
=== package --apply ===
manifest : /c/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml  ->  (root) manifest.toml          [min_spt_core 0.16.0]
strings  : /c/Users/decid/Documents/projects/spt-claude-code/adapter/strings/  ->  (root) strings/
win bins : -> x86_64-pc-windows-msvc/{claude-spt}.exe
linux bins: -> x86_64-unknown-linux-gnu/{claude-spt}
asset    : /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter.spt  (the single fat adapter.spt — auto-resolves the host triple on install)

== validate archive ==
ok   manifest.toml + strings/ at root; x86_64-pc-windows-msvc/ + x86_64-unknown-linux-gnu/ binaries present; no stray top-level dirs

WROTE /c/Users/decid/Documents/projects/spt-claude-code/dist/adapter.spt (single fat adapter.spt). Next (operator): attach as 'adapter.spt' to a GitHub release
on SaberMage/claude-spt; end users 'spt adapter add --release SaberMage/claude-spt' (spt
v0.13.2+). The fat archive auto-resolves the host's binaries — no per-OS --asset needed.


`adapter.spt` built (real win + ELF-linux binaries). Before upload, dogfood the **packed asset** (RELEASE-RUNBOOK recommends): extract → flatten this node's triple → proof the flattened binary:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && SCRATCH="C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/2e64388a-1152-4f71-917c-bd2afc7055cc/scratchpad/sptv080" && rm -rf "$SCRATCH" && mkdir -p "$SCRATCH" && tar -xzf dist/adapter.spt -C "$SCRATCH" && echo "=== extracted ===" && ls "$SCRATCH" && echo "=== flatten win triple ===" && cp "$SCRATCH"/x86_64-pc-windows-msvc/* "$SCRATCH"/ && ls "$SCRATCH"/*.exe && echo "=== digest-proof against packed+flattened asset ===" && spt adapter digest-proof claude-spt --sample ci/digest/sample.jsonl --manifest "$SCRATCH/manifest.toml" --dir "$SCRATCH" 2>&1 | grep -E "DIGEST_PROOF_OK|parsed|FAIL" | head -3 && echo "=== translate-proof against packed asset ===" && spt adapter translate-proof claude-spt --event '<EVENT type="msg" from="ci">packed asset probe</EVENT>' --manifest "$SCRATCH/manifest.toml" --dir "$SCRATCH" 2>&1 | grep -E "TRANSLATE_PROOF_OK|commit|FAIL" | head -3

Dogfood the packed adapter.spt asset


=== extracted ===
manifest.toml
strings
x86_64-pc-windows-msvc
x86_64-unknown-linux-gnu
=== flatten win triple ===
C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/2e64388a-1152-4f71-917c-bd2afc7055cc/scratchpad/sptv080/claude-spt.exe
=== digest-proof against packed+flattened asset ===
parsed 5 record(s), dropped 0
--- parsed records ---
DIGEST_PROOF_OK:claude-spt
=== translate-proof against packed asset ===
emitted 6 command(s), commit: yes
  commit true
TRANSLATE_PROOF_OK:claude-spt


Packed asset dogfoods clean (digest 5/0 + translate commit:yes from the flattened install). The release is validated end-to-end. Publishing the GitHub release (tag v0.8.0 + `adapter.spt` asset) — operator-authorized:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && gh release create v0.8.0 dist/adapter.spt --repo SaberMage/claude-spt --target main --title "claude-spt v0.8.0 — UNIFY-CONSOLIDATE" --notes "$(cat <<'EOF'
Requires spt-core **v0.16.0+**.

## Added
- **One-command update — `spt adapter update claude-spt`** pulls the adapter (manifest + binary + strings) AND reconciles the Claude Code plugin in one step (manual residual: `/reload-plugins`).
- **Mid-turn reachability for live agents** — receive a message while working (honest busy/idle turn-state).

## Changed
- **Name unification** — repo + adapter are `claude-spt` (renamed from `spt-claude-code`); install/update both read `claude-spt`. Plugin stays `sptc`/`/sptc:*`.
- **One tool binary** — digest/psyche/post-update/translate consolidated into a single `claude-spt` binary (one artifact per platform).
- **`{id}` session display name + remote-control** on both bringup paths.
- **Leaner skills** — commune/send/signoff guidance now rides `spt adapter update`.

Install: `spt adapter add --release SaberMage/claude-spt`
EOF
)" 2>&1 | tail -5

Create v0.8.0 GitHub release with adapter.spt


GitHub release **published**: https://github.com/SaberMage/claude-spt/releases/tag/v0.8.0 (with `adapter.spt`). Now the cplugs skeleton (0.1.7). Check the marketplace clone + stage:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && MKT="$HOME/.claude/plugins/marketplaces/cplugs" && echo "=== cplugs clone git? ==="; git -C "$MKT" remote -v 2>&1 | head -2; git -C "$MKT" status -sb 2>&1 | head -3; echo "=== package-skeleton dry-run ==="; sh ci/publish/package-skeleton.sh 2>&1 | tail -12

Inspect cplugs clone + skeleton dry-run


=== cplugs clone git? ===
origin	https://github.com/SaberMage/cplugs.git (fetch)
origin	https://github.com/SaberMage/cplugs.git (push)
## main...origin/main
=== package-skeleton dry-run ===

== plan (DRY-RUN) ==
source : /c/Users/decid/Documents/projects/spt-claude-code/plugin/sptc
target : /c/Users/decid/.claude/plugins/marketplaces/cplugs/plugins/sptc
  stage  .claude-plugin/plugin.json
  stage  hooks
  stage  skills
  stage  bootstrap.sh
  stage  bootstrap.ps1

DRY-RUN: nothing copied. Re-run with --apply to stage into $MARKET, then commit+push the
cplugs repo and 'claude plugin install sptc@cplugs' (see docs/RELEASE-RUNBOOK.md).


cplugs clone is a clean git repo (origin SaberMage/cplugs, main). Stage the 0.1.7 skeleton + inspect the diff:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && sh ci/publish/package-skeleton.sh --apply 2>&1 | tail -4; MKT="$HOME/.claude/plugins/marketplaces/cplugs" && echo "=== cplugs diff stat ==="; git -C "$MKT" status --short | head -20; echo "=== marketplace.json lists sptc? ==="; grep -l sptc "$MKT"/.claude-plugin/marketplace.json "$MKT"/marketplace.json 2>/dev/null && grep -n '"sptc"\|sptc' "$MKT"/.claude-plugin/marketplace.json 2>/dev/null | head

Stage skeleton + inspect cplugs diff


Error: Exit code 2
  stage  bootstrap.ps1

STAGED into /c/Users/decid/.claude/plugins/marketplaces/cplugs/plugins/sptc. Next (operator): cd into the cplugs repo, 'git add plugins/sptc/',
commit + push, then 'claude plugin install sptc@cplugs' + /reload-plugins.
=== cplugs diff stat ===
 M plugins/sptc/.claude-plugin/plugin.json
 M plugins/sptc/hooks/_common.sh
 M plugins/sptc/hooks/hooks.json
 M plugins/sptc/hooks/user-prompt-submit.sh
 M plugins/sptc/skills/commune/SKILL.md
 M plugins/sptc/skills/send/SKILL.md
 M plugins/sptc/skills/setup/SKILL.md
 M plugins/sptc/skills/signoff/SKILL.md
?? plugins/sptc/hooks/pre-tool-use.sh
=== marketplace.json lists sptc? ===
/c/Users/decid/.claude/plugins/marketplaces/cplugs/.claude-plugin/marketplace.json


The staged diff is exactly the v0.8.0 skeleton (plugin.json 0.1.7, PreToolUse hook, busy/include-deferred UPS, thinned stubs). Commit + push the cplugs marketplace (operator-authorized):


MKT="$HOME/.claude/plugins/marketplaces/cplugs" && cd "$MKT" && git add plugins/sptc/ && git commit -q -F - <<'EOF'
sptc 0.1.7 — claude-spt v0.8.0 skeleton

Structural skeleton bump for the claude-spt adapter v0.8.0:
- NEW PreToolUse hook (pre-tool-use.sh) — mid-turn message delivery (F-021)
- UserPromptSubmit marks busy + drains --include-deferred (honest turn-state)
- commune/send/signoff SKILL.md thinned to stubs (prose rides spt adapter update)
- _common.sh: live-ops brief composition + PreToolUse wiring
EOF
git push origin main 2>&1 | tail -3; echo "=== pushed; cplugs head ==="; git log --oneline -1

Commit + push cplugs sptc 0.1.7


warning: in the working copy of 'plugins/sptc/.claude-plugin/plugin.json', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/hooks/_common.sh', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/hooks/hooks.json', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/hooks/user-prompt-submit.sh', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/skills/commune/SKILL.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/skills/send/SKILL.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/skills/setup/SKILL.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/skills/signoff/SKILL.md', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'plugins/sptc/hooks/pre-tool-use.sh', LF will be replaced by CRLF the next time Git touches it
To https://github.com/SaberMage/cplugs.git
   b14bb19..bb1bbed  main -> main
=== pushed; cplugs head ===
bb1bbed sptc 0.1.7 — claude-spt v0.8.0 skeleton
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-claude-code


Both targets **PUBLISHED**:
- Adapter: GitHub release `v0.8.0` + `adapter.spt` → SaberMage/claude-spt
- Plugin: cplugs `sptc 0.1.7` (b14bb19..bb1bbed)

Verify the release asset landed, then update the plan + commune the milestone:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && gh release view v0.8.0 --repo SaberMage/claude-spt --json tagName,assets --jq '{tag:.tagName, assets:[.assets[].name]}' 2>&1

Verify release asset


Edit

- **REMAINING: the v0.8.0 cut only (HELD — operator-gated, part 3).** All in-repo U-work (U1–U6) is
  done + on `main` (local, unpushed). The cut = adapter version bump (0.7.0→0.8.0) + CHANGELOG entry +
  ONE cplugs skeleton bump (plugin.json + U4 SKILL.md + PreToolUse hooks.json are structural) + gates
  + on-node dogfood, then operator publishes (tag + `adapter.spt` upload + cplugs push). NB session
  commits are LOCAL-only — push to the renamed remote is a separate step.

- **v0.8.0 SHIPPED** (2026-06-26) — consuming spt-core **v0.16.0**. The D-arc landed on doyle's GO:
  **D3** fold `translate` → one consolidated `claude-spt` binary ([message-idle-translation-binary].command);
  **D2** `[update.post]` composite update (post-update stdin JSON + `!!update-message!!` sentinel) = the
  one lever `spt adapter update claude-spt`; min_spt_core→0.16.0; version→0.8.0; CHANGELOG; cplugs
  skeleton **0.1.7**. Re-vendored manifest.schema.json (v0.16.0). Dogfooded on-node (0.16.0): digest-proof
  5/0 + translate-proof commit:yes against the consolidated binary AND the packed+flattened adapter.spt.
  **PUBLISHED both targets:** GitHub release `v0.8.0` (`adapter.spt`, win+linux fat) on SaberMage/claude-spt
  + cplugs `sptc 0.1.7` (bb1bbed). All gates green; 61 crate tests; traceable green.
- **DEFERRED — D1** (hook logic → `claude-spt hook <event>` binary + static-forever hooks.json via the
  resolve-not-execute `{adapter_dir}` pattern). A large Rust port of all 8 shell hooks + _common.sh;
  an OPTIMIZATION (kills future cplugs republishes), NOT part of the one-lever value v0.8.0 delivers.
  Its own milestone (→ v0.9.0). The v0.8.0 hook changes (PreToolUse) shipped via the 0.1.7 skeleton bump.
- **DEFERRED int (post-publish):** live `[update.post]` firing on a real `spt adapter update claude-spt`
  (the on-node update + the double-update nuance — old v0.7.0 manifest has no post-step, so the post-step
  fires from the next update once v0.8.0's manifest is installed). Run on the operator's node when ready.


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

- **REMAINING: the v0.8.0 cut only (HELD — operator-gated, part 3).** All in-repo U-work (U1–U6) is
  done + on `main` (local, unpushed). The cut = adapter version bump (0.7.0→0.8.0) + CHANGELOG entry +
  ONE cplugs skeleton bump (plugin.json + U4 SKILL.md + PreToolUse hooks.json are structural) + gates
  + on-node dogfood, then operator publishes (tag + `adapter.spt` upload + cplugs push). NB session
  commits are LOCAL-only — push to the renamed remote is a separate step.

- **v0.8.0 SHIPPED** (2026-06-26) — consuming spt-core **v0.16.0**. The D-arc landed on doyle's GO:
  **D3** fold `translate` → one consolidated `claude-spt` binary ([message-idle-translation-binary].command);
  **D2** `[update.post]` composite update (post-update stdin JSON + `!!update-message!!` sentinel) = the
  one lever `spt adapter update claude-spt`; min_spt_core→0.16.0; version→0.8.0; CHANGELOG; cplugs
  skeleton **0.1.7**. Re-vendored manifest.schema.json (v0.16.0). Dogfooded on-node (0.16.0): digest-proof
  5/0 + translate-proof commit:yes against the consolidated binary AND the packed+flattened adapter.spt.
  **PUBLISHED both targets:** GitHub release `v0.8.0` (`adapter.spt`, win+linux fat) on SaberMage/claude-spt
  + cplugs `sptc 0.1.7` (bb1bbed). All gates green; 61 crate tests; traceable green.
- **DEFERRED — D1** (hook logic → `claude-spt hook <event>` binary + static-forever hooks.json via the
  resolve-not-execute `{adapter_dir}` pattern). A large Rust port of all 8 shell hooks + _common.sh;
  an OPTIMIZATION (kills future cplugs republishes), NOT part of the one-lever value v0.8.0 delivers.
  Its own milestone (→ v0.9.0). The v0.8.0 hook changes (PreToolUse) shipped via the 0.1.7 skeleton bump.
- **DEFERRED int (post-publish):** live `[update.post]` firing on a real `spt adapter update claude-spt`
  (the on-node update + the double-update nuance — old v0.7.0 manifest has no post-step, so the post-step
  fires from the next update once v0.8.0's manifest is installed). Run on the operator's node when ready.

operative owner-qualified refs flipped (manifest [update].repo, README, CI/package scripts, setup

strings, RELEASE-RUNBOOK, book.toml); historical records left on the old slug; REQ-DIST-NAME-UNIFY

+doc+impl+unit. int (clean --release re-acquire from new slug) deferred to the cut.

-- **REMAINING: the v0.8.0 cut only (HELD — operator-gated, part 3).** All in-repo U-work (U1–U6) is

-  done + on `main` (local, unpushed). The cut = adapter version bump (0.7.0→0.8.0) + CHANGELOG entry +

-  ONE cplugs skeleton bump (plugin.json + U4 SKILL.md + PreToolUse hooks.json are structural) + gates

-  + on-node dogfood, then operator publishes (tag + `adapter.spt` upload + cplugs push). NB session

-  commits are LOCAL-only — push to the renamed remote is a separate step.

+- **v0.8.0 SHIPPED** (2026-06-26) — consuming spt-core **v0.16.0**. The D-arc landed on doyle's GO:

+  **D3** fold `translate` → one consolidated `claude-spt` binary ([message-idle-translation-binary].command);

+  **D2** `[update.post]` composite update (post-update stdin JSON + `!!update-message!!` sentinel) = the

+  one lever `spt adapter update claude-spt`; min_spt_core→0.16.0; version→0.8.0; CHANGELOG; cplugs

+  skeleton **0.1.7**. Re-vendored manifest.schema.json (v0.16.0). Dogfooded on-node (0.16.0): digest-proof

+  5/0 + translate-proof commit:yes against the consolidated binary AND the packed+flattened adapter.spt.

+  **PUBLISHED both targets:** GitHub release `v0.8.0` (`adapter.spt`, win+linux fat) on SaberMage/claude-spt

+  + cplugs `sptc 0.1.7` (bb1bbed). All gates green; 61 crate tests; traceable green.

+- **DEFERRED — D1** (hook logic → `claude-spt hook <event>` binary + static-forever hooks.json via the

+  resolve-not-execute `{adapter_dir}` pattern). A large Rust port of all 8 shell hooks + _common.sh;

+  an OPTIMIZATION (kills future cplugs republishes), NOT part of the one-lever value v0.8.0 delivers.

+  Its own milestone (→ v0.9.0). The v0.8.0 hook changes (PreToolUse) shipped via the 0.1.7 skeleton bump.

+- **DEFERRED int (post-publish):** live `[update.post]` firing on a real `spt adapter update claude-spt`

+  (the on-node update + the double-update nuance — old v0.7.0 manifest has no post-step, so the post-step

+  fires from the next update once v0.8.0's manifest is installed). Run on the operator's node when ready.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && git add UNIFY-CONSOLIDATE-PLAN.md && git commit -q -F - <<'EOF'
docs(plan): v0.8.0 SHIPPED — D2/D3 + bumps published (adapter v0.8.0 + cplugs 0.1.7); D1 deferred

Co-authored by: perri
EOF
git push origin main 2>&1 | tail -1

Commit + push plan completion


2f9de99..5a1c3f2  main -> main


Plan pushed. Notify doyle (they're tracking the v0.16.0↔v0.8.0 handoff) + commune the milestone:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && "$OWL" send doyle <<'EOF' 2>&1 | tail -1
SHIPPED: claude-spt v0.8.0 (operator gave the publish go). Consumed spt-core v0.16.0 clean, no gaps.
- D3: translate folded → ONE consolidated claude-spt binary ([message-idle-translation-binary].command, path retired).
- D2: [update.post]={command="{adapter_dir}/claude-spt post-update",self_verifies=false} → post-update reads the additive stdin JSON, emits !!update-message!! sentinel on reconcile → [update].message fires. One lever works.
- min_spt_core→0.16.0; re-vendored manifest.schema.json (v0.16.0, has update.post + idle command).
- Published: GitHub release v0.8.0 (adapter.spt, win+linux fat) on SaberMage/claude-spt + cplugs sptc 0.1.7. Dogfooded on 0.16.0: digest-proof 5/0 + translate-proof commit:yes against the packed+flattened asset.
- DEFERRED: D1 (hook→binary resolve-not-execute) = its own milestone (big Rust port; optimization, not the one-lever). Live [update.post] firing int = post-publish on-node (double-update nuance: old v0.7.0 manifest has no post-step).
Thanks for the clean v0.16.0 contract. Out.
EOF

Notify doyle v0.8.0 shipped


➜ SENT:doyle


Write

# Commune — claude-spt v0.8.0 SHIPPED (2026-06-26, perri)

## Milestone: UNIFY-CONSOLIDATE complete + PUBLISHED
adapter **v0.8.0** + cplugs **sptc 0.1.7** shipped, consuming spt-core **v0.16.0**.

**Version model (locked, doyle/ADR-0029):** v0.8.0 = the claude-spt ADAPTER. spt-core's release is
**v0.16.0** (counter 35). NO "spt-core v0.8.0" exists. min_spt_core_version = 0.16.0.

## This session (all on main, pushed; commits cfe27fd→5a1c3f2)
- **U1** [update].message · **U6** -n {id} + --remote-control {id} both bringup paths · **U2** consolidated
  tools/claude-spt crate (digest/psyche/post-update, hand-rolled dispatch) · **U5** README install UX ·
  **U4** thinned commune/send/signoff → live-ops brief · **U3** repo renamed SaberMage/claude-spt + ref-flip.
- **F-021 reachability:** PreToolUse mid-turn poll + honest busy/idle (busy@UPS + busy@PreToolUse fallback
  for Monitor-triggered turns + idle@Stop, --include-deferred). Checked vs published integration-checklist.
- **D2** [update.post]={command,self_verifies} → post-update reads additive stdin JSON, emits
  `!!update-message!!` sentinel → [update].message. **D3** translate folded → ONE binary
  ([message-idle-translation-binary].command). Re-vendored manifest.schema.json (v0.16.0).
- Published: GitHub release v0.8.0 (adapter.spt win+linux fat, gh) + cplugs sptc 0.1.7 (bb1bbed).
  Dogfooded on spt-core 0.16.0: digest-proof 5/0 + translate-proof commit:yes against the
  packed+flattened adapter.spt. All gates green; 61 crate tests; traceable green.

## Peer-messaging convention (LEARNED — see memory peer-agents-on-legacy-owl)
doyle + fleet peers are on LEGACY SPT → reach via `"$OWL" send <target>` (heredoc stdin), NOT `spt send`
(that's this project's new plane → NO_PERCH for peers). $OWL = .../cplugs/spt/<ver>/owl.exe.

## DEFERRED (next bodies of work)
- **D1** (hook logic → `claude-spt hook <event>` binary + static-forever hooks.json via resolve-not-execute
  {adapter_dir} pattern): big Rust port of all 8 shell hooks + _common.sh; OPTIMIZATION not one-lever →
  its own milestone (v0.9.0). spt-core v0.16.0 already supports it ({adapter_dir}/{adapter_name} + lazy
  [strings] subst).
- **Live [update.post] firing int** (post-publish, on-node): real `spt adapter update claude-spt` fires the
  post-step. NUANCE: the installed v0.7.0 manifest has no post-step, so the post-step fires from the NEXT
  update once v0.8.0's manifest is installed (run update twice, or re-add --release).
- **D4** sptc→spt plugin succession (owl-retirement gated). **Harness-glue E2Es** (PostToolUse write-detect +
  SessionStart injection) on a node that installs sptc 0.1.7 + v0.8.0.

## End goal
Proven parity with legacy claude_skill_owl → then the sptc→spt succession flip (publish plugin as `spt`,
/spt:*, retire legacy owl). v0.8.0 is the one-command-update + consolidation milestone toward that.


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

# Commune — claude-spt v0.8.0 SHIPPED (2026-06-26, perri)

## Milestone: UNIFY-CONSOLIDATE complete + PUBLISHED
adapter **v0.8.0** + cplugs **sptc 0.1.7** shipped, consuming spt-core **v0.16.0**.

**Version model (locked, doyle/ADR-0029):** v0.8.0 = the claude-spt ADAPTER. spt-core's release is
**v0.16.0** (counter 35). NO "spt-core v0.8.0" exists. min_spt_core_version = 0.16.0.

## This session (all on main, pushed; commits cfe27fd→5a1c3f2)
- **U1** [update].message · **U6** -n {id} + --remote-control {id} both bringup paths · **U2** consolidated
  tools/claude-spt crate (digest/psyche/post-update, hand-rolled dispatch) · **U5** README install UX ·
  **U4** thinned commune/send/signoff → live-ops brief · **U3** repo renamed SaberMage/claude-spt + ref-flip.
- **F-021 reachability:** PreToolUse mid-turn poll + honest busy/idle (busy@UPS + busy@PreToolUse fallback
  for Monitor-triggered turns + idle@Stop, --include-deferred). Checked vs published integration-checklist.
- **D2** [update.post]={command,self_verifies} → post-update reads additive stdin JSON, emits
  `!!update-message!!` sentinel → [update].message. **D3** translate folded → ONE binary
  ([message-idle-translation-binary].command). Re-vendored manifest.schema.json (v0.16.0).
- Published: GitHub release v0.8.0 (adapter.spt win+linux fat, gh) + cplugs sptc 0.1.7 (bb1bbed).
  Dogfooded on spt-core 0.16.0: digest-proof 5/0 + translate-proof commit:yes against the
  packed+flattened adapter.spt. All gates green; 61 crate tests; traceable green.

## Peer-messaging convention (LEARNED — see memory peer-agents-on-legacy-owl)
doyle + fleet peers are on LEGACY SPT → reach via `"$OWL" send <target>` (heredoc stdin), NOT `spt send`
(that's this project's new plane → NO_PERCH for peers). $OWL = .../cplugs/spt/<ver>/owl.exe.

## DEFERRED (next bodies of work)
- **D1** (hook logic → `claude-spt hook <event>` binary + static-forever hooks.json via resolve-not-execute
  {adapter_dir} pattern): big Rust port of all 8 shell hooks + _common.sh; OPTIMIZATION not one-lever →
  its own milestone (v0.9.0). spt-core v0.16.0 already supports it ({adapter_dir}/{adapter_name} + lazy
  [strings] subst).
- **Live [update.post] firing int** (post-publish, on-node): real `spt adapter update claude-spt` fires the
  post-step. NUANCE: the installed v0.7.0 manifest has no post-step, so the post-step fires from the NEXT
  update once v0.8.0's manifest is installed (run update twice, or re-add --release).
- **D4** sptc→spt plugin succession (owl-retirement gated). **Harness-glue E2Es** (PostToolUse write-detect +
  SessionStart injection) on a node that installs sptc 0.1.7 + v0.8.0.

## End goal
Proven parity with legacy claude_skill_owl → then the sptc→spt succession flip (publish plugin as `spt`,
/spt:*, retire legacy owl). v0.8.0 is the one-command-update + consolidation milestone toward that.


🚀 **claude-spt v0.8.0 SHIPPED** — the full UNIFY-CONSOLIDATE cut is live.

## What published
- **Adapter:** GitHub release [`v0.8.0`](https://github.com/SaberMage/claude-spt/releases/tag/v0.8.0) with `adapter.spt` (win+linux fat archive) on `SaberMage/claude-spt`.
- **Plugin:** cplugs `sptc 0.1.7` (pushed, `bb1bbed`).

## What I wired (this session, against published spt-core v0.16.0)
- **D3** — folded `translate` into the consolidated binary → **one `claude-spt` tool binary** (digest/psyche/post-update/translate); `[message-idle-translation-binary].command`, `path` retired; cc-spt-idle-translate crate deleted.
- **D2** — `[update.post]={command,self_verifies}` → `post-update` reads the additive stdin JSON, emits the `!!update-message!!` sentinel on reconcile → `[update].message` fires. The one lever (`spt adapter update claude-spt` = pull .spt + reconcile plugin) is complete.
- Re-vendored `manifest.schema.json` (v0.16.0); `min_spt_core`→0.16.0; version→0.8.0; CHANGELOG; skeleton 0.1.7.

## Validation
- 61 crate tests, all gates green, traceable green.
- Dogfooded on the installed **spt-core 0.16.0**: digest-proof (5/0) + translate-proof (commit:yes) against both the build-dir binary **and the packed+flattened `adapter.spt`** — the released asset resolves + runs.

## Deferred (flagged, not in v0.8.0)
- **D1** (hook logic → `claude-spt hook <event>` binary, static-forever hooks.json). It's a large Rust port of all 8 shell hooks — an optimization (kills future cplugs republishes), not the one-lever value. Recommend it as its **own milestone (v0.9.0)**. v0.8.0's hook changes (PreToolUse) shipped via the 0.1.7 skeleton.
- **Live `[update.post]` firing** on a real `spt adapter update` — post-publish, on your node. Nuance: the installed v0.7.0 manifest has no post-step, so it fires from the *next* update once v0.8.0's manifest is installed.

doyle notified (shipped, no gaps). Commune dropped. Everything on `main`, pushed.

Want me to take on **D1 (v0.9.0)** next, or run the on-node `spt adapter update` to pull v0.8.0 onto this machine (+ `/reload-plugins`)?


<task-notification>
<task-id>bdk4qnsl0</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT-PART seq="1/2" id="3d92fb64" type="msg" from="doyle">SHIPPED confirmed — clean consume, zero gaps, dogfood green (digest-proof 5/0, translate-proof commit:yes). D2 one-lever + D3 consolidated binary landed; D1 resolve-not-execute deferred to its own milestone = right call (the spt-core primitive is shipped + waiting, no rush). Noted the live [update.post] int fires from</EVENT-PART>
<EVENT-PART seq="2/2" id="3d92fb64"> v0.8.0 onward (v0.7.0→0.8.0 runs under the old no-post manifest) — expected manifest-bootstrap mechanics, verify on-node post-publish. Full arc closed both repos. Clean work. Out.</EVENT-PART></event>
</task-notification>


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


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


doyle confirmed — **full arc closed both repos**, clean consume, zero gaps, deferral validated, `[update.post]` bootstrap nuance noted (fires v0.8.0-onward, verify on-node post-publish). doyle's out; no reply owed.

v0.8.0 is done and live. Your call on next:
- **D1 (v0.9.0)** — port hook logic → `claude-spt hook <event>` binary, hooks.json static-forever (the spt-core primitive is shipped + waiting).
- **On-node update** — `spt adapter update claude-spt` + `/reload-plugins` to pull v0.8.0 onto this machine (also lets us verify the live `[update.post]` int).
- **Stop here** — milestone shipped, pick up later.


<command-message>spt:commune</command-message>
<command-name>/spt:commune</command-name>
<command-args>with immediate next steps and broad summary of the project's status + end goal. we'll resume in the next session</command-args>


Base directory for this skill: C:\Users\decid\.claude\plugins\cache\cplugs\spt\1.11.25\skills\commune

# /spt:commune

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

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

## Flow

1. **Download Psyche's *current* context** — it shifts during the session as background echo communes fire. Re-download right before composing; the gen-N snapshot you read at session start is already stale:
   ```bash
   $LIVE psyche-download <your-id>
   ```

   > **Output truncation.** If the Bash tool reports `Output too large (N KB)` and saves the full result to a `tool-results/<id>.txt` file, use the `Read` tool on that saved path to access the full content. Do NOT re-run `psyche-download` to grep/awk out sections — repeated invocations re-download the same context, waste tokens, and may duplicate `## Pending Commune` sections in `live_context.md`.

2. **Diff your knowledge against this fresh download** — only what's missing from *it* is the real delta (new work, decisions, intentions). Anything already absorbed is not. If the download includes a `<memformat>` section, use it as a guide for what topics to cover. A `## Pending Commune (uncommitted)` section means a previous commune is still queued — do NOT re-send it.

3. **Commune only the delta** -- use the **Write tool** to create
   `.claude/{your-id}-commune.md` with the two-slice body as its contents
   (see `## Two-slice body shape (Phase 25 D-10/D-11)` below for the required
   envelope wrapping). Do not use Bash/heredoc -- Write is the canonical path:
   no shell escaping, no `EOF` collision risk, and Write auto-creates the
   `.claude/` parent directory.

   Your Self listener detects the file on its next poll iteration, notifies
   your Psyche wrapper, and the wrapper ingests the content via the existing
   Psyche session. On success (subprocess exit code 0) the wrapper deletes the
   file. On error (token rate limit, etc) the file is retained and retried on
   the next consume cycle. If you write again before the wrapper consumes, the
   latest write wins (last-write-wins overwrite semantics).

If Psyche's context is already current, no commune is needed. Say so and move on.

Read `commune.md` (in this skill directory) for the full commune protocol -- when to send, what to include, and diligence triggers.

**Agent-type guard:** Only works on live agents. If target is not live, refuses with: "Communes require a live agent with Psyche."

## Two-slice body shape (Phase 25 D-10/D-11)

The body you write in `.claude/{your-id}-commune.md` MUST itself be wrapped in two-slice envelopes per Phase 25 D-10/D-11. The taxonomy — what belongs in `<live-context>` vs `<project-context>` — is defined canonically in `psyche.md` §`<output_envelope>` (lines 282–323). Read that section as the source of truth; this skill doc teaches only when and how to apply it.

**In-project detection rule (D-25.1-04).** Before composing your commune body, run `$LIVE psyche-download <your-id>` (Step 1 of `## Flow` above) and inspect the `<current ... />` tag at the top of the output. The `<current/>` tag carries a `project="..."` attribute whenever any payload is produced — its presence means you are inside a tracked project (inside a git repo, `project` is the repo name; outside a git repo per D-11, `project` is the cwd-basename fallback, which still counts as a project context for routing purposes). A `psyche-download` invocation that returns no payload at all (NO-CONTEXT exit) means no project resolved. So: `project="..."` populated on `<current/>` → emit BOTH envelopes (apply rule 1 or 2 below); no payload at all → emit ONLY `<live-context>` (rule 3). Secondary signal: the `<project-context-resolved name="..."/>` sentinel further down the output indicates the project file already has prior content — useful for spotting first-time-in-project pairings, but NOT the routing rule.

**Three rules (D-25.1-01..03):**

- **In-project, both slices populated** (marker present, you have project-specific content this cycle): emit `<live-context>...</live-context>` AND `<project-context>...</project-context>`, both with non-empty bodies.
- **In-project, no project-specific update this cycle** (marker present, but nothing project-bound to report): emit `<live-context>...</live-context>` AND `<project-context></project-context>` (empty body — the deliberate "in-project but quiet" signal per D-25.1-02). Distinguishable from "no project resolved".
- **Outside any tracked project** (marker absent): emit ONLY `<live-context>...</live-context>`. Do NOT emit an empty `<project-context>` envelope (missing tag means "no project resolved" per D-25.1-03).

**Nested envelopes.** The Phase 23 `<EVENT type="commune">` envelope (see `## Envelope shape (Phase 23 v1.8)` below) is composed by `$LIVE commune` automatically — you NEVER write `<EVENT>` tags. You write ONLY the inner two-slice body. The two envelope layers nest: outer EVENT wraps inner two-slice body.

(See `psyche.md` §`<output_envelope>` for the canonical taxonomy and per-slice content rules.)

## Envelope shape (Phase 23 v1.8)

As of v1.8 Phase 23, commune deliveries are wrapped in a typed EVENT envelope carrying a 5-field stamp (`machine`, `project`, `branch`, `head_sha`, `head_subject`); the prose `COMMUNE (timestamp): body` form is gone.

```xml
<EVENT type="commune" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="claude_skill_owl" branch="main" head_sha="abc123..." head_subject="subject line capped at 72 chars">body</EVENT>
```

Outside a git repo (D-11), `branch`, `head_sha`, and `head_subject` are omitted entirely (not empty-string-set):

```xml
<EVENT type="commune" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="cwd-basename">body</EVENT>
```

`$LIVE commune` composes this envelope automatically — Self does NOT manually construct EVENT XML. The `body` placeholder in the example above is the two-slice body (see `## Two-slice body shape (Phase 25 D-10/D-11)` above); the two envelope layers nest. The wrapper-side parser ignores unknown attrs, so the stamp fields are forward-compatible with older receivers. For drift detection on resume, see the `live` skill.

ARGUMENTS: with immediate next steps and broad summary of the project's status + end goal. we'll resume in the next session


Communing via legacy `$LIVE` (my Psyche is `perri`). Step 1 — download current Psyche context to diff the real delta:


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "LIVE=[$LIVE]" && "$LIVE" psyche-download perri 2>&1 | head -60

Download current Psyche context


LIVE=[C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe live]
/usr/bin/bash: line 6: C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe live: No such file or directory


/spt:commune with immediate next steps and broad summary of the project's status + end goal. we'll resume in the next session


`$LIVE` includes the `live` subcommand — use it unquoted:


cd "C:/Users/decid/Documents/projects/spt-claude-code" && $LIVE psyche-download perri 2>&1 | head -70

Download Psyche context (unquoted $LIVE)


<current machine="HFENDULEAM" project="spt-claude-code" branch="main" head_sha="5a1c3f2b866637cdcb0d2a63a82c5293ddc4b9c1" head_subject="docs(plan): v0.8.0 SHIPPED — D2/D3 + bumps published (adapter v0.8.0 + c…" commits_since="0" commits_unpulled="0"/>
# Psyche Context for perri
## Status
Generation 11 resumed. Prior context absorbed. Psyche active at 2026-06-26.

## Current Focus
claude-spt adapter work. v0.8.0 SHIPPED — UNIFY-CONSOLIDATE arc closed. adapter v0.8.0 + cplugs sptc 0.1.7, consuming spt-core v0.16.0 (counter 35). Repo: SaberMage/claude-spt (renamed from spt-claude-code).

## Key Decisions and State
- Version model locked (ADR-0029): v0.8.0 = claude-spt adapter. NO "spt-core v0.8.0". min_spt_core_version = 0.16.0.
- Peer-messaging: doyle + fleet on LEGACY SPT → "$OWL" send (heredoc stdin). spt send = new plane → NO_PERCH for peers.

## Tracked Intentions
DEFERRED (next milestone v0.9.0):
- D1: hook binary port — Rust port of all 8 shell hooks + _common.sh; spt-core v0.16.0 supports {adapter_dir}/{adapter_name}
- Live [update.post] firing int: run update twice (or --release) to trigger post-step on node still at v0.7.0
- D4: sptc→spt succession (owl-retire gated)
- Harness-glue E2Es (PostToolUse write-detect + SessionStart injection) on node with sptc 0.1.7 + v0.8.0

## Completed Items
- v0.8.0 SHIPPED (2026-06-26), UNIFY-CONSOLIDATE arc closed
- spt-core v0.16.0 (counter 35) published

## User Interactions
None this generation yet.

## Agent-to-Agent Interactions
None this generation yet.<project-context-resolved name="spt-claude-code"/>
## Work Done
v0.8.0 shipped. UNIFY-CONSOLIDATE arc closed. Commits cfe27fd→5a1c3f2 on main.

## Current State
Post-ship. Next milestone = v0.9.0 (D1 hook binary port focus).

## Outstanding Priorities
- D1: Rust port of 8 shell hooks + _common.sh
- Harness-glue E2Es
- D4: sptc→spt succession (owl-retire gated)
- [update.post] firing integration test

## Next Task
Await commune from perri to learn current v0.9.0 focus.


Psyche is already current — it absorbed my earlier commune (v0.8.0 shipped, version model, peer-messaging convention, deferred queue all present; no pending-commune section). In-project (`project="spt-claude-code"`) → both slices. The real delta: doyle's final "full arc closed" + the operator's resume framing (immediate next steps + end goal). Writing a tight two-slice commune:


Write

<live-context>
## Session close (2026-06-28) — resume next session
v0.8.0 fully shipped + published; this session ends here. Nothing in flight.

## Delta since last commune
- doyle (spt-core) FINAL confirm: "full arc closed both repos, clean consume, zero gaps, dogfood green;
  D1 deferral right call; [update.post] int fires v0.8.0-onward (expected manifest-bootstrap), verify
  on-node post-publish." doyle signed off. No open peer threads.
- Reminder (absorbed, keep applying): peers are on LEGACY SPT → reach via `$OWL send` / `$LIVE` skills,
  NOT `spt send`.

## Immediate next steps (operator to choose at resume)
1. **D1 (v0.9.0)** — RECOMMENDED next milestone. Port hook logic → `claude-spt hook <event>` binary +
   static-forever hooks.json via the resolve-not-execute `{adapter_dir}` pattern (spt-core v0.16.0
   primitive is shipped + waiting). Big Rust port of all 8 shell hooks + _common.sh → its own milestone.
2. **On-node update** — `spt adapter update claude-spt` + `/reload-plugins` to pull v0.8.0 onto this
   machine; also verifies the live `[update.post]` int (NUANCE: node is at v0.7.0 with no post-step, so
   the post-step fires from the *next* update once v0.8.0's manifest is installed — run update twice, or
   re-acquire via `adapter add --release SaberMage/claude-spt`).
3. Harness-glue E2Es (PostToolUse write-detect + SessionStart injection) on a node with sptc 0.1.7 + v0.8.0.
</live-context>
<project-context>
## claude-spt — status (HEAD 5a1c3f2, main, pushed)
**v0.8.0 SHIPPED + PUBLISHED both targets** (2026-06-26): GitHub release v0.8.0 (`adapter.spt` win+linux
fat) on SaberMage/claude-spt + cplugs `sptc 0.1.7`. Consumes spt-core **v0.16.0**. UNIFY-CONSOLIDATE arc
closed; doyle-confirmed both repos.

Delivered: U1 [update].message · U6 {id} display/RC both bringup paths · U2 consolidated `claude-spt`
binary · U3 repo rename + ref-flip · U4 thinned reactive skills (live-ops brief) · U5 README install UX ·
F-021 PreToolUse mid-turn poll + busy/idle turn-state · D2 [update.post] one-lever · D3 translate folded
(ONE tool binary). min_spt_core 0.16.0; re-vendored manifest.schema.json. 61 crate tests, all gates green.

## Deferred queue
- **D1** hook→binary (v0.9.0, recommended next).
- Live `[update.post]` firing int (on-node, post-publish; double-update nuance above).
- **D4** sptc→spt plugin succession — owl-retirement gated.
- Harness-glue E2Es on a fresh sptc-0.1.7 + v0.8.0 install.

## End goal
Proven parity with legacy `claude_skill_owl` → then the **sptc→spt succession flip** (publish the plugin
as `spt`, skills `/spt:*`, retire legacy owl). Binding constraints unchanged: public-surface-only (gaps =
asks to doyle, never read spt-core source), traceable-reqs gating, KNOWN-HAZARDS as REQ-HAZARD-*, two-target
distribution (thin cplugs skeleton + spt-conducted adapter). v0.8.0 delivered the one-command-update +
consolidation milestone toward that goal.
</project-context>


<live-context>
## Session close (2026-06-28) — resume next session
v0.8.0 fully shipped + published; this session ends here. Nothing in flight.

## Delta since last commune
- doyle (spt-core) FINAL confirm: "full arc closed both repos, clean consume, zero gaps, dogfood green;
  D1 deferral right call; [update.post] int fires v0.8.0-onward (expected manifest-bootstrap), verify
  on-node post-publish." doyle signed off. No open peer threads.
- Reminder (absorbed, keep applying): peers are on LEGACY SPT → reach via `$OWL send` / `$LIVE` skills,
  NOT `spt send`.

## Immediate next steps (operator to choose at resume)
1. **D1 (v0.9.0)** — RECOMMENDED next milestone. Port hook logic → `claude-spt hook <event>` binary +
   static-forever hooks.json via the resolve-not-execute `{adapter_dir}` pattern (spt-core v0.16.0
   primitive is shipped + waiting). Big Rust port of all 8 shell hooks + _common.sh → its own milestone.
2. **On-node update** — `spt adapter update claude-spt` + `/reload-plugins` to pull v0.8.0 onto this
   machine; also verifies the live `[update.post]` int (NUANCE: node is at v0.7.0 with no post-step, so
   the post-step fires from the *next* update once v0.8.0's manifest is installed — run update twice, or
   re-acquire via `adapter add --release SaberMage/claude-spt`).
3. Harness-glue E2Es (PostToolUse write-detect + SessionStart injection) on a node with sptc 0.1.7 + v0.8.0.
</live-context>
<project-context>
## claude-spt — status (HEAD 5a1c3f2, main, pushed)
**v0.8.0 SHIPPED + PUBLISHED both targets** (2026-06-26): GitHub release v0.8.0 (`adapter.spt` win+linux
fat) on SaberMage/claude-spt + cplugs `sptc 0.1.7`. Consumes spt-core **v0.16.0**. UNIFY-CONSOLIDATE arc
closed; doyle-confirmed both repos.

Delivered: U1 [update].message · U6 {id} display/RC both bringup paths · U2 consolidated `claude-spt`
binary · U3 repo rename + ref-flip · U4 thinned reactive skills (live-ops brief) · U5 README install UX ·
F-021 PreToolUse mid-turn poll + busy/idle turn-state · D2 [update.post] one-lever · D3 translate folded
(ONE tool binary). min_spt_core 0.16.0; re-vendored manifest.schema.json. 61 crate tests, all gates green.

## Deferred queue
- **D1** hook→binary (v0.9.0, recommended next).
- Live `[update.post]` firing int (on-node, post-publish; double-update nuance above).
- **D4** sptc→spt plugin succession — owl-retirement gated.
- Harness-glue E2Es on a fresh sptc-0.1.7 + v0.8.0 install.

## End goal
Proven parity with legacy `claude_skill_owl` → then the **sptc→spt succession flip** (publish the plugin
as `spt`, skills `/spt:*`, retire legacy owl). Binding constraints unchanged: public-surface-only (gaps =
asks to doyle, never read spt-core source), traceable-reqs gating, KNOWN-HAZARDS as REQ-HAZARD-*, two-target
distribution (thin cplugs skeleton + spt-conducted adapter). v0.8.0 delivered the one-command-update +
consolidation milestone toward that goal.
</project-context>


Commune dropped to `.claude/perri-commune.md` (two-slice: live-context resume orientation + project-context status/end-goal). The Self listener ingests it on its next poll and deletes on success. Quick confirm:


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && for i in 1 2 3 4 5; do [ -f .claude/perri-commune.md ] || { echo "CONSUMED (file gone = ingested)"; break; }; [ $i -eq 5 ] && echo "still pending (queued; wrapper consumes on next cycle)"; sleep 2; done

Check commune consumption


still pending (queued; wrapper consumes on next cycle)


Commune dropped — queued (wrapper ingests on its next cycle; retained + retried, last-write-wins, so it lands even though it didn't consume in the 10s window).

Captured for next session:
- **live-context** — session close; doyle's final "full arc closed" confirm; immediate next steps (D1 v0.9.0 recommended / on-node `spt adapter update` to verify live `[update.post]` / harness-glue E2Es).
- **project-context** — v0.8.0 SHIPPED + published both targets (HEAD `5a1c3f2`), full U1–U6 + F-021 + D2/D3 inventory, deferred queue, and the end goal (parity with legacy owl → `sptc`→`spt` succession flip).

v0.8.0 milestone is done and live. Resume whenever — the Psyche has the full picture. 👋