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 = "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 = ["impl", "unit"]  # Parity wiring / task T3 — impl+unit ACTIVATED 2026-06-24 (coded against the FROZEN Tier-1 contract B); int DEFERRED until spt-core v0.15.0 W5 ships the verb (then add "int" + validate on doyle's gated+built ping). impl: session-start.sh bind|boundary brief branch -> sptc_psyche_download (mirrors `api poll` id-scoped auth: --session-id, no --project — project resolves from the bound cwd) + sptc_append_resume injects stdout VERBATIM below the identity brief, skip on NO-CONTEXT/empty (pre-v0.15.0 the verb is absent -> empty -> graceful skip). Fires on bind AND boundary — boundary IS the checkpoint re-seed path (post-/clear rebuild from the latest commune); the `api boundary` rotation itself is unchanged (additive pull). unit: tests/hooks-parse.sh sptc_append_resume skip-on-NO-CONTEXT tests. int (DEFERRED): real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 ping. No adapter-side raw-drop handling (core owns the race-free append).

+required_stages = ["impl", "unit", "int"]  # Parity wiring / task T3 — impl+unit ACTIVATED 2026-06-24 (coded against FROZEN contract B); int ACTIVATED 2026-06-24 when spt-core v0.15.0 (counter 34) PUBLISHED + verified on-node (doyle gate ruling: the CORE-CONTRACT half is deterministic CI, the SessionStart INJECTION E2E is logged dogfood). impl: session-start.sh bind|boundary brief branch -> sptc_psyche_download (mirrors `api poll` id-scoped auth: --session-id, no --project — project resolves from the bound cwd) + sptc_append_resume injects stdout VERBATIM below the identity brief, skip on NO-CONTEXT/empty (pre-v0.15.0 the verb is absent -> empty -> graceful skip). Fires on bind AND boundary — boundary IS the checkpoint re-seed path (post-/clear rebuild from the latest commune); the `api boundary` rotation itself is unchanged (additive pull). unit: tests/hooks-parse.sh sptc_append_resume skip-on-NO-CONTEXT tests. int (DEFERRED): real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 ping. No adapter-side raw-drop handling (core owns the race-free append).


Edit

unit: tests/hooks-parse.sh sptc_append_resume skip-on-NO-CONTEXT tests. int (DEFERRED): real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 ping. No adapter-side raw-drop handling (core owns the race-free append).

unit: tests/hooks-parse.sh sptc_append_resume skip-on-NO-CONTEXT tests. int: ci/psyche-download/psyche-download-int.sh — deterministic capability-detect CI against real spt 0.15.0 (NO real CC: a throwaway perch is bind-created): asserts verb-present + NO-CONTEXT (fresh perch -> exit 0, empty stdout, NO-CONTEXT:<id> stderr) + AUTH (wrong --session-id rejected) + POSITIVE (a not-yet-synthesized commune drop returned VERBATIM as <pending-commune>). GREEN on-node 2026-06-24. DEFERRED dogfood (NOT CI, logged in CHECKPOINT-COMMUNE-PLAN.md §Deferred validations): the SessionStart INJECTION E2E — session-start.sh actually consuming the pull + injecting into a live CC — needs the NEW plugin installed + real CC. No adapter-side raw-drop handling (core owns the race-free append).


-required_stages = ["impl", "unit", "int"]  # Parity wiring / task T3 — impl+unit ACTIVATED 2026-06-24 (coded against FROZEN contract B); int ACTIVATED 2026-06-24 when spt-core v0.15.0 (counter 34) PUBLISHED + verified on-node (doyle gate ruling: the CORE-CONTRACT half is deterministic CI, the SessionStart INJECTION E2E is logged dogfood). impl: session-start.sh bind|boundary brief branch -> sptc_psyche_download (mirrors `api poll` id-scoped auth: --session-id, no --project — project resolves from the bound cwd) + sptc_append_resume injects stdout VERBATIM below the identity brief, skip on NO-CONTEXT/empty (pre-v0.15.0 the verb is absent -> empty -> graceful skip). Fires on bind AND boundary — boundary IS the checkpoint re-seed path (post-/clear rebuild from the latest commune); the `api boundary` rotation itself is unchanged (additive pull). unit: tests/hooks-parse.sh sptc_append_resume skip-on-NO-CONTEXT tests. int (DEFERRED): real psyche-download output injected at SessionStart, durable+pending present — validate on doyle's W5 ping. No adapter-side raw-drop handling (core owns the race-free append).

+required_stages = ["impl", "unit", "int"]  # Parity wiring / task T3 — impl+unit ACTIVATED 2026-06-24 (coded against FROZEN contract B); int ACTIVATED 2026-06-24 when spt-core v0.15.0 (counter 34) PUBLISHED + verified on-node (doyle gate ruling: the CORE-CONTRACT half is deterministic CI, the SessionStart INJECTION E2E is logged dogfood). impl: session-start.sh bind|boundary brief branch -> sptc_psyche_download (mirrors `api poll` id-scoped auth: --session-id, no --project — project resolves from the bound cwd) + sptc_append_resume injects stdout VERBATIM below the identity brief, skip on NO-CONTEXT/empty (pre-v0.15.0 the verb is absent -> empty -> graceful skip). Fires on bind AND boundary — boundary IS the checkpoint re-seed path (post-/clear rebuild from the latest commune); the `api boundary` rotation itself is unchanged (additive pull). unit: tests/hooks-parse.sh sptc_append_resume skip-on-NO-CONTEXT tests. int: ci/psyche-download/psyche-download-int.sh — deterministic capability-detect CI against real spt 0.15.0 (NO real CC: a throwaway perch is bind-created): asserts verb-present + NO-CONTEXT (fresh perch -> exit 0, empty stdout, NO-CONTEXT:<id> stderr) + AUTH (wrong --session-id rejected) + POSITIVE (a not-yet-synthesized commune drop returned VERBATIM as <pending-commune>). GREEN on-node 2026-06-24. DEFERRED dogfood (NOT CI, logged in CHECKPOINT-COMMUNE-PLAN.md §Deferred validations): the SessionStart INJECTION E2E — session-start.sh actually consuming the pull + injecting into a live CC — needs the NEW plugin installed + real CC. No adapter-side raw-drop handling (core owns the race-free append).


Update the checkpoint REQ comment (live loopback validated; write-detect E2E = deferred dogfood):


int: ci/idle-translate/translate-proof-int.sh checkpoint block (envelope w/ json attr fires the clear+wake macro, custom + default wake, normal delivery never clears) — the EMIT half on the pre-0.15.0 daemon; the send-side --json-payload composition + live self-send loopback stay the heavier real-claude int (deferred to the v0.15.0/counter-34 publish, capability-detect-skip). The write-side !!checkpoint!! marker is one-shot (spt-core v0.15.0 strips it from durable+pending on ingest). min_spt_core stays 0.13.2 in-tree until doyle's publish ping, then bumps to 0.15.0. Self-send loopback PROVEN 2026-06-24.

int: ci/idle-translate/translate-proof-int.sh checkpoint block (envelope w/ json attr fires the clear+wake macro, custom + default wake, normal delivery never clears) — the deterministic EMIT half. The LIVE self-send loopback (real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` -> json envelope attr -> binary -> /clear+wake) was VALIDATED on-node 2026-06-24 on spt 0.15.0: a spt-hosted CC transcript showed the macro fire E2E (user turn `/clear`, next user turn = the wake). DEFERRED dogfood (NOT CI, logged in CHECKPOINT-COMMUNE-PLAN.md §Deferred validations): the PostToolUse WRITE-DETECT E2E — the hook detecting `!!checkpoint!!` in a Write + self-sending — needs the NEW plugin installed + real CC. The write-side !!checkpoint!! marker is one-shot (spt-core v0.15.0 strips it from durable+pending on ingest). min_spt_core BUMPED to 0.15.0 (manifest v0.7.0). Self-send loopback PROVEN 2026-06-24.


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 = ["doc", "impl", "unit", "int"]  # Feature 2 / task T2 — ACTIVATED 2026-06-24. WIRE-SENTINEL is now spt-core v0.15.0 `spt send --json-payload '{"checkpoint":"v1","wake":…}'` (doyle, grounded in the gated W4 code) — a structured json ATTR on the <EVENT> envelope, NOT the old string-prefix sniff (collision-proof: the marker lives inside the opaque attr, can't forge from/type). doc: full-fat /sptc:commune SKILL.md (--checkpoint: single trigger=default wake, paired triggers bracket a custom wake) + ADR-0004 + the checkpoint [[hints]] entry. impl: cc-spt-idle-translate checkpoint branch (extract json attr -> xml-attr-unescape (& LAST) -> serde parse -> checkpoint=="v1" -> clear+wake macro ctrl+s·50ms·/clear·enter·500ms·wake·enter·commit, default "Proceed with next steps") + NEW PostToolUse hook (manifest [hooks.PostToolUse] + hooks.json Write-matcher + post-tool-use.sh detector: !!checkpoint!! scan of tool_input content -> api state idle -> self-send --json-payload) + _common.sh pure helpers (sptc_has_checkpoint/sptc_checkpoint_wake/sptc_is_commune_write). unit: hooks-parse.sh checkpoint tests + the crate's checkpoint macro/parse/unescape tests. int: ci/idle-translate/translate-proof-int.sh checkpoint block (envelope w/ json attr fires the clear+wake macro, custom + default wake, normal delivery never clears) — the EMIT half on the pre-0.15.0 daemon; the send-side --json-payload composition + live self-send loopback stay the heavier real-claude int (deferred to the v0.15.0/counter-34 publish, capability-detect-skip). The write-side !!checkpoint!! marker is one-shot (spt-core v0.15.0 strips it from durable+pending on ingest). min_spt_core stays 0.13.2 in-tree until doyle's publish ping, then bumps to 0.15.0. Self-send loopback PROVEN 2026-06-24.

+required_stages = ["doc", "impl", "unit", "int"]  # Feature 2 / task T2 — ACTIVATED 2026-06-24. WIRE-SENTINEL is now spt-core v0.15.0 `spt send --json-payload '{"checkpoint":"v1","wake":…}'` (doyle, grounded in the gated W4 code) — a structured json ATTR on the <EVENT> envelope, NOT the old string-prefix sniff (collision-proof: the marker lives inside the opaque attr, can't forge from/type). doc: full-fat /sptc:commune SKILL.md (--checkpoint: single trigger=default wake, paired triggers bracket a custom wake) + ADR-0004 + the checkpoint [[hints]] entry. impl: cc-spt-idle-translate checkpoint branch (extract json attr -> xml-attr-unescape (& LAST) -> serde parse -> checkpoint=="v1" -> clear+wake macro ctrl+s·50ms·/clear·enter·500ms·wake·enter·commit, default "Proceed with next steps") + NEW PostToolUse hook (manifest [hooks.PostToolUse] + hooks.json Write-matcher + post-tool-use.sh detector: !!checkpoint!! scan of tool_input content -> api state idle -> self-send --json-payload) + _common.sh pure helpers (sptc_has_checkpoint/sptc_checkpoint_wake/sptc_is_commune_write). unit: hooks-parse.sh checkpoint tests + the crate's checkpoint macro/parse/unescape tests. int: ci/idle-translate/translate-proof-int.sh checkpoint block (envelope w/ json attr fires the clear+wake macro, custom + default wake, normal delivery never clears) — the deterministic EMIT half. The LIVE self-send loopback (real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` -> json envelope attr -> binary -> /clear+wake) was VALIDATED on-node 2026-06-24 on spt 0.15.0: a spt-hosted CC transcript showed the macro fire E2E (user turn `/clear`, next user turn = the wake). DEFERRED dogfood (NOT CI, logged in CHECKPOINT-COMMUNE-PLAN.md §Deferred validations): the PostToolUse WRITE-DETECT E2E — the hook detecting `!!checkpoint!!` in a Write + self-sending — needs the NEW plugin installed + real CC. The write-side !!checkpoint!! marker is one-shot (spt-core v0.15.0 strips it from durable+pending on ingest). min_spt_core BUMPED to 0.15.0 (manifest v0.7.0). Self-send loopback PROVEN 2026-06-24.


Add the referenced "§Deferred validations" section to the plan:


## Open items

- Exact `<id>` + auth-token threading for `api psyche-download` (mirror our other `api` calls).
- Commune body format (two-slice envelope vs freeform) — confirm in T2a.
- Macro 500ms timing adequacy post-`/clear` (validate in T2).

## Deferred validations (dogfood-after-plugin-land — LOGGED, not CI)

doyle's gate ruling (2026-06-24): the spt-core-owned halves are deterministic CI; the harness-glue
E2Es genuinely need the NEW plugin installed + a real CC session, so they stay a dogfood tier —
exercised once the v0.7.0 plugin is published/installed, not covered by an automated gate.

- **T2 — PostToolUse write-detect E2E.** The hook (`post-tool-use.sh`) detecting `!!checkpoint!!` in
  a live agent's own `Write` of `.claude/<id>-commune.md` and firing the self-send. Validated halves:
  the binary clear+wake branch (CI, `translate-proof-int.sh`) and the LIVE self-send loopback
  (manual, on-node 2026-06-24 — a real `spt send --json-payload` self-send drove a spt-hosted CC
  through `/clear` + the wake turn). The remaining gap is only the hook's own detection+emit running
  inside a real CC (the on-node validation issued the self-send manually = exactly what the hook emits).
- **T3 — SessionStart injection E2E.** `session-start.sh` consuming `api psyche-download` and injecting
  it as additionalContext into a live CC. Validated halves: the verb's full contract (CI,
  `psyche-download-int.sh`) and the pure append/skip logic (`tests/hooks-parse.sh`). The remaining gap
  is the hook calling the verb + emitting the result inside a real CC.

Both light up when the v0.7.0 plugin lands on a node (the spt-hosted CC then runs THIS repo's hooks,
not the older published `cplugs sptc`). Until then the spt-core-dependent behavior is fully proven.

## Open items

- Macro 500ms timing adequacy post-`/clear` — validated on-node 2026-06-24 (the wake turn landed
  cleanly after the clear); revisit only if a slower host shows a race.
- Publishing the v0.7.0 adapter (`.spt` re-cut + release) so the new plugin/binary ship — operator call.


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

## Deferred validations (dogfood-after-plugin-land — LOGGED, not CI)

doyle's gate ruling (2026-06-24): the spt-core-owned halves are deterministic CI; the harness-glue
E2Es genuinely need the NEW plugin installed + a real CC session, so they stay a dogfood tier —
exercised once the v0.7.0 plugin is published/installed, not covered by an automated gate.

- **T2 — PostToolUse write-detect E2E.** The hook (`post-tool-use.sh`) detecting `!!checkpoint!!` in
  a live agent's own `Write` of `.claude/<id>-commune.md` and firing the self-send. Validated halves:
  the binary clear+wake branch (CI, `translate-proof-int.sh`) and the LIVE self-send loopback
  (manual, on-node 2026-06-24 — a real `spt send --json-payload` self-send drove a spt-hosted CC
  through `/clear` + the wake turn). The remaining gap is only the hook's own detection+emit running
  inside a real CC (the on-node validation issued the self-send manually = exactly what the hook emits).
- **T3 — SessionStart injection E2E.** `session-start.sh` consuming `api psyche-download` and injecting
  it as additionalContext into a live CC. Validated halves: the verb's full contract (CI,
  `psyche-download-int.sh`) and the pure append/skip logic (`tests/hooks-parse.sh`). The remaining gap
  is the hook calling the verb + emitting the result inside a real CC.

Both light up when the v0.7.0 plugin lands on a node (the spt-hosted CC then runs THIS repo's hooks,
not the older published `cplugs sptc`). Until then the spt-core-dependent behavior is fully proven.

## Open items

- Macro 500ms timing adequacy post-`/clear` — validated on-node 2026-06-24 (the wake turn landed
  cleanly after the clear); revisit only if a slower host shows a race.
- Publishing the v0.7.0 adapter (`.spt` re-cut + release) so the new plugin/binary ship — operator call.

# CHECKPOINT-COMMUNE-PLAN.md — JIT plan

> Two operator-requested features + one foundational parity gap they surfaced. Grilled with
> doyle (spt-core) to map the public-surface boundary; design is locked against frozen contracts.
> Authored 2026-06-24 (perri). Companion ADR: `docs/adr/0004-checkpoint-via-self-send-loopback.md`
> (to write). Glossary terms landed in `CONTEXT.md` (`checkpoint`, `checkpoint trigger`).

## Scope

Three deliverables, separable:

1. **Feature 1 — multi-line `<EVENT>` envelope** (cosmetic; visual distinction of inbound idle msgs).
2. **Feature 2 — checkpoint communes** (agent-driven context-compaction: flagged commune → auto
   `/clear` → wake; re-seed carried by spt-core's resume verb).
3. **Parity wiring — `psyche-download` at SessionStart** (foundational; closes a pre-existing gap
   where claude-spt live agents get NO durable resume context today — only the identity brief).

## Hard public-surface findings (logged separately)

- **F-0xx psyche-download verb absent** — `download_psyche_context` (spt-core resume.rs:88) is
  exported-but-unexposed (zero `ApiCmd` callers). No published `spt` verb let an adapter pull resume
  context. CONFIRMED spt-core gap. **RESOLVED-PENDING: v0.15.0 W5** exposes the verb (frozen contract
  below) + W4 doc work. Foundational — claude-spt resume context is broken without it.
- **F-0xx PreToolUse poll missing** — claude-spt polls only on `UserPromptSubmit` (between turns).
  Legacy spt also polls+injects on **PreToolUse** = the mid-turn message-delivery half of live-agent
  reachability. Standalone parity gap; not yet wired here.
- **F-0xx subagent-perch reachability** — wiring is present (`subagent-start.sh` → `api worker-start`,
  `hostable_types` includes `Worker`); runtime reachability (`spt send` to a worker) is UNVERIFIED.
  Validation item, not a known gap.

## Frozen contracts to build against

### A. `{text}` is byte-verbatim (doyle, this thread)
spt-core writes `{text}` to the PTY verbatim **including `\n`**; `{key:enter}`→`\r`. There is NO
win32 key-encoding in spt-core (the DECSET-9001 theory was debunked F-019 misdirection). Whether CC
renders a raw `\n` as a soft-newline vs submit is **CC's behavior = our empirical test** (T1 below).
`{key:"shift+enter"}` is NOT recognized (key_to_bytes → None → silently skipped) — do not use.
Doc-gap (key-vocab not enumerated; `{text}`-verbatim unstated) → doyle folding into **v0.15.0 W4**.

### B. `psyche-download` resume verb — FROZEN Tier-1 (doyle, operator-approved; ships v0.15.0 W5)
```
spt api psyche-download <id> [--session-id <sid>]
  <id>         = self/agent id
  --session-id = optional (rotated SessionStart sid, for stamping; accepted, may be unused Tier-1)
  auth-gated like sibling id-scoped verbs (api poll / api presence) — pass link/auth token as
    trailing arg IFF our other api calls do
  project resolved internally from the endpoint's bound cwd — NO --project arg

STDOUT (inject VERBATIM as SessionStart additionalContext):
  <live-role>…</live-role>                       (if a role authored; else omitted)
  <live-context>…</live-context>                 (durable agents/<id>/live-context.md)
  <project-context-resolved name="<proj>"/>      (sentinel — present iff in a resolved project)
  <project-context>…</project-context>           (durable projects/<proj>/<id>/project-context.md)
  <pending-commune>…raw drop body…</pending-commune>   (ONLY if not-yet-synthesized; TRIGGER STRIPPED core-side)
  <pending-signoff>…raw drop body…</pending-signoff>   (likewise for signoff)

Empty/no-context → NO-CONTEXT signal on stderr (mirrors legacy $LIVE psyche-download) → inject nothing.
```
FRESHNESS: `<pending-*>` present iff a drop is not-yet-synthesized → one call always returns
durable + freshest un-synthesized delta. Race-free (core owns read+ingest; no adapter TOCTOU).
Self-clearing (post-synthesis the slice vanishes; no dup).
DEFERRED Tier-2 (NOT in verb yet, additive-forward): `<psyche-stamp/>`/`<current/>`/drift-directive,
`<memformat>`, Pulse Log. Separate parity item.

## Design decisions & invariants (the grill output)

- **Cyan color is impossible.** SGR bytes in `{text}` are eaten by CC's input box (win32-input-mode);
  CC user-turns are theme-fixed; no markdown/setting/control-seq colors a turn. PROVEN empirically +
  CC-guide. → multi-line plain-text framing is the only visual-distinction path. NO finding logged
  (operator: skip the color finding).
- **Self-send loopback works** (PROVEN this session): `spt send --from <id> <id>` routes through the
  endpoint's OWN translation binary and lands as a delivered EVENT. This is the checkpoint trigger path.
- **INLINE-PRE-CLEAR INVARIANT (load-bearing, ADR-worthy):** the agent authors+drops its commune
  INLINE during its own `/sptc:commune --checkpoint` turn (it IS the authoring LLM, pre-clear); the
  hook fires `/clear` strictly AFTER the file is on disk. This is doyle's "Shape 1" — the
  psyche-stale-after-clear gap does NOT apply. If checkpoint is ever rerouted through the
  boundary/resume-Self refresh path ("Shape 2"), the stale gap returns. Never defer authoring to post-clear.
- **Trigger marker = literal `!!checkpoint!!`** (operator-specified). ONE = checkpoint w/ default
  wake; a PAIR brackets a custom wake directive. **Strip is core-side** (todlando, W5 verb @20bfc1f):
  the verb's `<pending-commune>` arrives with all `!!checkpoint!!` tokens stripped, inter-marker text
  kept — no adapter strip. (Open: durable-ingest strip so the marker doesn't persist in
  live-context.md post-synthesis — doyle/todlando's call.)
- **Re-seed is core-owned** — no adapter-side raw drop reading (would TOCTOU core's pulse-delete,
  ingest.rs:161). The single `psyche-download` call carries durable + pending.

## Tasks

### T1 — Feature 1: multi-line envelope (binary) · REQ-DIST-IDLE-TRANSLATE (extend)
- **Empirical gate first:** verify CC soft-newlines on a raw `\n` in `{text}` (throwaway probe binary
  emitting `{text:"A"}{key? no}` → `{text:"A\nB"}` …; confirm ONE user turn, two lines, no early submit).
- If confirmed: `commands_for_event` splits the envelope into `opening-tag` `\n` `body` `\n`
  `closing-tag` (newline after the first `>`, before the final `</EVENT>`), embedding raw `\n` in `{text}`.
- Relax the CR/LF→space sanitizer for CC (it's over-defensive per doyle) — but only after T1 verify;
  keep neutralizing genuinely stray/internal newlines that would split the injection.
- Update the 11 binary tests.

### T2 — Feature 2: checkpoint mechanics
- **T2a `/sptc:commune` → full-fat skill doc + `--checkpoint`** (plugin/sptc/skills/commune/SKILL.md;
  drop the fetch-stub form so live agents are natively aware). Document: single `!!checkpoint!!` =
  default wake "Proceed with next steps"; DOUBLE markers = the text between them is the custom wake.
  Add a `[[hints]]` entry. Verify the commune body format matches the daemon's two-slice
  `<live-context>/<project-context>` envelope (or document the freeform→ingest behavior).
- **T2b PostToolUse hook (NEW)** — matcher Write; on Write of `.claude/<id>-commune.md`: scan
  `tool_input.content` (NOT a file re-read) for `!!checkpoint!!`; if present → `spt api state idle`
  → `spt send --from <id> <id> <wire-sentinel[+wake-text]>`. Resolve `<id>` (SPT_ENDPOINT_ID / whoami);
  guard to spt-hosted live endpoints only. Add `[hooks.PostToolUse]` to manifest + hooks.json.
  - **Wire-sentinel format:** `<reserved-prefix><wake-text>` (e.g. `__SPTC_CHECKPOINT_v1__…`),
    single-line-safe through the EVENT envelope; empty tail → default wake. Parse the double-marker
    inner text from `tool_input.content`.
- **T2c binary checkpoint branch** — `commands_for_line`: if the envelope body matches the
  wire-sentinel prefix → emit the clear+wake macro INSTEAD of normal delivery:
  `ctrl+s · 50ms · /clear · enter · 500ms · <wake-text> · enter · commit` (leading `ctrl+s` REQUIRED
  to stash lingering input; `<wake-text>` = parsed custom or default). New REQ (e.g.
  REQ-DIST-CHECKPOINT-MACRO).

### T3 — Parity wiring: psyche-download at SessionStart · new REQ (e.g. REQ-DIST-RESUME-CONTEXT)
- session-start.sh: on bind|boundary (perched), after existing logic, run
  `spt api psyche-download <self-id> [--session-id <sid>]`; inject stdout as additionalContext;
  skip on NO-CONTEXT (stderr). Boundary stays rotation-only (unchanged). Build against frozen
  contract B; **validate end-to-end only when v0.15.0 W5 ships** (doyle pings).

## Dependencies / sequencing

- T1 buildable+testable now (contract A frozen; needs the empirical gate).
- T2 buildable+testable now (self-send proven; mechanics have ZERO spt-core dep — the clear+wake
  fires regardless). Re-seed RICHNESS depends on T3+W5, but mechanics don't.
- T3 codeable now against frozen contract B; **un-validatable until v0.15.0 W5 ships**.
- PreToolUse poll + subagent-perch-reachability = separate parity items (not in this plan's build).

## Validation gate (per AGENTS.md)

- `cargo build` (binary) green; binary tests green.
- `traceable-reqs check` green (REQs added to `traceable-reqs.toml` FIRST, evidence tagged in-commit).
- T1: live endpoint shows the envelope across lines, one user turn.
- T2: live endpoint — flagged commune → `/clear` fires → wake turn appears (default + custom).
- T3: deferred to W5 ship — `psyche-download` output injected at SessionStart, durable+pending present.

## Open items

- Exact `<id>` + auth-token threading for `api psyche-download` (mirror our other `api` calls).
- Commune body format (two-slice envelope vs freeform) — confirm in T2a.
- Macro 500ms timing adequacy post-`/clear` (validate in T2).

+## Deferred validations (dogfood-after-plugin-land — LOGGED, not CI)

+doyle's gate ruling (2026-06-24): the spt-core-owned halves are deterministic CI; the harness-glue

+E2Es genuinely need the NEW plugin installed + a real CC session, so they stay a dogfood tier —

+exercised once the v0.7.0 plugin is published/installed, not covered by an automated gate.

+- **T2 — PostToolUse write-detect E2E.** The hook (`post-tool-use.sh`) detecting `!!checkpoint!!` in

+  a live agent's own `Write` of `.claude/<id>-commune.md` and firing the self-send. Validated halves:

+  the binary clear+wake branch (CI, `translate-proof-int.sh`) and the LIVE self-send loopback

+  (manual, on-node 2026-06-24 — a real `spt send --json-payload` self-send drove a spt-hosted CC

+  through `/clear` + the wake turn). The remaining gap is only the hook's own detection+emit running

+  inside a real CC (the on-node validation issued the self-send manually = exactly what the hook emits).

+- **T3 — SessionStart injection E2E.** `session-start.sh` consuming `api psyche-download` and injecting

+  it as additionalContext into a live CC. Validated halves: the verb's full contract (CI,

+  `psyche-download-int.sh`) and the pure append/skip logic (`tests/hooks-parse.sh`). The remaining gap

+  is the hook calling the verb + emitting the result inside a real CC.

+Both light up when the v0.7.0 plugin lands on a node (the spt-hosted CC then runs THIS repo's hooks,

+not the older published `cplugs sptc`). Until then the spt-core-dependent behavior is fully proven.

-- Exact `<id>` + auth-token threading for `api psyche-download` (mirror our other `api` calls).

-- Commune body format (two-slice envelope vs freeform) — confirm in T2a.

-- Macro 500ms timing adequacy post-`/clear` (validate in T2).

+- Macro 500ms timing adequacy post-`/clear` — validated on-node 2026-06-24 (the wake turn landed

+  cleanly after the clear); revisit only if a slower host shows a race.

+- Publishing the v0.7.0 adapter (`.spt` re-cut + release) so the new plugin/binary ship — operator call.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
traceable-reqs check 2>&1 | grep -E "REQ-DIST-RESUME-CONTEXT|REQ-DIST-CHECKPOINT-COMMUNE"
traceable-reqs check >/dev/null 2>&1 && echo "CHECK GREEN" || echo "CHECK RED"
echo "=== syntax all touched ci/test sh ==="; for f in ci/psyche-download/psyche-download-int.sh; do sh -n "$f" && echo "ok $f"; done


  [OK] REQ-DIST-CHECKPOINT-COMMUNE  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
  [OK] REQ-DIST-RESUME-CONTEXT  required: [impl, unit, int]  stages: -doc +impl +unit +int
CHECK GREEN
=== syntax all touched ci/test sh ===
ok ci/psyche-download/psyche-download-int.sh


Both REQs green with int active. Let me check whether there's an ints-runner/CI workflow listing the proof-ints, to register the new script alongside its siblings.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== references to translate-proof-int / digest-proof-int ==="
grep -rn "translate-proof-int\|digest-proof-int\|proof-int" --include='*.sh' --include='*.yml' --include='*.yaml' --include='*.md' . 2>/dev/null | grep -v 'ci/idle-translate/translate-proof-int.sh:\|ci/digest/digest-proof-int.sh:' | head -20


=== references to translate-proof-int / digest-proof-int ===
./CHECKPOINT-COMMUNE-PLAN.md:146:  the binary clear+wake branch (CI, `translate-proof-int.sh`) and the LIVE self-send loopback
./docs/SPT-CORE-FINDINGS.md:314:(`ci/digest/digest-proof-int.sh`, `REQ-DIST-DIGEST-EXTRACTOR`) **skips on the exact substitution
./docs/SPT-CORE-FINDINGS.md:996:**Workaround (in `ci/idle-translate/translate-proof-int.sh`).** Stage a disposable install dir that
./docs/SPT-CORE-FINDINGS.md:999:commit: yes). **Knock-on:** the older `ci/digest/digest-proof-int.sh` uses the bare-file form and so is
./docs/SPT-CORE-FINDINGS.md:1005:install. Tiny, and it un-stales `digest-proof-int.sh` too.
./V13-ADAPTER-PLAN.md:41:- ✅ **REQ-DIST-IDLE-TRANSLATE → int** (EMIT half): `ci/idle-translate/translate-proof-int.sh` — real


Proof-ints are standalone (not centrally wired); mine follows suit. Commit the post-publish work: manifest bump + int script + REQ int-activation + plan deferred section.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
git status --short
echo "=== final gate sweep ==="
traceable-reqs check >/dev/null 2>&1 && echo "traceable GREEN" || echo "traceable RED"
sh ci/manifest/check-manifest.sh >/dev/null 2>&1 && echo "manifest-schema PASS" || echo "FAIL"
sh ci/psyche-download/psyche-download-int.sh >/dev/null 2>&1 && echo "psyche-download-int PASS" || echo "psyche-download-int FAIL"


 M CHECKPOINT-COMMUNE-PLAN.md
 M adapter/claude-spt.toml
 M traceable-reqs.toml
?? ci/psyche-download/
=== final gate sweep ===
traceable GREEN
manifest-schema PASS
psyche-download-int PASS


cd "C:/Users/decid/Documents/projects/spt-claude-code"
git add adapter/claude-spt.toml traceable-reqs.toml CHECKPOINT-COMMUNE-PLAN.md ci/psyche-download/psyche-download-int.sh
git commit -q -F - <<'EOF'
feat(checkpoint-commune): v0.15.0 publish — floor bump, light the deferred ints

spt-core v0.15.0 published (counter 34); validated on-node. The checkpoint-
commune wave's spt-core-dependent surfaces are now proven live:

- T2 LIVE LOOPBACK validated: a real `spt send --from <id> <id> --json-payload
  '{"checkpoint":"v1","wake":…}'` self-send composed the json envelope attr,
  looped back through the translation binary, and fired the /clear + wake macro
  on a spt-hosted CC (transcript: user turn `/clear`, next turn = the wake).
- T3 psyche-download validated: NO-CONTEXT + POSITIVE (pending-commune verbatim)
  + AUTH, all four contract behaviors on real spt 0.15.0.
- Send axes (--idle-only/--active-only/--prefer-native) + the Windows
  no-window-flash fix confirmed.

Changes:
- Bump min_spt_core_version 0.13.2 -> 0.15.0 + adapter version 0.6.2 -> 0.7.0
  (T2's checkpoint self-send needs `send --json-payload`; T3's resume pull needs
  the `api psyche-download` verb — both land in 0.15.0).
- New deterministic int: ci/psyche-download/psyche-download-int.sh — capability-
  detect CI, NO real CC (bind-creates a throwaway perch): asserts verb-present,
  NO-CONTEXT, AUTH-reject, POSITIVE pending-commune. Activate REQ-DIST-RESUME-
  CONTEXT int.
- Per doyle's gate ruling: the two harness-glue E2Es (PostToolUse write-detect,
  SessionStart injection) stay a LOGGED dogfood-after-plugin-land tier
  (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations) — they need the new plugin
  installed + real CC; the spt-core-dependent halves are fully proven now.

traceable-reqs GREEN.

Co-authored by: perri
EOF
echo "commit exit=$?"; git log -5 --format='%h %s'


commit exit=0
a9aa714 feat(checkpoint-commune): v0.15.0 publish — floor bump, light the deferred ints
38044e2 feat(resume): T3 — pull durable resume context at SessionStart (psyche-download)
e27e0d3 feat(checkpoint): T2 — agent-driven checkpoint communes via self-send loopback
befa592 feat(idle-translate): T1 — frame inbound <EVENT> across multiple lines
211a5cc docs(checkpoint-commune): land design — ADR-0004, F-020/21/22, 5 REQ stubs


Committed (`a9aa714`). Ping doyle to gate the traceable diff (he asked).


cd "C:/Users/decid/Documents/projects/spt-claude-code"
$OWL send doyle perri <<'EOF'
TRACEABLE DIFF — ready for your gate. Committed a9aa714.

- REQ-DIST-RESUME-CONTEXT: impl+unit → impl+unit+INT. New script ci/psyche-download/psyche-download-int.sh, tagged [int->REQ-DIST-RESUME-CONTEXT]. Deterministic capability-detect CI, NO real CC — bind-creates a throwaway perch (`api bind <id> --set-session-id --subnet <discovered>`), asserts: verb-present, NO-CONTEXT (exit 0/empty stdout/NO-CONTEXT:<id> stderr), AUTH (wrong --session-id rejected), POSITIVE (not-yet-synthesized commune → <pending-commune> VERBATIM). GREEN on-node, idempotent, purges + rm's its perch/commune on exit.
- REQ-DIST-CHECKPOINT-COMMUNE: stays doc+impl+unit+int (EMIT-half CI unchanged); comment updated to record the LIVE loopback validation (2026-06-24) + the deferred PostToolUse write-detect dogfood.
- Both harness-glue E2Es LOGGED as dogfood-after-plugin-land in CHECKPOINT-COMMUNE-PLAN.md §Deferred validations (NOT CI) — per your split.
- min_spt_core 0.13.2→0.15.0, adapter version 0.6.2→0.7.0.

traceable-reqs check GREEN. Both REQs full-stage. Manifest-schema PASS. The whole checkpoint-commune wave is built + validated to the CI tier; only the new-plugin-install dogfood E2Es remain (gated on a v0.7.0 publish — operator call). Your gate on the diff?
EOF
echo sent=$?


➜ SENT:doyle
sent=0


Update memory to reflect the wave is now validated on published 0.15.0 (not blocked).


Edit

---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT+committed (multi-line idle, checkpoint communes, psyche-download resume); deferred ints wait on spt-core v0.15.0/counter-34 publish.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT & committed 2026-06-24 (perri), all gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone.

Commits on main: `211a5cc` doc-capture (ADR-0004, F-020/21/22, 5 REQ stubs) → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3.

---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

**VALIDATED on-node (real spt 0.15.0, spt-hosted CC endpoint):**
- **T2 LIVE LOOPBACK** ✅ — real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` → json envelope attr → translation binary → /clear+wake macro. Transcript proved: user turn `/clear`, next turn = the wake. E2E.
- **T3 psyche-download** ✅ — all 4 contract behaviors: NO-CONTEXT (exit 0/empty stdout/NO-CONTEXT stderr), POSITIVE (`<pending-commune>` verbatim from a dropped commune), AUTH (wrong --session-id rejected). Deterministic CI: `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch via `api bind --set-session-id --subnet <discovered>`, NO real CC; purges on exit).
- Send axes (--idle-only/--active-only=renamed-deferred/--prefer-native) + Windows no-window-flash fix confirmed.

**STILL DEFERRED (dogfood-after-plugin-land, LOGGED not CI — doyle gate ruling):** the two harness-glue E2Es need the NEW v0.7.0 plugin installed + real CC (the spt-hosted CC currently runs the OLD `cplugs sptc 0.1.4` hooks): (a) T2 PostToolUse write-detect (`post-tool-use.sh` detecting `!!checkpoint!!` in a Write + self-sending); (b) T3 SessionStart injection (`session-start.sh` consuming the pull + injecting into live CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`. Light up on a v0.7.0 publish (operator call).


<tool_use_error>String to replace not found in file.
String: ---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT+committed (multi-line idle, checkpoint communes, psyche-download resume); deferred ints wait on spt-core v0.15.0/counter-34 publish.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT & committed 2026-06-24 (perri), all gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone.

Commits on main: `211a5cc` doc-capture (ADR-0004, F-020/21/22, 5 REQ stubs) → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3.
(note: Edit also tried swapping \uXXXX escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. Re-read the file and copy the exact surrounding text.)</tool_use_error>

Error: String to replace not found in file.
String: ---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT+committed (multi-line idle, checkpoint communes, psyche-download resume); deferred ints wait on spt-core v0.15.0/counter-34 publish.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT & committed 2026-06-24 (perri), all gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone.

Commits on main: `211a5cc` doc-capture (ADR-0004, F-020/21/22, 5 REQ stubs) → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3.
(note: Edit also tried swapping \uXXXX escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. Re-read the file and copy the exact surrounding text.)


Let me rewrite the memory file wholesale:


Write

---
name: checkpoint-commune-wave-built
description: Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain.
metadata:
  type: project
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

**What got built (don't re-derive):**
- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits the `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). Empirical gate proved CC soft-newlines a bare `\n`.
- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) → PostToolUse(Write) hook `post-tool-use.sh` → `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":…}'`. Binary: parse `json="…"` envelope ATTR → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint` SKILL.md + `[[hints]]`.
- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

**VALIDATED on-node (real spt 0.15.0, spt-hosted CC endpoint ckptval):**
- **T2 LIVE LOOPBACK** — real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` fired the macro E2E. Transcript: user turn `/clear`, next turn = the wake.
- **T3 psyche-download** — all 4 contract behaviors. Deterministic CI: `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch via `api bind <id> --set-session-id <sid> --subnet <discovered-from-`spt endpoint list`>`, NO real CC; purge `--force --yes` on exit). Auth needs `--session-id` matching info.json (the CC post_spawn uuid, NOT the endpoint-run session).
- Send axes (--idle-only/--active-only=renamed-deferred/--prefer-native) + Windows no-window-flash fix confirmed.

**STILL DEFERRED (dogfood-after-plugin-land, LOGGED not CI — doyle gate ruling):** the two harness-glue E2Es need the NEW v0.7.0 plugin installed + real CC (the spt-hosted CC currently runs the OLD `cplugs sptc 0.1.4` hooks): (a) T2 PostToolUse write-detect (`post-tool-use.sh` detecting `!!checkpoint!!` in a Write + self-sending); (b) T3 SessionStart injection (`session-start.sh` consuming the pull + injecting into live CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`. Light up on a v0.7.0 publish (operator call) — the remaining open item.

**Probe/validation recipe:** swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`) with a dev binary (back up first, restore after), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id is in `owlery/<id>/info.json`, transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`, grep a unique token. Teardown: `spt endpoint shutdown <id>` + reap the binary + restore. Comms: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends.


<owl_messages>
<owl_message from="doyle" priority="high">Can't gate blind — a9aa714 isn't on the SaberMage/spt-claude-code remote yet (422 no-commit). Push the branch (or a9aa714 directly) so I can eyeball before ruling. Specifically reviewing:
  1. ci/psyche-download/psyche-download-int.sh — the AUTH case (wrong --session-id MUST actually reject, not just no-output), and the cleanup (perch+commune rm on exit incl. failure paths / trap).
  2. [int->REQ-DIST-RESUME-CONTEXT] tagged ON the real assertion, not the file top.
  3. The CHECKPOINT-COMMUNE comment update + §Deferred validations log.
Push + ping the branch name; I'll pull the diff and gate.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-claude-code\memory\checkpoint-commune-wave-built.md has been updated successfully. (file state is current in your context — no need to Read it back)

---
name: checkpoint-commune-wave-built
description: "Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain."
metadata:
  node_type: memory
  type: project
  originSessionId: ddb3691d-6635-4e91-bb00-3365d2fec908
---

Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

**What got built (don't re-derive):**
- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits the `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). Empirical gate proved CC soft-newlines a bare `\n`.
- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) → PostToolUse(Write) hook `post-tool-use.sh` → `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":…}'`. Binary: parse `json="…"` envelope ATTR → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint` SKILL.md + `[[hints]]`.
- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

**VALIDATED on-node (real spt 0.15.0, spt-hosted CC endpoint ckptval):**
- **T2 LIVE LOOPBACK** — real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` fired the macro E2E. Transcript: user turn `/clear`, next turn = the wake.
- **T3 psyche-download** — all 4 contract behaviors. Deterministic CI: `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch via `api bind <id> --set-session-id <sid> --subnet <discovered-from-`spt endpoint list`>`, NO real CC; purge `--force --yes` on exit). Auth needs `--session-id` matching info.json (the CC post_spawn uuid, NOT the endpoint-run session).
- Send axes (--idle-only/--active-only=renamed-deferred/--prefer-native) + Windows no-window-flash fix confirmed.

**STILL DEFERRED (dogfood-after-plugin-land, LOGGED not CI — doyle gate ruling):** the two harness-glue E2Es need the NEW v0.7.0 plugin installed + real CC (the spt-hosted CC currently runs the OLD `cplugs sptc 0.1.4` hooks): (a) T2 PostToolUse write-detect (`post-tool-use.sh` detecting `!!checkpoint!!` in a Write + self-sending); (b) T3 SessionStart injection (`session-start.sh` consuming the pull + injecting into live CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`. Light up on a v0.7.0 publish (operator call) — the remaining open item.

**Probe/validation recipe:** swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`) with a dev binary (back up first, restore after), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id is in `owlery/<id>/info.json`, transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`, grep a unique token. Teardown: `spt endpoint shutdown <id>` + reap the binary + restore. Comms: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends.

-description: "Checkpoint-commune wave T1/T2/T3 BUILT+committed (multi-line idle, checkpoint communes, psyche-download resume); deferred ints wait on spt-core v0.15.0/counter-34 publish."

+description: "Checkpoint-commune wave T1/T2/T3 BUILT + VALIDATED on published spt-core v0.15.0 (multi-line idle, checkpoint communes, psyche-download resume); only new-plugin-install dogfood E2Es remain."

-Checkpoint-commune wave (operator-requested) — BUILT & committed 2026-06-24 (perri), all gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone.

+Checkpoint-commune wave (operator-requested) — BUILT, committed, and VALIDATED on published spt-core v0.15.0 (counter 34) 2026-06-24 (perri). All gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone. Adapter now v0.7.0, min_spt_core 0.15.0.

-Commits on main: `211a5cc` doc-capture (ADR-0004, F-020/21/22, 5 REQ stubs) → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3.

+Commits on main: `211a5cc` doc-capture → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `a9aa714` v0.15.0 publish (floor bump + light deferred ints).

-- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE, impl+unit+int). EMPIRICAL GATE PASSED first: probe binary on a live spt-hosted endpoint → a two-line `{text}` landed as ONE CC user turn, `\n` preserved, no early submit. `frame_envelope` splits the envelope opening-tag/`\n`/body/`\n`/closing-tag AFTER stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>…</EVENT>` (incl. EVENT-PART). Cyan stays impossible.

-- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE, doc+impl+unit+int). Wire-sentinel = doyle's NEW `spt send --json-payload '{"checkpoint":"v1","wake":…}'` (v0.15.0 W4) — rides as an opaque `json="…"` ATTR on the `<EVENT>` envelope, NOT the old string-prefix sniff (collision-proof). Binary: extract json attr → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro (ctrl+s·50ms·/clear·enter·500ms·wake·enter·commit, default "Proceed with next steps"). PostToolUse(Write) hook `post-tool-use.sh`: scan `!!checkpoint!!` in `.claude/<id>-commune.md` content → `api state idle` → self-send. Single trigger=default wake; PAIR brackets custom wake. spt-core v0.15.0 STRIPS the `!!checkpoint!!` marker core-side on ingest → one-shot. Full-fat `/sptc:commune --checkpoint` SKILL.md + checkpoint `[[hints]]`.

-- **T3 resume-context parity** (REQ-DIST-RESUME-CONTEXT, impl+unit; int DEFERRED). Closes F-020 (claude-spt rehydrated NO durable context). `sptc_psyche_download` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM below the identity brief. Fires on bind AND boundary (boundary = the checkpoint re-seed path). Mirrors `api poll` auth (--session-id, no --project). Graceful skip on NO-CONTEXT / pre-0.15.0 absent verb.

+- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE impl+unit+int). `frame_envelope` splits the `<EVENT>` opening-tag/`\n`/body/`\n`/closing-tag after stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>` (incl. EVENT-PART). Empirical gate proved CC soft-newlines a bare `\n`.

+- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE doc+impl+unit+int). `!!checkpoint!!` write-trigger (single=default wake "Proceed with next steps"; PAIR brackets custom wake) → PostToolUse(Write) hook `post-tool-use.sh` → `api state idle` + self-send `spt send --json-payload '{"checkpoint":"v1","wake":…}'`. Binary: parse `json="…"` envelope ATTR → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro. Marker one-shot (core strips on ingest). Full-fat `/sptc:commune --checkpoint` SKILL.md + `[[hints]]`.

+- **T3 resume parity** (REQ-DIST-RESUME-CONTEXT impl+unit+int). `sptc_psyche_download` + `sptc_append_resume` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM. Fires bind AND boundary (boundary = checkpoint re-seed path). Closes F-020.

-**BLOCKED ON spt-core v0.15.0 publish (counter 34, PR#37 @18d5045 tagged, deployah release run firing):** doyle PINGS when hashes publish → then (1) bump `min_spt_core_version` 0.13.2→0.15.0 in adapter/claude-spt.toml, (2) light the two DEFERRED ints — the live `spt send --json-payload` self-send loopback (T2) + the psyche-download SessionStart injection (T3). EMIT-halves already proven via translate-proof on 0.14.2 (crafted the json= attr envelope directly).

+**VALIDATED on-node (real spt 0.15.0, spt-hosted CC endpoint ckptval):**

+- **T2 LIVE LOOPBACK** — real `spt send --from <id> <id> --json-payload '{"checkpoint":"v1","wake":…}'` fired the macro E2E. Transcript: user turn `/clear`, next turn = the wake.

+- **T3 psyche-download** — all 4 contract behaviors. Deterministic CI: `ci/psyche-download/psyche-download-int.sh` (bind-creates a throwaway perch via `api bind <id> --set-session-id <sid> --subnet <discovered-from-`spt endpoint list`>`, NO real CC; purge `--force --yes` on exit). Auth needs `--session-id` matching info.json (the CC post_spawn uuid, NOT the endpoint-run session).

+- Send axes (--idle-only/--active-only=renamed-deferred/--prefer-native) + Windows no-window-flash fix confirmed.

-Comms recipe unchanged: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends (PowerShell has no printf). Probe recipe: swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, grep `~/.claude/projects/<slug>/<cc-uuid>.jsonl` for a unique token, `spt endpoint shutdown <id>` + restore binary.

+**STILL DEFERRED (dogfood-after-plugin-land, LOGGED not CI — doyle gate ruling):** the two harness-glue E2Es need the NEW v0.7.0 plugin installed + real CC (the spt-hosted CC currently runs the OLD `cplugs sptc 0.1.4` hooks): (a) T2 PostToolUse write-detect (`post-tool-use.sh` detecting `!!checkpoint!!` in a Write + self-sending); (b) T3 SessionStart injection (`session-start.sh` consuming the pull + injecting into live CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`. Light up on a v0.7.0 publish (operator call) — the remaining open item.

+**Probe/validation recipe:** swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`) with a dev binary (back up first, restore after), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, perch session_id is in `owlery/<id>/info.json`, transcript at `~/.claude/projects/<slug>/<cc-uuid>.jsonl`, grep a unique token. Teardown: `spt endpoint shutdown <id>` + reap the binary + restore. Comms: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends.

---
name: checkpoint-commune-wave-built
description: "Checkpoint-commune wave T1/T2/T3 BUILT+committed (multi-line idle, checkpoint communes, psyche-download resume); deferred ints wait on spt-core v0.15.0/counter-34 publish."
metadata:
  node_type: memory
  type: project
  originSessionId: ddb3691d-6635-4e91-bb00-3365d2fec908
---

Checkpoint-commune wave (operator-requested) — BUILT & committed 2026-06-24 (perri), all gates GREEN. Builds on the [[f019-idle-submit-and-v0142]] keystone.

Commits on main: `211a5cc` doc-capture (ADR-0004, F-020/21/22, 5 REQ stubs) → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3.

- **T1 multi-line idle envelope** (REQ-DIST-IDLE-MULTILINE, impl+unit+int). EMPIRICAL GATE PASSED first: probe binary on a live spt-hosted endpoint → a two-line `{text}` landed as ONE CC user turn, `\n` preserved, no early submit. `frame_envelope` splits the envelope opening-tag/`\n`/body/`\n`/closing-tag AFTER stray-CR/LF neutralize; degenerate fallback for non-`<EVENT>…</EVENT>` (incl. EVENT-PART). Cyan stays impossible.
- **T2 checkpoint communes** (REQ-DIST-CHECKPOINT-COMMUNE, doc+impl+unit+int). Wire-sentinel = doyle's NEW `spt send --json-payload '{"checkpoint":"v1","wake":…}'` (v0.15.0 W4) — rides as an opaque `json="…"` ATTR on the `<EVENT>` envelope, NOT the old string-prefix sniff (collision-proof). Binary: extract json attr → xml-attr-unescape (`&` LAST) → serde → `checkpoint=="v1"` → clear+wake macro (ctrl+s·50ms·/clear·enter·500ms·wake·enter·commit, default "Proceed with next steps"). PostToolUse(Write) hook `post-tool-use.sh`: scan `!!checkpoint!!` in `.claude/<id>-commune.md` content → `api state idle` → self-send. Single trigger=default wake; PAIR brackets custom wake. spt-core v0.15.0 STRIPS the `!!checkpoint!!` marker core-side on ingest → one-shot. Full-fat `/sptc:commune --checkpoint` SKILL.md + checkpoint `[[hints]]`.
- **T3 resume-context parity** (REQ-DIST-RESUME-CONTEXT, impl+unit; int DEFERRED). Closes F-020 (claude-spt rehydrated NO durable context). `sptc_psyche_download` wired into `session-start.sh` bind|boundary brief → injects `spt api psyche-download <id>` stdout VERBATIM below the identity brief. Fires on bind AND boundary (boundary = the checkpoint re-seed path). Mirrors `api poll` auth (--session-id, no --project). Graceful skip on NO-CONTEXT / pre-0.15.0 absent verb.

**BLOCKED ON spt-core v0.15.0 publish (counter 34, PR#37 @18d5045 tagged, deployah release run firing):** doyle PINGS when hashes publish → then (1) bump `min_spt_core_version` 0.13.2→0.15.0 in adapter/claude-spt.toml, (2) light the two DEFERRED ints — the live `spt send --json-payload` self-send loopback (T2) + the psyche-download SessionStart injection (T3). EMIT-halves already proven via translate-proof on 0.14.2 (crafted the json= attr envelope directly).

Comms recipe unchanged: legacy `$OWL send doyle perri` (new-spt `spt send doyle` = NO_PERCH); Bash tool for sends (PowerShell has no printf). Probe recipe: swap the installed `cc-spt-idle-translate.exe` (Harness Pointer at `AppData/Local/spt-core/adapters/_github/SaberMage-spt-claude-code/`), `spt endpoint run --adapter claude-spt --id <id> --create --start --subnet SPT_DEV`, grep `~/.claude/projects/<slug>/<cc-uuid>.jsonl` for a unique token, `spt endpoint shutdown <id>` + restore binary.


doyle needs the commits on the remote to gate. ACK + push. This project's established workflow is direct-to-main (git log + the doyle-review loop confirm it), and this is the project's own remote for peer review (not a release). ACK first.


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
$OWL send doyle perri <<'EOF'
ACK — pushing now so you can pull the diff. Confirming your three review points up front: (1) AUTH case uses `if spt ... psyche-download <id> --session-id WRONG-<sid>; then FAIL` — it gates on the command's EXIT (AUTH_REFUSED → non-zero), not no-output; (2) `[int->REQ-DIST-RESUME-CONTEXT]` is tagged in the script's header block describing the assertions (not a bare file-top coverage tag) — I'll relocate it directly onto the assertion if you want it tighter; (3) trap cleanup runs `purge --force --yes` + `rm` the commune on EXIT/INT/TERM incl. failure paths. Branch = main (this project's direct-to-main flow). Pushing + will ping the SHA.
EOF
echo ack=$?
echo "=== remote + branch ==="; git remote -v 2>&1 | head -2; git rev-parse --abbrev-ref HEAD; git log origin/main..HEAD --oneline 2>/dev/null | head


➜ SENT:doyle
ack=0
=== remote + branch ===
origin	https://github.com/SaberMage/spt-claude-code.git (fetch)
origin	https://github.com/SaberMage/spt-claude-code.git (push)
main
a9aa714 feat(checkpoint-commune): v0.15.0 publish — floor bump, light the deferred ints
38044e2 feat(resume): T3 — pull durable resume context at SessionStart (psyche-download)
e27e0d3 feat(checkpoint): T2 — agent-driven checkpoint communes via self-send loopback
befa592 feat(idle-translate): T1 — frame inbound <EVENT> across multiple lines
211a5cc docs(checkpoint-commune): land design — ADR-0004, F-020/21/22, 5 REQ stubs


Before pushing, let me preemptively satisfy doyle's review point #2 — move the int tag from the header onto the real assertions (per AGENTS.md "never at file tops").


Edit

# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (docs/SPT-CORE-FINDINGS.md), NOT covered here
# (doyle's gate ruling, 2026-06-24). [int->REQ-DIST-RESUME-CONTEXT]
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch.

# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations),
# NOT covered here (doyle's gate ruling, 2026-06-24). The int evidence tag sits on the assertion block
# below, not here.
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch.


<owl_messages>
<owl_message from="doyle" priority="high">(1) and (3) good as described — exit-gated AUTH + trap on EXIT/INT/TERM is what I wanted. (2) YES relocate: per the binding traceable rule the tag goes ON or immediately above the real evidence, and for a test script the evidence is the assertion that exercises the contract — not a header block describing them. Move [int->REQ-DIST-RESUME-CONTEXT] directly above the POSITIVE not-yet-synthesized assertion (the core behavior). Do that, push, ping SHA — I'll pull + gate.</owl_message>
</owl_messages>

[OWL SYSTEM - HIGHEST PRIORITY] You have received high-priority owl messages above. STOP your current task and acknowledge each high-priority message BEFORE continuing with any tool use. If you are about to spawn subagent(s), run them in the background so you remain available for incoming messages.


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

# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations),
# NOT covered here (doyle's gate ruling, 2026-06-24). The int evidence tag sits on the assertion block
# below, not here.
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch.

#!/bin/sh
# Integration proof for the spt-core v0.15.0 `spt api psyche-download <id>` resume-context verb
# (REQ-DIST-RESUME-CONTEXT, T3). This is the CORE-CONTRACT half of the int: pure spt-core CLI
# behavior, deterministic, self-hosted, NO real CC needed — a throwaway perch is bind-created via
# `api bind` (which writes the perch record + session id directly, no broker/CC spawn), the four
# contract behaviors are asserted against a live 0.15.0 binary, then the perch is purged.
#
# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (docs/SPT-CORE-FINDINGS.md), NOT covered here
# (doyle's gate ruling, 2026-06-24). [int->REQ-DIST-RESUME-CONTEXT]
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.
set -u
ADAPTER=claude-spt
ID="sptc-pdint-$$"
SID="sptc-pdint-sess-$$"

command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }

# Verb capability-detect (lands in spt v0.15.0).
if ! spt api psyche-download --help >/dev/null 2>&1; then
  echo "SKIP: spt 'api psyche-download' absent (needs v0.15.0). Hook wiring: tests/hooks-parse.sh green."
  exit 0
fi
echo "ok   verb present: api psyche-download <id> --session-id"

# A subnet to home the throwaway perch (bind refuses to guess when the node holds >1 subnet).
SUBNET=$(spt endpoint list 2>/dev/null | sed -n 's/^SUBNET \([^ ]*\).*/\1/p' | head -1)
[ -n "$SUBNET" ] || { echo "SKIP: no subnet on this node to home the test perch"; exit 0; }

cleanup() {
  printf 'y\n' | spt endpoint purge "$ID" --force --yes >/dev/null 2>&1 || true
  rm -f ".claude/$ID-commune.md"
}
trap cleanup EXIT INT TERM

# Bind-create the perch (NO CC — api bind writes the perch record + session id directly).
if ! spt api --adapter "$ADAPTER" bind "$ID" --set-session-id "$SID" --subnet "$SUBNET" >/dev/null 2>&1; then
  echo "SKIP: could not bind-create the test perch (subnet $SUBNET)"
  exit 0
fi

rc=0
errf="${TMPDIR:-/tmp}/sptc-pdint-$$.err"

# 1. NO-CONTEXT: a fresh perch -> exit 0, EMPTY stdout, NO-CONTEXT:<id> on stderr (the hook skips).
out=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>"$errf"); ec=$?
err=$(cat "$errf" 2>/dev/null)
if [ "$ec" -eq 0 ] && [ -z "$out" ] && { printf '%s' "$err" | grep -q "NO-CONTEXT:$ID"; }; then
  echo "ok   NO-CONTEXT: fresh perch -> exit 0, empty stdout, NO-CONTEXT on stderr"
else
  echo "FAIL NO-CONTEXT contract: exit=$ec stdout=[$out] stderr=[$err]"; rc=1
fi

# 2. AUTH: a wrong --session-id is rejected (non-zero exit; AUTH_REFUSED).
if spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "WRONG-$SID" >/dev/null 2>&1; then
  echo "FAIL AUTH: a wrong --session-id was NOT rejected"; rc=1
else
  echo "ok   AUTH: wrong --session-id rejected"
fi

# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop).
MARK="PDINT-POS-$$"
mkdir -p .claude
printf '%s resume delta\n' "$MARK" > ".claude/$ID-commune.md"
got=""
i=0
while [ "$i" -lt 5 ]; do
  o=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>/dev/null)
  if printf '%s' "$o" | grep -q "$MARK"; then got="$o"; break; fi
  i=$((i + 1)); sleep 1
done
if { printf '%s' "$got" | grep -q '<pending-commune>'; } && { printf '%s' "$got" | grep -q "$MARK"; }; then
  echo "ok   POSITIVE: not-yet-synthesized commune returned VERBATIM as <pending-commune>"
else
  echo "FAIL POSITIVE: pending-commune not returned (got=[$got])"; rc=1
fi

rm -f "$errf"
[ "$rc" -eq 0 ] && { echo "PSYCHE-DOWNLOAD-INT OK"; exit 0; } || { echo "PSYCHE-DOWNLOAD-INT FAIL"; exit 1; }

-# it stays a LOGGED dogfood-after-plugin-land tier (docs/SPT-CORE-FINDINGS.md), NOT covered here

-# (doyle's gate ruling, 2026-06-24). [int->REQ-DIST-RESUME-CONTEXT]

+# it stays a LOGGED dogfood-after-plugin-land tier (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations),

+# NOT covered here (doyle's gate ruling, 2026-06-24). The int evidence tag sits on the assertion block


doyle confirms: tag goes directly above the POSITIVE assertion (the core behavior). ACK + relocate.


# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop).
MARK="PDINT-POS-$$"

# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop). This is
#    the core resume-context behavior the SessionStart hook injects. [int->REQ-DIST-RESUME-CONTEXT]
MARK="PDINT-POS-$$"


# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop). This is
#    the core resume-context behavior the SessionStart hook injects. [int->REQ-DIST-RESUME-CONTEXT]
MARK="PDINT-POS-$$"

#!/bin/sh
# Integration proof for the spt-core v0.15.0 `spt api psyche-download <id>` resume-context verb
# (REQ-DIST-RESUME-CONTEXT, T3). This is the CORE-CONTRACT half of the int: pure spt-core CLI
# behavior, deterministic, self-hosted, NO real CC needed — a throwaway perch is bind-created via
# `api bind` (which writes the perch record + session id directly, no broker/CC spawn), the four
# contract behaviors are asserted against a live 0.15.0 binary, then the perch is purged.
#
# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations),
# NOT covered here (doyle's gate ruling, 2026-06-24). The int evidence tag sits on the assertion block
# below, not here.
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.
set -u
ADAPTER=claude-spt
ID="sptc-pdint-$$"
SID="sptc-pdint-sess-$$"

command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }

# Verb capability-detect (lands in spt v0.15.0).
if ! spt api psyche-download --help >/dev/null 2>&1; then
  echo "SKIP: spt 'api psyche-download' absent (needs v0.15.0). Hook wiring: tests/hooks-parse.sh green."
  exit 0
fi
echo "ok   verb present: api psyche-download <id> --session-id"

# A subnet to home the throwaway perch (bind refuses to guess when the node holds >1 subnet).
SUBNET=$(spt endpoint list 2>/dev/null | sed -n 's/^SUBNET \([^ ]*\).*/\1/p' | head -1)
[ -n "$SUBNET" ] || { echo "SKIP: no subnet on this node to home the test perch"; exit 0; }

cleanup() {
  printf 'y\n' | spt endpoint purge "$ID" --force --yes >/dev/null 2>&1 || true
  rm -f ".claude/$ID-commune.md"
}
trap cleanup EXIT INT TERM

# Bind-create the perch (NO CC — api bind writes the perch record + session id directly).
if ! spt api --adapter "$ADAPTER" bind "$ID" --set-session-id "$SID" --subnet "$SUBNET" >/dev/null 2>&1; then
  echo "SKIP: could not bind-create the test perch (subnet $SUBNET)"
  exit 0
fi

rc=0
errf="${TMPDIR:-/tmp}/sptc-pdint-$$.err"

# 1. NO-CONTEXT: a fresh perch -> exit 0, EMPTY stdout, NO-CONTEXT:<id> on stderr (the hook skips).
out=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>"$errf"); ec=$?
err=$(cat "$errf" 2>/dev/null)
if [ "$ec" -eq 0 ] && [ -z "$out" ] && { printf '%s' "$err" | grep -q "NO-CONTEXT:$ID"; }; then
  echo "ok   NO-CONTEXT: fresh perch -> exit 0, empty stdout, NO-CONTEXT on stderr"
else
  echo "FAIL NO-CONTEXT contract: exit=$ec stdout=[$out] stderr=[$err]"; rc=1
fi

# 2. AUTH: a wrong --session-id is rejected (non-zero exit; AUTH_REFUSED).
if spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "WRONG-$SID" >/dev/null 2>&1; then
  echo "FAIL AUTH: a wrong --session-id was NOT rejected"; rc=1
else
  echo "ok   AUTH: wrong --session-id rejected"
fi

# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop).
MARK="PDINT-POS-$$"
mkdir -p .claude
printf '%s resume delta\n' "$MARK" > ".claude/$ID-commune.md"
got=""
i=0
while [ "$i" -lt 5 ]; do
  o=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>/dev/null)
  if printf '%s' "$o" | grep -q "$MARK"; then got="$o"; break; fi
  i=$((i + 1)); sleep 1
done
if { printf '%s' "$got" | grep -q '<pending-commune>'; } && { printf '%s' "$got" | grep -q "$MARK"; }; then
  echo "ok   POSITIVE: not-yet-synthesized commune returned VERBATIM as <pending-commune>"
else
  echo "FAIL POSITIVE: pending-commune not returned (got=[$got])"; rc=1
fi

rm -f "$errf"
[ "$rc" -eq 0 ] && { echo "PSYCHE-DOWNLOAD-INT OK"; exit 0; } || { echo "PSYCHE-DOWNLOAD-INT FAIL"; exit 1; }

-#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop).

+#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop). This is

+#    the core resume-context behavior the SessionStart hook injects. [int->REQ-DIST-RESUME-CONTEXT]


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== confirm tag location ==="; grep -n "int->REQ-DIST-RESUME-CONTEXT" ci/psyche-download/psyche-download-int.sh
echo "=== syntax + still green ==="; sh -n ci/psyche-download/psyche-download-int.sh && echo "sh -n ok"
sh ci/psyche-download/psyche-download-int.sh >/dev/null 2>&1 && echo "int PASS" || echo "int FAIL"
traceable-reqs check >/dev/null 2>&1 && echo "traceable GREEN" || echo "traceable RED"
$OWL send doyle perri <<'EOF'
Done — header tag removed, [int->REQ-DIST-RESUME-CONTEXT] now sits immediately above the POSITIVE not-yet-synthesized assertion (the core resume-context behavior). sh -n clean, script GREEN, traceable GREEN. Amending the commit + pushing now; SHA incoming.
EOF
echo ack=$?


=== confirm tag location ===
67:#    the core resume-context behavior the SessionStart hook injects. [int->REQ-DIST-RESUME-CONTEXT]
=== syntax + still green ===
sh -n ok
int PASS
traceable GREEN
➜ SENT:doyle
ack=0


Fold the tag relocation into `a9aa714` (unpushed; same file it introduced) and push to origin/main.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
git add ci/psyche-download/psyche-download-int.sh
git commit -q --amend --no-edit
echo "amend exit=$?"
echo "=== push main ==="; git push origin main 2>&1 | tail -8
echo "=== new SHA ==="; git rev-parse HEAD; git log origin/main..HEAD --oneline 2>/dev/null | head


amend exit=0
=== push main ===
To https://github.com/SaberMage/spt-claude-code.git
   4954293..eb609e5  main -> main
=== new SHA ===
eb609e5c3ce88dcd837baeb6e88bbd66e603c89c


Pushed — `origin/main` now at `eb609e5` (all 5 wave commits). Ping doyle the SHA for his gate.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
$OWL send doyle perri <<'EOF'
PUSHED — origin/main @ eb609e5 (4954293..eb609e5, the 5 wave commits: 211a5cc doc-capture, befa592 T1, e27e0d3 T2, 38044e2 T3, eb609e5 v0.15.0-publish+int — the last folds in the tag relocation you asked for). Pull + gate at your leisure. Review targets recap: ci/psyche-download/psyche-download-int.sh (AUTH exit-gated, trap cleanup on EXIT/INT/TERM, [int->REQ-DIST-RESUME-CONTEXT] now directly above the POSITIVE assertion), the REQ-DIST-CHECKPOINT-COMMUNE comment update, and CHECKPOINT-COMMUNE-PLAN.md §Deferred validations. Standing by for your ruling.
EOF
echo sent=$?


➜ SENT:doyle
sent=0


Pushed + doyle pinged for the gate. Let me confirm the tree/endpoints are clean and report.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== tree ==="; git status --short || echo clean
echo "=== stray validation endpoints? ==="; spt endpoint list 2>/dev/null | grep -iE 'ckptval|pdint|t1ml' || echo "none"
echo "=== branch sync ==="; git log origin/main..HEAD --oneline | head || echo "in sync with origin/main"


=== tree ===
=== stray validation endpoints? ===
  ckptval      HFENDULEAM (14efb80c…)  Suspended
  t1ml         HFENDULEAM (14efb80c…)  Suspended
  ckptval      HFENDULEAM (14efb80c…)  Suspended
  t1ml         HFENDULEAM (14efb80c…)  Suspended
  ckptval                      live_agent     -                      ready=false alive=true
  t1ml                         live_agent     -                      ready=false alive=true
=== branch sync ===


Throwaway validation endpoints linger (suspended). Purge them to clean up.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
for id in ckptval t1ml; do
  echo "=== purge $id ==="; spt endpoint purge "$id" --force --yes 2>&1 | tail -1
done
sleep 2
echo "=== reap any lingering spawned claude/translate procs from validation ==="
tasklist 2>/dev/null | grep -i 'cc-spt-idle' && taskkill //IM cc-spt-idle-translate.exe //F 2>/dev/null || echo "no translate procs"
echo "=== verify gone ==="; spt endpoint list 2>/dev/null | grep -iE 'ckptval|t1ml' || echo "both purged"


=== purge ckptval ===
PURGED:ckptval
=== purge t1ml ===
PURGED:t1ml
=== reap any lingering spawned claude/translate procs from validation ===
no translate procs
=== verify gone ===
  ckptval      HFENDULEAM (14efb80c…)  Suspended
  t1ml         HFENDULEAM (14efb80c…)  Suspended
  ckptval      HFENDULEAM (14efb80c…)  Suspended
  t1ml         HFENDULEAM (14efb80c…)  Suspended


Local perch records purged; remaining rows are stale subnet-roster cache (cosmetic, throwaway ids). Translate procs reaped, tree clean, branch in sync.

**Checkpoint-commune wave: COMPLETE, validated, pushed.**

Whole wave done end-to-end. `origin/main @ eb609e5`:

| | What | Validated |
|--|------|-----------|
| **T1** | multi-line idle `<EVENT>` frame | empirical gate + translate-proof |
| **T2** | checkpoint communes (`!!checkpoint!!` → `--json-payload` self-send → binary `/clear`+wake macro) | **LIVE loopback on real CC** (transcript: `/clear` turn → wake turn) |
| **T3** | `psyche-download` resume wiring (closes F-020) | **4-way contract** via new deterministic CI int |

Sequence since publish:
- `spt update` node → **0.15.0**; both new verbs confirmed.
- Bumped `min_spt_core` 0.13.2→**0.15.0**, adapter **0.7.0**.
- New int `ci/psyche-download/psyche-download-int.sh` — bind-creates a throwaway perch (no real CC): verb-present, NO-CONTEXT, AUTH-reject, POSITIVE pending-commune. GREEN, idempotent, self-cleaning.
- Activated REQ-DIST-RESUME-CONTEXT **int**. All 3 wave REQs now full-stage. `traceable-reqs` GREEN.
- doyle's gate split honored: spt-core-owned halves = deterministic CI; the two harness-glue E2Es (PostToolUse write-detect, SessionStart injection) = **logged dogfood deferreds** (need the new plugin installed + real CC). Logged in `CHECKPOINT-COMMUNE-PLAN.md §Deferred validations`.

**Open:**
1. **doyle's gate ruling** on the pushed traceable diff — pending (he's pulling `eb609e5`).
2. **v0.7.0 adapter publish** (`.spt` re-cut + release) — *operator call*. That ships the new plugin/binary to nodes, which lights up the two deferred dogfood E2Es. Say the word and I'll cut it.

Wave's done bar your call on the publish.


<task-notification>
<task-id>bdk4qnsl0</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="ec8ed348" type="msg" from="doyle">GATE-PASS @eb609e5. Pulled + reviewed the full diff (script + traceable + manifest + PLAN):

- psyche-download-int.sh: AUTH exit-gated (WRONG-sid -&gt; non-zero -&gt; reject) ✓, trap cleanup EXIT/INT/TERM (purge --force --yes + rm) ✓, SKIP-guards never false-red an old/subnet-less node ✓, [int-&gt;REQ-DIST-R</EVENT-PART>
<EVENT-PART seq="2/5" id="ec8ed348">ESUME-CONTEXT] now immediately above the POSITIVE not-yet-synthesized assertion ✓. Ordering correct (NO-CONTEXT + AUTH run before the drop, POSITIVE after).
- traceable: RESUME-CONTEXT -&gt; impl+unit+int, CHECKPOINT full-stage w/ live-loopback recorded; both harness-glue E2Es honestly logged as dogfood-after-plugin-land (no silent cap) </EVENT-PART>
<EVENT-PART seq="3/5" id="ec8ed348">✓. Consistent w/ the sibling *-int.sh on-node tier (translate-proof/poll/bind — none auto-swept; tag+on-node-green IS this repo's int model).
- manifest 0.6.2-&gt;0.7.0, floor 0.13.2-&gt;0.15.0, honest rationale ✓.

ONE WATCH-ITEM (non-blocking, don't re-cut): the POSITIVE retry is 5x1s vs the ~5s daemon ingest pulse — on a LO</EVENT-PART>
<EVENT-PART seq="4/5" id="ec8ed348">ADED host the drop can be ingested+cleared before the first poll catches it (cf. the seedmap starvation flake). Mitigated by the retry, but if it ever flakes on a contended box, widen to ~8-10 polls rather than treating it as a real fail. Note it in the script header.

Checkpoint-commune wave = CI-tier COMPLETE + gated. Remaining = the n</EVENT-PART>
<EVENT-PART seq="5/5" id="ec8ed348">ew-plugin-install dogfood E2Es, gated on a v0.7.0 .spt publish (operator call). Nice clean wave.</EVENT-PART></event>
</task-notification>


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


doyle **GATE-PASS @eb609e5** — full diff approved. One non-blocking watch-item: the POSITIVE retry window (5×1s) vs the ~5s daemon ingest pulse could flake on a loaded host; note it in the header, widen if it ever flakes. I'll note it + proactively widen to 8 polls (cheap insurance), no re-cut needed.


Edit

# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.

# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.
#
# WATCH-ITEM (doyle, 2026-06-24): the POSITIVE assertion polls for the <pending-commune> across a
# retry window that must straddle the daemon's ~5s ingest pulse — too NARROW a window can miss a
# late-appearing pending slice on a loaded host (cf. the seedmap starvation flake). Widened to 8 polls
# here for headroom; widen further (not lower) if it ever flakes on a contended box — never treat a
# miss as a real contract failure.


# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.
#
# WATCH-ITEM (doyle, 2026-06-24): the POSITIVE assertion polls for the <pending-commune> across a
# retry window that must straddle the daemon's ~5s ingest pulse — too NARROW a window can miss a
# late-appearing pending slice on a loaded host (cf. the seedmap starvation flake). Widened to 8 polls
# here for headroom; widen further (not lower) if it ever flakes on a contended box — never treat a
# miss as a real contract failure.

#!/bin/sh
# Integration proof for the spt-core v0.15.0 `spt api psyche-download <id>` resume-context verb
# (REQ-DIST-RESUME-CONTEXT, T3). This is the CORE-CONTRACT half of the int: pure spt-core CLI
# behavior, deterministic, self-hosted, NO real CC needed — a throwaway perch is bind-created via
# `api bind` (which writes the perch record + session id directly, no broker/CC spawn), the four
# contract behaviors are asserted against a live 0.15.0 binary, then the perch is purged.
#
# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations),
# NOT covered here (doyle's gate ruling, 2026-06-24). The int evidence tag sits on the assertion block
# below, not here.
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.
set -u
ADAPTER=claude-spt
ID="sptc-pdint-$$"
SID="sptc-pdint-sess-$$"

command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }

# Verb capability-detect (lands in spt v0.15.0).
if ! spt api psyche-download --help >/dev/null 2>&1; then
  echo "SKIP: spt 'api psyche-download' absent (needs v0.15.0). Hook wiring: tests/hooks-parse.sh green."
  exit 0
fi
echo "ok   verb present: api psyche-download <id> --session-id"

# A subnet to home the throwaway perch (bind refuses to guess when the node holds >1 subnet).
SUBNET=$(spt endpoint list 2>/dev/null | sed -n 's/^SUBNET \([^ ]*\).*/\1/p' | head -1)
[ -n "$SUBNET" ] || { echo "SKIP: no subnet on this node to home the test perch"; exit 0; }

cleanup() {
  printf 'y\n' | spt endpoint purge "$ID" --force --yes >/dev/null 2>&1 || true
  rm -f ".claude/$ID-commune.md"
}
trap cleanup EXIT INT TERM

# Bind-create the perch (NO CC — api bind writes the perch record + session id directly).
if ! spt api --adapter "$ADAPTER" bind "$ID" --set-session-id "$SID" --subnet "$SUBNET" >/dev/null 2>&1; then
  echo "SKIP: could not bind-create the test perch (subnet $SUBNET)"
  exit 0
fi

rc=0
errf="${TMPDIR:-/tmp}/sptc-pdint-$$.err"

# 1. NO-CONTEXT: a fresh perch -> exit 0, EMPTY stdout, NO-CONTEXT:<id> on stderr (the hook skips).
out=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>"$errf"); ec=$?
err=$(cat "$errf" 2>/dev/null)
if [ "$ec" -eq 0 ] && [ -z "$out" ] && { printf '%s' "$err" | grep -q "NO-CONTEXT:$ID"; }; then
  echo "ok   NO-CONTEXT: fresh perch -> exit 0, empty stdout, NO-CONTEXT on stderr"
else
  echo "FAIL NO-CONTEXT contract: exit=$ec stdout=[$out] stderr=[$err]"; rc=1
fi

# 2. AUTH: a wrong --session-id is rejected (non-zero exit; AUTH_REFUSED).
if spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "WRONG-$SID" >/dev/null 2>&1; then
  echo "FAIL AUTH: a wrong --session-id was NOT rejected"; rc=1
else
  echo "ok   AUTH: wrong --session-id rejected"
fi

# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop). This is
#    the core resume-context behavior the SessionStart hook injects. [int->REQ-DIST-RESUME-CONTEXT]
MARK="PDINT-POS-$$"
mkdir -p .claude
printf '%s resume delta\n' "$MARK" > ".claude/$ID-commune.md"
got=""
i=0
while [ "$i" -lt 5 ]; do
  o=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>/dev/null)
  if printf '%s' "$o" | grep -q "$MARK"; then got="$o"; break; fi
  i=$((i + 1)); sleep 1
done
if { printf '%s' "$got" | grep -q '<pending-commune>'; } && { printf '%s' "$got" | grep -q "$MARK"; }; then
  echo "ok   POSITIVE: not-yet-synthesized commune returned VERBATIM as <pending-commune>"
else
  echo "FAIL POSITIVE: pending-commune not returned (got=[$got])"; rc=1
fi

rm -f "$errf"
[ "$rc" -eq 0 ] && { echo "PSYCHE-DOWNLOAD-INT OK"; exit 0; } || { echo "PSYCHE-DOWNLOAD-INT FAIL"; exit 1; }

+# WATCH-ITEM (doyle, 2026-06-24): the POSITIVE assertion polls for the <pending-commune> across a

+# retry window that must straddle the daemon's ~5s ingest pulse — too NARROW a window can miss a

+# late-appearing pending slice on a loaded host (cf. the seedmap starvation flake). Widened to 8 polls

+# here for headroom; widen further (not lower) if it ever flakes on a contended box — never treat a


got=""
i=0
while [ "$i" -lt 8 ]; do  # 8-poll window straddles the ~5s daemon ingest pulse (see WATCH-ITEM header)


got=""
i=0
while [ "$i" -lt 8 ]; do  # 8-poll window straddles the ~5s daemon ingest pulse (see WATCH-ITEM header)

#!/bin/sh
# Integration proof for the spt-core v0.15.0 `spt api psyche-download <id>` resume-context verb
# (REQ-DIST-RESUME-CONTEXT, T3). This is the CORE-CONTRACT half of the int: pure spt-core CLI
# behavior, deterministic, self-hosted, NO real CC needed — a throwaway perch is bind-created via
# `api bind` (which writes the perch record + session id directly, no broker/CC spawn), the four
# contract behaviors are asserted against a live 0.15.0 binary, then the perch is purged.
#
# The SessionStart INJECTION E2E — the adapter's session-start.sh hook actually CONSUMING this pull
# and injecting it into a live CC — genuinely needs the NEW plugin installed + a real CC session, so
# it stays a LOGGED dogfood-after-plugin-land tier (CHECKPOINT-COMMUNE-PLAN.md §Deferred validations),
# NOT covered here (doyle's gate ruling, 2026-06-24). The int evidence tag sits on the assertion block
# below, not here.
#
# Needs spt >= 0.15.0 (the verb) + a subnet to home the throwaway perch. Feature-detects and SKIPs
# otherwise (never a hard fail on an older / subnet-less node). Idempotent. Run: exit 0 = pass.
#
# WATCH-ITEM (doyle, 2026-06-24): the POSITIVE assertion polls for the <pending-commune> across a
# retry window that must straddle the daemon's ~5s ingest pulse — too NARROW a window can miss a
# late-appearing pending slice on a loaded host (cf. the seedmap starvation flake). Widened to 8 polls
# here for headroom; widen further (not lower) if it ever flakes on a contended box — never treat a
# miss as a real contract failure.
set -u
ADAPTER=claude-spt
ID="sptc-pdint-$$"
SID="sptc-pdint-sess-$$"

command -v spt >/dev/null 2>&1 || { echo "SKIP: no spt on PATH"; exit 0; }

# Verb capability-detect (lands in spt v0.15.0).
if ! spt api psyche-download --help >/dev/null 2>&1; then
  echo "SKIP: spt 'api psyche-download' absent (needs v0.15.0). Hook wiring: tests/hooks-parse.sh green."
  exit 0
fi
echo "ok   verb present: api psyche-download <id> --session-id"

# A subnet to home the throwaway perch (bind refuses to guess when the node holds >1 subnet).
SUBNET=$(spt endpoint list 2>/dev/null | sed -n 's/^SUBNET \([^ ]*\).*/\1/p' | head -1)
[ -n "$SUBNET" ] || { echo "SKIP: no subnet on this node to home the test perch"; exit 0; }

cleanup() {
  printf 'y\n' | spt endpoint purge "$ID" --force --yes >/dev/null 2>&1 || true
  rm -f ".claude/$ID-commune.md"
}
trap cleanup EXIT INT TERM

# Bind-create the perch (NO CC — api bind writes the perch record + session id directly).
if ! spt api --adapter "$ADAPTER" bind "$ID" --set-session-id "$SID" --subnet "$SUBNET" >/dev/null 2>&1; then
  echo "SKIP: could not bind-create the test perch (subnet $SUBNET)"
  exit 0
fi

rc=0
errf="${TMPDIR:-/tmp}/sptc-pdint-$$.err"

# 1. NO-CONTEXT: a fresh perch -> exit 0, EMPTY stdout, NO-CONTEXT:<id> on stderr (the hook skips).
out=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>"$errf"); ec=$?
err=$(cat "$errf" 2>/dev/null)
if [ "$ec" -eq 0 ] && [ -z "$out" ] && { printf '%s' "$err" | grep -q "NO-CONTEXT:$ID"; }; then
  echo "ok   NO-CONTEXT: fresh perch -> exit 0, empty stdout, NO-CONTEXT on stderr"
else
  echo "FAIL NO-CONTEXT contract: exit=$ec stdout=[$out] stderr=[$err]"; rc=1
fi

# 2. AUTH: a wrong --session-id is rejected (non-zero exit; AUTH_REFUSED).
if spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "WRONG-$SID" >/dev/null 2>&1; then
  echo "FAIL AUTH: a wrong --session-id was NOT rejected"; rc=1
else
  echo "ok   AUTH: wrong --session-id rejected"
fi

# 3. POSITIVE: a not-yet-synthesized commune drop is returned VERBATIM as <pending-commune>. Retried
#    across the brief pre-synthesis window (the daemon eventually ingests + clears the drop). This is
#    the core resume-context behavior the SessionStart hook injects. [int->REQ-DIST-RESUME-CONTEXT]
MARK="PDINT-POS-$$"
mkdir -p .claude
printf '%s resume delta\n' "$MARK" > ".claude/$ID-commune.md"
got=""
i=0
while [ "$i" -lt 5 ]; do
  o=$(spt api --adapter "$ADAPTER" psyche-download "$ID" --session-id "$SID" 2>/dev/null)
  if printf '%s' "$o" | grep -q "$MARK"; then got="$o"; break; fi
  i=$((i + 1)); sleep 1
done
if { printf '%s' "$got" | grep -q '<pending-commune>'; } && { printf '%s' "$got" | grep -q "$MARK"; }; then
  echo "ok   POSITIVE: not-yet-synthesized commune returned VERBATIM as <pending-commune>"
else
  echo "FAIL POSITIVE: pending-commune not returned (got=[$got])"; rc=1
fi

rm -f "$errf"
[ "$rc" -eq 0 ] && { echo "PSYCHE-DOWNLOAD-INT OK"; exit 0; } || { echo "PSYCHE-DOWNLOAD-INT FAIL"; exit 1; }

+while [ "$i" -lt 8 ]; do  # 8-poll window straddles the ~5s daemon ingest pulse (see WATCH-ITEM header)


Bash

cd "C:/Users/decid/Documents/projects/spt-claude-code"
sh -n ci/psyche-download/psyche-download-int.sh && echo "sh -n ok"
sh ci/psyche-download/psyche-download-int.sh 2>&1 | tail -6
traceable-reqs check >/dev/null 2>&1 && echo "traceable GREEN" || echo RED


sh -n ok
ok   verb present: api psyche-download <id> --session-id
ok   NO-CONTEXT: fresh perch -> exit 0, empty stdout, NO-CONTEXT on stderr
ok   AUTH: wrong --session-id rejected
ok   POSITIVE: not-yet-synthesized commune returned VERBATIM as <pending-commune>
PSYCHE-DOWNLOAD-INT OK
traceable GREEN


Green. New commit (eb609e5 already pushed + gated — don't amend) for the watch-item note, then push.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
git add ci/psyche-download/psyche-download-int.sh
git commit -q -F - <<'EOF'
test(psyche-download-int): widen POSITIVE poll window + log the ingest-race watch-item

doyle's post-gate watch-item (non-blocking): the POSITIVE assertion's retry
window must straddle the daemon's ~5s ingest pulse — too narrow risks missing
a late-appearing <pending-commune> on a loaded host (cf. seedmap starvation
flake). Widen 5 -> 8 polls for headroom and document the failure mode + the
"widen, never treat a miss as a real fail" guidance in the script header.

No behavior change to the contract under test; green on-node.

Co-authored by: perri
EOF
echo "commit=$?"; git push origin main 2>&1 | tail -3; git rev-parse HEAD


commit=0
To https://github.com/SaberMage/spt-claude-code.git
   eb609e5..5a1d744  main -> main
5a1d744c016ed01dee744cf96e09c5e984abfd9b


cd "C:/Users/decid/Documents/projects/spt-claude-code"
$OWL send doyle perri <<'EOF'
GATE-PASS received, thanks. Watch-item handled (no re-cut): pushed 5a1d744 (origin/main eb609e5..5a1d744) — widened the POSITIVE poll window 5→8 to straddle the ~5s ingest pulse + documented the failure mode + "widen-never-fail" guidance in the script header. Contract unchanged, green on-node.

Checkpoint-commune wave = CI-tier COMPLETE + gated. Remaining = the new-plugin-install dogfood E2Es (PostToolUse write-detect + SessionStart injection), gated on a v0.7.0 .spt publish — operator call, holding for that. Clean wave — good collaborating with you on the contract.
EOF
echo sent=$?


**Wave shipped + gated. Done.**

`origin/main @ 5a1d744`. doyle **GATE-PASS** on the full diff; his one watch-item (POSITIVE poll-window vs ingest pulse) handled — widened 5→8 + documented, no re-cut.

Final state of the checkpoint-commune wave:
- **6 commits** pushed (`211a5cc` doc → `befa592` T1 → `e27e0d3` T2 → `38044e2` T3 → `eb609e5` publish/int → `5a1d744` watch-item).
- **3 REQs full-stage**, `traceable-reqs` GREEN. Manifest **v0.7.0**, floor **0.15.0**.
- **Validated on real spt 0.15.0**: T2 live checkpoint loopback (E2E on a spt-hosted CC), T3 psyche-download 4-way contract (deterministic CI).
- doyle's CI/dogfood split honored + logged.

**One open, operator-gated:** cut the **v0.7.0 `.spt` publish** — that ships the new plugin+binary to nodes and lights the two remaining dogfood E2Es (PostToolUse write-detect, SessionStart injection). Your call; say go and I cut it.

Nothing else pending. Clean stop.


<task-notification>
<task-id>bdk4qnsl0</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT type="msg" from="doyle">Verified 5a1d744 — poll widened to 8 straddling the ~5s pulse + header documents widen-never-fail. Watch-item closed. Checkpoint-commune wave fully gated, CI-tier complete. Holding with you on the v0.7.0 .spt publish (operator call) to light the two dogfood E2Es. Good clean wave — solid contract work.</EVENT></event>
</task-notification>


doyle confirmed — **watch-item closed, wave fully gated, CI-tier complete.**

Nothing pending on my side. Holding on the lone open item: **v0.7.0 `.spt` publish** (operator call) — that ships the new plugin+binary and lights the two deferred dogfood E2Es.

Say **go** to cut the publish, or leave it parked. Clean stop.


proceed with the publish


Publishing v0.7.0. Let me find the packaging/release machinery.


cd "C:/Users/decid/Documents/projects/spt-claude-code"
echo "=== ci/publish ==="; ls -la ci/publish/ 2>/dev/null
echo "=== release/publish scripts anywhere ==="; find . -path ./node_modules -prune -o \( -iname '*publish*' -o -iname '*package*adapter*' -o -iname '*release*' \) -name '*.sh' -print 2>/dev/null | grep -v target | head
echo "=== plugin.json version (cplugs skeleton) ==="; find . -name 'plugin.json' -not -path '*/node_modules/*' 2>/dev/null | head;


=== ci/publish ===
total 32
drwxr-xr-x 1 decid 197609    0 Jun 22 22:23 .
drwxr-xr-x 1 decid 197609    0 Jun 24 05:00 ..
-rwxr-xr-x 1 decid 197609 7204 Jun 22 22:23 package-adapter.sh
-rwxr-xr-x 1 decid 197609 2427 Jun 15 01:49 package-skeleton.sh
-rwxr-xr-x 1 decid 197609 3780 Jun 18 02:52 release-acquire-int.sh
-rwxr-xr-x 1 decid 197609 4600 Jun 15 01:50 validate-skeleton.sh
=== release/publish scripts anywhere ===
./ci/publish/package-adapter.sh
./ci/publish/release-acquire-int.sh
=== plugin.json version (cplugs skeleton) ===
./plugin/sptc/.claude-plugin/plugin.json