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)

#!/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)

# 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)
sptc_inject_skill "$(sptc_skill_key "$prompt")"

# 2. Message-drain needs a perch. No perch (session not readied) -> nothing to deliver.
id=$(sptc_self_id "$sid")
[ -z "$id" ] && exit 0

SPT=$(spt_bin)
frames=$("$SPT" api --adapter "$ADAPTER" poll "$id" --session-id "$sid" 2>/dev/null)
[ -z "$frames" ] && exit 0

# 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>.
render_frames "$frames"
exit 0

-# UserPromptSubmit: drain delivered messages (the hook-injection channel, L149) and surface

-# them to CC as additionalContext. Parses the canonical self-delimiting <EVENT> envelope

-# (ADR-0020), so multi-message drains split cleanly on </EVENT>. [impl->REQ-UPS-INJECTION]

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

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

+# 2. Message-drain needs a perch. No perch (session not readied) -> nothing to deliver.

-[ -z "$id" ] && exit 0   # no perch (session not readied) -> nothing to deliver

-# UserPromptSubmit: stdout is added to the prompt context. (JSON additionalContext shape is an

-# alternative — to confirm in throwaway validation which CC accepts here.)

#!/bin/sh
# UserPromptSubmit: drain delivered messages (the hook-injection channel, L149) and surface
# them to CC as additionalContext. Parses the canonical self-delimiting <EVENT> envelope
# (ADR-0020), so multi-message drains split cleanly on </EVENT>. [impl->REQ-UPS-INJECTION]
. "$CLAUDE_PLUGIN_ROOT/hooks/_common.sh"

input=$(cat)
sid=$(json_str "$input" session_id)
id=$(sptc_self_id "$sid")
[ -z "$id" ] && exit 0   # no perch (session not readied) -> nothing to deliver

SPT=$(spt_bin)
frames=$("$SPT" api --adapter "$ADAPTER" poll "$id" --session-id "$sid" 2>/dev/null)
[ -z "$frames" ] && exit 0

# 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>.
# UserPromptSubmit: stdout is added to the prompt context. (JSON additionalContext shape is an
# alternative — to confirm in throwaway validation which CC accepts here.)
render_frames "$frames"
exit 0


Add unit tests for `sptc_skill_key`. Read the tail of hooks-parse.sh.


40	  "$multi"
41
42	# Envelope without a from attr -> anonymous rendering.
43	anon=$(render_frames '<EVENT type="msg">sys note</EVENT>')
44	check "render no-from" "$(printf '<sptc_messages>\nsys note\n</sptc_messages>')" "$anon"
45
46	# Empty drain -> no output.
47	check "render empty" "" "$(render_frames '')"
48
49	[ "$fail" -eq 0 ] && { echo "ALL PASS"; exit 0; } || { echo "FAILURES"; exit 1; }
50

  "$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 '')"

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


Edit

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

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

# 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 '')"

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


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)

# 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 '')"

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

#!/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 '')"

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

+# --- 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 '')"


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code; sh tests/hooks-parse.sh 2>&1 | tail -15

Run hook parse unit tests


ok   unescape br+entities
ok   render named <EVENT>
ok   render entity body
ok   render multi-message
ok   render no-from
ok   render empty
ok   skill_key bare
ok   skill_key with args
ok   skill_key leading ws
ok   skill_key hyphenated
ok   skill_key mid-prose
ok   skill_key other ns
ok   skill_key not a cmd
ok   skill_key empty
ALL PASS


Unit green (14/14). Now extend the int: prove the hook helper `sptc_inject_skill` emits the resolved body end-to-end (adapter registered).


Edit

inline=$(spt adapter get-string claude-spt skills.version 2>&1)
[ "$inline" = "Report the spt-core-tracked adapter/binary version-of-truth." ] && ok "inline string prints as-is: skills.version" || bad "skills.version inline='$inline'"

# 5. Soft-deregister cleanly.

inline=$(spt adapter get-string claude-spt skills.version 2>&1)
[ "$inline" = "Report the spt-core-tracked adapter/binary version-of-truth." ] && ok "inline string prints as-is: skills.version" || bad "skills.version inline='$inline'"

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

# 5. Soft-deregister cleanly.


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)

inline=$(spt adapter get-string claude-spt skills.version 2>&1)
[ "$inline" = "Report the spt-core-tracked adapter/binary version-of-truth." ] && ok "inline string prints as-is: skills.version" || bad "skills.version inline='$inline'"

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

# 5. Soft-deregister cleanly.

#!/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

# 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.whoami 2>&1)
case "$body" in "# /sptc:whoami"*) ok "file-backed string resolves: skills.whoami -> body" ;; *) bad "skills.whoami not resolved to file body: '$(printf %.40s "$body")'" ;; esac
inline=$(spt adapter get-string claude-spt skills.version 2>&1)
[ "$inline" = "Report the spt-core-tracked adapter/binary version-of-truth." ] && ok "inline string prints as-is: skills.version" || bad "skills.version inline='$inline'"

# 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; }

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

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

+    '<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"


on 1: you already proved this in a previous session. iirc the results were recorded in the docs here somewhere. but tl;dr yes, CC UserPromptSubmit DOES fire on slash commands.


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code; SPTC_ACCEPTANCE=1 sh ci/manifest/registration-int.sh 2>&1

Run registration int with skill-injection assertion


ok   spt adapter add: registered
ok   listed: claude-spt active
ok   shipped profile resolves: claude-spt:deep
ok   strings resolve: adapter_label (base)
ok   overlay observable: :deep adapter_label differs
ok   file-backed string resolves: skills.whoami -> body
ok   inline string prints as-is: skills.version
ok   UPS skill-injection: /sptc:whoami -> wrapped body
ok   removed (soft-deregistered)
MANIFEST-REGISTRATION-INT OK


9/9 green including skill-injection end-to-end. Now refresh the stale notes + the two now-operative stubs. whoami/setup are operative (injection wired + proven); their "not yet operative / held pending" clauses are stale.


1	---
2	name: whoami
3	description: |
4	  Report this session's spt endpoint id — "which agent am I?".
5	allowed-tools: [Bash]
6	---
7
8	# /sptc:whoami
9
10	> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
11	> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
12	> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
13	>
14	> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
15	> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.
16
17	Resolves the current session to its spt endpoint id (`spt whoami`) so the agent can address
18	replies and discover its own perch identity.
19

---
name: whoami
description: |
  Report this session's spt endpoint id — "which agent am I?".
allowed-tools: [Bash]
---

# /sptc:whoami

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Resolves the current session to its spt endpoint id (`spt whoami`) so the agent can address
replies and discover its own perch identity.


Edit

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Resolves the current session to its spt endpoint id (`spt whoami`) so the agent can address
replies and discover its own perch identity.

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings.skills]`;
> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
>
> **Operative.** The UPS hook detects `/sptc:whoami` and injects the body from
> `adapter/strings/skills/whoami.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.

Resolves the current session to its spt endpoint id (`spt whoami`) so the agent can address
replies and discover its own perch identity.


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

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings.skills]`;
> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
>
> **Operative.** The UPS hook detects `/sptc:whoami` and injects the body from
> `adapter/strings/skills/whoami.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.

Resolves the current session to its spt endpoint id (`spt whoami`) so the agent can address
replies and discover its own perch identity.

---
name: whoami
description: |
  Report this session's spt endpoint id — "which agent am I?".
allowed-tools: [Bash]
---

# /sptc:whoami

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Resolves the current session to its spt endpoint id (`spt whoami`) so the agent can address
replies and discover its own perch identity.

-> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;

+> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings.skills]`;

-> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection

-> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

+> **Operative.** The UPS hook detects `/sptc:whoami` and injects the body from

+> `adapter/strings/skills/whoami.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.


1	---
2	name: setup
3	description: |
4	  Install or repair spt-core for this Claude Code session (mid-session installer).
5	allowed-tools: [Bash]
6	---
7
8	# /sptc:setup
9
10	> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
11	> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
12	> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
13	>
14	> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
15	> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.
16
17	Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not
18	had a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to
19	fetch + verify spt-core on demand.
20

---
name: setup
description: |
  Install or repair spt-core for this Claude Code session (mid-session installer).
allowed-tools: [Bash]
---

# /sptc:setup

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not
had a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to
fetch + verify spt-core on demand.


setup is special: it runs precisely when spt may be **absent** — so `sptc_inject_skill` (needs `spt get-string`) no-ops in the install case. The SKILL.md stub itself must self-carry the bootstrap. Make setup's stub self-contained (stub is the floor for this one skill).


> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not
had a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to
fetch + verify spt-core on demand.

> **Self-contained by necessity.** Unlike other `/sptc:*` skills, `setup` cannot rely on
> UPS-injection of its body: it runs precisely when spt-core may be **absent**, so
> `spt adapter get-string` (the injection source) is unavailable. The operative steps therefore live
> in this SKILL.md (the floor); the file-backed `[strings.skills].setup` body mirrors them for the
> spt-present repair path. See `docs/adr/0001-distribution-splits-by-volatility.md`.

Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not had
a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to fetch +
verify spt-core on demand.

**Do this:**

1. If spt-core is already present (`command -v spt && spt --version`), report the version and stop.
2. Otherwise run the published install-on-demand bootstrap (spt-releases
   `harness-contract/install-on-demand.md`):
   - **POSIX:** `curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh`
   - **Windows (PowerShell):** `irm https://sabermage.github.io/spt-releases/install.ps1 | iex`
3. `PATH` is not reloaded in this shell after a fresh install — verify with the absolute path:
   `"$HOME/.local/bin/spt" --version`. After this, `spt update` handles signed self-updates.


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

> **Self-contained by necessity.** Unlike other `/sptc:*` skills, `setup` cannot rely on
> UPS-injection of its body: it runs precisely when spt-core may be **absent**, so
> `spt adapter get-string` (the injection source) is unavailable. The operative steps therefore live
> in this SKILL.md (the floor); the file-backed `[strings.skills].setup` body mirrors them for the
> spt-present repair path. See `docs/adr/0001-distribution-splits-by-volatility.md`.

Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not had
a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to fetch +
verify spt-core on demand.

**Do this:**

1. If spt-core is already present (`command -v spt && spt --version`), report the version and stop.
2. Otherwise run the published install-on-demand bootstrap (spt-releases
   `harness-contract/install-on-demand.md`):
   - **POSIX:** `curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh`
   - **Windows (PowerShell):** `irm https://sabermage.github.io/spt-releases/install.ps1 | iex`
3. `PATH` is not reloaded in this shell after a fresh install — verify with the absolute path:
   `"$HOME/.local/bin/spt" --version`. After this, `spt update` handles signed self-updates.

---
name: setup
description: |
  Install or repair spt-core for this Claude Code session (mid-session installer).
allowed-tools: [Bash]
---

# /sptc:setup

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not
had a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to
fetch + verify spt-core on demand.

-> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the

-> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.

+> **Self-contained by necessity.** Unlike other `/sptc:*` skills, `setup` cannot rely on

+> UPS-injection of its body: it runs precisely when spt-core may be **absent**, so

+> `spt adapter get-string` (the injection source) is unavailable. The operative steps therefore live

+> in this SKILL.md (the floor); the file-backed `[strings.skills].setup` body mirrors them for the

+> spt-present repair path. See `docs/adr/0001-distribution-splits-by-volatility.md`.

-Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not

-had a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to

+Covers the mid-session install gap (ADR-0001): a user who installs the plugin mid-session has not had

+a SessionStart bootstrap fire, so `/sptc:setup` runs the same invisible-installer bootstrap to fetch +

+1. If spt-core is already present (`command -v spt && spt --version`), report the version and stop.

+2. Otherwise run the published install-on-demand bootstrap (spt-releases

+   `harness-contract/install-on-demand.md`):

+   - **POSIX:** `curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh`

+   - **Windows (PowerShell):** `irm https://sabermage.github.io/spt-releases/install.ps1 | iex`

+3. `PATH` is not reloaded in this shell after a fresh install — verify with the absolute path:

+   `"$HOME/.local/bin/spt" --version`. After this, `spt update` handles signed self-updates.


Fix ADR-0001's stale "UPS-fires is an assumption" open-item (now confirmed, ADR-0002).


76
77	- **UPS-fires-on-slash-command is an empirical assumption.** The design assumes
78	  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
79	  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
80	  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)
81	- **File-backed `[strings]` is an M12 spt-core dependency** — until M12 publishes, instruction
82	  bodies cannot be externalized; skeleton SKILL.md files may carry interim inline instructions.

- **UPS-fires-on-slash-command is an empirical assumption.** The design assumes
  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)
- **File-backed `[strings]` is an M12 spt-core dependency** — until M12 publishes, instruction
  bodies cannot be externalized; skeleton SKILL.md files may carry interim inline instructions.


- **UPS-fires-on-slash-command is an empirical assumption.** The design assumes
  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)

- **UPS-fires-on-slash-command — CONFIRMED (2026-06-15, ADR-0002 validation).** `UserPromptSubmit`
  fires on a `/sptc:X` slash-command with the token intact (`prompt:"/sptc:send doyle"` literal, hook
  fired, skill ran, on CC 2.1.177). The injection branch is built (`user-prompt-submit.sh`
  `sptc_skill_key` + `sptc_inject_skill`); the in-SKILL.md fallback is unneeded for injection (but
  `setup` stays self-contained — it runs when spt may be absent). (`REQ-UPS-INJECTION`.)


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

- **UPS-fires-on-slash-command — CONFIRMED (2026-06-15, ADR-0002 validation).** `UserPromptSubmit`
  fires on a `/sptc:X` slash-command with the token intact (`prompt:"/sptc:send doyle"` literal, hook
  fired, skill ran, on CC 2.1.177). The injection branch is built (`user-prompt-submit.sh`
  `sptc_skill_key` + `sptc_inject_skill`); the in-SKILL.md fallback is unneeded for injection (but
  `setup` stays self-contained — it runs when spt may be absent). (`REQ-UPS-INJECTION`.)

<!-- [doc->REQ-DIST-PLUGIN-SKELETON] -->
<!-- [doc->REQ-DIST-HOOKS-API] -->
<!-- [doc->REQ-DIST-BOOTSTRAP-INSTALL] -->
<!-- [doc->REQ-UPS-INJECTION] -->
# Distribution splits by volatility: a thin skeleton plugin, an spt-conducted manifest + binary

## Status

accepted (2026-06-14)

## Context

`spt-claude-code` is the `claude-spt` adapter — simultaneously spt-core's v1 acceptance proof,
its first casual-end-user entrypoint (a Claude Code plugin on `SaberMage/cplugs`), and an
invisible spt-core installer. It must ship as a CC plugin, yet almost all real logic now lives
inside the `spt` binary and the CC adapter manifest, both **conducted by spt-core**, not by the
plugin author.

Forces:

1. **Two very different churn rates.** Skill *instructions*, the `[digest]` extractor, profiles,
   strings, and hints change often. The plugin's *structure* (which skills exist, hook wiring,
   bootstrap) changes rarely. Coupling them to one release cadence means either over-bumping the
   marketplace or letting logic rot.
2. **Claude Code has no plugin-file integrity check.** In-place edits to an installed plugin do
   not orphan it; only *old version directories* orphan on a version bump (GC'd after 7 days).
   So low-churn structure is safe to ship via the marketplace, and high-churn logic does *not*
   need to ride the marketplace at all.
3. **The skill `/spt:` namespace must be preserved** (legacy parity). User-scope skills under
   `~/.claude/skills` cannot namespace (they surface as bare `/live`, collision-prone), so skills
   must stay **plugin-provided**.
4. **Users install mid-session**, so the plugin cannot assume a SessionStart fire has installed
   spt-core; the bootstrap and a `/spt:setup` skill must cover the gap.

## Decision

Split the adapter across **three layers by volatility** (see `SCOPE.md` §"Distribution
architecture"):

| Layer | Contents | Home | Churn |
|---|---|---|---|
| **Skeleton plugin** | namespaced `/spt:*` skill *skeletons*, `hooks.json`, SessionStart bootstrap, `plugin.json` | `SaberMage/cplugs` | low |
| **spt binary** | all logic | spt-core domain (signed, peer-propagated) | high |
| **CC adapter manifest** | `[digest]` extractor, profiles, strings, hints | spt-core adapter registry (**not** plugin files) | medium |

Concretely:

- **The marketplace plugin is a true thin skeleton.** It carries skill *skeletons* (frontmatter
  + a stub body), `hooks.json`, a SessionStart bootstrap, and `plugin.json` — **no binary, no
  manifest, no embedded logic.** [`REQ-DIST-PLUGIN-SKELETON`]
- **`hooks.json` delegates to `spt api`** rather than carrying adapter behavior. The hook commands
  invoke the spt binary's published `api` surface; the plugin ships no binary of its own (contrast
  legacy, which shipped `owl.exe` and called it directly). [`REQ-DIST-HOOKS-API`]
- **A SessionStart bootstrap installs spt-core when absent** — the invisible-installer pattern.
  A user who installs the plugin gets spt-core for free. [`REQ-DIST-BOOTSTRAP-INSTALL`]
- **Skill instructions are delivered by UPS-injection.** A `UserPromptSubmit` hook detects `/spt:X`
  in the prompt and injects X's real instructions as `additionalContext`, sourced from the adapter
  `[strings]` (file-backed, so the manifest doesn't bloat). SKILL.md files stay skeletons; the
  volatile instruction text lives in spt-core-conducted strings. [`REQ-UPS-INJECTION`]
- **The "bulk" install/update is manifest + binary**, conducted by spt-core's own framework (the
  file-pull channel), not a marketplace bump. The marketplace plugin rarely needs a version bump
  and never orphans an active install.

### Rejected alternatives

- **Global user-scope skills** (`~/.claude/skills`) — lose the `/spt:` namespace (bare `/live`,
  collision-prone). Rejected.
- **fetch-stub skills** (SKILL.md fetches its real body at runtime) — VETOED by the operator:
  adds terminal noise, latency, and an extra tool call per invocation. UPS-injection chosen
  instead. (`SCOPE.md` §"Skill-instruction delivery".)
- **A fat plugin shipping the binary** (legacy `owl.exe` model) — couples high-churn logic to the
  marketplace cadence and forces the binary/handoff/cache-prune machinery the thin skeleton has no
  need for. Rejected; logic rides the spt binary + adapter registry.

### Open / to-confirm

- **UPS-fires-on-slash-command is an empirical assumption.** The design assumes
  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and
  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md
  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)
- **File-backed `[strings]` is an M12 spt-core dependency** — until M12 publishes, instruction
  bodies cannot be externalized; skeleton SKILL.md files may carry interim inline instructions.
  **Update 2026-06-15 — SATISFIED.** M12 (`spt 0.7.0`) ships file-backed `[strings]` as a
  value-position table pointer: `key = { file = "rel" }` over the per-adapter aux dir
  `adapters/<adapter>/strings/`, resolved lazily at `get-string` (containment-checked; copied on
  `adapter add`). Confirmed by live byte-test + spt-core ruling (`REQ-MANIFEST-5`, M12-W3). The
  dependency is met — skill bodies are externalized via this pointer (no manifest bloat); the
  interim-inline fallback is retired. The pointer syntax was merely **undocumented** on the published
  surface (now finding **F-003**, docs-only, doyle publishing). See `docs/SPT-CORE-FINDINGS.md`.

## Consequences

<!-- [doc->REQ-DIST-MANIFEST-SCHEMA] -->
- The marketplace plugin can sit static across many manifest/binary updates → the
  version-of-truth is the spt-core-tracked manifest/binary version (`spt adapter list` /
  `/spt:version`), **not** the marketplace `plugin.json` version. The CC adapter manifest
  (`adapter/claude-spt.toml`) is authored against spt-core's **published** `manifest.schema.json`
  (vendored at `adapter/manifest.schema.json`) and CI-gated against it (`ci/manifest/`). These are distinct numbers on
  independent schedules (see `docs/RELEASE-RUNBOOK.md`).
- New obligation: a dual update path kept in sync — file-pull (real) + a cautionary
  `claude plugin update` (skeleton sync only).
<!-- [doc->REQ-DIST-SHORTCUT-BASENAME] -->
- The manifest brands the `spt endpoint run` launcher shortcut **`cc-<id>`** via
  `shortcut_basename = "cc"` (the M12 `cc` launcher; the harness-agnostic default would be
  `spt-<id>`, and it matches the schema's own `spt-claude-code → cc` example). This launcher brand
  is **decoupled** from the `sptc` plugin name — the `s/sptc/spt/` succession (next bullet) renames
  the plugin, not this shortcut. (Operator-ruled 2026-06-15.)
- Forces the cplugs **plugin-name** question, now **ruled (operator, 2026-06-14)**: because CC
  invokes skills as `/<plugin>:<skill>` (the skill prefix is hard-tied to the `plugin.json` name —
  no override; confirmed against the official plugin docs), the marketplace plugin is named
  **`sptc`** and skills surface as **`/sptc:*`** for now. **Succession plan:** once `spt-claude-code`
  proves parity, flip `plugin.json` name `sptc`→`spt` (skills become `/spt:*`) and retire/rename
  legacy owl's cplugs `spt` plugin in the same coordinated move (two plugins cannot share the
  `spt` name). `sptc` is the single-substitution seam — the flip is `s/sptc/spt/`.
- Mints no new REQs beyond those already seeded for the skeleton milestone; this ADR is the
  `doc`-stage evidence for `REQ-DIST-PLUGIN-SKELETON`, `REQ-DIST-HOOKS-API`,
  `REQ-DIST-BOOTSTRAP-INSTALL`, and `REQ-UPS-INJECTION`.

-- **UPS-fires-on-slash-command is an empirical assumption.** The design assumes

-  `UserPromptSubmit` fires when the prompt is a `/spt:X` slash-command. This is undocumented and

-  must be confirmed at build time; if it does **not** fire, the fallback is in-SKILL.md

-  instructions. (Tracked under `REQ-UPS-INJECTION`; see `SKELETON-PLAN.md`.)

+- **UPS-fires-on-slash-command — CONFIRMED (2026-06-15, ADR-0002 validation).** `UserPromptSubmit`

+  fires on a `/sptc:X` slash-command with the token intact (`prompt:"/sptc:send doyle"` literal, hook

+  fired, skill ran, on CC 2.1.177). The injection branch is built (`user-prompt-submit.sh`

+  `sptc_skill_key` + `sptc_inject_skill`); the in-SKILL.md fallback is unneeded for injection (but

+  `setup` stays self-contained — it runs when spt may be absent). (`REQ-UPS-INJECTION`.)


Now update the plan slice — injection branch built, UPS-fires confirmed (not open).


     - **NOT this slice (separate, REQ-UPS-INJECTION):** the UPS hook only *drains the inbox* today
       (`user-prompt-submit.sh`: `api poll` → `render_frames`); the **skill-body injection branch**
       (detect `/sptc:X` → inject `get-string skills.<x>`) is **unbuilt**, and its gate — *does CC's
       UserPromptSubmit even fire on a `/slash` prompt?* — is still ADR-0001's open empirical question
       (throwaway live-CC byte-test). That, not the manifest source, is the real blocker for operative
       skills. `REQ-UPS-INJECTION` int stays held on it. The whoami/setup SKILL.md stubs still say
       "held pending … M12 file-backed `[strings]`" — that clause is now stale (capability shipped);
       refresh when the injection branch lands.

   - **DONE (this session) — UPS skill-injection branch BUILT.** UPS-fires-on-slash was already
     proven (ADR-0002 validation 2026-06-15: `/sptc:X` fires with token intact on CC 2.1.177) — my
     earlier "still-open" framing was stale. Built: `_common.sh` `sptc_skill_key` (pure, detect
     `/sptc:<skill>` leading-only) + `sptc_inject_skill` (resolve body via `get-string skills.<x>`,
     wrap `<sptc_skill name>`); `user-prompt-submit.sh` now injects BEFORE the perch check (whoami/
     setup need no perch) then drains. Tests: hooks-parse unit (skill_key, 8 cases) + registration-int
     4c (end-to-end `/sptc:whoami` → wrapped body, 9/9 vs live v0.7.0). `setup` SKILL.md is
     self-contained (runs when spt may be absent → injection no-ops; stub is the floor). whoami/setup
     stubs refreshed (stale "not yet operative" removed). `REQ-UPS-INJECTION` `[doc,impl,unit]` stays
     covered; `int` still held — but ONLY on the message-DRAIN half (`api poll` → additionalContext
     round-trip, gated on REQ-MSG-ENVELOPE publish). The skill-injection int is satisfied (4c).


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

   - **DONE (this session) — UPS skill-injection branch BUILT.** UPS-fires-on-slash was already
     proven (ADR-0002 validation 2026-06-15: `/sptc:X` fires with token intact on CC 2.1.177) — my
     earlier "still-open" framing was stale. Built: `_common.sh` `sptc_skill_key` (pure, detect
     `/sptc:<skill>` leading-only) + `sptc_inject_skill` (resolve body via `get-string skills.<x>`,
     wrap `<sptc_skill name>`); `user-prompt-submit.sh` now injects BEFORE the perch check (whoami/
     setup need no perch) then drains. Tests: hooks-parse unit (skill_key, 8 cases) + registration-int
     4c (end-to-end `/sptc:whoami` → wrapped body, 9/9 vs live v0.7.0). `setup` SKILL.md is
     self-contained (runs when spt may be absent → injection no-ops; stub is the floor). whoami/setup
     stubs refreshed (stale "not yet operative" removed). `REQ-UPS-INJECTION` `[doc,impl,unit]` stays
     covered; `int` still held — but ONLY on the message-DRAIN half (`api poll` → additionalContext
     round-trip, gated on REQ-MSG-ENVELOPE publish). The skill-injection int is satisfied (4c).

# Adapter-manifest + M12 parity — JIT plan (next milestone)

> Fresh milestone after the CI/distribution slices. Authors the CC adapter **manifest** (publish
> target #2, the spt-core registry side) against the REAL v0.7.0 published surface, then the M12
> parity surfaces. NEEDS public-docs research first — run in clean context.

## Public-surface-only (hard constraint — re-read)

Build ONLY from `SaberMage/spt-releases` (v0.7.0) + `https://sabermage.github.io/spt-releases`
(harness-contract, CLI reference, manifest.schema.json, rustdoc, llms-full.txt). NEVER read
spt-core source. A missing capability = a FINDING for doyle/todlando, not a workaround. (AGENTS.md)

## Research first (before authoring)

1. Pull v0.7.0 `manifest.schema.json` from spt-releases — the authoritative shape the adapter
   manifest must validate against. Note required fields, `[adapter]` block, `[strings]` (file-backed
   large bodies), `shortcut_basename`, profiles/hints/`[digest]` extractor.
2. Read the published harness-contract + CLI reference for: `spt adapter` registration verbs,
   `spt endpoint run`, `shortcut_basename` semantics (emits `sptc-<id>` shortcuts), whoami-as-alias,
   adapter:profile fallback addressing.
3. Confirm where the manifest physically lives (spt-core-conducted registry, NOT plugin files —
   ADR-0001/SCOPE) and how it's validated locally.

## Slices (sequence; each its own commit + green gate)

1. **DONE (bd16644)** — **Adapter manifest authoring**. `adapter/claude-spt.toml` ([adapter]+
   shortcut_basename, [hooks.*], [identity], [inject], [digest], [profiles.deep], [strings],
   [[hints]]); vendored `adapter/manifest.schema.json`; manifest-schema gate
   (`ci/manifest/{validate_manifest.py,check-manifest.sh}`, py tomllib|tomli + jsonschema) wired
   into `run-gates.sh`. **REQ-DIST-MANIFEST-SCHEMA** → `[doc,impl,unit]` (int held: real
   `spt adapter add` registration-validation = 2nd layer). Deferred in-manifest with notes:
   `[session]`/roles, `[history]`, `[update]` (file_pull needs a signing key + registry repo not
   on the public surface — a finding).
2. **DONE (c93757d)** — **shortcut_basename** guard. `tests/manifest-shortcut.sh` asserts the
   `sptc` brand (succession seam). **REQ-DIST-SHORTCUT-BASENAME** → `[doc,unit]` (the field is
   declarative config, not impl — the scanner classifies impl by file type; launcher impl +
   real-picker int deferred to the endpoint-run/cc-launcher slice, gated on the v0.7.0 binary).
3. **M12 parity surfaces** (seed REQs per surface as each starts — activate-don't-pre-fail):
   - **DONE (8cd8372)** — `/sptc:whoami` + `/sptc:setup` skill skeletons (the manifest advertised
     both; skeletons now exist, covered by REQ-DIST-PLUGIN-SKELETON + skeleton-validate).
   - **DONE (435e1be)** — **manifest proven against live v0.7.0**. Local spt upgraded to 0.7.0;
     `ci/manifest/registration-int.sh` (SPTC_ACCEPTANCE-gated) green 6/6: `spt adapter add`
     accepted (2nd-layer cross-field validation), shipped `:deep` profile resolves, `[strings]`
     read + overlay observable via `get-string`, clean soft-remove. **REQ-DIST-MANIFEST-SCHEMA →
     [doc,impl,unit,int]**. Cross-field discovery fixed: `[digest]` needs `source` →
     `source="{home}/.claude/projects"`. Findings logged (key catalog + digest rule undocumented).
   - **IN PROGRESS — capability confirmed (F-003 resolved 2026-06-15)** — file-backed `[strings]`.
     Research conclusion REVERSED by live byte-test + doyle/todlando ruling: the mechanism **is
     shipped** in v0.7.0 as a value-position table pointer `key = { file = "rel" }` over
     `adapters/<adapter>/strings/` (lazy-resolved at `get-string`, containment-checked, copied on
     `adapter add`). My avenue search missed it (it's a value-table shape, not a CLI verb / `@file`).
     ADR-0001 dependency **SATISFIED**; residual is docs-only (doyle publishing, PR #13).
     - **DONE (this session)** — pattern proven end-to-end + first two bodies authored:
       `adapter/strings/skills/{whoami,setup}.md` + `[strings.skills].{whoami,setup} = { file = ... }`;
       `registration-int.sh` extended (8/8) — asserts a `{ file }` pointer resolves to FILE CONTENTS
       and an inline sibling prints as-is, vs live v0.7.0. `strings/` resolves next to the manifest
       FILE on `adapter add`. Bodies grounded in the public surface (`spt whoami`; shipped bootstrap).
     - **TODO (per-skill increments)** — convert the remaining inline summaries (commune, live, ready,
       send, signoff, version) to file-backed bodies as each is authored. ready/send should **delegate
       to `spt how-to ready|send`** (published agent guidance) rather than duplicate.
     - **NOT this slice (separate, REQ-UPS-INJECTION):** the UPS hook only *drains the inbox* today
       (`user-prompt-submit.sh`: `api poll` → `render_frames`); the **skill-body injection branch**
       (detect `/sptc:X` → inject `get-string skills.<x>`) is **unbuilt**, and its gate — *does CC's
       UserPromptSubmit even fire on a `/slash` prompt?* — is still ADR-0001's open empirical question
       (throwaway live-CC byte-test). That, not the manifest source, is the real blocker for operative
       skills. `REQ-UPS-INJECTION` int stays held on it. The whoami/setup SKILL.md stubs still say
       "held pending … M12 file-backed `[strings]`" — that clause is now stale (capability shipped);
       refresh when the injection branch lands.
   - **TODO** `[session.self]` + `spt endpoint run` bringup + `cc`/`sptc` launcher → unblocks the
     held **REQ-DIST-SHORTCUT-BASENAME** impl+int (the `<basename>-<id>` picker "lands in a later
     spt-core wave" per `endpoint run --help`; `endpoint run` spawns `[session.self]`, still deferred).
   - **TODO** digest extractor binary `claude-spt-digest` → unblocks `spt adapter digest-proof`
     (the deeper [digest] int; the seam is declared + registration-accepted, extractor unbuilt).

### Still held on spt-core (non-blocking)
- `sptc-<id>` shortcut EMISSION — spt-core picker wave (not in 0.7.0) + needs `[session.self]`.
- REQ-MSG-ENVELOPE poll→additionalContext bus-delivery int — ADR-0020 MERGED to spt-core main
  (`25ffd7a`) but NOT yet PUBLISHED; v0.7.0 still carries the `__REPLY_TO__` relic. Byte-capture tests
  the SHIPPED binary, so the int flip waits for the next RELEASE carrying ADR-0020 (doyle pings with
  the hash on publish), NOT the merge. Parse v0.7.0 as-is until then; then confirm-match + flip
  HOOKS-API/UPS-INJECTION int.

### Note: claude-spt is now a REGISTERED adapter artifact on this node (the v1 acceptance proof).
The int test soft-removes it each run; re-add with `spt adapter add adapter/claude-spt.toml`.
Local `spt` is **0.7.0** (operator-upgraded 2026-06-15).

## Still gated on spt-core (non-blocking)

poll→additionalContext bus-delivery `int` — REQ-MSG-ENVELOPE. ADR-0020 MERGED to spt-core main
(`25ffd7a`) but NOT PUBLISHED; v0.7.0 still emits the `__REPLY_TO__` relic. Byte-capture tests the
SHIPPED binary → int flip waits for the next RELEASE carrying ADR-0020 (doyle pings with the hash on
publish), not the merge. Then confirm-match + flip HOOKS-API/UPS-INJECTION `int`.

## Gate (every slice)

`sh ci/run-gates.sh` ⇒ PASS · `traceable-reqs check` green. Atomic commits, `Co-authored by: perri`.

## Session state at handoff (2026-06-15)

Shipped this session (all green, gates PASS, 16/16 traceable): CI ACCEPTANCE slice 1
(73677fd/d834611 — real-claude SUT + perch-collision guard, REQ-CI-ACCEPTANCE [doc,impl,int] +
REQ-HAZARD-PERCH-COLLISION); cplugs publish-prep (e28db92 — validate/package skeleton + untracked
a real perch-state leak, REQ-DIST-PLUGIN-SKELETON [doc,impl,unit]); mdBook docs-site
(d999c60/905c62c — drift-gated book + llms.txt, REQ-DOCS-SITE/REQ-DOCS-DRIFT). Collaborators:
doyle (design/gate) + todlando (builds spt-core) over the spt bus. v0.7.0/M12 is PUBLIC.

-     - **NOT this slice (separate, REQ-UPS-INJECTION):** the UPS hook only *drains the inbox* today

-       (`user-prompt-submit.sh`: `api poll` → `render_frames`); the **skill-body injection branch**

-       (detect `/sptc:X` → inject `get-string skills.<x>`) is **unbuilt**, and its gate — *does CC's

-       UserPromptSubmit even fire on a `/slash` prompt?* — is still ADR-0001's open empirical question

-       (throwaway live-CC byte-test). That, not the manifest source, is the real blocker for operative

-       skills. `REQ-UPS-INJECTION` int stays held on it. The whoami/setup SKILL.md stubs still say

-       "held pending … M12 file-backed `[strings]`" — that clause is now stale (capability shipped);

-       refresh when the injection branch lands.

+   - **DONE (this session) — UPS skill-injection branch BUILT.** UPS-fires-on-slash was already

+     proven (ADR-0002 validation 2026-06-15: `/sptc:X` fires with token intact on CC 2.1.177) — my

+     earlier "still-open" framing was stale. Built: `_common.sh` `sptc_skill_key` (pure, detect

+     `/sptc:<skill>` leading-only) + `sptc_inject_skill` (resolve body via `get-string skills.<x>`,

+     wrap `<sptc_skill name>`); `user-prompt-submit.sh` now injects BEFORE the perch check (whoami/

+     setup need no perch) then drains. Tests: hooks-parse unit (skill_key, 8 cases) + registration-int

+     4c (end-to-end `/sptc:whoami` → wrapped body, 9/9 vs live v0.7.0). `setup` SKILL.md is

+     self-contained (runs when spt may be absent → injection no-ops; stub is the floor). whoami/setup

+     stubs refreshed (stale "not yet operative" removed). `REQ-UPS-INJECTION` `[doc,impl,unit]` stays

+     covered; `int` still held — but ONLY on the message-DRAIN half (`api poll` → additionalContext

+     round-trip, gated on REQ-MSG-ENVELOPE publish). The skill-injection int is satisfied (4c).


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code; sh ci/run-gates.sh 2>&1 | tail -20; echo "=== traceable ==="; traceable-reqs check 2>&1 | tail -3

Full gates + traceable after injection branch build


ok  tests/manifest-schema.sh
ok  tests/manifest-shortcut.sh
ok  tests/msys-hazard.sh
ok  tests/skeleton-validate.sh

=== GATE: traceable-reqs check (requirement coverage) ===
ok  coverage green

=== GATE: skeleton-validate (cplugs installability) ===
ok  skeleton installable

=== GATE: manifest-schema ===
ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json

=== GATE: docs-drift ===
ok   mdbook build
ok   llms.txt in sync

=== RESULT: PASS ===
=== traceable ===
       /spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)
  [OK] REQ-UPS-INJECTION  required: [doc, impl, unit]  stages: +doc +impl +unit -int
       UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands


All gates PASS, traceable green, int 9/9. Commit the injection branch.


cd /c/Users/decid/Documents/projects/spt-claude-code
git add plugin/sptc/hooks/_common.sh plugin/sptc/hooks/user-prompt-submit.sh tests/hooks-parse.sh ci/manifest/registration-int.sh plugin/sptc/skills/whoami/SKILL.md plugin/sptc/skills/setup/SKILL.md docs/adr/0001-distribution-splits-by-volatility.md MANIFEST-PARITY-PLAN.md
git commit -q -F - <<'EOF'
feat(plugin): UPS skill-injection branch (/sptc:X -> get-string skills.<x>)

UPS-fires-on-slash was already proven (ADR-0002 validation 2026-06-15: a
/sptc:X prompt fires UserPromptSubmit with the token intact on CC 2.1.177) —
the only remaining dep was file-backed [strings], now shipped (F-003). So the
skill-body injection branch is buildable, and built here.

_common.sh:
- sptc_skill_key  — pure: detect /sptc:<skill> in the prompt (leading-only, so
  prose mentioning /sptc:x mid-sentence does not fire). [unit->REQ-UPS-INJECTION]
- sptc_inject_skill — resolve the body from [strings.skills].<skill> via
  `spt adapter get-string`, wrap <sptc_skill name="x">; silent no-op if spt is
  absent / adapter unregistered / key unset. [impl->REQ-UPS-INJECTION]

user-prompt-submit.sh now does both jobs on the one hook: skill-injection runs
BEFORE the perch check (whoami/setup are valid without a readied perch), then
the existing message-drain (perch-gated).

setup SKILL.md is made self-contained: it runs precisely when spt-core may be
absent, so injection (which needs `spt get-string`) no-ops — the stub carries
the operative bootstrap as the floor; the file-backed body mirrors it for the
spt-present repair path. whoami SKILL.md stub refreshed (stale "not yet
operative" removed); ADR-0001 open-item -> UPS-fires CONFIRMED.

Tests: hooks-parse unit (sptc_skill_key, 8 cases) + registration-int 4c
(end-to-end /sptc:whoami -> wrapped body, 9/9 vs live v0.7.0).

REQ-UPS-INJECTION [doc,impl,unit] covered; int still held — but only on the
message-DRAIN half (api poll -> additionalContext round-trip, gated on
REQ-MSG-ENVELOPE publish). The skill-injection int is satisfied (4c).

Gate: sh ci/run-gates.sh PASS, registration-int 9/9, traceable-reqs check green.

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

Commit UPS skill-injection branch


9d5516d feat(plugin): UPS skill-injection branch (/sptc:X -> get-string skills.<x>)


9d5516d committed. Slice done end-to-end.

## UPS skill-injection branch — built + proven

You were right — UPS-fires-on-slash was already proven (ADR-0002 validation, CC 2.1.177, token intact). My "still-open" framing was stale. So I built the branch:

- **`_common.sh`**: `sptc_skill_key` (pure — detect `/sptc:<skill>`, leading-only so prose doesn't false-fire) + `sptc_inject_skill` (resolve body via `get-string skills.<x>`, wrap `<sptc_skill>`, silent no-op if spt absent).
- **`user-prompt-submit.sh`**: now injects skill body **before** the perch check (whoami/setup need no perch), then drains messages.
- **`setup` made self-contained** — it runs when spt may be *absent*, so injection no-ops; stub carries the bootstrap as floor. whoami stub refreshed; ADR-0001 open-item → UPS-fires CONFIRMED.
- **Tests**: hooks-parse unit (8 `skill_key` cases) + registration-int **9/9** (`/sptc:whoami` → wrapped body, vs live v0.7.0).

whoami + setup now **operative end-to-end**.

## State
6 commits this session, all gated. `REQ-UPS-INJECTION [doc,impl,unit]` covered; `int` held **only** on the message-drain half (`api poll`→additionalContext, gated on REQ-MSG-ENVELOPE publish — doyle pings on release).

## Remaining
- Per-skill bodies for the other 9 (commune/live/ready/send/signoff/version/force-stop/list-agents/new-alarm) — convert inline→file-backed as authored; ready/send delegate to `spt how-to`.
- Or: `claude-spt-digest` extractor (the other unblocked slice).

Pick next, or I take per-skill bodies. Perch live.


proceed


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


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


"proceed" → per-skill bodies, making all skills operative. But I must ground each in the **public spt surface** (not invent, not copy spt-core source). Several skills are live-agent/Psyche features — verify their spt commands exist before authoring. Inspect the surface.


cd /c/Users/decid/Documents/projects/spt-claude-code
echo "=== how-to ready ==="; spt how-to ready 2>&1 | head -40
echo "=== how-to send ==="; spt how-to send 2>&1 | head -40

Read spt how-to ready + send guidance


=== how-to ready ===
# how-to: ready — receive messages

Pick a short lowercase perch id for yourself (`<id>` below). `spt whoami`
shows your session's endpoint (id, state, description) if it already has one.

Preferred — your harness can host a long-running background task:

    spt ready <id>

- Blocks for the session's life: run it as a background task and read its stdout.
- `READY:<id>` on stderr is the online signal.
- The spooled backlog drains first; then each delivery prints to stdout as it
  arrives.
- Delivery shape on stdout: one envelope line per message —
  `<EVENT type="msg" from="<sender>">body</EVENT>`. The body is
  HTML-escaped with newlines as `<br>` (unescape `< > "` then
  `&` last). Oversized deliveries arrive as `<EVENT-PART seq="k/n"
  id="…">` lines — concatenate the parts in seq order, then parse.

Fallback — no long-running tasks available (the one-shot loop):

    spt ready <id> --once

- Drains the backlog, waits for one live delivery, then exits.
- Re-run it after every exit to stay reachable.

Replying: pipe the body to `spt send --reply-to <sender>` — `<sender>` is the
envelope's `from` attribute (empty = anonymous; no reply possible).

Going offline cleanly: `spt endpoint stop <id>` (the spool is preserved;
messages sent while you are away queue and drain on your next `ready`).
=== how-to send ===
# how-to: send — deliver messages

Fire-and-forget (the body is read from stdin):

    spt send <target>

- `SENT:<target>`   — delivered live to a listening agent.
- `QUEUED:<target>` — the target is not listening; the message spooled and
  drains when it next runs `spt ready`. QUEUED is success, not an error — do
  not retry.

Reply to a message you received (body from stdin; prints `REPLIED:<sender>`):

    spt send --reply-to <sender>

Send and block for the reply (body from stdin; the reply body prints to stdout):

    spt ring <target> --timeout 60

- `TIMEOUT` after the wait is exit 0 — the message still landed; the reply will
  arrive on your listener instead.

Your own sender id is auto-detected in most sessions; `--from <id>` overrides.


`spt how-to` covers ready/send fully (delegate to those). ready/send/whoami are **core** spt commands. Check `spt endpoint` for list-agents/force-stop mapping + version-of-truth; live/commune/signoff/new-alarm are likely live-layer (deferred).


cd /c/Users/decid/Documents/projects/spt-claude-code
echo "=== endpoint --help ==="; spt endpoint --help 2>&1 | head -35
echo "=== adapter list (version-of-truth) ==="; spt adapter list 2>&1 | head -8

endpoint subcommands + adapter list for version


=== endpoint --help ===
Endpoint operations: list, lifecycle, fork, digest, access.

The noun home for per-endpoint verbs (M8 decision 1). Bare `spt endpoint` renders the merged listing
— every member subnet's endpoints grouped by subnet, this session's own endpoint pinned distinctly
at the top.

Usage: spt.exe endpoint [COMMAND]

Commands:
  list         Merged endpoint listing (the bare `spt endpoint` view)
  run          Bring up an spt-hosted harness endpoint into a broker-held PTY
  fork         Fork an endpoint into another subnet as a NEW identity
  suspend      Rest an endpoint cold (the suspend edge)
  wake         Wake a resting endpoint in place
  shutdown     Gracefully shut down an agent's own endpoint
  stop         Soft-stop a perch (spool preserved)
  rename       Rename an endpoint's logical id across its on-disk state
  digest       Show a session's live activity buffer (session digest)
  access       Endpoint access whitelist for unsolicited off-node inbound
  description  The endpoint's service-description blurb (ex-`resources`)
  role         Show or set the endpoint's durable **role** — a broad statement of purpose stored in
               the mind (`tracked/agents/<id>/live-role.md`), which replicates with the agent and
               renders FIRST at start-transition context injection. Bare `role` prints the current
               role; `--overwrite <file>` replaces it from a file. This is the **sole writer** of
               the role — no automated path (reconcile / echo-commune / signoff) ever mutates it
  help         Print this message or the help of the given subcommand(s)

Options:
  -h, --help
          Print help (see a summary with '-h')
=== adapter list (version-of-truth) ===
claude-spt: Harness Copy deregistered (from C:/Users/decid/Documents/projects/spt-claude-code/adapter)
  claude-spt:deep (shipped)
sptc-probe-f003: Harness Copy deregistered (from C:/Users/decid/AppData/Local/Temp/sptc-probe-f003)


Mapping found: ready/send/whoami/version are **core** spt (clean to author, delegate to `spt how-to`). live/commune/signoff/new-alarm/force-stop/list-agents touch the live/Psyche layer — partial/uncertain core mapping, defer to avoid inventing. Author the 3 cleanly-grounded bodies now (ready, send, version).


Write

# /sptc:ready — operative instructions

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

**Goal:** make this session reachable for inter-agent messages (register a perch and listen).

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Read the canonical, always-current guidance: run `spt how-to ready` and follow it.
3. Operative summary (from that guidance):
   - Pick a short lowercase perch id, or reuse this session's id from `spt whoami`.
   - Preferred (harness hosts a long-running task): `spt ready <id>` — it **blocks** for the
     session's life, so run it as a **background task** and read its stdout. `READY:<id>` on stderr
     is the online signal; the spooled backlog drains first, then each delivery prints as it arrives.
   - Fallback (no long-running tasks): `spt ready <id> --once` — drains the backlog, waits for one
     delivery, exits. Re-run after each exit to stay reachable.
4. Delivery shape on stdout is the `<EVENT type="msg" from="<sender>">body</EVENT>` envelope (body
   HTML-escaped, newlines as `<br>`). To reply, pipe the body to `spt send --reply-to <sender>`.


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

# /sptc:ready — operative instructions

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

**Goal:** make this session reachable for inter-agent messages (register a perch and listen).

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Read the canonical, always-current guidance: run `spt how-to ready` and follow it.
3. Operative summary (from that guidance):
   - Pick a short lowercase perch id, or reuse this session's id from `spt whoami`.
   - Preferred (harness hosts a long-running task): `spt ready <id>` — it **blocks** for the
     session's life, so run it as a **background task** and read its stdout. `READY:<id>` on stderr
     is the online signal; the spooled backlog drains first, then each delivery prints as it arrives.
   - Fallback (no long-running tasks): `spt ready <id> --once` — drains the backlog, waits for one
     delivery, exits. Re-run after each exit to stay reachable.
4. Delivery shape on stdout is the `<EVENT type="msg" from="<sender>">body</EVENT>` envelope (body
   HTML-escaped, newlines as `<br>`). To reply, pipe the body to `spt send --reply-to <sender>`.


# /sptc:send — operative instructions

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

**Goal:** deliver a message to another spt agent.

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Read the canonical, always-current guidance: run `spt how-to send` and follow it.
3. Operative summary (from that guidance) — the message **body is read from stdin**:
   - Fire-and-forget: `printf '%s' "<body>" | spt send <target>`.
     - `SENT:<target>` = delivered live. `QUEUED:<target>` = target not listening; spooled, drains
       on its next `spt ready`. **QUEUED is success — do not retry.**
   - Reply to a received message: `... | spt send --reply-to <sender>` (`<sender>` = the envelope's
     `from` attribute; prints `REPLIED:<sender>`).
   - Send and block for a reply: `... | spt ring <target> --timeout 60` (reply body prints to
     stdout; `TIMEOUT` is exit 0 — the message still landed, the reply arrives on your listener).
4. Your own sender id is auto-detected in most sessions; `--from <id>` overrides. To receive replies
   on your own listener, run `/sptc:ready` first.


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

# /sptc:send — operative instructions

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

**Goal:** deliver a message to another spt agent.

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Read the canonical, always-current guidance: run `spt how-to send` and follow it.
3. Operative summary (from that guidance) — the message **body is read from stdin**:
   - Fire-and-forget: `printf '%s' "<body>" | spt send <target>`.
     - `SENT:<target>` = delivered live. `QUEUED:<target>` = target not listening; spooled, drains
       on its next `spt ready`. **QUEUED is success — do not retry.**
   - Reply to a received message: `... | spt send --reply-to <sender>` (`<sender>` = the envelope's
     `from` attribute; prints `REPLIED:<sender>`).
   - Send and block for a reply: `... | spt ring <target> --timeout 60` (reply body prints to
     stdout; `TIMEOUT` is exit 0 — the message still landed, the reply arrives on your listener).
4. Your own sender id is auto-detected in most sessions; `--from <id>` overrides. To receive replies
   on your own listener, run `/sptc:ready` first.


# /sptc:version — operative instructions

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

**Goal:** report the **version-of-truth** — the spt-core-tracked binary + adapter-manifest version,
NOT the marketplace skeleton's `plugin.json` version (ADR-0001: the thin skeleton sits static across
many binary/manifest updates, so its version is not authoritative).

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Report the spt-core binary version: `spt --version`.
3. Report the registered adapter (the conducted layer's version-of-truth): `spt adapter list` — find
   the `claude-spt` entry and its active profile(s). This is what actually drives the session.
4. State plainly that the cplugs plugin version (`plugin.json`) is intentionally NOT the
   version-of-truth; the binary + adapter manifest above are.


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

# /sptc:version — operative instructions

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

**Goal:** report the **version-of-truth** — the spt-core-tracked binary + adapter-manifest version,
NOT the marketplace skeleton's `plugin.json` version (ADR-0001: the thin skeleton sits static across
many binary/manifest updates, so its version is not authoritative).

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Report the spt-core binary version: `spt --version`.
3. Report the registered adapter (the conducted layer's version-of-truth): `spt adapter list` — find
   the `claude-spt` entry and its active profile(s). This is what actually drives the session.
4. State plainly that the cplugs plugin version (`plugin.json`) is intentionally NOT the
   version-of-truth; the binary + adapter manifest above are.


Wire the three into the manifest as file-backed pointers.


Edit

whoami = { file = "skills/whoami.md" }
setup = { file = "skills/setup.md" }
# --- inline summaries, pending file-backed bodies (next increments) ---
commune = "Send a communal context update to your Psyche."
live = "Run or resume a live agent session."
ready = "Enable inter-agent messaging for this session."
send = "Send a message to another spt agent."
signoff = "Graceful live-agent shutdown with a final context summary."
version = "Report the spt-core-tracked adapter/binary version-of-truth."

whoami = { file = "skills/whoami.md" }
setup = { file = "skills/setup.md" }
ready = { file = "skills/ready.md" }
send = { file = "skills/send.md" }
version = { file = "skills/version.md" }
# --- inline summaries, pending file-backed bodies. live/commune/signoff touch the live-agent +
# Psyche layer; their core-`spt`-surface mapping is not yet confirmed (likely gated on the
# endpoint-run/live wave or a finding) — author once verified, don't invent commands. ---
commune = "Send a communal context update to your Psyche."
live = "Run or resume a live agent session."
signoff = "Graceful live-agent shutdown with a final context summary."


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)

whoami = { file = "skills/whoami.md" }
setup = { file = "skills/setup.md" }
ready = { file = "skills/ready.md" }
send = { file = "skills/send.md" }
version = { file = "skills/version.md" }
# --- inline summaries, pending file-backed bodies. live/commune/signoff touch the live-agent +
# Psyche layer; their core-`spt`-surface mapping is not yet confirmed (likely gated on the
# endpoint-run/live wave or a finding) — author once verified, don't invent commands. ---
commune = "Send a communal context update to your Psyche."
live = "Run or resume a live agent session."
signoff = "Graceful live-agent shutdown with a final context summary."

# claude-spt — Claude Code adapter manifest (publish target #2: the spt-core adapter registry side).
#
# This is the spt-core-conducted layer of the distribution split (ADR-0001): the volatile
# `[digest]` extractor / profiles / strings / hints ride here, NOT in the thin cplugs skeleton.
# Registered with `spt adapter add <path>`; the version-of-truth is `spt adapter list`, not the
# marketplace `plugin.json` version.
#
# Authored against spt-core's PUBLISHED v0.7.0 surface ONLY (spt-releases + sabermage.github.io/
# spt-releases) — `manifest.schema.json` is vendored beside this file (adapter/manifest.schema.json,
# provenance: https://sabermage.github.io/spt-releases/manifest.schema.json). Validated by the
# manifest-schema CI gate (ci/manifest/). NEVER copy spt-core internals (AGENTS.md).
#
# Deferred to later parity slices (not in this slice; each declares an adapter seam we have not
# yet built, or needs a capability not on the public surface — see notes inline):
#   * [session] + [session.<role>]  — live-agent / Psyche / commune / signoff / notif spawn
#     templates. Land with the M12 parity surfaces (/sptc:live, commune, signoff).
#   * [history]                     — transcript-access seam. Lands with [digest]'s sibling fetcher.
#   * [update]                      — distribution self-update. `file_pull` needs (a) a published
#     adapter-registry repo target and (b) an Ed25519 content-signing key we do not yet hold;
#     `delegated` to `claude plugin update` needs CC to attest signature verification
#     (`self_verifies`) — unconfirmed on the public surface. Tracked as a finding
#     (docs/SPT-CORE-FINDINGS.md) rather than guessed here.
#
# [impl->REQ-DIST-MANIFEST-SCHEMA]

[adapter]
name = "claude-spt"
kind = "harness"
version = "0.1.0"
# Local spt binary is 0.6.0; the published contract we build to is v0.7.0 (manifest.schema.json,
# shortcut_basename). 0.7.0 is the floor this manifest's fields require.
min_spt_core_version = "0.7.0"
hostable_types = ["LiveAgent", "ReadyAgent", "Worker"]
# Brands the `spt endpoint run` picker shortcut as `cc-<id>` (default would be `spt-<id>`) — this is
# the M12 `cc` launcher, and matches the schema's own example (spt-claude-code → `cc`). The launcher
# brand is DECOUPLED from the plugin name (`sptc`, skills `/sptc:*`): the s/sptc/spt/ succession seam
# (ADR-0001) renames the plugin, not this shortcut. Guarded by tests/manifest-shortcut.sh
# (unit->REQ-DIST-SHORTCUT-BASENAME).
shortcut_basename = "cc"

# ── [hooks.<event>] ──────────────────────────────────────────────────────────────────────────
# CC hook event → the opaque `api …` command it fires. These mirror the hand-written plugin
# hooks.json (plugin/sptc/hooks/hooks.json) one-for-one; the plugin shells `spt api`, this table
# is the registry-side declaration (ADR-0002, F-001 resolution). `reads` = the CC hook-stdin
# fields we map; `can_inject` = whether the event can surface additionalContext back to the agent.

[hooks.SessionStart]
# Seed the endpoint. {parent_pid} + {adapter_name} are spt-filled keys (confirmed against the
# published mock-adapter manifest); session_id is a CC stdin field. NOT the blocking poll/listen
# loop — that is /sptc:ready|live, never SessionStart.
fires = "api seed --session-id {session_id} --pid {parent_pid} --adapter {adapter_name}"
reads = ["session_id", "parent_pid"]
can_inject = true

[hooks.UserPromptSubmit]
# Drain the inbox (api poll → additionalContext) and run keyword hints (api hint over the prompt).
fires = "api poll {session_id}"
reads = ["session_id", "prompt"]
can_inject = true

[hooks.Stop]
# Idle marker on turn-end. Stop cannot inject → relay/sentinel fallback (echo-gate, F-001 §Gap3).
fires = "api state idle"
reads = ["session_id"]
can_inject = false

[hooks.SessionEnd]
fires = "api session-end {session_id}"
reads = ["session_id"]
can_inject = false

[hooks.SubagentStart]
fires = "api worker-start {session_id}"
reads = ["session_id"]
can_inject = false

[hooks.SubagentStop]
fires = "api worker-stop {session_id}"
reads = ["session_id"]
can_inject = false

# ── [identity] ─────────────────────────────────────────────────────────────────────────────
[identity]
# CC hands the session_id on hook stdin once running (discovered post-spawn, not injected).
session_id_source = "post_spawn"
# Process-tree anchor when session_id is absent: the Claude Code binary.
parent_ancestor_name = "claude"

# ── [inject] ───────────────────────────────────────────────────────────────────────────────
[inject]
# v1 delivery is the hook channel only (UserPromptSubmit/SessionStart additionalContext).
# PTY injection is spt-core M3 roadmap (F-001 resolution); not claimed here.
activity = ["hook"]
idle = ["hook"]

# ── [digest] ───────────────────────────────────────────────────────────────────────────────
# Session-digest extractor seam (ADR-0019). CC's transcript is JSONL (one event per line); the
# extractor maps that native log → the published {role,text,tool,ts} digest-record contract,
# one JSON record per output line. (CC has a rich log, so the extractor seam fits — a defensible
# divergence from the deliberately log-less mock adapter, which declares no [digest].)
#
# spt-core's registration cross-field rule requires [digest] to name where it reads from (`source`
# or a [history] `locate_template`) — verified against the live v0.7.0 `spt adapter add`. CC stores
# transcripts at `{home}/.claude/projects/<cwd-slug>/<session_id>.jsonl`; the cwd-slug subdir is a
# CC-internal encoding NOT expressible as a flat template with the published key catalog, so
# `source` names the per-project ROOT dir and the extractor finds `{session_id}.jsonl` within it
# (the `source` field documents accepting "log file(s)" — a dir is valid). The extractor binary
# ships in the spt-core-conducted layer (built in a later parity slice); this is its declaration.
[digest]
extractor = "claude-spt-digest --session {session_id} --in {source}"
source = "{home}/.claude/projects"
window_turns = 6
arg_truncation = 240
sprint_collapse = true

# ── [profiles.<name>] ────────────────────────────────────────────────────────────────────────
# Shipped profile overlays (sparse leaf-replace; selected as the composite `claude-spt:<name>`).
# `deep` widens the digest window for long-context review sessions without forking the base, and
# overrides a [strings] leaf so the overlay is observable via `spt adapter get-string` (base vs
# `:deep` differ). (Node-local user profiles are created with `spt adapter create-profile` and
# live in the registry, never in this manifest.)
[profiles.deep.digest]
window_turns = 20

[profiles.deep.strings]
adapter_label = "Claude Code (spt, deep)"

# ── [strings] ──────────────────────────────────────────────────────────────────────────────
# Opaque adapter data (spt-core never executes it), dot-path-readable via `spt adapter get-string`.
# Seed only this slice — the file-backed skill-instruction BODIES (UPS-injection source) land in
# the M12 file-backed-strings parity slice. A profile may leaf-replace any key here.
[strings]
adapter_label = "Claude Code (spt)"

[strings.notice]
not_installed = "spt-core not found on PATH — run /sptc:setup to install it."

[strings.skills]
# Operative UPS-injection skill bodies. File-backed values (`{ file = "skills/<x>.md" }`) live in the
# adapter aux dir `adapter/strings/skills/` (copied to `adapters/<adapter>/strings/` on `adapter add`,
# resolved lazily at `spt adapter get-string` — proven vs live v0.7.0, F-003). File-backed so the
# manifest does not bloat (ADR-0001). Skills not yet authored keep a one-line inline summary until
# their body lands; converting an inline summary to a `{ file }` pointer is the per-skill increment.
whoami = { file = "skills/whoami.md" }
setup = { file = "skills/setup.md" }
# --- inline summaries, pending file-backed bodies (next increments) ---
commune = "Send a communal context update to your Psyche."
live = "Run or resume a live agent session."
ready = "Enable inter-agent messaging for this session."
send = "Send a message to another spt agent."
signoff = "Graceful live-agent shutdown with a final context summary."
version = "Report the spt-core-tracked adapter/binary version-of-truth."

# ── [[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: /sptc:whoami reports 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."

-# --- inline summaries, pending file-backed bodies (next increments) ---

+version = { file = "skills/version.md" }

+# --- inline summaries, pending file-backed bodies. live/commune/signoff touch the live-agent +

+# Psyche layer; their core-`spt`-surface mapping is not yet confirmed (likely gated on the

+# endpoint-run/live wave or a finding) — author once verified, don't invent commands. ---

-ready = "Enable inter-agent messaging for this session."

-send = "Send a message to another spt agent."

-version = "Report the spt-core-tracked adapter/binary version-of-truth."


Verify schema + live resolve of the three new bodies.


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code
sh ci/manifest/check-manifest.sh 2>&1 | tail -2
echo "=== live resolve ==="
spt adapter add adapter/claude-spt.toml >/dev/null 2>&1
for k in ready send version; do
  out=$(spt adapter get-string claude-spt "skills.$k" 2>&1 | head -1)
  echo "skills.$k -> $out"
done
spt adapter remove claude-spt >/dev/null 2>&1; echo "removed"

Schema gate + live resolve of ready/send/version bodies


ok   C:/Users/decid/Documents/projects/spt-claude-code/adapter/claude-spt.toml
ok   manifest validates against published manifest.schema.json
=== live resolve ===
skills.ready -> # /sptc:ready — operative instructions
skills.send -> # /sptc:send — operative instructions
skills.version -> # /sptc:version — operative instructions
removed


All three resolve. Refresh the now-operative ready/send/version stubs (drop stale "not yet operative" clause). Read them.


cd /c/Users/decid/Documents/projects/spt-claude-code; for s in ready send version; do echo "===== $s ====="; cat "plugin/sptc/skills/$s/SKILL.md"; done

Read ready/send/version stubs


===== ready =====
---
name: ready
description: |
  Enable inter-agent messaging for the current session.
argument-hint: "[<id>]"
allowed-tools: [Bash]
---

# /sptc:ready

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Registers a perch so this session can receive inter-agent messages.
===== send =====
---
name: send
description: |
  Send a message to another SPT agent.
argument-hint: "<target> [--reply-to <sender>]"
allowed-tools: [Bash]
---

# /sptc:send

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Delivers a message to another agent; supports reply-to and ring/ask.
===== version =====
---
name: version
description: |
  Report the spt-core-tracked adapter manifest/binary version.
allowed-tools: [Bash]
---

# /sptc:version

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.


Same stale clause in all three. Refresh ready/send/version individually (the other 6 un-authored skeletons keep theirs — not operative yet).


Edit

> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Registers a perch so this session can receive inter-agent messages.

> **Operative.** The UPS hook detects `/sptc:ready` and injects the body from
> `adapter/strings/skills/ready.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.

Registers a perch so this session can receive inter-agent messages.


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

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


1	---
2	name: ready
3	description: |
4	  Enable inter-agent messaging for the current session.
5	argument-hint: "[<id>]"
6	allowed-tools: [Bash]
7	---
8
9	# /sptc:ready
10
11	> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
12	> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
13	> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
14	>
15	> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
16	> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.
17
18	Registers a perch so this session can receive inter-agent messages.
19

---
name: ready
description: |
  Enable inter-agent messaging for the current session.
argument-hint: "[<id>]"
allowed-tools: [Bash]
---

# /sptc:ready

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Registers a perch so this session can receive inter-agent messages.


> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Registers a perch so this session can receive inter-agent messages.

> **Operative.** The UPS hook detects `/sptc:ready` and injects the body from
> `adapter/strings/skills/ready.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.

Registers a perch so this session can receive inter-agent messages.


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

> **Operative.** The UPS hook detects `/sptc:ready` and injects the body from
> `adapter/strings/skills/ready.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.

Registers a perch so this session can receive inter-agent messages.

---
name: ready
description: |
  Enable inter-agent messaging for the current session.
argument-hint: "[<id>]"
allowed-tools: [Bash]
---

# /sptc:ready

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Registers a perch so this session can receive inter-agent messages.

-> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection

-> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

+> **Operative.** The UPS hook detects `/sptc:ready` and injects the body from

+> `adapter/strings/skills/ready.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.


1	---
2	name: send
3	description: |
4	  Send a message to another SPT agent.
5	argument-hint: "<target> [--reply-to <sender>]"
6	allowed-tools: [Bash]
7	---
8
9	# /sptc:send
10
11	> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
12	> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
13	> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
14	>
15	> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
16	> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.
17
18	Delivers a message to another agent; supports reply-to and ring/ask.
19

---
name: send
description: |
  Send a message to another SPT agent.
argument-hint: "<target> [--reply-to <sender>]"
allowed-tools: [Bash]
---

# /sptc:send

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Delivers a message to another agent; supports reply-to and ring/ask.


> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Delivers a message to another agent; supports reply-to and ring/ask.

> **Operative.** The UPS hook detects `/sptc:send` and injects the body from
> `adapter/strings/skills/send.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.

Delivers a message to another agent; supports reply-to and ring/ask.


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

> **Operative.** The UPS hook detects `/sptc:send` and injects the body from
> `adapter/strings/skills/send.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.

Delivers a message to another agent; supports reply-to and ring/ask.

---
name: send
description: |
  Send a message to another SPT agent.
argument-hint: "<target> [--reply-to <sender>]"
allowed-tools: [Bash]
---

# /sptc:send

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Delivers a message to another agent; supports reply-to and ring/ask.

+> **Operative.** The UPS hook detects `/sptc:send` and injects the body from

+> `adapter/strings/skills/send.md` (UPS-fires-on-slash confirmed, ADR-0002; file-backed `[strings]`


1	---
2	name: version
3	description: |
4	  Report the spt-core-tracked adapter manifest/binary version.
5	allowed-tools: [Bash]
6	---
7
8	# /sptc:version
9
10	> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
11	> `sptc` adapter at invocation time (UserPromptSubmit injection from the adapter `[strings]`;
12	> see `docs/adr/0001-distribution-splits-by-volatility.md`). This SKILL.md stays a stub.
13	>
14	> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
15	> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.
16
17	Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.
18

---
name: version
description: |
  Report the spt-core-tracked adapter manifest/binary version.
allowed-tools: [Bash]
---

# /sptc:version

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.


> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.

> **Operative.** The UPS hook detects `/sptc:version` and injects the body from
> `adapter/strings/skills/version.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.

Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.


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

> **Operative.** The UPS hook detects `/sptc:version` and injects the body from
> `adapter/strings/skills/version.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.

Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.

---
name: version
description: |
  Report the spt-core-tracked adapter manifest/binary version.
allowed-tools: [Bash]
---

# /sptc:version

> **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.
>
> **Not yet operative.** Wiring is held pending spt-core finding **F-001** (the injection
> contract — `docs/SPT-CORE-FINDINGS.md`) and the M12 file-backed `[strings]` dependency.

Reports the version-of-truth (adapter manifest/binary tracked by spt-core), not the marketplace skeleton version.

+> **Operative.** The UPS hook detects `/sptc:version` and injects the body from

+> `adapter/strings/skills/version.md` (UPS-fires-on-slash confirmed, ADR-0002; file-backed `[strings]`


Update plan slice, then gates + commit.