1	#!/bin/sh
2	# Pack the SINGLE MULTI-PLATFORM `adapter.spt` release asset — the end-user distribution acquired by
3	# `spt adapter add --release SaberMage/spt-claude-code` and carried by the `[update] gh_release`
4	# avenue. ONE fat archive covers every supported platform (ADR-0024 W1, spt-core >= 0.13.2):
5	# [impl->REQ-DIST-ADAPTER-RELEASE] [impl->REQ-DIST-ADAPTER-PEROS]
6	#
7	#   adapter.spt (tar.gz)
8	#   ├── manifest.toml                 ← SHARED, at archive root (renamed from claude-spt.toml)
9	#   ├── strings/                      ← SHARED, at archive root
10	#   ├── x86_64-pc-windows-msvc/       ← this triple's binaries, mirroring the flat-root tree
11	#   │   ├── claude-spt-digest.exe · claude-spt-psyche.exe · cc-spt-idle-translate.exe
12	#   └── x86_64-unknown-linux-gnu/
13	#       └── claude-spt-digest · claude-spt-psyche · cc-spt-idle-translate
14	#
15	# spt-core CLASSIFIES on top-level entry names: a known target-triple dir ⇒ multi-platform; it places
16	# the shared-root entries + FLATTENS this node's <triple>/* into the install dir (so a bare-name
17	# command token still resolves at <install_dir>/<program>, REQ-INSTALL-11). This RETIRES the F-014
18	# per-OS stopgap (the old single-OS `adapter.spt`-by-default that broke on a foreign host).
19	#
20	# CONSTRAINTS (doyle, extract_release_archive): only x86_64-pc-windows-msvc + x86_64-unknown-linux-gnu
21	# are recognized triples — an UNRECOGNIZED dir name is silently treated as a shared-root entry and
22	# lands flat (no error), so for platforms beyond these two ship a separate single-triple asset via
23	# `--asset` instead of adding a dir here. A fat archive REQUIRES the manifest's
24	# min_spt_core_version >= 0.13.2 (older spt-core cannot read it). DRY-RUN by default; never uploads.
25	#
26	# Usage:
27	#   package-adapter.sh            # dry-run: validate + print the archive plan, write nothing
28	#   package-adapter.sh --apply    # write dist/adapter.spt (validated)
29	set -u
30	HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
31	ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
32	ADAPTER="$ROOT/adapter"
33	MANIFEST="$ADAPTER/claude-spt.toml"   # renamed to manifest.toml INSIDE the archive (root-only rule)
34	STRINGS="$ADAPTER/strings"
35	TOOLS="$ROOT/tools"
36	OUT="${ADAPTER_SPT_OUT:-$ROOT/dist/adapter.spt}"   # overridable so the unit test writes to a tmp file
37	APPLY=0
38	[ "${1:-}" = "--apply" ] && APPLY=1
39
40	# The recognized triples and where each platform's release binaries live. A native Windows build lands
41	# in target/release; the Linux build is cross-compiled (cargo-zigbuild) into
42	# target/x86_64-unknown-linux-gnu/release. Override SPTC_WIN_RELSUB / SPTC_LINUX_RELSUB for a
43	# non-default layout. The three tool binaries each live in their own crate under tools/.
44	WIN_TRIPLE=x86_64-pc-windows-msvc
45	LINUX_TRIPLE=x86_64-unknown-linux-gnu
46	WIN_RELSUB="${SPTC_WIN_RELSUB:-release}"
47	LINUX_RELSUB="${SPTC_LINUX_RELSUB:-$LINUX_TRIPLE/release}"
48	BINS="claude-spt-digest claude-spt-psyche cc-spt-idle-translate"
49
50	# Validate the manifest first — refuse to ship an invalid adapter.
51	echo "== validate manifest =="
52	if ! sh "$ROOT/ci/manifest/check-manifest.sh"; then
53	  echo "REFUSING to package: manifest failed schema validation (fix above, then retry)." >&2
54	  exit 1
55	fi
56
57	# A fat archive is only readable on spt-core >= 0.13.2 — the manifest MUST declare that floor.
58	floor=$(grep -E '^min_spt_core_version' "$MANIFEST" | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
59	case "$floor" in
60	  0.13.2|0.13.[3-9]*|0.1[4-9].*|0.[2-9][0-9].*|[1-9].*) : ;;
61	  *) echo "REFUSING to package: min_spt_core_version is '$floor' but a multi-platform fat .spt needs >= 0.13.2 (doyle)." >&2; exit 1 ;;
62	esac
63
64	# Require BOTH platforms' release binaries (NOT auto-copied by register — they ride the archive).
65	echo
66	echo "== tool binaries =="
67	missing=0
68	win_path()   { echo "$TOOLS/$1/target/$WIN_RELSUB/$1.exe"; }
69	linux_path() { echo "$TOOLS/$1/target/$LINUX_RELSUB/$1"; }
70	for b in $BINS; do
71	  for p in "$(win_path "$b")" "$(linux_path "$b")"; do
72	    if [ -f "$p" ]; then echo "  ok    $p"; else echo "  MISS  $p"; missing=1; fi
73	  done
74	done
75	if [ "$missing" -ne 0 ]; then
76	  echo "REFUSING to package: build BOTH platforms first — Windows: sh ci/{digest,psyche,idle-translate}/build.sh;" >&2
77	  echo "Linux (cross): cargo-zigbuild --release --target $LINUX_TRIPLE for each tool crate (see docs/RELEASE-RUNBOOK.md)." >&2
78	  exit 1
79	fi
80
81	echo
82	echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
83	echo "manifest : $MANIFEST  ->  (root) manifest.toml          [min_spt_core $floor]"
84	echo "strings  : $STRINGS/  ->  (root) strings/"
85	echo "win bins : -> $WIN_TRIPLE/{$(echo $BINS | tr ' ' ',')}.exe"
86	echo "linux bins: -> $LINUX_TRIPLE/{$(echo $BINS | tr ' ' ',')}"
87	echo "asset    : $OUT  (the single fat adapter.spt — auto-resolves the host triple on install)"
88
89	if [ "$APPLY" -ne 1 ]; then
90	  echo
91	  echo "DRY-RUN: nothing written. Re-run with --apply to write $OUT, then attach it as 'adapter.spt'"
92	  echo "to a GitHub release on the monorepo. End users: 'spt adapter add --release SaberMage/spt-claude-code'"
93	  echo "(default asset adapter.spt). Needs spt v0.13.2+. See docs/RELEASE-RUNBOOK.md."
94	  exit 0
95	fi
96
97	# Stage the archive ROOT in a temp dir, then tar from it so paths are root-relative (no leading dirs).
98	STAGE=$(mktemp -d "${TMPDIR:-/tmp}/sptc-adapter.XXXXXX") || { echo "FATAL: mktemp failed"; exit 2; }
99	trap 'rm -rf "$STAGE"' EXIT
100	cp "$MANIFEST" "$STAGE/manifest.toml"
101	cp -r "$STRINGS" "$STAGE/strings"
102	mkdir -p "$STAGE/$WIN_TRIPLE" "$STAGE/$LINUX_TRIPLE"
103	for b in $BINS; do
104	  cp "$(win_path "$b")"   "$STAGE/$WIN_TRIPLE/$b.exe"
105	  cp "$(linux_path "$b")" "$STAGE/$LINUX_TRIPLE/$b"
106	done
107
108	mkdir -p "$(dirname "$OUT")" || { echo "FATAL: cannot create $(dirname "$OUT")"; exit 2; }
109	( cd "$STAGE" && tar -czf "$OUT" manifest.toml strings "$WIN_TRIPLE" "$LINUX_TRIPLE" ) \
110	  || { echo "FATAL: tar failed"; exit 2; }
111
112	# Self-validate the produced archive: shared manifest at ROOT + BOTH triple dirs present.
113	echo
114	echo "== validate archive =="
115	listing=$(tar -tzf "$OUT")
116	fatal=0
117	echo "$listing" | grep -qx "manifest.toml" || { echo "FATAL: manifest.toml not at archive root" >&2; fatal=1; }
118	echo "$listing" | grep -q "^$WIN_TRIPLE/claude-spt-psyche.exe$" || { echo "FATAL: missing $WIN_TRIPLE/ binaries" >&2; fatal=1; }
119	echo "$listing" | grep -q "^$LINUX_TRIPLE/claude-spt-psyche$"   || { echo "FATAL: missing $LINUX_TRIPLE/ binaries" >&2; fatal=1; }
120	# Guard the footgun: no UNRECOGNIZED top-level dir (would silently flatten as a shared-root entry).
121	badtop=$(echo "$listing" | awk -F/ 'NF>1 {print $1}' | sort -u | grep -vE "^(strings|$WIN_TRIPLE|$LINUX_TRIPLE)$" || true)
122	[ -n "$badtop" ] && { echo "FATAL: unrecognized top-level dir(s) [$badtop] — spt-core would mis-place them flat" >&2; fatal=1; }
123	[ "$fatal" -eq 0 ] || exit 1
124	echo "ok   manifest.toml + strings/ at root; $WIN_TRIPLE/ + $LINUX_TRIPLE/ binaries present; no stray top-level dirs"
125
126	echo
127	echo "WROTE $OUT (single fat adapter.spt). Next (operator): attach as 'adapter.spt' to a GitHub release"
128	echo "on SaberMage/spt-claude-code; end users 'spt adapter add --release SaberMage/spt-claude-code' (spt"
129	echo "v0.13.2+). The fat archive auto-resolves the host's binaries — no per-OS --asset needed."
130	exit 0
131

#!/bin/sh
# Pack the SINGLE MULTI-PLATFORM `adapter.spt` release asset — the end-user distribution acquired by
# `spt adapter add --release SaberMage/spt-claude-code` and carried by the `[update] gh_release`
# avenue. ONE fat archive covers every supported platform (ADR-0024 W1, spt-core >= 0.13.2):
# [impl->REQ-DIST-ADAPTER-RELEASE] [impl->REQ-DIST-ADAPTER-PEROS]
#
#   adapter.spt (tar.gz)
#   ├── manifest.toml                 ← SHARED, at archive root (renamed from claude-spt.toml)
#   ├── strings/                      ← SHARED, at archive root
#   ├── x86_64-pc-windows-msvc/       ← this triple's binaries, mirroring the flat-root tree
#   │   ├── claude-spt-digest.exe · claude-spt-psyche.exe · cc-spt-idle-translate.exe
#   └── x86_64-unknown-linux-gnu/
#       └── claude-spt-digest · claude-spt-psyche · cc-spt-idle-translate
#
# spt-core CLASSIFIES on top-level entry names: a known target-triple dir ⇒ multi-platform; it places
# the shared-root entries + FLATTENS this node's <triple>/* into the install dir (so a bare-name
# command token still resolves at <install_dir>/<program>, REQ-INSTALL-11). This RETIRES the F-014
# per-OS stopgap (the old single-OS `adapter.spt`-by-default that broke on a foreign host).
#
# CONSTRAINTS (doyle, extract_release_archive): only x86_64-pc-windows-msvc + x86_64-unknown-linux-gnu
# are recognized triples — an UNRECOGNIZED dir name is silently treated as a shared-root entry and
# lands flat (no error), so for platforms beyond these two ship a separate single-triple asset via
# `--asset` instead of adding a dir here. A fat archive REQUIRES the manifest's
# min_spt_core_version >= 0.13.2 (older spt-core cannot read it). DRY-RUN by default; never uploads.
#
# Usage:
#   package-adapter.sh            # dry-run: validate + print the archive plan, write nothing
#   package-adapter.sh --apply    # write dist/adapter.spt (validated)
set -u
HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
ADAPTER="$ROOT/adapter"
MANIFEST="$ADAPTER/claude-spt.toml"   # renamed to manifest.toml INSIDE the archive (root-only rule)
STRINGS="$ADAPTER/strings"
TOOLS="$ROOT/tools"
OUT="${ADAPTER_SPT_OUT:-$ROOT/dist/adapter.spt}"   # overridable so the unit test writes to a tmp file
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1

# The recognized triples and where each platform's release binaries live. A native Windows build lands
# in target/release; the Linux build is cross-compiled (cargo-zigbuild) into
# target/x86_64-unknown-linux-gnu/release. Override SPTC_WIN_RELSUB / SPTC_LINUX_RELSUB for a
# non-default layout. The three tool binaries each live in their own crate under tools/.
WIN_TRIPLE=x86_64-pc-windows-msvc
LINUX_TRIPLE=x86_64-unknown-linux-gnu
WIN_RELSUB="${SPTC_WIN_RELSUB:-release}"
LINUX_RELSUB="${SPTC_LINUX_RELSUB:-$LINUX_TRIPLE/release}"
BINS="claude-spt-digest claude-spt-psyche cc-spt-idle-translate"

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

# A fat archive is only readable on spt-core >= 0.13.2 — the manifest MUST declare that floor.
floor=$(grep -E '^min_spt_core_version' "$MANIFEST" | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
case "$floor" in
  0.13.2|0.13.[3-9]*|0.1[4-9].*|0.[2-9][0-9].*|[1-9].*) : ;;
  *) echo "REFUSING to package: min_spt_core_version is '$floor' but a multi-platform fat .spt needs >= 0.13.2 (doyle)." >&2; exit 1 ;;
esac

# Require BOTH platforms' release binaries (NOT auto-copied by register — they ride the archive).
echo
echo "== tool binaries =="
missing=0
win_path()   { echo "$TOOLS/$1/target/$WIN_RELSUB/$1.exe"; }
linux_path() { echo "$TOOLS/$1/target/$LINUX_RELSUB/$1"; }
for b in $BINS; do
  for p in "$(win_path "$b")" "$(linux_path "$b")"; do
    if [ -f "$p" ]; then echo "  ok    $p"; else echo "  MISS  $p"; missing=1; fi
  done
done
if [ "$missing" -ne 0 ]; then
  echo "REFUSING to package: build BOTH platforms first — Windows: sh ci/{digest,psyche,idle-translate}/build.sh;" >&2
  echo "Linux (cross): cargo-zigbuild --release --target $LINUX_TRIPLE for each tool crate (see docs/RELEASE-RUNBOOK.md)." >&2
  exit 1
fi

echo
echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
echo "manifest : $MANIFEST  ->  (root) manifest.toml          [min_spt_core $floor]"
echo "strings  : $STRINGS/  ->  (root) strings/"
echo "win bins : -> $WIN_TRIPLE/{$(echo $BINS | tr ' ' ',')}.exe"
echo "linux bins: -> $LINUX_TRIPLE/{$(echo $BINS | tr ' ' ',')}"
echo "asset    : $OUT  (the single fat adapter.spt — auto-resolves the host triple on install)"

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "DRY-RUN: nothing written. Re-run with --apply to write $OUT, then attach it as 'adapter.spt'"
  echo "to a GitHub release on the monorepo. End users: 'spt adapter add --release SaberMage/spt-claude-code'"
  echo "(default asset adapter.spt). Needs spt v0.13.2+. See docs/RELEASE-RUNBOOK.md."
  exit 0
fi

# Stage the archive ROOT in a temp dir, then tar from it so paths are root-relative (no leading dirs).
STAGE=$(mktemp -d "${TMPDIR:-/tmp}/sptc-adapter.XXXXXX") || { echo "FATAL: mktemp failed"; exit 2; }
trap 'rm -rf "$STAGE"' EXIT
cp "$MANIFEST" "$STAGE/manifest.toml"
cp -r "$STRINGS" "$STAGE/strings"
mkdir -p "$STAGE/$WIN_TRIPLE" "$STAGE/$LINUX_TRIPLE"
for b in $BINS; do
  cp "$(win_path "$b")"   "$STAGE/$WIN_TRIPLE/$b.exe"
  cp "$(linux_path "$b")" "$STAGE/$LINUX_TRIPLE/$b"
done

mkdir -p "$(dirname "$OUT")" || { echo "FATAL: cannot create $(dirname "$OUT")"; exit 2; }
( cd "$STAGE" && tar -czf "$OUT" manifest.toml strings "$WIN_TRIPLE" "$LINUX_TRIPLE" ) \
  || { echo "FATAL: tar failed"; exit 2; }

# Self-validate the produced archive: shared manifest at ROOT + BOTH triple dirs present.
echo
echo "== validate archive =="
listing=$(tar -tzf "$OUT")
fatal=0
echo "$listing" | grep -qx "manifest.toml" || { echo "FATAL: manifest.toml not at archive root" >&2; fatal=1; }
echo "$listing" | grep -q "^$WIN_TRIPLE/claude-spt-psyche.exe$" || { echo "FATAL: missing $WIN_TRIPLE/ binaries" >&2; fatal=1; }
echo "$listing" | grep -q "^$LINUX_TRIPLE/claude-spt-psyche$"   || { echo "FATAL: missing $LINUX_TRIPLE/ binaries" >&2; fatal=1; }
# Guard the footgun: no UNRECOGNIZED top-level dir (would silently flatten as a shared-root entry).
badtop=$(echo "$listing" | awk -F/ 'NF>1 {print $1}' | sort -u | grep -vE "^(strings|$WIN_TRIPLE|$LINUX_TRIPLE)$" || true)
[ -n "$badtop" ] && { echo "FATAL: unrecognized top-level dir(s) [$badtop] — spt-core would mis-place them flat" >&2; fatal=1; }
[ "$fatal" -eq 0 ] || exit 1
echo "ok   manifest.toml + strings/ at root; $WIN_TRIPLE/ + $LINUX_TRIPLE/ binaries present; no stray top-level dirs"

echo
echo "WROTE $OUT (single fat adapter.spt). Next (operator): attach as 'adapter.spt' to a GitHub release"
echo "on SaberMage/spt-claude-code; end users 'spt adapter add --release SaberMage/spt-claude-code' (spt"
echo "v0.13.2+). The fat archive auto-resolves the host's binaries — no per-OS --asset needed."
exit 0


Publish = two targets (ADR-0001): adapter `.spt` (gh_release) + cplugs plugin skeleton (hooks live there). Let me read the runbook + plugin.json to get exact steps.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== plugin.json ==="; cat plugin/sptc/.claude-plugin/plugin.json
echo; echo "=== RELEASE-RUNBOOK exists? ==="; ls docs/RELEASE-RUNBOOK.md 2>/dev/null && head -60 docs/RELEASE-RUNBOOK.md
echo "=== cplugs / marketplace refs ==="; find . -iname '*.json' -path '*marketplace*' 2>/dev/null | head; grep -rln "cplugs" --include='*.md' . 2>/dev/null | head


=== plugin.json ===
{
  "name": "sptc",
  "version": "0.1.5",
  "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).",
  "author": {
    "name": "perri"
  },
  "keywords": ["spt", "spacetime", "messaging", "agents", "live", "claude-spt"]
}

=== RELEASE-RUNBOOK exists? ===
docs/RELEASE-RUNBOOK.md
# Release runbook

> How an spt-claude-code release ships. The spine is fixed (changelog · bump · regenerate
> docs · tag · publish · docs publish). **Release here is LIGHT: signing is DELEGATED to the
> `spt` binary — there is NO two-key signing ceremony in this project.** spt-claude-code is a
> thin adapter; the heavy signed-release machinery lives in spt-core, not here.

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

## Publish targets

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

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

## One-time setup

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

## Per release

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

## cplugs skeleton publish — concrete mechanics

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
=== cplugs / marketplace refs ===
./adapter/strings/skills/version.md
./CC-LAUNCHER-BIND-PLAN.md
./CHANGELOG.md
./CHECKPOINT-COMMUNE-PLAN.md
./CONTEXT.md
./CPLUGS-PREP-PLAN.md
./docs/adr/0001-distribution-splits-by-volatility.md
./docs/RELEASE-RUNBOOK.md
./docs/SPT-CORE-FINDINGS.md
./docs-site/src/quickstart.md


This wave structurally changed the skeleton (new PostToolUse hook wiring) → both targets need publishing. Read the concrete publish mechanics.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
sed -n '60,160p' docs/RELEASE-RUNBOOK.md


<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
The concrete cplugs marketplace steps, captured from the sister project's
`claude_skill_owl/docs/DEPLOY.md`. **Take only the SKELETON SUBSET.** spt-claude-code's cplugs
target is the **thin skeleton** — `/spt:*` skill skeletons, `hooks.json`, the SessionStart
bootstrap, `plugin.json`. **No binary, no manifest in cplugs** — the binary + adapter manifest
ride the **spt-core adapter registry** (spt-conducted; the *other* publish target above), so
none of owl's binary-lifecycle machinery applies here.

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

**One-time marketplace setup:**

```bash
# Clone the marketplace repo
git clone https://github.com/SaberMage/cplugs.git ~/.claude/plugins/marketplaces/cplugs
# Register a "cplugs" entry in ~/.claude/plugins/known_marketplaces.json
#   source: { source: "github", repo: "SaberMage/cplugs" }
```

**Per skeleton bump** (only when the skeleton *structurally* changes — see "Per release"):

```bash
# 1. Bump plugin.json version. The plugin manager compares this to the cached copy and
#    SKIPS the update entirely if they match — a stale version = a silent no-update.
# 2. Copy skeleton files (NO binary) into the marketplace clone:
MARKET=~/.claude/plugins/marketplaces/cplugs/plugins/sptc
cp -r skills/*  "$MARKET/skills/"
cp -r hooks/*   "$MARKET/hooks/"
cp .claude-plugin/plugin.json "$MARKET/.claude-plugin/"
# 3. Commit + push the cplugs repo:
cd ~/.claude/plugins/marketplaces/cplugs && git add plugins/sptc/ \
  && git commit -m "sptc: <change>" && git push
```

**Pointer flip** (the authoritative install-state update):

```bash
claude plugin install sptc@cplugs   # un-orphans + rewrites installed_plugins.json atomically
# then, inside Claude Code:
/reload-plugins
```

**Gotchas (these DO apply to the skeleton):**
- **Never hand-patch `installed_plugins.json`** (`jq`/`sed` are brittle; CC may silently reject).
  Use the `claude plugin install` CLI — it's the atomic pointer flip + orphan-marker cleanup.
- **No legacy manual-install dir alongside the marketplace install.** A `~/.claude/plugins/<name>/`
  regular dir coexisting with the marketplace cache install makes `/plugin` report the plugin
  **not installed** (CC enumerates both; the untracked one wins the conflict). Keep the two exclusive.
- **Restart the Claude Code session after install** so the SessionStart hook re-runs and repopulates
  the plugin's env (`$OWL`/`$LIVE` equivalents) in Bash subprocesses.

**Explicitly NOT ours** (owl ships a live self-migrating binary; the thin skeleton has none, so
skip all of it): `owl.exe` binary sync · the cache targeted-prune + keep-PREVIOUS-version logic ·
the seamless binary handoff (owl Phase 18.4/18.5) · `DEPLOY.ps1`'s build/handoff orchestration.

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

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

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

Since v0.6.1 the asset is **ONE multi-platform fat `adapter.spt`** (ADR-0024 W1, spt-core ≥ 0.13.2):
a single archive bundles every supported platform's binaries, and install auto-resolves the host's.
This RETIRED the F-014 per-OS stopgap — there is no longer a default-vs-per-OS-asset split.

- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes the single
  `dist/adapter.spt`. It validates the manifest, refuses a `min_spt_core_version < 0.13.2` (a fat
  archive needs it), requires **both** platforms' built tool binaries, and tars the archive **ROOT** =
  `manifest.toml` (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name) +
  `strings/` **shared at root**, plus each recognized target-triple's binaries under a `<triple>/` dir.
  Never uploads (operator's step).
<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Fat layout (REQ-DIST-ADAPTER-PEROS).** Recognized triples = **`x86_64-pc-windows-msvc`** (win
  `.exe`) + **`x86_64-unknown-linux-gnu`** (linux ELF) — the ONLY two spt-core classifies in a fat
  archive. On install it places the shared root + **flattens this node's `<triple>/*` into the install
  dir**, so the bare-name command tokens (`claude-spt-digest`, …) still resolve at `<install_dir>/`
  (REQ-INSTALL-11). **Footgun:** an unrecognized top-level dir is silently treated as a shared-root
  entry and lands flat — the packer guards this (refuses any stray top-level dir); for a platform
  beyond the two triples, ship a *separate* single-triple asset via `--asset`, never a third dir here.
- **Build both platforms first** (the packer needs both binary sets present):
  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
    ci/idle-translate/build.sh` → `tools/*/target/release/*.exe`.
  - **Linux cross-built FROM Windows (proven 2026-06-16):** bare `cargo build --target
    x86_64-unknown-linux-gnu` fails (`error: linker 'cc' not found` — the crate compiles, only the
    link needs a Linux linker), so use **`cargo-zigbuild`** (zig supplies the cross-linker):
    ```sh


Let me read the cross-build specifics + check tooling availability (cargo-zigbuild, gh auth, cplugs clone) in parallel.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
sed -n '160,210p' docs/RELEASE-RUNBOOK.md


    ```sh
    # one-time: install zig (a self-contained binary) + cargo-zigbuild
    cargo install cargo-zigbuild
    # download zig (e.g. 0.14.1) to a dir OUTSIDE the repo (do NOT keep it under tools/ — it's a
    # traceable-reqs scan root and zig's bundled libc C-headers trip the scanner), put zig.exe on PATH:
    #   e.g. ~/.sptc-zig/zig-x86_64-windows-0.14.1/zig.exe  →  export PATH="$HOME/.sptc-zig/...:$PATH"
    rustup target add x86_64-unknown-linux-gnu
    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-digest/Cargo.toml
    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-psyche/Cargo.toml
    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/cc-spt-idle-translate/Cargo.toml
    ```
    → `tools/*/target/x86_64-unknown-linux-gnu/release/*` carrying real `ELF x86-64 GNU/Linux`
    binaries (verified). Then `sh ci/publish/package-adapter.sh --apply` packs BOTH into one
    `dist/adapter.spt`.
- **Publish:** attach the single `dist/adapter.spt` to the GitHub release on `SaberMage/spt-claude-code`.
  End users `spt adapter add --release SaberMage/spt-claude-code` (default asset `adapter.spt`, or
  `--tag <ver>` to pin) — the fat archive auto-resolves the host's binaries, no `--asset` needed. The
  `[update] avenue = "gh_release"` self-update fetches the same default `adapter.spt`, host-agnostic.
  - **F-015 note (Windows):** a binary-changing update still can't overwrite a binary a running live
    agent locks — but spt-core ≥ 0.13.2's W3 fix runs the live psyche from a `<perch>/.live-bin`
    own-copy, so the install-dir binary stays lock-free (validated 2026-06-22). Strings/hook-only
    releases always land fine.
  - **Local dogfood before upload (recommended):** extract `dist/adapter.spt`, flatten this node's
    triple into a dir (`cp <triple>/* .`), then `spt adapter {translate,digest}-proof claude-spt --dir
    <dir> --manifest <dir>/manifest.toml` — proves the flattened binaries resolve + run without any
    upload or registry mutation.
  - **`/sptc:setup` coupling.** The fat default is host-agnostic, so setup needs no os-detection /
    `--asset` — a bare `adapter add --release` suffices. (The current setup body still detects os/arch
    + passes `--asset`; harmless, simplifying it to the bare default is a follow-on.)
- **Binaries (install-dir resolution — REQ-INSTALL-11).** A `--release`/`--github` acquisition extracts
  the `.spt` archive's `manifest.toml` + `strings/` **+ both tool binaries** into the adapter install
  dir (`…/adapters/_github/<safe>/`). The command templates use **bare names** (`claude-spt-digest`,
  `claude-spt-psyche`), which spt-core resolves **from that install dir** (v0.8.0 Feature B /
  REQ-INSTALL-11) — **no PATH placement needed** (dogfood-proven on v0.8.1 for both binaries; F-006
  RESOLVED, interim retired). The pack therefore MUST carry both binaries (the resolution source). Note:
  a plain local-dev `spt adapter add <manifest.toml>` copies manifest + strings only (no binaries in the
  install dir), so local-dev runtime still relies on the built binary being on PATH (e.g. the CI ints
  prepend `target/release`); the install-dir path is the end-user `--release` story.
- **Acquisition only:** `--release` does **not** establish an `[update]` self-update route (our
  manifest declares no `[update]` → COPY-mode registration). Re-acquire a newer version by re-running
  `--release --tag <newer>`. The signed `file_pull` self-update channel is a separate, later concern.

## Update path (dual, kept in sync)

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


echo "=== cargo-zigbuild ==="; cargo zigbuild --version 2>&1 | head -2 || echo "NO zigbuild"
echo "=== zig ==="; zig version 2>&1 | head -1 || echo "no zig"
echo "=== gh auth ==="; gh auth status 2>&1 | head -4
echo "=== cplugs marketplace clone ==="; ls ~/.claude/plugins/marketplaces/cplugs/plugins/sptc 2>&1 | head -5 || echo "NO cplugs clone"
echo "=== linux target installed ==="; rustup target list --installed 2>/dev/null | grep linux || echo "no linux target"


=== cargo-zigbuild ===
error: unexpected argument '--version' found

=== zig ===
/usr/bin/bash: line 11: zig: command not found
=== gh auth ===
github.com
  ✓ Logged in to github.com account SaberMage (keyring)
  - Active account: true
  - Git operations protocol: https
=== cplugs marketplace clone ===
bootstrap.ps1
bootstrap.sh
hooks
skills
=== linux target installed ===
x86_64-unknown-linux-gnu


Tooling: gh authed (SaberMage ✓), cplugs clone present ✓, linux target ✓, cargo-zigbuild installed. Only zig isn't on PATH. Let me locate it.


echo "=== find zig ==="; ls -d ~/.sptc-zig/*/ 2>/dev/null; find ~/.sptc-zig -name 'zig.exe' 2>/dev/null | head; find ~ -maxdepth 4 -name 'zig.exe' 2>/dev/null | head -3
echo "=== cargo-zigbuild present path ==="; ls ~/.cargo/bin/cargo-zigbuild* 2>/dev/null


=== find zig ===
/c/Users/decid/.sptc-zig/zig-x86_64-windows-0.14.1/
/c/Users/decid/.sptc-zig/zig-x86_64-windows-0.14.1/zig.exe
/c/Users/decid/.sptc-zig/zig-x86_64-windows-0.14.1/zig.exe
=== cargo-zigbuild present path ===
/c/Users/decid/.cargo/bin/cargo-zigbuild.exe


zig found (`~/.sptc-zig/zig-x86_64-windows-0.14.1`). All tooling available. Let me check CHANGELOG format, dist/ tracking, and docs-drift state before executing the release sequence.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== CHANGELOG head ==="; head -40 CHANGELOG.md
echo "=== dist tracked? ==="; git ls-files dist/ | head
echo "=== docs drift check ==="; test -f ci/docs/check-docs.sh && sh ci/docs/check-docs.sh 2>&1 | tail -8 || echo "no check-docs"


=== CHANGELOG head ===
# 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.6.2] - 2026-06-23

> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no
> change to commands or setup.

### Fixed
- **Messages delivered while you're idle now actually send.** Previously an incoming message from
  another agent could be typed into your Claude Code input box but never submitted — it just sat
  there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered
  messages arrive and run on their own. Your half-typed draft is still stashed and restored around
  the delivery, exactly as before.

## [0.6.1] - 2026-06-22

> Requires spt-core **v0.13.2 or newer** (the version that can install a multi-platform package).
> Packaging-only release — no change to what the adapter does, only how it ships.

### Packaging
- **One download now covers every platform.** The adapter ships as a single multi-platform
  `adapter.spt` that bundles both the Windows and Linux builds beside one shared manifest; installing
  picks your platform automatically. This replaces the previous per-OS files and the Windows-only
  stopgap `adapter.spt` (which broke self-update on Linux), so `spt adapter add --release …` and
  `spt adapter update` now just work on either OS with no platform to pick. Requires spt-core
  **v0.13.2+** (the version that can read a multi-platform package).

### Changed
- **Setup is simpler.** `/sptc:setup` now activates the adapter with a plain
  `spt adapter add --release SaberMage/spt-claude-code` — no OS detection or asset selection, since
  the one package is host-agnostic.
- Adapter version of truth is now **0.6.1** (shown in `/sptc:version` and the release tag). The
=== dist tracked? ===
=== docs drift check ===
ok   mdbook build
ok   llms.txt in sync


CHANGELOG format clear; dist/ untracked (asset-only); docs in sync (no regen). Write the v0.7.0 changelog entry (user-facing, no internal lingo).


Edit

## [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
  one dense single line.
- **Requires spt-core v0.15.0+** (up from v0.13.2). Self-checkpointing and resume context both rely
  on capabilities introduced in that release; `/sptc:setup` and `spt adapter update` will keep you
  current.
- Adapter version of truth is now **0.7.0** (shown in `/sptc:version` and the release tag).

## [0.6.2] - 2026-06-23


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

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


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.6.2] - 2026-06-23
12

# 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.6.2] - 2026-06-23


## [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
  one dense single line.
- **Requires spt-core v0.15.0+** (up from v0.13.2). Self-checkpointing and resume context both rely
  on capabilities introduced in that release; `/sptc:setup` and `spt adapter update` will keep you
  current.
- Adapter version of truth is now **0.7.0** (shown in `/sptc:version` and the release tag).

## [0.6.2] - 2026-06-23


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.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
  one dense single line.
- **Requires spt-core v0.15.0+** (up from v0.13.2). Self-checkpointing and resume context both rely
  on capabilities introduced in that release; `/sptc:setup` and `spt adapter update` will keep you
  current.
- Adapter version of truth is now **0.7.0** (shown in `/sptc:version` and the release tag).

## [0.6.2] - 2026-06-23

# 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.6.2] - 2026-06-23

> Requires spt-core **v0.13.2 or newer** (unchanged). A small but important delivery fix — no
> change to commands or setup.

### Fixed
- **Messages delivered while you're idle now actually send.** Previously an incoming message from
  another agent could be typed into your Claude Code input box but never submitted — it just sat
  there as an unsent draft. The delivery now presses **Enter** to send it, so idle-delivered
  messages arrive and run on their own. Your half-typed draft is still stashed and restored around
  the delivery, exactly as before.

## [0.6.1] - 2026-06-22

> Requires spt-core **v0.13.2 or newer** (the version that can install a multi-platform package).
> Packaging-only release — no change to what the adapter does, only how it ships.

### Packaging
- **One download now covers every platform.** The adapter ships as a single multi-platform
  `adapter.spt` that bundles both the Windows and Linux builds beside one shared manifest; installing
  picks your platform automatically. This replaces the previous per-OS files and the Windows-only
  stopgap `adapter.spt` (which broke self-update on Linux), so `spt adapter add --release …` and
  `spt adapter update` now just work on either OS with no platform to pick. Requires spt-core
  **v0.13.2+** (the version that can read a multi-platform package).

### Changed
- **Setup is simpler.** `/sptc:setup` now activates the adapter with a plain
  `spt adapter add --release SaberMage/spt-claude-code` — no OS detection or asset selection, since
  the one package is host-agnostic.
- Adapter version of truth is now **0.6.1** (shown in `/sptc:version` and the release tag). The
  cplugs plugin skeleton bumps with the simplified setup body.

## [0.6.0] - 2026-06-22

> Requires spt-core **v0.13.0 or newer** for the two new capabilities below. On older spt-core
> they're simply inactive — nothing else changes, so this release is safe to take on any version.

### Added
- **Idle agents now receive your messages.** When an spt-hosted agent is sitting at its prompt
  (idle, not mid-task), a message sent to it now lands *in that session* — typed in and submitted
  for you — instead of going unseen until the agent next acted. If you were part-way through typing
  your own input, your draft is stashed first and restored right after, so an incoming message never
  eats what you were writing.
- **Resuming an agent brings back its conversation.** Relaunching a session now reloads its prior
  transcript instead of cold-starting a blank one, and the resume picker shows each session's project
  folder so you can tell them apart.

### Changed
- Adapter version of truth is now **0.6.0** (shown in `/sptc:version` and the release tag). The
  cplugs plugin skeleton is unchanged (still on its own slower track).

## [0.5.0] - 2026-06-19

### Added
- **Agents now know who they are the moment a session starts.** When a session is already
  reachable — you launched it through `cc`, or it cleared/compacted mid-run — it now opens with a
  short brief: its own agent name, a reminder that it's already live (so it won't try to re-arm),
  and how to message other agents (`spt send` and reply, plus `spt endpoint list` to see who's
  around). Previously a resumed or spt-launched session could start not knowing its own name.
- **Sessions that aren't reachable yet learn how to reach out.** On a machine that belongs to a
  subnet, a plain session with no perch now gets a one-line tip on reaching other agents with
  `spt ring` — without having to go reachable first. Solo machines with no peers see nothing.

## [0.4.0] - 2026-06-18

### Changed
- **Your live-agent companion now runs in a safe, limited mode.** When you go live with
  `/sptc:live`, the background companion that writes your context-resume notes works with a
  file-only toolset and a lighter, cheaper model. It is more contained and costs less to run,
  with no change to what you see or do.
- **Removed the experimental `:deep` profile.** `claude-spt:deep` was an unused placeholder. The
  one shipped profile overlay is `claude-spt:ccs` (routes sessions through `ccs`). If you ever
  selected `:deep`, use `:ccs` or the base adapter instead.

### Fixed
- **Sessions that spt starts for you no longer stall on a permission prompt.** When spt brings up
  a Claude Code session on your behalf (the live companion, and the spt-hosted launcher), it could
  hang waiting on an approval prompt that no one was there to answer. Those sessions now start
  cleanly.

## [0.3.0] - 2026-06-17

### Changed
- **Going live is now a single, seamless step.** A live agent comes up directly — no behind-the-scenes
  adapter selection, no chained setup commands. `/sptc:live` just brings your session up live, and
  `/sptc:ready` brings it up reachable-but-light; the difference is simply which one you run. This is
  full parity with the original live-agent experience.
- **Requires spt-core 0.9.0 or newer.** This release uses spt-core's newer automatic
  harness-resolution, so the adapter now needs spt-core **0.9.0+**. If you keep spt-core up to date
  (it self-updates), there's nothing to do; on an older spt-core the adapter will decline to install
  until you update.

### Fixed
- **Setup pins this adapter as your default.** `/sptc:setup` now records `claude-spt` as the active
  adapter for Claude Code, so going live or ready needs no extra flags.

## [0.2.1] - 2026-06-16

### Fixed
- **Going live shows a clean status, not raw machine output.** Starting a live agent previously
  leaked internal setup markers and tokens into the chat. `/sptc:live` now reports a single tidy
  summary — your agent id, online status, how other agents reach you, and how to reply — and
  nothing else.

### Changed
- **Self-contained live setup.** Bringing a session live no longer bounces you to a separate help
  topic; `/sptc:live` carries the full bringup itself, so going live works in one step.

## [0.2.0] - 2026-06-16

### Fixed
- **Live agents now reliably start their background companion.** Previously a live agent could come
  up marked "online" while its persistent companion silently failed to start, leaving it with no
  running context. The companion now launches and stays resident.

### Changed
- **Simpler setup — no manual PATH step.** The adapter's helper tools are now found automatically
  from where the adapter is installed; `/sptc:setup` no longer asks you to copy anything onto your
  PATH.
- **Automatic adapter updates.** The adapter now updates itself from this project's GitHub releases
  (`spt adapter update`) — logic and instruction changes reach you without a reinstall.

### Added
- **Linux support.** The adapter ships native **Windows and Linux** builds; `/sptc:setup` detects
  your OS and installs the right one.
- **ccs integration.** If you use [ccs](https://github.com/kaitranntt/ccs), `/sptc:setup` wires the
  shipped `claude-spt:ccs` profile so live/ready agents can run on your ccs backends instead of
  `claude`.
- **Private-network onboarding.** `/sptc:setup` now offers to create or join a private network
  (subnet) so you can pair machines and reach agents across them.

## [0.1.0] - 2026-06-15

First public release.

### Added
- **One-step setup — `/sptc:setup`.** Sets the plugin up and installs the Spacetime (`spt`)
  engine for you if it isn't already present, so messaging and live agents just work. Also offers
  to create or join a private network and to register the always-on background service.
- **Agent messaging.** `/sptc:ready` turns on your inbox so other agents can reach this session;
  `/sptc:send` messages another agent; `/sptc:list-agents` shows who is currently active.
- **Live agents.** `/sptc:live` upgrades the current session into a live agent with a persistent
  companion that keeps its own running context. `/sptc:commune` pushes a context update to that
  companion, and `/sptc:signoff` ends a live session cleanly with a final summary.
- **`/sptc:force-stop`** immediately tears down a listening or live agent when you need it gone.
- **Cross-machine networks — `/sptc:subnet`.** Pair machines into a private network (create,
  show the join code, or join an existing one) so your agents can reach each other across nodes.
- **`/sptc:version`** reports the running engine version.
- **Alternate model backends (advanced).** Ships a ready-made `ccs` profile template you can
  select to launch sessions through [ccs](https://github.com/kaitranntt/ccs) — useful for routing
  an agent to a different model or billing backend. You supply your own ccs configuration.
- **Invisible engine install.** Starting a session automatically installs the `spt` engine the
  first time if it is missing — no separate install step.

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

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

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

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

+- **Requires spt-core v0.15.0+** (up from v0.13.2). Self-checkpointing and resume context both rely

+  on capabilities introduced in that release; `/sptc:setup` and `spt adapter update` will keep you

+- Adapter version of truth is now **0.7.0** (shown in `/sptc:version` and the release tag).


Bump plugin.json (structural skeleton change — new PostToolUse hook):


1	{
2	  "name": "sptc",
3	  "version": "0.1.5",
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).",


Both files updated. Build Windows binaries (native, all 3 tool crates):


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
for g in digest psyche idle-translate; do
  echo "=== ci/$g/build.sh ==="; sh ci/$g/build.sh 2>&1 | tail -3
done
echo "=== windows release bins present? ==="
for b in claude-spt-digest claude-spt-psyche cc-spt-idle-translate; do
  ls tools/$b/target/release/$b.exe 2>/dev/null && echo "  ok $b" || echo "  MISS $b"
done


=== ci/digest/build.sh ===
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  claude-spt-digest: cargo test + release build
=== ci/psyche/build.sh ===
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  claude-spt-psyche: cargo test + release build
=== ci/idle-translate/build.sh ===
test result: ok. 22 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

ok  cc-spt-idle-translate: cargo test + release build
=== windows release bins present? ===
tools/claude-spt-digest/target/release/claude-spt-digest.exe
  ok claude-spt-digest
tools/claude-spt-psyche/target/release/claude-spt-psyche.exe
  ok claude-spt-psyche
tools/cc-spt-idle-translate/target/release/cc-spt-idle-translate.exe
  ok cc-spt-idle-translate


Windows binaries built. Now Linux cross-build (zig on PATH, 3 crates):


cd "C:/Users/decid/Documents/projects/spt-claude-code"
export PATH="$HOME/.sptc-zig/zig-x86_64-windows-0.14.1:$PATH"
zig version 2>&1 | head -1
for c in claude-spt-digest claude-spt-psyche cc-spt-idle-translate; do
  echo "=== zigbuild $c ==="
  cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/$c/Cargo.toml 2>&1 | tail -3
done


0.14.1
=== zigbuild claude-spt-digest ===
    Finished `release` profile [optimized] target(s) in 0.09s
=== zigbuild claude-spt-psyche ===
    Finished `release` profile [optimized] target(s) in 0.02s
=== zigbuild cc-spt-idle-translate ===
   Compiling cc-spt-idle-translate v0.1.0 (C:\Users\decid\Documents\projects\spt-claude-code\tools\cc-spt-idle-translate)
    Finished `release` profile [optimized] target(s) in 5.19s


Linux binaries built. Verify all 3 are real ELF (idle-translate is freshest with the wave changes):


cd "C:/Users/decid/Documents/projects/spt-claude-code"
for b in claude-spt-digest claude-spt-psyche cc-spt-idle-translate; do
  p="tools/$b/target/x86_64-unknown-linux-gnu/release/$b"
  if [ -f "$p" ]; then echo "$b: $(file "$p" 2>/dev/null | sed 's/.*: //' | cut -c1-40)  [$(stat -c %y "$p" 2>/dev/null | cut -c1-19)]"; else echo "$b MISSING"; fi
done
echo "=== checkpoint string in the fresh linux idle binary? ==="
grep -c "checkpoint" "tools/cc-spt-idle-translate/target/x86_64-unknown-linux-gnu/release/cc-spt-idle-translate" >/dev/null 2>&1 && echo "yes (wave binary)" || echo "NO"


claude-spt-digest: ELF 64-bit LSB pie executable, x86-64, v  [2026-06-18 16:01:41]
claude-spt-psyche: ELF 64-bit LSB pie executable, x86-64, v  [2026-06-18 16:01:44]
cc-spt-idle-translate: ELF 64-bit LSB pie executable, x86-64, v  [2026-06-24 19:12:39]
=== checkpoint string in the fresh linux idle binary? ===
yes (wave binary)


All 3 Linux ELF present; idle-translate is the fresh wave binary. Package the fat `.spt`:


cd "C:/Users/decid/Documents/projects/spt-claude-code"
sh ci/publish/package-adapter.sh --apply 2>&1 | tail -25


== 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-digest/target/release/claude-spt-digest.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-digest/target/x86_64-unknown-linux-gnu/release/claude-spt-digest
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-psyche/target/release/claude-spt-psyche.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/claude-spt-psyche/target/x86_64-unknown-linux-gnu/release/claude-spt-psyche
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/cc-spt-idle-translate/target/release/cc-spt-idle-translate.exe
  ok    /c/Users/decid/Documents/projects/spt-claude-code/tools/cc-spt-idle-translate/target/x86_64-unknown-linux-gnu/release/cc-spt-idle-translate

== plan (APPLY) ==
manifest : /c/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml  ->  (root) manifest.toml          [min_spt_core 0.15.0]
strings  : /c/Users/decid/Documents/projects/spt-claude-code/adapter/strings/  ->  (root) strings/
win bins : -> x86_64-pc-windows-msvc/{claude-spt-digest,claude-spt-psyche,cc-spt-idle-translate}.exe
linux bins: -> x86_64-unknown-linux-gnu/{claude-spt-digest,claude-spt-psyche,cc-spt-idle-translate}
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/spt-claude-code; end users 'spt adapter add --release SaberMage/spt-claude-code' (spt
v0.13.2+). The fat archive auto-resolves the host's binaries — no per-OS --asset needed.


`dist/adapter.spt` written (floor 0.15.0, both triples). Local dogfood before upload — extract, flatten this node's triple, translate-proof the packed binary (normal + checkpoint):


cd "C:/Users/decid/Documents/projects/spt-claude-code"
EXD="C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/ddb3691d-6635-4e91-bb00-3365d2fec908/scratchpad/spt-extract"
rm -rf "$EXD"; mkdir -p "$EXD"
tar -xzf dist/adapter.spt -C "$EXD"
echo "=== extracted tree ==="; ls "$EXD";
echo "=== flatten windows triple ==="; cp "$EXD/x86_64-pc-windows-msvc/"* "$EXD/" && ls "$EXD"/*.exe
echo "=== translate-proof NORMAL (packed binary) ==="
spt adapter translate-proof claude-spt --event '<EVENT type="msg" from="ci">packed dogfood</EVENT>' --manifest "$EXD/manifest.toml" --dir "$EXD" 2>&1 | grep -E 'TRANSLATE_PROOF_OK|text|commit:'
echo "=== translate-proof CHECKPOINT (packed binary) ==="
spt adapter translate-proof claude-spt --event '<EVENT type="msg" from="self" json="{"checkpoint":"v1","wake":"packed wake"}">x</EVENT>' --manifest "$EXD/manifest.toml" --dir "$EXD" 2>&1 | grep -E 'TRANSLATE_PROOF_OK|/clear|packed wake|commit:'


=== extracted tree ===
manifest.toml
strings
x86_64-pc-windows-msvc
x86_64-unknown-linux-gnu
=== flatten windows triple ===
C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/ddb3691d-6635-4e91-bb00-3365d2fec908/scratchpad/spt-extract/cc-spt-idle-translate.exe
C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/ddb3691d-6635-4e91-bb00-3365d2fec908/scratchpad/spt-extract/claude-spt-digest.exe
C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/ddb3691d-6635-4e91-bb00-3365d2fec908/scratchpad/spt-extract/claude-spt-psyche.exe
=== translate-proof NORMAL (packed binary) ===
emitted 6 command(s), commit: yes
  text  "<EVENT type=\"msg\" from=\"ci\">\npacked dogfood\n</EVENT>"
TRANSLATE_PROOF_OK:claude-spt
=== translate-proof CHECKPOINT (packed binary) ===
emitted 8 command(s), commit: yes
  text  "/clear"
  text  "packed wake"
TRANSLATE_PROOF_OK:claude-spt


Packed binary dogfood-proven (normal framing + checkpoint macro both fire from the released asset). Commit the release prep, then tag.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
traceable-reqs check >/dev/null 2>&1 && echo "traceable GREEN" || echo "RED"
git add CHANGELOG.md plugin/sptc/.claude-plugin/plugin.json
git commit -q -F - <<'EOF'
chore(release): v0.7.0 — changelog + cplugs skeleton bump (0.1.6)

The checkpoint-commune wave ships. User-facing CHANGELOG [0.7.0] (self-
checkpointing /sptc:commune --checkpoint, automatic resume context, multi-line
message delivery; requires spt-core v0.15.0+). Bump plugin.json 0.1.5 -> 0.1.6
— the skeleton changed structurally this release (new PostToolUse hook wiring +
post-tool-use.sh), so the marketplace skeleton must resync.

Adapter version of truth = 0.7.0 (manifest); cplugs skeleton = 0.1.6.

Co-authored by: perri
EOF
echo "commit=$?"; git push origin main 2>&1 | tail -2; git rev-parse --short HEAD


traceable GREEN
commit=0
To https://github.com/SaberMage/spt-claude-code.git
   5a1d744..50eac11  main -> main
50eac11


Release prep on main. Tag v0.7.0 + push (triggers docs-publish):


cd "C:/Users/decid/Documents/projects/spt-claude-code"
git tag v0.7.0 2>&1 && echo "tagged"
git push origin v0.7.0 2>&1 | tail -3


tagged
To https://github.com/SaberMage/spt-claude-code.git
 * [new tag]         v0.7.0 -> v0.7.0


Tag pushed. Create the GitHub release with the `adapter.spt` asset + the changelog section as the body. Extract the [0.7.0] body first:


cd "C:/Users/decid/Documents/projects/spt-claude-code"
BODY="C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/ddb3691d-6635-4e91-bb00-3365d2fec908/scratchpad/relbody.md"
awk '/^## \[0\.7\.0\]/{f=1;next} /^## \[0\.6\.2\]/{f=0} f' CHANGELOG.md | sed '1{/^$/d}' > "$BODY"
echo "=== release body ==="; cat "$BODY"
echo "=== asset check ==="; ls -la dist/adapter.spt


=== release body ===
> 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
  one dense single line.
- **Requires spt-core v0.15.0+** (up from v0.13.2). Self-checkpointing and resume context both rely
  on capabilities introduced in that release; `/sptc:setup` and `spt adapter update` will keep you
  current.
- Adapter version of truth is now **0.7.0** (shown in `/sptc:version` and the release tag).

=== asset check ===
-rw-r--r-- 1 decid 197609 1075822 Jun 24 19:12 dist/adapter.spt


cd "C:/Users/decid/Documents/projects/spt-claude-code"
BODY="C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-claude-code/ddb3691d-6635-4e91-bb00-3365d2fec908/scratchpad/relbody.md"
gh release create v0.7.0 "dist/adapter.spt#adapter.spt" --title "v0.7.0 — self-checkpointing, resume context, multi-line messages" --notes-file "$BODY" 2>&1 | tail -5


Release created. Verify the asset attached + named `adapter.spt`:


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


Error: Exit code 1
Unknown JSON field: "isLatest"
Available fields:
  apiUrl
  assets
  author
  body
  createdAt
  databaseId
  id
  isDraft
  isImmutable
  isPrerelease
  name
  publishedAt
  tagName
  tarballUrl
  targetCommitish
  uploadUrl
  url
  zipballUrl


cd "C:/Users/decid/Documents/projects/spt-claude-code"
gh release view v0.7.0 --json tagName,assets,isDraft,isPrerelease --jq '{tag:.tagName, draft:.isDraft, pre:.isPrerelease, assets:[.assets[]|{name,size}]}' 2>&1


Adapter release **v0.7.0 LIVE** (asset `adapter.spt`, 1.05MB, not draft). Now the cplugs skeleton publish. Let me check `package-skeleton.sh` to stage the skeleton subset correctly.


1	#!/bin/sh
2	# Stage the sptc cplugs SKELETON into a marketplace clone — the runbook "per skeleton bump" cp
3	# mechanic as a real, idempotent, validated script. DRY-RUN by default: prints the plan and stages
4	# nothing. Never pushes (marketplace credentials + the commit/push are the operator's step).
5	# [impl->REQ-DIST-PLUGIN-SKELETON]
6	#
7	# Usage:
8	#   package-skeleton.sh                 # dry-run against the default $MARKET
9	#   package-skeleton.sh --apply         # actually copy the skeleton subset into $MARKET
10	#   MARKET=/path/to/cplugs/plugins/sptc package-skeleton.sh --apply
11	set -u
12	HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
13	ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
14	PLUGIN="$ROOT/plugin/sptc"
15	MARKET="${MARKET:-$HOME/.claude/plugins/marketplaces/cplugs/plugins/sptc}"
16	APPLY=0
17	[ "${1:-}" = "--apply" ] && APPLY=1
18
19	# Validate first — refuse to stage an invalid / leaky skeleton.
20	echo "== validate skeleton =="
21	if ! sh "$HERE/validate-skeleton.sh" "$PLUGIN"; then
22	  echo "REFUSING to package: skeleton failed validation (fix above, then retry)." >&2
23	  exit 1
24	fi
25
26	# The skeleton SUBSET — exactly what cplugs carries (no binary, no manifest, no runtime state).
27	SUBSET=".claude-plugin/plugin.json hooks skills bootstrap.sh bootstrap.ps1"
28
29	echo
30	echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
31	echo "source : $PLUGIN"
32	echo "target : $MARKET"
33	for item in $SUBSET; do
34	  if [ -e "$PLUGIN/$item" ]; then echo "  stage  $item"; else echo "  WARN   missing source: $item"; fi
35	done
36
37	if [ "$APPLY" -ne 1 ]; then
38	  echo
39	  echo "DRY-RUN: nothing copied. Re-run with --apply to stage into \$MARKET, then commit+push the"
40	  echo "cplugs repo and 'claude plugin install sptc@cplugs' (see docs/RELEASE-RUNBOOK.md)."
41	  exit 0
42	fi
43
44	mkdir -p "$MARKET/.claude-plugin" "$MARKET/hooks" "$MARKET/skills" || { echo "FATAL: cannot create $MARKET"; exit 2; }
45	cp "$PLUGIN/.claude-plugin/plugin.json" "$MARKET/.claude-plugin/"
46	# Replace dir contents wholesale so removed skeleton files don't linger in the marketplace clone.
47	rm -rf "$MARKET/hooks" "$MARKET/skills"
48	cp -r "$PLUGIN/hooks" "$MARKET/hooks"
49	cp -r "$PLUGIN/skills" "$MARKET/skills"
50	for b in bootstrap.sh bootstrap.ps1; do [ -f "$PLUGIN/$b" ] && cp "$PLUGIN/$b" "$MARKET/"; done
51
52	echo
53	echo "STAGED into $MARKET. Next (operator): cd into the cplugs repo, 'git add plugins/sptc/',"
54	echo "commit + push, then 'claude plugin install sptc@cplugs' + /reload-plugins."
55	exit 0
56

#!/bin/sh
# Stage the sptc cplugs SKELETON into a marketplace clone — the runbook "per skeleton bump" cp
# mechanic as a real, idempotent, validated script. DRY-RUN by default: prints the plan and stages
# nothing. Never pushes (marketplace credentials + the commit/push are the operator's step).
# [impl->REQ-DIST-PLUGIN-SKELETON]
#
# Usage:
#   package-skeleton.sh                 # dry-run against the default $MARKET
#   package-skeleton.sh --apply         # actually copy the skeleton subset into $MARKET
#   MARKET=/path/to/cplugs/plugins/sptc package-skeleton.sh --apply
set -u
HERE=$(CDPATH= cd "$(dirname "$0")" && pwd)
ROOT=$(CDPATH= cd "$HERE/../.." && pwd)
PLUGIN="$ROOT/plugin/sptc"
MARKET="${MARKET:-$HOME/.claude/plugins/marketplaces/cplugs/plugins/sptc}"
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1

# Validate first — refuse to stage an invalid / leaky skeleton.
echo "== validate skeleton =="
if ! sh "$HERE/validate-skeleton.sh" "$PLUGIN"; then
  echo "REFUSING to package: skeleton failed validation (fix above, then retry)." >&2
  exit 1
fi

# The skeleton SUBSET — exactly what cplugs carries (no binary, no manifest, no runtime state).
SUBSET=".claude-plugin/plugin.json hooks skills bootstrap.sh bootstrap.ps1"

echo
echo "== plan ($([ "$APPLY" -eq 1 ] && echo APPLY || echo DRY-RUN)) =="
echo "source : $PLUGIN"
echo "target : $MARKET"
for item in $SUBSET; do
  if [ -e "$PLUGIN/$item" ]; then echo "  stage  $item"; else echo "  WARN   missing source: $item"; fi
done

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "DRY-RUN: nothing copied. Re-run with --apply to stage into \$MARKET, then commit+push the"
  echo "cplugs repo and 'claude plugin install sptc@cplugs' (see docs/RELEASE-RUNBOOK.md)."
  exit 0
fi

mkdir -p "$MARKET/.claude-plugin" "$MARKET/hooks" "$MARKET/skills" || { echo "FATAL: cannot create $MARKET"; exit 2; }
cp "$PLUGIN/.claude-plugin/plugin.json" "$MARKET/.claude-plugin/"
# Replace dir contents wholesale so removed skeleton files don't linger in the marketplace clone.
rm -rf "$MARKET/hooks" "$MARKET/skills"
cp -r "$PLUGIN/hooks" "$MARKET/hooks"
cp -r "$PLUGIN/skills" "$MARKET/skills"
for b in bootstrap.sh bootstrap.ps1; do [ -f "$PLUGIN/$b" ] && cp "$PLUGIN/$b" "$MARKET/"; done

echo
echo "STAGED into $MARKET. Next (operator): cd into the cplugs repo, 'git add plugins/sptc/',"
echo "commit + push, then 'claude plugin install sptc@cplugs' + /reload-plugins."
exit 0


`package-skeleton.sh` validates + stages. Dry-run first:


cd "C:/Users/decid/Documents/projects/spt-claude-code"
sh ci/publish/package-skeleton.sh 2>&1 | tail -20


ok   skill ok: send
ok   skill ok: setup
ok   skill ok: signoff
ok   skill ok: subnet
ok   skill ok: version
ok   published surface clean (no runtime/binary/manifest leak)

=== SKELETON: INSTALLABLE ===

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


Skeleton INSTALLABLE. Apply (stage into the marketplace clone):


cd "C:/Users/decid/Documents/projects/spt-claude-code"
sh ci/publish/package-skeleton.sh --apply 2>&1 | tail -5
echo "=== cplugs repo status ==="
cd ~/.claude/plugins/marketplaces/cplugs
git status --short 2>&1 | head -20
echo "=== post-tool-use.sh present in staged? ==="; ls plugins/sptc/hooks/post-tool-use.sh 2>&1
echo "=== plugin.json version staged ==="; grep version plugins/sptc/.claude-plugin/plugin.json


  stage  bootstrap.sh
  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 repo status ===
 M plugins/sptc/.claude-plugin/plugin.json
 M plugins/sptc/hooks/_common.sh
 M plugins/sptc/hooks/hooks.json
 M plugins/sptc/hooks/session-start.sh
 M plugins/sptc/skills/commune/SKILL.md
?? plugins/sptc/hooks/post-tool-use.sh
=== post-tool-use.sh present in staged? ===
plugins/sptc/hooks/post-tool-use.sh
=== plugin.json version staged ===
  "version": "0.1.6",
Shell cwd was reset to C:\Users\decid\Documents\projects\spt-claude-code


All skeleton changes staged in the cplugs clone (incl. new `post-tool-use.sh`, plugin.json 0.1.6). Commit + push the marketplace repo (via `git -C`):


CPLUGS="C:/Users/decid/.claude/plugins/marketplaces/cplugs"
git -C "$CPLUGS" add plugins/sptc/
git -C "$CPLUGS" commit -q -F - <<'EOF'
sptc 0.1.6: PostToolUse checkpoint hook + multi-line/resume skeleton sync

Structural skeleton bump for spt-claude-code v0.7.0:
- NEW hooks/post-tool-use.sh + hooks.json Write-matcher (the agent-driven
  checkpoint detector — !!checkpoint!! in a commune self-sends the clear+wake).
- hooks/_common.sh: checkpoint + resume-context helpers.
- hooks/session-start.sh: pull psyche-download resume context on bind|boundary.
- skills/commune/SKILL.md: full-fat /sptc:commune --checkpoint.
- plugin.json 0.1.5 -> 0.1.6.

Logic rides the spt binary + adapter manifest (v0.7.0); requires spt-core v0.15.0+.

Co-authored by: perri
EOF
echo "commit=$?"
git -C "$CPLUGS" push 2>&1 | tail -3
echo "=== cplugs HEAD ==="; git -C "$CPLUGS" log -1 --format='%h %s'


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/session-start.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/hooks/post-tool-use.sh', LF will be replaced by CRLF the next time Git touches it
commit=0
To https://github.com/SaberMage/cplugs.git
   871becb..b14bb19  main -> main
=== cplugs HEAD ===
b14bb19 sptc 0.1.6: PostToolUse checkpoint hook + multi-line/resume skeleton sync


Both publish targets shipped. Verify the release is Latest, the docs-publish workflow triggered, and the marketplace pointer:


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== latest release ==="; gh release view --json tagName,assets --jq '{latest_tag:.tagName, assets:[.assets[].name]}' 2>&1
echo "=== workflows triggered by the tag/push ==="; gh run list --limit 4 --json workflowName,headBranch,status,event --jq '.[]|"\(.workflowName) [\(.event)/\(.headBranch)] \(.status)"' 2>&1
echo "=== cplugs marketplace HEAD (remote) ==="; gh api repos/SaberMage/cplugs/commits/main --jq '.sha[0:7]+" "+.commit.message' 2>&1 | head -1


=== latest release ===
{"assets":["adapter.spt"],"latest_tag":"v0.7.0"}
=== workflows triggered by the tag/push ===
=== cplugs marketplace HEAD (remote) ===
b14bb19 sptc 0.1.6: PostToolUse checkpoint hook + multi-line/resume skeleton sync


Release Latest + cplugs at 0.1.6 on remote. `gh run list` empty — let me confirm whether a docs-publish workflow exists (the runbook says the tag triggers it):


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== workflows in repo ==="; ls .github/workflows/ 2>/dev/null || echo "no .github/workflows dir"
echo "=== any runs at all ==="; gh run list --limit 3 2>&1 | head


=== workflows in repo ===
no .github/workflows dir
=== any runs at all ===


No `.github/workflows` exists — the runbook's docs-publish workflow was never wired (pre-existing; not something this release introduced). Docs are in-repo + llms.txt in sync, so nothing blocks. **Publish functionally complete.** Ping doyle (deferred E2Es now unblocked) + update memory.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== project repo clean + pushed? ==="; git status --short; git log origin/main..HEAD --oneline | head || echo "in sync"
$OWL send doyle perri <<'EOF'
v0.7.0 PUBLISHED — both targets shipped:
1. Adapter .spt → GitHub release v0.7.0 on SaberMage/spt-claude-code (asset adapter.spt, Latest, not draft). Packed fat archive (both triples, floor 0.15.0); dogfood-proven pre-upload — extracted+flattened, translate-proof fired BOTH normal multi-line framing AND the checkpoint /clear+wake macro from the released binary.
2. cplugs skeleton 0.1.6 → pushed to SaberMage/cplugs main @ b14bb19 (NEW post-tool-use.sh + hooks.json Write-matcher + _common.sh checkpoint/resume helpers + session-start.sh psyche-download wiring + full-fat commune SKILL.md).

Build: Windows native + Linux cross (cargo-zigbuild/zig 0.14.1), all 3 tool crates. Tag v0.7.0 pushed. CHANGELOG [0.7.0] user-facing. traceable GREEN throughout.

This UNBLOCKS the two deferred dogfood E2Es you gated: a node that installs sptc 0.1.6 + adapter v0.7.0 and spawns a fresh spt-hosted CC will run the NEW hooks → can now dogfood (a) PostToolUse write-detect (real !!checkpoint!! Write → self-send) and (b) SessionStart injection (hook consuming psyche-download into live CC). I'll run those once a node lands the new plugin. Note: no .github/workflows in the repo, so the tag triggers no docs-publish action (pre-existing — docs are in-repo + llms.txt in sync).

Checkpoint-commune wave: SHIPPED.
EOF
echo "sent=$?"


=== project repo clean + pushed? ===
➜ SENT:doyle
sent=0


Project repo clean + in sync. Update memory to reflect SHIPPED.


Edit

---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave SHIPPED — adapter v0.7.0 released + cplugs sptc 0.1.6 published (multi-line idle, checkpoint communes via --json-payload, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — SHIPPED 2026-06-24 (perri). BUILT + VALIDATED on published spt-core v0.15.0 (counter 34) + doyle GATE-PASS + PUBLISHED both targets. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter v0.7.0, min_spt_core 0.15.0, cplugs sptc 0.1.6.

**PUBLISHED (both ADR-0001 targets):**
- Adapter `.spt` → GitHub release **v0.7.0** on SaberMage/spt-claude-code (asset `adapter.spt`, Latest). Fat archive both triples (floor 0.15.0), dogfood-proven pre-upload. Build: Windows native + Linux cross (cargo-zigbuild + zig 0.14.1 at `~/.sptc-zig/zig-x86_64-windows-0.14.1`). Pack: `sh ci/publish/package-adapter.sh --apply`. Release: `gh release create v0.7.0 dist/adapter.spt#adapter.spt`.
- cplugs skeleton **0.1.6** → SaberMage/cplugs main @ b14bb19 (structural bump: new PostToolUse hook). Stage: `sh ci/publish/package-skeleton.sh --apply` → marketplace clone `~/.claude/plugins/marketplaces/cplugs/plugins/sptc` → commit+push via `git -C`.
- NOTE: no `.github/workflows` in repo → tag triggers NO docs-publish action (pre-existing gap; docs in-repo + llms.txt in sync). Pointer-flip (`claude plugin install sptc@cplugs` + restart) is a per-node operator step (NOT auto-run — would disrupt the active session).

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish → `5a1d744` watch-item → `50eac11` release prep (changelog+plugin.json). Tag `v0.7.0`.


<tool_use_error>String to replace not found in file.
String: ---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).
(note: Edit also tried swapping \uXXXX escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. Re-read the file and copy the exact surrounding text.)</tool_use_error>

Error: String to replace not found in file.
String: ---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).
(note: Edit also tried swapping \uXXXX escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. Re-read the file and copy the exact surrounding text.)


Arrow chars mismatching again — rewrite the file:


Write

---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave SHIPPED — adapter v0.7.0 released + cplugs sptc 0.1.6 published (multi-line idle, checkpoint communes via --json-payload, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — SHIPPED 2026-06-24 (perri). BUILT + VALIDATED on published spt-core v0.15.0 (counter 34) + doyle GATE-PASS + PUBLISHED both targets. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter v0.7.0, min_spt_core 0.15.0, cplugs sptc 0.1.6.

**PUBLISHED (both ADR-0001 targets):**
- Adapter `.spt` -> GitHub release **v0.7.0** on SaberMage/spt-claude-code (asset `adapter.spt`, Latest, not draft). Fat archive both triples (floor 0.15.0), dogfood-proven pre-upload (extract+flatten+translate-proof fired normal framing AND checkpoint macro). Build: Windows native + Linux cross (cargo-zigbuild + zig 0.14.1 at `~/.sptc-zig/zig-x86_64-windows-0.14.1`, add to PATH). Pack: `sh ci/publish/package-adapter.sh --apply` -> dist/adapter.spt. Release: `gh release create v0.7.0 dist/adapter.spt#adapter.spt`.
- cplugs skeleton **0.1.6** -> SaberMage/cplugs main @ b14bb19 (structural bump: new PostToolUse hook). Stage: `sh ci/publish/package-skeleton.sh --apply` -> marketplace clone `~/.claude/plugins/marketplaces/cplugs/plugins/sptc` -> commit+push via `git -C`.
- NOTE: no `.github/workflows` in repo -> tag triggers NO docs-publish action (pre-existing gap; docs in-repo + llms.txt in sync). Pointer-flip (`claude plugin install sptc@cplugs` + session restart) is a per-node operator step, NOT auto-run (would disrupt the active session).

Commits on main: `211a5cc` doc-capture, `befa592` T1, `e27e0d3` T2, `38044e2` T3, `a9aa714` v0.15.0-publish, `5a1d744` watch-item, `50eac11` release-prep. Tag `v0.7.0`.

**What shipped (don't re-derive):**
- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). CC soft-newlines a bare `\n` (empirically gated).
- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) -> PostToolUse(Write) hook `post-tool-use.sh` -> `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":...}'`. Binary parses the `json="..."` envelope ATTR -> xml-attr-unescape (`&` LAST) -> serde -> `checkpoint=="v1"` -> clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint`.
- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` in `session-start.sh` bind|boundary brief -> injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

**VALIDATED on-node (real spt 0.15.0):** T2 LIVE LOOPBACK (real --json-payload self-send drove a spt-hosted CC through /clear + wake turn, transcript-proven); T3 psyche-download 4-way contract (verb/NO-CONTEXT/AUTH/POSITIVE) via deterministic CI `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch, no real CC; 8-poll window straddles the ~5s ingest pulse per doyle's watch-item).

**STILL OPEN — the two harness-glue dogfood E2Es** (doyle gate ruling: logged, NOT CI; CHECKPOINT-COMMUNE-PLAN.md §Deferred validations). Now UNBLOCKED by the publish: a node that installs sptc 0.1.6 + adapter v0.7.0 and spawns a fresh spt-hosted CC runs the NEW hooks -> dogfood (a) T2 PostToolUse write-detect (real `!!checkpoint!!` Write -> self-send), (b) T3 SessionStart injection (hook consuming psyche-download into live CC). Run once a node lands the new plugin.

**Recipe:** comms = legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends (PowerShell has no printf). Validation: swap installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id in `owlery/<id>/info.json` (the CC post_spawn uuid, NOT the endpoint-run session), transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`. Teardown: `spt endpoint purge <id> --force --yes` + restore binary.


<owl_messages>
<owl_message from="doyle" priority="high">Verified — v0.7.0 Latest on spt-claude-code (adapter.spt 1.05MB fat archive, both triples, floor 0.15.0), cplugs sptc 0.1.6 @b14bb19. Operator-call item CLOSED, dogfood-proven pre-upload (extract+flatten + translate-proof both branches from the released binary). Only thread left = the two dogfood E2Es on a node that lands sptc 0.1.6 + v0.7.0 — yours to run on next install; ping outcomes. Checkpoint-commune wave SHIPPED end-to-end. Clean work, perri.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.