Write

---
name: v071-published
description: v0.7.1 PUBLIC 2026-06-15 (counter 13) — ADR-0020 <EVENT>-everywhere + confident update-apply msg + docs
metadata:
  type: project
---

v0.7.1 PUBLIC 2026-06-15 (counter 13), patch bump 0.7.0→0.7.1. Release base main @ cb05600 (version bump + CHANGELOG), tagged off it. doyle gated CI-green pre-tag, todlando tagged, deployah signed+published (7th publish leg). Transient TLS-handshake-timeout on the gh-release-create draft step → deployah rerun the failed `assemble` job only (builds reused, NO re-tag) → exit 0.

Hashes: linux `81eededc…cc7bfd5`, win `94bb5429…aa47b898`. signed `54188f12…05e3ec9`, update-set v13 "0.7.1". https://github.com/SaberMage/spt-releases/releases/tag/v0.7.1

Contents (this session's whole chain):
- **ADR-0020** (PR #12 @ 25ffd7a): `<EVENT>` is the SOLE arriving-message format at every surface incl api poll/worker-poll; `__REPLY_TO__` relic removed; shell-link relay EXEMPT (raw MAC'd frames, documented carve-out + notify_shell_e2e guard); F-002 dissolved. See [[event-envelope-reply-to-removal]].
- fix: shell-link notify raw passthrough (the notify regression caught in CI).
- fix(update): confident `spt update apply` → "Updated spt-core to vX." not internal "trial" wording + canonical github.com URL ([[update-apply-confident-message]]).
- docs: published {key} substitution catalog + [digest] register rule + [strings] file-pointer syntax (perri F-003 docs leg).
- ci: workspace-scoped Windows exe-lock reap guard ([[no-machinewide-kill-shared-runner]]).

**perri's REQ-MSG-ENVELOPE byte-capture unblocked on this publish** (poll→additionalContext now emits `<EVENT>`; she flips HOOKS-API/UPS-INJECTION int once confirmed against win `94bb5429…`).


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

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


ignore, proceed. she messaged you already


<owl_messages>
<owl_message from="todlando" priority="high">v0.7.1 PUBLIC — counter 13 (deployah signed+published; transient TLS blip cleared on rerun, no re-tag). Hashes:
  linux 81eededc724676565a323a0a5b12bfed5f8b596c3e60eaa467d5d8b50cc7bfd5
  win   94bb542970dfb6d7b5eff6abe308d0ee9cfeb69f62c04d43fad84b40aa47b898
The ADR-0020 <EVENT>-everywhere change is now on the published surface → your perri relay for the poll->additionalContext <EVENT> byte-capture is UNBLOCKED (she builds from published only). My full release leg done (bump→changelog→push→your green gate→tag→publish handoff). Fleet roll = operator/deployah-gated next phase, not initiating. On-call.</owl_message>
<owl_message from="deployah" priority="high">Confirmed — perri unblocked, counter 13 clean. Good run. Idle on-call for the next release or any fleet-roll verify.</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.


1	---
2	name: v071-published
3	description: "v0.7.1 PUBLIC 2026-06-15 (counter 13); ADR-0020 EVENT-everywhere + __REPLY_TO__ removal; release.yml hit transient TLS timeout, cleared on rerun --failed"
4	metadata:
5	  node_type: memory
6	  type: project
7	  originSessionId: 1fb9fc52-b258-4b44-9c4c-7cae9345098f
8	---
9
10	v0.7.1 SIGNED + PUBLISHED 2026-06-15 (release counter 13; v0.7.0=12, v0.7.1=13). Patch bump 0.7.0→0.7.1. doyle-coordinated (todlando bump+CHANGELOG+tag cb05600, deployah sign+publish+hash). Contents: ADR-0020 `<EVENT>`-everywhere + `__REPLY_TO__` removal + notify fix, confident `update apply` message, docs-completeness ({key} catalog / [digest] rule / [strings] file-pointer), CI exe-lock guard.
11
12	Hashes: linux `81eededc724676565a323a0a5b12bfed5f8b596c3e60eaa467d5d8b50cc7bfd5`, win `94bb542970dfb6d7b5eff6abe308d0ee9cfeb69f62c04d43fad84b40aa47b898`. signed `54188f12…05e3ec9`. update-set.json v13. https://github.com/SaberMage/spt-releases/releases/tag/v0.7.1
13
14	**Why:** the win binary 94bb5429 carries the ADR-0020 `<EVENT>`-everywhere poll surface → unblocks perri's REQ-MSG-ENVELOPE byte-capture (doyle relays the hash). See [[event-envelope-reply-to-removal]].
15
16	**GOTCHA (transient, recurring-class):** release.yml `assemble` job's "Create draft release on spt-releases" step can fail on a GitHub API network blip — saw `Post .../releases: net/http: TLS handshake timeout`, exit 1. This is NOT code and NOT the hfenduleam disk-full class — binary builds succeed, only the gh-release-create call blips. Fix: `gh run rerun <id> --repo SaberMage/spt-core --failed` (green build jobs are reused, only assemble re-runs), no re-tag needed. My BUILD-FAILED draft-watch correctly caught it; always read the failed step before alarming — distinguish transient-API / disk-full / real-code.
17
18	**How to apply:** todlando rolls fleet, deployah verifies hashes on ping. enlyzeam still pending catch-up (lands straight on v0.7.1 win 94bb5429 if caught up). Publisher leg proven 7× (counter 7-13). Seed `SPT_RELEASE_SEED` resident in ENV (64-hex); Monitor=bash.
19

---
name: v071-published
description: "v0.7.1 PUBLIC 2026-06-15 (counter 13); ADR-0020 EVENT-everywhere + __REPLY_TO__ removal; release.yml hit transient TLS timeout, cleared on rerun --failed"
metadata:
  node_type: memory
  type: project
  originSessionId: 1fb9fc52-b258-4b44-9c4c-7cae9345098f
---

v0.7.1 SIGNED + PUBLISHED 2026-06-15 (release counter 13; v0.7.0=12, v0.7.1=13). Patch bump 0.7.0→0.7.1. doyle-coordinated (todlando bump+CHANGELOG+tag cb05600, deployah sign+publish+hash). Contents: ADR-0020 `<EVENT>`-everywhere + `__REPLY_TO__` removal + notify fix, confident `update apply` message, docs-completeness ({key} catalog / [digest] rule / [strings] file-pointer), CI exe-lock guard.

Hashes: linux `81eededc724676565a323a0a5b12bfed5f8b596c3e60eaa467d5d8b50cc7bfd5`, win `94bb542970dfb6d7b5eff6abe308d0ee9cfeb69f62c04d43fad84b40aa47b898`. signed `54188f12…05e3ec9`. update-set.json v13. https://github.com/SaberMage/spt-releases/releases/tag/v0.7.1

**Why:** the win binary 94bb5429 carries the ADR-0020 `<EVENT>`-everywhere poll surface → unblocks perri's REQ-MSG-ENVELOPE byte-capture (doyle relays the hash). See [[event-envelope-reply-to-removal]].

**GOTCHA (transient, recurring-class):** release.yml `assemble` job's "Create draft release on spt-releases" step can fail on a GitHub API network blip — saw `Post .../releases: net/http: TLS handshake timeout`, exit 1. This is NOT code and NOT the hfenduleam disk-full class — binary builds succeed, only the gh-release-create call blips. Fix: `gh run rerun <id> --repo SaberMage/spt-core --failed` (green build jobs are reused, only assemble re-runs), no re-tag needed. My BUILD-FAILED draft-watch correctly caught it; always read the failed step before alarming — distinguish transient-API / disk-full / real-code.

**How to apply:** todlando rolls fleet, deployah verifies hashes on ping. enlyzeam still pending catch-up (lands straight on v0.7.1 win 94bb5429 if caught up). Publisher leg proven 7× (counter 7-13). Seed `SPT_RELEASE_SEED` resident in ENV (64-hex); Monitor=bash.


v071 memory already captured (auto-memory has it, with the transient-TLS gotcha — more complete than mine; leaving it). todlando + deployah both confirm done/idle/on-call — no reply needed (closing, not blocked).

**The full chain is shipped.** Quick cleanup of stale worktrees + merged branches, then final report:


Bash

cd C:/Users/decid/Documents/projects/spt-core; echo "=== remove stale worktrees ==="; git worktree remove spt-gate-updmsg --force 2>&1 || echo "gate-updmsg: $?"; git worktree remove ../spt-gate-updmsg --force 2>&1; git worktree remove ../spt-core-wt-updapply --force 2>&1 || echo "wt-updapply removal: (may have transient lock, retry later)"; git worktree prune; echo "=== remaining worktrees ==="; git worktree list; echo "=== delete merged remote branches ==="; git push origin --delete docs/msg-envelope-adr-0020 fix/update-apply-confident-message 2>&1 | tail -4

Clean stale worktrees + merged remote branches


=== remove stale worktrees ===
fatal: '../spt-gate-updmsg' is not a working tree
fatal: '../spt-core-wt-updapply' is not a working tree
wt-updapply removal: (may have transient lock, retry later)
=== remaining worktrees ===
C:/Users/decid/Documents/projects/spt-core  cb05600 [main]
=== delete merged remote branches ===
To https://github.com/SaberMage/spt-core.git
 - [deleted]         docs/msg-envelope-adr-0020
 - [deleted]         fix/update-apply-confident-message


Clean — single worktree on `main @ cb05600`, both merged branches deleted from origin. (`spt-core-wt-updapply` was already pruned by todlando.)

## Session complete — v0.7.1 shipped (counter 13, public)

Everything from this session landed and shipped:

**Your direct asks:**
- `spt update apply` "trial" message → confident **"Updated spt-core to vX."** (+ fixed `www.github.com` → canonical URL)
- Milestone/wave codes kept out of public docs → version numbers; saved as a standing rule

**The cascade it pulled in:**
- **ADR-0020** gated + merged: `<EVENT>` is the sole arriving format everywhere; `__REPLY_TO__` removed; shell-link relay carved out as exempt (documented + test-guarded); F-002 dissolved
- **perri's 3 docs-completeness fixes** packed in: `{key}` substitution catalog, `[digest]` register rule, `[strings]` file-pointer syntax (F-003 resolved — capability was shipped, gap was docs)
- **3 real defects CI/I caught** (all root-caused, fixed): poll-auth false-green (my own bad local gate — ambient `$OWL_SESSION_ID`), notify-shell `<EVENT>`-wrap regression, and the **fleet-wide Windows exe-lock** (latent on any `cli.rs` PR)
- **Safety catch**: blocked todlando's machine-wide `Stop-Process` on the shared self-hosted runner → scoped the reap guard to the CI workspace; confirmed no live perch disrupted

**Release:** gated CI-green pre-tag → todlando tagged v0.7.1 → deployah signed+published (transient TLS blip auto-recovered, no re-tag). Hashes relayed to **perri → her REQ-MSG-ENVELOPE byte-capture is unblocked**.

Fleet idle/on-call (todlando, deployah). Fleet-roll is the operator/deployah-gated next phase — not initiating. Nothing open on my side.


<task-notification>
<task-id>bg7cqpvni</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT-PART seq="1/3" id="dd69cdfc" type="msg" from="perri">ACK PUBLISH PING — v0.7.1 (counter 13) noted, hashes logged. Pivoting to the REQ-MSG-ENVELOPE byte-capture now. First finishing a tiny in-flight commit (signoff/list-agents file-backed bodies), then: upgrade local 0.7.0-&gt;0.7.1, establish a throwaway perch, send a message, capture RAW poll bytes via the UPS drain pa</EVENT-PART>
<EVENT-PART seq="2/3" id="dd69cdfc">th, confirm the &lt;EVENT type=&quot;msg&quot; from=…&gt;body&lt;/EVENT&gt; envelope (self-delimiting, one whole envelope per msg, no &lt;EVENT-PART&gt; on the hook drain), then flip HOOKS-API/UPS-INJECTION int + send you the confirm-match bytes. Also noted: v0.7.1 publishes my 3 flagged docs ({key} catalog, [digest] register rule, [strings]</EVENT-PART>
<EVENT-PART seq="3/3" id="dd69cdfc"> file-pointer) — F-003 docs-residual now CLOSED on the published surface; will mark it resolved. Reporting bytes shortly.</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.


perri pivoting to the byte-capture (upgrade → throwaway perch → capture raw poll bytes → confirm `<EVENT>` → flip int). Brief ack per standing rule, then awaiting her confirm-bytes — the final validation of the ADR-0020 chain on the published surface:


cd C:/Users/decid/Documents/projects/spt-core; $OWL send perri doyle <<'EOF'
ACK — plan is exactly right: upgrade 0.7.0→0.7.1, throwaway perch, send, capture RAW poll bytes via the UPS drain, confirm `<EVENT type="msg" from=…>body</EVENT>` (self-delimiting, one whole envelope/msg, NO <EVENT-PART> on the hook drain — that's the F-002-dissolves property), flip HOOKS-API/UPS-INJECTION int. Finish your in-flight signoff/list-agents file-backed commit first, no rush. F-003 docs-residual CLOSED on the published surface — confirmed, mark it resolved. Awaiting your confirm-match bytes; that closes the ADR-0020 loop end-to-end (design→impl→gate→ship→real-surface-verify). Send them when you have them.
EOF

Ack perri's byte-capture plan


➜ SENT:perri


Standing by for perri's confirm-bytes — the last open thread, her async validation of the `<EVENT>` poll surface against the published v0.7.1 binary. Everything else this session is shipped and closed. When her bytes land and she flips HOOKS-API/UPS-INJECTION int, the ADR-0020 loop is closed end-to-end (design → impl → gate → ship → real-surface verify). Idle until then.


<task-notification>
<task-id>bg7cqpvni</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="c235f670" type="msg" from="perri">CONFIRM-MATCH bytes (api poll drain, live v0.7.1, throwaway perch, od-verified) — ADR-0020 loop closed end-to-end:

Single msg (od -c, exact):
&lt;EVENT type=&quot;msg&quot; from=&quot;probe-sender&quot;&gt;hello from probe&lt;br&gt;second &amp;lt;line&amp;gt; &amp;amp; &amp;quot;stuff&amp;quot;&lt;/EVENT&gt;</EVENT-PART>
<EVENT-PART seq="2/6" id="c235f670">\n

Multi-message (two sends -&gt; two WHOLE envelopes, self-delimiting):
&lt;EVENT type=&quot;msg&quot; from=&quot;alice&quot;&gt;msg one&lt;/EVENT&gt;\n&lt;EVENT type=&quot;msg&quot; from=&quot;bob&quot;&gt;msg two&lt;/EVENT&gt;\n

Confirmed:
- type=&quot;msg&quot; from=&quot;&lt;sender&gt;&quot; envelope, body escaping = &</EVENT-PART>
<EVENT-PART seq="3/6" id="c235f670">lt;br&gt; for newline + &amp;lt;/&amp;gt;/&amp;quot; entities + &amp;amp; (decode &amp;amp; LAST). EXACT match to my render_frames decode rule.
- Each envelope is ONE WHOLE &lt;EVENT&gt;...&lt;/EVENT&gt; terminated by \n. Self-delimiting AND newline-framed. NO &lt;EVENT-PART&gt; on a normal-size hook drain. NO __REPLY_TO__ relic (gone).
</EVENT-PART>
<EVENT-PART seq="4/6" id="c235f670">- Multi-drain splits cleanly on &lt;/EVENT&gt; -&gt; F-002 DISSOLVED on the published surface (no delimiter guessing).
- Piped the raw drain through my hook parser render_frames -&gt; correct &lt;sptc_messages from=&quot;alice/bob&quot;&gt; output (parser confirm-match PASS).

Notify events also ride the same envelope: &lt;EVENT type=</EVENT-PART>
<EVENT-PART seq="5/6" id="c235f670">&quot;notify&quot; from=&quot;spt-update&quot; notif_id=&quot;...&quot; subnet=&quot;...&quot;&gt;body&lt;/EVENT&gt;\n.

Building the committed int now (ci/hooks/poll-int.sh, SPTC_ACCEPTANCE-gated: bind-&gt;send-&gt;api poll-&gt;render_frames assert), activating REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int, marking F-002 resolved-shipped +</EVENT-PART>
<EVENT-PART seq="6/6" id="c235f670"> F-003 docs-residual CLOSED (v0.7.1 published the 3 docs). ADR-0020 verified on real surface. Thanks doyle.</EVENT-PART></event>
</task-notification>


perri's **CONFIRM-MATCH bytes close the ADR-0020 loop end-to-end on the live published v0.7.1 surface** — `<EVENT>` envelope verified (body escaping exact, self-delimiting, no `<EVENT-PART>` on hook drain, no `__REPLY_TO__` relic, F-002 dissolved, her parser confirm-match PASS). Capstone ack + updating the memory to closed-verified:


cd C:/Users/decid/Documents/projects/spt-core; $OWL send perri doyle <<'EOF'
CAPSTONE CONFIRMED — that's the whole ADR-0020 loop closed on the REAL published surface. od-verified bytes are exactly right:
- `<EVENT type="msg" from=…>body</EVENT>\n` per message, body escaping <br>+entities+& LAST = your render_frames decode rule, exact match.
- WHOLE envelopes, self-delimiting AND \n-framed, no <EVENT-PART> on normal hook drains, no __REPLY_TO__ — F-002 DISSOLVED on the published binary.
- Raw drain → your render_frames → correct <sptc_messages> = parser confirm-match PASS. That's the proof that matters.
- notify rides the same envelope.

GO flip REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int, commit ci/hooks/poll-int.sh (SPTC_ACCEPTANCE-gated bind→send→poll→render_frames assert is exactly the right guard), mark F-002 resolved-shipped + F-003 docs-CLOSED. design→impl→gate→ship→real-surface-verify, end to end. Excellent work perri — this is the cleanest possible close. Ping if anything snags in the int commit; otherwise you're clear.
EOF
echo "=== read event-envelope memory to update ===";

Capstone ack to perri


➜ SENT:perri
=== read event-envelope memory to update ===


1	---
2	name: event-envelope-reply-to-removal
3	description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. MERGED to main @25ffd7a 2026-06-15 (PR #12); release operator-gated next. perri ping = doyle's"
4	metadata:
5	  node_type: memory
6	  type: project
7	  originSessionId: ce7ce158-107f-4da4-a203-cea98da80b27
8	---
9
10	**ADR-0020 (2026-06-15, crystallized via /grill-with-docs, operator-ruled).** Triggered by
11	perri's finding **F-002** (`api poll` multi-message output non-self-delimiting). Operator clarified the
12	design intent → grilled against CONTEXT/ADRs → these LOCKED decisions:
13
14	- **The `<EVENT type="msg" from="…">body</EVENT>` envelope (spt-proto::event, the ADR-0001 grammar) is
15	  the SOLE canonical arriving-message format at EVERY delivery surface — `api listen`/`spt ready` AND
16	  `api poll`/`worker-poll`, byte-identical.** Adapters parse the `<EVENT>` envelope (the `from` attr).
17	  Reverses REQ-MSG-4's "hook drains keep the raw frame by contract."
18	- **`__REPLY_TO__` was MIS-ELEVATED** during the clean-room port to a fake ADR-0001 "stable wire
19	  format" (spt-msg/wire.rs, lib.rs). It is REMOVED entirely: spool format_row, the spt-msg TCP frame,
20	  emit::parse_frame. `(from, body)` carried structurally; `<EVENT>` composed once at the delivery edge.
21	- **No legacy sister-interop** — spt-core never required byte-compat with legacy spt; the wire.rs
22	  "byte-for-byte with a sister" claim was part of the mis-elevation. (Operator confirmed.)
23	- **Reply-correlation rebinds onto the structural `from` / `<EVENT from=…>`** — ADR-0009 access-gate
24	  (reply-vs-new) + ADR-0012 Psyche/spt-live reply-target. Both ADRs amended (dated notes). Sender-
25	  identity granularity preserved (the relic carried the same `from`); no new correlation attribute.
26	- **F-002 dissolves** — `<EVENT>…</EVENT>` self-delimits; no record delimiter introduced.
27	- WAN wire (spt-net/wanmsg.rs) UNAFFECTED — `from` already structural (JSON field).
28
29	**DOCS DONE:** ADR-0020 + ADR-0009/0012 amendments + CONTEXT.md §Messaging-substrate glossary entry
30	("arriving-message envelope") + REQ-MSG-ENVELOPE registered (required_stages=["doc"], doc evidenced;
31	traceable 208/208 EXIT=0).
32
33	**IMPL DISPATCHED TO TODLANDO 2026-06-15 (spt-core executor; doyle gates — NOT doyle's to execute, NOT perri's).** Plan = `REQ-MSG-ENVELOPE-PLAN.md` (7 tasks, 3 open impl-decisions: TCP-wire shape, drain API, anon from). todlando JIT-plans → design-check to doyle → builds → doyle gates. Multi-crate scope (activate impl/unit/int when it lands): spt-store
34	(spool format_row), spt-msg (wire.rs delete, emit parse_frame, deliver/listener/ring/ready, lib.rs
35	ADR-0001 framing), spt (api poll/worker-poll delivery.rs::emit → compose `<EVENT>`, cli --from help),
36	spt-daemon (psyrelay/notif/access/shellchan reply-target), spt-live (outbound), KNOWN-HAZARDS Psyche
37	invariant. Until it lands, `api poll` STILL emits the raw `__REPLY_TO__` relic on the public binary.
38
39	**IMPL BUILT + COMMITTED 2026-06-15 @ `5da7fcd`** (branch docs/msg-envelope-adr-0020, stacked on docs `24e0628`; both off main). doyle blessed the 3 decisions (TCP wire = u32 len(from)+from+body structural inner-frame; DrainedMsg{from,body} struct; anon from="" preserved) + 2 re-emphases (adversarial typed-passthrough no-double-wrap; grep-clean incl test fixtures). All 7 tasks done across 27 files: spool DrainedMsg (format_row deleted), wire encode/decode_frame (parse_frame deleted), emit render_event_whole (hook drains, no chunk) + render_event_lines (listener, chunks), api poll/worker compose WHOLE <EVENT>, daemon/live/net reply-target on structural `from`. Self-verify GREEN: 1053 nextest + doctests + clippy -D + traceable EXIT=0 (REQ-MSG-ENVELOPE +doc+impl+unit+int) + grep-clean ZERO __REPLY_TO__ in crates/ + new poll-whole-self-delimiting int + xtask OK. **doyle GATE-PASS 2026-06-15** (independent repro from 5da7fcd: 1054 nextest, traceable 208/208 EXIT=0, reviewed structural frame/DrainedMsg/render_event_whole/adversarial no-double-wrap/psyrelay anti-spoof — all sound). **PR #12 CI then RED on my new poll int** — FALSE-GREEN bug: `spt api poll` is AUTH-GATED (REQ-HAZARD-LOCAL-API-AUTH; needs session_id matching info.json or --token); my E2E pinned neither → passed only where ambient $OWL_SESSION_ID set (dev box), AUTH_REFUSED on clean CI (random sid). FIX @ `ac6c802`: pin const SID on BOTH `ready --once` + the `poll` via .env(OWL_SESSION_ID) + .env_remove(SPT_API_TOKEN); verified with both vars UNSET locally (CI-clean repro). LESSON: any E2E spawning `api poll`/auth-gated machinery must pin OWL_SESSION_ID hermetically — never lean on ambient env (false green); re-verify with the env UNSET. doyle's local gate missed it (his env was set) — gate-repro must clear ambient env too. **PR #12 RED a 2nd time (different test): `notify_shell_e2e` — my cmd_poll_shell over-applied `<EVENT>` to the SHELL-LINK relay (`api poll <shell-id> --link`). Shell frames are RAW MAC'd stamped payloads (spooled anon, from="") the shell child consumes verbatim — NOT a harness `<EVENT>` reader; wrapping broke MAC/parse → never rendered.** FIX @ `caaf797`: cmd_poll_shell raw passthrough (`println!("{}", m.body)`); agent-perch poll/worker/listen keep `<EVENT>`. SCOPE CARVE-OUT documented 4 places (comment at cmd_poll_shell + ADR-0020 clause + REQ-MSG-ENVELOPE title + CONTEXT:685): `<EVENT>`-everywhere = harness ARRIVING-MESSAGE surfaces on AGENT perches only; shell-command channel = distinct internal transport, EXEMPT; notify_shell_e2e = named guard. **2nd LESSON: `cargo nextest run --workspace` SILENTLY SKIPS env-gated E2Es** (notify_shell_e2e self-skips when SPT_NOTIFY_SHELL_BIN unset; the CI `test` job = plain `cargo test -p spt --test notify_shell_e2e` runs it for real). Pre-push sweep MUST build the adapter (`projects/spt-shell-notify` → `cargo build --release` → target/release/notify-shell.exe) + set SPT_NOTIFY_SHELL_BIN + run the cargo-test-only E2Es; nextest-green ≠ CI-green.
40
41	**3rd RED: Windows docs-drift gate exe-lock** (`xtask check` → "failed to remove target\debug\spt.exe", os-error-5). A test-spawned detached daemon (REQ-DAEMON-3 autostart) holds spt.exe; Windows can't overwrite a running exe (Linux can → Linux passed). LATENT on ANY cli.rs-touching PR (doyle's PR #13 hit it too with NO new E2Es — the docs-drift xtask rebuild collides; main never rebuilt there so it stayed hidden). FIX @ `f747e1e`: a Windows-only reap step BEFORE the docs-drift gate, **$GITHUB_WORKSPACE-path-scoped** (see [[no-machinewide-killon-shared-runner]] — NEVER machine-wide on the shared self-hosted runner). Guard rode PR #12 to main → fixes it fleet-wide.
42
43	**ALL MERGED to main @ `25ffd7a` (PR #12, 2026-06-15):** 5da7fcd impl + d2433d4 doyle-T6-docs + ac6c802 poll-hermetic + caaf797 shell-link-exempt + f747e1e CI-guard. doyle pinged perri (byte-capture waits for the operator-gated next release). doyle next: rebase PR #13 (update-apply [[update-apply-confident-message]] + docs incl F-003 strings-pointer) onto main → merge → release. **doyle found T6 doc-drift = MY MISS** (I built T0–T5+T7 code/tests, skipped T6's doc sweep: KNOWN-HAZARDS:348 Psyche-routing 7.x + FAULT-MATRIX:42 + docs-site still call __REPLY_TO__ the live wire header); doyle clearing it himself (docs+impl one PR), I stayed hands-off to avoid collision. NEXT: doyle merges branch→main → ping perri (poll byte-capture confirm) on publish. SHARED-WORKTREE LESSON: doyle saw my mid-flight uncommitted edits on the shared docs branch + tried to build (RED) — coordinate hands-off + gate-from-commit when both agents touch one tree.
44
45	**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their
46	838fff8 render_frames retargets parse side to `<EVENT>`, `from`→`<sptc_messages from=>` unchanged). Build
47	against canonical `<EVENT>`; don't validate vs poll's CURRENT relic output — **doyle/todlando ping perri when this MERGES to main + publishes** (perri owes a byte-capture confirm of poll output then). See [[spt-core-harness-boundary-and-grounding]], [[spt-claude-code-next]].
48

---
name: event-envelope-reply-to-removal
description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. MERGED to main @25ffd7a 2026-06-15 (PR #12); release operator-gated next. perri ping = doyle's"
metadata:
  node_type: memory
  type: project
  originSessionId: ce7ce158-107f-4da4-a203-cea98da80b27
---

**ADR-0020 (2026-06-15, crystallized via /grill-with-docs, operator-ruled).** Triggered by
perri's finding **F-002** (`api poll` multi-message output non-self-delimiting). Operator clarified the
design intent → grilled against CONTEXT/ADRs → these LOCKED decisions:

- **The `<EVENT type="msg" from="…">body</EVENT>` envelope (spt-proto::event, the ADR-0001 grammar) is
  the SOLE canonical arriving-message format at EVERY delivery surface — `api listen`/`spt ready` AND
  `api poll`/`worker-poll`, byte-identical.** Adapters parse the `<EVENT>` envelope (the `from` attr).
  Reverses REQ-MSG-4's "hook drains keep the raw frame by contract."
- **`__REPLY_TO__` was MIS-ELEVATED** during the clean-room port to a fake ADR-0001 "stable wire
  format" (spt-msg/wire.rs, lib.rs). It is REMOVED entirely: spool format_row, the spt-msg TCP frame,
  emit::parse_frame. `(from, body)` carried structurally; `<EVENT>` composed once at the delivery edge.
- **No legacy sister-interop** — spt-core never required byte-compat with legacy spt; the wire.rs
  "byte-for-byte with a sister" claim was part of the mis-elevation. (Operator confirmed.)
- **Reply-correlation rebinds onto the structural `from` / `<EVENT from=…>`** — ADR-0009 access-gate
  (reply-vs-new) + ADR-0012 Psyche/spt-live reply-target. Both ADRs amended (dated notes). Sender-
  identity granularity preserved (the relic carried the same `from`); no new correlation attribute.
- **F-002 dissolves** — `<EVENT>…</EVENT>` self-delimits; no record delimiter introduced.
- WAN wire (spt-net/wanmsg.rs) UNAFFECTED — `from` already structural (JSON field).

**DOCS DONE:** ADR-0020 + ADR-0009/0012 amendments + CONTEXT.md §Messaging-substrate glossary entry
("arriving-message envelope") + REQ-MSG-ENVELOPE registered (required_stages=["doc"], doc evidenced;
traceable 208/208 EXIT=0).

**IMPL DISPATCHED TO TODLANDO 2026-06-15 (spt-core executor; doyle gates — NOT doyle's to execute, NOT perri's).** Plan = `REQ-MSG-ENVELOPE-PLAN.md` (7 tasks, 3 open impl-decisions: TCP-wire shape, drain API, anon from). todlando JIT-plans → design-check to doyle → builds → doyle gates. Multi-crate scope (activate impl/unit/int when it lands): spt-store
(spool format_row), spt-msg (wire.rs delete, emit parse_frame, deliver/listener/ring/ready, lib.rs
ADR-0001 framing), spt (api poll/worker-poll delivery.rs::emit → compose `<EVENT>`, cli --from help),
spt-daemon (psyrelay/notif/access/shellchan reply-target), spt-live (outbound), KNOWN-HAZARDS Psyche
invariant. Until it lands, `api poll` STILL emits the raw `__REPLY_TO__` relic on the public binary.

**IMPL BUILT + COMMITTED 2026-06-15 @ `5da7fcd`** (branch docs/msg-envelope-adr-0020, stacked on docs `24e0628`; both off main). doyle blessed the 3 decisions (TCP wire = u32 len(from)+from+body structural inner-frame; DrainedMsg{from,body} struct; anon from="" preserved) + 2 re-emphases (adversarial typed-passthrough no-double-wrap; grep-clean incl test fixtures). All 7 tasks done across 27 files: spool DrainedMsg (format_row deleted), wire encode/decode_frame (parse_frame deleted), emit render_event_whole (hook drains, no chunk) + render_event_lines (listener, chunks), api poll/worker compose WHOLE <EVENT>, daemon/live/net reply-target on structural `from`. Self-verify GREEN: 1053 nextest + doctests + clippy -D + traceable EXIT=0 (REQ-MSG-ENVELOPE +doc+impl+unit+int) + grep-clean ZERO __REPLY_TO__ in crates/ + new poll-whole-self-delimiting int + xtask OK. **doyle GATE-PASS 2026-06-15** (independent repro from 5da7fcd: 1054 nextest, traceable 208/208 EXIT=0, reviewed structural frame/DrainedMsg/render_event_whole/adversarial no-double-wrap/psyrelay anti-spoof — all sound). **PR #12 CI then RED on my new poll int** — FALSE-GREEN bug: `spt api poll` is AUTH-GATED (REQ-HAZARD-LOCAL-API-AUTH; needs session_id matching info.json or --token); my E2E pinned neither → passed only where ambient $OWL_SESSION_ID set (dev box), AUTH_REFUSED on clean CI (random sid). FIX @ `ac6c802`: pin const SID on BOTH `ready --once` + the `poll` via .env(OWL_SESSION_ID) + .env_remove(SPT_API_TOKEN); verified with both vars UNSET locally (CI-clean repro). LESSON: any E2E spawning `api poll`/auth-gated machinery must pin OWL_SESSION_ID hermetically — never lean on ambient env (false green); re-verify with the env UNSET. doyle's local gate missed it (his env was set) — gate-repro must clear ambient env too. **PR #12 RED a 2nd time (different test): `notify_shell_e2e` — my cmd_poll_shell over-applied `<EVENT>` to the SHELL-LINK relay (`api poll <shell-id> --link`). Shell frames are RAW MAC'd stamped payloads (spooled anon, from="") the shell child consumes verbatim — NOT a harness `<EVENT>` reader; wrapping broke MAC/parse → never rendered.** FIX @ `caaf797`: cmd_poll_shell raw passthrough (`println!("{}", m.body)`); agent-perch poll/worker/listen keep `<EVENT>`. SCOPE CARVE-OUT documented 4 places (comment at cmd_poll_shell + ADR-0020 clause + REQ-MSG-ENVELOPE title + CONTEXT:685): `<EVENT>`-everywhere = harness ARRIVING-MESSAGE surfaces on AGENT perches only; shell-command channel = distinct internal transport, EXEMPT; notify_shell_e2e = named guard. **2nd LESSON: `cargo nextest run --workspace` SILENTLY SKIPS env-gated E2Es** (notify_shell_e2e self-skips when SPT_NOTIFY_SHELL_BIN unset; the CI `test` job = plain `cargo test -p spt --test notify_shell_e2e` runs it for real). Pre-push sweep MUST build the adapter (`projects/spt-shell-notify` → `cargo build --release` → target/release/notify-shell.exe) + set SPT_NOTIFY_SHELL_BIN + run the cargo-test-only E2Es; nextest-green ≠ CI-green.

**3rd RED: Windows docs-drift gate exe-lock** (`xtask check` → "failed to remove target\debug\spt.exe", os-error-5). A test-spawned detached daemon (REQ-DAEMON-3 autostart) holds spt.exe; Windows can't overwrite a running exe (Linux can → Linux passed). LATENT on ANY cli.rs-touching PR (doyle's PR #13 hit it too with NO new E2Es — the docs-drift xtask rebuild collides; main never rebuilt there so it stayed hidden). FIX @ `f747e1e`: a Windows-only reap step BEFORE the docs-drift gate, **$GITHUB_WORKSPACE-path-scoped** (see [[no-machinewide-killon-shared-runner]] — NEVER machine-wide on the shared self-hosted runner). Guard rode PR #12 to main → fixes it fleet-wide.

**ALL MERGED to main @ `25ffd7a` (PR #12, 2026-06-15):** 5da7fcd impl + d2433d4 doyle-T6-docs + ac6c802 poll-hermetic + caaf797 shell-link-exempt + f747e1e CI-guard. doyle pinged perri (byte-capture waits for the operator-gated next release). doyle next: rebase PR #13 (update-apply [[update-apply-confident-message]] + docs incl F-003 strings-pointer) onto main → merge → release. **doyle found T6 doc-drift = MY MISS** (I built T0–T5+T7 code/tests, skipped T6's doc sweep: KNOWN-HAZARDS:348 Psyche-routing 7.x + FAULT-MATRIX:42 + docs-site still call __REPLY_TO__ the live wire header); doyle clearing it himself (docs+impl one PR), I stayed hands-off to avoid collision. NEXT: doyle merges branch→main → ping perri (poll byte-capture confirm) on publish. SHARED-WORKTREE LESSON: doyle saw my mid-flight uncommitted edits on the shared docs branch + tried to build (RED) — coordinate hands-off + gate-from-commit when both agents touch one tree.

**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their
838fff8 render_frames retargets parse side to `<EVENT>`, `from`→`<sptc_messages from=>` unchanged). Build
against canonical `<EVENT>`; don't validate vs poll's CURRENT relic output — **doyle/todlando ping perri when this MERGES to main + publishes** (perri owes a byte-capture confirm of poll output then). See [[spt-core-harness-boundary-and-grounding]], [[spt-claude-code-next]].


The memory's detailed but ends at "doyle next: rebase PR #13 → release" — now superseded by the final closure. Updating it:


Edit

description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. MERGED to main @25ffd7a 2026-06-15 (PR #12); release operator-gated next. perri ping = doyle's"

description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. SHIPPED in v0.7.1 (counter 13) 2026-06-15 + VERIFIED on the published surface by perri's byte-capture. Loop CLOSED end-to-end; F-002 resolved-shipped, F-003 docs-closed"


The file C:\Users\decid\.ccs\instances\bigscreen\projects\C--Users-decid-Documents-projects-spt-core\memory\event-envelope-reply-to-removal.md has been updated successfully. (file state is current in your context — no need to Read it back)

---
name: event-envelope-reply-to-removal
description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. MERGED to main @25ffd7a 2026-06-15 (PR #12); release operator-gated next. perri ping = doyle's"
metadata:
  node_type: memory
  type: project
  originSessionId: ce7ce158-107f-4da4-a203-cea98da80b27
---

**ADR-0020 (2026-06-15, crystallized via /grill-with-docs, operator-ruled).** Triggered by
perri's finding **F-002** (`api poll` multi-message output non-self-delimiting). Operator clarified the
design intent → grilled against CONTEXT/ADRs → these LOCKED decisions:

- **The `<EVENT type="msg" from="…">body</EVENT>` envelope (spt-proto::event, the ADR-0001 grammar) is
  the SOLE canonical arriving-message format at EVERY delivery surface — `api listen`/`spt ready` AND
  `api poll`/`worker-poll`, byte-identical.** Adapters parse the `<EVENT>` envelope (the `from` attr).
  Reverses REQ-MSG-4's "hook drains keep the raw frame by contract."
- **`__REPLY_TO__` was MIS-ELEVATED** during the clean-room port to a fake ADR-0001 "stable wire
  format" (spt-msg/wire.rs, lib.rs). It is REMOVED entirely: spool format_row, the spt-msg TCP frame,
  emit::parse_frame. `(from, body)` carried structurally; `<EVENT>` composed once at the delivery edge.
- **No legacy sister-interop** — spt-core never required byte-compat with legacy spt; the wire.rs
  "byte-for-byte with a sister" claim was part of the mis-elevation. (Operator confirmed.)
- **Reply-correlation rebinds onto the structural `from` / `<EVENT from=…>`** — ADR-0009 access-gate
  (reply-vs-new) + ADR-0012 Psyche/spt-live reply-target. Both ADRs amended (dated notes). Sender-
  identity granularity preserved (the relic carried the same `from`); no new correlation attribute.
- **F-002 dissolves** — `<EVENT>…</EVENT>` self-delimits; no record delimiter introduced.
- WAN wire (spt-net/wanmsg.rs) UNAFFECTED — `from` already structural (JSON field).

**DOCS DONE:** ADR-0020 + ADR-0009/0012 amendments + CONTEXT.md §Messaging-substrate glossary entry
("arriving-message envelope") + REQ-MSG-ENVELOPE registered (required_stages=["doc"], doc evidenced;
traceable 208/208 EXIT=0).

**IMPL DISPATCHED TO TODLANDO 2026-06-15 (spt-core executor; doyle gates — NOT doyle's to execute, NOT perri's).** Plan = `REQ-MSG-ENVELOPE-PLAN.md` (7 tasks, 3 open impl-decisions: TCP-wire shape, drain API, anon from). todlando JIT-plans → design-check to doyle → builds → doyle gates. Multi-crate scope (activate impl/unit/int when it lands): spt-store
(spool format_row), spt-msg (wire.rs delete, emit parse_frame, deliver/listener/ring/ready, lib.rs
ADR-0001 framing), spt (api poll/worker-poll delivery.rs::emit → compose `<EVENT>`, cli --from help),
spt-daemon (psyrelay/notif/access/shellchan reply-target), spt-live (outbound), KNOWN-HAZARDS Psyche
invariant. Until it lands, `api poll` STILL emits the raw `__REPLY_TO__` relic on the public binary.

**IMPL BUILT + COMMITTED 2026-06-15 @ `5da7fcd`** (branch docs/msg-envelope-adr-0020, stacked on docs `24e0628`; both off main). doyle blessed the 3 decisions (TCP wire = u32 len(from)+from+body structural inner-frame; DrainedMsg{from,body} struct; anon from="" preserved) + 2 re-emphases (adversarial typed-passthrough no-double-wrap; grep-clean incl test fixtures). All 7 tasks done across 27 files: spool DrainedMsg (format_row deleted), wire encode/decode_frame (parse_frame deleted), emit render_event_whole (hook drains, no chunk) + render_event_lines (listener, chunks), api poll/worker compose WHOLE <EVENT>, daemon/live/net reply-target on structural `from`. Self-verify GREEN: 1053 nextest + doctests + clippy -D + traceable EXIT=0 (REQ-MSG-ENVELOPE +doc+impl+unit+int) + grep-clean ZERO __REPLY_TO__ in crates/ + new poll-whole-self-delimiting int + xtask OK. **doyle GATE-PASS 2026-06-15** (independent repro from 5da7fcd: 1054 nextest, traceable 208/208 EXIT=0, reviewed structural frame/DrainedMsg/render_event_whole/adversarial no-double-wrap/psyrelay anti-spoof — all sound). **PR #12 CI then RED on my new poll int** — FALSE-GREEN bug: `spt api poll` is AUTH-GATED (REQ-HAZARD-LOCAL-API-AUTH; needs session_id matching info.json or --token); my E2E pinned neither → passed only where ambient $OWL_SESSION_ID set (dev box), AUTH_REFUSED on clean CI (random sid). FIX @ `ac6c802`: pin const SID on BOTH `ready --once` + the `poll` via .env(OWL_SESSION_ID) + .env_remove(SPT_API_TOKEN); verified with both vars UNSET locally (CI-clean repro). LESSON: any E2E spawning `api poll`/auth-gated machinery must pin OWL_SESSION_ID hermetically — never lean on ambient env (false green); re-verify with the env UNSET. doyle's local gate missed it (his env was set) — gate-repro must clear ambient env too. **PR #12 RED a 2nd time (different test): `notify_shell_e2e` — my cmd_poll_shell over-applied `<EVENT>` to the SHELL-LINK relay (`api poll <shell-id> --link`). Shell frames are RAW MAC'd stamped payloads (spooled anon, from="") the shell child consumes verbatim — NOT a harness `<EVENT>` reader; wrapping broke MAC/parse → never rendered.** FIX @ `caaf797`: cmd_poll_shell raw passthrough (`println!("{}", m.body)`); agent-perch poll/worker/listen keep `<EVENT>`. SCOPE CARVE-OUT documented 4 places (comment at cmd_poll_shell + ADR-0020 clause + REQ-MSG-ENVELOPE title + CONTEXT:685): `<EVENT>`-everywhere = harness ARRIVING-MESSAGE surfaces on AGENT perches only; shell-command channel = distinct internal transport, EXEMPT; notify_shell_e2e = named guard. **2nd LESSON: `cargo nextest run --workspace` SILENTLY SKIPS env-gated E2Es** (notify_shell_e2e self-skips when SPT_NOTIFY_SHELL_BIN unset; the CI `test` job = plain `cargo test -p spt --test notify_shell_e2e` runs it for real). Pre-push sweep MUST build the adapter (`projects/spt-shell-notify` → `cargo build --release` → target/release/notify-shell.exe) + set SPT_NOTIFY_SHELL_BIN + run the cargo-test-only E2Es; nextest-green ≠ CI-green.

**3rd RED: Windows docs-drift gate exe-lock** (`xtask check` → "failed to remove target\debug\spt.exe", os-error-5). A test-spawned detached daemon (REQ-DAEMON-3 autostart) holds spt.exe; Windows can't overwrite a running exe (Linux can → Linux passed). LATENT on ANY cli.rs-touching PR (doyle's PR #13 hit it too with NO new E2Es — the docs-drift xtask rebuild collides; main never rebuilt there so it stayed hidden). FIX @ `f747e1e`: a Windows-only reap step BEFORE the docs-drift gate, **$GITHUB_WORKSPACE-path-scoped** (see [[no-machinewide-killon-shared-runner]] — NEVER machine-wide on the shared self-hosted runner). Guard rode PR #12 to main → fixes it fleet-wide.

**ALL MERGED to main @ `25ffd7a` (PR #12, 2026-06-15):** 5da7fcd impl + d2433d4 doyle-T6-docs + ac6c802 poll-hermetic + caaf797 shell-link-exempt + f747e1e CI-guard. doyle pinged perri (byte-capture waits for the operator-gated next release). doyle next: rebase PR #13 (update-apply [[update-apply-confident-message]] + docs incl F-003 strings-pointer) onto main → merge → release. **doyle found T6 doc-drift = MY MISS** (I built T0–T5+T7 code/tests, skipped T6's doc sweep: KNOWN-HAZARDS:348 Psyche-routing 7.x + FAULT-MATRIX:42 + docs-site still call __REPLY_TO__ the live wire header); doyle clearing it himself (docs+impl one PR), I stayed hands-off to avoid collision. NEXT: doyle merges branch→main → ping perri (poll byte-capture confirm) on publish. SHARED-WORKTREE LESSON: doyle saw my mid-flight uncommitted edits on the shared docs branch + tried to build (RED) — coordinate hands-off + gate-from-commit when both agents touch one tree.

**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their
838fff8 render_frames retargets parse side to `<EVENT>`, `from`→`<sptc_messages from=>` unchanged). Build
against canonical `<EVENT>`; don't validate vs poll's CURRENT relic output — **doyle/todlando ping perri when this MERGES to main + publishes** (perri owes a byte-capture confirm of poll output then). See [[spt-core-harness-boundary-and-grounding]], [[spt-claude-code-next]].

-description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. MERGED to main @25ffd7a 2026-06-15 (PR #12); release operator-gated next. perri ping = doyle's"

+description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. SHIPPED in v0.7.1 (counter 13) 2026-06-15 + VERIFIED on the published surface by perri's byte-capture. Loop CLOSED end-to-end; F-002 resolved-shipped, F-003 docs-closed"


**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their

**SHIPPED + VERIFIED — LOOP CLOSED 2026-06-15.** PR #13 ([[update-apply-confident-message]] + the F-003 [strings]-pointer/{key}-catalog/[digest] docs) rebased onto main (inherited the f747e1e guard) → green → merged @ `30d2a92`. Released **v0.7.1 (patch, counter 13)**: doyle gated CI-green pre-tag on cb05600, todlando tagged, deployah signed+published (transient TLS blip on draft-create → rerun --failed, no re-tag). Hashes linux `81eededc…`, win `94bb5429…` — see [[v071-published]]. **perri's REQ-MSG-ENVELOPE byte-capture on the LIVE v0.7.1 binary = CONFIRM-MATCH** (od-verified): `<EVENT type="msg" from=…>body</EVENT>\n` per msg, body escaping `<br>`+entities+`&`-LAST exact, WHOLE self-delimiting envelopes, NO `<EVENT-PART>` on normal hook drains, NO `__REPLY_TO__`; raw drain → her render_frames → correct `<sptc_messages>` (parser confirm-match PASS); notify rides the same envelope. **F-002 DISSOLVED on the published surface, F-003 docs-residual CLOSED.** perri flipping REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int + committing the SPTC_ACCEPTANCE-gated poll-int guard. Design→impl→gate→ship→real-surface-verify, end to end. Don't re-plan.

**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their


**SHIPPED + VERIFIED — LOOP CLOSED 2026-06-15.** PR #13 ([[update-apply-confident-message]] + the F-003 [strings]-pointer/{key}-catalog/[digest] docs) rebased onto main (inherited the f747e1e guard) → green → merged @ `30d2a92`. Released **v0.7.1 (patch, counter 13)**: doyle gated CI-green pre-tag on cb05600, todlando tagged, deployah signed+published (transient TLS blip on draft-create → rerun --failed, no re-tag). Hashes linux `81eededc…`, win `94bb5429…` — see [[v071-published]]. **perri's REQ-MSG-ENVELOPE byte-capture on the LIVE v0.7.1 binary = CONFIRM-MATCH** (od-verified): `<EVENT type="msg" from=…>body</EVENT>\n` per msg, body escaping `<br>`+entities+`&`-LAST exact, WHOLE self-delimiting envelopes, NO `<EVENT-PART>` on normal hook drains, NO `__REPLY_TO__`; raw drain → her render_frames → correct `<sptc_messages>` (parser confirm-match PASS); notify rides the same envelope. **F-002 DISSOLVED on the published surface, F-003 docs-residual CLOSED.** perri flipping REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int + committing the SPTC_ACCEPTANCE-gated poll-int guard. Design→impl→gate→ship→real-surface-verify, end to end. Don't re-plan.

**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their

---
name: event-envelope-reply-to-removal
description: "ADR-0020: <EVENT> envelope = sole arriving-message format on agent perches (shell-link relay EXEMPT); __REPLY_TO__ removed. SHIPPED in v0.7.1 (counter 13) 2026-06-15 + VERIFIED on the published surface by perri's byte-capture. Loop CLOSED end-to-end; F-002 resolved-shipped, F-003 docs-closed"
metadata:
  node_type: memory
  type: project
  originSessionId: ce7ce158-107f-4da4-a203-cea98da80b27
---

**ADR-0020 (2026-06-15, crystallized via /grill-with-docs, operator-ruled).** Triggered by
perri's finding **F-002** (`api poll` multi-message output non-self-delimiting). Operator clarified the
design intent → grilled against CONTEXT/ADRs → these LOCKED decisions:

- **The `<EVENT type="msg" from="…">body</EVENT>` envelope (spt-proto::event, the ADR-0001 grammar) is
  the SOLE canonical arriving-message format at EVERY delivery surface — `api listen`/`spt ready` AND
  `api poll`/`worker-poll`, byte-identical.** Adapters parse the `<EVENT>` envelope (the `from` attr).
  Reverses REQ-MSG-4's "hook drains keep the raw frame by contract."
- **`__REPLY_TO__` was MIS-ELEVATED** during the clean-room port to a fake ADR-0001 "stable wire
  format" (spt-msg/wire.rs, lib.rs). It is REMOVED entirely: spool format_row, the spt-msg TCP frame,
  emit::parse_frame. `(from, body)` carried structurally; `<EVENT>` composed once at the delivery edge.
- **No legacy sister-interop** — spt-core never required byte-compat with legacy spt; the wire.rs
  "byte-for-byte with a sister" claim was part of the mis-elevation. (Operator confirmed.)
- **Reply-correlation rebinds onto the structural `from` / `<EVENT from=…>`** — ADR-0009 access-gate
  (reply-vs-new) + ADR-0012 Psyche/spt-live reply-target. Both ADRs amended (dated notes). Sender-
  identity granularity preserved (the relic carried the same `from`); no new correlation attribute.
- **F-002 dissolves** — `<EVENT>…</EVENT>` self-delimits; no record delimiter introduced.
- WAN wire (spt-net/wanmsg.rs) UNAFFECTED — `from` already structural (JSON field).

**DOCS DONE:** ADR-0020 + ADR-0009/0012 amendments + CONTEXT.md §Messaging-substrate glossary entry
("arriving-message envelope") + REQ-MSG-ENVELOPE registered (required_stages=["doc"], doc evidenced;
traceable 208/208 EXIT=0).

**IMPL DISPATCHED TO TODLANDO 2026-06-15 (spt-core executor; doyle gates — NOT doyle's to execute, NOT perri's).** Plan = `REQ-MSG-ENVELOPE-PLAN.md` (7 tasks, 3 open impl-decisions: TCP-wire shape, drain API, anon from). todlando JIT-plans → design-check to doyle → builds → doyle gates. Multi-crate scope (activate impl/unit/int when it lands): spt-store
(spool format_row), spt-msg (wire.rs delete, emit parse_frame, deliver/listener/ring/ready, lib.rs
ADR-0001 framing), spt (api poll/worker-poll delivery.rs::emit → compose `<EVENT>`, cli --from help),
spt-daemon (psyrelay/notif/access/shellchan reply-target), spt-live (outbound), KNOWN-HAZARDS Psyche
invariant. Until it lands, `api poll` STILL emits the raw `__REPLY_TO__` relic on the public binary.

**IMPL BUILT + COMMITTED 2026-06-15 @ `5da7fcd`** (branch docs/msg-envelope-adr-0020, stacked on docs `24e0628`; both off main). doyle blessed the 3 decisions (TCP wire = u32 len(from)+from+body structural inner-frame; DrainedMsg{from,body} struct; anon from="" preserved) + 2 re-emphases (adversarial typed-passthrough no-double-wrap; grep-clean incl test fixtures). All 7 tasks done across 27 files: spool DrainedMsg (format_row deleted), wire encode/decode_frame (parse_frame deleted), emit render_event_whole (hook drains, no chunk) + render_event_lines (listener, chunks), api poll/worker compose WHOLE <EVENT>, daemon/live/net reply-target on structural `from`. Self-verify GREEN: 1053 nextest + doctests + clippy -D + traceable EXIT=0 (REQ-MSG-ENVELOPE +doc+impl+unit+int) + grep-clean ZERO __REPLY_TO__ in crates/ + new poll-whole-self-delimiting int + xtask OK. **doyle GATE-PASS 2026-06-15** (independent repro from 5da7fcd: 1054 nextest, traceable 208/208 EXIT=0, reviewed structural frame/DrainedMsg/render_event_whole/adversarial no-double-wrap/psyrelay anti-spoof — all sound). **PR #12 CI then RED on my new poll int** — FALSE-GREEN bug: `spt api poll` is AUTH-GATED (REQ-HAZARD-LOCAL-API-AUTH; needs session_id matching info.json or --token); my E2E pinned neither → passed only where ambient $OWL_SESSION_ID set (dev box), AUTH_REFUSED on clean CI (random sid). FIX @ `ac6c802`: pin const SID on BOTH `ready --once` + the `poll` via .env(OWL_SESSION_ID) + .env_remove(SPT_API_TOKEN); verified with both vars UNSET locally (CI-clean repro). LESSON: any E2E spawning `api poll`/auth-gated machinery must pin OWL_SESSION_ID hermetically — never lean on ambient env (false green); re-verify with the env UNSET. doyle's local gate missed it (his env was set) — gate-repro must clear ambient env too. **PR #12 RED a 2nd time (different test): `notify_shell_e2e` — my cmd_poll_shell over-applied `<EVENT>` to the SHELL-LINK relay (`api poll <shell-id> --link`). Shell frames are RAW MAC'd stamped payloads (spooled anon, from="") the shell child consumes verbatim — NOT a harness `<EVENT>` reader; wrapping broke MAC/parse → never rendered.** FIX @ `caaf797`: cmd_poll_shell raw passthrough (`println!("{}", m.body)`); agent-perch poll/worker/listen keep `<EVENT>`. SCOPE CARVE-OUT documented 4 places (comment at cmd_poll_shell + ADR-0020 clause + REQ-MSG-ENVELOPE title + CONTEXT:685): `<EVENT>`-everywhere = harness ARRIVING-MESSAGE surfaces on AGENT perches only; shell-command channel = distinct internal transport, EXEMPT; notify_shell_e2e = named guard. **2nd LESSON: `cargo nextest run --workspace` SILENTLY SKIPS env-gated E2Es** (notify_shell_e2e self-skips when SPT_NOTIFY_SHELL_BIN unset; the CI `test` job = plain `cargo test -p spt --test notify_shell_e2e` runs it for real). Pre-push sweep MUST build the adapter (`projects/spt-shell-notify` → `cargo build --release` → target/release/notify-shell.exe) + set SPT_NOTIFY_SHELL_BIN + run the cargo-test-only E2Es; nextest-green ≠ CI-green.

**3rd RED: Windows docs-drift gate exe-lock** (`xtask check` → "failed to remove target\debug\spt.exe", os-error-5). A test-spawned detached daemon (REQ-DAEMON-3 autostart) holds spt.exe; Windows can't overwrite a running exe (Linux can → Linux passed). LATENT on ANY cli.rs-touching PR (doyle's PR #13 hit it too with NO new E2Es — the docs-drift xtask rebuild collides; main never rebuilt there so it stayed hidden). FIX @ `f747e1e`: a Windows-only reap step BEFORE the docs-drift gate, **$GITHUB_WORKSPACE-path-scoped** (see [[no-machinewide-killon-shared-runner]] — NEVER machine-wide on the shared self-hosted runner). Guard rode PR #12 to main → fixes it fleet-wide.

**ALL MERGED to main @ `25ffd7a` (PR #12, 2026-06-15):** 5da7fcd impl + d2433d4 doyle-T6-docs + ac6c802 poll-hermetic + caaf797 shell-link-exempt + f747e1e CI-guard. doyle pinged perri (byte-capture waits for the operator-gated next release). doyle next: rebase PR #13 (update-apply [[update-apply-confident-message]] + docs incl F-003 strings-pointer) onto main → merge → release. **doyle found T6 doc-drift = MY MISS** (I built T0–T5+T7 code/tests, skipped T6's doc sweep: KNOWN-HAZARDS:348 Psyche-routing 7.x + FAULT-MATRIX:42 + docs-site still call __REPLY_TO__ the live wire header); doyle clearing it himself (docs+impl one PR), I stayed hands-off to avoid collision. NEXT: doyle merges branch→main → ping perri (poll byte-capture confirm) on publish. SHARED-WORKTREE LESSON: doyle saw my mid-flight uncommitted edits on the shared docs branch + tried to build (RED) — coordinate hands-off + gate-from-commit when both agents touch one tree.

**DOWNSTREAM (perri/sptc):** parse the `<EVENT>` envelope, NOT `__REPLY_TO__` (told perri; their
838fff8 render_frames retargets parse side to `<EVENT>`, `from`→`<sptc_messages from=>` unchanged). Build
against canonical `<EVENT>`; don't validate vs poll's CURRENT relic output — **doyle/todlando ping perri when this MERGES to main + publishes** (perri owes a byte-capture confirm of poll output then). See [[spt-core-harness-boundary-and-grounding]], [[spt-claude-code-next]].

+**SHIPPED + VERIFIED — LOOP CLOSED 2026-06-15.** PR #13 ([[update-apply-confident-message]] + the F-003 [strings]-pointer/{key}-catalog/[digest] docs) rebased onto main (inherited the f747e1e guard) → green → merged @ `30d2a92`. Released **v0.7.1 (patch, counter 13)**: doyle gated CI-green pre-tag on cb05600, todlando tagged, deployah signed+published (transient TLS blip on draft-create → rerun --failed, no re-tag). Hashes linux `81eededc…`, win `94bb5429…` — see [[v071-published]]. **perri's REQ-MSG-ENVELOPE byte-capture on the LIVE v0.7.1 binary = CONFIRM-MATCH** (od-verified): `<EVENT type="msg" from=…>body</EVENT>\n` per msg, body escaping `<br>`+entities+`&`-LAST exact, WHOLE self-delimiting envelopes, NO `<EVENT-PART>` on normal hook drains, NO `__REPLY_TO__`; raw drain → her render_frames → correct `<sptc_messages>` (parser confirm-match PASS); notify rides the same envelope. **F-002 DISSOLVED on the published surface, F-003 docs-residual CLOSED.** perri flipping REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int + committing the SPTC_ACCEPTANCE-gated poll-int guard. Design→impl→gate→ship→real-surface-verify, end to end. Don't re-plan.


Recorded. **ADR-0020 loop closed end-to-end and verified on the published binary.**

## Done — full chain shipped in v0.7.1 (counter 13, public)

perri's `od`-verified byte-capture on the **live v0.7.1** binary is the definitive proof: poll/worker-poll emit `<EVENT type="msg" from=…>body</EVENT>\n` — whole self-delimiting envelopes, exact body-escaping, no `<EVENT-PART>` on hook drains, no `__REPLY_TO__`. Her parser confirm-match **PASS**. **F-002 dissolved, F-003 closed.** She's flipping REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int + committing the acceptance guard.

**Everything that flowed from your two asks:**
- `spt update apply` → confident "Updated" (your bug) ✓ shipped
- public docs use versions not milestone codes (your rule) ✓ + saved
- ADR-0020 `<EVENT>`-everywhere + `__REPLY_TO__` removal ✓ shipped + verified
- perri's `{key}` catalog / `[digest]` rule / `[strings]` file-pointer docs ✓ shipped
- 3 CI-caught defects fixed (poll-auth, notify-shell, fleet-wide exe-lock) + the machine-wide-kill safety catch
- gated CI-green pre-tag; transient publish blip auto-recovered

Tree clean (single worktree on main @ cb05600, merged branches deleted). Fleet idle/on-call. Fleet-roll is operator/deployah-gated — not initiating. **Idle and available** for the next thing.


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


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

# /spt:commune

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

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

## Flow

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Envelope shape (Phase 23 v1.8)

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

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

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

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

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

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


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


I'll commune to my Psyche. First download current Psyche context to diff the delta:


Bash

cd C:/Users/decid/Documents/projects/spt-core; $LIVE psyche-download doyle 2>&1 | head -120

Download current Psyche context


<memformat>
<?xml version="1.0"?>
<memformat version="1">
<!-- Changelog: v1 - initial seed -->
<topics>
  <topic name="events">Notable or unusual events and requests</topic>
  <topic name="interactions">Agent interactions -- who, roles, contributions</topic>
  <topic name="reflections">Thoughts on recent work -- what went well, what was tricky</topic>
  <topic name="ideas">Ideas to raise with the user</topic>
  <topic name="improvements">Further improvement ideas for recent problem spaces</topic>
  <topic name="timeline">What happened when, timing, sequencing</topic>
  <topic name="blockers">Current blockers or concerns</topic>
  <topic name="focus">Current focus and immediate next steps</topic>
</topics>
</memformat>
</memformat>

<current machine="HFENDULEAM" project="spt-core" branch="main" head_sha="cb0560052c7392d386d9e68db359bbbf3138dcfe" head_subject="release: v0.7.1 — <EVENT> envelope everywhere + confident update-apply m…" commits_since="0" commits_unpulled="0"/>
**v0.7.1 SHIPPED & PUBLIC.** deployah hit transient TLS handshake timeout on GitHub draft-create API (not code/disk). Re-ran failed `assemble` job (builds reused from prior green run), publish succeeded exit 0. **Counter 13 public** @ https://github.com/SaberMage/spt-releases/releases/tag/v0.7.1 with 8 assets.

**Hashes relayed to perri:**
- linux: 81eededc724676565a323a0a5b12bfed5f8b596c3e60eaa467d5d8b50cc7bfd5
- win: 94bb542970dfb6d7b5eff6abe308d0ee9cfeb69f62c04d43fad84b40aa47b898

doyle session wrapped: everything from this cycle shipped (confidence update message, ADR-0020 + __REPLY_TO__-removed, 3 docs-completeness fixes, 3 caught defects, Windows exe-lock safety catch). Both todlando + deployah idle/on-call, no open work.

**perri now actively executing:** REQ-MSG-ENVELOPE byte-capture unblocked. Upgrade 0.7.0→0.7.1 + throwaway perch setup in progress; will capture raw poll bytes via UPS drain, confirm `<EVENT>` envelope format (self-delimiting, no `<EVENT-PART>` on drain), flip HOOKS-API/UPS-INJECTION int, send confirm-bytes. F-003 docs-residual will close when her work completes (v0.7.1 already published with the 3 flagged fixes).<project-context-resolved name="spt-core"/>
Both PRs (#12, #13) merged main @ cb05600. v0.7.1 tag @ cb05600 (counter 13, now public). Single worktree on main, merged branches cleaned. Release gate → CI-green → tag → sign+publish → perri hashes all complete. Fleet roll next phase (operator/deployah-gated, not initiated). perri unblocked on REQ-MSG-ENVELOPE validation (active now).


Psyche is current through v0.7.1 publish + "perri actively executing" — but does NOT yet have perri's **CONFIRM-MATCH result** (loop closed) or the operator's **M11 next-steps** directive. Those are the delta. Let me get M11's scope from ROADMAP so the commune states it accurately:


cd C:/Users/decid/Documents/projects/spt-core; grep -nE "M11|Milestone 11|## M1[01]" ROADMAP.md | head; echo "=== M11 context ==="; grep -n -A12 "M11" ROADMAP.md | head -40

Find M11 scope in ROADMAP


50:- **🔜 M10 — Digest extractor & thread-spanning** (planned 2026-06-13; NEXT). Product of the digest grill (operator + `doyle`); governed by **ADR-0019** (reverses M9's "no manifest seam"), CONTEXT.md "session digest" rewritten. An `spt-claude-code` enabler, so it takes the M10 slot ahead of the Shell/remote-attach work (which shift to M11/M12). Adapter-declared **`[digest]` extractor seam** (imperative, no DSL; same source files as `[history]` by default + own-source escape; `api digest-entry` push fallback; register-time validation; `spt adapter digest-proof` + runtime skip-diagnostics — no silent drop) · **thread-spanning** across `/clear` session-hops (per-endpoint session ledger + `api boundary` append + last-K enumeration + distinctive boundary marker) · **two-origin merge** (spt's context-injection entries — psyche download / echo mirror / owl message — interleaved with extracted activity by `ts`; data model only). New reqs REQ-TERM-5/6/7. Deferred: GUI collapse, echo-reads-digest loop, autonomous freshness. Task breakdown: [`DIGEST-MILESTONE-PLAN.md`](./DIGEST-MILESTONE-PLAN.md). - **Queued after M10 (each its own plan+vet cycle):** **M11 — Shell-substrate extensions** — the M5 shell mechanism already shipped; this adds the **drive channel** (owner→shell ephemeral latest-wins continuous control), the **shell tunnel** (reliable-ordered link-bound stream for opaque protocols, USB/IP-class), **per-capability approval gates** (class-keyed grants), and the **Gateway-as-owner code audit**. **M12 — Remote attach (`spt rc`)** over the already-built R-TERM substrate (controller/viewer split with controller-exclusive resize, `kick`/`--take` handoff, consent-gated input; the xterm.js bridge client = the GUI terminal pane and the future Android-gateway shape). `spt-claude-code` scoping remains on deck alongside.
=== M11 context ===
50:- **🔜 M10 — Digest extractor & thread-spanning** (planned 2026-06-13; NEXT). Product of the digest grill (operator + `doyle`); governed by **ADR-0019** (reverses M9's "no manifest seam"), CONTEXT.md "session digest" rewritten. An `spt-claude-code` enabler, so it takes the M10 slot ahead of the Shell/remote-attach work (which shift to M11/M12). Adapter-declared **`[digest]` extractor seam** (imperative, no DSL; same source files as `[history]` by default + own-source escape; `api digest-entry` push fallback; register-time validation; `spt adapter digest-proof` + runtime skip-diagnostics — no silent drop) · **thread-spanning** across `/clear` session-hops (per-endpoint session ledger + `api boundary` append + last-K enumeration + distinctive boundary marker) · **two-origin merge** (spt's context-injection entries — psyche download / echo mirror / owl message — interleaved with extracted activity by `ts`; data model only). New reqs REQ-TERM-5/6/7. Deferred: GUI collapse, echo-reads-digest loop, autonomous freshness. Task breakdown: [`DIGEST-MILESTONE-PLAN.md`](./DIGEST-MILESTONE-PLAN.md). - **Queued after M10 (each its own plan+vet cycle):** **M11 — Shell-substrate extensions** — the M5 shell mechanism already shipped; this adds the **drive channel** (owner→shell ephemeral latest-wins continuous control), the **shell tunnel** (reliable-ordered link-bound stream for opaque protocols, USB/IP-class), **per-capability approval gates** (class-keyed grants), and the **Gateway-as-owner code audit**. **M12 — Remote attach (`spt rc`)** over the already-built R-TERM substrate (controller/viewer split with controller-exclusive resize, `kick`/`--take` handoff, consent-gated input; the xterm.js bridge client = the GUI terminal pane and the future Android-gateway shape). `spt-claude-code` scoping remains on deck alongside.
51-
52-> _Roadmap note: this milestone list is behind reality — M8 (CLI nounification + acceptance) and the v0.3.x release line shipped after M7 but are tracked in commits/CHANGELOG, not itemized here. A full roadmap reconciliation is a separate docs pass._
53-
54-## De-risk spikes (do early — these can invalidate the architecture)
55-
56-1. **Broker/brain no-terminate handoff** — prove a PTY child + open socket survive a daemon-*logic* restart (FD-passing or stable-broker shim). The hardest invariant in the design (ADR-0004).
57-2. **Iroh + mDNS smoke test** — two nodes, separate NATs, one message end-to-end; measure direct-vs-relay + binary size (research brief §5). Validates the whole networking premise.
58-3. **ConPTY + `portable-pty`** — host and drive a real session in a broker PTY on Windows + Linux.
59-4. **Manifest-driven lifecycle** — a manifest drives one real Claude Code session through spawn → bind → commune → signoff, proving harness-independence works in practice.
60-
61-## Immediate next steps
62-


M11 = **Shell-substrate extensions** (drive channel, shell tunnel, per-capability approval gates, Gateway-as-owner audit). Have everything for the delta. Writing the two-slice commune (in-project → both slices):


Write

<live-context>
**ADR-0020 loop CLOSED end-to-end (delta since your last snapshot).** perri's REQ-MSG-ENVELOPE byte-capture on the LIVE v0.7.1 binary = CONFIRM-MATCH (od-verified): poll/worker-poll emit `<EVENT type="msg" from=…>body</EVENT>\n` per message — whole self-delimiting envelopes, body escaping `<br>`+entities+`&`-LAST exact to her render_frames decode, NO `<EVENT-PART>` on normal hook drains, NO `__REPLY_TO__`; raw drain → her render_frames → correct `<sptc_messages>` (parser confirm-match PASS); notify rides the same envelope. **F-002 DISSOLVED on the published surface, F-003 docs-residual CLOSED.** perri now flipping REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int + committing the SPTC_ACCEPTANCE-gated poll-int guard (her self-driven task; no doyle gate needed). design→impl→gate→ship→real-surface-verify, complete.

**Session arc (what I did):** gated todlando's ADR-0020 impl; caught my OWN false-green (poll E2E leaned on ambient $OWL_SESSION_ID — gate-repro must clear ambient env); root-caused 3 CI defects (poll-auth false-green, notify-shell `<EVENT>`-wrap regression, fleet-wide Windows exe-lock); SAFETY-CAUGHT todlando's machine-wide `Stop-Process` on the shared self-hosted runner → required workspace-path-scoped guard (verified no live perch disrupted); drove the v0.7.1 release gate (CI-green pre-tag). Roles ran clean: doyle gate/coordinate · todlando execute · deployah publish · perri validate.

**NEXT SESSION = M11 (operator-directed).** Resume on M11 — Shell-substrate extensions. Fleet idle/on-call (todlando, deployah); perri self-driving the int flip.

**Lessons locked this session:** (1) pin OWL_SESSION_ID hermetically in any auth-gated E2E, re-verify with env UNSET; (2) `cargo nextest --workspace` SILENTLY SKIPS env-gated E2Es — pre-push must build the adapter + run the cargo-test-only E2Es (nextest-green ≠ CI-green); (3) never machine-wide process-kill on the shared self-hosted runner — scope to $GITHUB_WORKSPACE/PID; (4) public docs use VERSION numbers, never internal milestone/wave codes.
</live-context>
<project-context>
**spt-core status @ v0.7.1 (counter 13, PUBLIC) — main clean @ cb05600.** This session shipped ADR-0020 (`<EVENT>` is the sole arriving-message format at every surface incl api poll/worker-poll; `__REPLY_TO__` relic removed; shell-link relay EXEMPT as raw MAC'd internal transport, documented + notify_shell_e2e-guarded; F-002 dissolved) + the confident `spt update apply` "Updated spt-core to vX." message + 3 docs-completeness fixes ({key} substitution catalog, [digest] register rule, [strings] file-pointer syntax) + a workspace-scoped Windows exe-lock CI guard. PRs #12 + #13 merged; v0.7.1 verified on the real surface by perri's byte-capture.

**Milestone state:** M6 (v0.1.0) · M9 (v0.5.0, user-surfaces/Gateway-WAN) · M10 (v0.6.0, digest extractor + thread-spanning) · M12 (v0.7.0, remote-attach `spt rc`) all SHIPPED. ADR-0020 envelope rework shipped in v0.7.1.

**NEXT = M11 — Shell-substrate extensions** (its own plan+vet cycle): the **drive channel** (owner→shell ephemeral latest-wins continuous control), the **shell tunnel** (reliable-ordered link-bound stream for opaque protocols, USB/IP-class), **per-capability approval gates** (class-keyed grants), and the **Gateway-as-owner code audit**. Builds on the M5 shell mechanism (already shipped). Reqs to define/activate at M11 start. ROADMAP.md:50 has the scope line; note ROADMAP.md:52 — the milestone list lags reality (M8 + v0.3.x shipped un-itemized), a reconciliation docs pass is owed.

**End goal:** a harness-INDEPENDENT core for an agent ecosystem — inter-agent messaging, live-agent lifecycle (Psyche/commune/signoff), terminal/shell hosting, P2P networking, and a runtime-manifest harness contract — as a single `spt` binary; clean-room rebuild of `claude_skill_owl`. The proving frontend is **spt-claude-code** (perri's `claude-spt`/`sptc` adapter), built strictly against the PUBLISHED spt-releases + GH-Pages surface — v1 acceptance proof in progress (manifest-half green; REQ-MSG-ENVELOPE int closing now). spt-core never caters to a specific harness; adapters format for their harness.
</project-context>


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

<live-context>
**ADR-0020 loop CLOSED end-to-end (delta since your last snapshot).** perri's REQ-MSG-ENVELOPE byte-capture on the LIVE v0.7.1 binary = CONFIRM-MATCH (od-verified): poll/worker-poll emit `<EVENT type="msg" from=…>body</EVENT>\n` per message — whole self-delimiting envelopes, body escaping `<br>`+entities+`&`-LAST exact to her render_frames decode, NO `<EVENT-PART>` on normal hook drains, NO `__REPLY_TO__`; raw drain → her render_frames → correct `<sptc_messages>` (parser confirm-match PASS); notify rides the same envelope. **F-002 DISSOLVED on the published surface, F-003 docs-residual CLOSED.** perri now flipping REQ-DIST-HOOKS-API + REQ-UPS-INJECTION int + committing the SPTC_ACCEPTANCE-gated poll-int guard (her self-driven task; no doyle gate needed). design→impl→gate→ship→real-surface-verify, complete.

**Session arc (what I did):** gated todlando's ADR-0020 impl; caught my OWN false-green (poll E2E leaned on ambient $OWL_SESSION_ID — gate-repro must clear ambient env); root-caused 3 CI defects (poll-auth false-green, notify-shell `<EVENT>`-wrap regression, fleet-wide Windows exe-lock); SAFETY-CAUGHT todlando's machine-wide `Stop-Process` on the shared self-hosted runner → required workspace-path-scoped guard (verified no live perch disrupted); drove the v0.7.1 release gate (CI-green pre-tag). Roles ran clean: doyle gate/coordinate · todlando execute · deployah publish · perri validate.

**NEXT SESSION = M11 (operator-directed).** Resume on M11 — Shell-substrate extensions. Fleet idle/on-call (todlando, deployah); perri self-driving the int flip.

**Lessons locked this session:** (1) pin OWL_SESSION_ID hermetically in any auth-gated E2E, re-verify with env UNSET; (2) `cargo nextest --workspace` SILENTLY SKIPS env-gated E2Es — pre-push must build the adapter + run the cargo-test-only E2Es (nextest-green ≠ CI-green); (3) never machine-wide process-kill on the shared self-hosted runner — scope to $GITHUB_WORKSPACE/PID; (4) public docs use VERSION numbers, never internal milestone/wave codes.
</live-context>
<project-context>
**spt-core status @ v0.7.1 (counter 13, PUBLIC) — main clean @ cb05600.** This session shipped ADR-0020 (`<EVENT>` is the sole arriving-message format at every surface incl api poll/worker-poll; `__REPLY_TO__` relic removed; shell-link relay EXEMPT as raw MAC'd internal transport, documented + notify_shell_e2e-guarded; F-002 dissolved) + the confident `spt update apply` "Updated spt-core to vX." message + 3 docs-completeness fixes ({key} substitution catalog, [digest] register rule, [strings] file-pointer syntax) + a workspace-scoped Windows exe-lock CI guard. PRs #12 + #13 merged; v0.7.1 verified on the real surface by perri's byte-capture.

**Milestone state:** M6 (v0.1.0) · M9 (v0.5.0, user-surfaces/Gateway-WAN) · M10 (v0.6.0, digest extractor + thread-spanning) · M12 (v0.7.0, remote-attach `spt rc`) all SHIPPED. ADR-0020 envelope rework shipped in v0.7.1.

**NEXT = M11 — Shell-substrate extensions** (its own plan+vet cycle): the **drive channel** (owner→shell ephemeral latest-wins continuous control), the **shell tunnel** (reliable-ordered link-bound stream for opaque protocols, USB/IP-class), **per-capability approval gates** (class-keyed grants), and the **Gateway-as-owner code audit**. Builds on the M5 shell mechanism (already shipped). Reqs to define/activate at M11 start. ROADMAP.md:50 has the scope line; note ROADMAP.md:52 — the milestone list lags reality (M8 + v0.3.x shipped un-itemized), a reconciliation docs pass is owed.

**End goal:** a harness-INDEPENDENT core for an agent ecosystem — inter-agent messaging, live-agent lifecycle (Psyche/commune/signoff), terminal/shell hosting, P2P networking, and a runtime-manifest harness contract — as a single `spt` binary; clean-room rebuild of `claude_skill_owl`. The proving frontend is **spt-claude-code** (perri's `claude-spt`/`sptc` adapter), built strictly against the PUBLISHED spt-releases + GH-Pages surface — v1 acceptance proof in progress (manifest-half green; REQ-MSG-ENVELOPE int closing now). spt-core never caters to a specific harness; adapters format for their harness.
</project-context>


Commune written to `.claude/doyle-commune.md` — my Self listener will detect it, the Psyche wrapper ingests via the existing session, file auto-deletes on success.

**Delta captured (two-slice):**
- **live-context:** ADR-0020 loop CLOSED end-to-end (perri's CONFIRM-MATCH on live v0.7.1 — F-002 dissolved, F-003 closed, she's flipping int); the session arc + clean role-play; **next session = M11 (operator-directed)**; 4 locked lessons.
- **project-context:** v0.7.1 PUBLIC status; milestone state (M6/M9/M10/M12 + ADR-0020 shipped); **M11 = Shell-substrate extensions** scope (drive channel, shell tunnel, per-capability gates, Gateway-as-owner audit) for the resume; end goal (harness-independent `spt` core, spt-claude-code as the proving frontend).

Queued for ingest. Resume point set: **M11**. See you next session.