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

 requiring a manual `.spt` re-extract to recover |

> **F-012 (NOT logged as spt-core)**

 requiring a manual `.spt` re-extract to recover |
| F-020 | 2026-06-24 | **RESOLVED-PENDING — frozen Tier-1 contract, ships v0.15.0 W5** (doyle ruled + grounded; verb committed by todlando @20bfc1f). spt-core publishes NO `spt api` verb to pull resume/psyche context at SessionStart — `download_psyche_context` (resume.rs:88) is exported-but-unexposed (ZERO `ApiCmd` callers); `resume.rs:9` documents the "adapter pulls it in its SessionStart hook" intent but no verb was ever published. **Consequence: claude-spt live agents rehydrate NO durable context on resume today** — SessionStart only runs `api boundary` (session-id rotation, output discarded) + the identity brief. (The `<psyche-context>` seen on a live session is LEGACY owl's hook — the parity precedent, not claude-spt.) **Fix (v0.15.0 W5, FROZEN):** new `spt api psyche-download <id> [--session-id <sid>]` → stdout `<live-role>`/`<live-context>`/`<project-context-resolved/>`/`<project-context>` + appended `<pending-commune>`/`<pending-signoff>` (raw not-yet-synthesized drop, `!!checkpoint!!` trigger stripped core-side), `NO-CONTEXT:<id>` on stderr when empty; race-free + self-clearing. **claude-spt action:** wire SessionStart to call+inject (`REQ-DIST-RESUME-CONTEXT`); validate E2E on doyle's W5 ping. | No published verb lets an adapter's SessionStart hook pull resume context — the documented adapter-pull path was never given a CLI verb, so claude-spt cannot rehydrate a live agent's durable context on resume at all (foundational parity gap, surfaced building checkpoint communes) |

> **F-021 / F-022 (NOT spt-core findings — claude-spt parity items, tracked as REQs; surfaced during the 2026-06-24 checkpoint-commune grill)** — (F-021) claude-spt polls only on `UserPromptSubmit` (between turns); legacy spt also polls+injects on **PreToolUse** = the mid-turn half of live-agent reachability (the main path by which a live agent receives a message WHILE working). `api poll` already exists on the public surface → adapter wiring, not an spt-core gap → **`REQ-DIST-PRETOOL-POLL`**. (F-022) subagent worker-perch reachability: wiring is present (`subagent-start.sh` → `api worker-start`; `hostable_types` includes `Worker`) but runtime `spt send`-to-a-worker is UNVERIFIED → validation item **`REQ-DIST-WORKER-PERCH-REACH`**. Neither is an spt-core public-surface gap.

> **F-012 (NOT logged as spt-core)**

| F-016 | 2026-06-22 | **RESOLVED — both fixes SHIPPED** (doyle, broker.rs-confirmed). The published `[message-idle-translation-binary]` doc omitted `{commit}` from the stdout vocabulary AND its degenerate baseline `{text}{key:enter}` would itself FAULT. `{commit:true}` is the MANDATORY inject-sequence terminator (`run_inject_worker` broker.rs:1075-1090; no-commit → 5s `INJECT_COMMIT_DEADLINE` FAULT, broker.rs:151-169; reference `{text}{key:enter}{commit:true}` translation.rs:74-78). **(i) adapter binary appends trailing `{commit:true}` — DONE (cc-spt-idle-translate, 11 tests); (ii) contract republished + LIVE on gh-pages 2026-06-22 — `{commit}` in the vocabulary + commit-deadline/inject-floor semantics + corrected degenerate; re-verified by re-fetch.** Residual: end-to-end `translate-proof` re-confirm pending v0.13.1 (counter 28+, runner-load delay, not code). Caught by the blind-build BEFORE a live FAULT | The published harness-contract documented the idle-translation binary's stdout vocabulary as `{key}`/`{delay_ms}`/`{text}` only, with a `{text}{key:enter}` degenerate example — but the broker requires a trailing `{commit:true}` terminator or every delivery FAULTs at the 5s commit deadline. A harness author building from the public surface alone ships a binary that faults live |

| F-017 | 2026-06-22 | **RESOLVED-SHIPPED (spt-core v0.14.0, counter 30) + VALIDATED on real claude-spt (perri, 2026-06-23)**. v0.14.0's endpoint-creation-flow milestone (REQ-RUN-MULTISUBNET-HOME, ADRs 0026/0027) closed it: `spt endpoint run` now HOMES at creation — multi-subnet node w/o `--subnet` → instant **`MULTI_SUBNET_HOME`** refuse + subnet list (0.058s, NOT the old ~25s silent `ENDPOINT_RUN_ONLINE_TIMEOUT`); `--subnet <name>` → homes + harness binds (UNBOUND → online), no HOME_REFUSED. Bonus W-state confirmed: the hollow **UNBOUND** pre-bind row shows in `endpoint list` between spawn and bind. My W6 seed `ci/subnet/multi-subnet-bringup-int.sh` FLIPPED from "gap reproduces" → "fix confirmed" (cases 1+2 green; case 3 E2E `--subnet` bringup homed+bound on-node). The home-assignment POLICY (`api bind` w/o home still HOME_REFUSEs) is unchanged + correct — it's WHY endpoint run needed `--subnet`. No claude-spt change: the adapter manifest/hooks were correct as-is throughout (no SPT_DEV workaround existed to revert — grep of adapter + plugin + my own bringup all clean). Originally reported QUEUED (ADRs 0026/0027, v0.13.3+). NOT a version regression — established home-assignment policy that only BITES once a node holds 2+ subnets (real trigger: this node gaining BIGNET as a 2nd subnet). On a multi-subnet node a NEW-endpoint bind must be told its home: `spt api bind <id> --set-session-id <sid>` → **`HOME_REFUSED: this node holds 2 subnets … pass --subnet`**; the same bind WITH `--subnet <name>` → BOUND. The spt-HOSTED bringup cannot supply it: `spt endpoint run` has **no `--subnet` flag** and the broker injects `$SPT_ENDPOINT_ID` but **no home-subnet**, so the plugin SessionStart `bind)` branch fires `api bind … --set-session-id …` (no `--subnet`) → HOME_REFUSED → the hook's `\|\| true` swallows it → zero perch → server `ENDPOINT_RUN_ONLINE_TIMEOUT` (~25s). Pinned HOME_REFUSED on 0.11.0/0.12.0/0.12.1/0.13.1/**0.13.2** (policy, not a code bug). **doyle's fix (ADRs 0026/0027): broker injects `$SPT_ENDPOINT_SUBNET` + `api bind` env-fallback → the SHIPPED hooks stay UNCHANGED.** Adapter manifest/hooks are CORRECT as-is; fix is spt-core-side. **Regression seed COMMITTED: `ci/subnet/multi-subnet-bringup-int.sh`** — 3 cases (Case 2 no-subnet→HOME_REFUSED, Case 3 --subnet→BOUND, Case 1 E2E endpoint-run→no-perch/timeout, gated SPTC_ACCEPTANCE=1); multi-subnet-gated (single-subnet auto-homes + hides it). Operator earlier DECLINED a temp `--subnet` hook band-aid (wait for the real fix). | On a node holding 2+ subnets, every NEW-endpoint bind needs `--subnet <home>` (else HOME_REFUSED), but the spt-hosted `endpoint run` path provides no way to pass it — the broker injects the id but not the home subnet, so the SessionStart self-bind HOME_REFUSEs and the spt-hosted bringup silently yields zero perch (ONLINE_TIMEOUT). Single-subnet nodes auto-home and never see it |

| F-018 | 2026-06-22 | **REPORTED to doyle (perri) — destructive footgun, os-2 family (F-011 sibling)**. Surfaced while constructing the F-015 brick repro on v0.13.2. `spt adapter add --github <user/repo>` on an **already-registered, already-extracted gh_release Pointer adapter** is destructive-then-cryptic: it git-clones the SOURCE repo over the extracted install dir (wiping the root `manifest.toml` + the 3 runtime `.exe` binaries the Pointer resolves), then the post-add first-`[update]` conduct fails bare **`ADAPTER_ADD_FAIL: io: ... (os error 2)`** — leaving the adapter `manifest-not-present` (same broken state as F-011, but operator-induced). No confirm prompt, no "already registered — did you mean `adapter update`?" guard, no rollback. Recovered by extracting `dist/adapter-windows-x86_64.spt` over the install dir (manifest+binaries restored, version 0.6.0, translate-proof OK). **Needs (doyle):** for an already-registered gh_release adapter, `add --github` should refuse/route to `adapter update` (or at least not wipe the working install before the avenue succeeds), and the `os error 2` should carry path+cause (the F-011 diagnostic ask, same resolver). | Re-running `spt adapter add --github user/repo` to "reinstall" a healthy gh_release Pointer adapter silently destroys the working install (clones raw source over the extracted runtime, drops manifest+binaries) and fails `os error 2`, requiring a manual `.spt` re-extract to recover |

+| F-020 | 2026-06-24 | **RESOLVED-PENDING — frozen Tier-1 contract, ships v0.15.0 W5** (doyle ruled + grounded; verb committed by todlando @20bfc1f). spt-core publishes NO `spt api` verb to pull resume/psyche context at SessionStart — `download_psyche_context` (resume.rs:88) is exported-but-unexposed (ZERO `ApiCmd` callers); `resume.rs:9` documents the "adapter pulls it in its SessionStart hook" intent but no verb was ever published. **Consequence: claude-spt live agents rehydrate NO durable context on resume today** — SessionStart only runs `api boundary` (session-id rotation, output discarded) + the identity brief. (The `<psyche-context>` seen on a live session is LEGACY owl's hook — the parity precedent, not claude-spt.) **Fix (v0.15.0 W5, FROZEN):** new `spt api psyche-download <id> [--session-id <sid>]` → stdout `<live-role>`/`<live-context>`/`<project-context-resolved/>`/`<project-context>` + appended `<pending-commune>`/`<pending-signoff>` (raw not-yet-synthesized drop, `!!checkpoint!!` trigger stripped core-side), `NO-CONTEXT:<id>` on stderr when empty; race-free + self-clearing. **claude-spt action:** wire SessionStart to call+inject (`REQ-DIST-RESUME-CONTEXT`); validate E2E on doyle's W5 ping. | No published verb lets an adapter's SessionStart hook pull resume context — the documented adapter-pull path was never given a CLI verb, so claude-spt cannot rehydrate a live agent's durable context on resume at all (foundational parity gap, surfaced building checkpoint communes) |

+> **F-021 / F-022 (NOT spt-core findings — claude-spt parity items, tracked as REQs; surfaced during the 2026-06-24 checkpoint-commune grill)** — (F-021) claude-spt polls only on `UserPromptSubmit` (between turns); legacy spt also polls+injects on **PreToolUse** = the mid-turn half of live-agent reachability (the main path by which a live agent receives a message WHILE working). `api poll` already exists on the public surface → adapter wiring, not an spt-core gap → **`REQ-DIST-PRETOOL-POLL`**. (F-022) subagent worker-perch reachability: wiring is present (`subagent-start.sh` → `api worker-start`; `hostable_types` includes `Worker`) but runtime `spt send`-to-a-worker is UNVERIFIED → validation item **`REQ-DIST-WORKER-PERCH-REACH`**. Neither is an spt-core public-surface gap.

> **F-012 (NOT logged as spt-core)** — legacy-owl 1.11.25 poll-loop exits 1 / orphans the Psyche across daemon churn (`/spt:revive` started gen-7 wrapper+psyche fine but the foreground poll died with a non-fatal `sessions log seal failed: git failed (continuing)` line). doyle ruled this is the **legacy owl listener** (a separate daemon from spt-core), NOT an spt-core public-surface finding; the seal line is non-fatal/continues so isn't the exit cause; it dies with legacy owl's retirement. Re-open as spt-core ONLY if repro'd clean-room (sptc listener, zero legacy owl). No spt-core action.


log the 3 findings and mint the req stubs now. then /commune with immediate next steps and broad summary of the project's status + end goal. we'll resume in the next session


Edit

The published contract example always showed {text}{key:enter}; this adapter now follows it literally.

The published contract example always showed {text}{key:enter}; this adapter now follows it literally.

# ─────────────── Checkpoint-commune wave (planned 2026-06-24, CHECKPOINT-COMMUNE-PLAN.md) ───────────────
# All inactive (required_stages = []) — activate-don't-pre-fail. Activate per-task as the build starts.

[[requirements]]
id = "REQ-DIST-IDLE-MULTILINE"
title = "The idle-translation binary renders each inbound <EVENT> envelope across MULTIPLE LINES in CC's input box for visual distinction — an embedded raw newline byte in the {text} payload after the opening <EVENT ...> tag and before the closing </EVENT> (spt-core writes {text} byte-verbatim including newlines, doyle-confirmed; CC soft-newlines on a bare newline, empirically gated). Cyan color is impossible (SGR bytes eaten by CC input handling; user-turns theme-fixed)."
required_stages = []  # Feature 1 / task T1. ACTIVATE at build. impl: cc-spt-idle-translate splits envelope opening/body/closing + embeds raw newline; relax the CR/LF-neutralize ONLY for CC AFTER the empirical soft-newline gate. unit: binary tests. int: live endpoint shows the envelope across lines, one user turn. Contract A frozen (doyle: {text} raw-verbatim; key-vocab + {text}-verbatim doc folds into spt-core v0.15.0 W4).

[[requirements]]
id = "REQ-DIST-CHECKPOINT-COMMUNE"
title = "Agent-driven checkpoint: a live agent flags a commune with the literal trigger !!checkpoint!! (one = default wake; a pair brackets a custom wake directive); a PostToolUse hook detects the trigger in the Write tool_input.content for .claude/<id>-commune.md, sets the perch idle (api state idle), and self-sends a reserved wire-sentinel (spt send --from <id> <id>); the message loops back through the endpoint's own translation binary, which fires the clear+wake macro (ctrl+s · 50ms · /clear · enter · 500ms · wake-text · enter · commit). spt-hosted-only. INVARIANT: the commune is authored INLINE pre-clear (ADR-0004, Shape 1), never via a post-clear resume-Self refresh."
required_stages = []  # Feature 2 / task T2. ACTIVATE at build. doc: ADR-0004 + CHECKPOINT-COMMUNE-PLAN.md. impl: full-fat /sptc:commune SKILL.md (+ --checkpoint flag + [[hints]]) + NEW PostToolUse hook (manifest [hooks.PostToolUse] + hooks.json + detector reading tool_input.content) + cc-spt-idle-translate checkpoint branch (wire-sentinel filter -> clear+wake macro + double-trigger custom-wake parse). unit: binary macro tests + hook parse tests. int: live endpoint — flagged commune -> /clear fires -> wake turn (default + custom). Mechanics have ZERO spt-core dependency; self-send loopback PROVEN 2026-06-24.

[[requirements]]
id = "REQ-DIST-RESUME-CONTEXT"
title = "claude-spt SessionStart pulls the live agent resume context via the v0.15.0 `spt api psyche-download <self-id> [--session-id <sid>]` verb and injects stdout as additionalContext (skip on NO-CONTEXT) — closing a pre-existing parity gap (claude-spt rehydrates NO durable context today; F-020). One call returns durable role/live/project tiers + the freshest not-yet-synthesized <pending-commune>/<pending-signoff> (trigger stripped core-side); it is also the checkpoint re-seed fast-path."
required_stages = []  # Parity wiring / task T3. ACTIVATE when spt-core v0.15.0 W5 ships the verb (F-020, frozen Tier-1 contract). impl: session-start.sh bind|boundary branch -> call `spt api psyche-download <self-id>` + inject stdout, skip on NO-CONTEXT (stderr). boundary stays rotation-only. unit: tests/hooks-parse.sh. int: real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 gated+built ping. No adapter-side drop handling (core owns the race-free append).

[[requirements]]
id = "REQ-DIST-PRETOOL-POLL"
title = "claude-spt wires a PreToolUse hook firing `api poll` so a live agent receives messages MID-TURN (legacy-parity reachability; today claude-spt drains only on UserPromptSubmit = between turns). F-021."
required_stages = []  # Reachability-parity item (F-021). ACTIVATE at the reachability-parity milestone. impl: NEW PreToolUse hook (manifest [hooks.PreToolUse] + hooks.json) firing `api poll {session_id}` -> additionalContext (mirrors the UserPromptSubmit drain). unit: tests/hooks-parse.sh. int: a mid-turn message delivered as additionalContext on a real live agent. `api poll` is already on the public surface — adapter wiring, NOT an spt-core gap.

[[requirements]]
id = "REQ-DIST-WORKER-PERCH-REACH"
title = "Subagent worker perches are runtime-REACHABLE: a `spt send` to a nested worker perch (created by subagent-start.sh -> api worker-start; hostable_types includes Worker) actually delivers. Validates existing wiring; F-022."
required_stages = []  # Reachability-parity validation (F-022). ACTIVATE at the reachability-parity milestone. int: bring up a CC session, spawn a subagent, confirm the worker perch appears in the roster + `spt send <worker-id>` delivers (worker-poll drains). Wiring is present (subagent-start.sh + hostable Worker); runtime reachability is the only open question.


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)

The published contract example always showed {text}{key:enter}; this adapter now follows it literally.

# ─────────────── Checkpoint-commune wave (planned 2026-06-24, CHECKPOINT-COMMUNE-PLAN.md) ───────────────
# All inactive (required_stages = []) — activate-don't-pre-fail. Activate per-task as the build starts.

[[requirements]]
id = "REQ-DIST-IDLE-MULTILINE"
title = "The idle-translation binary renders each inbound <EVENT> envelope across MULTIPLE LINES in CC's input box for visual distinction — an embedded raw newline byte in the {text} payload after the opening <EVENT ...> tag and before the closing </EVENT> (spt-core writes {text} byte-verbatim including newlines, doyle-confirmed; CC soft-newlines on a bare newline, empirically gated). Cyan color is impossible (SGR bytes eaten by CC input handling; user-turns theme-fixed)."
required_stages = []  # Feature 1 / task T1. ACTIVATE at build. impl: cc-spt-idle-translate splits envelope opening/body/closing + embeds raw newline; relax the CR/LF-neutralize ONLY for CC AFTER the empirical soft-newline gate. unit: binary tests. int: live endpoint shows the envelope across lines, one user turn. Contract A frozen (doyle: {text} raw-verbatim; key-vocab + {text}-verbatim doc folds into spt-core v0.15.0 W4).

[[requirements]]
id = "REQ-DIST-CHECKPOINT-COMMUNE"
title = "Agent-driven checkpoint: a live agent flags a commune with the literal trigger !!checkpoint!! (one = default wake; a pair brackets a custom wake directive); a PostToolUse hook detects the trigger in the Write tool_input.content for .claude/<id>-commune.md, sets the perch idle (api state idle), and self-sends a reserved wire-sentinel (spt send --from <id> <id>); the message loops back through the endpoint's own translation binary, which fires the clear+wake macro (ctrl+s · 50ms · /clear · enter · 500ms · wake-text · enter · commit). spt-hosted-only. INVARIANT: the commune is authored INLINE pre-clear (ADR-0004, Shape 1), never via a post-clear resume-Self refresh."
required_stages = []  # Feature 2 / task T2. ACTIVATE at build. doc: ADR-0004 + CHECKPOINT-COMMUNE-PLAN.md. impl: full-fat /sptc:commune SKILL.md (+ --checkpoint flag + [[hints]]) + NEW PostToolUse hook (manifest [hooks.PostToolUse] + hooks.json + detector reading tool_input.content) + cc-spt-idle-translate checkpoint branch (wire-sentinel filter -> clear+wake macro + double-trigger custom-wake parse). unit: binary macro tests + hook parse tests. int: live endpoint — flagged commune -> /clear fires -> wake turn (default + custom). Mechanics have ZERO spt-core dependency; self-send loopback PROVEN 2026-06-24.

[[requirements]]
id = "REQ-DIST-RESUME-CONTEXT"
title = "claude-spt SessionStart pulls the live agent resume context via the v0.15.0 `spt api psyche-download <self-id> [--session-id <sid>]` verb and injects stdout as additionalContext (skip on NO-CONTEXT) — closing a pre-existing parity gap (claude-spt rehydrates NO durable context today; F-020). One call returns durable role/live/project tiers + the freshest not-yet-synthesized <pending-commune>/<pending-signoff> (trigger stripped core-side); it is also the checkpoint re-seed fast-path."
required_stages = []  # Parity wiring / task T3. ACTIVATE when spt-core v0.15.0 W5 ships the verb (F-020, frozen Tier-1 contract). impl: session-start.sh bind|boundary branch -> call `spt api psyche-download <self-id>` + inject stdout, skip on NO-CONTEXT (stderr). boundary stays rotation-only. unit: tests/hooks-parse.sh. int: real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 gated+built ping. No adapter-side drop handling (core owns the race-free append).

[[requirements]]
id = "REQ-DIST-PRETOOL-POLL"
title = "claude-spt wires a PreToolUse hook firing `api poll` so a live agent receives messages MID-TURN (legacy-parity reachability; today claude-spt drains only on UserPromptSubmit = between turns). F-021."
required_stages = []  # Reachability-parity item (F-021). ACTIVATE at the reachability-parity milestone. impl: NEW PreToolUse hook (manifest [hooks.PreToolUse] + hooks.json) firing `api poll {session_id}` -> additionalContext (mirrors the UserPromptSubmit drain). unit: tests/hooks-parse.sh. int: a mid-turn message delivered as additionalContext on a real live agent. `api poll` is already on the public surface — adapter wiring, NOT an spt-core gap.

[[requirements]]
id = "REQ-DIST-WORKER-PERCH-REACH"
title = "Subagent worker perches are runtime-REACHABLE: a `spt send` to a nested worker perch (created by subagent-start.sh -> api worker-start; hostable_types includes Worker) actually delivers. Validates existing wiring; F-022."
required_stages = []  # Reachability-parity validation (F-022). ACTIVATE at the reachability-parity milestone. int: bring up a CC session, spawn a subagent, confirm the worker perch appears in the roster + `spt send <worker-id>` delivers (worker-poll drains). Wiring is present (subagent-start.sh + hostable Worker); runtime reachability is the only open question.

title = "The adapter ships the [message-idle-translation-binary] (path=\"cc-spt-idle-translate\") — a lifecycle-managed stdin→stdout JSON-lines filter that turns each idle inbound <EVENT> envelope into the keystroke choreography spt-core applies ATOMICALLY to the broker PTY: ctrl+s (stash any draft) · 50ms · <envelope> (type the text, no trailing CR) · 50ms · {key:enter} (submit the PTY line) · {commit:true} (terminate the inject sequence) — so an inbound message never clobbers a half-typed operator draft (CC auto-restores the stashed draft after submit; no trailing restore keystroke). The submit is a DISCRETE {key:enter}, NOT a trailing \\r in the text: a \\r byte does NOT submit a Claude Code message (corrected 2026-06-23). The trailing {commit} is MANDATORY: spt-core's run_inject_worker (broker.rs:1075-1090) ends a sequence only on {commit}; without it the broker FAULTs at the 5s INJECT_COMMIT_DEADLINE on every delivery (the enter key submits the line, {commit} releases the InjectFloor — two distinct signals). Idle delivery ONLY (the spt-hosted complement to the busy/mid-turn [inject] hook path); coexists with a live `spt rc` controller."

required_stages = ["impl", "unit", "int"]  # impl: tools/cc-spt-idle-translate (Rust JSON-lines filter — event→6-step choreography [stash · 50ms · <envelope> text · 50ms · {key:enter} submit · {commit:true} terminator; CC auto-restores the draft so NO trailing restore keystroke], init/input/unknown-type emit nothing, CR/LF in the envelope neutralized so the injection can't split — only the discrete enter submits (a \r byte does NOT submit a CC message, corrected 2026-06-23), forward-compat unknown fields ignored) + [message-idle-translation-binary] path="cc-spt-idle-translate" in adapter/claude-spt.toml + ci/idle-translate/build.sh gate (wired into ci/run-gates.sh) + packed into adapter.spt (ci/publish/package-adapter.sh, the 3rd binary). unit: the crate's 11 cargo tests (run by ci/idle-translate/build.sh: the choreography order, submit-is-discrete-enter-key-not-trailing-CR, mandatory terminating commit, stash-precedes-submit, single-ctrl+s no-trailing-restore, CR/LF neutralization, dispatch, no-output types, envelope-less event, unknown fields, single-field command shape) + tests/{manifest-shortcut.sh (manifest path), adapter-archive.sh (binary packs at root)}. int: ci/idle-translate/translate-proof-int.sh — `spt adapter translate-proof claude-spt --event …` on real spt >=0.13.1 (counter 28) spawns+feeds the binary daemon-identically and asserts TRANSLATE_PROOF_OK + "commit: yes" + the discrete {key:enter} submit (and that the text carries NO trailing \r). GREEN 2026-06-22 (disposable install-dir staged manifest.toml+strings+binary — the GhReleaseManaged resolver wants the extracted shape; soft-removed on teardown). This is the EMIT half ONLY; the daemon's atomic PTY APPLY + spawn-on-up/reap-on-down lifecycle stay covered by a real-claude bringup int (heavier tier, still deferred) — translate-proof explicitly does not exercise apply (ADR-0022). The no-{commit} FAULT (F-016) is exactly what the proof's no-commit gate catches. doc DEFERRED to the docs-site harness-contract RUN-group slice (documented in the manifest + module comment meanwhile). Public-surface-complete: the contract page (harness-contract/manifest.html, message-idle-translation-binary since v0.13.0) is the authority; the operator-specified ctrl+s choreography is the adapter's own UX layer atop the generic key/text/delay_ms protocol. SOURCE-CONFIRMED (doyle 2026-06-20): spt-core applies a {text} command VERBATIM (broker.rs:1016-1017, no control-char strip), so the envelope-internal CR/LF neutralization is required (an un-neutralized \n/\r would reach the PTY and could split the injection). CORRECTION (2026-06-23): the submit is now a DISCRETE {key:enter} after the text, NOT a trailing \r — empirically a \r byte in injected text does NOT trigger Claude Code's message submission (CC needs the discrete Enter key event; the earlier assumption that a verbatim \r == {key:enter} held at the byte level but not for CC's input handling). The published contract example always showed {text}{key:enter}; this adapter now follows it literally.

+# ─────────────── Checkpoint-commune wave (planned 2026-06-24, CHECKPOINT-COMMUNE-PLAN.md) ───────────────

+# All inactive (required_stages = []) — activate-don't-pre-fail. Activate per-task as the build starts.

+title = "The idle-translation binary renders each inbound <EVENT> envelope across MULTIPLE LINES in CC's input box for visual distinction — an embedded raw newline byte in the {text} payload after the opening <EVENT ...> tag and before the closing </EVENT> (spt-core writes {text} byte-verbatim including newlines, doyle-confirmed; CC soft-newlines on a bare newline, empirically gated). Cyan color is impossible (SGR bytes eaten by CC input handling; user-turns theme-fixed)."

+required_stages = []  # Feature 1 / task T1. ACTIVATE at build. impl: cc-spt-idle-translate splits envelope opening/body/closing + embeds raw newline; relax the CR/LF-neutralize ONLY for CC AFTER the empirical soft-newline gate. unit: binary tests. int: live endpoint shows the envelope across lines, one user turn. Contract A frozen (doyle: {text} raw-verbatim; key-vocab + {text}-verbatim doc folds into spt-core v0.15.0 W4).

+title = "Agent-driven checkpoint: a live agent flags a commune with the literal trigger !!checkpoint!! (one = default wake; a pair brackets a custom wake directive); a PostToolUse hook detects the trigger in the Write tool_input.content for .claude/<id>-commune.md, sets the perch idle (api state idle), and self-sends a reserved wire-sentinel (spt send --from <id> <id>); the message loops back through the endpoint's own translation binary, which fires the clear+wake macro (ctrl+s · 50ms · /clear · enter · 500ms · wake-text · enter · commit). spt-hosted-only. INVARIANT: the commune is authored INLINE pre-clear (ADR-0004, Shape 1), never via a post-clear resume-Self refresh."

+required_stages = []  # Feature 2 / task T2. ACTIVATE at build. doc: ADR-0004 + CHECKPOINT-COMMUNE-PLAN.md. impl: full-fat /sptc:commune SKILL.md (+ --checkpoint flag + [[hints]]) + NEW PostToolUse hook (manifest [hooks.PostToolUse] + hooks.json + detector reading tool_input.content) + cc-spt-idle-translate checkpoint branch (wire-sentinel filter -> clear+wake macro + double-trigger custom-wake parse). unit: binary macro tests + hook parse tests. int: live endpoint — flagged commune -> /clear fires -> wake turn (default + custom). Mechanics have ZERO spt-core dependency; self-send loopback PROVEN 2026-06-24.

+title = "claude-spt SessionStart pulls the live agent resume context via the v0.15.0 `spt api psyche-download <self-id> [--session-id <sid>]` verb and injects stdout as additionalContext (skip on NO-CONTEXT) — closing a pre-existing parity gap (claude-spt rehydrates NO durable context today; F-020). One call returns durable role/live/project tiers + the freshest not-yet-synthesized <pending-commune>/<pending-signoff> (trigger stripped core-side); it is also the checkpoint re-seed fast-path."

+required_stages = []  # Parity wiring / task T3. ACTIVATE when spt-core v0.15.0 W5 ships the verb (F-020, frozen Tier-1 contract). impl: session-start.sh bind|boundary branch -> call `spt api psyche-download <self-id>` + inject stdout, skip on NO-CONTEXT (stderr). boundary stays rotation-only. unit: tests/hooks-parse.sh. int: real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 gated+built ping. No adapter-side drop handling (core owns the race-free append).

+title = "claude-spt wires a PreToolUse hook firing `api poll` so a live agent receives messages MID-TURN (legacy-parity reachability; today claude-spt drains only on UserPromptSubmit = between turns). F-021."

+required_stages = []  # Reachability-parity item (F-021). ACTIVATE at the reachability-parity milestone. impl: NEW PreToolUse hook (manifest [hooks.PreToolUse] + hooks.json) firing `api poll {session_id}` -> additionalContext (mirrors the UserPromptSubmit drain). unit: tests/hooks-parse.sh. int: a mid-turn message delivered as additionalContext on a real live agent. `api poll` is already on the public surface — adapter wiring, NOT an spt-core gap.

+title = "Subagent worker perches are runtime-REACHABLE: a `spt send` to a nested worker perch (created by subagent-start.sh -> api worker-start; hostable_types includes Worker) actually delivers. Validates existing wiring; F-022."

+required_stages = []  # Reachability-parity validation (F-022). ACTIVATE at the reachability-parity milestone. int: bring up a CC session, spawn a subagent, confirm the worker perch appears in the roster + `spt send <worker-id>` delivers (worker-poll drains). Wiring is present (subagent-start.sh + hostable Worker); runtime reachability is the only open question.


Both logged. Verifying traceable-reqs stays green (new REQs are inactive, shouldn't affect the gate):


Bash

cd /c/Users/decid/Documents/projects/spt-claude-code
if command -v traceable-reqs >/dev/null 2>&1; then traceable-reqs check 2>&1 | tail -20; else echo "traceable-reqs CLI not on PATH"; ls -1 traceable-reqs* 2>/dev/null; [ -x ./traceable-reqs ] && ./traceable-reqs check 2>&1 | tail -20; fi

Run traceable-reqs check


  [OK] REQ-HAZARD-PERCH-COLLISION  required: [unit]  stages: +doc +impl +unit +int
       The acceptance harness spawns every nested claude SUT under a DISPOSABLE identity (SPT_AGENT_ID=sptc-ci-<n>), never a live agent's perch id — a colliding id tears down the live agent's perch + poll stream (name-keyed, last-establish-wins)
  [OK] REQ-HAZARD-PSYCHE-PERMS-DEADLOCK  required: [doc, unit]  stages: +doc -impl +unit -int
       Every CC process spt-core spawns NON-INTERACTIVELY carries --dangerously-skip-permissions: each claude-spt-psyche turn (seed + every pulse, detached/null-stdin), both [session.self] bringup commands (base claude + ccs profile, broker PTY), AND [session.resume] (native-resume into the broker PTY). An interactive permission prompt with no operator deadlocks the turn (silently, for the detached Psyche). The Psyche additionally runs inside a Read/Edit/Write tool sandbox so auto-approve is bounded.
  [OK] REQ-PARITY-AUDIT  required: [doc]  stages: +doc -impl -unit -int
       The adapter's operative skill/profile surface is reconciled against the SCOPE.md LOCKED parity-trim (KEEP/ADD/DROP/TRANSFORM): every divergence is either resolved or logged as an accepted gap in docs/PARITY.md — the 'proven parity' record the sptc->spt succession flip gates on
  [OK] REQ-SETUP-ACTIVATE  required: [doc, int]  stages: +doc -impl -unit +int
       /sptc:setup, after confirming the spt binary is present, ACTIVATES the claude-spt adapter (`spt adapter add` — the manifest file in local dev; `--github <published-adapter-repo>` for end-users) so profiles/strings/hints/[digest] go live, then verifies (adapter no longer `deregistered`). Binary-present is NOT a no-op — this is the F-005 post-install activation bridge.
  [OK] REQ-SETUP-CCS  required: [doc]  stages: +doc -impl -unit -int
       /sptc:setup wires the ccs integration (SCOPE LOCKED setup #7): if `~/.ccs` is present, point the user at the shipped `claude-spt:ccs` profile (routes live/ready sessions through `ccs`, a drop-in for `claude`, via `--adapter claude-spt:ccs`) + sanity-check `ccs` on PATH; if absent, offer to install ccs with a one-sentence value prop (optional — base claude-spt is unaffected).
  [OK] REQ-SETUP-SUBNET  required: [doc]  stages: +doc -impl -unit -int
       /sptc:setup offers subnet onboarding (SCOPE LOCKED setup #3/#4): detect membership (`spt subnet status`); not-in-a-subnet -> offer create (`spt subnet create`, prints code/URI/QR) or join (`spt subnet join`); in-a-subnet -> show-code to invite / add-this-machine. Delegates the verb mechanics to /sptc:subnet and surfaces the OS-elevation requirement (create/join/show-code are seed-reveal/enroll-gated) with the per-context elevation paths; automated context-aware elevation is a deeper wave.
  [OK] REQ-SKILL-LIVE  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       /sptc:live upgrades THIS CC session to a LiveAgent: base claude-spt is live-capable ([session.psyche_init] in the BASE manifest, Option A) so ready-vs-live is the COMMAND not a profile — bare `spt api listen <id>` stamps the perch state=live_agent and the daemon hosts the Psyche (poll stays ready, livehost.rs:282 gate); the claude-spt-psyche runner keeps the Psyche claude alive (one --continue turn per pulse, NOT one-shot `claude -p`); a resident Monitor relay is the single delivery pipe; zero --adapter (host_binaries resolution, spt-core v0.9.0+)
  [OK] REQ-SKILL-SUBNET  required: [doc, int]  stages: +doc -impl -unit +int
       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)
  [OK] REQ-SKILL-VERSION  required: []  stages: -doc -impl -unit -int
       /spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)
  [OK] REQ-UPS-INJECTION  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands


cd /c/Users/decid/Documents/projects/spt-claude-code
./traceable-reqs check >/tmp/trcheck.txt 2>&1; rc=$?
echo "EXIT=$rc"
echo "=== FAIL/ERROR lines (none = good) ==="; grep -iE 'FAIL|ERROR|missing|invalid' /tmp/trcheck.txt | head
echo "=== new checkpoint reqs present + OK ==="; grep -E 'REQ-DIST-(IDLE-MULTILINE|CHECKPOINT-COMMUNE|RESUME-CONTEXT|PRETOOL-POLL|WORKER-PERCH-REACH)' /tmp/trcheck.txt
echo "=== summary line ==="; tail -3 /tmp/trcheck.txt

Confirm traceable-reqs green + new reqs registered


EXIT=127
=== FAIL/ERROR lines (none = good) ===
=== new checkpoint reqs present + OK ===
=== summary line ===
/usr/bin/bash: line 12: ./traceable-reqs: No such file or directory


PATH binary, not `./`. Re-running:


cd /c/Users/decid/Documents/projects/spt-claude-code
traceable-reqs check >/tmp/trcheck.txt 2>&1; rc=$?
echo "EXIT=$rc"
echo "=== FAIL/ERROR (none=good) ==="; grep -iE '\[FAIL\]|ERROR|manifest_error|missing evidence|invalid' /tmp/trcheck.txt | head
echo "=== new reqs ==="; grep -E 'REQ-DIST-(IDLE-MULTILINE|CHECKPOINT-COMMUNE|RESUME-CONTEXT|PRETOOL-POLL|WORKER-PERCH-REACH)' /tmp/trcheck.txt
echo "=== summary tail ==="; tail -4 /tmp/trcheck.txt

Run traceable-reqs check via PATH binary


EXIT=0
=== FAIL/ERROR (none=good) ===
=== new reqs ===
  [OK] REQ-DIST-CHECKPOINT-COMMUNE  required: []  stages: -doc -impl -unit -int
  [OK] REQ-DIST-IDLE-MULTILINE  required: []  stages: -doc -impl -unit -int
  [OK] REQ-DIST-PRETOOL-POLL  required: []  stages: -doc -impl -unit -int
  [OK] REQ-DIST-RESUME-CONTEXT  required: []  stages: -doc -impl -unit -int
  [OK] REQ-DIST-WORKER-PERCH-REACH  required: []  stages: -doc -impl -unit -int
=== summary tail ===
  [OK] REQ-SKILL-VERSION  required: []  stages: -doc -impl -unit -int
       /spt:version reports the spt-core-tracked manifest/binary version (the version-of-truth, not the marketplace skeleton version)
  [OK] REQ-UPS-INJECTION  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       UserPromptSubmit hook detects /sptc:X and injects X's real instructions as additionalContext (SKILL.md files stay skeletons); must be empirically confirmed UPS fires on slash-commands


`traceable-reqs check` **EXIT=0 (green)** — all 5 new REQs registered `[OK]`, inactive (`required:[]`, activate-don't-pre-fail). No failures.

**Logged + minted:**
- `docs/SPT-CORE-FINDINGS.md` — **F-020** (psyche-download verb gap, RESOLVED-PENDING v0.15.0 W5) + **F-021/F-022** note (adapter parity, tracked as REQs).
- `traceable-reqs.toml` — 5 stubs: `REQ-DIST-IDLE-MULTILINE`, `-CHECKPOINT-COMMUNE`, `-RESUME-CONTEXT`, `-PRETOOL-POLL`, `-WORKER-PERCH-REACH`.

Now the commune. This session runs on **legacy owl** (perri) — using the legacy commune:


Launching skill: spt:commune


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

# /spt:commune

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

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

## Flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Envelope shape (Phase 23 v1.8)

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

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

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

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

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


"$LIVE" psyche-download perri 2>&1 | head -60

Download current psyche context before composing commune


/usr/bin/bash: line 6: C:/Users/decid/.claude/plugins/cache/cplugs/spt/1.11.25/owl.exe live: No such file or directory


`$LIVE` holds `owl.exe live` (two tokens) — must run unquoted so it word-splits:


$LIVE psyche-download perri 2>&1 | head -70

Download psyche context (unquoted so it word-splits)


<current machine="HFENDULEAM" project="spt-claude-code" branch="main" head_sha="4954293569af0e0657bff4e1805efcde7b66497f" head_subject="docs(findings): F-019 RESOLVED — translation binary never spawned (relat…" commits_since="0" commits_unpulled="0"/>
**Status:** 2026-06-24T10:09:02Z (grill-with-docs capture complete)

**GRILLING PHASE COMPLETE.** Design fully locked, public-surface boundary mapped. All outstanding artifacts delivered:
- `CONTEXT.md` — checkpoint + checkpoint trigger glossary ✓
- `CHECKPOINT-COMMUNE-PLAN.md` — full JIT plan ✓
- `docs/adr/0004-checkpoint-via-self-send-loopback.md` — load-bearing inline-pre-clear invariant + self-send-loopback rationale + rejected alternatives ✓

**Cross-team contracts fully frozen & confirmed:**
- **doyle:** `{text}` literal raw-`\n` token, psyche-download Tier-1 verb @20bfc1f, "Nothing more needed until then. Onward."
- **todlando:** trigger marker `!!checkpoint!!` strip rule locked — remove every sentinel, keep inter-marker text, collapse whitespace; lands post-W5-gate @20bfc1f.

**Outstanding (doc-only, non-blocking):**
1. Log 3 findings in `docs/SPT-CORE-FINDINGS.md` + mint `REQ-*` stubs in `traceable-reqs.toml` (required_stages=[], activate-don't-pre-fail)
2. Build T1→T3 (code/binaries, timeline W5 validation)

**Next:** Execute findings + REQ capture, then build T1 (raw-`\n` multi-line test).

**echo_commune log:** Same SHA @4954293, no push yet. Progress: grilling → findings logging → T1 build ready.<project-context-resolved name="spt-claude-code"/>
Grill-with-docs cycle complete. ADR 0004 written (the load-bearing inline-pre-clear invariant + self-send-loopback indirection). Cross-team marker contract confirmed with todlando (strip `!!checkpoint!!` core-side post-W5-gate). Design now 100% locked + boundary fully mapped. Execution phase ready: findings logging + T1 build next.


Current psyche context already has the grill-complete state + lists "log findings + mint REQ stubs" as the next step — which I just **completed**. So the delta is: doc-capture phase done, resume point is now the **build**. Composing the two-slice commune (in-project → both slices):


Write

<live-context>
**2026-06-24 — checkpoint-commune wave: doc-capture phase COMPLETE; resume = THE BUILD.**

Delta since last download: findings + REQ stubs are now LANDED (the "outstanding item #1" is done):
- `docs/SPT-CORE-FINDINGS.md`: F-020 (psyche-download verb gap, RESOLVED-PENDING v0.15.0 W5) + F-021/F-022 note (adapter parity, tracked as REQs).
- `traceable-reqs.toml`: 5 stubs minted, all `required_stages = []` — REQ-DIST-IDLE-MULTILINE, -CHECKPOINT-COMMUNE, -RESUME-CONTEXT, -PRETOOL-POLL, -WORKER-PERCH-REACH. `traceable-reqs check` GREEN (exit 0).

**IMMEDIATE NEXT STEPS (resume here, in order — this is the build):**
1. **T1 (Feature 1, multi-line envelope).** EMPIRICAL GATE FIRST: throwaway probe binary on a fresh spt-hosted endpoint → confirm CC soft-newlines on a bare `\n` in `{text}` (one user turn, two lines, no early submit). If yes → `cc-spt-idle-translate` splits the envelope opening-tag / `\n` / body / `\n` / closing-tag; relax the CR/LF→space sanitizer FOR CC only. Activate REQ-DIST-IDLE-MULTILINE; update the 11 binary tests.
2. **T2 (Feature 2 mechanics — ZERO spt-core dep, fully buildable now).** Full-fat `/sptc:commune` SKILL.md (drop fetch-stub) + `--checkpoint` flag + `[[hints]]`. NEW PostToolUse hook (manifest `[hooks.PostToolUse]` + hooks.json + detector): trigger `!!checkpoint!!` in Write `tool_input.content` for `.claude/<id>-commune.md` → `spt api state idle` → `spt send --from <id> <id> <wire-sentinel[+wake]>`. Binary checkpoint branch: filter the wire-sentinel → macro `ctrl+s · 50ms · /clear · enter · 500ms · <wake-text> · enter · commit`; parse double-`!!checkpoint!!` custom wake. Activate REQ-DIST-CHECKPOINT-COMMUNE. Verify commune body format (two-slice envelope vs freeform).
3. **T3 (parity wiring — code-now / validate-on-W5).** `session-start.sh` bind|boundary → `spt api psyche-download <self-id>` → inject stdout as additionalContext, skip on NO-CONTEXT (stderr). boundary stays rotation-only. Activate REQ-DIST-RESUME-CONTEXT. Validate E2E on doyle's W5 ping.

**WAITING ON spt-core v0.15.0 (doyle/todlando):** W5 ships `spt api psyche-download <id>` (FROZEN Tier-1, committed @20bfc1f, after W4) — doyle PINGS when gated+built → then do T3 validation + todlando's `!!checkpoint!!` pending-strip follow-up lands. W4 = key-vocab + `{text}`-verbatim doc.

**RECIPE NOTES:** this session = LEGACY owl (perri). Reach doyle/todlando via `$OWL send <target> perri` (new-spt `spt send doyle` = NO_PERCH). `$LIVE` = `owl.exe live` — run UNQUOTED so it word-splits. PowerShell has NO printf → use the Bash tool for spt/owl sends. Self-send loopback PROVEN (`spt send --from <id> <id>` → own translation binary → user turn). Probe recipe: `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`; spawned-CC transcript at `~/.claude/projects/<slug>/<cc-session-uuid>.jsonl` (NOT spt's session id; grep a unique token). Cleanup: `spt endpoint shutdown <id>` + reap `cc-spt-idle-translate.exe`.
</live-context>
<project-context>
**Checkpoint-commune wave (2026-06-24) — design LOCKED, contracts FROZEN, doc-capture DONE. Build pending.**

Artifacts all landed: `CONTEXT.md` (checkpoint + checkpoint-trigger terms), `CHECKPOINT-COMMUNE-PLAN.md` (full JIT plan), `docs/adr/0004-checkpoint-via-self-send-loopback.md`, F-020 + F-021/F-022 in `docs/SPT-CORE-FINDINGS.md`, 5 REQ stubs in `traceable-reqs.toml` (check GREEN).

**Durable design facts (don't re-derive):**
- **Cyan is IMPOSSIBLE** — SGR bytes eaten by CC's input box (win32-input-mode); CC user-turns are theme-fixed. PROVEN (empirical + CC-guide). Visual distinction = plain-text multi-line only.
- **Multi-line = raw `\n` in `{text}`** — spt-core writes `{text}` byte-verbatim incl. `\n` (NO win32-encoding; the DECSET-9001 theory was debunked F-019 misdirection). `{key:"shift+enter"}` is silently dropped (not recognized). Pending only the T1 empirical CC-soft-newline gate.
- **Checkpoint = self-send-loopback** — the binary is stdin-driven, can't read the commune file, and the daemon-push alternative needs an absent spt-core capability; so a PostToolUse hook detects the trigger and self-sends a wire-sentinel that loops back through the endpoint's own binary, which fires the clear+wake macro. spt-hosted-only by design.
- **INVARIANT (ADR-0004):** the commune is authored INLINE pre-clear (Shape 1) — never via a post-clear resume-Self refresh (Shape 2 = the legacy psyche-stale-after-clear gap).
- **Re-seed is spt-core-owned** — adapter does NOT read the raw drop (TOCTOU vs core's pulse-delete). The W5 `psyche-download` verb returns durable role/live/project + appended `<pending-commune>`/`<pending-signoff>` (raw not-yet-synthesized, `!!checkpoint!!` stripped CORE-SIDE), race-free + self-clearing.
- **Foundational gap F-020:** claude-spt pulls NO durable resume context today (SessionStart only does `api boundary` rotation + identity brief; the `<psyche-context>` on a live session is LEGACY owl's hook, the parity precedent). Fixed by wiring the W5 verb (T3 / REQ-DIST-RESUME-CONTEXT).
- **Two adapter parity items (separate from this wave):** PreToolUse mid-turn poll (F-021 / REQ-DIST-PRETOOL-POLL — claude-spt only polls on UserPromptSubmit); subagent worker-perch reachability (F-022 / REQ-DIST-WORKER-PERCH-REACH — wiring present, runtime unverified).

**Also this session:** F-019 validated ALL-GREEN on spt-core v0.14.2 (keystone closed — CC idle delivery works E2E on the real harness; doyle ACK'd). spt-core v0.14.3 published (counter 33: raw-inject removal — a no-binary/faulted idle path now QUEUES honestly, never false-types). Repaired a corrupted `.claude.json` (interrupted-write stray `}]`/`}` after valid JSON).

**Project end-goal (broad):** spt-claude-code = the rebuilt Claude Code harness adapter (`claude-spt`), built against spt-core's PUBLISHED surface ONLY (gaps = findings to doyle; never read spt-core source). It is spt-core's v1 acceptance proof + first casual-end-user entrypoint (CC plugin) + invisible installer. Node: claude-spt v0.6.2, spt-core v0.14.2→v0.14.3. End goal = proven parity with legacy `claude_skill_owl` → then the `sptc`→`spt` succession flip (publish as spt, retire legacy owl). The checkpoint-commune wave + the resume-context/PreToolUse/worker-perch parity gaps are part of closing that parity. Binding constraints: public-surface-only, traceable-reqs gating, KNOWN-HAZARDS as REQ-HAZARD-* conformance.
</project-context>


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

<live-context>
**2026-06-24 — checkpoint-commune wave: doc-capture phase COMPLETE; resume = THE BUILD.**

Delta since last download: findings + REQ stubs are now LANDED (the "outstanding item #1" is done):
- `docs/SPT-CORE-FINDINGS.md`: F-020 (psyche-download verb gap, RESOLVED-PENDING v0.15.0 W5) + F-021/F-022 note (adapter parity, tracked as REQs).
- `traceable-reqs.toml`: 5 stubs minted, all `required_stages = []` — REQ-DIST-IDLE-MULTILINE, -CHECKPOINT-COMMUNE, -RESUME-CONTEXT, -PRETOOL-POLL, -WORKER-PERCH-REACH. `traceable-reqs check` GREEN (exit 0).

**IMMEDIATE NEXT STEPS (resume here, in order — this is the build):**
1. **T1 (Feature 1, multi-line envelope).** EMPIRICAL GATE FIRST: throwaway probe binary on a fresh spt-hosted endpoint → confirm CC soft-newlines on a bare `\n` in `{text}` (one user turn, two lines, no early submit). If yes → `cc-spt-idle-translate` splits the envelope opening-tag / `\n` / body / `\n` / closing-tag; relax the CR/LF→space sanitizer FOR CC only. Activate REQ-DIST-IDLE-MULTILINE; update the 11 binary tests.
2. **T2 (Feature 2 mechanics — ZERO spt-core dep, fully buildable now).** Full-fat `/sptc:commune` SKILL.md (drop fetch-stub) + `--checkpoint` flag + `[[hints]]`. NEW PostToolUse hook (manifest `[hooks.PostToolUse]` + hooks.json + detector): trigger `!!checkpoint!!` in Write `tool_input.content` for `.claude/<id>-commune.md` → `spt api state idle` → `spt send --from <id> <id> <wire-sentinel[+wake]>`. Binary checkpoint branch: filter the wire-sentinel → macro `ctrl+s · 50ms · /clear · enter · 500ms · <wake-text> · enter · commit`; parse double-`!!checkpoint!!` custom wake. Activate REQ-DIST-CHECKPOINT-COMMUNE. Verify commune body format (two-slice envelope vs freeform).
3. **T3 (parity wiring — code-now / validate-on-W5).** `session-start.sh` bind|boundary → `spt api psyche-download <self-id>` → inject stdout as additionalContext, skip on NO-CONTEXT (stderr). boundary stays rotation-only. Activate REQ-DIST-RESUME-CONTEXT. Validate E2E on doyle's W5 ping.

**WAITING ON spt-core v0.15.0 (doyle/todlando):** W5 ships `spt api psyche-download <id>` (FROZEN Tier-1, committed @20bfc1f, after W4) — doyle PINGS when gated+built → then do T3 validation + todlando's `!!checkpoint!!` pending-strip follow-up lands. W4 = key-vocab + `{text}`-verbatim doc.

**RECIPE NOTES:** this session = LEGACY owl (perri). Reach doyle/todlando via `$OWL send <target> perri` (new-spt `spt send doyle` = NO_PERCH). `$LIVE` = `owl.exe live` — run UNQUOTED so it word-splits. PowerShell has NO printf → use the Bash tool for spt/owl sends. Self-send loopback PROVEN (`spt send --from <id> <id>` → own translation binary → user turn). Probe recipe: `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`; spawned-CC transcript at `~/.claude/projects/<slug>/<cc-session-uuid>.jsonl` (NOT spt's session id; grep a unique token). Cleanup: `spt endpoint shutdown <id>` + reap `cc-spt-idle-translate.exe`.
</live-context>
<project-context>
**Checkpoint-commune wave (2026-06-24) — design LOCKED, contracts FROZEN, doc-capture DONE. Build pending.**

Artifacts all landed: `CONTEXT.md` (checkpoint + checkpoint-trigger terms), `CHECKPOINT-COMMUNE-PLAN.md` (full JIT plan), `docs/adr/0004-checkpoint-via-self-send-loopback.md`, F-020 + F-021/F-022 in `docs/SPT-CORE-FINDINGS.md`, 5 REQ stubs in `traceable-reqs.toml` (check GREEN).

**Durable design facts (don't re-derive):**
- **Cyan is IMPOSSIBLE** — SGR bytes eaten by CC's input box (win32-input-mode); CC user-turns are theme-fixed. PROVEN (empirical + CC-guide). Visual distinction = plain-text multi-line only.
- **Multi-line = raw `\n` in `{text}`** — spt-core writes `{text}` byte-verbatim incl. `\n` (NO win32-encoding; the DECSET-9001 theory was debunked F-019 misdirection). `{key:"shift+enter"}` is silently dropped (not recognized). Pending only the T1 empirical CC-soft-newline gate.
- **Checkpoint = self-send-loopback** — the binary is stdin-driven, can't read the commune file, and the daemon-push alternative needs an absent spt-core capability; so a PostToolUse hook detects the trigger and self-sends a wire-sentinel that loops back through the endpoint's own binary, which fires the clear+wake macro. spt-hosted-only by design.
- **INVARIANT (ADR-0004):** the commune is authored INLINE pre-clear (Shape 1) — never via a post-clear resume-Self refresh (Shape 2 = the legacy psyche-stale-after-clear gap).
- **Re-seed is spt-core-owned** — adapter does NOT read the raw drop (TOCTOU vs core's pulse-delete). The W5 `psyche-download` verb returns durable role/live/project + appended `<pending-commune>`/`<pending-signoff>` (raw not-yet-synthesized, `!!checkpoint!!` stripped CORE-SIDE), race-free + self-clearing.
- **Foundational gap F-020:** claude-spt pulls NO durable resume context today (SessionStart only does `api boundary` rotation + identity brief; the `<psyche-context>` on a live session is LEGACY owl's hook, the parity precedent). Fixed by wiring the W5 verb (T3 / REQ-DIST-RESUME-CONTEXT).
- **Two adapter parity items (separate from this wave):** PreToolUse mid-turn poll (F-021 / REQ-DIST-PRETOOL-POLL — claude-spt only polls on UserPromptSubmit); subagent worker-perch reachability (F-022 / REQ-DIST-WORKER-PERCH-REACH — wiring present, runtime unverified).

**Also this session:** F-019 validated ALL-GREEN on spt-core v0.14.2 (keystone closed — CC idle delivery works E2E on the real harness; doyle ACK'd). spt-core v0.14.3 published (counter 33: raw-inject removal — a no-binary/faulted idle path now QUEUES honestly, never false-types). Repaired a corrupted `.claude.json` (interrupted-write stray `}]`/`}` after valid JSON).

**Project end-goal (broad):** spt-claude-code = the rebuilt Claude Code harness adapter (`claude-spt`), built against spt-core's PUBLISHED surface ONLY (gaps = findings to doyle; never read spt-core source). It is spt-core's v1 acceptance proof + first casual-end-user entrypoint (CC plugin) + invisible installer. Node: claude-spt v0.6.2, spt-core v0.14.2→v0.14.3. End goal = proven parity with legacy `claude_skill_owl` → then the `sptc`→`spt` succession flip (publish as spt, retire legacy owl). The checkpoint-commune wave + the resume-context/PreToolUse/worker-perch parity gaps are part of closing that parity. Binding constraints: public-surface-only, traceable-reqs gating, KNOWN-HAZARDS as REQ-HAZARD-* conformance.
</project-context>


Commune written to `.claude/perri-commune.md`. Verifying the Self listener ingests it (file disappearing = success):


Bash

sleep 8
if [ -f /c/Users/decid/Documents/projects/spt-claude-code/.claude/perri-commune.md ]; then
  echo "STILL PRESENT — not yet consumed (will retry on next poll iteration)"
else
  echo "CONSUMED — commune ingested by Psyche (file deleted = success)"
fi

Check if commune file was consumed


STILL PRESENT — not yet consumed (will retry on next poll iteration)


sleep 22
if [ -f /c/Users/decid/Documents/projects/spt-claude-code/.claude/perri-commune.md ]; then
  echo "STILL PRESENT after ~30s — will be consumed on the next Self poll iteration (retained-until-success by design; safe to end session)"
else
  echo "CONSUMED — Psyche ingested the commune (file deleted = success)"
fi

Re-check commune consumption after longer wait


CONSUMED — Psyche ingested the commune (file deleted = success)


**Commune CONSUMED** — Psyche ingested it (file deleted = success). Resume context is durably saved.

## Session complete — wrap

**Shipped this session:**
- **F-019 validated ALL-GREEN** on spt-core v0.14.2 (keystone closed — CC idle delivery works end-to-end on the real harness; doyle ACK'd). v0.14.3 published (counter 33, raw-inject removal).
- Repaired corrupted `.claude.json` (interrupted-write stray brackets).
- **grill-with-docs on 2 features → design fully locked, public-surface boundary mapped, contracts frozen** with doyle (`{text}` raw-`\n`; `psyche-download` Tier-1 verb @20bfc1f) + todlando (`!!checkpoint!!` strip core-side).

**Captured (durable):**
- `CONTEXT.md` — `checkpoint` + `checkpoint trigger` terms
- `CHECKPOINT-COMMUNE-PLAN.md` — full JIT plan
- `docs/adr/0004-checkpoint-via-self-send-loopback.md` — inline-pre-clear invariant + self-send rationale
- `docs/SPT-CORE-FINDINGS.md` — F-020 (psyche-download verb gap) + F-021/F-022 note
- `traceable-reqs.toml` — 5 REQ stubs, `check` GREEN
- Psyche commune — ingested

**Resume next session = THE BUILD:**
T1 (raw-`\n` soft-newline test → multi-line envelope) → T2 (checkpoint mechanics: skill + PostToolUse hook + binary macro — zero spt-core dep) → T3 (psyche-download wiring — code-now, validate on doyle's W5 ping).

Nothing pushed (same SHA @4954293). Clean stopping point. 👋