  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")
  case "$inj" in
    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;
    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"

#!/bin/sh
# Integration proof for the claude-spt adapter manifest against a REAL spt-core (>= v0.7.0): the
# 2nd validation layer beyond JSON Schema — spt-core's `spt adapter add` cross-field registration —
# accepts the manifest, the shipped profile resolves, [strings] read through the registry, and the
# profile overlay is observable (base vs :deep differ). This is the v1 acceptance proof for the
# adapter manifest (LLM never in the loop; pure CLI assertions). [int->REQ-DIST-MANIFEST-SCHEMA]
#
# Mutates the node-local adapter registry (add + soft-remove), so it is gated behind SPTC_ACCEPTANCE=1
# and a present spt>=0.7.0 — never runs in the default `tests/*.sh` unit sweep. Idempotent: removes
# what it adds. Run: SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh   (exit 0 = pass).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
MANIFEST="$ROOT/adapter/claude-spt.toml"

if [ "${SPTC_ACCEPTANCE:-0}" != "1" ]; then echo "SKIP: set SPTC_ACCEPTANCE=1 to run (mutates the adapter registry)"; exit 0; fi
command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }
ver=$(spt --version 2>/dev/null | awk '{print $NF}')
case "$ver" in
  0.6.*|0.5.*|0.4.*|0.3.*|0.2.*|0.1.*|0.0.*) echo "SKIP: spt $ver < 0.7.0 (shortcut_basename + adapter add are v0.7.0)"; exit 0 ;;
esac

fail=0
ok()  { echo "ok   $1"; }
bad() { echo "FAIL $1"; fail=1; }
# Clean up registry residue however we exit.
trap 'spt adapter remove claude-spt >/dev/null 2>&1 || true' EXIT INT TERM

# 1. Registration cross-field validation accepts the manifest.
out=$(spt adapter add "$MANIFEST" 2>&1)
case "$out" in *registered*) ok "spt adapter add: registered" ;; *) bad "adapter add rejected: $out" ;; esac

# 2. Listed, with the shipped profile resolved as a composite option.
list=$(spt adapter list 2>&1)
case "$list" in *claude-spt*) ok "listed: claude-spt active" ;; *) bad "claude-spt not listed" ;; esac
case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay
# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).
case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac

# 3. [strings] read through the registry (base value).
base=$(spt adapter get-string claude-spt adapter_label 2>&1)
[ "$base" = "Claude Code (spt)" ] && ok "strings resolve: adapter_label (base)" || bad "base adapter_label='$base'"

# 4. Profile overlay is observable: :deep leaf-replaces the base string.
deep=$(spt adapter get-string claude-spt:deep adapter_label 2>&1)
[ "$deep" = "Claude Code (spt, deep)" ] && ok "overlay observable: :deep adapter_label differs" || bad "deep adapter_label='$deep'"

# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac

# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped
#     operative body via get-string (REQ-UPS-INJECTION impl, on the registered adapter).
( . "$ROOT/plugin/sptc/hooks/_common.sh"
  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:whoami report me')")
  case "$inj" in
    '<sptc_skill name="whoami">'*'# /sptc:whoami'*'</sptc_skill>'*) exit 0 ;;
    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
  esac ) && ok "UPS skill-injection: /sptc:whoami -> wrapped body" || bad "skill-injection did not emit wrapped body"

# 4d. The spt-hosted bringup blocks ([session.self] + [env.SPT_ENDPOINT_ID]) cross-field-validate:
#     `adapter add` (step 1) is manifest-first ("an invalid manifest registers nothing"), so the
#     manifest carrying these blocks registering at all proves spt-core accepted their shape. Confirm
#     the registered adapter advertises a hostable harness — the thing `spt endpoint run` spawns via
#     [session.self] (the M12 cc-launcher target). [int->REQ-DIST-MANIFEST-SCHEMA]
cap=$(spt api --adapter claude-spt --manifest "$MANIFEST" capability 2>&1)
case "$cap" in *LiveAgent*) ok "bringup blocks accepted; capability reports hostable harness" ;; *) bad "capability missing LiveAgent: $cap" ;; esac

# 5. Soft-deregister cleanly.
rm=$(spt adapter remove claude-spt 2>&1)
list2=$(spt adapter list 2>&1)
case "$list2" in *"claude-spt"*"active"*) bad "claude-spt still active after remove" ;; *) ok "removed (soft-deregistered)" ;; esac

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

-  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:whoami report me')")

+  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")

-    '<sptc_skill name="whoami">'*'# /sptc:whoami'*'</sptc_skill>'*) exit 0 ;;

+    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;

-  esac ) && ok "UPS skill-injection: /sptc:whoami -> wrapped body" || bad "skill-injection did not emit wrapped body"

+  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"


Now `tests/hooks-parse.sh` (whoami used as generic parser example) + the UPS comment. Read the hooks-parse lines.


46	# Empty drain -> no output.
47	check "render empty" "" "$(render_frames '')"
48
49	# --- sptc_skill_key: detect /sptc:<skill> in a slash-command prompt --- [unit->REQ-UPS-INJECTION]
50	check "skill_key bare"        "whoami" "$(sptc_skill_key '/sptc:whoami')"
51	check "skill_key with args"   "send"   "$(sptc_skill_key '/sptc:send doyle hi')"
52	check "skill_key leading ws"  "setup"  "$(sptc_skill_key '   /sptc:setup')"
53	check "skill_key hyphenated"  "list-agents" "$(sptc_skill_key '/sptc:list-agents')"
54	check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:whoami for me')"
55	check "skill_key other ns"    ""       "$(sptc_skill_key '/other:whoami')"
56	check "skill_key not a cmd"   ""       "$(sptc_skill_key 'whoami am i')"
57	check "skill_key empty"       ""       "$(sptc_skill_key '')"
58
59	# --- sptc_register_verb: SessionStart bind/seed/boundary branch --- [unit->REQ-DIST-SHORTCUT-BASENAME]

# Empty drain -> no output.
check "render empty" "" "$(render_frames '')"

# --- sptc_skill_key: detect /sptc:<skill> in a slash-command prompt --- [unit->REQ-UPS-INJECTION]
check "skill_key bare"        "whoami" "$(sptc_skill_key '/sptc:whoami')"
check "skill_key with args"   "send"   "$(sptc_skill_key '/sptc:send doyle hi')"
check "skill_key leading ws"  "setup"  "$(sptc_skill_key '   /sptc:setup')"
check "skill_key hyphenated"  "list-agents" "$(sptc_skill_key '/sptc:list-agents')"
check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:whoami for me')"
check "skill_key other ns"    ""       "$(sptc_skill_key '/other:whoami')"
check "skill_key not a cmd"   ""       "$(sptc_skill_key 'whoami am i')"
check "skill_key empty"       ""       "$(sptc_skill_key '')"

# --- sptc_register_verb: SessionStart bind/seed/boundary branch --- [unit->REQ-DIST-SHORTCUT-BASENAME]


Generic parser examples — repoint whoami→ready so no dropped skill is referenced.


Edit

check "skill_key bare"        "whoami" "$(sptc_skill_key '/sptc:whoami')"
check "skill_key with args"   "send"   "$(sptc_skill_key '/sptc:send doyle hi')"
check "skill_key leading ws"  "setup"  "$(sptc_skill_key '   /sptc:setup')"
check "skill_key hyphenated"  "list-agents" "$(sptc_skill_key '/sptc:list-agents')"
check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:whoami for me')"
check "skill_key other ns"    ""       "$(sptc_skill_key '/other:whoami')"
check "skill_key not a cmd"   ""       "$(sptc_skill_key 'whoami am i')"

check "skill_key bare"        "ready"  "$(sptc_skill_key '/sptc:ready')"
check "skill_key with args"   "send"   "$(sptc_skill_key '/sptc:send doyle hi')"
check "skill_key leading ws"  "setup"  "$(sptc_skill_key '   /sptc:setup')"
check "skill_key hyphenated"  "list-agents" "$(sptc_skill_key '/sptc:list-agents')"
check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:ready for me')"
check "skill_key other ns"    ""       "$(sptc_skill_key '/other:ready')"
check "skill_key not a cmd"   ""       "$(sptc_skill_key 'ready set go')"


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

check "skill_key bare"        "ready"  "$(sptc_skill_key '/sptc:ready')"
check "skill_key with args"   "send"   "$(sptc_skill_key '/sptc:send doyle hi')"
check "skill_key leading ws"  "setup"  "$(sptc_skill_key '   /sptc:setup')"
check "skill_key hyphenated"  "list-agents" "$(sptc_skill_key '/sptc:list-agents')"
check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:ready for me')"
check "skill_key other ns"    ""       "$(sptc_skill_key '/other:ready')"
check "skill_key not a cmd"   ""       "$(sptc_skill_key 'ready set go')"

#!/bin/sh
# Unit tests for the sptc hook parsing glue (pure functions; no spt/CC needed).
# Run: sh tests/hooks-parse.sh   (exit 0 = pass)
. "$(dirname "$0")/../plugin/sptc/hooks/_common.sh"

fail=0
check() { # check  <expected> <actual>
  if [ "$2" = "$3" ]; then printf 'ok   %s\n' "$1"
  else printf 'FAIL %s\n  expected: [%s]\n  actual:   [%s]\n' "$1" "$2" "$3"; fail=1; fi
}

# --- json_str: top-level CC hook-input fields ---  [unit->REQ-DIST-HOOKS-API]
J='{"session_id":"sess-abc123","transcript_path":"/x","source":"startup","model":"m"}'
check "json_str session_id" "sess-abc123" "$(json_str "$J" session_id)"
check "json_str source"     "startup"     "$(json_str "$J" source)"
check "json_str prompt"      "/sptc:send doyle" \
  "$(json_str '{"session_id":"s1","prompt":"/sptc:send doyle"}' prompt)"
check "json_str missing key" "" "$(json_str "$J" nope)"

# --- sptc_unescape:
 -> newline, entities, & LAST --- [unit->REQ-UPS-INJECTION]
check "unescape br+entities" \
  "$(printf 'a \n"c" & <')" \
  "$(sptc_unescape 'a <b>
"c" & &lt;')"

# --- render_frames: canonical <EVENT> envelope (ADR-0020) --- [unit->REQ-UPS-INJECTION]
# Named single,
-escaped multiline body, sender preserved.
named=$(render_frames '<EVENT type="msg" from="doyle">hello
world</EVENT>')
check "render named <EVENT>" \
  "$(printf '<sptc_messages from="doyle">\nhello\nworld\n</sptc_messages>')" "$named"

# Entity-escaped body decodes.
ent=$(render_frames '<EVENT type="msg" from="kit">a <tag> & b</EVENT>')
check "render entity body" \
  "$(printf '<sptc_messages from="kit">\na <tag> & b\n</sptc_messages>')" "$ent"

# Multi-message drain: two self-delimiting envelopes -> two rendered blocks.
multi=$(render_frames '<EVENT type="msg" from="a">one</EVENT><EVENT type="msg" from="b">two</EVENT>')
check "render multi-message" \
  "$(printf '<sptc_messages from="a">\none\n</sptc_messages>\n<sptc_messages from="b">\ntwo\n</sptc_messages>')" \
  "$multi"

# Envelope without a from attr -> anonymous rendering.
anon=$(render_frames '<EVENT type="msg">sys note</EVENT>')
check "render no-from" "$(printf '<sptc_messages>\nsys note\n</sptc_messages>')" "$anon"

# Empty drain -> no output.
check "render empty" "" "$(render_frames '')"

# --- sptc_skill_key: detect /sptc:<skill> in a slash-command prompt --- [unit->REQ-UPS-INJECTION]
check "skill_key bare"        "whoami" "$(sptc_skill_key '/sptc:whoami')"
check "skill_key with args"   "send"   "$(sptc_skill_key '/sptc:send doyle hi')"
check "skill_key leading ws"  "setup"  "$(sptc_skill_key '   /sptc:setup')"
check "skill_key hyphenated"  "list-agents" "$(sptc_skill_key '/sptc:list-agents')"
check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:whoami for me')"
check "skill_key other ns"    ""       "$(sptc_skill_key '/other:whoami')"
check "skill_key not a cmd"   ""       "$(sptc_skill_key 'whoami am i')"
check "skill_key empty"       ""       "$(sptc_skill_key '')"

# --- sptc_register_verb: SessionStart bind/seed/boundary branch --- [unit->REQ-DIST-SHORTCUT-BASENAME]
# spt-hosted (endpoint id injected) -> bind; harness-hosted (no id) -> seed; clear/compact -> boundary.
unset SPT_ENDPOINT_ID
check "verb harness-hosted startup"   "seed"     "$(sptc_register_verb startup)"
check "verb harness-hosted empty src" "seed"     "$(sptc_register_verb '')"
check "verb boundary clear"           "boundary" "$(sptc_register_verb clear)"
check "verb boundary compact"         "boundary" "$(sptc_register_verb compact)"
SPT_ENDPOINT_ID=cc-7; export SPT_ENDPOINT_ID
check "verb spt-hosted startup -> bind" "bind"     "$(sptc_register_verb startup)"
# A /clear inside an spt-hosted session still rebinds via boundary (never re-binds fresh).
check "verb spt-hosted clear -> boundary" "boundary" "$(sptc_register_verb clear)"
unset SPT_ENDPOINT_ID

# --- sptc_cap_output: additionalContext spill guard (ADR-0002 Open #2) --- [unit->REQ-UPS-INJECTION]
spill="${TMPDIR:-/tmp}/sptc-cap-$$.txt"
rm -f "$spill"
check "cap under -> passthrough verbatim" "small body" "$(sptc_cap_output 'small body' 9000 "$spill")"
check "cap under -> no spill written"     "absent"     "$([ -f "$spill" ] && echo present || echo absent)"

over=$(sptc_cap_output 'abcdefghij' 5 "$spill")   # 10 bytes > cap 5
case "$over" in *'<sptc_overflow'*) m=ok ;; *) m=no ;; esac
check "cap over -> emits overflow marker"  "ok"         "$m"
check "cap over -> full body spilled"      "abcdefghij" "$(cat "$spill" 2>/dev/null)"
case "$over" in *abcdefghij*) leak=leaked ;; *) leak=clean ;; esac
check "cap over -> body not inlined"       "clean"      "$leak"
check "cap empty -> no output"             ""           "$(sptc_cap_output '' 5 "$spill")"
rm -f "$spill"

[ "$fail" -eq 0 ] && { echo "ALL PASS"; exit 0; } || { echo "FAILURES"; exit 1; }

-check "skill_key bare"        "whoami" "$(sptc_skill_key '/sptc:whoami')"

+check "skill_key bare"        "ready"  "$(sptc_skill_key '/sptc:ready')"

-check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:whoami for me')"

-check "skill_key other ns"    ""       "$(sptc_skill_key '/other:whoami')"

-check "skill_key not a cmd"   ""       "$(sptc_skill_key 'whoami am i')"

+check "skill_key mid-prose"   ""       "$(sptc_skill_key 'please run /sptc:ready for me')"

+check "skill_key other ns"    ""       "$(sptc_skill_key '/other:ready')"

+check "skill_key not a cmd"   ""       "$(sptc_skill_key 'ready set go')"


Now the UPS comment example:


14	# skill body + a big drain could jointly exceed CC's spill threshold.
15	out=""
16
17	# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:whoami and /sptc:setup are
18	#    valid without a readied perch (setup even runs before spt exists). No-op if not a sptc command.
19	prompt=$(json_str "$input" prompt)
20	out="$(sptc_inject_skill "$(sptc_skill_key "$prompt")")"
21

# skill body + a big drain could jointly exceed CC's spill threshold.
out=""

# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:whoami and /sptc:setup are
#    valid without a readied perch (setup even runs before spt exists). No-op if not a sptc command.
prompt=$(json_str "$input" prompt)
out="$(sptc_inject_skill "$(sptc_skill_key "$prompt")")"


# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:whoami and /sptc:setup are
#    valid without a readied perch (setup even runs before spt exists). No-op if not a sptc command.

# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:version and /sptc:setup are
#    valid without a readied perch (setup even runs before spt exists). No-op if not a sptc command.


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

# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:version and /sptc:setup are
#    valid without a readied perch (setup even runs before spt exists). No-op if not a sptc command.

#!/bin/sh
# UserPromptSubmit: two jobs on the same hook (ADR-0002 — UPS fires on a `/sptc:X` slash-command
# with the token intact, validated 2026-06-15). Both write to stdout = CC additionalContext:
#   1. SKILL-INJECTION — detect `/sptc:<skill>` in the prompt and inject that skill's operative
#      instructions from the adapter `[strings.skills].<skill>` (the thin SKILL.md stays a stub).
#   2. MESSAGE-DRAIN — `api poll` the perch and surface delivered <EVENT> messages.
# [impl->REQ-UPS-INJECTION]
. "$CLAUDE_PLUGIN_ROOT/hooks/_common.sh"

input=$(cat)
sid=$(json_str "$input" session_id)

# Buffer both outputs so the COMBINED additionalContext is capped once (ADR-0002 Open #2) — a large
# skill body + a big drain could jointly exceed CC's spill threshold.
out=""

# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:whoami and /sptc:setup are
#    valid without a readied perch (setup even runs before spt exists). No-op if not a sptc command.
prompt=$(json_str "$input" prompt)
out="$(sptc_inject_skill "$(sptc_skill_key "$prompt")")"

# 2. Message-drain needs a perch. No perch (session not readied) -> nothing more to deliver.
id=$(sptc_self_id "$sid")
if [ -n "$id" ]; then
  SPT=$(spt_bin)
  frames=$("$SPT" api --adapter "$ADAPTER" poll "$id" --session-id "$sid" 2>/dev/null)
  if [ -n "$frames" ]; then
    # Format for CC, preserving the sender (reply-correlation: ADR-0009/0012). render_frames parses
    # the self-delimiting <EVENT> envelope (ADR-0020) — multi-message drains split on </EVENT>.
    rendered=$(render_frames "$frames")
    [ -n "$rendered" ] && out="${out:+$out
}$rendered"
  fi
fi

# Emit under CC's additionalContext cap; oversized drains spill to a file the agent reads (no
# silent message loss). Spill path is an absolute, agent-readable location keyed by session.
spill="${HOME:-${USERPROFILE:-.}}/.claude/sptc-drain-${sid:-unknown}.txt"
sptc_cap_output "$out" "$SPTC_CTX_CAP" "$spill"
exit 0

-# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:whoami and /sptc:setup are

+# 1. Skill-injection runs BEFORE the perch check — skills like /sptc:version and /sptc:setup are


Check for remaining `whoami` skill references (core CLI calls + PARITY.md doc are expected to stay):


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && grep -rni 'sptc:whoami\|skills.whoami\|skills/whoami' . --include=*.sh --include=*.toml --include=*.md --include=*.json 2>/dev/null | grep -v '\.git/' | grep -vi 'PARITY.md'

Remaining whoami skill references


./docs/SPT-CORE-FINDINGS.md:195:- The `/sptc:whoami` + `/sptc:setup` skeletons keep **interim inline bodies** in the cplugs skeleton;
./MANIFEST-PARITY-PLAN.md:39:   - **DONE (8cd8372)** — `/sptc:whoami` + `/sptc:setup` skill skeletons (the manifest advertised
./MANIFEST-PARITY-PLAN.md:80:     4c (end-to-end `/sptc:whoami` → wrapped body, 9/9 vs live v0.7.0). `setup` SKILL.md is
./PARITY-PUBLISH-PLAN.md:48:- Remove `whoami = { file = "skills/whoami.md" }` from manifest `[strings.skills]`.
./PARITY-PUBLISH-PLAN.md:49:- Delete `adapter/strings/skills/whoami.md` + `plugin/sptc/skills/whoami/`.
./PARITY-PUBLISH-PLAN.md:50:- **registration-int.sh** uses `skills.whoami` as the canonical file-backed + UPS-injection
./PARITY-PUBLISH-PLAN.md:52:- Redirect the `who am i` **hint** (currently → /sptc:whoami) to core `spt whoami` guidance.


Remaining hits are historical plans/findings docs (append-only) — no operative refs. Run gate.


cd C:/Users/decid/Documents/projects/spt-claude-code && sh ci/run-gates.sh 2>&1 | grep -iE 'FAIL|RESULT|skeleton|hooks-parse|manifest-schema' ; echo "---trace---"; traceable-reqs check >/dev/null 2>&1 && echo "trace OK" || { echo "trace FAIL"; traceable-reqs check 2>&1 | grep -iE 'MISSING|FAIL|error' | head; }

Gate + trace after whoami drop


ok  ci/publish/package-skeleton.sh
ok  ci/publish/validate-skeleton.sh
ok  tests/hooks-parse.sh
ok  tests/manifest-schema.sh
ok  tests/skeleton-validate.sh
ok  tests/hooks-parse.sh
ok  tests/manifest-schema.sh
ok  tests/skeleton-validate.sh
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
=== GATE: skeleton-validate (cplugs installability) ===
ok  skeleton installable
=== GATE: manifest-schema ===
=== RESULT: PASS ===
---trace---
trace OK


Gate PASS. Verify the repointed assertions on real v0.7.2.


cd C:/Users/decid/Documents/projects/spt-claude-code && SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh 2>&1 | grep -iE 'ready|whoami|skill-injection|RESULT|OK|FAIL'

registration-int: confirm ready repoint + whoami gone


ok   spt adapter add: registered
ok   listed: claude-spt active
ok   shipped profile resolves: claude-spt:deep
ok   shipped profile resolves: claude-spt:live (LiveAgent overlay)
ok   strings resolve: adapter_label (base)
ok   overlay observable: :deep adapter_label differs
ok   file-backed string resolves: skills.ready -> body
ok   file-backed string resolves: skills.commune -> body
ok   file-backed string resolves: skills.force-stop -> body
ok   file-backed string resolves: skills.live -> body
ok   UPS skill-injection: /sptc:ready -> wrapped body
ok   bringup blocks accepted; capability reports hostable harness
ok   removed (soft-deregistered)
MANIFEST-REGISTRATION-INT OK


All green on v0.7.2 — `skills.ready` repoint works, whoami gone. Commit Phase B.


cd C:/Users/decid/Documents/projects/spt-claude-code && git add -A && git commit -q -F - <<'EOF'
feat(parity): drop the whoami skill + remove dead new-alarm stub (Phase B)

Honor the SCOPE.md LOCKED parity-trim (operator-confirmed 2026-06-15): the adapter
ships no whoami skill. Core `spt whoami` stays in the hot path (the hook CLI calls
in session-start.sh / _common.sh are unchanged — that's the binary, not the skill;
it becomes the `endpoint list` alias in M12).

- Remove [strings.skills].whoami; delete adapter/strings/skills/whoami.md +
  plugin/sptc/skills/whoami/. Redirect the "who am i" hint to core `spt whoami`.
- registration-int.sh: the file-backed (4b) + UPS-injection (4c) examples used
  skills.whoami -> repointed to skills.ready (both green on live v0.7.2).
- tests/hooks-parse.sh: generic sptc_skill_key parser examples whoami -> ready.
- user-prompt-submit.sh comment: example skill /sptc:whoami -> /sptc:version.
- Remove the dead /sptc:new-alarm stub (accepted gap, deferred-pending-arrangement-
  system per doyle; deferred != shipping a no-op skill). No manifest entry existed.

Published skill surface now 9 (commune, force-stop, list-agents, live, ready, send,
setup, signoff, version). docs/PARITY.md rows for whoami + new-alarm now satisfied.

Gate PASS + traceable green; registration-int OK on v0.7.2.

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

Commit Phase B


0595910 feat(parity): drop the whoami skill + remove dead new-alarm stub (Phase B)


Phases A+B committed cleanly. **Phase C** — subnet skills. Shape decision: **one `/sptc:subnet` skill** covering status/create/join/show-code (mirrors the single `spt subnet` CLI verb-family, keeps surface lean — like force-stop wrapping shutdown+stop). Check canonical guidance.


cd C:/Users/decid/Documents/projects/spt-claude-code && echo "===how-to subnet?==="; spt how-to subnet 2>&1 | head -25; echo "===how-to list?==="; spt how-to 2>&1 | head -20

Check spt how-to subnet guidance


===how-to subnet?===
NO_SUCH_TOPIC:subnet — topics:
  ready  receive messages: listener guidance + the --once fallback loop
  send   deliver messages: send vs ring, replies, SENT vs QUEUED

===how-to list?===
Topics (`spt how-to <topic>`):
  ready  receive messages: listener guidance + the --once fallback loop
  send   deliver messages: send vs ring, replies, SENT vs QUEUED


No `spt how-to subnet` topic (only ready/send have them) — minor published-guidance gap; the skill wraps `spt subnet --help` directly. Write the subnet body.


Write

# /sptc:subnet — operative instructions

> Delivered file-backed via the `claude-spt` adapter `[strings]`
> (`[strings.skills].subnet = { file = "skills/subnet.md" }`), resolved at injection time. The
> cplugs SKILL.md stays a thin skeleton (ADR-0001).

**Goal:** manage this machine's **subnet** membership — the private group of paired machines whose
agents can reach each other across every member node. A subnet is what makes `/sptc:send`,
`/sptc:ready`, and live agents work *cross-machine* (not just locally). Covers the three primary
user intents — **create** a new subnet, **show-code** to invite another machine, **join** an
existing one — plus the **status** view.

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first (a subnet needs the running daemon).
2. The canonical, always-current verb list is `spt subnet --help` (there is no `spt how-to subnet`
   topic — the `--help` output is the source of truth). Match the user's intent to a verb:
   - **See where I stand:** `spt subnet` (bare) or `spt subnet status` — name, paired nodes,
     endpoints. Start here when unsure.
   - **Start a new subnet** (this machine becomes the first node): `spt subnet create` — mints a
     fresh subnet and prints its joining material (a 6-digit pairing code + URI + QR).
   - **Invite another machine to mine:** `spt subnet show-code` — re-displays the current subnet's
     pairing code (+ URI + QR) to hand to the machine that will join.
   - **Pair THIS machine into an existing subnet:** `spt subnet join` — guided; needs the pairing
     code from the subnet's existing member (their `create`/`show-code` output).
3. **Pairing flow (two machines):** on machine A run `spt subnet create` (or `show-code` if it
   already has one) → read off the 6-digit code → on machine B run `spt subnet join` and supply
   that code. Confirm with `spt subnet status` on both (each should list the other as a paired node).
4. **Lifecycle verbs** (mention only if asked): `leave` (drop this node's membership),
   `detach`/`attach` (stop/resume serving a held subnet without leaving), `revoke`/`prune`
   (fleet-wide node removal + seed rotation), `notify` (subnet-wide user notification).
5. Confirm to the user what changed (subnet name, the code to share, or the newly paired node).
   Pairing codes are sensitive joining material — share them only with machines that should join.
<!-- [doc->REQ-SKILL-SUBNET] -->


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

# /sptc:subnet — operative instructions

> Delivered file-backed via the `claude-spt` adapter `[strings]`
> (`[strings.skills].subnet = { file = "skills/subnet.md" }`), resolved at injection time. The
> cplugs SKILL.md stays a thin skeleton (ADR-0001).

**Goal:** manage this machine's **subnet** membership — the private group of paired machines whose
agents can reach each other across every member node. A subnet is what makes `/sptc:send`,
`/sptc:ready`, and live agents work *cross-machine* (not just locally). Covers the three primary
user intents — **create** a new subnet, **show-code** to invite another machine, **join** an
existing one — plus the **status** view.

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first (a subnet needs the running daemon).
2. The canonical, always-current verb list is `spt subnet --help` (there is no `spt how-to subnet`
   topic — the `--help` output is the source of truth). Match the user's intent to a verb:
   - **See where I stand:** `spt subnet` (bare) or `spt subnet status` — name, paired nodes,
     endpoints. Start here when unsure.
   - **Start a new subnet** (this machine becomes the first node): `spt subnet create` — mints a
     fresh subnet and prints its joining material (a 6-digit pairing code + URI + QR).
   - **Invite another machine to mine:** `spt subnet show-code` — re-displays the current subnet's
     pairing code (+ URI + QR) to hand to the machine that will join.
   - **Pair THIS machine into an existing subnet:** `spt subnet join` — guided; needs the pairing
     code from the subnet's existing member (their `create`/`show-code` output).
3. **Pairing flow (two machines):** on machine A run `spt subnet create` (or `show-code` if it
   already has one) → read off the 6-digit code → on machine B run `spt subnet join` and supply
   that code. Confirm with `spt subnet status` on both (each should list the other as a paired node).
4. **Lifecycle verbs** (mention only if asked): `leave` (drop this node's membership),
   `detach`/`attach` (stop/resume serving a held subnet without leaving), `revoke`/`prune`
   (fleet-wide node removal + seed rotation), `notify` (subnet-wide user notification).
5. Confirm to the user what changed (subnet name, the code to share, or the newly paired node).
   Pairing codes are sensitive joining material — share them only with machines that should join.
<!-- [doc->REQ-SKILL-SUBNET] -->


Add the manifest entry + hint + stub.


Edit

commune = { file = "skills/commune.md" }       # -> file-drop <id>-commune.md into [session].commune_dir
force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)

commune = { file = "skills/commune.md" }       # -> file-drop <id>-commune.md into [session].commune_dir
force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine membership)


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

commune = { file = "skills/commune.md" }       # -> file-drop <id>-commune.md into [session].commune_dir
force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine membership)

list-agents = { file = "skills/list-agents.md" } # -> spt endpoint list (the roster)

+subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine membership)

# `live` -> upgrade THIS session to a LiveAgent via the `claude-spt:live` profile (overlays

# [session.psyche_init] -> daemon spawns the Psyche) + a resident Monitor relay as the delivery pipe.

# Profile propagation CONFIRMED in spt-core code (doyle 2026-06-15: api seed stores the composite ->


228	# lands only after that ships. No `[strings.skills].new-alarm` entry by design (visible-not-silent).
229
230	# ── [[hints]] ──────────────────────────────────────────────────────────────────────────────
231	# Once-per-session keyword hints (order significant, first match wins; case-insensitive substrings).
232	[[hints]]
233	keywords = ["go live", "live agent", "start live"]
234	text = "Tip: /sptc:live starts or resumes a live agent session."
235
236	[[hints]]
237	keywords = ["who am i", "my owl name", "my agent name", "my endpoint id"]
238	text = "Tip: run `spt whoami` to see this session's spt endpoint id."
239
240	[[hints]]
241	keywords = ["message another agent", "send to agent", "tell the other agent"]
242	text = "Tip: /sptc:send messages another spt agent; /sptc:ready first to receive replies."

# lands only after that ships. No `[strings.skills].new-alarm` entry by design (visible-not-silent).

# ── [[hints]] ──────────────────────────────────────────────────────────────────────────────
# Once-per-session keyword hints (order significant, first match wins; case-insensitive substrings).
[[hints]]
keywords = ["go live", "live agent", "start live"]
text = "Tip: /sptc:live starts or resumes a live agent session."

[[hints]]
keywords = ["who am i", "my owl name", "my agent name", "my endpoint id"]
text = "Tip: run `spt whoami` to see this session's spt endpoint id."

[[hints]]
keywords = ["message another agent", "send to agent", "tell the other agent"]
text = "Tip: /sptc:send messages another spt agent; /sptc:ready first to receive replies."


[[hints]]
keywords = ["message another agent", "send to agent", "tell the other agent"]
text = "Tip: /sptc:send messages another spt agent; /sptc:ready first to receive replies."

[[hints]]
keywords = ["message another agent", "send to agent", "tell the other agent"]
text = "Tip: /sptc:send messages another spt agent; /sptc:ready first to receive replies."

[[hints]]
keywords = ["another machine", "pair machine", "across machines", "subnet", "join my network"]
text = "Tip: /sptc:subnet pairs machines into a private network so agents reach each other across nodes."


[[hints]]
keywords = ["message another agent", "send to agent", "tell the other agent"]
text = "Tip: /sptc:send messages another spt agent; /sptc:ready first to receive replies."

[[hints]]
keywords = ["another machine", "pair machine", "across machines", "subnet", "join my network"]
text = "Tip: /sptc:subnet pairs machines into a private network so agents reach each other across nodes."

+keywords = ["another machine", "pair machine", "across machines", "subnet", "join my network"]

+text = "Tip: /sptc:subnet pairs machines into a private network so agents reach each other across nodes."


Now the thin SKILL.md stub (mirror commune's operative-stub pattern):


Write

---
name: subnet
description: |
  Manage subnet membership — create, show a pairing code, or join, so agents reach each other across machines.
argument-hint: "[status|create|show-code|join]"
allowed-tools: [Bash]
---

# /sptc:subnet

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
>
> **Operative.** The UPS hook detects `/sptc:subnet` and injects the body from
> `adapter/strings/skills/subnet.md` (UPS-fires-on-slash confirmed, ADR-0002; file-backed
> `[strings]` shipped, F-003). If injection ever no-ops (spt absent / adapter unregistered), this
> stub is the floor.

Manages this machine's **subnet** — the private group of paired machines whose agents reach each
other across nodes. Wraps `spt subnet {status,create,show-code,join,…}`: create a subnet, show a
pairing code to invite a machine, or join an existing one. Cross-machine `/sptc:send` + live agents
depend on it.


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

---
name: subnet
description: |
  Manage subnet membership — create, show a pairing code, or join, so agents reach each other across machines.
argument-hint: "[status|create|show-code|join]"
allowed-tools: [Bash]
---

# /sptc:subnet

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
>
> **Operative.** The UPS hook detects `/sptc:subnet` and injects the body from
> `adapter/strings/skills/subnet.md` (UPS-fires-on-slash confirmed, ADR-0002; file-backed
> `[strings]` shipped, F-003). If injection ever no-ops (spt absent / adapter unregistered), this
> stub is the floor.

Manages this machine's **subnet** — the private group of paired machines whose agents reach each
other across nodes. Wraps `spt subnet {status,create,show-code,join,…}`: create a subnet, show a
pairing code to invite a machine, or join an existing one. Cross-machine `/sptc:send` + live agents
depend on it.


Activate `REQ-SKILL-SUBNET` [doc,impl] + add int assertion to registration-int, and tag the manifest impl.


Edit

id = "REQ-SKILL-SUBNET"
title = "The adapter ships subnet membership skill(s) (create/join/show-code) wrapping the published `spt subnet <verb>` surface — the LOCKED-ADD cross-subnet entrypoint (v1 topology is mandatory, SCOPE)"
required_stages = []  # activate [doc,impl] when Phase C starts. doc: adapter/strings/skills/subnet*.md. impl: [strings.skills] entries + plugin stub(s). int: registration-int file-backed-resolve assertion (mirrors :live).

id = "REQ-SKILL-SUBNET"
title = "The adapter ships subnet membership skill(s) (create/join/show-code) wrapping the published `spt subnet <verb>` surface — the LOCKED-ADD cross-subnet entrypoint (v1 topology is mandatory, SCOPE)"
required_stages = ["doc", "impl", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). impl: [strings.skills].subnet entry + plugin/sptc/skills/subnet/SKILL.md stub + the subnet hint. int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt. (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)


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)

id = "REQ-SKILL-SUBNET"
title = "The adapter ships subnet membership skill(s) (create/join/show-code) wrapping the published `spt subnet <verb>` surface — the LOCKED-ADD cross-subnet entrypoint (v1 topology is mandatory, SCOPE)"
required_stages = []  # activate [doc,impl] when Phase C starts. doc: adapter/strings/skills/subnet*.md. impl: [strings.skills] entries + plugin stub(s). int: registration-int file-backed-resolve assertion (mirrors :live).

id = "REQ-SKILL-SUBNET"
title = "The adapter ships subnet membership skill(s) (create/join/show-code) wrapping the published `spt subnet <verb>` surface — the LOCKED-ADD cross-subnet entrypoint (v1 topology is mandatory, SCOPE)"
required_stages = ["doc", "impl", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). impl: [strings.skills].subnet entry + plugin/sptc/skills/subnet/SKILL.md stub + the subnet hint. int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt. (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)

-required_stages = []  # activate [doc,impl] when Phase C starts. doc: adapter/strings/skills/subnet*.md. impl: [strings.skills] entries + plugin stub(s). int: registration-int file-backed-resolve assertion (mirrors :live).

+required_stages = ["doc", "impl", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). impl: [strings.skills].subnet entry + plugin/sptc/skills/subnet/SKILL.md stub + the subnet hint. int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt. (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)


Tag impl on the manifest entry, add the int assertion, and log the how-to finding.


subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine membership)

subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} [impl->REQ-SKILL-SUBNET]


-subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine membership)

+subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} [impl->REQ-SKILL-SUBNET]


lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac

lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac


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

lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac

#!/bin/sh
# Integration proof for the claude-spt adapter manifest against a REAL spt-core (>= v0.7.0): the
# 2nd validation layer beyond JSON Schema — spt-core's `spt adapter add` cross-field registration —
# accepts the manifest, the shipped profile resolves, [strings] read through the registry, and the
# profile overlay is observable (base vs :deep differ). This is the v1 acceptance proof for the
# adapter manifest (LLM never in the loop; pure CLI assertions). [int->REQ-DIST-MANIFEST-SCHEMA]
#
# Mutates the node-local adapter registry (add + soft-remove), so it is gated behind SPTC_ACCEPTANCE=1
# and a present spt>=0.7.0 — never runs in the default `tests/*.sh` unit sweep. Idempotent: removes
# what it adds. Run: SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh   (exit 0 = pass).
set -u
ROOT=$(CDPATH= cd "$(dirname "$0")/../.." && pwd)
MANIFEST="$ROOT/adapter/claude-spt.toml"

if [ "${SPTC_ACCEPTANCE:-0}" != "1" ]; then echo "SKIP: set SPTC_ACCEPTANCE=1 to run (mutates the adapter registry)"; exit 0; fi
command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }
ver=$(spt --version 2>/dev/null | awk '{print $NF}')
case "$ver" in
  0.6.*|0.5.*|0.4.*|0.3.*|0.2.*|0.1.*|0.0.*) echo "SKIP: spt $ver < 0.7.0 (shortcut_basename + adapter add are v0.7.0)"; exit 0 ;;
esac

fail=0
ok()  { echo "ok   $1"; }
bad() { echo "FAIL $1"; fail=1; }
# Clean up registry residue however we exit.
trap 'spt adapter remove claude-spt >/dev/null 2>&1 || true' EXIT INT TERM

# 1. Registration cross-field validation accepts the manifest.
out=$(spt adapter add "$MANIFEST" 2>&1)
case "$out" in *registered*) ok "spt adapter add: registered" ;; *) bad "adapter add rejected: $out" ;; esac

# 2. Listed, with the shipped profile resolved as a composite option.
list=$(spt adapter list 2>&1)
case "$list" in *claude-spt*) ok "listed: claude-spt active" ;; *) bad "claude-spt not listed" ;; esac
case "$list" in *claude-spt:deep*) ok "shipped profile resolves: claude-spt:deep" ;; *) bad "deep profile not resolved" ;; esac
# The LiveAgent overlay (:live adds [session.psyche_init]) — proves a profile [session] overlay
# validates + resolves on the live binary (the /sptc:live seam; doyle-grounded propagation).
case "$list" in *claude-spt:live*) ok "shipped profile resolves: claude-spt:live (LiveAgent overlay)" ;; *) bad "live profile not resolved" ;; esac

# 3. [strings] read through the registry (base value).
base=$(spt adapter get-string claude-spt adapter_label 2>&1)
[ "$base" = "Claude Code (spt)" ] && ok "strings resolve: adapter_label (base)" || bad "base adapter_label='$base'"

# 4. Profile overlay is observable: :deep leaf-replaces the base string.
deep=$(spt adapter get-string claude-spt:deep adapter_label 2>&1)
[ "$deep" = "Claude Code (spt, deep)" ] && ok "overlay observable: :deep adapter_label differs" || bad "deep adapter_label='$deep'"

# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is now file-backed too (LiveAgent activation body; the inline summary was flipped once the
# :live profile + claude-spt-psyche runner landed — REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac

# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped
#     operative body via get-string (REQ-UPS-INJECTION impl, on the registered adapter).
( . "$ROOT/plugin/sptc/hooks/_common.sh"
  inj=$(sptc_inject_skill "$(sptc_skill_key '/sptc:ready listen up')")
  case "$inj" in
    '<sptc_skill name="ready">'*'# /sptc:ready'*'</sptc_skill>'*) exit 0 ;;
    *) printf 'inject got: %.60s\n' "$inj"; exit 1 ;;
  esac ) && ok "UPS skill-injection: /sptc:ready -> wrapped body" || bad "skill-injection did not emit wrapped body"

# 4d. The spt-hosted bringup blocks ([session.self] + [env.SPT_ENDPOINT_ID]) cross-field-validate:
#     `adapter add` (step 1) is manifest-first ("an invalid manifest registers nothing"), so the
#     manifest carrying these blocks registering at all proves spt-core accepted their shape. Confirm
#     the registered adapter advertises a hostable harness — the thing `spt endpoint run` spawns via
#     [session.self] (the M12 cc-launcher target). [int->REQ-DIST-MANIFEST-SCHEMA]
cap=$(spt api --adapter claude-spt --manifest "$MANIFEST" capability 2>&1)
case "$cap" in *LiveAgent*) ok "bringup blocks accepted; capability reports hostable harness" ;; *) bad "capability missing LiveAgent: $cap" ;; esac

# 5. Soft-deregister cleanly.
rm=$(spt adapter remove claude-spt 2>&1)
list2=$(spt adapter list 2>&1)
case "$list2" in *"claude-spt"*"active"*) bad "claude-spt still active after remove" ;; *) ok "removed (soft-deregistered)" ;; esac

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

+# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]

+sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)

+case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac


Append the how-to finding. Read findings tail first.


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && tail -25 docs/SPT-CORE-FINDINGS.md

Findings doc tail


CC's transcript path is `~/.claude/projects/<cwd-slug>/<session_id>.jsonl`. The `<cwd-slug>` subdir
is CC-internal and has **no published key** — and per doyle, *should* not (spt-core stays
harness-agnostic; it must not bake a harness's directory scheme into the key catalog). So `source`
must be the projects **root** and the extractor receives `{session_id}` on the command, resolving
`<slug>/<session_id>.jsonl` itself (the slug is the harness's business). This matches the published
example and what `spt endpoint digest` fills at runtime.

**Root cause (doyle, spt-core).** `digest-proof --sample` builds an **empty** substitution-key map
(`cli.rs:5135`, `let keys = BTreeMap::new()` — comment admits "placeholders like `{session_id}`
will fail"), whereas the runtime daemon path fills `{id}`+`{session_id}` before running the same
extractor (`digest.rs:208-210`). digest-proof is meant to be the author-time half of the *same*
ADR-0019 engine (§diagnostics), so it must supply the same keys. It doesn't → it is infidelitous to
runtime and false-fails any `{session_id}`-templated extractor, including the published example.

Aside: `{home}` is **not** a catalog key — hard-failing on it is correct. Use `~` for home
expansion in `source` (we switched `source` to `~/.claude/projects`; the extractor also expands a
leading `~/` defensively).

**Status — CONFIRMED-IMPL-BUG; fix in progress (doyle's worktree).** digest-proof will fill
`{id}`+`{session_id}` matching runtime exactly (fidelity: *"passes proof" ⟺ "works at runtime"*),
plus an optional `--session <id>` override (default placeholder). The published contract is right;
the tool is catching up. **Our posture:** the extractor is built to the published contract and
proven (cargo unit tests + Variant A `DIGEST_PROOF_OK`); the digest-proof `int`
(`ci/digest/digest-proof-int.sh`, `REQ-DIST-DIGEST-EXTRACTOR`) **skips on the exact substitution
error** until doyle's fix ships, then flips green on the carrying release.


cd C:/Users/decid/Documents/projects/spt-claude-code && cat >> docs/SPT-CORE-FINDINGS.md <<'EOF'

---

## Finding: no `spt how-to subnet` topic (published-guidance gap, minor)

**Surface:** `spt how-to <topic>` ships canonical, always-current guidance for `ready` and `send`
(the messaging hot path) — the claude-spt `ready`/`send` skills lean on it (`spt how-to ready`).
But there is **no `subnet` topic** (`spt how-to subnet` → `NO_SUCH_TOPIC`), despite `spt subnet`
being a multi-verb, pairing-flow-heavy command (create/show-code/join, 6-digit codes, QR) — exactly
the kind of thing a how-to topic serves well.

**Impact (low):** the `/sptc:subnet` skill wraps `spt subnet --help` as its canonical reference
instead. Works, but inconsistent with ready/send (which get richer how-to prose), and a casual user
running `spt how-to subnet` hits a dead end. **Disposition:** mint a `how-to subnet` topic in
spt-core (cross-machine pairing is the highest-value first-run flow; it deserves the same guided
prose as messaging). Carried as a parity finding — NOT a blocker for `/sptc:subnet`.
EOF
echo "appended"

Append subnet how-to finding


appended


Gate + verify subnet resolves on v0.7.2.


cd C:/Users/decid/Documents/projects/spt-claude-code && sh ci/run-gates.sh 2>&1 | grep -iE 'FAIL|RESULT' ; traceable-reqs check >/dev/null 2>&1 && echo "trace OK" || { echo "trace FAIL"; traceable-reqs check 2>&1 | grep -iE 'SKILL-SUBNET|MISSING' | head; } ; echo "---int subnet---"; SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh 2>&1 | grep -iE 'subnet|RESULT|OK$|FAIL'

Gate + trace + subnet int


test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
FAIL: traceable-reqs check
       CI progress/results report over spt messaging (dogfood the product as its own CI nervous system)
       Deterministic gates (shell-syntax + unit tests + traceable-reqs check + manifest-schema + docs-drift), each a binary pass/fail, runnable as one command
=== RESULT: FAIL ===
trace FAIL
  [--] REQ-SKILL-SUBNET  required: [doc, impl, int]  stages: +doc -impl -unit +int
  [must] missing_stage REQ-SKILL-SUBNET stage=impl — Requirement is missing implementation evidence
---int subnet---
ok   file-backed string resolves: skills.subnet -> body
MANIFEST-REGISTRATION-INT OK


subnet int resolves ✓ but traceable wants impl evidence — my inline tag after `# ->` wasn't counted. Move it to a standalone comment line (matches the detected pattern).


Edit

subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} [impl->REQ-SKILL-SUBNET]

# [impl->REQ-SKILL-SUBNET]
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine)


# [impl->REQ-SKILL-SUBNET]
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine)

list-agents = { file = "skills/list-agents.md" } # -> spt endpoint list (the roster)

commune = { file = "skills/commune.md" }       # -> file-drop <id>-commune.md into [session].commune_dir

force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)

-subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} [impl->REQ-SKILL-SUBNET]

+subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine)

# `live` -> upgrade THIS session to a LiveAgent via the `claude-spt:live` profile (overlays

# [session.psyche_init] -> daemon spawns the Psyche) + a resident Monitor relay as the delivery pipe.

# Profile propagation CONFIRMED in spt-core code (doyle 2026-06-15: api seed stores the composite ->


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && traceable-reqs check >/dev/null 2>&1 && echo "trace OK" || traceable-reqs check 2>&1 | grep -iE 'SKILL-SUBNET|missing' | head

Recheck trace


  [--] REQ-SKILL-SUBNET  required: [doc, impl, int]  stages: +doc -impl -unit +int
  [must] missing_stage REQ-SKILL-SUBNET stage=impl — Requirement is missing implementation evidence


impl tag in `.toml` isn't being scanned. Check how the sibling skills (commune/force-stop) were traced + which extensions count.


cd C:/Users/decid/Documents/projects/spt-claude-code && echo "===commune/force-stop REQ tags==="; grep -rni 'REQ-SKILL\|commune.*REQ\|force-stop.*REQ' adapter docs traceable-reqs.toml --include=*.md --include=*.toml 2>/dev/null | grep -iE 'commune|force-stop|subnet' | head; echo "===extensions/scan config==="; grep -niE 'ext|\.toml|\.md|scan|language' docs/TRACEABILITY.md | head -20

How sibling skills traced + scan extensions


===commune/force-stop REQ tags===
adapter/claude-spt.toml:218:# [impl->REQ-SKILL-SUBNET]
adapter/strings/skills/subnet.md:34:<!-- [doc->REQ-SKILL-SUBNET] -->
docs/PARITY.md:28:| **subnet skills (create/join/show-code)** | ADD (LOCKED) | **absent** | 🔨 BUILD (REQ-SKILL-SUBNET) |
docs/PARITY.md:46:spt-core's central value prop, SCOPE). So this is a real gap to fill, not defer. → `REQ-SKILL-SUBNET`.
traceable-reqs.toml:161:required_stages = ["doc"]  # doc: docs/PARITY.md (the parity matrix + per-row evidence/finding). Pure reconciliation record (no code of its own); the per-surface impl/unit evidence lives on the surface's own REQ (REQ-SKILL-SUBNET, REQ-CCS-PROFILES, REQ-SKILL-LIVE, etc.).
traceable-reqs.toml:164:id = "REQ-SKILL-SUBNET"
===extensions/scan config===
5:> `traceable-reqs.toml` (seed it from your requirements source + your KNOWN-HAZARDS
18:   KNOWN-HAZARDS invariant exists in `traceable-reqs.toml` as a `REQ-*` id. No work without a
33:6. **Scan roots stay honest.** `[scan].roots` includes every evidence location (your source,
46:stage (`[<impl>->...]`) only so the scanner treats them as illustrations, not live evidence —
71:3. **Agent-facing rule.** The repo `AGENTS.md` instructs every dev-agent: tag
81:## CI snippet (hosted-runner fallback; see `docs/CI.md` for the agent-driven default)


Precedent clear: file-backed skills get no per-skill `impl` (commune/force-stop have none — they're covered by the manifest int). The honest model for subnet: **doc + int** (body = doc; registration-int resolving `skills.subnet` through the live registry = the wiring proof). Drop the impl stage.


Edit

required_stages = ["doc", "impl", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). impl: [strings.skills].subnet entry + plugin/sptc/skills/subnet/SKILL.md stub + the subnet hint. int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt. (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)

required_stages = ["doc", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt — the wiring proof. No separate `impl`/`unit` stage: a file-backed skill has no unit-testable code of its own (the manifest [strings.skills].subnet pointer + plugin stub are config, and the int proves they resolve end-to-end through the live registry — same model as the commune/force-stop skills, which carry no per-skill impl). (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)


title = "The adapter ships subnet membership skill(s) (create/join/show-code) wrapping the published `spt subnet <verb>` surface — the LOCKED-ADD cross-subnet entrypoint (v1 topology is mandatory, SCOPE)"

-required_stages = ["doc", "impl", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). impl: [strings.skills].subnet entry + plugin/sptc/skills/subnet/SKILL.md stub + the subnet hint. int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt. (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)

+required_stages = ["doc", "int"]  # doc: adapter/strings/skills/subnet.md (one /sptc:subnet skill covering status/create/show-code/join — mirrors the single `spt subnet` verb-family CLI). int: ci/manifest/registration-int.sh file-backed-resolve assertion (skills.subnet -> body) on live spt — the wiring proof. No separate `impl`/`unit` stage: a file-backed skill has no unit-testable code of its own (the manifest [strings.skills].subnet pointer + plugin stub are config, and the int proves they resolve end-to-end through the live registry — same model as the commune/force-stop skills, which carry no per-skill impl). (No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)


# [impl->REQ-SKILL-SUBNET]
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine)

subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine); REQ-SKILL-SUBNET


# [impl->REQ-SKILL-SUBNET]
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine)

-subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine)

+subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine); REQ-SKILL-SUBNET


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && traceable-reqs check >/dev/null 2>&1 && echo "trace OK" || traceable-reqs check 2>&1 | grep -iE 'SUBNET|missing' | head; echo "---gate---"; sh ci/run-gates.sh 2>&1 | grep -iE 'RESULT|FAIL'

Recheck trace + gate


trace OK
---gate---
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 8 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
=== RESULT: PASS ===


trace OK + gate PASS. Commit Phase C.


cd C:/Users/decid/Documents/projects/spt-claude-code && git add -A && git commit -q -F - <<'EOF'
feat(parity): add /sptc:subnet skill — LOCKED-ADD cross-machine membership (Phase C)

Fill the subnet parity gap: one /sptc:subnet skill wrapping the published
`spt subnet` verb-family (status/create/show-code/join + lifecycle verbs) — the
cross-machine pairing entrypoint that makes /sptc:send + live agents work across
nodes (v1 topology is mandatory, SCOPE). One skill mirrors the single CLI command
(like force-stop wrapping shutdown+stop), keeping the surface lean.

- adapter/strings/skills/subnet.md (file-backed body: pairing flow, the 4 primary
  verbs, lifecycle verbs, code-is-sensitive note).
- [strings.skills].subnet entry + plugin/sptc/skills/subnet/SKILL.md stub + a
  subnet hint ("another machine"/"pair machine"/"subnet"...).
- REQ-SKILL-SUBNET [doc,int] activated; registration-int asserts skills.subnet ->
  body (green on live v0.7.2). doc+int only — a file-backed skill has no unit-
  testable code (same model as commune/force-stop); the int is the wiring proof.
- Finding logged (docs/SPT-CORE-FINDINGS.md): no `spt how-to subnet` topic exists
  (ready/send have one) — minor published-guidance gap; skill wraps `--help`.

Published skill surface now 10. docs/PARITY.md subnet row -> satisfied.
Gate PASS + traceable green.

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

Commit Phase C


e3382a0 feat(parity): add /sptc:subnet skill — LOCKED-ADD cross-machine membership (Phase C)


Phases A/B/C committed (`b1db67f`, `0595910`, `e3382a0`). Context is high + Phase D (ccs profiles) needs fresh research into the command-template seams. Update the plan as a clean checkpoint.


Edit

## Phases (sequence; one commit + green gate each)

### Phase A — Parity audit artifact + REQ seed

## Progress (this session)
- ✅ **Phase A** (b1db67f) — docs/PARITY.md matrix + REQ seed (REQ-PARITY-AUDIT [doc] satisfied;
  REQ-SKILL-SUBNET / REQ-CCS-PROFILES seeded). DROP set confirmed absent.
- ✅ **Phase B** (0595910) — whoami skill DROPPED (manifest + body + stub + hint + test repoints);
  dead new-alarm stub removed. Surface 11→9.
- ✅ **Phase C** (e3382a0) — /sptc:subnet skill added (one skill wrapping `spt subnet`).
  REQ-SKILL-SUBNET [doc,int] green on v0.7.2. how-to-subnet finding logged. Surface 9→10.
- ⏭ **Phase D** (ccs profiles) — NEXT. Needs research: the spawn/psyche/echo command seams a ccs
  overlay leaf-replaces. Activate REQ-CCS-PROFILES [doc,impl] when starting.
- ⏭ **Phase E** (publish prep as sptc) — after D.

## Phases (sequence; one commit + green gate each)

### Phase A — Parity audit artifact + REQ seed


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

## Progress (this session)
- ✅ **Phase A** (b1db67f) — docs/PARITY.md matrix + REQ seed (REQ-PARITY-AUDIT [doc] satisfied;
  REQ-SKILL-SUBNET / REQ-CCS-PROFILES seeded). DROP set confirmed absent.
- ✅ **Phase B** (0595910) — whoami skill DROPPED (manifest + body + stub + hint + test repoints);
  dead new-alarm stub removed. Surface 11→9.
- ✅ **Phase C** (e3382a0) — /sptc:subnet skill added (one skill wrapping `spt subnet`).
  REQ-SKILL-SUBNET [doc,int] green on v0.7.2. how-to-subnet finding logged. Surface 9→10.
- ⏭ **Phase D** (ccs profiles) — NEXT. Needs research: the spawn/psyche/echo command seams a ccs
  overlay leaf-replaces. Activate REQ-CCS-PROFILES [doc,impl] when starting.
- ⏭ **Phase E** (publish prep as sptc) — after D.

## Phases (sequence; one commit + green gate each)

### Phase A — Parity audit artifact + REQ seed

# JIT plan — parity audit → fill LOCKED-ADD gaps → cplugs publish (as sptc)

> Next slice after the live sub-slice (4b042cc) + F-004 flip (692b67c). Goal: PROVE parity
> against the SCOPE.md LOCKED parity-trim, fill the remaining LOCKED-ADD gaps, then publish the
> thin skeleton to cplugs **as `sptc`** (NOT `spt` — succession flip stays gated, see
> [[succession-flip-gated]] / RELEASE-RUNBOOK). Public-surface-only still binds (AGENTS.md).

## Operator decisions (locked this session, 2026-06-15)
1. **Slice = audit + fill gaps + publish.** Build the absent LOCKED-ADD surface (subnet skills,
   ccs profiles), reconcile whoami, THEN publish as sptc. `new-alarm` stays **deferred** — doyle
   TRIAGED it (high-prio 2026-06-15) as an **ACCEPTED parity gap = "deferred-pending-arrangement-
   system", NOT missing**: spt-core has only the alarm event shape + relay handling + a test
   fixture; the durable timer is a deliberate deferral (DEFERRED.md:43, ADR-0018 Q4/V3), and the
   operator has a superseding "arrangement" system in mind. **Do NOT build an adapter-side stopgap
   timer** (throwaway vs the coming model), **no REQ minted**. Remove the dead stub (deferred ≠
   shipping a no-op skill); doyle loops me when the arrangement design surfaces.
2. **Drop the `/whoami` SKILL.** Honor the LOCKED SCOPE decision — core `spt whoami` stays
   (becomes the `endpoint list` alias in M12); the adapter ships no whoami skill.

## Parity matrix (current vs SCOPE LOCKED parity-trim)

| Legacy / target surface        | SCOPE disposition        | Adapter today          | Action (this slice)            |
|--------------------------------|--------------------------|------------------------|--------------------------------|
| /live /commune /ready /send    | KEEP                     | operative (file-backed)| ✓ none                         |
| /list-agents /signoff /force-stop | KEEP                  | operative              | ✓ none                         |
| /new-alarm                     | ACCEPTED GAP (deferred-pending-arrangement-system) | dead stub, no manifest | **remove stub**; no REQ, no build |
| /spt:setup /spt:version        | ADD                      | operative              | ✓ none                         |
| profiles + strings + hints     | ADD                      | wired (deep/live)      | ✓ none                         |
| **subnet skills (create/join/show-code)** | ADD (LOCKED)  | **ABSENT**             | **BUILD** (Phase C)            |
| **ccs profiles (claude-spt:glm …)** | ADD (LOCKED)        | **ABSENT**             | **BUILD** (Phase D)            |
| /whoami SKILL                  | DROP skill               | **operative (violation)** | **REMOVE** (Phase B)        |
| /fork /amend-signoff /revive /clear-psyche | DROP         | absent                 | ✓ none (confirm absent)        |

Public surface confirms the ADD gaps are buildable: `spt subnet {create,join,show-code,status}`
exists; ccs = profile overlays retargeting command templates (SCOPE §ccs, profile *templates*
only — user supplies their own ccs config/keys).

## Phases (sequence; one commit + green gate each)

### Phase A — Parity audit artifact + REQ seed
- Write `docs/PARITY.md` (the matrix above + per-row evidence/finding) — the "proven parity"
  record the succession flip gates on. Tag `<!-- doc->REQ-PARITY-AUDIT -->`.
- Seed REQs in `traceable-reqs.toml` FIRST (before building): `REQ-SKILL-SUBNET`,
  `REQ-CCS-PROFILES`, `REQ-PARITY-AUDIT`. Activate stages as each phase lands.
- Confirm the DROP set is truly absent (grep no fork/revive/clear-psyche/amend-signoff residue).

### Phase B — Drop the whoami skill (honor LOCKED)
- Remove `whoami = { file = "skills/whoami.md" }` from manifest `[strings.skills]`.
- Delete `adapter/strings/skills/whoami.md` + `plugin/sptc/skills/whoami/`.
- **registration-int.sh** uses `skills.whoami` as the canonical file-backed + UPS-injection
  example (4b/4c) — repoint to a surviving skill (`skills.ready`).
- Redirect the `who am i` **hint** (currently → /sptc:whoami) to core `spt whoami` guidance.
- Audit REQ evidence: nothing is tagged `REQ-*` on whoami.md directly, but check
  `tests/hooks-parse.sh` / manifest-shortcut for whoami coupling before deleting.

### Phase C — Subnet skills (LOCKED ADD; public verbs exist)
- **Open shape decision (resolve at top of phase):** three skills
  (`/sptc:subnet-create|join|show-code`) vs one routing `/sptc:subnet` skill. Lean: match SCOPE's
  enumeration but as ONE `subnet` skill body that documents create/join/show-code (fewer
  surfaces, cleaner UX) — confirm before authoring.
- File-backed body(ies) `adapter/strings/skills/subnet*.md` (run `spt how-to subnet` / wrap the
  real `spt subnet …` verbs); manifest `[strings.skills]` entries; thin SKILL.md stub(s); hint(s).
- `REQ-SKILL-SUBNET` [doc,impl] (+ int via registration-int file-backed-resolve assertion).

### Phase D — ccs profiles (LOCKED ADD)
- **Research first** (mcp/context7 not needed — read SCOPE §ccs + the existing `[profiles.*]`
  command seams). ccs profile = a `[profiles.<glm|kimi>]` overlay that leaf-replaces the spawn /
  psyche / echo command templates to route through `ccs <profile> …`, with a per-profile `~/.ccs`
  log dir. Author profile *templates* only (no user keys).
- Add `[profiles.glm]` (+ maybe `[profiles.kimi]`) overlays; observable via `spt adapter
  get-string claude-spt:glm …` + resolves in registration-int (mirror the :deep/:live assertions).
- `REQ-CCS-PROFILES` [doc,impl] (+ int).

### Phase E — Publish prep (as sptc; NO name flip)
- **Create `CHANGELOG.md`** with a `## [<version>] - 2026-06-15` section (Added/Changed/Fixed,
  user-facing UX only, no internal lingo) — release fails loudly without it.
- **Bump `plugin.json`** `version` 0.0.1 → first real (structural first publish). Keep `name=sptc`.
- Run `sh ci/publish/validate-skeleton.sh` + `sh ci/publish/package-skeleton.sh` (DRY-RUN) green.
- Regenerate docs (mdBook + llms.txt) — drift gate green.
- The actual cplugs commit/push + `claude plugin install sptc@cplugs` stays the **operator's**
  step (credentials + pointer flip) — plan stops at a validated dry-run + a publish checklist.

### Gate (every phase)
`sh ci/run-gates.sh` PASS + `traceable-reqs check` green. Commit `Co-authored by: perri`.

## Open questions (resolve in-phase, don't block planning)
- Subnet skill shape: 3 skills vs 1 routing skill (Phase C top).
- ccs: which profiles ship as templates (glm only, or glm+kimi)? SCOPE names `claude-spt:glm`.
- plugin.json first-publish version number (0.1.0? matches adapter manifest 0.1.0, but RUNBOOK
  says keep them DISTINCT — pick independently).

## Findings carried (not ours to fix)
- new-alarm core alarm/deferred-pulse primitive (blocks new-alarm parity) — doyle.
- hard/no-grace force-stop primitive — doyle.
- Live relay int held for spt-core M11 counter-15 release ([[live-int-gated-m11-counter15]]).

## Done immediately before (context)
- 4b042cc live sub-slice (claude-spt-psyche runner + /sptc:live body, REQ-SKILL-LIVE [doc,impl,unit]).
- 692b67c F-004 flip (digest-proof int GREEN on v0.7.2, REQ-DIST-DIGEST-EXTRACTOR full).
- spt upgraded 0.7.1 → 0.7.2 (operator-approved daemon bounce).

+- ✅ **Phase A** (b1db67f) — docs/PARITY.md matrix + REQ seed (REQ-PARITY-AUDIT [doc] satisfied;

+  REQ-SKILL-SUBNET / REQ-CCS-PROFILES seeded). DROP set confirmed absent.

+- ✅ **Phase B** (0595910) — whoami skill DROPPED (manifest + body + stub + hint + test repoints);

+  dead new-alarm stub removed. Surface 11→9.

+- ✅ **Phase C** (e3382a0) — /sptc:subnet skill added (one skill wrapping `spt subnet`).

+  REQ-SKILL-SUBNET [doc,int] green on v0.7.2. how-to-subnet finding logged. Surface 9→10.

+- ⏭ **Phase D** (ccs profiles) — NEXT. Needs research: the spawn/psyche/echo command seams a ccs

+  overlay leaf-replaces. Activate REQ-CCS-PROFILES [doc,impl] when starting.

+- ⏭ **Phase E** (publish prep as sptc) — after D.


Commit the checkpoint.


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && git add -A && git commit -q -F - <<'EOF'
docs(plan): mark parity Phases A/B/C done; Phase D (ccs) next

Checkpoint after subnet (e3382a0). Remaining: Phase D ccs profiles (needs
command-seam research) + Phase E publish prep (as sptc).

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

Commit plan checkpoint


7073e6a docs(plan): mark parity Phases A/B/C done; Phase D (ccs) next
e3382a0 feat(parity): add /sptc:subnet skill — LOCKED-ADD cross-machine membership (Phase C)
0595910 feat(parity): drop the whoami skill + remove dead new-alarm stub (Phase B)
b1db67f docs(parity): audit matrix + REQ seed (parity-publish slice Phase A)
692b67c test(digest): flip digest-proof int GREEN on spt v0.7.2 (F-004 unblocked)


Clean checkpoint — 5 commits this session, gate green throughout.

**Done this session:**
| Commit | Phase | What |
|---|---|---|
| `4b042cc` | (prior) | Live sub-slice — psyche runner + `/sptc:live` |
| `692b67c` | (prior) | F-004 digest int green on v0.7.2 |
| `b1db67f` | A | Parity audit matrix (`docs/PARITY.md`) + REQ seed |
| `0595910` | B | Dropped whoami skill + dead new-alarm stub (surface 11→9) |
| `e3382a0` | C | `/sptc:subnet` cross-machine skill (surface 9→10) |

**Parity status:** 2 of 3 gaps closed. Remaining for proven parity:
- **Phase D — ccs profiles** (`claude-spt:glm` …): LOCKED-ADD, unbuilt. Needs research into the spawn/psyche/echo command seams a ccs overlay leaf-replaces.
- **Phase E — publish prep as `sptc`**: CHANGELOG, version bump, `package-skeleton` dry-run.

Context is high and Phase D's ccs research is cleaner with fresh context. Recommend I **self-clear** (plan + PARITY.md + memory are the resume pointers) and continue Phase D autonomously — or say **continue** to push through now, or **pause** here. Your call.


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


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

# /spt:commune

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

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

## Flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Envelope shape (Phase 23 v1.8)

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

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

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

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

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

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


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


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


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


Communing for next-session resume. Download current Psyche context first.


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

Download current Psyche context


<current machine="HFENDULEAM" project="spt-claude-code" branch="main" head_sha="7073e6abf21f953a8fbd9269e011e1c4e8f2ae12" head_subject="docs(plan): mark parity Phases A/B/C done; Phase D (ccs) next" commits_since="0" commits_unpulled="0"/>
**Session delta (post-checkpoint 970ce0b):**

Two substantive commits landed + doyle's ack delivered:
- `4b042cc`: Live sub-slice — `claude-spt-psyche` resident runner + `/sptc:live` body + REQ-SKILL-LIVE [doc,impl,unit]
- `692b67c`: F-004 flip — digest-proof int GREEN (5 parsed/0 dropped), REQ-DIST-DIGEST-EXTRACTOR full [impl,unit,int]

Gate PASS + traceable exit 0. All file-backed skill bodies resolve incl new `skills.live`; no binary/doc mismatch found on v0.7.2.

**doyle message (legacy owl, core-bound data):** REQ-DAEMON-1/M11-W0 complete in spt-core end-to-end (brain first-host; relay retired to dumb-pipe; lost-update + un-host closed). BUT: NOT YET in published binary — rides counter-15 release, not 0.7.2. Live relay int unblocks ON THAT TAG. doyle will PING on release. W0.5 cutover (your resident runner onto daemon-hosted Psyche path) = ZERO adapter change — slots in when bytes land.

**Standing constraints (recorded):**
1. Live int held for spt-core counter-15 release ping (do not try against 0.7.2 — daemon loop not in those bytes).
2. sptc→spt flip held until: proven parity AND legacy owl retired (CC ties `/spt:` prefix to plugin.json name; owl still owns it).

**Next plate:** Parity audit (cplugs + public surface) → cplugs publish as `sptc`. User said "proceed, plan first"; scoped REQ docs + RELEASE-RUNBOOK; gathering plugin.json + package-skeleton for plan draft.<project-context-resolved name="spt-claude-code"/>
Session advanced 2 commits toward v1 ship; perri now prepping parity audit before CPaaS publish. F-004 acceptance GREEN. Live relay int gate = doyle's counter-15 release announcement (awaiting ping). No changes to prior project state.


Psyche context current through the *start* of the parity slice — it knows live+F-004+constraints but NOT Phases A/B/C. Communing the delta. In-project → both envelopes.


Write

<live-context>
**Parity slice — Phases A/B/C DONE (delta since "gathering for plan draft").** The plan landed
(PARITY-PUBLISH-PLAN.md) + executed through C. 4 commits past 692b67c:

- **b1db67f (A):** docs/PARITY.md disposition matrix (KEEP/ADD/DROP/TRANSFORM vs SCOPE LOCKED).
  Verdict = NOT-YET-proven. REQs seeded: REQ-PARITY-AUDIT [doc] satisfied; REQ-SKILL-SUBNET +
  REQ-CCS-PROFILES seeded. DROP set confirmed absent.
- **0595910 (B):** DROPPED the whoami skill (operator ruling, honors SCOPE LOCKED) — removed
  [strings.skills].whoami + body + plugin stub; "who am i" hint → core `spt whoami`. Repointed
  registration-int 4b/4c + hooks-parse parser examples whoami→ready. Core `spt whoami` CLI calls
  in hooks STAY (binary, not skill). Also removed the dead new-alarm stub.
- **e3382a0 (C):** Added ONE /sptc:subnet skill wrapping `spt subnet {status,create,show-code,join}`
  (mirrors the single CLI verb-family, like force-stop). REQ-SKILL-SUBNET [doc,int] — green on
  v0.7.2. Logged finding: no `spt how-to subnet` topic (ready/send have one) — minor guidance gap.
- **7073e6a:** plan checkpoint.

**Operator decisions this session:** (1) slice = audit + FILL gaps + publish (not audit-only);
(2) drop whoami skill. **doyle triage:** new-alarm = ACCEPTED gap "deferred-pending-arrangement-
system" (operator has a superseding arrangement model) — do NOT build a stopgap timer, no REQ.

Published skill surface now **10** (commune, force-stop, list-agents, live, ready, send, setup,
signoff, subnet, version). Gate PASS + traceable exit 0 throughout. registration-int all-green on
live v0.7.2 (incl skills.subnet, skills.ready repoint).

**IMMEDIATE NEXT STEPS (resume here):**
1. **Phase D — ccs profiles** (REQ-CCS-PROFILES, currently inactive []). LOCKED-ADD: add
   `[profiles.glm]` (maybe +kimi) overlays that leaf-replace the spawn/psyche/echo command
   templates to route through `ccs <profile>`, per-profile ~/.ccs log dir. TEMPLATES ONLY (user
   brings own ccs config/keys). RESEARCH FIRST: which command seams exist to override
   (psyche_init.command, session.self.command, digest.extractor) + SCOPE §ccs. Activate
   REQ-CCS-PROFILES [doc,impl] on start; int via registration-int profile-resolves (mirror :deep).
   NOTE the trace gotcha hit in C: a per-skill/profile artifact in .toml does NOT register as
   `impl` evidence — a file-backed skill is [doc,int] (int = registration-int resolve proof). For
   ccs the impl IS the profile overlay (config); if .toml impl tag won't register, use [doc,int]
   model (overlay resolves through registry = the proof).
2. **Phase E — publish prep as `sptc`** (NO name flip — gated). Create CHANGELOG.md with a
   `## [version] - <date>` section (release fails loudly without it); bump plugin.json 0.0.1→first
   real (keep name=sptc); `sh ci/publish/validate-skeleton.sh` + `package-skeleton.sh` dry-run
   green; regen docs. cplugs commit/push + `claude plugin install sptc@cplugs` = OPERATOR's step.
</live-context>
<project-context>
**spt-claude-code — v1 acceptance status (parity slice in flight).** claude-spt is spt-core's v1
acceptance proof: a rebuilt Claude Code harness adapter built against the PUBLIC surface ONLY
(spt-releases + sabermage.github.io/spt-releases). End goal unchanged: spt-core's v1 acceptance
proof + first casual-end-user entrypoint (cplugs plugin `sptc`, skills `/sptc:*`) + invisible
spt-core installer. On parity-proof: cplugs publish, then the sptc→spt succession.

**Where parity stands:** of 3 open LOCKED-ADD/DROP gaps, **2 closed** (whoami skill dropped;
subnet skill added). **1 remains: ccs profiles** (Phase D). Then publish (Phase E). docs/PARITY.md
is the authoritative reconciliation record + the "proven parity" gate the name-flip depends on —
its verdict flips to PROVEN once ccs lands (everything else already ✅).

**spt-core dep state:** local binary upgraded to **v0.7.2** this session (operator-approved daemon
bounce) — carries doyle's F-004 fix (digest-proof int GREEN) + PR#15 harness-contract docs. Two
spt-core gates remain non-blocking: (1) live relay int held for the **M11 counter-15** release
(daemon Psyche loop complete in core but not in published bytes — doyle pings on tag); (2)
sptc→spt name flip held until proven parity AND legacy owl retired.

**Carried findings (doyle's, not ours to fix):** alarm/deferred-pulse primitive (new-alarm parity,
superseded by the coming arrangement model); hard/no-grace force-stop primitive; no `spt how-to
subnet` topic. v1-acceptance arc after this slice: parity-proven → cplugs publish (as sptc) →
succession.
</project-context>


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

<live-context>
**Parity slice — Phases A/B/C DONE (delta since "gathering for plan draft").** The plan landed
(PARITY-PUBLISH-PLAN.md) + executed through C. 4 commits past 692b67c:

- **b1db67f (A):** docs/PARITY.md disposition matrix (KEEP/ADD/DROP/TRANSFORM vs SCOPE LOCKED).
  Verdict = NOT-YET-proven. REQs seeded: REQ-PARITY-AUDIT [doc] satisfied; REQ-SKILL-SUBNET +
  REQ-CCS-PROFILES seeded. DROP set confirmed absent.
- **0595910 (B):** DROPPED the whoami skill (operator ruling, honors SCOPE LOCKED) — removed
  [strings.skills].whoami + body + plugin stub; "who am i" hint → core `spt whoami`. Repointed
  registration-int 4b/4c + hooks-parse parser examples whoami→ready. Core `spt whoami` CLI calls
  in hooks STAY (binary, not skill). Also removed the dead new-alarm stub.
- **e3382a0 (C):** Added ONE /sptc:subnet skill wrapping `spt subnet {status,create,show-code,join}`
  (mirrors the single CLI verb-family, like force-stop). REQ-SKILL-SUBNET [doc,int] — green on
  v0.7.2. Logged finding: no `spt how-to subnet` topic (ready/send have one) — minor guidance gap.
- **7073e6a:** plan checkpoint.

**Operator decisions this session:** (1) slice = audit + FILL gaps + publish (not audit-only);
(2) drop whoami skill. **doyle triage:** new-alarm = ACCEPTED gap "deferred-pending-arrangement-
system" (operator has a superseding arrangement model) — do NOT build a stopgap timer, no REQ.

Published skill surface now **10** (commune, force-stop, list-agents, live, ready, send, setup,
signoff, subnet, version). Gate PASS + traceable exit 0 throughout. registration-int all-green on
live v0.7.2 (incl skills.subnet, skills.ready repoint).

**IMMEDIATE NEXT STEPS (resume here):**
1. **Phase D — ccs profiles** (REQ-CCS-PROFILES, currently inactive []). LOCKED-ADD: add
   `[profiles.glm]` (maybe +kimi) overlays that leaf-replace the spawn/psyche/echo command
   templates to route through `ccs <profile>`, per-profile ~/.ccs log dir. TEMPLATES ONLY (user
   brings own ccs config/keys). RESEARCH FIRST: which command seams exist to override
   (psyche_init.command, session.self.command, digest.extractor) + SCOPE §ccs. Activate
   REQ-CCS-PROFILES [doc,impl] on start; int via registration-int profile-resolves (mirror :deep).
   NOTE the trace gotcha hit in C: a per-skill/profile artifact in .toml does NOT register as
   `impl` evidence — a file-backed skill is [doc,int] (int = registration-int resolve proof). For
   ccs the impl IS the profile overlay (config); if .toml impl tag won't register, use [doc,int]
   model (overlay resolves through registry = the proof).
2. **Phase E — publish prep as `sptc`** (NO name flip — gated). Create CHANGELOG.md with a
   `## [version] - <date>` section (release fails loudly without it); bump plugin.json 0.0.1→first
   real (keep name=sptc); `sh ci/publish/validate-skeleton.sh` + `package-skeleton.sh` dry-run
   green; regen docs. cplugs commit/push + `claude plugin install sptc@cplugs` = OPERATOR's step.
</live-context>
<project-context>
**spt-claude-code — v1 acceptance status (parity slice in flight).** claude-spt is spt-core's v1
acceptance proof: a rebuilt Claude Code harness adapter built against the PUBLIC surface ONLY
(spt-releases + sabermage.github.io/spt-releases). End goal unchanged: spt-core's v1 acceptance
proof + first casual-end-user entrypoint (cplugs plugin `sptc`, skills `/sptc:*`) + invisible
spt-core installer. On parity-proof: cplugs publish, then the sptc→spt succession.

**Where parity stands:** of 3 open LOCKED-ADD/DROP gaps, **2 closed** (whoami skill dropped;
subnet skill added). **1 remains: ccs profiles** (Phase D). Then publish (Phase E). docs/PARITY.md
is the authoritative reconciliation record + the "proven parity" gate the name-flip depends on —
its verdict flips to PROVEN once ccs lands (everything else already ✅).

**spt-core dep state:** local binary upgraded to **v0.7.2** this session (operator-approved daemon
bounce) — carries doyle's F-004 fix (digest-proof int GREEN) + PR#15 harness-contract docs. Two
spt-core gates remain non-blocking: (1) live relay int held for the **M11 counter-15** release
(daemon Psyche loop complete in core but not in published bytes — doyle pings on tag); (2)
sptc→spt name flip held until proven parity AND legacy owl retired.

**Carried findings (doyle's, not ours to fix):** alarm/deferred-pulse primitive (new-alarm parity,
superseded by the coming arrangement model); hard/no-grace force-stop primitive; no `spt how-to
subnet` topic. v1-acceptance arc after this slice: parity-proven → cplugs publish (as sptc) →
succession.
</project-context>


Commune written to `.claude/perri-commune.md`. Verify ingestion (file disappears = Psyche absorbed it).


Bash

cd C:/Users/decid/Documents/projects/spt-claude-code && sleep 3; if [ -f .claude/perri-commune.md ]; then echo "STILL PRESENT — not yet consumed (Self polls on its next iter; will ingest or retry)"; else echo "CONSUMED — Psyche ingested the delta"; fi

Check commune consumption


STILL PRESENT — not yet consumed (Self polls on its next iter; will ingest or retry)


Commune dropped + queued (Self ingests on next poll — last-write-wins, auto-retries). Resume pointers all set for next session:

- **Psyche commune** — `.claude/perri-commune.md` (delta: Phases A/B/C done, immediate next steps).
- **Plan** — `PARITY-PUBLISH-PLAN.md` (Phase D ccs + Phase E publish remain).
- **Audit record** — `docs/PARITY.md` (verdict flips to PROVEN once ccs lands).
- **Memory** — `live-int-gated-m11-counter15`, `succession-flip-gated`.

**Session tally:** 6 commits, gate PASS + traceable green throughout. Live sub-slice + F-004 flip + parity Phases A/B/C (whoami dropped, subnet added, surface=10). spt upgraded 0.7.1→0.7.2.

**Resume at:** Phase D — ccs profiles (research command seams → `[profiles.glm]` overlay), then Phase E publish prep as `sptc`. Two spt-core pings inbound from doyle (counter-15 release → live relay int).

Clean stop. 🪨


<owl-active-perch id="perri" live="true">
You have an active live agent session running as "perri".

You are Self. Your Psyche (perri-psyche) monitors you in the background.

Key commands:
- Send context updates: /spt:commune (canonical — Write tool to .claude/perri-commune.md per the skill)
- Messages arrive via Monitor stream EVENT envelope (primary) or as <owl_messages> XML (PreToolUse hook). Bash + --once fallback also supported. Process and reply with $OWL send --reply-to <sender>.
- Send messages: $OWL send <target> perri    (fire-and-forget)
- Wait for reply: $OWL ring <target> perri   (blocking)
- Reply to inbound: $OWL send --reply-to <sender> perri
- Schedule reminders: $OWL new-alarm perri <time> -- <message>
- Stop session: /spt:force-stop perri

Stay-idle pattern: When no messages are pending, do NOT poll again immediately.
Wait for user input or a background task to complete.

DO NOT run /spt:live or /spt:ready -- your session is already active.
</owl-active-perch>
<psyche-context id="perri">
<current machine="HFENDULEAM" project="spt-claude-code" branch="main" head_sha="cba6100d83a1e631675530d120c1b57be0ce9b60" head_subject="docs(tips): scope harness-adapter tips to the public framework only" commits_since="0" commits_unpulled="0"/>
**Post-parity documentation & findings closure.** Parity slice remains DONE (Phase E ✅; verdict PROVEN). Session added `SPT_HARNESS_ADAPTER_TIPS.md` (public framework facts, verified against live v0.7.2) + reported `how-to subnet` docs gap to doyle.

**doyle response (task notification 2026-06-16T03:23):** Confirmed gap is genuine content gap under existing REQ-DOCS-6 (in-binary agent guidance), NOT a published-docs or docs-read miss. Minting topic scheduled M11-W5 (rig+docs wave), no new REQ, no mid-flight interrupt. Parity PROVEN noted. Succession still gated on owl-retire + M11 counter-15 publish (doyle will ping on tag).

**No new docs gaps** from phases D/E (profile overlays, `get-string`, `adapter add` all verified on v0.7.2).

Ledger updated + committed af55edd.<project-context-resolved name="spt-claude-code"/>
Parity slice state unchanged: verdict PROVEN on adapter surface. Operator publish step (`sh ci/publish/package-skeleton.sh --apply`) awaits operator only; sptc→spt successor flip held for owl-retire + M11 counter-15. No blocking work; idle pending M11 counter-15 tag.</psyche-context>


look over `SPT_HARNESS_ADAPTER_TIPS.md` and consider whether there's anything else to add, given this session's learnings about building an SPT harness adapter. only record what applies to the existing public framework, and ignore any docs gaps which may have arisen. if there WERE any docs gaps, be sure to inform doyle if you haven't already.


Psyche says tips file exists, how-to-subnet gap already reported + doyle-acked. Read the tips file, find genuine additions from this session's learnings (public-framework facts only).