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

#!/bin/sh
# Unit test for the adapter.spt packer (ci/publish/package-adapter.sh).
# Asserts the MULTI-PLATFORM fat-archive invariants `spt adapter add --release` depends on (ADR-0024
# W1, spt-core >= 0.13.2): one .spt holds the SHARED manifest.toml (named exactly) + strings/ at the
# archive ROOT, plus each recognized target-triple's binaries under a <triple>/ dir mirroring the
# flat-root tree. spt-core classifies on the top-level triple dir and flattens this node's triple into
# the install dir, so a bare-name command token still resolves. [unit->REQ-DIST-ADAPTER-RELEASE]
# [unit->REQ-DIST-ADAPTER-PEROS]
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
PACKER="$ROOT/ci/publish/package-adapter.sh"
WIN_TRIPLE=x86_64-pc-windows-msvc
LINUX_TRIPLE=x86_64-unknown-linux-gnu
BINS="claude-spt-digest claude-spt-psyche cc-spt-idle-translate"
rc=0
fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

# The fat archive needs BOTH platforms' release binaries. Probe both triples.
have_all=1
for b in $BINS; do
  [ -f "$ROOT/tools/$b/target/release/$b.exe" ] || have_all=0
  [ -f "$ROOT/tools/$b/target/$LINUX_TRIPLE/release/$b" ] || have_all=0
done

if [ "$have_all" -ne 1 ]; then
  # Missing a platform → assert the packer's REFUSE guard fires (no silent pass), then skip the
  # archive-build assertion (no hidden coverage gap). Both platforms are required for a fat archive.
  if sh "$PACKER" --apply >/dev/null 2>&1; then
    fail "packer should refuse (exit!=0) when a platform's binaries are absent"
  else
    echo "ok   packer refuses --apply when a platform's binaries absent (guard works)"
  fi
  echo "SKIP: archive-build assertion — need BOTH platforms (win native + linux cross-build) present"
  exit "$rc"
fi

# Both platforms present → build a real fat archive to a temp file and assert its structure.
TMP=$(mktemp -d "${TMPDIR:-/tmp}/sptc-archtest.XXXXXX") || { echo "FAIL: mktemp"; exit 1; }
trap 'rm -rf "$TMP"' EXIT
OUT="$TMP/adapter.spt"

if ADAPTER_SPT_OUT="$OUT" sh "$PACKER" --apply >/dev/null 2>&1; then
  echo "ok   packer --apply succeeded"
else
  fail "packer --apply exited non-zero"
  exit "$rc"
fi
[ -f "$OUT" ] || { fail "no archive written at $OUT"; exit "$rc"; }

LIST=$(tar -tzf "$OUT")
# SHARED manifest.toml MUST be at the root (exact, no leading path) — the add --release contract.
echo "$LIST" | grep -qx "manifest.toml" \
  && echo "ok   shared manifest.toml at archive root" \
  || fail "manifest.toml not at archive root (add --release would reject)"
# SHARED strings/ at root (not under a triple).
echo "$LIST" | grep -q "^strings/" \
  && echo "ok   shared strings/ at root" \
  || fail "strings/ missing from archive root"
# Each triple carries all three binaries, under its <triple>/ dir, mirroring the flat-root tree.
for b in $BINS; do
  echo "$LIST" | grep -qx "$WIN_TRIPLE/$b.exe" \
    && echo "ok   $WIN_TRIPLE/$b.exe present" \
    || fail "$WIN_TRIPLE/$b.exe missing from archive"
  echo "$LIST" | grep -qx "$LINUX_TRIPLE/$b" \
    && echo "ok   $LINUX_TRIPLE/$b present" \
    || fail "$LINUX_TRIPLE/$b missing from archive"
done
# Negative: the SHARED files must NOT sit under a triple (would not be shared), and no nested wrapper.
if echo "$LIST" | grep -qE "^$WIN_TRIPLE/(manifest\.toml|strings/)"; then
  fail "manifest/strings duplicated under a triple — they must be shared at root only"
else
  echo "ok   manifest/strings not duplicated under a triple"
fi
# Negative: no UNRECOGNIZED top-level dir (spt-core would silently flatten it as a shared-root entry).
badtop=$(echo "$LIST" | awk -F/ 'NF>1 {print $1}' | sort -u | grep -vE "^(strings|$WIN_TRIPLE|$LINUX_TRIPLE)$" || true)
if [ -n "$badtop" ]; then
  fail "unrecognized top-level dir(s) [$badtop] — spt-core would mis-place them flat (silent footgun)"
else
  echo "ok   no stray/unrecognized top-level dirs (only strings/ + the two recognized triples)"
fi

# A fat archive REQUIRES min_spt_core_version >= 0.13.2 — the packer must refuse a lower floor.
# (Smoke the guard by reading the dry-run plan's advertised floor.)
plan=$(sh "$PACKER" 2>&1)
echo "$plan" | grep -qE "min_spt_core 0\.(1[3-9]|[2-9][0-9])" \
  && echo "ok   packer advertises a fat-capable floor (>= 0.13.2)" \
  || fail "packer floor advertisement missing/too low; plan=[$plan]"

[ "$rc" -eq 0 ] && echo "PASS: adapter-archive"
exit "$rc"

-# Asserts the archive-ROOT invariant that `spt adapter add --release` depends on: the packed .spt

-# holds manifest.toml (named exactly) + strings/ + the three tool binaries at the archive root.

+# Asserts the MULTI-PLATFORM fat-archive invariants `spt adapter add --release` depends on (ADR-0024

+# W1, spt-core >= 0.13.2): one .spt holds the SHARED manifest.toml (named exactly) + strings/ at the

+# archive ROOT, plus each recognized target-triple's binaries under a <triple>/ dir mirroring the

+# flat-root tree. spt-core classifies on the top-level triple dir and flattens this node's triple into

+# the install dir, so a bare-name command token still resolves. [unit->REQ-DIST-ADAPTER-RELEASE]

ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)

fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

-[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"

+# The fat archive needs BOTH platforms' release binaries. Probe both triples.

+  [ -f "$ROOT/tools/$b/target/release/$b.exe" ] || have_all=0

+  [ -f "$ROOT/tools/$b/target/$LINUX_TRIPLE/release/$b" ] || have_all=0

-  # No release binaries on this host yet → assert the packer's REFUSE guard fires (no silent pass),

-  # and announce the skip of the build-the-archive assertion (no hidden coverage gap).

-  if sh "$PACKER" >/dev/null 2>&1; then

-    fail "packer should refuse (exit!=0) when release binaries are absent"

+  # Missing a platform → assert the packer's REFUSE guard fires (no silent pass), then skip the

+  # archive-build assertion (no hidden coverage gap). Both platforms are required for a fat archive.

+  if sh "$PACKER" --apply >/dev/null 2>&1; then

+    fail "packer should refuse (exit!=0) when a platform's binaries are absent"

-    echo "ok   packer refuses when binaries absent (guard works)"

+    echo "ok   packer refuses --apply when a platform's binaries absent (guard works)"

-  echo "SKIP: archive-build assertion — release binaries not present (run ci/{digest,psyche,idle-translate}/build.sh)"

+  echo "SKIP: archive-build assertion — need BOTH platforms (win native + linux cross-build) present"

-# Binaries present → build a real archive to a temp file and assert its structure.

+# Both platforms present → build a real fat archive to a temp file and assert its structure.

-# manifest.toml MUST be at the root (exact, no leading path) — the add --release contract.

+# SHARED manifest.toml MUST be at the root (exact, no leading path) — the add --release contract.

echo "$LIST" | grep -qx "manifest.toml" \

-  && echo "ok   manifest.toml at archive root" \

+  && echo "ok   shared manifest.toml at archive root" \

-# strings/ present (any entry under strings/).

+# SHARED strings/ at root (not under a triple).

-  || fail "strings/ missing from archive"

-# all three tool binaries present at root.

-echo "$LIST" | grep -qx "claude-spt-digest$EXE" \

-  && echo "ok   claude-spt-digest$EXE at root" \

-  || fail "claude-spt-digest$EXE missing from archive"

-echo "$LIST" | grep -qx "claude-spt-psyche$EXE" \

-  && echo "ok   claude-spt-psyche$EXE at root" \

-  || fail "claude-spt-psyche$EXE missing from archive"

-echo "$LIST" | grep -qx "cc-spt-idle-translate$EXE" \

-  && echo "ok   cc-spt-idle-translate$EXE at root" \

-  || fail "cc-spt-idle-translate$EXE missing from archive"

-# negative: no stray leading-dir wrapper (e.g. "adapter/manifest.toml").

-if echo "$LIST" | grep -q "/manifest.toml"; then

-  fail "manifest.toml appears under a subdir — archive root is wrong"

+  && echo "ok   shared strings/ at root" \

+  || fail "strings/ missing from archive root"

+# Each triple carries all three binaries, under its <triple>/ dir, mirroring the flat-root tree.

+  echo "$LIST" | grep -qx "$WIN_TRIPLE/$b.exe" \

+    && echo "ok   $WIN_TRIPLE/$b.exe present" \

+    || fail "$WIN_TRIPLE/$b.exe missing from archive"

+  echo "$LIST" | grep -qx "$LINUX_TRIPLE/$b" \

+    && echo "ok   $LINUX_TRIPLE/$b present" \

+    || fail "$LINUX_TRIPLE/$b missing from archive"

+# Negative: the SHARED files must NOT sit under a triple (would not be shared), and no nested wrapper.

+if echo "$LIST" | grep -qE "^$WIN_TRIPLE/(manifest\.toml|strings/)"; then

+  fail "manifest/strings duplicated under a triple — they must be shared at root only"

+  echo "ok   manifest/strings not duplicated under a triple"

+# Negative: no UNRECOGNIZED top-level dir (spt-core would silently flatten it as a shared-root entry).

+badtop=$(echo "$LIST" | awk -F/ 'NF>1 {print $1}' | sort -u | grep -vE "^(strings|$WIN_TRIPLE|$LINUX_TRIPLE)$" || true)

+  fail "unrecognized top-level dir(s) [$badtop] — spt-core would mis-place them flat (silent footgun)"

+  echo "ok   no stray/unrecognized top-level dirs (only strings/ + the two recognized triples)"

-# Per-OS asset NAMING (REQ-DIST-ADAPTER-PEROS): the default asset is adapter-<os>-<arch>.spt and the

-# name follows SPTC_OS/SPTC_ARCH overrides; the dry-run plan advertises the matching end-user

-# `--asset`. (Dry-run = no write; the cross-OS WARN to stderr on a mismatched host is non-fatal.)

-plan=$(SPTC_OS=linux SPTC_ARCH=aarch64 sh "$PACKER" 2>&1)

-echo "$plan" | grep -q "adapter-linux-aarch64\.spt" \

-  && echo "ok   per-OS asset name honors SPTC_OS/SPTC_ARCH (adapter-linux-aarch64.spt)" \

-  || fail "per-OS override name wrong; plan=[$plan]"

-echo "$plan" | grep -q -- "--asset adapter-linux-aarch64\.spt" \

-  && echo "ok   dry-run advertises the end-user --asset for that OS" \

-  || fail "dry-run missing '--asset adapter-linux-aarch64.spt'; plan=[$plan]"

-echo "$hostplan" | grep -qE "adapter-(windows|linux|macos|unknown)-[A-Za-z0-9_]+\.spt" \

-  && echo "ok   host default asset name matches adapter-<os>-<arch>.spt" \

-  || fail "host default asset name not adapter-<os>-<arch>.spt; plan=[$hostplan]"

+# A fat archive REQUIRES min_spt_core_version >= 0.13.2 — the packer must refuse a lower floor.

+# (Smoke the guard by reading the dry-run plan's advertised floor.)

+  && echo "ok   packer advertises a fat-capable floor (>= 0.13.2)" \

+  || fail "packer floor advertisement missing/too low; plan=[$plan]"

[ "$rc" -eq 0 ] && echo "PASS: adapter-archive"

#!/bin/sh
# Unit test for the adapter.spt packer (ci/publish/package-adapter.sh).
# Asserts the archive-ROOT invariant that `spt adapter add --release` depends on: the packed .spt
# holds manifest.toml (named exactly) + strings/ + the three tool binaries at the archive root.
# [unit->REQ-DIST-ADAPTER-RELEASE]
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
PACKER="$ROOT/ci/publish/package-adapter.sh"
rc=0
fail() { printf 'FAIL: %s\n' "$1"; rc=1; }

EXE=""
[ -f "$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest.exe" ] && EXE=".exe"
DIGEST="$ROOT/tools/claude-spt-digest/target/release/claude-spt-digest$EXE"
PSYCHE="$ROOT/tools/claude-spt-psyche/target/release/claude-spt-psyche$EXE"
IDLE="$ROOT/tools/cc-spt-idle-translate/target/release/cc-spt-idle-translate$EXE"

if [ ! -f "$DIGEST" ] || [ ! -f "$PSYCHE" ] || [ ! -f "$IDLE" ]; then
  # No release binaries on this host yet → assert the packer's REFUSE guard fires (no silent pass),
  # and announce the skip of the build-the-archive assertion (no hidden coverage gap).
  if sh "$PACKER" >/dev/null 2>&1; then
    fail "packer should refuse (exit!=0) when release binaries are absent"
  else
    echo "ok   packer refuses when binaries absent (guard works)"
  fi
  echo "SKIP: archive-build assertion — release binaries not present (run ci/{digest,psyche,idle-translate}/build.sh)"
  exit "$rc"
fi

# Binaries present → build a real archive to a temp file and assert its structure.
TMP=$(mktemp -d "${TMPDIR:-/tmp}/sptc-archtest.XXXXXX") || { echo "FAIL: mktemp"; exit 1; }
trap 'rm -rf "$TMP"' EXIT
OUT="$TMP/adapter.spt"

if ADAPTER_SPT_OUT="$OUT" sh "$PACKER" --apply >/dev/null 2>&1; then
  echo "ok   packer --apply succeeded"
else
  fail "packer --apply exited non-zero"
  exit "$rc"
fi

[ -f "$OUT" ] || { fail "no archive written at $OUT"; exit "$rc"; }

LIST=$(tar -tzf "$OUT")
# manifest.toml MUST be at the root (exact, no leading path) — the add --release contract.
echo "$LIST" | grep -qx "manifest.toml" \
  && echo "ok   manifest.toml at archive root" \
  || fail "manifest.toml not at archive root (add --release would reject)"
# strings/ present (any entry under strings/).
echo "$LIST" | grep -q "^strings/" \
  && echo "ok   strings/ present" \
  || fail "strings/ missing from archive"
# all three tool binaries present at root.
echo "$LIST" | grep -qx "claude-spt-digest$EXE" \
  && echo "ok   claude-spt-digest$EXE at root" \
  || fail "claude-spt-digest$EXE missing from archive"
echo "$LIST" | grep -qx "claude-spt-psyche$EXE" \
  && echo "ok   claude-spt-psyche$EXE at root" \
  || fail "claude-spt-psyche$EXE missing from archive"
echo "$LIST" | grep -qx "cc-spt-idle-translate$EXE" \
  && echo "ok   cc-spt-idle-translate$EXE at root" \
  || fail "cc-spt-idle-translate$EXE missing from archive"
# negative: no stray leading-dir wrapper (e.g. "adapter/manifest.toml").
if echo "$LIST" | grep -q "/manifest.toml"; then
  fail "manifest.toml appears under a subdir — archive root is wrong"
else
  echo "ok   no nested manifest.toml"
fi

# Per-OS asset NAMING (REQ-DIST-ADAPTER-PEROS): the default asset is adapter-<os>-<arch>.spt and the
# name follows SPTC_OS/SPTC_ARCH overrides; the dry-run plan advertises the matching end-user
# `--asset`. (Dry-run = no write; the cross-OS WARN to stderr on a mismatched host is non-fatal.)
# [unit->REQ-DIST-ADAPTER-PEROS]
plan=$(SPTC_OS=linux SPTC_ARCH=aarch64 sh "$PACKER" 2>&1)
echo "$plan" | grep -q "adapter-linux-aarch64\.spt" \
  && echo "ok   per-OS asset name honors SPTC_OS/SPTC_ARCH (adapter-linux-aarch64.spt)" \
  || fail "per-OS override name wrong; plan=[$plan]"
echo "$plan" | grep -q -- "--asset adapter-linux-aarch64\.spt" \
  && echo "ok   dry-run advertises the end-user --asset for that OS" \
  || fail "dry-run missing '--asset adapter-linux-aarch64.spt'; plan=[$plan]"
hostplan=$(sh "$PACKER" 2>&1)
echo "$hostplan" | grep -qE "adapter-(windows|linux|macos|unknown)-[A-Za-z0-9_]+\.spt" \
  && echo "ok   host default asset name matches adapter-<os>-<arch>.spt" \
  || fail "host default asset name not adapter-<os>-<arch>.spt; plan=[$hostplan]"

[ "$rc" -eq 0 ] && echo "PASS: adapter-archive"
exit "$rc"


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && sh tests/adapter-archive.sh 2>&1; echo "TEST_RC=$?"

Run rewritten adapter-archive unit test


ok   packer --apply succeeded
ok   shared manifest.toml at archive root
ok   shared strings/ at root
ok   x86_64-pc-windows-msvc/claude-spt-digest.exe present
ok   x86_64-unknown-linux-gnu/claude-spt-digest present
ok   x86_64-pc-windows-msvc/claude-spt-psyche.exe present
ok   x86_64-unknown-linux-gnu/claude-spt-psyche present
ok   x86_64-pc-windows-msvc/cc-spt-idle-translate.exe present
ok   x86_64-unknown-linux-gnu/cc-spt-idle-translate present
ok   manifest/strings not duplicated under a triple
ok   no stray/unrecognized top-level dirs (only strings/ + the two recognized triples)
ok   packer advertises a fat-capable floor (>= 0.13.2)
PASS: adapter-archive
TEST_RC=0


Unit test green for the fat layout. Update the `REQ-DIST-ADAPTER-PEROS` requirement text to the multi-platform model (its doc/impl/unit descriptions reference the old per-OS scheme).


230	[[requirements]]
231	id = "REQ-DIST-ADAPTER-PEROS"
232	title = "The adapter.spt asset is the tool binaries' platform, so it ships PER-OS: the packer emits `adapter-<os>-<arch>.spt` (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64) and /sptc:setup detects the host os+arch and passes the matching `--asset adapter-<os>-<arch>.spt` to `adapter add --release` (spt's `--asset` flag is caller-named; default adapter.spt). v1 OSes = windows + linux."
233	required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md per-OS publish section (native per-OS build OR cross-build Linux FROM Windows via cargo-zigbuild + SPTC_TARGET — proven 2026-06-16, real ELF x86-64 binaries; bare `cargo build --target` fails `cc not found`, zig supplies the linker) + the setup body's os-detection step. impl: ci/publish/package-adapter.sh emits adapter-<os>-<arch>.spt (host-derived os/arch, overridable via SPTC_OS/SPTC_ARCH; SPTC_TARGET points at target/<triple>/release for a cross build) + adapter/strings/skills/setup.md & plugin/sptc/skills/setup/SKILL.md detect os/arch (uname) → `--asset`. unit: tests/adapter-archive.sh asserts the per-OS asset NAME (adapter-<os>-<arch>.spt) + the root invariant holds for it. int (real per-OS `--asset` acquire) folds into release-acquire-int.sh once the 2nd-OS asset is published on a release; both adapter-windows-x86_64.spt + adapter-linux-x86_64.spt now pack locally (linux verified = ELF GNU/Linux). See REQ-DIST-ADAPTER-RELEASE.

[[requirements]]
id = "REQ-DIST-ADAPTER-PEROS"
title = "The adapter.spt asset is the tool binaries' platform, so it ships PER-OS: the packer emits `adapter-<os>-<arch>.spt` (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64) and /sptc:setup detects the host os+arch and passes the matching `--asset adapter-<os>-<arch>.spt` to `adapter add --release` (spt's `--asset` flag is caller-named; default adapter.spt). v1 OSes = windows + linux."
required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md per-OS publish section (native per-OS build OR cross-build Linux FROM Windows via cargo-zigbuild + SPTC_TARGET — proven 2026-06-16, real ELF x86-64 binaries; bare `cargo build --target` fails `cc not found`, zig supplies the linker) + the setup body's os-detection step. impl: ci/publish/package-adapter.sh emits adapter-<os>-<arch>.spt (host-derived os/arch, overridable via SPTC_OS/SPTC_ARCH; SPTC_TARGET points at target/<triple>/release for a cross build) + adapter/strings/skills/setup.md & plugin/sptc/skills/setup/SKILL.md detect os/arch (uname) → `--asset`. unit: tests/adapter-archive.sh asserts the per-OS asset NAME (adapter-<os>-<arch>.spt) + the root invariant holds for it. int (real per-OS `--asset` acquire) folds into release-acquire-int.sh once the 2nd-OS asset is published on a release; both adapter-windows-x86_64.spt + adapter-linux-x86_64.spt now pack locally (linux verified = ELF GNU/Linux). See REQ-DIST-ADAPTER-RELEASE.


Edit

title = "The adapter.spt asset is the tool binaries' platform, so it ships PER-OS: the packer emits `adapter-<os>-<arch>.spt` (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64) and /sptc:setup detects the host os+arch and passes the matching `--asset adapter-<os>-<arch>.spt` to `adapter add --release` (spt's `--asset` flag is caller-named; default adapter.spt). v1 OSes = windows + linux."
required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md per-OS publish section (native per-OS build OR cross-build Linux FROM Windows via cargo-zigbuild + SPTC_TARGET — proven 2026-06-16, real ELF x86-64 binaries; bare `cargo build --target` fails `cc not found`, zig supplies the linker) + the setup body's os-detection step. impl: ci/publish/package-adapter.sh emits adapter-<os>-<arch>.spt (host-derived os/arch, overridable via SPTC_OS/SPTC_ARCH; SPTC_TARGET points at target/<triple>/release for a cross build) + adapter/strings/skills/setup.md & plugin/sptc/skills/setup/SKILL.md detect os/arch (uname) → `--asset`. unit: tests/adapter-archive.sh asserts the per-OS asset NAME (adapter-<os>-<arch>.spt) + the root invariant holds for it. int (real per-OS `--asset` acquire) folds into release-acquire-int.sh once the 2nd-OS asset is published on a release; both adapter-windows-x86_64.spt + adapter-linux-x86_64.spt now pack locally (linux verified = ELF GNU/Linux). See REQ-DIST-ADAPTER-RELEASE.

title = "The adapter ships ONE host-agnostic MULTI-PLATFORM `adapter.spt` (ADR-0024 W1, spt-core >= 0.13.2): a single fat archive bundles every recognized target-triple's tool binaries under a `<triple>/` dir beside the SHARED manifest.toml + strings/ at the archive root; install classifies the triple dirs and flattens THIS node's triple into the install dir, so a bare-name command token resolves (REQ-INSTALL-11). `spt adapter add --release` (default asset adapter.spt) + the `[update] gh_release` avenue auto-resolve the host's binaries from the one asset — RETIRING the F-014 per-OS stopgap (the old single-OS adapter.spt-by-default that broke on a foreign host). v1 triples = x86_64-pc-windows-msvc + x86_64-unknown-linux-gnu (the only two spt-core recognizes in a fat archive; platforms beyond these ship a separate single-triple asset via `--asset`)."
required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md fat-archive packaging section (win native + linux cross-build via cargo-zigbuild — proven 2026-06-16, real ELF x86-64; bare `cargo build --target` fails `cc not found`, zig supplies the linker). impl: ci/publish/package-adapter.sh stages the shared root (manifest.toml + strings/) + each <triple>/ (win .exe, linux ELF) into ONE adapter.spt, refuses a < 0.13.2 floor, and guards the unrecognized-top-level-dir footgun. unit: tests/adapter-archive.sh asserts shared-root + both <triple>/ binary sets + no stray top-level dir + the fat-capable floor. int (real spt-core extract+flatten of the fat asset) folds into release-acquire-int.sh once the fat adapter.spt is published on a release; the win-triple flatten was LOCAL-dogfooded 2026-06-22 (extract → flatten win triple → translate-proof + digest-proof OK). NOTE: /sptc:setup's os-detection + `--asset` step is now OPTIONAL (the default fat adapter.spt is host-agnostic) — retained as harmless; simplifying setup to the bare default is a follow-on. See REQ-DIST-ADAPTER-RELEASE."


go ahead and apply (C) to the existing release


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.spt asset is the tool binaries' platform, so it ships PER-OS: the packer emits `adapter-<os>-<arch>.spt` (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64) and /sptc:setup detects the host os+arch and passes the matching `--asset adapter-<os>-<arch>.spt` to `adapter add --release` (spt's `--asset` flag is caller-named; default adapter.spt). v1 OSes = windows + linux."
required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md per-OS publish section (native per-OS build OR cross-build Linux FROM Windows via cargo-zigbuild + SPTC_TARGET — proven 2026-06-16, real ELF x86-64 binaries; bare `cargo build --target` fails `cc not found`, zig supplies the linker) + the setup body's os-detection step. impl: ci/publish/package-adapter.sh emits adapter-<os>-<arch>.spt (host-derived os/arch, overridable via SPTC_OS/SPTC_ARCH; SPTC_TARGET points at target/<triple>/release for a cross build) + adapter/strings/skills/setup.md & plugin/sptc/skills/setup/SKILL.md detect os/arch (uname) → `--asset`. unit: tests/adapter-archive.sh asserts the per-OS asset NAME (adapter-<os>-<arch>.spt) + the root invariant holds for it. int (real per-OS `--asset` acquire) folds into release-acquire-int.sh once the 2nd-OS asset is published on a release; both adapter-windows-x86_64.spt + adapter-linux-x86_64.spt now pack locally (linux verified = ELF GNU/Linux). See REQ-DIST-ADAPTER-RELEASE.

title = "The adapter ships ONE host-agnostic MULTI-PLATFORM `adapter.spt` (ADR-0024 W1, spt-core >= 0.13.2): a single fat archive bundles every recognized target-triple's tool binaries under a `<triple>/` dir beside the SHARED manifest.toml + strings/ at the archive root; install classifies the triple dirs and flattens THIS node's triple into the install dir, so a bare-name command token resolves (REQ-INSTALL-11). `spt adapter add --release` (default asset adapter.spt) + the `[update] gh_release` avenue auto-resolve the host's binaries from the one asset — RETIRING the F-014 per-OS stopgap (the old single-OS adapter.spt-by-default that broke on a foreign host). v1 triples = x86_64-pc-windows-msvc + x86_64-unknown-linux-gnu (the only two spt-core recognizes in a fat archive; platforms beyond these ship a separate single-triple asset via `--asset`)."
required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md fat-archive packaging section (win native + linux cross-build via cargo-zigbuild — proven 2026-06-16, real ELF x86-64; bare `cargo build --target` fails `cc not found`, zig supplies the linker). impl: ci/publish/package-adapter.sh stages the shared root (manifest.toml + strings/) + each <triple>/ (win .exe, linux ELF) into ONE adapter.spt, refuses a < 0.13.2 floor, and guards the unrecognized-top-level-dir footgun. unit: tests/adapter-archive.sh asserts shared-root + both <triple>/ binary sets + no stray top-level dir + the fat-capable floor. int (real spt-core extract+flatten of the fat asset) folds into release-acquire-int.sh once the fat adapter.spt is published on a release; the win-triple flatten was LOCAL-dogfooded 2026-06-22 (extract → flatten win triple → translate-proof + digest-proof OK). NOTE: /sptc:setup's os-detection + `--asset` step is now OPTIONAL (the default fat adapter.spt is host-agnostic) — retained as harmless; simplifying setup to the bare default is a follow-on. See REQ-DIST-ADAPTER-RELEASE."

-title = "The adapter.spt asset is the tool binaries' platform, so it ships PER-OS: the packer emits `adapter-<os>-<arch>.spt` (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64) and /sptc:setup detects the host os+arch and passes the matching `--asset adapter-<os>-<arch>.spt` to `adapter add --release` (spt's `--asset` flag is caller-named; default adapter.spt). v1 OSes = windows + linux."

-required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md per-OS publish section (native per-OS build OR cross-build Linux FROM Windows via cargo-zigbuild + SPTC_TARGET — proven 2026-06-16, real ELF x86-64 binaries; bare `cargo build --target` fails `cc not found`, zig supplies the linker) + the setup body's os-detection step. impl: ci/publish/package-adapter.sh emits adapter-<os>-<arch>.spt (host-derived os/arch, overridable via SPTC_OS/SPTC_ARCH; SPTC_TARGET points at target/<triple>/release for a cross build) + adapter/strings/skills/setup.md & plugin/sptc/skills/setup/SKILL.md detect os/arch (uname) → `--asset`. unit: tests/adapter-archive.sh asserts the per-OS asset NAME (adapter-<os>-<arch>.spt) + the root invariant holds for it. int (real per-OS `--asset` acquire) folds into release-acquire-int.sh once the 2nd-OS asset is published on a release; both adapter-windows-x86_64.spt + adapter-linux-x86_64.spt now pack locally (linux verified = ELF GNU/Linux). See REQ-DIST-ADAPTER-RELEASE.

+title = "The adapter ships ONE host-agnostic MULTI-PLATFORM `adapter.spt` (ADR-0024 W1, spt-core >= 0.13.2): a single fat archive bundles every recognized target-triple's tool binaries under a `<triple>/` dir beside the SHARED manifest.toml + strings/ at the archive root; install classifies the triple dirs and flattens THIS node's triple into the install dir, so a bare-name command token resolves (REQ-INSTALL-11). `spt adapter add --release` (default asset adapter.spt) + the `[update] gh_release` avenue auto-resolve the host's binaries from the one asset — RETIRING the F-014 per-OS stopgap (the old single-OS adapter.spt-by-default that broke on a foreign host). v1 triples = x86_64-pc-windows-msvc + x86_64-unknown-linux-gnu (the only two spt-core recognizes in a fat archive; platforms beyond these ship a separate single-triple asset via `--asset`)."

+required_stages = ["doc", "impl", "unit"]  # doc: docs/RELEASE-RUNBOOK.md fat-archive packaging section (win native + linux cross-build via cargo-zigbuild — proven 2026-06-16, real ELF x86-64; bare `cargo build --target` fails `cc not found`, zig supplies the linker). impl: ci/publish/package-adapter.sh stages the shared root (manifest.toml + strings/) + each <triple>/ (win .exe, linux ELF) into ONE adapter.spt, refuses a < 0.13.2 floor, and guards the unrecognized-top-level-dir footgun. unit: tests/adapter-archive.sh asserts shared-root + both <triple>/ binary sets + no stray top-level dir + the fat-capable floor. int (real spt-core extract+flatten of the fat asset) folds into release-acquire-int.sh once the fat adapter.spt is published on a release; the win-triple flatten was LOCAL-dogfooded 2026-06-22 (extract → flatten win triple → translate-proof + digest-proof OK). NOTE: /sptc:setup's os-detection + `--asset` step is now OPTIONAL (the default fat adapter.spt is host-agnostic) — retained as harmless; simplifying setup to the bare default is a follow-on. See REQ-DIST-ADAPTER-RELEASE."

# ── Milestone: V13-ADAPTER (spt-core harness-contract v0.13.0 seams) — minted 2026-06-20 from

# doyle's owl: the v0.13.0 harness contract is PUBLISHED (sabermage.github.io/spt-releases/


Update the RELEASE-RUNBOOK packaging section (REQ-PEROS doc evidence) to the fat model + the F-014 finding status. Find the runbook's packaging section.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && grep -n -iE "per-os|per OS|adapter-.*\.spt|package-adapter|cargo.?zigbuild|F-014|## .*[Pp]ackag|## .*[Pp]er-OS|adapter\.spt|--asset" docs/RELEASE-RUNBOOK.md | head -30

Locate runbook packaging/per-OS sections


128:## adapter manifest publish — the `adapter.spt` release asset
131:The CC adapter manifest (publish target #2) ships to end users as an **`adapter.spt`** GitHub
136:- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes
137:  `dist/adapter-<os>-<arch>.spt`. It validates the manifest, requires the built tool binaries (`sh
141:- **Publish:** attach every per-OS `adapter-<os>-<arch>.spt` to the SAME GitHub release on
142:  `SaberMage/spt-claude-code`. End users `spt adapter add --release SaberMage/spt-claude-code --asset
143:  adapter-<os>-<arch>.spt` (or `--tag <ver>` to pin). `/sptc:setup` derives the host's os/arch and
144:  passes the matching `--asset` automatically.
145:- **ALSO attach a default `adapter.spt`** (a copy of the host-OS build — windows for the windows-centric
146:  fleet) **until F-014 lands.** The `[update] avenue = "gh_release"` self-update fetches the fixed default
147:  asset name `adapter.spt` (no per-OS resolution, no `--asset` override — F-014). Without it,
148:  `spt adapter update` 404s even though acquisition via `--asset adapter-<os>-<arch>.spt` works. Verified
149:  the hard way on v0.5.0 (2026-06-20). Drop this once F-014 ships per-OS update resolution. Note F-015:
154:- **Per-OS (REQ-DIST-ADAPTER-PEROS) — the asset carries native binaries, so it is platform-specific.**
155:  v1 ships **windows + linux**. spt's `--release` takes a caller-named `--asset` (default
156:  `adapter.spt`) — it does NOT auto-pick per-OS, so we name + select. Naming: `adapter-<os>-<arch>.spt`
158:  `SPTC_OS` / `SPTC_ARCH` (+ `SPTC_TARGET` = the cargo target triple for a cross build). Per-OS publish:
160:    ci/publish/package-adapter.sh --apply` → `dist/adapter-windows-x86_64.spt`.
162:    — cargo, no `.exe`) → `dist/adapter-linux-x86_64.spt`.
165:    link needs a Linux linker), so use **`cargo-zigbuild`** (zig supplies the cross-linker):
167:    # one-time: install zig (a self-contained binary) + cargo-zigbuild
168:    cargo install cargo-zigbuild
174:    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-digest/Cargo.toml
175:    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-psyche/Cargo.toml
177:    SPTC_OS=linux SPTC_ARCH=x86_64 SPTC_TARGET=x86_64-unknown-linux-gnu sh ci/publish/package-adapter.sh --apply
179:    → `dist/adapter-linux-x86_64.spt` carrying real `ELF x86-64 GNU/Linux` binaries (verified).
182:    strings) requests `--asset adapter-<os>-<arch>.spt`, so it must ship **with** a release that
183:    carries those per-OS assets — never before. Sequence: cut the per-OS-asset release FIRST, then


128	## adapter manifest publish — the `adapter.spt` release asset
129
130	<!-- [doc->REQ-DIST-ADAPTER-RELEASE] -->
131	The CC adapter manifest (publish target #2) ships to end users as an **`adapter.spt`** GitHub
132	**release asset** on this monorepo, acquired with **`spt adapter add --release SaberMage/spt-claude-code`**
133	(doyle's `--release` source; needs **spt v0.7.3+ / counter 15** — not in 0.7.2). No dedicated
134	adapter repo: the asset is packed straight from `adapter/`.
135
136	- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes
137	  `dist/adapter-<os>-<arch>.spt`. It validates the manifest, requires the built tool binaries (`sh
138	  ci/digest/build.sh && sh ci/psyche/build.sh`), and tars the archive **ROOT** = `manifest.toml`
139	  (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name), `strings/`, and the two
140	  binaries. Never uploads (operator's step).
141	- **Publish:** attach every per-OS `adapter-<os>-<arch>.spt` to the SAME GitHub release on
142	  `SaberMage/spt-claude-code`. End users `spt adapter add --release SaberMage/spt-claude-code --asset
143	  adapter-<os>-<arch>.spt` (or `--tag <ver>` to pin). `/sptc:setup` derives the host's os/arch and
144	  passes the matching `--asset` automatically.
145	- **ALSO attach a default `adapter.spt`** (a copy of the host-OS build — windows for the windows-centric
146	  fleet) **until F-014 lands.** The `[update] avenue = "gh_release"` self-update fetches the fixed default
147	  asset name `adapter.spt` (no per-OS resolution, no `--asset` override — F-014). Without it,
148	  `spt adapter update` 404s even though acquisition via `--asset adapter-<os>-<arch>.spt` works. Verified
149	  the hard way on v0.5.0 (2026-06-20). Drop this once F-014 ships per-OS update resolution. Note F-015:
150	  on Windows the update can't overwrite a binary locked by a running live agent (`tar exit 1`) — manifest
151	  + strings still apply, so a strings/hook-only release lands fine; a binary-changing release needs every
152	  live claude-spt agent stopped first.
153	<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
154	- **Per-OS (REQ-DIST-ADAPTER-PEROS) — the asset carries native binaries, so it is platform-specific.**
155	  v1 ships **windows + linux**. spt's `--release` takes a caller-named `--asset` (default
156	  `adapter.spt`) — it does NOT auto-pick per-OS, so we name + select. Naming: `adapter-<os>-<arch>.spt`
157	  (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64), host-derived from `uname`, overridable with
158	  `SPTC_OS` / `SPTC_ARCH` (+ `SPTC_TARGET` = the cargo target triple for a cross build). Per-OS publish:
159	  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
160	    ci/publish/package-adapter.sh --apply` → `dist/adapter-windows-x86_64.spt`.
161	  - **Linux on a Linux host/runner (native):** the identical commands (the build scripts are portable
162	    — cargo, no `.exe`) → `dist/adapter-linux-x86_64.spt`.
163	  - **Linux cross-built FROM Windows (proven 2026-06-16):** bare `cargo build --target
164	    x86_64-unknown-linux-gnu` fails (`error: linker 'cc' not found` — the crate compiles, only the
165	    link needs a Linux linker), so use **`cargo-zigbuild`** (zig supplies the cross-linker):
166	    ```sh
167	    # one-time: install zig (a self-contained binary) + cargo-zigbuild
168	    cargo install cargo-zigbuild
169	    # download zig (e.g. 0.14.1) to a dir OUTSIDE the repo (do NOT keep it under tools/ — it's a
170	    # traceable-reqs scan root and zig's bundled libc C-headers trip the scanner), put zig.exe on PATH:
171	    #   e.g. ~/.sptc-zig/zig-x86_64-windows-0.14.1/zig.exe  →  export PATH="$HOME/.sptc-zig/...:$PATH"
172	    rustup target add x86_64-unknown-linux-gnu
173	    # cross-build both tool crates with zig as the linker
174	    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-digest/Cargo.toml
175	    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/claude-spt-psyche/Cargo.toml
176	    # pack the linux asset (SPTC_TARGET points the packer at target/<triple>/release)
177	    SPTC_OS=linux SPTC_ARCH=x86_64 SPTC_TARGET=x86_64-unknown-linux-gnu sh ci/publish/package-adapter.sh --apply
178	    ```
179	    → `dist/adapter-linux-x86_64.spt` carrying real `ELF x86-64 GNU/Linux` binaries (verified).
180	  - Attach BOTH assets to the one release.
181	  - **Coupling (do not skew):** the os-detecting `/sptc:setup` floor (cplugs skeleton + adapter
182	    strings) requests `--asset adapter-<os>-<arch>.spt`, so it must ship **with** a release that
183	    carries those per-OS assets — never before. Sequence: cut the per-OS-asset release FIRST, then
184	    republish the cplugs skeleton (next `plugin.json` bump) that points at it. A skeleton bump ahead
185	    of the assets would 404 end-user setup.
186	- **Binaries (install-dir resolution — REQ-INSTALL-11).** A `--release`/`--github` acquisition extracts
187	  the `.spt` archive's `manifest.toml` + `strings/` **+ both tool binaries** into the adapter install
188	  dir (`…/adapters/_github/<safe>/`). The command templates use **bare names** (`claude-spt-digest`,
189	  `claude-spt-psyche`), which spt-core resolves **from that install dir** (v0.8.0 Feature B /

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

- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes
  `dist/adapter-<os>-<arch>.spt`. It validates the manifest, requires the built tool binaries (`sh
  ci/digest/build.sh && sh ci/psyche/build.sh`), and tars the archive **ROOT** = `manifest.toml`
  (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name), `strings/`, and the two
  binaries. Never uploads (operator's step).
- **Publish:** attach every per-OS `adapter-<os>-<arch>.spt` to the SAME GitHub release on
  `SaberMage/spt-claude-code`. End users `spt adapter add --release SaberMage/spt-claude-code --asset
  adapter-<os>-<arch>.spt` (or `--tag <ver>` to pin). `/sptc:setup` derives the host's os/arch and
  passes the matching `--asset` automatically.
- **ALSO attach a default `adapter.spt`** (a copy of the host-OS build — windows for the windows-centric
  fleet) **until F-014 lands.** The `[update] avenue = "gh_release"` self-update fetches the fixed default
  asset name `adapter.spt` (no per-OS resolution, no `--asset` override — F-014). Without it,
  `spt adapter update` 404s even though acquisition via `--asset adapter-<os>-<arch>.spt` works. Verified
  the hard way on v0.5.0 (2026-06-20). Drop this once F-014 ships per-OS update resolution. Note F-015:
  on Windows the update can't overwrite a binary locked by a running live agent (`tar exit 1`) — manifest
  + strings still apply, so a strings/hook-only release lands fine; a binary-changing release needs every
  live claude-spt agent stopped first.
<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Per-OS (REQ-DIST-ADAPTER-PEROS) — the asset carries native binaries, so it is platform-specific.**
  v1 ships **windows + linux**. spt's `--release` takes a caller-named `--asset` (default
  `adapter.spt`) — it does NOT auto-pick per-OS, so we name + select. Naming: `adapter-<os>-<arch>.spt`
  (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64), host-derived from `uname`, overridable with
  `SPTC_OS` / `SPTC_ARCH` (+ `SPTC_TARGET` = the cargo target triple for a cross build). Per-OS publish:
  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
    ci/publish/package-adapter.sh --apply` → `dist/adapter-windows-x86_64.spt`.
  - **Linux on a Linux host/runner (native):** the identical commands (the build scripts are portable
    — cargo, no `.exe`) → `dist/adapter-linux-x86_64.spt`.
  - **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
    # 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
    # cross-build both tool crates with zig as the linker
    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
    # pack the linux asset (SPTC_TARGET points the packer at target/<triple>/release)
    SPTC_OS=linux SPTC_ARCH=x86_64 SPTC_TARGET=x86_64-unknown-linux-gnu sh ci/publish/package-adapter.sh --apply
    ```
    → `dist/adapter-linux-x86_64.spt` carrying real `ELF x86-64 GNU/Linux` binaries (verified).
  - Attach BOTH assets to the one release.
  - **Coupling (do not skew):** the os-detecting `/sptc:setup` floor (cplugs skeleton + adapter
    strings) requests `--asset adapter-<os>-<arch>.spt`, so it must ship **with** a release that
    carries those per-OS assets — never before. Sequence: cut the per-OS-asset release FIRST, then
    republish the cplugs skeleton (next `plugin.json` bump) that points at it. A skeleton bump ahead
    of the assets would 404 end-user setup.
- **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 /


Rewrite the runbook packaging section for the fat-archive model (keeping the cross-build recipe, which still feeds the fat packer; retiring the F-014 stopgap + per-OS-asset coupling).


Edit

- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes
  `dist/adapter-<os>-<arch>.spt`. It validates the manifest, requires the built tool binaries (`sh
  ci/digest/build.sh && sh ci/psyche/build.sh`), and tars the archive **ROOT** = `manifest.toml`
  (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name), `strings/`, and the two
  binaries. Never uploads (operator's step).
- **Publish:** attach every per-OS `adapter-<os>-<arch>.spt` to the SAME GitHub release on
  `SaberMage/spt-claude-code`. End users `spt adapter add --release SaberMage/spt-claude-code --asset
  adapter-<os>-<arch>.spt` (or `--tag <ver>` to pin). `/sptc:setup` derives the host's os/arch and
  passes the matching `--asset` automatically.
- **ALSO attach a default `adapter.spt`** (a copy of the host-OS build — windows for the windows-centric
  fleet) **until F-014 lands.** The `[update] avenue = "gh_release"` self-update fetches the fixed default
  asset name `adapter.spt` (no per-OS resolution, no `--asset` override — F-014). Without it,
  `spt adapter update` 404s even though acquisition via `--asset adapter-<os>-<arch>.spt` works. Verified
  the hard way on v0.5.0 (2026-06-20). Drop this once F-014 ships per-OS update resolution. Note F-015:
  on Windows the update can't overwrite a binary locked by a running live agent (`tar exit 1`) — manifest
  + strings still apply, so a strings/hook-only release lands fine; a binary-changing release needs every
  live claude-spt agent stopped first.
<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Per-OS (REQ-DIST-ADAPTER-PEROS) — the asset carries native binaries, so it is platform-specific.**
  v1 ships **windows + linux**. spt's `--release` takes a caller-named `--asset` (default
  `adapter.spt`) — it does NOT auto-pick per-OS, so we name + select. Naming: `adapter-<os>-<arch>.spt`
  (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64), host-derived from `uname`, overridable with
  `SPTC_OS` / `SPTC_ARCH` (+ `SPTC_TARGET` = the cargo target triple for a cross build). Per-OS publish:
  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
    ci/publish/package-adapter.sh --apply` → `dist/adapter-windows-x86_64.spt`.
  - **Linux on a Linux host/runner (native):** the identical commands (the build scripts are portable
    — cargo, no `.exe`) → `dist/adapter-linux-x86_64.spt`.
  - **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
    # 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
    # cross-build both tool crates with zig as the linker
    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
    # pack the linux asset (SPTC_TARGET points the packer at target/<triple>/release)
    SPTC_OS=linux SPTC_ARCH=x86_64 SPTC_TARGET=x86_64-unknown-linux-gnu sh ci/publish/package-adapter.sh --apply
    ```
    → `dist/adapter-linux-x86_64.spt` carrying real `ELF x86-64 GNU/Linux` binaries (verified).
  - Attach BOTH assets to the one release.
  - **Coupling (do not skew):** the os-detecting `/sptc:setup` floor (cplugs skeleton + adapter
    strings) requests `--asset adapter-<os>-<arch>.spt`, so it must ship **with** a release that
    carries those per-OS assets — never before. Sequence: cut the per-OS-asset release FIRST, then
    republish the cplugs skeleton (next `plugin.json` bump) that points at it. A skeleton bump ahead
    of the assets would 404 end-user setup.

Since v0.6.0 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).
- **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
    # 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.)


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

- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes
  `dist/adapter-<os>-<arch>.spt`. It validates the manifest, requires the built tool binaries (`sh
  ci/digest/build.sh && sh ci/psyche/build.sh`), and tars the archive **ROOT** = `manifest.toml`
  (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name), `strings/`, and the two
  binaries. Never uploads (operator's step).
- **Publish:** attach every per-OS `adapter-<os>-<arch>.spt` to the SAME GitHub release on
  `SaberMage/spt-claude-code`. End users `spt adapter add --release SaberMage/spt-claude-code --asset
  adapter-<os>-<arch>.spt` (or `--tag <ver>` to pin). `/sptc:setup` derives the host's os/arch and
  passes the matching `--asset` automatically.
- **ALSO attach a default `adapter.spt`** (a copy of the host-OS build — windows for the windows-centric
  fleet) **until F-014 lands.** The `[update] avenue = "gh_release"` self-update fetches the fixed default
  asset name `adapter.spt` (no per-OS resolution, no `--asset` override — F-014). Without it,
  `spt adapter update` 404s even though acquisition via `--asset adapter-<os>-<arch>.spt` works. Verified
  the hard way on v0.5.0 (2026-06-20). Drop this once F-014 ships per-OS update resolution. Note F-015:
  on Windows the update can't overwrite a binary locked by a running live agent (`tar exit 1`) — manifest
  + strings still apply, so a strings/hook-only release lands fine; a binary-changing release needs every
  live claude-spt agent stopped first.
<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Per-OS (REQ-DIST-ADAPTER-PEROS) — the asset carries native binaries, so it is platform-specific.**
  v1 ships **windows + linux**. spt's `--release` takes a caller-named `--asset` (default
  `adapter.spt`) — it does NOT auto-pick per-OS, so we name + select. Naming: `adapter-<os>-<arch>.spt`
  (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64), host-derived from `uname`, overridable with
  `SPTC_OS` / `SPTC_ARCH` (+ `SPTC_TARGET` = the cargo target triple for a cross build). Per-OS publish:
  - **Windows (native):** `sh ci/digest/build.sh && sh ci/psyche/build.sh && sh
    ci/publish/package-adapter.sh --apply` → `dist/adapter-windows-x86_64.spt`.
  - **Linux on a Linux host/runner (native):** the identical commands (the build scripts are portable
    — cargo, no `.exe`) → `dist/adapter-linux-x86_64.spt`.
  - **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
    # 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
    # cross-build both tool crates with zig as the linker
    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
    # pack the linux asset (SPTC_TARGET points the packer at target/<triple>/release)
    SPTC_OS=linux SPTC_ARCH=x86_64 SPTC_TARGET=x86_64-unknown-linux-gnu sh ci/publish/package-adapter.sh --apply
    ```
    → `dist/adapter-linux-x86_64.spt` carrying real `ELF x86-64 GNU/Linux` binaries (verified).
  - Attach BOTH assets to the one release.
  - **Coupling (do not skew):** the os-detecting `/sptc:setup` floor (cplugs skeleton + adapter
    strings) requests `--asset adapter-<os>-<arch>.spt`, so it must ship **with** a release that
    carries those per-OS assets — never before. Sequence: cut the per-OS-asset release FIRST, then
    republish the cplugs skeleton (next `plugin.json` bump) that points at it. A skeleton bump ahead
    of the assets would 404 end-user setup.

Since v0.6.0 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).
- **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
    # 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.)

(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/`.

-- **Pack:** `sh ci/publish/package-adapter.sh` (DRY-RUN) → `--apply` writes

-  `dist/adapter-<os>-<arch>.spt`. It validates the manifest, requires the built tool binaries (`sh

-  ci/digest/build.sh && sh ci/psyche/build.sh`), and tars the archive **ROOT** = `manifest.toml`

-  (renamed from `claude-spt.toml` — `adapter add` is root-only + exact-name), `strings/`, and the two

-  binaries. Never uploads (operator's step).

-- **Publish:** attach every per-OS `adapter-<os>-<arch>.spt` to the SAME GitHub release on

-  `SaberMage/spt-claude-code`. End users `spt adapter add --release SaberMage/spt-claude-code --asset

-  adapter-<os>-<arch>.spt` (or `--tag <ver>` to pin). `/sptc:setup` derives the host's os/arch and

-  passes the matching `--asset` automatically.

-- **ALSO attach a default `adapter.spt`** (a copy of the host-OS build — windows for the windows-centric

-  fleet) **until F-014 lands.** The `[update] avenue = "gh_release"` self-update fetches the fixed default

-  asset name `adapter.spt` (no per-OS resolution, no `--asset` override — F-014). Without it,

-  `spt adapter update` 404s even though acquisition via `--asset adapter-<os>-<arch>.spt` works. Verified

-  the hard way on v0.5.0 (2026-06-20). Drop this once F-014 ships per-OS update resolution. Note F-015:

-  on Windows the update can't overwrite a binary locked by a running live agent (`tar exit 1`) — manifest

-  + strings still apply, so a strings/hook-only release lands fine; a binary-changing release needs every

-- **Per-OS (REQ-DIST-ADAPTER-PEROS) — the asset carries native binaries, so it is platform-specific.**

-  v1 ships **windows + linux**. spt's `--release` takes a caller-named `--asset` (default

-  `adapter.spt`) — it does NOT auto-pick per-OS, so we name + select. Naming: `adapter-<os>-<arch>.spt`

-  (os ∈ windows|linux|macos, arch ∈ x86_64|aarch64), host-derived from `uname`, overridable with

-  `SPTC_OS` / `SPTC_ARCH` (+ `SPTC_TARGET` = the cargo target triple for a cross build). Per-OS publish:

+Since v0.6.0 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) +

+- **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/publish/package-adapter.sh --apply` → `dist/adapter-windows-x86_64.spt`.

-  - **Linux on a Linux host/runner (native):** the identical commands (the build scripts are portable

-    — cargo, no `.exe`) → `dist/adapter-linux-x86_64.spt`.

+    ci/idle-translate/build.sh` → `tools/*/target/release/*.exe`.

- **Linux cross-built FROM Windows (proven 2026-06-16):** bare `cargo build --target

link needs a Linux linker), so use **`cargo-zigbuild`** (zig supplies the cross-linker):

rustup target add x86_64-unknown-linux-gnu

-    # cross-build both tool crates with zig as the linker

-    # pack the linux asset (SPTC_TARGET points the packer at target/<triple>/release)

-    SPTC_OS=linux SPTC_ARCH=x86_64 SPTC_TARGET=x86_64-unknown-linux-gnu sh ci/publish/package-adapter.sh --apply

+    cargo zigbuild --release --target x86_64-unknown-linux-gnu --manifest-path tools/cc-spt-idle-translate/Cargo.toml

-    → `dist/adapter-linux-x86_64.spt` carrying real `ELF x86-64 GNU/Linux` binaries (verified).

-  - Attach BOTH assets to the one release.

-  - **Coupling (do not skew):** the os-detecting `/sptc:setup` floor (cplugs skeleton + adapter

-    strings) requests `--asset adapter-<os>-<arch>.spt`, so it must ship **with** a release that

-    carries those per-OS assets — never before. Sequence: cut the per-OS-asset release FIRST, then

-    republish the cplugs skeleton (next `plugin.json` bump) that points at it. A skeleton bump ahead

-    of the assets would 404 end-user setup.

+    → `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

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

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

+  - **`/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`,


Update the F-014 finding — now resolved by the fat archive.


OS; the active fleet is windows). **Needs (doyle):** per-OS resolution in the gh_release update avenue
— an `asset = "adapter-{os}-{arch}.spt"` placeholder, or host-derive like `--release` acquisition.

OS; the active fleet is windows). **Needs (doyle):** per-OS resolution in the gh_release update avenue
— an `asset = "adapter-{os}-{arch}.spt"` placeholder, or host-derive like `--release` acquisition.

- **RESOLVED-SHIPPED (spt-core v0.13.2, ADR-0024 W1) — applied to claude-spt 2026-06-22 (perri, C).**
  doyle's fix took the third path: NOT a per-OS asset placeholder, but **one host-agnostic
  multi-platform fat `.spt`** — a single archive bundles each recognized target-triple's binaries
  under a `<triple>/` dir beside the SHARED `manifest.toml` + `strings/` at root; install classifies
  the triple dirs and flattens this node's triple into the install dir (bare-name resolution
  preserved). So the `[update]` avenue's single fixed `asset = adapter.spt` is now correct on every
  host — no `{os}/{arch}` placeholder needed. **Adapter change (C):** `ci/publish/package-adapter.sh`
  now emits the single fat `dist/adapter.spt` (win `x86_64-pc-windows-msvc/` + linux
  `x86_64-unknown-linux-gnu/`), `min_spt_core_version` → 0.13.2 (mandatory for a fat archive), the
  F-014 windows-copy `adapter.spt` stopgap DROPPED. Win-triple flatten LOCAL-dogfooded (extract →
  flatten → translate-proof + digest-proof OK). **Layout was a published-docs GAP** (only in spt-core
  internal CONTEXT.md; not on docs-site/llms — confirmed with doyle, who is adding it to
  harness-contract + llms; not separately logged per his call). Constraint: only the two x86_64 triples
  are recognized in a fat archive today (an unrecognized dir silently flattens as a shared-root entry —
  the packer guards it); platforms beyond those ship a separate single-triple asset via `--asset`.


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

OS; the active fleet is windows). **Needs (doyle):** per-OS resolution in the gh_release update avenue
— an `asset = "adapter-{os}-{arch}.spt"` placeholder, or host-derive like `--release` acquisition.

- **RESOLVED-SHIPPED (spt-core v0.13.2, ADR-0024 W1) — applied to claude-spt 2026-06-22 (perri, C).**
  doyle's fix took the third path: NOT a per-OS asset placeholder, but **one host-agnostic
  multi-platform fat `.spt`** — a single archive bundles each recognized target-triple's binaries
  under a `<triple>/` dir beside the SHARED `manifest.toml` + `strings/` at root; install classifies
  the triple dirs and flattens this node's triple into the install dir (bare-name resolution
  preserved). So the `[update]` avenue's single fixed `asset = adapter.spt` is now correct on every
  host — no `{os}/{arch}` placeholder needed. **Adapter change (C):** `ci/publish/package-adapter.sh`
  now emits the single fat `dist/adapter.spt` (win `x86_64-pc-windows-msvc/` + linux
  `x86_64-unknown-linux-gnu/`), `min_spt_core_version` → 0.13.2 (mandatory for a fat archive), the
  F-014 windows-copy `adapter.spt` stopgap DROPPED. Win-triple flatten LOCAL-dogfooded (extract →
  flatten → translate-proof + digest-proof OK). **Layout was a published-docs GAP** (only in spt-core
  internal CONTEXT.md; not on docs-site/llms — confirmed with doyle, who is adding it to
  harness-contract + llms; not separately logged per his call). Constraint: only the two x86_64 triples
  are recognized in a fat archive today (an unrecognized dir silently flattens as a shared-root entry —
  the packer guards it); platforms beyond those ship a separate single-triple asset via `--asset`.

+- **RESOLVED-SHIPPED (spt-core v0.13.2, ADR-0024 W1) — applied to claude-spt 2026-06-22 (perri, C).**

+  doyle's fix took the third path: NOT a per-OS asset placeholder, but **one host-agnostic

+  multi-platform fat `.spt`** — a single archive bundles each recognized target-triple's binaries

+  under a `<triple>/` dir beside the SHARED `manifest.toml` + `strings/` at root; install classifies

+  the triple dirs and flattens this node's triple into the install dir (bare-name resolution

+  preserved). So the `[update]` avenue's single fixed `asset = adapter.spt` is now correct on every

+  host — no `{os}/{arch}` placeholder needed. **Adapter change (C):** `ci/publish/package-adapter.sh`

+  now emits the single fat `dist/adapter.spt` (win `x86_64-pc-windows-msvc/` + linux

+  `x86_64-unknown-linux-gnu/`), `min_spt_core_version` → 0.13.2 (mandatory for a fat archive), the

+  F-014 windows-copy `adapter.spt` stopgap DROPPED. Win-triple flatten LOCAL-dogfooded (extract →

+  flatten → translate-proof + digest-proof OK). **Layout was a published-docs GAP** (only in spt-core

+  internal CONTEXT.md; not on docs-site/llms — confirmed with doyle, who is adding it to

+  harness-contract + llms; not separately logged per his call). Constraint: only the two x86_64 triples

+  are recognized in a fat archive today (an unrecognized dir silently flattens as a shared-root entry —

+  the packer guards it); platforms beyond those ship a separate single-triple asset via `--asset`.

## F-015 — Windows update-extract fails (`tar exit 1`) when a live agent locks a bundled binary; whole update reported failed despite manifest+strings applying

**Surfaced:** 2026-06-20 — after the F-014 stopgap, update got past fetch but →


Update 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.6.0] - 2026-06-22
12
13	> Requires spt-core **v0.13.0 or newer** for the two new capabilities below. On older spt-core
14	> they're simply inactive — nothing else changes, so this release is safe to take on any version.
15
16	### Added
17	- **Idle agents now receive your messages.** When an spt-hosted agent is sitting at its prompt
18	  (idle, not mid-task), a message sent to it now lands *in that session* — typed in and submitted
19	  for you — instead of going unseen until the agent next acted. If you were part-way through typing
20	  your own input, your draft is stashed first and restored right after, so an incoming message never
21	  eats what you were writing.
22	- **Resuming an agent brings back its conversation.** Relaunching a session now reloads its prior
23	  transcript instead of cold-starting a blank one, and the resume picker shows each session's project
24	  folder so you can tell them apart.
25
26	### Changed
27	- Adapter version of truth is now **0.6.0** (shown in `/sptc:version` and the release tag). The
28	  cplugs plugin skeleton is unchanged (still on its own slower track).
29
30	## [0.5.0] - 2026-06-19

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


The 0.6.0 note ("safe to take on any version") is now contradicted by the 0.13.2 floor. Update it + add the packaging note.


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

## [0.6.0] - 2026-06-22

> Requires spt-core **v0.13.2 or newer**. The two capabilities below need v0.13.0; the v0.6.0 release
> now also ships as a single multi-platform package (see *Packaging*) that only spt-core ≥ 0.13.2 can
> install, so 0.13.2 is the effective floor.


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.6.0] - 2026-06-22

> Requires spt-core **v0.13.2 or newer**. The two capabilities below need v0.13.0; the v0.6.0 release
> now also ships as a single multi-platform package (see *Packaging*) that only spt-core ≥ 0.13.2 can
> install, so 0.13.2 is the effective floor.

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

+> Requires spt-core **v0.13.2 or newer**. The two capabilities below need v0.13.0; the v0.6.0 release

+> now also ships as a single multi-platform package (see *Packaging*) that only spt-core ≥ 0.13.2 can

+> install, so 0.13.2 is the effective floor.


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

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

### 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 `--asset` to choose. Requires spt-core
  **v0.13.2+** (the version that can read a multi-platform package).


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

### 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 `--asset` to choose. Requires spt-core
  **v0.13.2+** (the version that can read a multi-platform package).

# 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.0] - 2026-06-22

> Requires spt-core **v0.13.2 or newer**. The two capabilities below need v0.13.0; the v0.6.0 release
> now also ships as a single multi-platform package (see *Packaging*) that only spt-core ≥ 0.13.2 can
> install, so 0.13.2 is the effective floor.

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

+- **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 `--asset` to choose. Requires spt-core

+  **v0.13.2+** (the version that can read a multi-platform package).


Run traceable-reqs check + docs drift — my runbook rewrite may have dropped the `[doc->REQ-DIST-ADAPTER-PEROS]` tag.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && traceable-reqs check 2>&1 | grep -iE 'PEROS|FAIL|MISSING|exit|error|ADAPTER-RELEASE' | head; echo "TR_EXIT=${PIPESTATUS[0]}"; echo "=== peros doc tag present? ==="; grep -rn "doc->REQ-DIST-ADAPTER-PEROS" docs/

Check traceable-reqs for PEROS coverage


       Deterministic gates (shell-syntax + unit tests + traceable-reqs check + manifest-schema + docs-drift), each a binary pass/fail, runnable as one command
  [OK] REQ-DIST-ADAPTER-PEROS  required: [doc, impl, unit]  stages: +doc +impl +unit -int
  [OK] REQ-DIST-ADAPTER-RELEASE  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
TR_EXIT=0
=== peros doc tag present? ===


traceable-reqs is green (+doc +impl +unit for PEROS, exit 0). The doc tag must live elsewhere (setup skill). Confirm where, to be sure it's not stale after my changes.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && grep -rn "doc->REQ-DIST-ADAPTER-PEROS\|impl->REQ-DIST-ADAPTER-PEROS\|unit->REQ-DIST-ADAPTER-PEROS" . --include=*.sh --include=*.md --include=*.toml 2>/dev/null | grep -v node_modules

Locate all PEROS evidence tags


./adapter/strings/skills/setup.md:31:       <!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
./ci/publish/package-adapter.sh:5:# [impl->REQ-DIST-ADAPTER-RELEASE] [impl->REQ-DIST-ADAPTER-PEROS]
./PEROS-PLAN.md:31:   build-binaries-first guard. `[impl->REQ-DIST-ADAPTER-PEROS]`.
./tests/adapter-archive.sh:8:# [unit->REQ-DIST-ADAPTER-PEROS]


All PEROS tags resolve (doc via setup.md, impl via packer, unit via test) — green. For honesty I'll also tag the runbook's new fat-packaging section as PEROS doc evidence (it documents the actual fat model, vs setup.md's now-optional os-detection).


Edit

- **Fat layout (REQ-DIST-ADAPTER-PEROS).** Recognized triples =

<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Fat layout (REQ-DIST-ADAPTER-PEROS).** Recognized triples =


<!-- [doc->REQ-DIST-ADAPTER-PEROS] -->
- **Fat layout (REQ-DIST-ADAPTER-PEROS).** Recognized triples =

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

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


Run the deterministic gates affected by my changes (docs-drift since I edited docs, shell-syntax on the rewritten scripts, traceable-reqs).


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code" && echo "=== shell syntax ==="; for f in ci/publish/package-adapter.sh tests/adapter-archive.sh ci/idle-translate/translate-proof-int.sh ci/digest/digest-proof-int.sh ci/subnet/multi-subnet-bringup-int.sh; do sh -n "$f" && echo "ok  $f" || echo "BAD $f"; done; echo "=== docs drift ==="; sh ci/docs/check-docs.sh 2>&1 | tail -8; echo "DOCS_RC=${PIPESTATUS[0]}"; echo "=== traceable-reqs ==="; traceable-reqs check >/dev/null 2>&1 && echo "TR OK" || echo "TR FAIL"

Shell syntax + docs drift + traceable-reqs gates


=== shell syntax ===
ok  ci/publish/package-adapter.sh
ok  tests/adapter-archive.sh
ok  ci/idle-translate/translate-proof-int.sh
ok  ci/digest/digest-proof-int.sh
ok  ci/subnet/multi-subnet-bringup-int.sh
=== docs drift ===
ok   mdbook build
ok   llms.txt in sync
DOCS_RC=0
=== traceable-reqs ===
TR OK


All gates green (syntax, docs-drift, traceable-reqs). Commit the C source changes (holding the outward release upload). Check status first.


cd "C:/Users/decid/Documents/projects/spt-claude-code" && git status --short

Show working tree status before commit


 M CHANGELOG.md
 M adapter/claude-spt.toml
 M ci/digest/digest-proof-int.sh
 M ci/idle-translate/translate-proof-int.sh
 M ci/publish/package-adapter.sh
 M docs/RELEASE-RUNBOOK.md
 M docs/SPT-CORE-FINDINGS.md
 M tests/adapter-archive.sh
 M traceable-reqs.toml


Clean change set (dist/ excluded). Commit on main — the outward release upload stays held for operator GO.