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)

setup = { file = "skills/setup.md" }
ready = { file = "skills/ready.md" }
send = { file = "skills/send.md" }
version = { file = "skills/version.md" }
signoff = { file = "skills/signoff.md" }       # -> spt endpoint shutdown (graceful + final save)
list-agents = { file = "skills/list-agents.md" } # -> spt endpoint list (the roster)
commune = { file = "skills/commune.md" }       # -> file-drop <id>-commune.md into [session].commune_dir
force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine); REQ-SKILL-SUBNET

setup = { file = "skills/setup.md" }
ready = { file = "skills/ready.md" }
version = { file = "skills/version.md" }
list-agents = { file = "skills/list-agents.md" } # -> spt endpoint list (the roster)
force-stop = { file = "skills/force-stop.md" } # -> spt endpoint shutdown (graceful, tears down Psyche)
subnet = { file = "skills/subnet.md" }         # -> spt subnet {status,create,show-code,join} (cross-machine); REQ-SKILL-SUBNET
# `send` / `commune` / `signoff` are DELIBERATELY NOT injected here (2026-06-19). A live agent uses
# them REACTIVELY — replying to a message, briefing its Psyche, signing off — WITHOUT the user typing
# `/sptc:<skill>`, so UPS-injection (which fires only on that slash-command in the prompt) is the wrong
# delivery. Their full operative bodies live in the plugin SKILL.md (full-fat, always model-loadable);
# there is no `[strings.skills]` entry and no adapter aux body for them. (sptc_inject_skill no-ops
# silently on the absent key — the full SKILL.md is the single source.)

# "who am i" hint below points at the core CLI. (docs/PARITY.md.)

-signoff = { file = "skills/signoff.md" }       # -> spt endpoint shutdown (graceful + final save)

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

+# `send` / `commune` / `signoff` are DELIBERATELY NOT injected here (2026-06-19). A live agent uses

+# them REACTIVELY — replying to a message, briefing its Psyche, signing off — WITHOUT the user typing

+# `/sptc:<skill>`, so UPS-injection (which fires only on that slash-command in the prompt) is the wrong

+# delivery. Their full operative bodies live in the plugin SKILL.md (full-fat, always model-loadable);

+# there is no `[strings.skills]` entry and no adapter aux body for them. (sptc_inject_skill no-ops

+# silently on the absent key — the full SKILL.md is the single source.)

# `live` -> upgrade THIS session to a LiveAgent (Option A, 2026-06-17): base claude-spt is live-capable

# ([session.psyche_init] in base), so going live is just the COMMAND — bare `spt api listen <id>` under

# a Monitor relay. The live listen path stamps the perch state=live_agent → the daemon livehost hosts


Delete the three adapter bodies, drop the `commune` int assertion:


Bash

git rm -q adapter/strings/skills/send.md adapter/strings/skills/commune.md adapter/strings/skills/signoff.md && echo "deleted 3 adapter bodies" && ls adapter/strings/skills/

Delete send/commune/signoff adapter bodies


deleted 3 adapter bodies
force-stop.md
list-agents.md
live.md
ready.md
setup.md
subnet.md
version.md


54	body=$(spt adapter get-string claude-spt skills.ready 2>&1)
55	case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
56	# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
57	cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
58	case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
59	fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
60	case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
61	# `live` is file-backed too (the LiveAgent bringup body — base [session.psyche_init] + bare
62	# `api listen`; REQ-SKILL-LIVE).
63	lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
64	case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
65	# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
66	sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
67	case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac
68
69	# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped

body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is file-backed too (the LiveAgent bringup body — base [session.psyche_init] + bare
# `api listen`; REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac

# 4c. UPS skill-injection end-to-end: the hook helper resolves a /sptc:<skill> prompt to the wrapped


Edit

# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)

# force-stop = endpoint shutdown body. (send/commune/signoff are NOT injected — full-fat in the
# plugin SKILL.md, no [strings.skills] entry — so they are intentionally absent from this resolve set.)
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)


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)

# force-stop = endpoint shutdown body. (send/commune/signoff are NOT injected — full-fat in the
# plugin SKILL.md, no [strings.skills] entry — so they are intentionally absent from this resolve set.)
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)

#!/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 :ccs 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
# NOTE: there is no `:live` profile (Option A, PREP-4) — base claude-spt is live-capable
# ([session.psyche_init] in base); the LiveAgent capability is asserted via `spt api capability` at
# step 4d below, not a composite resolve.
# The ccs overlay (:ccs leaf-replaces [session.self].command -> `ccs`, a drop-in for `claude`) —
# the LOCKED-ADD ccs profile template (REQ-CCS-PROFILES; validated vs sister project claude_skill_owl).
case "$list" in *claude-spt:ccs*) ok "shipped profile resolves: claude-spt:ccs (ccs overlay)" ;; *) bad "ccs 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: :ccs leaf-replaces the base string. (The placeholder :deep
#    profile was removed 2026-06-18; :ccs is the sole shipped overlay and proves the same seam.)
# :ccs overlay observable (leaf-replaced adapter_label). [int->REQ-CCS-PROFILES]
ccs=$(spt adapter get-string claude-spt:ccs adapter_label 2>&1)
[ "$ccs" = "Claude Code (spt, ccs)" ] && ok "overlay observable: :ccs adapter_label differs" || bad "ccs adapter_label='$ccs'"

# 4b. File-backed [strings] pointer resolves to FILE CONTENTS (not the table, not raw); an inline
#     sibling still prints as-is. Proves `{ file = "skills/<x>.md" }` over adapter/strings/ (F-003).
body=$(spt adapter get-string claude-spt skills.ready 2>&1)
case "$body" in "# /sptc:ready"*) ok "file-backed string resolves: skills.ready -> body" ;; *) bad "skills.ready not resolved to file body: '$(printf %.40s "$body")'" ;; esac
# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).
cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)
case "$cbody" in "# /sptc:commune"*) ok "file-backed string resolves: skills.commune -> body" ;; *) bad "skills.commune not resolved to file body: '$(printf %.40s "$cbody")'" ;; esac
fbody=$(spt adapter get-string claude-spt "skills.force-stop" 2>&1)
case "$fbody" in "# /sptc:force-stop"*) ok "file-backed string resolves: skills.force-stop -> body" ;; *) bad "skills.force-stop not resolved to file body: '$(printf %.40s "$fbody")'" ;; esac
# `live` is file-backed too (the LiveAgent bringup body — base [session.psyche_init] + bare
# `api listen`; REQ-SKILL-LIVE).
lbody=$(spt adapter get-string claude-spt skills.live 2>&1)
case "$lbody" in "# /sptc:live"*) ok "file-backed string resolves: skills.live -> body" ;; *) bad "skills.live not resolved to file body: '$(printf %.40s "$lbody")'" ;; esac
# subnet (LOCKED-ADD cross-machine membership skill — wraps `spt subnet`). [int->REQ-SKILL-SUBNET]
sbody=$(spt adapter get-string claude-spt skills.subnet 2>&1)
case "$sbody" in "# /sptc:subnet"*) ok "file-backed string resolves: skills.subnet -> body" ;; *) bad "skills.subnet not resolved to file body: '$(printf %.40s "$sbody")'" ;; esac

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

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

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

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

-# Newly file-backed skills (commune = file-drop body; force-stop = endpoint shutdown body).

-cbody=$(spt adapter get-string claude-spt skills.commune 2>&1)

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

+# force-stop = endpoint shutdown body. (send/commune/signoff are NOT injected — full-fat in the

+# plugin SKILL.md, no [strings.skills] entry — so they are intentionally absent from this resolve set.)


Now the three **full-fat** plugin SKILLs (operative body in SKILL.md, no skeleton note, no injection):


Write

---
name: send
description: |
  Send a message to another SPT agent. Use when the user says "send to", "message",
  "tell <agent>", or when you need to reach or reply to another agent yourself.
argument-hint: "<target>"
allowed-tools: [Bash]
---

# /sptc:send

Deliver a message to another agent. The **body is read from stdin**.

- **Send:** `printf '%s' "<body>" | spt send <target>`. `SENT:<target>` = delivered live;
  `QUEUED:<target>` = target offline, spooled for its next listen. **QUEUED is success — do not retry.**
- **Reply** to a message you received: `printf '%s' "<body>" | spt send <sender>` — the sender id is
  the `from` on the `<EVENT>` you received.
- **Send and wait** for a reply: `printf '%s' "<body>" | spt ring <target> --timeout 60` (reply prints
  to stdout; `TIMEOUT` is exit 0 — the message still landed).

Full guidance: `spt how-to send`. To receive replies on your own perch, go `/sptc:ready` or `/sptc:live`.


1. confirm 2. confirm 3. confirm 4. confirm 5. confirm. the point is to reduce perceived friction for the end user, who is waiting on the agent to finish their perch bringup. absolute minimum number o…


Update Claude-spt adapter configuration and flags


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)

---
name: send
description: |
  Send a message to another SPT agent. Use when the user says "send to", "message",
  "tell <agent>", or when you need to reach or reply to another agent yourself.
argument-hint: "<target>"
allowed-tools: [Bash]
---

# /sptc:send

Deliver a message to another agent. The **body is read from stdin**.

- **Send:** `printf '%s' "<body>" | spt send <target>`. `SENT:<target>` = delivered live;
  `QUEUED:<target>` = target offline, spooled for its next listen. **QUEUED is success — do not retry.**
- **Reply** to a message you received: `printf '%s' "<body>" | spt send <sender>` — the sender id is
  the `from` on the `<EVENT>` you received.
- **Send and wait** for a reply: `printf '%s' "<body>" | spt ring <target> --timeout 60` (reply prints
  to stdout; `TIMEOUT` is exit 0 — the message still landed).

Full guidance: `spt how-to send`. To receive replies on your own perch, go `/sptc:ready` or `/sptc:live`.

-  "tell <agent>", or wants to reach another agent.

-argument-hint: "<target> [--reply-to <sender>]"

+  "tell <agent>", or when you need to reach or reply to another agent yourself.

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

-> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.

-> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check

-> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

+Deliver a message to another agent. The **body is read from stdin**.

-Delivers a message to another agent; supports reply-to.

+- **Send:** `printf '%s' "<body>" | spt send <target>`. `SENT:<target>` = delivered live;

+  `QUEUED:<target>` = target offline, spooled for its next listen. **QUEUED is success — do not retry.**

+- **Reply** to a message you received: `printf '%s' "<body>" | spt send <sender>` — the sender id is

+  the `from` on the `<EVENT>` you received.

+- **Send and wait** for a reply: `printf '%s' "<body>" | spt ring <target> --timeout 60` (reply prints

+  to stdout; `TIMEOUT` is exit 0 — the message still landed).

+Full guidance: `spt how-to send`. To receive replies on your own perch, go `/sptc:ready` or `/sptc:live`.

---
name: send
description: |
  Send a message to another SPT agent. Use when the user says "send to", "message",
  "tell <agent>", or wants to reach another 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. Look out for the UserPromptSubmit additionalContext.
>
> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check
> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

Delivers a message to another agent; supports reply-to.


---
name: commune
description: |
  Push a context update to your Psyche so it can brief your resume across a reset. Use when the
  user says "commune" or "update psyche", or — as a live agent — after a significant body of work,
  before a /clear or /compact.
allowed-tools: [Bash, Write]
---

# /sptc:commune

Brief your Psyche with a context DELTA so it can carry you across a `/clear` or compact. Live agents
only (a ready agent has no Psyche).

This is a **file-drop, not a command** — you write the file; spt absorbs it.

1. Get your id: `spt whoami` (empty ⇒ no perch — `/sptc:live` first).
2. Write `.claude/<id>-commune.md` — a concise delta: current task + status, decisions since the last
   commune, immediate next steps. One atomic write of the full body.
3. The file disappearing is the success signal (spt ingested it). If it lingers, you are not live.


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

---
name: commune
description: |
  Push a context update to your Psyche so it can brief your resume across a reset. Use when the
  user says "commune" or "update psyche", or — as a live agent — after a significant body of work,
  before a /clear or /compact.
allowed-tools: [Bash, Write]
---

# /sptc:commune

Brief your Psyche with a context DELTA so it can carry you across a `/clear` or compact. Live agents
only (a ready agent has no Psyche).

This is a **file-drop, not a command** — you write the file; spt absorbs it.

1. Get your id: `spt whoami` (empty ⇒ no perch — `/sptc:live` first).
2. Write `.claude/<id>-commune.md` — a concise delta: current task + status, decisions since the last
   commune, immediate next steps. One atomic write of the full body.
3. The file disappearing is the success signal (spt ingested it). If it lingers, you are not live.

-  Push a context update to your Psyche so it can brief a resume across a reset. Use when the

-  user says "commune", "update psyche", or wants to brief their Psyche before a /clear or compact.

+  Push a context update to your Psyche so it can brief your resume across a reset. Use when the

+  user says "commune" or "update psyche", or — as a live agent — after a significant body of work,

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

-> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.

-> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check

-> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

+Brief your Psyche with a context DELTA so it can carry you across a `/clear` or compact. Live agents

-Drops a context delta for the Psyche to absorb (a file the daemon ingests).

+This is a **file-drop, not a command** — you write the file; spt absorbs it.

+1. Get your id: `spt whoami` (empty ⇒ no perch — `/sptc:live` first).

+2. Write `.claude/<id>-commune.md` — a concise delta: current task + status, decisions since the last

+   commune, immediate next steps. One atomic write of the full body.

+3. The file disappearing is the success signal (spt ingested it). If it lingers, you are not live.

---
name: commune
description: |
  Push a context update to your Psyche so it can brief a resume across a reset. Use when the
  user says "commune", "update psyche", or wants to brief their Psyche before a /clear or compact.
  Live agents only.
allowed-tools: [Bash, Write]
---

# /sptc:commune

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.
>
> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check
> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

Drops a context delta for the Psyche to absorb (a file the daemon ingests).


---
name: signoff
description: |
  Gracefully shut down your live session, saving a final context summary. Use when the user says
  "sign off" or "graceful stop", or when you (a live agent) are done and want to go offline cleanly.
allowed-tools: [Bash, Write]
---

# /sptc:signoff

End your session's endpoint gracefully, with the final context save.

1. (Optional) Write a brief closing summary first, so the saved context is useful on resume.
2. `spt endpoint shutdown` (your own perch by default) — stops the listener, fires the final context
   save, and for a live agent takes the Psyche down with it.
3. Confirm to the user. You are no longer reachable; `/sptc:ready` or `/sptc:live` brings you back.

Lighter, no-save stop: `spt endpoint stop`. Full options: `spt endpoint shutdown --help`.


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

---
name: signoff
description: |
  Gracefully shut down your live session, saving a final context summary. Use when the user says
  "sign off" or "graceful stop", or when you (a live agent) are done and want to go offline cleanly.
allowed-tools: [Bash, Write]
---

# /sptc:signoff

End your session's endpoint gracefully, with the final context save.

1. (Optional) Write a brief closing summary first, so the saved context is useful on resume.
2. `spt endpoint shutdown` (your own perch by default) — stops the listener, fires the final context
   save, and for a live agent takes the Psyche down with it.
3. Confirm to the user. You are no longer reachable; `/sptc:ready` or `/sptc:live` brings you back.

Lighter, no-save stop: `spt endpoint stop`. Full options: `spt endpoint shutdown --help`.

-  Gracefully shut down this live agent, saving a final context summary. Use when the user

-  says "sign off", "graceful stop", or wants to cleanly end a live session.

+  Gracefully shut down your live session, saving a final context summary. Use when the user says

+  "sign off" or "graceful stop", or when you (a live agent) are done and want to go offline cleanly.

+End your session's endpoint gracefully, with the final context save.

-Cleanly ends this session's endpoint, saving a final summary at teardown.

+1. (Optional) Write a brief closing summary first, so the saved context is useful on resume.

+2. `spt endpoint shutdown` (your own perch by default) — stops the listener, fires the final context

+   save, and for a live agent takes the Psyche down with it.

+3. Confirm to the user. You are no longer reachable; `/sptc:ready` or `/sptc:live` brings you back.

+Lighter, no-save stop: `spt endpoint stop`. Full options: `spt endpoint shutdown --help`.

---
name: signoff
description: |
  Gracefully shut down this live agent, saving a final context summary. Use when the user
  says "sign off", "graceful stop", or wants to cleanly end a live session.
allowed-tools: [Bash, Write]
---

# /sptc:signoff

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.
>
> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check
> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

Cleanly ends this session's endpoint, saving a final summary at teardown.


Now the lean injected adapter bodies (de-internal, `how-to`/`--help` anchors, minimal-friction bringup). ready:


# /sptc:ready — operative instructions

Make this session reachable for inter-agent messages. Fewest steps — the user is waiting on bringup.

1. Pick the id: the one the user gave, else `spt whoami`. Call it `<id>`.
2. Run the listener as a single PERSISTENT background task via the **Monitor** tool:
   - `command: "spt ready <id>"`
   - `persistent: true`
   - `description: "« spt event »"`

   It blocks for the session's life, emitting one `<EVENT type="msg" from="<sender>">body</EVENT>` per
   delivery. Announce reachable — don't wait on markers.
3. To reply, pipe the message body to `spt send <sender>` (sender = the `from` on the EVENT).

Full guidance: `spt how-to ready`. (No long-running tasks available? `spt ready <id> --once` drains the
backlog, waits for one delivery, then exits — re-run to stay reachable.)


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

# /sptc:ready — operative instructions

Make this session reachable for inter-agent messages. Fewest steps — the user is waiting on bringup.

1. Pick the id: the one the user gave, else `spt whoami`. Call it `<id>`.
2. Run the listener as a single PERSISTENT background task via the **Monitor** tool:
   - `command: "spt ready <id>"`
   - `persistent: true`
   - `description: "« spt event »"`

   It blocks for the session's life, emitting one `<EVENT type="msg" from="<sender>">body</EVENT>` per
   delivery. Announce reachable — don't wait on markers.
3. To reply, pipe the message body to `spt send <sender>` (sender = the `from` on the EVENT).

Full guidance: `spt how-to ready`. (No long-running tasks available? `spt ready <id> --once` drains the
backlog, waits for one delivery, then exits — re-run to stay reachable.)

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

+Make this session reachable for inter-agent messages. Fewest steps — the user is waiting on bringup.

+1. Pick the id: the one the user gave, else `spt whoami`. Call it `<id>`.

+2. Run the listener as a single PERSISTENT background task via the **Monitor** tool:

-1. If `spt` is not on `PATH`, run `/sptc:setup` first.

-2. Pick a short lowercase perch id, or reuse this session's id from `spt whoami`. Call it `<id>`.

-3. Preferred (harness hosts a long-running task): run `spt ready <id>` as a **background task** and

-   read its stdout. It **blocks** for the session's life — `READY:<id>` (stderr) is the online

-   signal; the spooled backlog drains first, then each delivery prints as it arrives.

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

-5. Deliveries arrive as `<EVENT type="msg" from="<sender>">body</EVENT>` (body HTML-escaped, newlines

-   as `<br>`). To reply, pipe the body to `spt send --reply-to <sender>`.

+   It blocks for the session's life, emitting one `<EVENT type="msg" from="<sender>">body</EVENT>` per

+   delivery. Announce reachable — don't wait on markers.

+3. To reply, pipe the message body to `spt send <sender>` (sender = the `from` on the EVENT).

+Full guidance: `spt how-to ready`. (No long-running tasks available? `spt ready <id> --once` drains the

+backlog, waits for one delivery, then exits — re-run to stay reachable.)

# /sptc:ready — operative instructions

**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. Pick a short lowercase perch id, or reuse this session's id from `spt whoami`. Call it `<id>`.
3. Preferred (harness hosts a long-running task): run `spt ready <id>` as a **background task** and
   read its stdout. It **blocks** for the session's life — `READY:<id>` (stderr) is the online
   signal; the spooled backlog drains first, then each delivery prints as it arrives.
4. 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.
5. Deliveries arrive as `<EVENT type="msg" from="<sender>">body</EVENT>` (body HTML-escaped, newlines
   as `<br>`). To reply, pipe the body to `spt send --reply-to <sender>`.


# /sptc:subnet — operative instructions

Manage this machine's **subnet** — the private group of paired machines whose agents reach each other
across nodes. Cross-machine `/sptc:send` and live agents depend on it.

Match the user's intent to a verb (full guidance: `spt how-to subnet`, or `spt subnet --help`):

- **Where I stand:** `spt subnet status` (or bare `spt subnet`).
- **Start a new subnet** (this machine = first node): `spt subnet create` — prints the 6-digit code
  + URI + QR.
- **Invite a machine:** `spt subnet show-code` — re-displays the current code.
- **Join an existing one:** `spt subnet join` — needs the code from a current member.

Pairing: on A run `create` (or `show-code`) → read the code → on B run `join` with it → `spt subnet
status` on both to confirm. Pairing codes are sensitive — share only with machines that should join.
<!-- [doc->REQ-SKILL-SUBNET] -->


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

# /sptc:subnet — operative instructions

Manage this machine's **subnet** — the private group of paired machines whose agents reach each other
across nodes. Cross-machine `/sptc:send` and live agents depend on it.

Match the user's intent to a verb (full guidance: `spt how-to subnet`, or `spt subnet --help`):

- **Where I stand:** `spt subnet status` (or bare `spt subnet`).
- **Start a new subnet** (this machine = first node): `spt subnet create` — prints the 6-digit code
  + URI + QR.
- **Invite a machine:** `spt subnet show-code` — re-displays the current code.
- **Join an existing one:** `spt subnet join` — needs the code from a current member.

Pairing: on A run `create` (or `show-code`) → read the code → on B run `join` with it → `spt subnet
status` on both to confirm. Pairing codes are sensitive — share only with machines that should join.
<!-- [doc->REQ-SKILL-SUBNET] -->

-**Goal:** manage this machine's **subnet** membership — the private group of paired machines whose

-agents reach each other across nodes. Cross-machine `/sptc:send`, `/sptc:ready`, and live agents

+Manage this machine's **subnet** — the private group of paired machines whose agents reach each other

+across nodes. Cross-machine `/sptc:send` and live agents depend on it.

+Match the user's intent to a verb (full guidance: `spt how-to subnet`, or `spt subnet --help`):

-1. If `spt` is not on `PATH`, run `/sptc:setup` first (a subnet needs the running daemon).

-2. The canonical, always-current verb list is `spt subnet --help`. Match the user's intent:

-   - **Where I stand:** `spt subnet` (bare) or `spt subnet status` — name, paired nodes, endpoints.

-   - **Start a new subnet** (this machine = first node): `spt subnet create` — mints it and prints

-     the joining material (6-digit pairing code + URI + QR).

-   - **Invite another machine:** `spt subnet show-code` — re-displays the current code (+ URI + QR).

-   - **Pair THIS machine into an existing subnet:** `spt subnet join` — needs the code from an

-3. **Pairing (two machines):** on A run `create` (or `show-code`) → read the 6-digit code → on B run

-   `join` and supply it → confirm with `spt subnet status` on both (each lists the other).

-4. **Lifecycle verbs (only if asked):** `leave`, `detach`/`attach`, `revoke`/`prune`, `notify`.

-5. Confirm what changed. Pairing codes are sensitive joining material — share them only with machines

+- **Where I stand:** `spt subnet status` (or bare `spt subnet`).

+- **Start a new subnet** (this machine = first node): `spt subnet create` — prints the 6-digit code

+- **Invite a machine:** `spt subnet show-code` — re-displays the current code.

+- **Join an existing one:** `spt subnet join` — needs the code from a current member.

+Pairing: on A run `create` (or `show-code`) → read the code → on B run `join` with it → `spt subnet

+status` on both to confirm. Pairing codes are sensitive — share only with machines that should join.

# /sptc:subnet — operative instructions

**Goal:** manage this machine's **subnet** membership — the private group of paired machines whose
agents reach each other across nodes. Cross-machine `/sptc:send`, `/sptc:ready`, and live agents
depend on it.

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first (a subnet needs the running daemon).
2. The canonical, always-current verb list is `spt subnet --help`. Match the user's intent:
   - **Where I stand:** `spt subnet` (bare) or `spt subnet status` — name, paired nodes, endpoints.
     Start here when unsure.
   - **Start a new subnet** (this machine = first node): `spt subnet create` — mints it and prints
     the joining material (6-digit pairing code + URI + QR).
   - **Invite another machine:** `spt subnet show-code` — re-displays the current code (+ URI + QR).
   - **Pair THIS machine into an existing subnet:** `spt subnet join` — needs the code from an
     existing member.
3. **Pairing (two machines):** on A run `create` (or `show-code`) → read the 6-digit code → on B run
   `join` and supply it → confirm with `spt subnet status` on both (each lists the other).
4. **Lifecycle verbs (only if asked):** `leave`, `detach`/`attach`, `revoke`/`prune`, `notify`.
5. Confirm what changed. Pairing codes are sensitive joining material — share them only with machines
   that should join.
<!-- [doc->REQ-SKILL-SUBNET] -->


# /sptc:list-agents — operative instructions

Show the roster of spt endpoints reachable from this node.

1. `spt endpoint list` (or bare `spt endpoint`) — every member subnet's endpoints, grouped, with this
   session's own pinned at the top.
2. Summarize who's online and which is this session. Message one with `/sptc:send <id>`.

Options: `spt endpoint list --help`.


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

# /sptc:list-agents — operative instructions

Show the roster of spt endpoints reachable from this node.

1. `spt endpoint list` (or bare `spt endpoint`) — every member subnet's endpoints, grouped, with this
   session's own pinned at the top.
2. Summarize who's online and which is this session. Message one with `/sptc:send <id>`.

Options: `spt endpoint list --help`.

-**Goal:** show the roster of spt endpoints/agents reachable from this node.

+Show the roster of spt endpoints reachable from this node.

+1. `spt endpoint list` (or bare `spt endpoint`) — every member subnet's endpoints, grouped, with this

+2. Summarize who's online and which is this session. Message one with `/sptc:send <id>`.

-1. If `spt` is not on `PATH`, run `/sptc:setup` first.

-2. Run `spt endpoint list` (or bare `spt endpoint`): every member subnet's endpoints, grouped by

-   subnet, with **this session's own endpoint pinned distinctly at the top**.

-3. Summarize who is online, their ids, and which is this session. To message one use

-   `/sptc:send <id>`; to become reachable yourself, `/sptc:ready`.

# /sptc:list-agents — operative instructions

**Goal:** show the roster of spt endpoints/agents reachable from this node.

**Do this:**

1. If `spt` is not on `PATH`, run `/sptc:setup` first.
2. Run `spt endpoint list` (or bare `spt endpoint`): every member subnet's endpoints, grouped by
   subnet, with **this session's own endpoint pinned distinctly at the top**.
3. Summarize who is online, their ids, and which is this session. To message one use
   `/sptc:send <id>`; to become reachable yourself, `/sptc:ready`.


# /sptc:version — operative instructions

Report the **version-of-truth** — the spt-core-tracked binary + adapter manifest, NOT the marketplace
`plugin.json` version.

1. `spt --version` — the binary.
2. `spt adapter list` — the registered `claude-spt` entry + active profile (what drives the session).
3. Note that the cplugs plugin version is intentionally not the version-of-truth.


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

# /sptc:version — operative instructions

Report the **version-of-truth** — the spt-core-tracked binary + adapter manifest, NOT the marketplace
`plugin.json` version.

1. `spt --version` — the binary.
2. `spt adapter list` — the registered `claude-spt` entry + active profile (what drives the session).
3. Note that the cplugs plugin version is intentionally not the version-of-truth.

-**Goal:** report the **version-of-truth** — the spt-core-tracked binary + adapter-manifest version,

-NOT the marketplace `plugin.json` version (the thin skeleton sits static across many binary/manifest

-updates, so its version is not authoritative).

+Report the **version-of-truth** — the spt-core-tracked binary + adapter manifest, NOT the marketplace

-2. Report the binary version: `spt --version`.

-3. Report the registered adapter (the conducted layer): `spt adapter list` — 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.

+2. `spt adapter list` — the registered `claude-spt` entry + active profile (what drives the session).

+3. Note that the cplugs plugin version is intentionally not the version-of-truth.

# /sptc:version — operative instructions

**Goal:** report the **version-of-truth** — the spt-core-tracked binary + adapter-manifest version,
NOT the marketplace `plugin.json` version (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 binary version: `spt --version`.
3. Report the registered adapter (the conducted layer): `spt adapter list` — 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.


# /sptc:force-stop — operative instructions

Tear down an spt agent's endpoint — your own, or another agent's.

1. Target id `<id>`: your own (`spt whoami`) by default, or the one the user named.
2. **Graceful (default):** `spt endpoint shutdown [<id>]` — stops the listener, fires the final save,
   and for a live agent takes its Psyche down too.
3. **Lighter, no-save:** `spt endpoint stop <id>` — unregisters the perch; the spool is preserved.

Confirm what is now offline. Options: `spt endpoint --help`. (No hard/no-grace kill in core spt —
`shutdown` is the strongest path.)


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

# /sptc:force-stop — operative instructions

Tear down an spt agent's endpoint — your own, or another agent's.

1. Target id `<id>`: your own (`spt whoami`) by default, or the one the user named.
2. **Graceful (default):** `spt endpoint shutdown [<id>]` — stops the listener, fires the final save,
   and for a live agent takes its Psyche down too.
3. **Lighter, no-save:** `spt endpoint stop <id>` — unregisters the perch; the spool is preserved.

Confirm what is now offline. Options: `spt endpoint --help`. (No hard/no-grace kill in core spt —
`shutdown` is the strongest path.)

-**Goal:** tear down an spt agent's endpoint — this session's own, or another agent's.

+Tear down an spt agent's endpoint — your own, or another agent's.

+1. Target id `<id>`: your own (`spt whoami`) by default, or the one the user named.

+2. **Graceful (default):** `spt endpoint shutdown [<id>]` — stops the listener, fires the final save,

+   and for a live agent takes its Psyche down too.

+3. **Lighter, no-save:** `spt endpoint stop <id>` — unregisters the perch; the spool is preserved.

-1. If `spt` is not on `PATH`, there is nothing running to stop.

-2. Identify the target id `<id>` — default is this session's own (`spt whoami`), or the agent the

-3. **Graceful teardown (default):** `spt endpoint shutdown [<id>]` (omit `<id>` for your own perch).

-   It soft-stops the listener, takes the suspend edge (the final context save fires), and for a

-   **live agent** tears the **Psyche** down with it.

-4. **Lighter, no-save:** `spt endpoint stop <id>` — unregisters the perch; the spool is preserved (no

-5. Confirm which path you took and what is now offline.

-**No hard force in core spt** — `endpoint shutdown` is graceful-only and is the correct

-teardown-including-Psyche path for v1 (a no-grace primitive is a tracked spt-core gap).

+Confirm what is now offline. Options: `spt endpoint --help`. (No hard/no-grace kill in core spt —

# /sptc:force-stop — operative instructions

**Goal:** tear down an spt agent's endpoint — this session's own, or another agent's.

**Do this:**

1. If `spt` is not on `PATH`, there is nothing running to stop.
2. Identify the target id `<id>` — default is this session's own (`spt whoami`), or the agent the
   user named.
3. **Graceful teardown (default):** `spt endpoint shutdown [<id>]` (omit `<id>` for your own perch).
   It soft-stops the listener, takes the suspend edge (the final context save fires), and for a
   **live agent** tears the **Psyche** down with it.
4. **Lighter, no-save:** `spt endpoint stop <id>` — unregisters the perch; the spool is preserved (no
   suspend/save, no Psyche teardown).
5. Confirm which path you took and what is now offline.

**No hard force in core spt** — `endpoint shutdown` is graceful-only and is the correct
teardown-including-Psyche path for v1 (a no-grace primitive is a tracked spt-core gap).


Fixing the now-stale `REQ-SKILL-SUBNET` note (`spt how-to subnet` exists now — I just referenced it):


Edit

(No `spt how-to subnet` topic exists — minor published-guidance gap noted in docs/SPT-CORE-FINDINGS.md; the skill wraps `spt subnet --help` as canonical.)

(Skill points at `spt how-to subnet` + `spt subnet --help` as the canonical self-drive anchors — the how-to topic now ships.)


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

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

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

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


Now revising `docs/SKILL-AUTHORING.md` to carry the throughline + the two delivery patterns:


Write

# Skill authoring

> How `/sptc:*` skills are written here. The guiding rule: a skill tells the agent **what to do, what
> it will see, and how to act** — never **how the plugin or spt-core implements it**. Strip internals,
> strip jargon from anything the user reads, never ship an untrue step, and keep the agent's actions to
> the minimum (the end user is often waiting on it).

## What a skill body must NOT contain

- **Plugin / spt-core internals.** No adapter resolution (`--adapter`, parent-pid / `host_binaries`
  matching), no perch-state stamping (`state=live_agent`), no daemon/poll-vs-relay coordination, no
  seed/bind wiring. The agent runs a command; it does not need to know how spt routes it.
- **Internal jargon in user-facing text.** What the *user* reads (e.g. the LIVE announcement) carries
  zero `Perch` / `Psyche` / `daemon` / `LiveAgent` / marker names. Say "Now running as `<id>`", not
  "LiveAgent, Psyche hosted by the daemon".
- **Untrue or stale steps.** Verify against behavior. (E.g. the persistent Monitor relay *survives*
  `/clear`/compact — there is no "re-fire the relay" step.)
- **Ceremony.** Don't make the agent gate on `BOUND`/`READY` markers in the happy path. Fire it,
  announce, move on — inspect markers only when troubleshooting.

Keep the legacy operational tells that *are* useful: the Monitor task `description: "« spt event »"`,
and the `<EVENT type="msg|alarm|echo_commune|init_signoff" …>` envelope catalog the agent handles.

## Minimum-friction bringup

`ready` and `live` bring a perch up while the **user waits**. Optimize for the fewest agent
actions and zero avoidable delay: resolve the id (given, else `spt whoami`), fire the listener as one
persistent Monitor task, announce reachable. No marker-watching, no extra round-trips.

## Two delivery patterns

| Pattern | Files | Use for |
|---|---|---|
| **A — injected** | thin `plugin/sptc/skills/<n>/SKILL.md` stub + `adapter/strings/skills/<n>.md` body (manifest `[strings.skills].<n>`) | Skills the **user** invokes (`/sptc:<n>`) or a hint surfaces. The UPS hook injects the body on the slash-command. |
| **B — full-fat** | `plugin/sptc/skills/<n>/SKILL.md` only — operative body in the SKILL.md, **no** `[strings.skills]` entry, **no** adapter body | Skills the **agent self-drives reactively** without the user typing `/sptc:<n>` — `send`, `commune`, `signoff`. UPS-injection fires only on a slash-command in the prompt, so it cannot deliver these; the always-loadable SKILL.md does. |

Self-drive anchor (both patterns): point the agent at the live source of truth where one exists —
`spt how-to <topic>` (ready · send · subnet · live) or `spt <verb> --help` — instead of duplicating
full reference inline.

## Templates

**Pattern A — plugin skeleton** (thin; the `description` is the routing surface):

```md
---
name: <name>
description: |
  <What it does.> Use when the user says "<trigger>", "<trigger>", or wants to <intent>.
argument-hint: "<args>"        # omit if none
allowed-tools: [Bash]          # the minimum the skill needs
---

# /sptc:<name>

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.
>
> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check
> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

<One line naming what the skill does.>
```

**Pattern A — adapter body** (the injected operative instructions):

```md
# /sptc:<name> — operative instructions

<One line: the outcome.>

1. <step> … <step>

Full guidance: `spt how-to <name>` (or `spt <verb> --help`).
<!-- [<doc>->REQ-NAME] -->   # keep the traceability tag if this body is doc evidence
```

**Pattern B — full-fat SKILL.md** (no skeleton note, no adapter body):

```md
---
name: <name>
description: |
  <What it does.> Use when the user says "<trigger>", or when you need to <intent> yourself.
allowed-tools: [Bash]
---

# /sptc:<name>

<The full operative instructions — the agent loads this directly.>
```

## The `setup` exception

`setup` is **self-contained on both surfaces**: it runs precisely when spt-core may be absent, so
injection can no-op. Its plugin SKILL.md keeps the operative steps as the floor; tighten it, never
reduce it to a stub.

## `description` rules (from write-a-skill)

Third person, **what it does** then **"Use when [triggers]"**, ≤1024 chars, no time-sensitive info,
consistent terms. Route phrases reference **`/sptc:`** (the shipped plugin name; the `s/sptc/spt/`
succession renames them later). Trigger phrasing is adapted from the legacy `claude_skill_owl`
descriptions, dropping legacy internals (`$OWL`/`$LIVE`/`info.json`).

## Checklist

- [ ] Body says what to do / what you'll see / how to act — no plugin or spt-core internals.
- [ ] User-facing text carries no internal jargon; no untrue or stale steps.
- [ ] Bringup skills (`ready`/`live`) are minimum-friction — fire and announce, no marker-gating.
- [ ] Right pattern: agent-self-driven (`send`/`commune`/`signoff`) → full-fat, no injection.
- [ ] Cites `spt how-to`/`--help` where one exists; `allowed-tools` is the minimum.
- [ ] `[<doc>->REQ-*]` traceability tags preserved.


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

# Skill authoring

> How `/sptc:*` skills are written here. The guiding rule: a skill tells the agent **what to do, what
> it will see, and how to act** — never **how the plugin or spt-core implements it**. Strip internals,
> strip jargon from anything the user reads, never ship an untrue step, and keep the agent's actions to
> the minimum (the end user is often waiting on it).

## What a skill body must NOT contain

- **Plugin / spt-core internals.** No adapter resolution (`--adapter`, parent-pid / `host_binaries`
  matching), no perch-state stamping (`state=live_agent`), no daemon/poll-vs-relay coordination, no
  seed/bind wiring. The agent runs a command; it does not need to know how spt routes it.
- **Internal jargon in user-facing text.** What the *user* reads (e.g. the LIVE announcement) carries
  zero `Perch` / `Psyche` / `daemon` / `LiveAgent` / marker names. Say "Now running as `<id>`", not
  "LiveAgent, Psyche hosted by the daemon".
- **Untrue or stale steps.** Verify against behavior. (E.g. the persistent Monitor relay *survives*
  `/clear`/compact — there is no "re-fire the relay" step.)
- **Ceremony.** Don't make the agent gate on `BOUND`/`READY` markers in the happy path. Fire it,
  announce, move on — inspect markers only when troubleshooting.

Keep the legacy operational tells that *are* useful: the Monitor task `description: "« spt event »"`,
and the `<EVENT type="msg|alarm|echo_commune|init_signoff" …>` envelope catalog the agent handles.

## Minimum-friction bringup

`ready` and `live` bring a perch up while the **user waits**. Optimize for the fewest agent
actions and zero avoidable delay: resolve the id (given, else `spt whoami`), fire the listener as one
persistent Monitor task, announce reachable. No marker-watching, no extra round-trips.

## Two delivery patterns

| Pattern | Files | Use for |
|---|---|---|
| **A — injected** | thin `plugin/sptc/skills/<n>/SKILL.md` stub + `adapter/strings/skills/<n>.md` body (manifest `[strings.skills].<n>`) | Skills the **user** invokes (`/sptc:<n>`) or a hint surfaces. The UPS hook injects the body on the slash-command. |
| **B — full-fat** | `plugin/sptc/skills/<n>/SKILL.md` only — operative body in the SKILL.md, **no** `[strings.skills]` entry, **no** adapter body | Skills the **agent self-drives reactively** without the user typing `/sptc:<n>` — `send`, `commune`, `signoff`. UPS-injection fires only on a slash-command in the prompt, so it cannot deliver these; the always-loadable SKILL.md does. |

Self-drive anchor (both patterns): point the agent at the live source of truth where one exists —
`spt how-to <topic>` (ready · send · subnet · live) or `spt <verb> --help` — instead of duplicating
full reference inline.

## Templates

**Pattern A — plugin skeleton** (thin; the `description` is the routing surface):

```md
---
name: <name>
description: |
  <What it does.> Use when the user says "<trigger>", "<trigger>", or wants to <intent>.
argument-hint: "<args>"        # omit if none
allowed-tools: [Bash]          # the minimum the skill needs
---

# /sptc:<name>

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.
>
> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check
> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

<One line naming what the skill does.>
```

**Pattern A — adapter body** (the injected operative instructions):

```md
# /sptc:<name> — operative instructions

<One line: the outcome.>

1. <step> … <step>

Full guidance: `spt how-to <name>` (or `spt <verb> --help`).
<!-- [<doc>->REQ-NAME] -->   # keep the traceability tag if this body is doc evidence
```

**Pattern B — full-fat SKILL.md** (no skeleton note, no adapter body):

```md
---
name: <name>
description: |
  <What it does.> Use when the user says "<trigger>", or when you need to <intent> yourself.
allowed-tools: [Bash]
---

# /sptc:<name>

<The full operative instructions — the agent loads this directly.>
```

## The `setup` exception

`setup` is **self-contained on both surfaces**: it runs precisely when spt-core may be absent, so
injection can no-op. Its plugin SKILL.md keeps the operative steps as the floor; tighten it, never
reduce it to a stub.

## `description` rules (from write-a-skill)

Third person, **what it does** then **"Use when [triggers]"**, ≤1024 chars, no time-sensitive info,
consistent terms. Route phrases reference **`/sptc:`** (the shipped plugin name; the `s/sptc/spt/`
succession renames them later). Trigger phrasing is adapted from the legacy `claude_skill_owl`
descriptions, dropping legacy internals (`$OWL`/`$LIVE`/`info.json`).

## Checklist

- [ ] Body says what to do / what you'll see / how to act — no plugin or spt-core internals.
- [ ] User-facing text carries no internal jargon; no untrue or stale steps.
- [ ] Bringup skills (`ready`/`live`) are minimum-friction — fire and announce, no marker-gating.
- [ ] Right pattern: agent-self-driven (`send`/`commune`/`signoff`) → full-fat, no injection.
- [ ] Cites `spt how-to`/`--help` where one exists; `allowed-tools` is the minimum.
- [ ] `[<doc>->REQ-*]` traceability tags preserved.

-> How `/sptc:*` skills are written in this repo. Skills live on **two surfaces** (ADR-0001's

-> thin-skeleton split). Keep both lean: the plugin file is a stub, the adapter file is the operative

-> instructions. Bloat — provenance notes, requirement ids, ADR/finding citations, rationale the

-> running agent doesn't need — does not belong in either.

+> How `/sptc:*` skills are written here. The guiding rule: a skill tells the agent **what to do, what

+> it will see, and how to act** — never **how the plugin or spt-core implements it**. Strip internals,

+> strip jargon from anything the user reads, never ship an untrue step, and keep the agent's actions to

+> the minimum (the end user is often waiting on it).

-| **Plugin skeleton** | `plugin/sptc/skills/<name>/SKILL.md` | Thin stub shipped on cplugs. Frontmatter `description` is the routing surface; body is a fixed note. | The model deciding *which* skill to load. |

-| **Adapter body** | `adapter/strings/skills/<name>.md` | The operative instructions, UPS-injected at invocation from the adapter `[strings]`. | The agent *running* the skill. |

+- **Plugin / spt-core internals.** No adapter resolution (`--adapter`, parent-pid / `host_binaries`

+  matching), no perch-state stamping (`state=live_agent`), no daemon/poll-vs-relay coordination, no

+  seed/bind wiring. The agent runs a command; it does not need to know how spt routes it.

+- **Internal jargon in user-facing text.** What the *user* reads (e.g. the LIVE announcement) carries

+  zero `Perch` / `Psyche` / `daemon` / `LiveAgent` / marker names. Say "Now running as `<id>`", not

+  "LiveAgent, Psyche hosted by the daemon".

+- **Untrue or stale steps.** Verify against behavior. (E.g. the persistent Monitor relay *survives*

+  `/clear`/compact — there is no "re-fire the relay" step.)

+- **Ceremony.** Don't make the agent gate on `BOUND`/`READY` markers in the happy path. Fire it,

+  announce, move on — inspect markers only when troubleshooting.

-The plugin skeleton carries **no operative steps** — those are injected from the adapter body at call

-time. The two never duplicate instructions.

+Keep the legacy operational tells that *are* useful: the Monitor task `description: "« spt event »"`,

+and the `<EVENT type="msg|alarm|echo_commune|init_signoff" …>` envelope catalog the agent handles.

+`ready` and `live` bring a perch up while the **user waits**. Optimize for the fewest agent

+actions and zero avoidable delay: resolve the id (given, else `spt whoami`), fire the listener as one

+persistent Monitor task, announce reachable. No marker-watching, no extra round-trips.

+| **A — injected** | thin `plugin/sptc/skills/<n>/SKILL.md` stub + `adapter/strings/skills/<n>.md` body (manifest `[strings.skills].<n>`) | Skills the **user** invokes (`/sptc:<n>`) or a hint surfaces. The UPS hook injects the body on the slash-command. |

+| **B — full-fat** | `plugin/sptc/skills/<n>/SKILL.md` only — operative body in the SKILL.md, **no** `[strings.skills]` entry, **no** adapter body | Skills the **agent self-drives reactively** without the user typing `/sptc:<n>` — `send`, `commune`, `signoff`. UPS-injection fires only on a slash-command in the prompt, so it cannot deliver these; the always-loadable SKILL.md does. |

+Self-drive anchor (both patterns): point the agent at the live source of truth where one exists —

+`spt how-to <topic>` (ready · send · subnet · live) or `spt <verb> --help` — instead of duplicating

+**Pattern A — plugin skeleton** (thin; the `description` is the routing surface):

-  <One sentence: what it does.> Use when the user says "<trigger>", "<trigger>", or

+  <What it does.> Use when the user says "<trigger>", "<trigger>", or wants to <intent>.

argument-hint: "<args>"        # omit if none

-allowed-tools: [Bash]          # the minimum the skill needs (Bash/Read/Write/Monitor)

+allowed-tools: [Bash]          # the minimum the skill needs

-The skeleton note above is **verbatim and identical** across every skill (except `setup` — see

-below). Do not re-add ADR/finding/requirement citations to it.

+**Pattern A — adapter body** (the injected operative instructions):

-**Goal:** <one line — the outcome the agent is producing>.

+Full guidance: `spt how-to <name>` (or `spt <verb> --help`).

+**Pattern B — full-fat SKILL.md** (no skeleton note, no adapter body):

-- **No provenance preamble.** Do not open with "Delivered file-backed via the adapter `[strings]`…"

-  — the running agent does not need to know its own delivery mechanism.

-- **Self-contained.** Inline the operative steps; do **not** redirect to `spt how-to <topic>`. (A

-  verb's own `spt <verb> --help` is fine to cite as the live source of truth where the surface is

-- **Concise.** Commands, the must-do sequence, the reply/output shape — nothing else. Cut rationale,

-- **Keep `[<doc>->REQ-*]` HTML-comment tags** — they are traceability evidence; place them on/above the

+  <What it does.> Use when the user says "<trigger>", or when you need to <intent> yourself.

-## `description` rules (from write-a-skill)

-The `description` is the only thing the model sees when choosing a skill. Per

-`write-a-skill`: third person, **what it does** then **"Use when [triggers]"**, ≤1024 chars, no

-time-sensitive info, consistent terms. Route phrases reference **`/sptc:`** (the shipped plugin

-name; the `s/sptc/spt/` succession flip renames them later). The legacy `claude_skill_owl`

-descriptions are the basis for the trigger phrasing — adapt them, dropping legacy internals

+<The full operative instructions — the agent loads this directly.>

-`setup` is **self-contained on both surfaces**: it runs precisely when spt-core may be **absent**,

-so UPS-injection (which needs `spt adapter get-string`) can no-op. Its plugin SKILL.md therefore

-keeps the operative steps as the floor, and the adapter body mirrors them for the spt-present repair

-path. Tighten `setup` for concision, but do **not** reduce it to a stub.

+`setup` is **self-contained on both surfaces**: it runs precisely when spt-core may be absent, so

+injection can no-op. Its plugin SKILL.md keeps the operative steps as the floor; tighten it, never

+## `description` rules (from write-a-skill)

+Third person, **what it does** then **"Use when [triggers]"**, ≤1024 chars, no time-sensitive info,

+consistent terms. Route phrases reference **`/sptc:`** (the shipped plugin name; the `s/sptc/spt/`

+succession renames them later). Trigger phrasing is adapted from the legacy `claude_skill_owl`

+descriptions, dropping legacy internals (`$OWL`/`$LIVE`/`info.json`).

-- [ ] `description` is third-person, has "Use when …" triggers, routes via `/sptc:`.

-- [ ] Plugin SKILL.md uses the verbatim skeleton note + one what-it-does line (except `setup`).

-- [ ] Adapter body has no provenance preamble, is self-contained, ≤ ~40 lines (large surfaces aside).

-- [ ] `allowed-tools` is the minimum the skill needs.

-- [ ] Traceability `[<doc>->REQ-*]` tags preserved.

+- [ ] Body says what to do / what you'll see / how to act — no plugin or spt-core internals.

+- [ ] User-facing text carries no internal jargon; no untrue or stale steps.

+- [ ] Bringup skills (`ready`/`live`) are minimum-friction — fire and announce, no marker-gating.

+- [ ] Right pattern: agent-self-driven (`send`/`commune`/`signoff`) → full-fat, no injection.

+- [ ] Cites `spt how-to`/`--help` where one exists; `allowed-tools` is the minimum.

+- [ ] `[<doc>->REQ-*]` traceability tags preserved.

# Skill authoring

> How `/sptc:*` skills are written in this repo. Skills live on **two surfaces** (ADR-0001's
> thin-skeleton split). Keep both lean: the plugin file is a stub, the adapter file is the operative
> instructions. Bloat — provenance notes, requirement ids, ADR/finding citations, rationale the
> running agent doesn't need — does not belong in either.

## The two surfaces

| Surface | File | Role | Audience |
|---|---|---|---|
| **Plugin skeleton** | `plugin/sptc/skills/<name>/SKILL.md` | Thin stub shipped on cplugs. Frontmatter `description` is the routing surface; body is a fixed note. | The model deciding *which* skill to load. |
| **Adapter body** | `adapter/strings/skills/<name>.md` | The operative instructions, UPS-injected at invocation from the adapter `[strings]`. | The agent *running* the skill. |

The plugin skeleton carries **no operative steps** — those are injected from the adapter body at call
time. The two never duplicate instructions.

## Plugin SKILL.md template

```md
---
name: <name>
description: |
  <One sentence: what it does.> Use when the user says "<trigger>", "<trigger>", or
  wants to <intent>.
argument-hint: "<args>"        # omit if none
allowed-tools: [Bash]          # the minimum the skill needs (Bash/Read/Write/Monitor)
---

# /sptc:<name>

> **Skeleton — thin by design.** Operative instructions for this skill are delivered by the
> `sptc` adapter at invocation time. Look out for the UserPromptSubmit additionalContext.
>
> **Operative.** If injection ever no-ops (spt absent / adapter unregistered), check
> SPT's installation status using the skill `sptc:setup`. Otherwise, avoid additional steps.

<One line naming what the skill does.>
```

The skeleton note above is **verbatim and identical** across every skill (except `setup` — see
below). Do not re-add ADR/finding/requirement citations to it.

## Adapter body template

```md
# /sptc:<name> — operative instructions

**Goal:** <one line — the outcome the agent is producing>.

**Do this:**

1. <step>
2. <step>
<!-- [<doc>->REQ-NAME] -->   # keep the traceability tag if this body is doc evidence
```

Rules for the body:

- **No provenance preamble.** Do not open with "Delivered file-backed via the adapter `[strings]`…"
  — the running agent does not need to know its own delivery mechanism.
- **Self-contained.** Inline the operative steps; do **not** redirect to `spt how-to <topic>`. (A
  verb's own `spt <verb> --help` is fine to cite as the live source of truth where the surface is
  large, e.g. `subnet`.)
- **Concise.** Commands, the must-do sequence, the reply/output shape — nothing else. Cut rationale,
  repeated warnings, and history.
- **Keep `[<doc>->REQ-*]` HTML-comment tags** — they are traceability evidence; place them on/above the
  real evidence.

## `description` rules (from write-a-skill)

The `description` is the only thing the model sees when choosing a skill. Per
`write-a-skill`: third person, **what it does** then **"Use when [triggers]"**, ≤1024 chars, no
time-sensitive info, consistent terms. Route phrases reference **`/sptc:`** (the shipped plugin
name; the `s/sptc/spt/` succession flip renames them later). The legacy `claude_skill_owl`
descriptions are the basis for the trigger phrasing — adapt them, dropping legacy internals
(`$OWL`/`$LIVE`/`info.json`).

## The `setup` exception

`setup` is **self-contained on both surfaces**: it runs precisely when spt-core may be **absent**,
so UPS-injection (which needs `spt adapter get-string`) can no-op. Its plugin SKILL.md therefore
keeps the operative steps as the floor, and the adapter body mirrors them for the spt-present repair
path. Tighten `setup` for concision, but do **not** reduce it to a stub.

## Checklist

- [ ] `description` is third-person, has "Use when …" triggers, routes via `/sptc:`.
- [ ] Plugin SKILL.md uses the verbatim skeleton note + one what-it-does line (except `setup`).
- [ ] Adapter body has no provenance preamble, is self-contained, ≤ ~40 lines (large surfaces aside).
- [ ] `allowed-tools` is the minimum the skill needs.
- [ ] Traceability `[<doc>->REQ-*]` tags preserved.


All files done. Running gates.


Bash

command -v traceable-reqs >/dev/null 2>&1 && { traceable-reqs check >/tmp/tr.txt 2>&1; echo "traceable exit=$?"; grep -iE 'parse_error|FAIL|missing|invalid|orphan' /tmp/tr.txt | head; }; echo "=== unit sweep ==="; pass=0; for t in tests/*.sh; do if sh "$t" >/dev/null 2>&1; then pass=$((pass+1)); else echo "FAIL $t"; sh "$t" 2>&1 | grep -iE 'fail|missing|skill' | head -4; fi; done; echo "unit: $pass/9 ok"

Run traceable + unit sweep after full-fat refactor


traceable exit=0
       Deterministic gates (shell-syntax + unit tests + traceable-reqs check + manifest-schema + docs-drift), each a binary pass/fail, runnable as one command
=== unit sweep ===
unit: 9/9 ok


Green (traceable 0, 9/9 unit incl skeleton-validate). Checking for any dangling references to the deleted adapter files:


echo "=== refs to deleted adapter bodies? ==="; grep -rnE 'skills/(send|commune|signoff)\.md|skills\.(send|commune|signoff)' --include='*.toml' --include='*.sh' --include='*.md' . 2>/dev/null | grep -v '/target/' | grep -vE 'plugin/sptc/skills/(send|commune|signoff)/SKILL' || echo "NONE (clean)"
echo "=== final skill inventory ==="; echo "plugin SKILLs:"; ls plugin/sptc/skills/; echo "adapter bodies:"; ls adapter/strings/skills/

Check dangling refs to deleted files + final inventory


=== refs to deleted adapter bodies? ===
./docs/PARITY.md:19:| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
./docs/PARITY.md:21:| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
./docs/PARITY.md:23:| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |
=== final skill inventory ===
plugin SKILLs:
commune
force-stop
list-agents
live
ready
send
setup
signoff
subnet
version
adapter bodies:
force-stop.md
list-agents.md
live.md
ready.md
setup.md
subnet.md
version.md


`PARITY.md` rows still point at the deleted adapter bodies. Updating to reflect full-fat delivery:


19	| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
20	| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
21	| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
22	| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
23	| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |

| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |


Edit

| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |

| `/commune` | KEEP | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` (injected) | ✅ parity |
| `/send` | KEEP | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` (injected) | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |


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

| `/commune` | KEEP | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` (injected) | ✅ parity |
| `/send` | KEEP | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` (injected) | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |

# Parity audit — claude-spt vs the legacy owl surface

> The reconciliation record: the adapter's operative surface measured against the **LOCKED
> parity-trim** in `SCOPE.md` (KEEP / ADD / DROP / TRANSFORM). This is the **"proven parity"**
> gate the `sptc`→`spt` succession flip depends on (see `docs/RELEASE-RUNBOOK.md` +
> `docs/KNOWN-HAZARDS.md`): the name flip happens only once this audit shows no unresolved
> divergence AND legacy owl is retired. Audited 2026-06-15 against published spt v0.7.2.
>
> Public-surface-only binds (AGENTS.md): a capability missing from the published `spt` surface is
> a **finding**, never a reason to reach into spt-core source.

<!-- [doc->REQ-PARITY-AUDIT] -->

## Disposition matrix

| Surface | SCOPE bucket | Adapter today | Status |
|---|---|---|---|
| `/live` | KEEP | operative — `skills/live.md` (base-manifest `[session.psyche_init]` + bare `api listen` + Monitor relay; Option A, no `:live` profile) | ✅ parity |
| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |
| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |
| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |
| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |
| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |
| `/force-stop` | KEEP (topology-aware) | operative — `skills/force-stop.md` | ✅ parity |
| `/spt:setup` | ADD | operative — `skills/setup.md` | ✅ parity |
| `/spt:version` | ADD | operative — `skills/version.md` | ✅ parity |
| profiles + strings + hints | ADD | wired (`:ccs`, `[strings]`, `[[hints]]`; live-capability is in base, not a profile) | ✅ parity |
| subnet skill (status/create/show-code/join) | ADD (LOCKED) | operative — `skills/subnet.md` (wraps `spt subnet`) | ✅ parity (REQ-SKILL-SUBNET) |
| ccs profile (`claude-spt:ccs`) | ADD (LOCKED) | operative — `[profiles.ccs]` overlay + CLAUDE_CONFIG_DIR-aware extractor | ✅ parity (REQ-CCS-PROFILES) |
| `/whoami` skill | **DROP** (core `spt whoami` stays → M12 `endpoint list` alias) | removed (was carry-over) | ✅ dropped |
| `/new-alarm` | accepted gap | dead stub, no manifest entry | ⏸ DEFER + remove stub |
| `/fork` | DROP | absent | ✅ confirmed absent |
| `/amend-signoff` | DROP | absent | ✅ confirmed absent |
| `/revive` | DROP (daemon owns liveness) | absent | ✅ confirmed absent |
| `/clear-psyche` | DROP (= delete+recreate endpoint) | absent | ✅ confirmed absent |

TRANSFORM bucket (already realised, not skills): capsule→spt-hosted+`cc` (`[session.self]` +
`shortcut_basename="cc"`); echo-commune→`[digest]` extractor (`claude-spt-digest`); psyche-sync→
`/spt:setup`; doctor→setup-verify; working-perches→`api worker-start/stop` hooks. ✅

## Open divergences and dispositions

### subnet skills — LOCKED-ADD gap → BUILD
The published surface carries the verbs (`spt subnet {create,join,show-code,status,…}`), and v1
topology scope is **mandatory** (operator vetoed harness-hosted-only — cross-subnet/PTY proof is
spt-core's central value prop, SCOPE). So this is a real gap to fill, not defer. → `REQ-SKILL-SUBNET`.

### ccs profile — LOCKED-ADD gap → BUILT ✅
<!-- [doc->REQ-CCS-PROFILES] -->
SCOPE §ccs (LOCKED): ccs ships as a **profile template** under the adapter that retargets the spawn
command template through `ccs`, with ccs's relocated log dir honored. Templates only — the user
supplies their own ccs config/keys. **Operator ruling (2026-06-15):** the shipped profile is
`claude-spt:ccs` invoking **bare `ccs`** (the account set via `ccs auth default <name>`), NOT
`glm`/`kimi` — those were SCOPE *examples*; this profile IS the worked example of adapter-profile
authoring **and** the operator's own SPT-ecosystem hook. Built + validated against the known-good
sister project **claude_skill_owl**:
- **Spawn seam.** `[profiles.ccs.session.self].command = "ccs"` leaf-replaces the base `claude`.
  owl proves ccs is a drop-in for the `claude` binary on the same argv (`live/wrapper/claude.rs`
  Tier-2 PULSE recovery latches `cli_binary = "ccs"`); `SPT_ENDPOINT_ID` rides inherited env through
  the wrapper unchanged.
- **Log-dir seam → in the extractor, not a manifest leaf.** ccs relocates CC's whole state tree
  (incl. `projects/`) via the **`CLAUDE_CONFIG_DIR`** env var (`~/.ccs/instances/<account>/.claude`),
  a per-account runtime value with no static catalog path. SCOPE's "per-profile `~/.ccs` log dir"
  is therefore honored **in `claude-spt-digest`** (dir-locate branch prefers `$CLAUDE_CONFIG_DIR/
  projects` over the `--in` root) — the owl-validated `owlery::claude_projects_root` pattern. The
  base `[digest]` config thus serves base **and** ccs sessions transparently; no `[profiles.ccs.
  digest]` leaf. This env-aware resolver is REQ-CCS-PROFILES's `impl`/`unit` evidence (a `.toml`
  profile leaf alone does not register as `impl`).
- **Finding (refines SCOPE's model).** SCOPE conflated ccs *profiles* (glm/kimi = API-provider
  env-swap, NO transcript relocation) with ccs *accounts* (isolated instances that DO relocate via
  `CLAUDE_CONFIG_DIR`). The operator runs the account/default path, which relocates — so the digest
  seam is real, but its mechanism is `CLAUDE_CONFIG_DIR`, not a fixed `~/.ccs` path. → `REQ-CCS-PROFILES`.

### `/whoami` skill — DROP (honoring LOCKED)
SCOPE DROPs the whoami **skill**; core `spt whoami` stays in the hot path (and becomes the
`endpoint list` alias in M12). The adapter currently still ships a whoami skill (a carry-over from
an early slice). Operator confirmed the drop 2026-06-15. Action: remove `[strings.skills].whoami` +
the file body + the plugin stub; redirect the "who am i" hint to core `spt whoami`. No REQ rides
whoami evidence (the file-backed-string + UPS-injection int examples repoint to `skills.ready`;
the hook-parser unit examples repoint to a surviving skill name). Core `spt whoami` calls in the
hooks (`session-start.sh`, `_common.sh`) are the CLI, not the skill — they STAY.

### `/new-alarm` — ACCEPTED GAP (deferred-pending-arrangement-system)
**Not missing — deliberately deferred.** doyle triage (2026-06-15): spt-core carries only the
alarm *event shape* (spt-proto) + relay handling (psyrelay.rs) + a test fixture; the durable
"fire-at-target-time" timer still lives in legacy owl in-memory, and an in-daemon durable scheduler
is a deliberate deferral (DEFERRED.md:43 — deferred because the daemon had no
one-shot consumer; building pre-consumer = untested dead code). The **operator has a superseding
"arrangement" system** in mind that would replace the alarm port. Disposition: **do not build an
adapter-side stopgap timer** (throwaway vs the coming model); **no REQ minted**; **remove the dead
`/sptc:new-alarm` stub** from the published surface (deferred ≠ shipping a no-op skill). doyle loops
me when the arrangement design surfaces.

## Parity verdict
**PROVEN (adapter surface).** All three open divergences are closed this slice: subnet skill added
(`skills/subnet.md`, REQ-SKILL-SUBNET), ccs profile added (`[profiles.ccs]` + CLAUDE_CONFIG_DIR-aware
extractor, REQ-CCS-PROFILES), whoami skill dropped. The full KEEP/ADD set is ✅ and the DROP set is
confirmed absent. `/new-alarm` is an accepted, documented gap (deferred-pending-arrangement-system)
and does **not** block the verdict.

The `sptc`→`spt` succession flip remains separately gated (NOT unblocked by this verdict alone):
it also requires **legacy owl retired** (KNOWN-HAZARDS / RELEASE-RUNBOOK) and rides the live-relay
int held for spt-core M11 counter-15. This audit clears only the parity precondition.

-| `/commune` | KEEP | operative — `skills/commune.md` (file-drop) | ✅ parity |

-| `/ready` | KEEP | operative — `skills/ready.md` | ✅ parity |

-| `/send` | KEEP | operative — `skills/send.md` | ✅ parity |

-| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` | ✅ parity |

-| `/signoff` | KEEP (harness-hosted v1) | operative — `skills/signoff.md` | ✅ parity |

+| `/commune` | KEEP | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |

+| `/ready` | KEEP | operative — `skills/ready.md` (injected) | ✅ parity |

+| `/send` | KEEP | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |

+| `/list-agents` | KEEP (→ `spt endpoint list`) | operative — `skills/list-agents.md` (injected) | ✅ parity |

+| `/signoff` | KEEP (harness-hosted v1) | operative — full-fat plugin SKILL (agent-self-driven, not injected) | ✅ parity |


Re-running the doc-affecting gate, then committing:


Bash

sh tests/docs-gen.sh >/dev/null 2>&1 && echo "ok docs-gen" || echo "FAIL docs-gen"
git add -A
git status --short
git commit -q -F - <<'EOF'
docs(skills): strip internals, full-fat send/commune/signoff, how-to anchors

Carry the live.md direction across every skill: a body says what to do, what the
agent will see, and how to act — never how the plugin/spt-core implements it.

- **Strip internals + jargon.** Cut adapter-resolution, perch-state stamping,
  daemon/poll-vs-relay mechanics from bodies; keep internal terms out of any
  user-facing text. Kept the useful legacy tells (Monitor `« spt event »`, the
  <EVENT type=...> catalog). Bringup (ready/live) is now minimum-friction —
  fire the listener and announce, no marker-gating (the user is waiting).
- **send / commune / signoff → full-fat plugin SKILL.md, injection dropped.** A
  live agent uses these reactively, without the user typing /sptc:<skill>, so
  UPS-injection (fires only on the slash-command) can't deliver them. Removed
  their [strings.skills] manifest entries + deleted the adapter aux bodies; the
  hook no-ops on the absent key. registration-int drops the commune resolve
  assertion accordingly.
- **Self-drive anchors.** ready/subnet/list-agents/version/force-stop bodies now
  cite `spt how-to <topic>` (ready/send/subnet/live ship one) or `spt … --help`.
- Revised docs/SKILL-AUTHORING.md: the two delivery patterns (injected vs
  full-fat), the no-internals/no-jargon/correctness/min-friction rules. Refreshed
  PARITY.md rows + a stale REQ-SKILL-SUBNET note (how-to subnet now exists).

Gates: traceable-reqs check green; 9/9 unit; docs-drift OK.

Co-authored by: perri
EOF
git log -1 --format='%h %s' && git push 2>&1 | tail -2

Commit + push the skill internals/full-fat refactor


ok docs-gen
M  adapter/claude-spt.toml
D  adapter/strings/skills/commune.md
M  adapter/strings/skills/force-stop.md
M  adapter/strings/skills/list-agents.md
M  adapter/strings/skills/ready.md
D  adapter/strings/skills/send.md
D  adapter/strings/skills/signoff.md
M  adapter/strings/skills/subnet.md
M  adapter/strings/skills/version.md
M  ci/manifest/registration-int.sh
M  docs/PARITY.md
M  docs/SKILL-AUTHORING.md
M  plugin/sptc/skills/commune/SKILL.md
M  plugin/sptc/skills/send/SKILL.md
M  plugin/sptc/skills/signoff/SKILL.md
M  traceable-reqs.toml
28f3d77 docs(skills): strip internals, full-fat send/commune/signoff, how-to anchors
To https://github.com/SaberMage/spt-claude-code.git
   4aa9284..28f3d77  main -> main


Done. Pushed `28f3d77`. We were aligned — executed the whole ripple.

## Shipped
- **`send` / `commune` / `signoff` → full-fat plugin SKILLs, injection dropped.** Operative bodies now live in the SKILL.md (always model-loadable, so a live agent self-drives them); removed their `[strings.skills]` manifest entries + deleted the 3 adapter aux bodies; `registration-int` drops the commune resolve assertion. Hook no-ops on the absent key (verified).
- **Lean injected bodies** (ready/subnet/list-agents/version/force-stop) — stripped plugin/spt-core internals, cite `spt how-to <topic>` (ready/send/subnet/live ship one) or `spt … --help` as self-drive anchors. **ready** bringup now minimum-friction: fire the Monitor `« spt event »` listener and announce, no marker-gating.
- **live** left as your reference; **setup** left (self-contained floor).
- **`docs/SKILL-AUTHORING.md`** revised — the two delivery patterns (injected vs full-fat) + the throughline rules (no internals, no user-facing jargon, correctness, min-friction bringup, legacy tells).
- Fixed PARITY rows + a stale `REQ-SKILL-SUBNET` note (`spt how-to subnet` ships now).

Gates: traceable ✅ · 9/9 unit (incl skeleton-validate) ✅ · docs-drift ✅.

**Ship caveat (unchanged):** these bodies bundle into `dist/*.spt` — the live v0.4.0 assets still carry the prior copy; the lean set reaches users on the next `.spt` rebuild. Holding per "more changes to come." Ready for the next.


<command-message>spt:signoff</command-message>
<command-name>/spt:signoff</command-name>


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

# /spt:signoff

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

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

## Flow

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

   > **Output truncation.** If the Bash tool reports `Output too large (N KB)` and saves the full result to a `tool-results/<id>.txt` file, use the `Read` tool on that saved path to access the full content.

2. **Diff your knowledge against this fresh download** — only *what's missing from it* belongs in the FINAL COMMUNE body (work completed, decisions made, intentions formed). Anything already absorbed is not.

3. **Sign off** -- use the **Write tool** to create
   `.claude/{your-id}-signoff.md`. Do not use Bash/heredoc -- Write is the
   canonical path: no shell escaping, no `EOF` collision risk, and Write
   auto-creates the `.claude/` parent directory.

   - **Plain signoff** (no final commune body): Write the file with an empty
     string as its contents.
   - **Signoff with a FINAL COMMUNE body**: Write the file with the final
     commune body as its contents — the body MUST be wrapped in the same
     two-slice envelope as a regular commune (see `## Two-slice body shape`
	 below, or `/spt:commune` for the full teaching).

	 Your live agent listener detects the file, notifies your Psyche wrapper,
	 prints `STOP:{your-id} (signoff dropped)`, and exits cleanly. The Psyche
	 wrapper independently consumes the file and tears itself down.

   **Offline path (no live listener):** If no session is currently live for
   your agent, the dropped `.claude/{your-id}-signoff.md` simply persists on
   disk. The next time anyone runs `$LIVE psyche-download <your-id>`, that
   signoff body reflected under a `## Pending Signoff (written {mtime})`
   header. This is the way to record post-session-end context updates.

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

When the FINAL-COMMUNE body is non-empty (the second sub-bullet of Step 3 above), it follows the same Phase 25 D-10/D-11 two-slice envelope contract as a regular commune body:

- **In a tracked project** (detected via the `project="..."` attribute on the `<current ... />` tag at the top of `$LIVE psyche-download` output): emit `<live-context>...</live-context>` plus either `<project-context>...</project-context>` (with content) or `<project-context></project-context>` (empty body — "in-project but quiet" signal per D-25.1-02).
- **Outside any tracked project** (no payload at all from `psyche-download` — no `<current/>` tag, no `project="..."` attribute): emit ONLY `<live-context>...</live-context>`. Do NOT emit an empty `<project-context>` envelope.
- **Plain signoff** (empty file body, first sub-bullet of Step 3): the two-slice contract does NOT apply — the file is literally empty.

See `/spt:commune` → `## Two-slice body shape (Phase 25 D-10/D-11)` for the full teaching with three worked examples. See `psyche.md` §`<output_envelope>` for the canonical per-slice content taxonomy. See `psyche.md` §`<absorption>` (25.3-02) for the inbound-merge + re-emit rules — Psyche absorbs and re-emits these envelopes; on the FINAL_COMMUNE the wrapper's last Psyche session emits the two-slice body for the same routing pass.

**Routing: emit this envelope; SPT routes it to disk (Phase 25.3-02).** You write the two-slice body in the signoff file; the wrapper feeds it through Psyche's final session, and SPT (the Rust runtime) routes each emitted slice to its destination file. The `<live-context>` body lands at `agents/{your-id}/live_context.md`. The `<project-context>` body lands at `projects/<resolved_project>/{your-id}.md`, where `<resolved_project>` is resolved BY SPT via Self's perch `info.json` `cwd` field (25.3-01 Defect B2 fix). You do NOT name the project (Option A no-name shape locked); you do NOT know the filesystem path. NOT "Psyche writes the file." NOT "the agent persists to the project file." You emit; SPT routes.

**Signoff vs Stop:**
- **`/spt:signoff`** -- Graceful: INIT_SIGNOFF (with optional final commune) to Psyche first. Use for normal session end.
- **`/spt:force-stop`** -- Force: kills immediately. Use when unresponsive or need immediate kill.

## Envelope shape (Phase 23 v1.8)

As of v1.8 Phase 23, the init_signoff envelope carries a 5-field stamp (`machine`, `project`, `branch`, `head_sha`, `head_subject`) alongside `timestamp`.

```xml
<EVENT type="init_signoff" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="claude_skill_owl" branch="main" head_sha="abc123..." head_subject="subject line">Signoff initiated by Self...</EVENT>
```

With an optional final-commune body (when the signoff drop file is non-empty):

```xml
<EVENT type="init_signoff" timestamp="2026-05-20T06:00:00-07:00" machine="hostname" project="claude_skill_owl" branch="main" head_sha="abc123..." head_subject="subject line">FINAL COMMUNE: ...body... | Signoff initiated by Self...</EVENT>
```

Outside a git repo (D-11), `branch`, `head_sha`, and `head_subject` are omitted entirely. The wrapper-side `type="init_signoff"` predicate is matched case-insensitively and ignores unknown attrs, so the new stamp fields are forward-compatible with older receivers. For drift detection on resume, see the `live` skill.

The `body` placeholder in the INIT_SIGNOFF examples above (when a FINAL COMMUNE is present) is the two-slice body — see `## Two-slice body shape (Phase 25 D-10/D-11)` above; the Phase 23 EVENT envelope and the Phase 25 two-slice body nest, with EVENT wrapping the two-slice body. `$LIVE signoff` composes the EVENT envelope automatically — you NEVER write `<EVENT>` tags.


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


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


Legacy `/spt:signoff` — graceful end of my live session. Let me resolve my identity and pull current Psyche context first.


echo "=== whoami ==="; spt whoami 2>&1 | head -3; echo "=== \$LIVE resolves? ==="; echo "${LIVE:-<unset>}"; echo "=== OWL_SESSION_ID ==="; echo "${OWL_SESSION_ID:-<unset>}"

Resolve live identity + $LIVE env


=== whoami ===
SUBNET SPT_DEV
  wall-a  HFENDULEAM (14efb80c…)  Active
ENDPOINTS:1
=== $LIVE resolves? ===
C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe live
=== OWL_SESSION_ID ===
a6fa2219-fa60-4b97-a2e9-de64d1e51a44


`$LIVE` = legacy owl. Identity not cleanly printed — confirming I'm `perri` (per memory, Gen 8) and pulling current context.


C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe list 2>&1 | head -20

List owl agents to confirm self id


⚠ (3 orphan directories present — run `owl doctor --fix` to GC)
✓ ACTIVE:deployah
✓   - ACTIVE:deployah-psyche
✓ ACTIVE:doyle
✓   - ACTIVE:doyle-psyche
✓ ACTIVE:perri
✓   - ACTIVE:perri-psyche
✓ ACTIVE:todlando
✓   - ACTIVE:todlando-psyche


`perri` is active with its Psyche. I'm perri. Downloading current Psyche context to diff before composing the final commune.


C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe live psyche-download perri 2>&1 | head -80

Download perri's current psyche context


<current machine="HFENDULEAM" project="spt-claude-code" branch="main" head_sha="28f3d779fb6c5725a59e445d4a6faab660661090" head_subject="docs(skills): strip internals, full-fat send/commune/signoff, how-to anc…" commits_since="0" commits_unpulled="0"/>
**Generation 8 active. v0.4.0 RELEASED** (skill authoring rewrite @ 4aa9284). Follow-up cleanup SHIPPED (28f3d77):

**Adapter bodies moved + injection dropped.** send/commune/signoff now full-fat plugin SKILLs (operative bodies live in SKILL.md, always model-loadable). Deleted 3 aux adapter bodies from manifest + registration-int drops commune assertion. Hook no-ops on absent key.

**Lean injected bodies** (ready/subnet/list-agents/version/force-stop) stripped of internals; anchor to `spt how-to <topic>` or `--help`. Ready bringup minimum-friction: Monitor listener + announce. Live/setup preserved.

**docs/SKILL-AUTHORING.md** revised: two delivery patterns (injected vs full-fat) + throughline rules. PARITY + REQ-SKILL-SUBNET note updated.

Gates: traceable ✅ · 9/9 unit ✅ · docs-drift ✅.

**Ship note:** v0.4.0 assets still carry prior bloat; lean reaches users next `.spt` rebuild. Holding pending more changes.

Deferred: F-008/F-011/F-004, setup #8/#9, sptc→spt gating (parity ✅, owl-retire pending).<project-context-resolved name="spt-claude-code"/>
Post-v0.4.0 cleanup complete. Skill authoring infra fully gated. Buildable for REQ-SKILL-VERSION activation. No new blockers. Ready for next phase.


Psyche context is fully current — it already reflects the latest commit (28f3d77), the full-fat refactor, gates, ship note, and deferred items. **Nothing material is missing**, so per the skill this is a plain signoff (empty body), not a final commune. Verifying the tree is clean first.


git status --short && echo "---" && git log -1 --format='%h %s' && echo "unpushed:" && git log origin/main..HEAD --oneline | head

Confirm clean working tree + all pushed before signoff


---
28f3d77 docs(skills): strip internals, full-fat send/commune/signoff, how-to anchors
unpushed:


Clean tree, all pushed, Psyche current. Writing the plain signoff drop (empty body, per the skill — Write, not Bash):


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


<task-notification>
<task-id>bpcwxkxh7</task-id>
<tool-use-id>toolu_01Qw7Y2wRC8q26NWizVPrfkM</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\4b08b6c6-5f2a-42f7-a56d-cf3d9a866de7\tasks\bpcwxkxh7.output</output-file>
<status>completed</status>
<summary>Monitor "« spt event »" stream ended</summary>
</task-notification>


Signoff dropped: `.claude/perri-signoff.md` (empty = plain). Confirming teardown.


ls .claude/perri-signoff.md 2>&1; echo "--- owl roster ---"; C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe list 2>&1 | grep -i perri

Check signoff consumption + perri teardown state


ls: cannot access '.claude/perri-signoff.md': No such file or directory
--- owl roster ---
⚠ ACTIVE:perri-psyche [orphaned]


Prompt:
<task-notification>
<task-id>bpcwxkxh7</task-id>
<tool-use-id>toolu_01Qw7Y2wRC8q26NWizVPrfkM</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-claude-code\4b08b6c6-5f2a-42f7-a56d-cf3d9a866de7\tasks\bpcwxkxh7.output</output-file>
<status>completed</status>
<summary>Monitor "« spt event »" stream ended</summary>
</task-notification>


Signed off. ✅

- Signoff file **consumed** (gone) → the live listener detected it.
- Monitor `« spt event »` stream **ended** (clean STOP:perri exit).
- `perri` off the roster; `perri-psyche` now orphaned (wrapper tearing down / GC next `owl doctor`).

**perri offline.** Plain signoff — Psyche was already current (had everything through `28f3d77`), so no final commune needed. Working tree clean, all pushed.

Session arc this run: v0.4.0 shipped (deep removal · psyche sandbox · skip-perms · ADR-leak wipe) → skill rewrite → internals-strip + full-fat send/commune/signoff. Next pickup: REQ-SKILL-VERSION or the held `.spt` rebuild. 👋


<local-command-caveat>Caveat: The messages below were generated by the user while running local commands. DO NOT respond to these messages or otherwise consider them in your response unless the user explicitly asks you to.</local-command-caveat>


<command-name>/exit</command-name>
            <command-message>exit</command-message>
            <command-args></command-args>


<local-command-stdout>Goodbye!</local-command-stdout>