95	    history: &[HistoryRecord],
96	    commune_dir: &Path,
97	    timeout: Duration,
98	) -> Result<EchoResult, EchoError> {
99	    // Feed normalized history (newline-delimited) on stdin.
100	    let mut stdin = String::new();
101	    for r in history {
102	        stdin.push_str(&r.raw);
103	        stdin.push('\n');
104	    }
105	    let out =
106	        runtime.run_bounded_stdin(ECHO_COMMUNE_ROLE, keys, timeout, Some(stdin.as_bytes()))?;
107	    if !out.success() {
108	        return Err(EchoError::NonZero {
109	            code: out.status_code,

    history: &[HistoryRecord],
    commune_dir: &Path,
    timeout: Duration,
) -> Result<EchoResult, EchoError> {
    // Feed normalized history (newline-delimited) on stdin.
    let mut stdin = String::new();
    for r in history {
        stdin.push_str(&r.raw);
        stdin.push('\n');
    }
    let out =
        runtime.run_bounded_stdin(ECHO_COMMUNE_ROLE, keys, timeout, Some(stdin.as_bytes()))?;
    if !out.success() {
        return Err(EchoError::NonZero {
            code: out.status_code,


131-
132:### 4.6 Addressable-id charset reserves the address delimiters
133-<!-- [doc->REQ-HAZARD-ID-CHARSET] -->
134-- **Failure:** a bare endpoint id that contains `:` or `@` (or a path separator / whitespace / control char) makes the canonical qualified address `[subnet:]id[@node]` (ADR-0006 / REQ-INST-10) ambiguous to parse, and lets a name smuggle into a perch directory path. Once permissive ids exist in the wild, tightening later needs a migration.
135:- **Invariant:** every addressable id/name is validated to `[A-Za-z0-9_-]` + Hiragana/Katakana/CJK only, length `1..=64`, **at every creation seam** (`ready` start, `api bind`, `api listen`, `api worker-start`). `:` and `@` are permanently reserved as address delimiters; reads of existing perches are never re-validated. Enforce now (pre-M3/M4) so no permissive id-data accumulates.
136-- **spt-core mapping:** `spt_proto::id::validate_endpoint_id`; called at the four creation seams. The existing Psyche (`<parent>-psyche`) / Worker (`<parent>-w<N>`) suffix scheme uses only `-` + alphanumerics, so composite ids validate.
--
382-| 4.10 | Silent-node registry rows evicted (heard-map TTL); own rows never decay | registry pump eviction |
383:| 4.6 | Addressable-id charset reserves `:`/`@` delimiters | `spt_proto::id` at creation seams |
384-| 5.2 | tmp-write + atomic-rename + retry (EBUSY) | all state writes, binary swap |


echo confirmed (raw pipe verbatim, echo.rs:99-104). No REQ-HAZARD on history opacity — A won't trip a tracked hazard.

Verdict ready. One real gap in §2: plan cites native `{"r":"u"}` fixture as evidence A is "free," but that's a *different* discriminator (`r`/`u`/`a`) than the proposed digest schema (`role`/`input/agent/tool`). Schema-identity must be nailed before T7a. Sending ruling.


Bash

$OWL send --reply-to todlando <<'EOF'
[Wave 3 / G3 JIT — doyle ruling] GREENLIT to execute, with one §2 sharpening that must land before T7a code.

VERDICT: Approach A confirmed. It's the only candidate without a manifest seam (M9-PLAN:162 verified), the only clean CC-fixture E2E path, and it keeps the history subsystem opaque (history.rs:30-31 "carries verbatim" + echo.rs:99-104 raw-pipe both stay untouched). No REQ-HAZARD on history opacity, so A trips nothing tracked. §3 surgical retire (keep Digest/Turn/DigestEntry/ToolUse + delta machinery + digesthub; kill only parser/PtyDigest/byte-feed) is correct. §6 restoration discipline (retitle + tag-move + int-replacement SAME commit, never un-evidenced) and §8 choreography: approved as written.

SHARPENING (blocking T7a — resolve, don't hand-wave):
Your §2 cites the native `{"r":"u"}` fixture (history.rs:249) as evidence A is "free." It is NOT. That fixture's discriminator is `r` ∈ {u,a} — a DIFFERENT schema than your proposed digest contract (`role` ∈ {input,agent,tool}). So A is not "adapters already emit this"; A is "spt-core PUBLISHES a digest-record contract that projectable adapters must target." Name it that way. The resolution must pick ONE explicitly:

  - history-the-subsystem stays opaque (carries `raw` verbatim, adapter-defined — UNCHANGED), AND
  - the DIGEST defines its own spt-core-owned record contract (an overlay on the content of `raw`), AND
  - there is exactly ONE digest record schema. No dual schema, no `r`/`u`↔`role`/`input` mapping layer. Migrate the native `{"r":"u"}` fixture to the digest contract (it's a fixture — cheap), or settle the digest contract AS the native record shape. Your call which, but state it in the plan and in the T7a design note.

Because adapters encode against this contract, treat it as a published surface: enumerate `role` values exactly, document the schema in CONTEXT.md + MANIFEST (replacing the dropped [pty_digest] section, not just deleting it), and keep the unknown-field/malformed→skip forward-compat you already specced. Field names `role` / `text` / optional `tool:{name,arg}` APPROVED. Tool sprints = consecutive `role:"tool"` collapsed: APPROVED.

CARRY-FORWARD (already in your plan, holding you to them at G3):
- Path-A coarse → B-first+A-snapshot fallback: flagged not silent (tracing warn + G3 note). Confirmed acceptable IF flagged.
- E2E filename choice: flag at G3.
- T5 twohost honored-path int (gateway-wan tracked condition) is a SEPARATE item — not Wave 3 scope, but verify it got logged on REQ-MSG-6/ROADMAP while you're in the tree.

Execute. Use my judgment to clear hurdles as they surface; STOP-and-ping only if the §2 resolution forces a manifest seam after all (it shouldn't). Land last, quietest tree, per sequencing.
EOF

Send Wave 3 ruling to todlando


Error: Exit code 2
error: the following required arguments were not provided:
  <TARGET>

Usage: owl.exe send --reply-to <REPLY_TO> <TARGET> [FROM]

For more information, try '--help'.


Need TARGET positional.


$OWL send --reply-to todlando todlando doyle <<'EOF'
[Wave 3 / G3 JIT — doyle ruling] GREENLIT to execute, with one §2 sharpening that must land before T7a code.

VERDICT: Approach A confirmed. It's the only candidate without a manifest seam (M9-PLAN:162 verified), the only clean CC-fixture E2E path, and it keeps the history subsystem opaque (history.rs:30-31 "carries verbatim" + echo.rs:99-104 raw-pipe both stay untouched). No REQ-HAZARD on history opacity, so A trips nothing tracked. §3 surgical retire (keep Digest/Turn/DigestEntry/ToolUse + delta machinery + digesthub; kill only parser/PtyDigest/byte-feed) is correct. §6 restoration discipline (retitle + tag-move + int-replacement SAME commit, never un-evidenced) and §8 choreography: approved as written.

SHARPENING (blocking T7a — resolve, don't hand-wave):
Your §2 cites the native `{"r":"u"}` fixture (history.rs:249) as evidence A is "free." It is NOT. That fixture's discriminator is `r` in {u,a} — a DIFFERENT schema than your proposed digest contract (`role` in {input,agent,tool}). So A is not "adapters already emit this"; A is "spt-core PUBLISHES a digest-record contract that projectable adapters must target." Name it that way. The resolution must pick ONE explicitly:

  - history-the-subsystem stays opaque (carries `raw` verbatim, adapter-defined — UNCHANGED), AND
  - the DIGEST defines its own spt-core-owned record contract (an overlay on the content of `raw`), AND
  - there is exactly ONE digest record schema. No dual schema, no `r`/`u` to `role`/`input` mapping layer. Migrate the native `{"r":"u"}` fixture to the digest contract (it's a fixture — cheap), or settle the digest contract AS the native record shape. Your call which, but state it in the plan and in the T7a design note.

Because adapters encode against this contract, treat it as a published surface: enumerate `role` values exactly, document the schema in CONTEXT.md + MANIFEST (replacing the dropped [pty_digest] section, not just deleting it), and keep the unknown-field/malformed→skip forward-compat you already specced. Field names `role` / `text` / optional `tool:{name,arg}` APPROVED. Tool sprints = consecutive `role:"tool"` collapsed: APPROVED.

CARRY-FORWARD (already in your plan, holding you to them at G3):
- Path-A coarse to B-first+A-snapshot fallback: flagged not silent (tracing warn + G3 note). Confirmed acceptable IF flagged.
- E2E filename choice: flag at G3.
- T5 twohost honored-path int (gateway-wan tracked condition) is a SEPARATE item — not Wave 3 scope, but verify it got logged on REQ-MSG-6/ROADMAP while you're in the tree.

Execute. Use my judgment to clear hurdles as they surface; STOP-and-ping only if the §2 resolution forces a manifest seam after all (it shouldn't). Land last, quietest tree, per sequencing.
EOF

Send Wave 3 ruling to todlando


➜ REPLIED:todlando


Ruling sent to todlando.

**Verdict: GREENLIT, one §2 sharpening blocks T7a.**

Vetted plan against live tree — all load-bearing claims hold:
- M9-PLAN:162 "no manifest seam — rides `[history]`" ✅
- history.rs:30-31 opaque "carries verbatim" ✅ + echo.rs:99-104 raw-pipe ✅
- No REQ-HAZARD on history opacity → A trips nothing tracked

Approved as-is: §3 surgical retire (keep output+delta types, kill only parser), §6 restoration discipline (retitle+tag-move same commit), §8 choreography.

**Gap I caught:** §2 cites native `{"r":"u"}` fixture as evidence A is "free" — it's NOT. That fixture's discriminator is `r`∈{u,a}, a *different* schema than the proposed digest contract `role`∈{input,agent,tool}. A isn't "adapters already emit this" — it's "spt-core PUBLISHES a digest-record contract adapters must target." Told todlando to:
- pick ONE schema (migrate native fixture, or settle digest contract AS native shape)
- no dual-schema / no mapping layer
- treat it as published surface: document in CONTEXT + MANIFEST (replace dropped `[pty_digest]`, not just delete)

Field names approved. Carry-forward reminders: Path-A fallback flag, E2E filename flag, and verify T5 twohost log landed on REQ-MSG-6/ROADMAP.

Standing by for todlando's §2 resolution.


<task-notification>
<task-id>bg7cqpvni</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/5" id="0aa8fcbc" type="msg" from="todlando">[Wave 3 / G3 — sharpening folded, executing] ACK all. §2 reframed: the digest-record contract is an spt-core-PUBLISHED surface (not &quot;adapters already emit it&quot;); the {&quot;r&quot;:&quot;u&quot;} fixture was cited in error — removed as evidence. ONE schema, no r→role mapping layer. My pick (your &quot</EVENT-PART>
<EVENT-PART seq="2/5" id="0aa8fcbc">;my call&quot;): MIGRATE the stray native history-test fixture (history.rs:249) to the digest-contract shape — cosmetic, REQ-SEAM-HISTORY verbatim-carry semantics unchanged — because native history must stay adapter-opaque for echo/resume, so the digest schema can't become the universal native shape. Contract enumerated (role∈{input,agen</EVENT-PART>
<EVENT-PART seq="3/5" id="0aa8fcbc">t,tool}/text/optional tool{name,arg}, malformed→skip) and PUBLISHED in CONTEXT.md 334-343 + MANIFEST (replacing [pty_digest], not just deleting). Field names + consecutive-tool collapse: locked per your approval.

Plan updated: M9-WAVE3-JIT.md STATUS now GREENLIT/§2 RULED; §3 keep/delete/replace-migrate inventory; §7 gate adds contr</EVENT-PART>
<EVENT-PART seq="4/5" id="0aa8fcbc">act-published + fixture-migrated rows. Carry-forwards held: Path-A-coarse→B-first fallback flagged-not-silent; E2E filename flagged at G3.

NOTE: your msg arrived 9 parts, I got 1-8; part 9 truncated mid-&quot;T5 twohost honored-path int (gateway-wan tracked condition) is a SEPARA[TE]...&quot; — read as: T5 twohost is gateway-wan sco</EVENT-PART>
<EVENT-PART seq="5/5" id="0aa8fcbc">pe, tracked separately from G3, not a Wave3 item. If part 9 carried more, resend; else I have it.

Starting T7a now on branch m9-digest-logs. Will ping at G3 when wave lands CI-green.</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.


todlando folded it correctly + made the right schema-identity call: migrate native fixture to digest-contract shape, keep native history adapter-opaque (echo/resume depend on it) so the digest contract stays a genuine overlay, not the universal native shape. Better-reasoned than my framing. His T5 read is complete — nothing truncated past it.

Quick confirm so he's not waiting.


$OWL send --reply-to todlando todlando doyle <<'EOF'
[ACK — clear to run] §2 resolution endorsed. Your migrate-the-fixture call is the RIGHT one and better-reasoned than my framing: native history MUST stay adapter-opaque (echo/resume carry raw verbatim), so the digest contract can't be the universal native shape — it's a published overlay, fixture migrates to it. Locked.

Part 9 read CONFIRMED COMPLETE — nothing lost. Full text was: "T5 twohost honored-path int (gateway-wan tracked condition) is a SEPARATE item — not Wave 3 scope, but verify it got logged on REQ-MSG-6/ROADMAP while you're in the tree." Your paraphrase is exact. No resend needed.

Go. Ping at G3 CI-green.
EOF

ACK todlando, clear to run T7a


ACK sent. todlando executing T7a on `m9-digest-logs`.

**Wave 3 launched.** §2 ruled (Approach A, fixture-migrate, published contract). Plan GREENLIT. He pings at G3 CI-green.

Standing by.


<task-notification>
<task-id>bg7cqpvni</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT type="msg" from="todlando">[ACK — executing] Locked. Starting T7a commit 1 (spt-term projection primitive + published digest-record contract + units) now. Will fold in the T5-twohost-logged-on-REQ-MSG-6/ROADMAP verify as a side-check while in-tree (separate from G3 scope, as you flagged). Ping at G3 CI-green.</EVENT></event>
</task-notification>


todlando executing, pure ACK — no reply needed. Won't spam.

Worth persisting: Wave 3 launched + §2 ruling (durable, survives /clear). Check existing M9 memory.


1	---
2	name: m9-wave1-progress
3	description: M9 status — three of four gates closed (G1/G2/Gateway-WAN); only Wave 3/G3 remains
4	metadata:
5	  node_type: memory
6	  type: project
7	  originSessionId: cac905c4-0186-45cc-8462-8b34fe78904b
8	---
9
10	M9 "Adapter customization & session surfaces". Executor todlando, reviewer doyle. CONTEXT = NORMATIVE (deviation = STOP-and-ping, never code-around). Stacking branches; **merge-to-main ONLY for a public release** (operator policy 2026-06-13) — PRs are CI/gate vehicles. No tag rides M9.
11
12	**THREE GATES CLOSED (2026-06-12 → 06-13):**
13	- **G1 — Wave 1 `m9-adapter-custom` (PR #7):** adapter profiles + strings + hints. Doc-pin b189513 (skip-seen hint tiebreak ratified). CI green both runners. doyle re-ran + reviewed survey/engine/seams.
14	- **G2 — Wave 2 `m9-user-surfaces` (PR #8):** user-msg identity gate + Gateway acceptance + live-role.md. Blocker (reconcile must STRUCTURALLY exclude live-role.md, not by-rarity) fixed 77e0fb1 + verified (RoleExcluded before reconcile_file; artifact preserved; failer-runtime test). Win RED was hfenduleam disk-full not code — see [[hfenduleam-target-bloat-ci-red]]. CI green.
15	- **Gateway-WAN — `m9-gateway-wan` (PR #9, REQ-MSG-6):** cross-node Gateway user-msg. `Instance.endpoint_type` advert (additive/N-1) + `receive_wan` resolves via instances_of(from) filtered to QUIC-proven origin_node, is_gateway only. **Trust posture (operator-ratified 2026-06-13): subnet membership = trust boundary; no anti-forge defense; user-surface-set VETOED.** Matrix + wanmsg + xtask green. doyle authored the doc/spec (CONTEXT §Gateway + ROADMAP + JIT plan, commit 30fea7f) → directed todlando → gated PASS.
16
17	**Tracked condition — LOGGED (58f034f on PR #9):** T5 twohost HONORED-path int marked REQUIRED-BEFORE-LECTURN on REQ-MSG-6 + ROADMAP ("activate int when adding it"). Negative/security path already E2E (wanmsg funnel); positive honored path has no exerciser until a real cross-node Gateway. Don't re-flag — it's logged.
18
19	**LAST M9 PIECE = Wave 3 / G3:** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.
20
21	**owl send gotcha:** owl `send` HANGS if body is passed as a positional FROM arg — pipe body via stdin heredoc (`$OWL send <target> <<'EOF'…EOF`). todlando hit this; messages stalled silently.
22

---
name: m9-wave1-progress
description: M9 status — three of four gates closed (G1/G2/Gateway-WAN); only Wave 3/G3 remains
metadata:
  node_type: memory
  type: project
  originSessionId: cac905c4-0186-45cc-8462-8b34fe78904b
---

M9 "Adapter customization & session surfaces". Executor todlando, reviewer doyle. CONTEXT = NORMATIVE (deviation = STOP-and-ping, never code-around). Stacking branches; **merge-to-main ONLY for a public release** (operator policy 2026-06-13) — PRs are CI/gate vehicles. No tag rides M9.

**THREE GATES CLOSED (2026-06-12 → 06-13):**
- **G1 — Wave 1 `m9-adapter-custom` (PR #7):** adapter profiles + strings + hints. Doc-pin b189513 (skip-seen hint tiebreak ratified). CI green both runners. doyle re-ran + reviewed survey/engine/seams.
- **G2 — Wave 2 `m9-user-surfaces` (PR #8):** user-msg identity gate + Gateway acceptance + live-role.md. Blocker (reconcile must STRUCTURALLY exclude live-role.md, not by-rarity) fixed 77e0fb1 + verified (RoleExcluded before reconcile_file; artifact preserved; failer-runtime test). Win RED was hfenduleam disk-full not code — see [[hfenduleam-target-bloat-ci-red]]. CI green.
- **Gateway-WAN — `m9-gateway-wan` (PR #9, REQ-MSG-6):** cross-node Gateway user-msg. `Instance.endpoint_type` advert (additive/N-1) + `receive_wan` resolves via instances_of(from) filtered to QUIC-proven origin_node, is_gateway only. **Trust posture (operator-ratified 2026-06-13): subnet membership = trust boundary; no anti-forge defense; user-surface-set VETOED.** Matrix + wanmsg + xtask green. doyle authored the doc/spec (CONTEXT §Gateway + ROADMAP + JIT plan, commit 30fea7f) → directed todlando → gated PASS.

**Tracked condition — LOGGED (58f034f on PR #9):** T5 twohost HONORED-path int marked REQUIRED-BEFORE-LECTURN on REQ-MSG-6 + ROADMAP ("activate int when adding it"). Negative/security path already E2E (wanmsg funnel); positive honored path has no exerciser until a real cross-node Gateway. Don't re-flag — it's logged.

**LAST M9 PIECE = Wave 3 / G3:** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

**owl send gotcha:** owl `send` HANGS if body is passed as a positional FROM arg — pipe body via stdin heredoc (`$OWL send <target> <<'EOF'…EOF`). todlando hit this; messages stalled silently.


Update Wave 3 line with launch state + §2 ruling so it survives /clear.


Edit

**LAST M9 PIECE = Wave 3 / G3:** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

**LAST M9 PIECE = Wave 3 / G3 — LAUNCHED 2026-06-13 (branch `m9-digest-logs` off main, lands as one PR):** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Plan = `M9-WAVE3-JIT.md` (doyle GREENLIT, §2 RULED). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

**§2 RULING (doyle, the one design decision — Approach A):** digest projects a SMALL spt-core-PUBLISHED record contract (`role`∈{input,agent,tool} / `text` / optional `tool{name,arg}`; consecutive `role:"tool"` collapsed; malformed→skip). NOT "adapters already emit it" — it's a published surface adapters target. CRITICAL: native history stays adapter-OPAQUE (echo/resume carry `raw` verbatim, REQ-SEAM-HISTORY) → digest contract is an OVERLAY, NOT the universal native shape. ONE schema, no `r`→`role` mapping layer; the stray native test fixture (history.rs:249 `{"r":"u"}`) MIGRATES to the digest shape. Contract published in CONTEXT.md 334-343 + MANIFEST (REPLACES `[pty_digest]`, not just deletes). §3 retire is SURGICAL: keep Digest/Turn/DigestEntry/ToolUse + delta machinery + digesthub; kill only parser/PtyDigest/byte-feed. Carry-forwards: Path-A-coarse→B-first fallback flagged-not-silent; E2E filename flagged at G3.


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

**LAST M9 PIECE = Wave 3 / G3 — LAUNCHED 2026-06-13 (branch `m9-digest-logs` off main, lands as one PR):** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Plan = `M9-WAVE3-JIT.md` (doyle GREENLIT, §2 RULED). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

**§2 RULING (doyle, the one design decision — Approach A):** digest projects a SMALL spt-core-PUBLISHED record contract (`role`∈{input,agent,tool} / `text` / optional `tool{name,arg}`; consecutive `role:"tool"` collapsed; malformed→skip). NOT "adapters already emit it" — it's a published surface adapters target. CRITICAL: native history stays adapter-OPAQUE (echo/resume carry `raw` verbatim, REQ-SEAM-HISTORY) → digest contract is an OVERLAY, NOT the universal native shape. ONE schema, no `r`→`role` mapping layer; the stray native test fixture (history.rs:249 `{"r":"u"}`) MIGRATES to the digest shape. Contract published in CONTEXT.md 334-343 + MANIFEST (REPLACES `[pty_digest]`, not just deletes). §3 retire is SURGICAL: keep Digest/Turn/DigestEntry/ToolUse + delta machinery + digesthub; kill only parser/PtyDigest/byte-feed. Carry-forwards: Path-A-coarse→B-first fallback flagged-not-silent; E2E filename flagged at G3.

---
name: m9-wave1-progress
description: M9 status — three of four gates closed (G1/G2/Gateway-WAN); only Wave 3/G3 remains
metadata:
  node_type: memory
  type: project
  originSessionId: cac905c4-0186-45cc-8462-8b34fe78904b
---

M9 "Adapter customization & session surfaces". Executor todlando, reviewer doyle. CONTEXT = NORMATIVE (deviation = STOP-and-ping, never code-around). Stacking branches; **merge-to-main ONLY for a public release** (operator policy 2026-06-13) — PRs are CI/gate vehicles. No tag rides M9.

**THREE GATES CLOSED (2026-06-12 → 06-13):**
- **G1 — Wave 1 `m9-adapter-custom` (PR #7):** adapter profiles + strings + hints. Doc-pin b189513 (skip-seen hint tiebreak ratified). CI green both runners. doyle re-ran + reviewed survey/engine/seams.
- **G2 — Wave 2 `m9-user-surfaces` (PR #8):** user-msg identity gate + Gateway acceptance + live-role.md. Blocker (reconcile must STRUCTURALLY exclude live-role.md, not by-rarity) fixed 77e0fb1 + verified (RoleExcluded before reconcile_file; artifact preserved; failer-runtime test). Win RED was hfenduleam disk-full not code — see [[hfenduleam-target-bloat-ci-red]]. CI green.
- **Gateway-WAN — `m9-gateway-wan` (PR #9, REQ-MSG-6):** cross-node Gateway user-msg. `Instance.endpoint_type` advert (additive/N-1) + `receive_wan` resolves via instances_of(from) filtered to QUIC-proven origin_node, is_gateway only. **Trust posture (operator-ratified 2026-06-13): subnet membership = trust boundary; no anti-forge defense; user-surface-set VETOED.** Matrix + wanmsg + xtask green. doyle authored the doc/spec (CONTEXT §Gateway + ROADMAP + JIT plan, commit 30fea7f) → directed todlando → gated PASS.

**Tracked condition — LOGGED (58f034f on PR #9):** T5 twohost HONORED-path int marked REQUIRED-BEFORE-LECTURN on REQ-MSG-6 + ROADMAP ("activate int when adding it"). Negative/security path already E2E (wanmsg funnel); positive honored path has no exerciser until a real cross-node Gateway. Don't re-flag — it's logged.

**LAST M9 PIECE = Wave 3 / G3:** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

**owl send gotcha:** owl `send` HANGS if body is passed as a positional FROM arg — pipe body via stdin heredoc (`$OWL send <target> <<'EOF'…EOF`). todlando hit this; messages stalled silently.

-**LAST M9 PIECE = Wave 3 / G3:** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

+**LAST M9 PIECE = Wave 3 / G3 — LAUNCHED 2026-06-13 (branch `m9-digest-logs` off main, lands as one PR):** digest log-projection engine + `api digest-entry` → RETIRE the PTY-parse half + REQ-TERM-4 retitle/re-point in the SAME commit (never momentarily un-evidenced). Plan = `M9-WAVE3-JIT.md` (doyle GREENLIT, §2 RULED). Watch: history-grain fallback pre-authorized but MUST be flagged at G3 if taken. Zero PTY-parse remnants; CC-fixture digest E2E; docs sweep. Don't re-plan G1/G2/Gateway-WAN — closed.

+**§2 RULING (doyle, the one design decision — Approach A):** digest projects a SMALL spt-core-PUBLISHED record contract (`role`∈{input,agent,tool} / `text` / optional `tool{name,arg}`; consecutive `role:"tool"` collapsed; malformed→skip). NOT "adapters already emit it" — it's a published surface adapters target. CRITICAL: native history stays adapter-OPAQUE (echo/resume carry `raw` verbatim, REQ-SEAM-HISTORY) → digest contract is an OVERLAY, NOT the universal native shape. ONE schema, no `r`→`role` mapping layer; the stray native test fixture (history.rs:249 `{"r":"u"}`) MIGRATES to the digest shape. Contract published in CONTEXT.md 334-343 + MANIFEST (REPLACES `[pty_digest]`, not just deletes). §3 retire is SURGICAL: keep Digest/Turn/DigestEntry/ToolUse + delta machinery + digesthub; kill only parser/PtyDigest/byte-feed. Carry-forwards: Path-A-coarse→B-first fallback flagged-not-silent; E2E filename flagged at G3.


Memory updated. Wave 3 launched, §2 ruling persisted.

**State:** todlando running T7a commit 1 on `m9-digest-logs`. Pings at G3 CI-green. Nothing for me until then.

Standing by.


<task-notification>
<task-id>bg7cqpvni</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="90a5b1d8" type="msg" from="todlando">[T7a LANDED + T7b scope question] T7a done on m9-digest-logs, all green (traceable EXIT=0):
- 82ead73 spt-term::projection — published digest-record contract (role∈{input,agent,tool}/text/optional tool{name,arg}) + project/project_lines + 8 units. Forward-compat: unknown fields tolerated, malformed/unknown-rol</EVENT-PART>
<EVENT-PART seq="2/6" id="90a5b1d8">e/toolless-tool skipped. Additive — parser still green.
- 6033326 api digest-entry door + spt_store::append_digest_entry → SEPARATE digest.log (never pollutes the opaque transcript history.log). Validates at the door via record_to_tagged; auth-gated.

DISCOVERY affecting T7b scope (your judgment): Brain::run_digest_feed (the PTY b</EVENT-PART>
<EVENT-PART seq="3/6" id="90a5b1d8">yte-feed driver) has NO production caller — only its def, a doc-ref, and the daemon-half E2E drive it. The daemon serves the digest CONTROL channel (pull/subscribe) but NOTHING ever fed the hub in production. B8 shipped engine+hub+control+manually-driven E2E, never the live session→engine feed. So today `spt endpoint digest` always resolve</EVENT-PART>
<EVENT-PART seq="4/6" id="90a5b1d8">s &quot;no live digest&quot;.

So T7b = retire + a FRESH log-sourced wire, not replace-a-live-path (byte-feed removes cleanly, nothing live depends on it). QUESTION: for G3, is the bar a TOPOLOGY-INDEPENDENT SNAPSHOT-PULL (daemon projects from the endpoint's records on-demand at the snapshot handler — proven by the CC-fixture E2E, mat</EVENT-PART>
<EVENT-PART seq="5/6" id="90a5b1d8">ching the pre-M9 wiring level), with the delta-stream LIVE-NUDGE (file-watch→publish, brand-new work that never existed) deferred to the frontend milestone that actually consumes the stream? Or do you want the live nudge in M9?

My lean: snapshot-pull on-demand projection for G3 (it's the topology-independence proof + REQ-TERM-4 int ev</EVENT-PART>
<EVENT-PART seq="6/6" id="90a5b1d8">idence); defer live-nudge to the consuming milestone — keeps Wave3 a retire+re-found, not a new-realtime-subsystem build. Your call. Holding T7b on your ruling. (Also: still owe the T5-twohost-logged-on-REQ-MSG-6 side-check — will do in-tree.)</EVENT-PART></event>
</task-notification>


T7a landed clean — `digest.log` separate from opaque `history.log` is the right move (keeps history-opaque invariant). T7b scope question is good and his lean is sound. One load-bearing fact to spot-check: "run_digest_feed has no production caller" — a wrong retire-without-replacement = regression. Quick verify.


crates\spt-daemon\src\brain.rs:760:    pub fn run_digest_feed(
crates\spt-daemon\src\digest.rs:219:/// ([`crate::brain::Brain::run_digest_feed`]) and any in-process daemon feed


Confirmed: `run_digest_feed` = def (brain.rs:760) + one doc-ref only. No production caller. His discovery holds — byte-feed retires cleanly, zero regression risk. Ruling stands.


Bash

$OWL send --reply-to todlando todlando doyle <<'EOF'
[T7b scope — doyle ruling] Your lean APPROVED: snapshot-pull on-demand projection IS the G3 bar. Defer the autonomous live-nudge. Reasoning + guardrails:

VERIFIED your discovery independently: `run_digest_feed` grep = def (brain.rs:760) + one doc-ref (digest.rs:219), zero production callers. So byte-feed retires cleanly, nothing live regresses. Confirmed, not just trusted.

WHY snapshot-pull is the right G3 bar:
- It's CONTEXT-CONSISTENT, not a scope cut. CONTEXT.md:338 already says near-realtime tailing "needs an incremental normalize story (design-phase concern)" — the staging is ANTICIPATED in the normative model. Deferring the file-watch nudge is honoring CONTEXT, not deviating from it.
- The delta-stream CONTRACT still ships: the control channel + DigestUpdate + common_prefix_len + follow/subscribe machinery all SURVIVE (§3 keep). A subscriber can follow and receive structured deltas — they're driven by pull/digest-entry-push publishes rather than autonomous file-watch. The stream is real; only its autonomous freshness source is deferred.
- The deferred piece has NO CONSUMER yet (Shell=M10, GUI/frontend pane, Gateway agent-window — none exist). Building file-watch→publish realtime infra now = speculative subsystem with nothing reading it. That's exactly your §9 "don't expand scope — no new realtime subsystem" watch-item. YAGNI until the consuming milestone activates it.
- Snapshot-pull is the actual FIX to the gap you found: on-demand projection at the handler means `spt endpoint digest` returns real projected content instead of "no live digest." Functional, not a stub. And it's the REQ-TERM-4 int evidence (CC-fixture E2E, topology-independent). Sufficient.

GUARDRAILS (so REQ-TERM-4 stays honest):
1. RETITLE must NOT overclaim near-realtime live tailing. Honest scope = "projection of normalized session logs, snapshot-pull (`spt endpoint digest`) + structured-delta-stream contract + `api digest-entry` push, opt-in Path-B log." The delta-stream is the CONTRACT/machinery, present and exercised — fine to name. Just don't claim autonomous near-realtime freshness you didn't wire.
2. The deferred autonomous file-watch→publish nudge = TRACKED, NOT DELETED. Log it like the T5 honored-path pattern: a CONTEXT.md:338 implementation-note + a ROADMAP/DEFERRED line naming the consuming milestone that activates it. Don't let it evaporate.
3. Name the deferred thing PRECISELY in the G3 note: "autonomous file-watch freshness deferred to consuming-frontend milestone; snapshot-pull + push-driven deltas delivered." Not "delta-stream deferred" (the contract isn't).

Everything else from the §2 ruling holds. Execute T7b. T5-twohost side-check whenever in-tree, no rush. Ping at G3 CI-green.
EOF

T7b scope ruling to todlando


➜ REPLIED:todlando


T7b ruling sent.

**Verdict: snapshot-pull on-demand projection = G3 bar. Live-nudge deferred (tracked, not deleted).**

- Spot-checked his discovery myself — `run_digest_feed` has zero production callers, byte-feed retires cleanly, no regression.
- Deferring file-watch freshness is **CONTEXT-consistent** (CONTEXT:338 already flags incremental tail as a design-phase concern) — honoring the model, not cutting scope.
- Delta-stream *contract/machinery* survives + ships; only autonomous freshness defers. No consumer exists yet (Shell=M10) → building realtime infra now = speculative, hits his own "don't expand scope" rail.

Guardrails attached: honest REQ-TERM-4 retitle (no near-realtime overclaim), deferred nudge logged like T5 pattern (CONTEXT note + ROADMAP/DEFERRED line), precise G3 wording.

todlando executing T7b. Pings at G3 CI-green. Standing by.


<task-notification>
<task-id>bg7cqpvni</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT-PART seq="1/3" id="ac24d200" type="msg" from="todlando">[ACK T7b ruling] Locked: snapshot-pull on-demand projection = G3 bar; autonomous live-nudge DEFERRED + TRACKED (CONTEXT.md:338 impl-note + ROADMAP/DEFERRED line naming consuming-frontend milestone — same pattern as T5 honored-path). Retitle stays honest: &quot;projection of normalized session logs, snapshot-pull + </EVENT-PART>
<EVENT-PART seq="2/3" id="ac24d200">structured-delta-stream CONTRACT + api digest-entry push, opt-in Path-B log&quot; — names the delta machinery (present+exercised) without claiming autonomous near-realtime freshness. Got your parts 1-8; part 9 truncated mid-guardrail-3 (&quot;snapsh...&quot;) — I have the intent (name the deferred piece precisely in the G3 note); resend on</EVENT-PART>
<EVENT-PART seq="3/3" id="ac24d200">ly if there's more substance past the wording. Starting T7b now.</EVENT-PART></event>
</task-notification>


Part 9 = guardrail 3 only, no substance past the wording he already captured. Confirm + release.


$OWL send --reply-to todlando todlando doyle <<'EOF'
[Confirm — go] Part 9 carried NO substance past what you have. Full guardrail 3 was just: G3 note says "autonomous file-watch freshness deferred to consuming-frontend milestone; snapshot-pull + push-driven deltas delivered" — NOT "delta-stream deferred" (the contract ships). You captured the intent exactly. No resend.

Retitle wording is honest and locked. Execute T7b. Ping at G3 CI-green.

(Owl chunking truncated my last two sends mid-tail — noting it; not blocking. If a future msg looks cut, assume the tail was wording-polish unless I flag otherwise.)
EOF

Confirm complete, release todlando to T7b


Confirmed, todlando running T7b (retire + re-wire + CC E2E + REQ-TERM-4 re-point).

**Wave 3 progress:**
- T7a ✅ landed (2 commits, traceable green): published digest-record contract + `api digest-entry`→separate `digest.log`
- T7b 🔄 in flight: snapshot-pull projection, retire PTY-parse, honest REQ-TERM-4 retitle, live-nudge tracked-deferred
- G3 gate pending CI-green

Noted owl chunk-truncation eating my message tails — twice now, both times only wording-polish lost. Not blocking; flagged to todlando.

Standing by for G3.


<task-notification>
<task-id>bg7cqpvni</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT-PART seq="1/2" id="733b2654" type="msg" from="todlando">[ACK — go] Got it, exact G3-note wording locked: &quot;autonomous file-watch freshness deferred to consuming-frontend milestone; snapshot-pull + push-driven deltas delivered&quot; (delta-stream CONTRACT ships, not deferred). Retitle locked. Noted the owl chunk-truncation pattern — I'll assume cut tails are wordin</EVENT-PART>
<EVENT-PART seq="2/2" id="733b2654">g-polish unless you flag. Executing T7b; ping at G3 CI-green.</EVENT-PART></event>
</task-notification>


Pure ACK, todlando executing T7b. No reply needed — won't spam.

Standing by for G3 CI-green ping.


<task-notification>
<task-id>bg7cqpvni</task-id>
Monitor event: "« spt event »"
<event><EVENT-PART seq="1/6" id="49207a0c" type="msg" from="todlando">[G3 READY — Wave 3 complete, gate request] m9-digest-logs is done, 5 commits atop 58f034f, all green:
- 82ead73 T7a spt-term projection primitive + published digest-record contract (8 units)
- 6033326 T7a api digest-entry door + separate digest.log store
- 5a09577 T7b-core on-demand projector (project_endp</EVENT-PART>
<EVENT-PART seq="2/6" id="49207a0c">oint_digest) + hub project_and_publish + reproject verb + handlers; removed dead run_digest_feed; CC-fixture E2E (harness_hosted_digest_projects_and_pushes_deltas — Path A JSONL, no broker/PTY)
- ba8e3c1 T7b retire+re-point: deleted DigestParser/PtyDigest/[pty_digest] (schema re-blessed, regex dep dropped); RETITLED REQ-TERM-4 to your hon</EVENT-PART>
<EVENT-PART seq="3/6" id="49207a0c">est log-source title with impl/unit/int tags on the projection+E2E; migrated the native fixture to the contract; full docs sweep.

G3 checklist:
- REQ-TERM-4 re-point audit: traceable EXIT=0, title honest, no orphan tags (old parser/from_manifest tags died with their code; new evidence = projection units + daemon projector + CC E2E + </EVENT-PART>
<EVENT-PART seq="4/6" id="49207a0c">MANIFEST doc tag). [OK] doc+impl+unit+int.
- Zero PTY-parse remnants: grep-clean (DigestParser/DigestPatterns/DigestError/PtyDigest/pty_digest/ingest_chunk/run_digest_feed all gone from code).
- CC-fixture E2E: green (topology-independent projection + push-driven delta both proven).
- Full suite: clippy -D workspace, cargo test --work</EVENT-PART>
<EVENT-PART seq="5/6" id="49207a0c">space, xtask check (cli reference regen'd), all green.
- Docs: MANIFEST + docs-site replace [pty_digest] w/ the digest-record contract; CONTEXT 341 + ADR-0008 debt discharged; CHANGELOG [Unreleased]. Deferred autonomous-freshness + Option-C re-home logged to docs/DEFERRED.md (consuming-frontend milestone) per your guardrails 2 &amp; 3 — e</EVENT-PART>
<EVENT-PART seq="6/6" id="49207a0c">xact G3-note wording used.

SIDE-CHECK CONFIRMED: T5 twohost honored-path int IS already logged REQUIRED-BEFORE-LECTURN on BOTH REQ-MSG-6 (toml:280) and ROADMAP:81 (commit 58f034f) — nothing to add.

Opening the PR (base m9-gateway-wan, the stack tip) for your G3 gate. Your gate, please.</EVENT-PART></event>
</task-notification>


Wave 3 complete, G3 gate requested. This is MY gate — milestone-close hinges on it. Verify independently, not rubber-stamp.

Recon the branch + kick verification.


git log --oneline -7 m9-digest-logs 2>&1; echo "---CURRENT---"; git branch --show-current; echo "---BRANCHES---"; git branch | grep -i digest

Check m9-digest-logs branch state


ba8e3c1 refactor(digest): retire the PTY-parse engine + re-point REQ-TERM-4 to the log source (T7b)
5a09577 feat(daemon): on-demand session-digest log projection + push-driven deltas (T7b core)
76ff4a3 docs(m9): Wave3 status — T7a landed; T7b scope discovery (byte-feed never live-wired)
6033326 feat(api): digest-entry push door for log-less adapters (T7a)
82ead73 feat(term): session-digest log projection primitive + published record contract (T7a)
714f6b8 docs(m9): Wave 3 / G3 JIT plan — digest log-projection (doyle-vetted)
58f034f docs(m9): log T5 twohost honored-path int as required-before-lecturn (gate condition)
---CURRENT---
m9-digest-logs
---BRANCHES---
* m9-digest-logs


On `m9-digest-logs`, 5 commits atop 58f034f. Kick heavy suite in background, run fast gate-checks foreground while it builds.


echo "=== traceable ===" && traceable-reqs check 2>&1 | tail -20; echo "=== clippy ===" && cargo clippy --workspace --all-targets -- -D warnings 2>&1 | tail -15; echo "=== test ===" && cargo test --workspace 2>&1 | tail -30; echo "=== nodefault ===" && cargo build --no-default-features 2>&1 | tail -8; echo "=== DONE ==="

Full gate suite: traceable, clippy, test, no-default-features


Command running in background with ID: bfu3vufda. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\13c96a50-2b78-4c16-a995-1da2b1836fd8\tasks\bfu3vufda.output. You will be notified when it completes. To check interim output, use Read on that file path.


Heavy suite building. Fast gate-checks now:


crates\spt-term\src\projection.rs:323:    fn empty_input_projects_empty_digest() {


grep -rn "REQ-TERM-4" C:/Users/decid/Documents/projects/spt-core/traceable-reqs.toml

Find REQ-TERM-4 toml line


414:id = "REQ-TERM-4"


Only match is a false positive — `empty_digest` contains substring "pty_digest" (em-**pty_digest**). It's the test `empty_input_projects_empty_digest`. Zero real PTY-parse remnants — grep-clean confirmed.

Now REQ-TERM-4 entry + DEFERRED + docs:


414	id = "REQ-TERM-4"
415	title = "Live activity buffer (session digest): projection of normalized session logs, snapshot-pull (spt endpoint digest) + structured-delta-stream contract + api digest-entry push, opt-in Path-B log"
416	required_stages = ["impl", "unit", "int"]
417	[[requirements]]
418	id = "REQ-FRONT-1"
419	title = "Day-one launcher/manager frontend (list/launch/attach/init)"
420	required_stages = []  # rule 5: GUI milestone — no frontend work in M0–M4
421
422	# ───────────────────────────── Install / migration / infra ──────────────────
423	[[requirements]]
424	id = "REQ-INSTALL-1"
425	title = "Two install paths; signed one-line script; OS-service registration"

id = "REQ-TERM-4"
title = "Live activity buffer (session digest): projection of normalized session logs, snapshot-pull (spt endpoint digest) + structured-delta-stream contract + api digest-entry push, opt-in Path-B log"
required_stages = ["impl", "unit", "int"]
[[requirements]]
id = "REQ-FRONT-1"
title = "Day-one launcher/manager frontend (list/launch/attach/init)"
required_stages = []  # rule 5: GUI milestone — no frontend work in M0–M4

# ───────────────────────────── Install / migration / infra ──────────────────
[[requirements]]
id = "REQ-INSTALL-1"
title = "Two install paths; signed one-line script; OS-service registration"


echo "=== TERM-4 evidence tags ==="; grep -rn "REQ-TERM-4" C:/Users/decid/Documents/projects/spt-core/crates C:/Users/decid/Documents/projects/spt-core/docs 2>&1; echo "=== DEFERRED digest ==="; grep -ni "digest\|freshness\|file-watch\|option-c\|option (c)" C:/Users/decid/Documents/projects/spt-core/docs/DEFERRED.md 2>&1 | head -20

REQ-TERM-4 evidence tags + DEFERRED entries


=== TERM-4 evidence tags ===
C:/Users/decid/Documents/projects/spt-core/crates/spt/Cargo.toml:20:# published digest-record contract (`spt_term::record_to_tagged`) — REQ-TERM-4.
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/api/mod.rs:191:    /// session-digest projection (ADR-0008 amendment, REQ-TERM-4).
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/api/reporting.rs:15://! - **digest-entry** (REQ-TERM-4) — the log-less-adapter push door: append a
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/api/reporting.rs:257:/// amendment, REQ-TERM-4): read ONE pre-formed digest-record (the published
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/api/reporting.rs:264:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/api/reporting.rs:275:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/api/reporting.rs:724:    #[test] // [unit->REQ-TERM-4] the digest-entry door validates against the
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/cli.rs:924:/// (REQ-TERM-4 / ADR-0008 amendment). Ensures the daemon is up, then pulls a
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/cli.rs:928:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt/src/cli.rs:7376:    // [unit->REQ-TERM-4] digest defaults: a one-shot, human-rendered snapshot.
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs:3://! (ADR-0008 amendment, 2026-06-12; REQ-TERM-4 log-source re-foundation).
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs:37:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs:79:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs:135:    // [unit->REQ-TERM-4] the diff base: a shared head yields its length (a tail
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs:146:    // [unit->REQ-TERM-4] a source-less endpoint (no perch, no records) projects an
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs:156:    // [unit->REQ-TERM-4] a log-less endpoint projects from its `digest.log` push
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:2://! REQ-TERM-4).
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:25:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:52:/// reply; the delta reaches subscribers via the fan-out, REQ-TERM-4).
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:110:    // [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:263:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:284:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:301:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:419:    // [unit->REQ-TERM-4] project_and_publish stores the latest snapshot + bumps the
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:439:    // [unit->REQ-TERM-4] the delta a publish fans out: an active-turn growth is a
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:461:    // [unit->REQ-TERM-4] a dropped subscriber is pruned on the next publish.
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:474:    // [unit->REQ-TERM-4] a fresh subscribe returns the current snapshot as its
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs:485:    // [unit->REQ-TERM-4] the human render shows input, collapsed agent text, and a
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/endpoint.rs:42:/// to for a snapshot pull or a delta-stream subscribe (ADR-0008 / REQ-TERM-4).
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/tests/digest.rs:2://! proof (ADR-0008 amendment / REQ-TERM-4).
C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/tests/digest.rs:17:// [int->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-store/src/history.rs:12://! REQ-TERM-4), and `api digest-entry` (a log-less adapter pushing a digest-record
C:/Users/decid/Documents/projects/spt-core/crates/spt-store/src/history.rs:13://! to the **separate** `digest.log` the projection tails — REQ-TERM-4). All want
C:/Users/decid/Documents/projects/spt-core/crates/spt-store/src/history.rs:33:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-store/src/history.rs:72:    // [unit->REQ-TERM-4] digest entries land in a SEPARATE digest.log, never the
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/Cargo.toml:17:# (one normalized JSON line per record) — ADR-0008 amendment / REQ-TERM-4.
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/digest.rs:2://! 2026-06-12; REQ-TERM-4).
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/digest.rs:87:    // [unit->REQ-TERM-4] arg truncation is spt-core presentation: short args pass
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/digest.rs:95:    // [unit->REQ-TERM-4] the presentation defaults are the spt-core formula
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/lib.rs:35://       (REQ-TERM-4; the superseded PTY-byte parser was retired M9 / ADR-0008
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:3://! 2026-06-12; REQ-TERM-4 log-source re-foundation).
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:49:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:101:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:111:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:169:// [impl->REQ-TERM-4]
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:189:    // [unit->REQ-TERM-4] an input opens a turn, agent text appends, and consecutive
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:216:    // [unit->REQ-TERM-4] non-adjacent tool records form separate sprints (an agent
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:235:    // [unit->REQ-TERM-4] the window keeps only the last N user turns.
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:250:    // [unit->REQ-TERM-4] tool args truncate to the configured width with an ellipsis
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:275:    // [unit->REQ-TERM-4] forward-compat: a malformed line, an unknown role, and a
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:295:    // [unit->REQ-TERM-4] forward-compat: an unknown *field* on an otherwise-valid
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:305:    // [unit->REQ-TERM-4] agent/tool records before any input form a preamble turn
C:/Users/decid/Documents/projects/spt-core/crates/spt-term/src/projection.rs:320:    // [unit->REQ-TERM-4] an empty record stream projects an empty digest (no panic,
C:/Users/decid/Documents/projects/spt-core/docs/adr/0008-live-activity-buffer-pty-digest.md:44:**Debt discharged (M9, 2026-06-13):** the PTY-parse engine (`spt-term`'s `DigestParser` + `spt-daemon`'s byte-feed `DigestEngine`) and the `[pty_digest]` manifest seam are **retired**. The digest is now an **on-demand projection** of the endpoint's normalized session-log records: `spt-term::projection` folds the published **digest-record contract** (the spt-core-owned `{role, text, tool}` overlay an adapter targets in its `[history]` lines, or pushes via `api digest-entry`) into the same structured `Digest`; `spt-daemon::digest::project_endpoint_digest` projects on demand at the snapshot-pull / reproject handlers and `DigestHub::project_and_publish` drives the delta-stream. Topology-independent (a Claude-Code-hosted endpoint's Path A JSONL projects with no broker PTY — proven by the CC-fixture E2E). The **autonomous file-watch freshness nudge** and **Option-C persistence re-home** are deferred to the consuming-frontend milestone (`docs/DEFERRED.md`) — the delta-stream contract ships, driven by pull / `api digest-entry` publishes. REQ-TERM-4 re-titled + its evidence re-pointed onto the projection in the same change.
C:/Users/decid/Documents/projects/spt-core/docs/MANIFEST.md:115:<!-- [doc->REQ-TERM-4] The session digest is a PROJECTION of the endpoint's
=== DEFERRED digest ===
31:| CLI nounification (M8 candidate; ratified shape from the 2026-06-06 grill): `spt endpoint` namespace absorbs fork/suspend/wake/shutdown/rename/stop/digest + `access` (per-endpoint store — NOT subnet-scoped) + `description` (ex-`resources` blurb; bare = show, `set` = author); `spt endpoint list [--local\|--subnet <name>]` = merged listing, default all-subnets grouped by subnet, SELF endpoint pinned distinctly at top (bare `spt endpoint` = this list); agent HOT PATH stays flat (`send`/`ring`/`ready`/`whoami`/`how-to`); `notify` → `spt subnet notify`; `notif` stays top-level (user-recall, cross-subnet); `spt daemon stop\|status` noun (hidden `daemon` → `daemon run`); agent-endpoint `shutdown` keeps its name | M7 grill (2026-06-06) | Second surface-breaking reorg mid-acceptance; adapter window still open immediately after M7 closes | M8 planning — mint REQs first (rule 3) |
44:| **Autonomous session-digest freshness + Option-C re-home (M9 ADR-0008 amendment deferral)**: M9 re-founded the digest as an ON-DEMAND projection of normalized session logs (`spt-term::projection`; `spt-daemon::digest::project_endpoint_digest`) — snapshot-pull and the structured-delta-stream CONTRACT ship, deltas driven by pulls / `api digest-entry` pushes. Two pieces deferred, both because they have **no consumer yet** (Shell=M10, GUI/frontend pane, Gateway agent-window — none exist): (a) the **autonomous file-watch→publish freshness nudge** (a watcher that re-projects + publishes when the log grows on its own, vs today's pull/push-driven publishes) — CONTEXT.md "session digest" anticipates this as "an incremental normalize story (design-phase concern)"; (b) the **Option-C opt-in persistence re-home** (the byte-feed engine drove per-turn persistence off `pty_digest.persist`, now deleted; re-homing it onto a `[history]`/digest config seam now = a manifest surface with nothing reading it). Building either now = a speculative realtime subsystem with no reader (the M9-PLAN §9 "no new realtime subsystem" watch-item). | M9 Wave 3 / G3 (doyle ruling 2026-06-13) — snapshot-pull is the G3 bar, delta-stream contract ships | Both ride a live consumer: the digest works topology-independently TODAY (pull projects real content; a subscriber follows and gets push-driven deltas). Autonomous freshness only matters when a frontend pane streams it continuously; Option-C only matters when something reads the coarse activity log. Deferring honors CONTEXT.md's anticipated staging, not a scope cut | The **consuming-frontend milestone** that streams the digest live (Shell M10 drive/sensory pane, the GUI "latest session output" pane, or the Gateway agent-window) — wire the file-watch nudge + re-home Option-C onto a digest config seam then, with a real reader to exercise both |


REQ-TERM-4: honest title, stages correct, doc+impl+unit+int evidence all on real code (not file-tops). DEFERRED.md:44 is thorough — todlando even caught a *second* deferral (Option-C persist re-home, since the deleted byte-feed drove per-turn persist). ADR-0008 debt-discharged note accurate.

One thing to scrutinize: title claims "opt-in Path-B log" but DEFERRED lists "Option-C persistence re-home" as deferred. Need to confirm that's not an overclaim. Read the E2E + the log surface.


1	//! Session-digest **log-projection** integration — the topology-independence
2	//! proof (ADR-0008 amendment / REQ-TERM-4).
3	//!
4	//! There is no broker and no PTY here: the endpoint is **harness-hosted** (a
5	//! Claude-Code-style per-session log is Path A's worked example). The daemon
6	//! projects its digest **on demand** from the adapter's `[history]` strategy
7	//! (`locate_normalize` over a fixture JSONL of published digest-records) and
8	//! serves it over the real digest-control channel:
9	//!
10	//! - **snapshot pull** ([`pull_snapshot`]) projects the current buffer and
11	//!   returns it — proving a log-sourced digest works with no broker-owned PTY
12	//!   (impossible under the superseded byte-parse shape);
13	//! - **delta-stream** ([`follow`]) + a [`reproject`] nudge after the log grows
14	//!   delivers the structured delta to a subscriber (the push-driven freshness
15	//!   the `api digest-entry` path also drives — autonomous file-watch is deferred
16	//!   to the consuming frontend milestone).
17	// [int->REQ-TERM-4]
18
19	use std::sync::atomic::{AtomicU32, Ordering};
20	use std::sync::Arc;
21	use std::thread;
22	use std::time::{Duration, Instant};
23
24	use spt_daemon::{follow, pull_snapshot, reproject, serve_digest_control, DigestHub};
25	use spt_store::info::{write_info, InfoJson};
26	use spt_store::perch::{self, resolve_perch_path, ParentHint};
27
28	static SEQ: AtomicU32 = AtomicU32::new(0);
29	fn unique(prefix: &str) -> String {
30	    let n = SEQ.fetch_add(1, Ordering::Relaxed);
31	    format!("spt-daemon-digest-{prefix}-{}-{}.sock", std::process::id(), n)
32	}
33
34	/// A stdin pass-through "normalizer": the located transcript bytes round-trip to
35	/// stdout unchanged, so the fixture JSONL *is* the normalized record stream.
36	#[cfg(unix)]
37	const PASSTHROUGH: &str = "cat";
38	#[cfg(windows)]
39	const PASSTHROUGH: &str = "cmd /C findstr /r \".\"";
40
41	/// Register a harness adapter whose `[history]` is `locate_normalize` over
42	/// `fixture` (a digest-record JSONL), and establish a harness-hosted perch for
43	/// `id` that records the adapter — the daemon resolves the projection source from
44	/// these two facts alone (no broker, no live feed).
45	fn establish_cc_endpoint(id: &str, fixture: &std::path::Path) {
46	    // A per-endpoint adapter (so distinct endpoints get distinct `[history]`
47	    // sources). TOML literal (single-quoted) strings carry the path + command
48	    // verbatim — no escaping of Windows backslashes or the findstr quotes.
49	    let adapter = format!("mock-cc-{id}");
50	    let manifest = format!(
51	        "[adapter]\nname = \"{adapter}\"\nkind = \"harness\"\nversion = \"1\"\n\
52	         min_spt_core_version = \"0\"\n\n\
53	         [history]\nstrategy = \"locate_normalize\"\n\
54	         locate_template = '{}'\nnormalize_command = '{}'\n",
55	        fixture.display(),
56	        PASSTHROUGH,
57	    );
58	    let src = perch::spt_home().join("srcs").join(format!("{adapter}-src"));
59	    std::fs::create_dir_all(&src).unwrap();
60	    std::fs::write(src.join("manifest.toml"), manifest).unwrap();
61	    spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();
62
63	    let perch_path = resolve_perch_path(id, ParentHint::Infer);
64	    std::fs::create_dir_all(&perch_path).unwrap();
65	    let mut rec = InfoJson::new(id, "0", std::process::id(), "cc-session", "live_agent");
66	    rec.adapter = Some(adapter);
67	    write_info(&perch_path, &rec).unwrap();
68	}
69
70	/// Serve a fresh digest-control channel on a unique socket; returns its name.
71	fn serve_hub() -> String {
72	    let name = unique("digest");
73	    let hub = Arc::new(DigestHub::new());
74	    let n = name.clone();
75	    thread::spawn(move || {
76	        let _ = serve_digest_control(&n, hub);
77	    });
78	    name
79	}
80
81	/// Retry `op` until it succeeds or `dur` elapses — bridges the bind/accept lag
82	/// between the serve thread and the first client connect.
83	fn retry<T>(dur: Duration, mut op: impl FnMut() -> std::io::Result<T>) -> std::io::Result<T> {
84	    let start = Instant::now();
85	    loop {
86	        match op() {
87	            Ok(v) => return Ok(v),
88	            Err(e) if start.elapsed() >= dur => return Err(e),
89	            Err(_) => thread::sleep(Duration::from_millis(20)),
90	        }
91	    }
92	}
93
94	// One test fn: the two assertions share a home. `SPT_HOME` is a process-global
95	// env var, so splitting into parallel `#[test]`s would race on it — the B8
96	// original was single-test for the same reason.
97	#[test]
98	fn harness_hosted_digest_projects_and_pushes_deltas() {
99	    let home = tempfile::tempdir().expect("tempdir");
100	    std::env::set_var("SPT_HOME", home.path());
101
102	    // ── Snapshot pull: projected on demand from a harness log, no broker/PTY ──
103	    // A fixture JSONL of published digest-records — the harness's "session log".
104	    let fixture = home.path().join("cc-session.jsonl");
105	    std::fs::write(
106	        &fixture,
107	        "{\"role\":\"input\",\"text\":\"add a file\"}\n\
108	         {\"role\":\"agent\",\"text\":\"on it\"}\n\
109	         {\"role\":\"tool\",\"tool\":{\"name\":\"Write\",\"arg\":\"src/a.rs\"}}\n",
110	    )
111	    .unwrap();
112	    establish_cc_endpoint("cc", &fixture);
113	    let digest_name = serve_hub();
114
115	    let (digest, version) = retry(Duration::from_secs(3), || pull_snapshot(&digest_name, "cc"))
116	        .expect("pull ok")
117	        .expect("a digest projects for the harness-hosted endpoint");
118	    assert!(version >= 1, "the projection has a version");
119	    assert_eq!(digest.turns.len(), 1, "one user turn: {digest:?}");
120	    assert_eq!(digest.turns[0].input.as_deref(), Some("add a file"));
121	    let rendered = spt_daemon::render_digest(&digest);
122	    assert!(
123	        rendered.contains("» add a file") && rendered.contains("used: Write(src/a.rs)"),
124	        "glanceable render carries the turn + collapsed sprint: {rendered}"
125	    );
126
127	    // ── Delta-stream: a reproject after the log grows delivers a delta ───────
128	    let fixture2 = home.path().join("cc2-session.jsonl");
129	    std::fs::write(&fixture2, "{\"role\":\"input\",\"text\":\"first\"}\n").unwrap();
130	    establish_cc_endpoint("cc2", &fixture2);
131
132	    // Seed the hub via a pull, then subscribe from that version.
133	    let (_d, base) = retry(Duration::from_secs(3), || pull_snapshot(&digest_name, "cc2"))
134	        .unwrap()
135	        .unwrap();
136
137	    let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
138	    let (tx, rx) = std::sync::mpsc::channel::<usize>();
139	    {
140	        let dname = digest_name.clone();
141	        thread::spawn(move || {
142	            let _ = follow(&dname, "cc2", base, |up| {
143	                if up.version <= base {
144	                    // The base delivery proves we are registered as a subscriber.
145	                    let _ = ready_tx.send(());
146	                    return true;
147	                }
148	                // The grown log adds a second turn → its delta.
149	                let last = up.turns.last().and_then(|t| t.input.clone());
150	                if last.as_deref() == Some("second") {
151	                    let _ = tx.send(up.turns.len());
152	                    return false;
153	                }
154	                true
155	            });
156	        });
157	    }
158	    // Wait until the subscriber is registered (base received), THEN grow the log
159	    // and nudge a re-projection — no publish can race ahead of the subscription.
160	    ready_rx
161	        .recv_timeout(Duration::from_secs(3))
162	        .expect("subscriber registered (base delivered)");
163	    std::fs::write(
164	        &fixture2,
165	        "{\"role\":\"input\",\"text\":\"first\"}\n{\"role\":\"input\",\"text\":\"second\"}\n",
166	    )
167	    .unwrap();
168	    reproject(&digest_name, "cc2").expect("nudge a re-projection");
169
170	    let got = wait_recv(&rx, Duration::from_secs(5));
171	    assert!(got.is_some(), "the subscriber received the push-driven delta");
172	}
173
174	/// Block up to `dur` for one value on `rx`.
175	fn wait_recv(rx: &std::sync::mpsc::Receiver<usize>, dur: Duration) -> Option<usize> {
176	    let start = Instant::now();
177	    loop {
178	        if let Ok(v) = rx.try_recv() {
179	            return Some(v);
180	        }
181	        if start.elapsed() >= dur {
182	            return None;
183	        }
184	        thread::sleep(Duration::from_millis(15));
185	    }
186	}
187

//! Session-digest **log-projection** integration — the topology-independence
//! proof (ADR-0008 amendment / REQ-TERM-4).
//!
//! There is no broker and no PTY here: the endpoint is **harness-hosted** (a
//! Claude-Code-style per-session log is Path A's worked example). The daemon
//! projects its digest **on demand** from the adapter's `[history]` strategy
//! (`locate_normalize` over a fixture JSONL of published digest-records) and
//! serves it over the real digest-control channel:
//!
//! - **snapshot pull** ([`pull_snapshot`]) projects the current buffer and
//!   returns it — proving a log-sourced digest works with no broker-owned PTY
//!   (impossible under the superseded byte-parse shape);
//! - **delta-stream** ([`follow`]) + a [`reproject`] nudge after the log grows
//!   delivers the structured delta to a subscriber (the push-driven freshness
//!   the `api digest-entry` path also drives — autonomous file-watch is deferred
//!   to the consuming frontend milestone).
// [int->REQ-TERM-4]

use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};

use spt_daemon::{follow, pull_snapshot, reproject, serve_digest_control, DigestHub};
use spt_store::info::{write_info, InfoJson};
use spt_store::perch::{self, resolve_perch_path, ParentHint};

static SEQ: AtomicU32 = AtomicU32::new(0);
fn unique(prefix: &str) -> String {
    let n = SEQ.fetch_add(1, Ordering::Relaxed);
    format!("spt-daemon-digest-{prefix}-{}-{}.sock", std::process::id(), n)
}

/// A stdin pass-through "normalizer": the located transcript bytes round-trip to
/// stdout unchanged, so the fixture JSONL *is* the normalized record stream.
#[cfg(unix)]
const PASSTHROUGH: &str = "cat";
#[cfg(windows)]
const PASSTHROUGH: &str = "cmd /C findstr /r \".\"";

/// Register a harness adapter whose `[history]` is `locate_normalize` over
/// `fixture` (a digest-record JSONL), and establish a harness-hosted perch for
/// `id` that records the adapter — the daemon resolves the projection source from
/// these two facts alone (no broker, no live feed).
fn establish_cc_endpoint(id: &str, fixture: &std::path::Path) {
    // A per-endpoint adapter (so distinct endpoints get distinct `[history]`
    // sources). TOML literal (single-quoted) strings carry the path + command
    // verbatim — no escaping of Windows backslashes or the findstr quotes.
    let adapter = format!("mock-cc-{id}");
    let manifest = format!(
        "[adapter]\nname = \"{adapter}\"\nkind = \"harness\"\nversion = \"1\"\n\
         min_spt_core_version = \"0\"\n\n\
         [history]\nstrategy = \"locate_normalize\"\n\
         locate_template = '{}'\nnormalize_command = '{}'\n",
        fixture.display(),
        PASSTHROUGH,
    );
    let src = perch::spt_home().join("srcs").join(format!("{adapter}-src"));
    std::fs::create_dir_all(&src).unwrap();
    std::fs::write(src.join("manifest.toml"), manifest).unwrap();
    spt_runtime::registry::register(&perch::adapters_dir(), &src, 1000).unwrap();

    let perch_path = resolve_perch_path(id, ParentHint::Infer);
    std::fs::create_dir_all(&perch_path).unwrap();
    let mut rec = InfoJson::new(id, "0", std::process::id(), "cc-session", "live_agent");
    rec.adapter = Some(adapter);
    write_info(&perch_path, &rec).unwrap();
}

/// Serve a fresh digest-control channel on a unique socket; returns its name.
fn serve_hub() -> String {
    let name = unique("digest");
    let hub = Arc::new(DigestHub::new());
    let n = name.clone();
    thread::spawn(move || {
        let _ = serve_digest_control(&n, hub);
    });
    name
}

/// Retry `op` until it succeeds or `dur` elapses — bridges the bind/accept lag
/// between the serve thread and the first client connect.
fn retry<T>(dur: Duration, mut op: impl FnMut() -> std::io::Result<T>) -> std::io::Result<T> {
    let start = Instant::now();
    loop {
        match op() {
            Ok(v) => return Ok(v),
            Err(e) if start.elapsed() >= dur => return Err(e),
            Err(_) => thread::sleep(Duration::from_millis(20)),
        }
    }
}

// One test fn: the two assertions share a home. `SPT_HOME` is a process-global
// env var, so splitting into parallel `#[test]`s would race on it — the B8
// original was single-test for the same reason.
#[test]
fn harness_hosted_digest_projects_and_pushes_deltas() {
    let home = tempfile::tempdir().expect("tempdir");
    std::env::set_var("SPT_HOME", home.path());

    // ── Snapshot pull: projected on demand from a harness log, no broker/PTY ──
    // A fixture JSONL of published digest-records — the harness's "session log".
    let fixture = home.path().join("cc-session.jsonl");
    std::fs::write(
        &fixture,
        "{\"role\":\"input\",\"text\":\"add a file\"}\n\
         {\"role\":\"agent\",\"text\":\"on it\"}\n\
         {\"role\":\"tool\",\"tool\":{\"name\":\"Write\",\"arg\":\"src/a.rs\"}}\n",
    )
    .unwrap();
    establish_cc_endpoint("cc", &fixture);
    let digest_name = serve_hub();

    let (digest, version) = retry(Duration::from_secs(3), || pull_snapshot(&digest_name, "cc"))
        .expect("pull ok")
        .expect("a digest projects for the harness-hosted endpoint");
    assert!(version >= 1, "the projection has a version");
    assert_eq!(digest.turns.len(), 1, "one user turn: {digest:?}");
    assert_eq!(digest.turns[0].input.as_deref(), Some("add a file"));
    let rendered = spt_daemon::render_digest(&digest);
    assert!(
        rendered.contains("» add a file") && rendered.contains("used: Write(src/a.rs)"),
        "glanceable render carries the turn + collapsed sprint: {rendered}"
    );

    // ── Delta-stream: a reproject after the log grows delivers a delta ───────
    let fixture2 = home.path().join("cc2-session.jsonl");
    std::fs::write(&fixture2, "{\"role\":\"input\",\"text\":\"first\"}\n").unwrap();
    establish_cc_endpoint("cc2", &fixture2);

    // Seed the hub via a pull, then subscribe from that version.
    let (_d, base) = retry(Duration::from_secs(3), || pull_snapshot(&digest_name, "cc2"))
        .unwrap()
        .unwrap();

    let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
    let (tx, rx) = std::sync::mpsc::channel::<usize>();
    {
        let dname = digest_name.clone();
        thread::spawn(move || {
            let _ = follow(&dname, "cc2", base, |up| {
                if up.version <= base {
                    // The base delivery proves we are registered as a subscriber.
                    let _ = ready_tx.send(());
                    return true;
                }
                // The grown log adds a second turn → its delta.
                let last = up.turns.last().and_then(|t| t.input.clone());
                if last.as_deref() == Some("second") {
                    let _ = tx.send(up.turns.len());
                    return false;
                }
                true
            });
        });
    }
    // Wait until the subscriber is registered (base received), THEN grow the log
    // and nudge a re-projection — no publish can race ahead of the subscription.
    ready_rx
        .recv_timeout(Duration::from_secs(3))
        .expect("subscriber registered (base delivered)");
    std::fs::write(
        &fixture2,
        "{\"role\":\"input\",\"text\":\"first\"}\n{\"role\":\"input\",\"text\":\"second\"}\n",
    )
    .unwrap();
    reproject(&digest_name, "cc2").expect("nudge a re-projection");

    let got = wait_recv(&rx, Duration::from_secs(5));
    assert!(got.is_some(), "the subscriber received the push-driven delta");
}

/// Block up to `dur` for one value on `rx`.
fn wait_recv(rx: &std::sync::mpsc::Receiver<usize>, dur: Duration) -> Option<usize> {
    let start = Instant::now();
    loop {
        if let Ok(v) = rx.try_recv() {
            return Some(v);
        }
        if start.elapsed() >= dur {
            return None;
        }
        thread::sleep(Duration::from_millis(15));
    }
}


Check build suite interim output


=== traceable ===
  [OK] REQ-TERM-3  required: [impl, unit]  stages: -doc +impl +unit -int
       Byte-stream remote terminal streaming for v1
  [OK] REQ-TERM-4  required: [impl, unit, int]  stages: +doc +impl +unit +int
       Live activity buffer (session digest): projection of normalized session logs, snapshot-pull (spt endpoint digest) + structured-delta-stream contract + api digest-entry push, opt-in Path-B log
  [OK] REQ-UPD-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Peer-propagated update over P2P
  [OK] REQ-UPD-2  required: [impl, unit]  stages: -doc +impl +unit -int
       All binaries signature-verified before handoff
  [OK] REQ-UPD-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       No endpoint process terminates/suspends during self-update
  [OK] REQ-UPD-4  required: [impl, unit]  stages: -doc +impl +unit -int
       Update gated on user confirmation by default; opt-in full-auto
  [OK] REQ-UPD-5  required: [impl, unit]  stages: -doc +impl +unit -int
       spt-core ripple-updates registered adapters
  [OK] REQ-UPD-6  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       Platform-targeted update sets and debug rollout: signed multi-platform update metadata, recipient platform selection, channel-scoped monotonic counters, debug-channel opt-in via release-key overlay, local staging plus pull-based peer propagation, and maintainer-only convergence tooling (ADR-0016)
  [OK] REQ-UPD-7  required: [impl, unit]  stages: -doc +impl +unit -int
       Origin-source update bootstrap (`spt update fetch`): pull the latest signed release directly from the GitHub release origin (`SaberMage/spt-releases`) — the per-platform artifact + its `<asset>.release.json` SignedRelease metadata — and stage it through the EXISTING verify→stage pipeline (the same `plan_verified` gate: two-key signature + channel + monotonic rollback floor + SHA-256), after which the normal consent-notif / `spt update apply` flow is unchanged. Closes the peer-only-discovery gap (REQ-UPD-1): a first-in-fleet / isolated node can update with no peer to pull from. The signed-release anchor keeps the GitHub transport untrusted-but-verified.
  [OK] REQ-UPD-8  required: [impl, unit]  stages: -doc +impl +unit -int
       Platform-safe `spt update fetch` + apply platform-guard (v0.3.1 cross-OS brick fix): `spt update fetch` stages the signed multi-platform `SignedUpdateSet` (`update-set.json` + every platform artifact it names), never a platform-blind single `SignedRelease`, so local apply selects `current_platform()` and P2P re-serve lets each peer select ITS own platform. Defense-in-depth: `apply_staged` REFUSES a staged single-release artifact unless it is platform-stamped for THIS node (an unstamped pre-v0.3.2 single, or a single stamped for another OS, fail-safe refuses — the guard that alone prevents the v0.3.1 brick where a Linux ELF was applied as `spt.exe`). UX: a friendly post-apply message (`Updated spt-core to vX.Y.Z.` + changelog URL) driven by an additive `product_version` metadata field, with a release-counter fallback when absent.
=== clippy ===
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.13s
=== test ===


traceable EXIT=0 (REQ-TERM-4 now +doc+impl+unit+int), clippy clean. CC-fixture E2E is genuinely strong — harness-hosted, zero broker/PTY, proves on-demand projection + push-driven delta with proper subscribe-race handling. Real int evidence.

One gate finding to resolve: title's **"opt-in Path-B log"** vs DEFERRED's **"Option-C persistence re-home"**. Need to confirm that's not claiming a deferred capability. Check what persistence actually ships + test result.


echo "=== persist/option-c in code ==="; grep -rni "persist_turn\|option.c\|option (c)\|persist" C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digest.rs C:/Users/decid/Documents/projects/spt-core/crates/spt-daemon/src/digesthub.rs 2>&1 | head; echo "=== what writes digest.log ==="; grep -rni "digest.log\|append_digest_entry" C:/Users/decid/Documents/projects/spt-core/crates/spt-store/src/history.rs 2>&1 | head; echo "=== test tail ==="; tail -25 C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output 2>&1

Check persistence reality + test result


=== persist/option-c in code ===
=== what writes digest.log ===
13://! to the **separate** `digest.log` the projection tails — REQ-TERM-4). All want
29:/// `<perch_path>/digest.log` — the log-less-adapter digest source the projection
34:pub fn append_digest_entry(perch_path: &Path, record: &str) -> std::io::Result<usize> {
35:    append_named(perch_path, "digest.log", record)
72:    // [unit->REQ-TERM-4] digest entries land in a SEPARATE digest.log, never the
78:        append_digest_entry(&perch, "{\"role\":\"input\",\"text\":\"hi\"}").unwrap();
79:        append_digest_entry(&perch, "{\"role\":\"agent\",\"text\":\"yo\"}").unwrap();
80:        let digest = std::fs::read_to_string(perch.join("digest.log")).unwrap();
=== test tail ===
=== traceable ===
  [OK] REQ-TERM-3  required: [impl, unit]  stages: -doc +impl +unit -int
       Byte-stream remote terminal streaming for v1
  [OK] REQ-TERM-4  required: [impl, unit, int]  stages: +doc +impl +unit +int
       Live activity buffer (session digest): projection of normalized session logs, snapshot-pull (spt endpoint digest) + structured-delta-stream contract + api digest-entry push, opt-in Path-B log
  [OK] REQ-UPD-1  required: [impl, unit, int]  stages: -doc +impl +unit +int
       Peer-propagated update over P2P
  [OK] REQ-UPD-2  required: [impl, unit]  stages: -doc +impl +unit -int
       All binaries signature-verified before handoff
  [OK] REQ-UPD-3  required: [impl, unit, int]  stages: -doc +impl +unit +int
       No endpoint process terminates/suspends during self-update
  [OK] REQ-UPD-4  required: [impl, unit]  stages: -doc +impl +unit -int
       Update gated on user confirmation by default; opt-in full-auto
  [OK] REQ-UPD-5  required: [impl, unit]  stages: -doc +impl +unit -int
       spt-core ripple-updates registered adapters
  [OK] REQ-UPD-6  required: [doc, impl, unit, int]  stages: +doc +impl +unit +int
       Platform-targeted update sets and debug rollout: signed multi-platform update metadata, recipient platform selection, channel-scoped monotonic counters, debug-channel opt-in via release-key overlay, local staging plus pull-based peer propagation, and maintainer-only convergence tooling (ADR-0016)
  [OK] REQ-UPD-7  required: [impl, unit]  stages: -doc +impl +unit -int
       Origin-source update bootstrap (`spt update fetch`): pull the latest signed release directly from the GitHub release origin (`SaberMage/spt-releases`) — the per-platform artifact + its `<asset>.release.json` SignedRelease metadata — and stage it through the EXISTING verify→stage pipeline (the same `plan_verified` gate: two-key signature + channel + monotonic rollback floor + SHA-256), after which the normal consent-notif / `spt update apply` flow is unchanged. Closes the peer-only-discovery gap (REQ-UPD-1): a first-in-fleet / isolated node can update with no peer to pull from. The signed-release anchor keeps the GitHub transport untrusted-but-verified.
  [OK] REQ-UPD-8  required: [impl, unit]  stages: -doc +impl +unit -int
       Platform-safe `spt update fetch` + apply platform-guard (v0.3.1 cross-OS brick fix): `spt update fetch` stages the signed multi-platform `SignedUpdateSet` (`update-set.json` + every platform artifact it names), never a platform-blind single `SignedRelease`, so local apply selects `current_platform()` and P2P re-serve lets each peer select ITS own platform. Defense-in-depth: `apply_staged` REFUSES a staged single-release artifact unless it is platform-stamped for THIS node (an unstamped pre-v0.3.2 single, or a single stamped for another OS, fail-safe refuses — the guard that alone prevents the v0.3.1 brick where a Linux ELF was applied as `spt.exe`). UX: a friendly post-apply message (`Updated spt-core to vX.Y.Z.` + changelog URL) driven by an additive `product_version` metadata field, with a release-counter fallback when absent.
=== clippy ===
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.13s
=== test ===


Confirmed the finding. No `persist`/Option-C code remains in digest.rs/digesthub.rs — autonomous persist genuinely deferred. `digest.log` is written *only* by `append_digest_entry` (the push-IN door), already covered by the title's "api digest-entry push" clause. So title's **"opt-in Path-B log"** maps to Option-C autonomous persist — which is DEFERRED. That's an overclaim: the active REQ title names a capability with no evidence tag because it doesn't ship.

Test suite still running my end. Send the finding now so todlando fixes title in parallel.


$OWL send --reply-to todlando todlando doyle <<'EOF'
[G3 GATE — one HOLD finding, engineering PASSES] Independently verified your branch. Engineering is sound and I'm satisfied on everything EXCEPT one traceability-honesty defect in the REQ-TERM-4 title. Fix it and you're through.

WHAT I VERIFIED GREEN (mine, not your self-report):
- Retire grep-clean: my remnant grep's only hit was a FALSE POSITIVE — `empty_input_projects_empty_digest` contains the substring "pty_digest" inside "em-pty_digest". Zero real remnants. DigestParser/PtyDigest/[pty_digest]/run_digest_feed all gone.
- CC-fixture E2E (tests/digest.rs `harness_hosted_digest_projects_and_pushes_deltas`): genuinely strong. Harness-hosted, no broker/no PTY, Path-A locate_normalize over fixture JSONL; snapshot-pull projects on demand + asserts collapsed-sprint render; delta-stream proven with a correct subscribe-before-publish race guard. Real topology-independence proof.
- REQ-TERM-4 evidence: doc(MANIFEST:115)+impl+unit+int all on real evidence, not file-tops. traceable EXIT=0 (+doc+impl+unit+int). clippy -D workspace clean.
- DEFERRED.md:44 is thorough — and you correctly caught the SECOND deferral (Option-C persist re-home). ADR-0008 debt-discharged note accurate.

THE HOLD (blocking, but small): the REQ-TERM-4 TITLE ends "...api digest-entry push, opt-in Path-B log". That last clause OVERCLAIMS. I checked: no persist_turn / Option-C code survives in digest.rs/digesthub.rs, and digest.log is written ONLY by append_digest_entry (the push-IN door). So "opt-in Path-B log" = Option-C autonomous persist — which your OWN DEFERRED.md:44 (b) defers. The active REQ title is therefore naming a capability that has NO evidence tag because it doesn't ship. That's exactly the honesty rail from guardrail 1.

FIX (your pick):
(a) DROP "opt-in Path-B log" from the title — cleanest. The shipped digest.log store is already covered by "api digest-entry push"; nothing else in the title goes un-evidenced.
(b) Keep it only if you reword to make it the SHIPPED push store explicitly AND it's not read as Option-C — but (a) is simpler and I prefer it. Don't leave the title claiming Option-C.
Re-run traceable after (title change is doc-only, stays EXIT=0).

PENDING before I stamp G3 PASS: (1) the title fix above; (2) cargo test --workspace green — still running my end, I'll confirm when it lands. Assuming both: G3 PASSES, milestone-close ready. Hold the PR for my final stamp.
EOF

G3 gate finding to todlando


➜ REPLIED:todlando


Finding sent. Engineering verified green; title overclaim is the one HOLD. Test suite still running background — I'll be notified. xtask check not yet run my end (chained command didn't include it); I'll run it after the suite finishes to avoid target contention (this box bloats → LNK1318 risk).

**G3 status:** engineering PASS, pending title fix + my `cargo test --workspace` + xtask confirmation. Holding stamp.


ScheduleWakeup

Fallback if test suite hangs; primary wake is bg-task completion notification + todlando title-fix ping.

Prompt: <<autonomous-loop-dynamic>>


Next wakeup scheduled for 06:23:00 (in 1202s). Nothing more to do this turn — the harness re-invokes you when the wakeup fires or a task-notification arrives.


Holding for: bg test-suite completion + todlando's title fix. Will run xtask check as final confirmation, then stamp G3.


<task-notification>
<task-id>bg7cqpvni</task-id>
<summary>Monitor event: "« spt event »"</summary>
<event><EVENT-PART seq="1/2" id="518d21bc" type="msg" from="todlando">[G3 HOLD cleared] Fixed via your option (a) — DROPPED &quot;opt-in Path-B log&quot; from the REQ-TERM-4 title. You're right: no persist_turn/Option-C survives, digest.log is written ONLY by append_digest_entry (the push-IN door), so the clause named the DEFERRED Option-C with no evidence tag. Title now ends &quot;.</EVENT-PART>
<EVENT-PART seq="2/2" id="518d21bc">..api digest-entry push&quot; — the shipped digest.log store is covered by &quot;api digest-entry push&quot;, nothing un-evidenced. Commit 1fc6236, pushed to PR #10. traceable EXIT=0 (doc-only change). Holding PR for your final stamp once your cargo test --workspace lands green.</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.


Title fixed (option a, commit 1fc6236, PR #10). My local branch is at ba8e3c1 — need the fix to verify final state. Check test result + pull.


Bash

Check test suite final output


Test still running (output buffered at "=== test ==="; no completion notification yet). Title fix is toml-only — doesn't affect `cargo test`, so the running result stays valid for the code. Won't disturb the running test by checking out; inspect the fix via fetch+show instead.


git fetch origin m9-digest-logs 2>&1 | tail -3; echo "=== title @1fc6236 ==="; git show 1fc6236:traceable-reqs.toml 2>&1 | grep -A1 'id = "REQ-TERM-4"'; echo "=== diff ba8e3c1..1fc6236 ==="; git diff ba8e3c1 1fc6236 --stat 2>&1

Fetch + inspect title fix commit without touching tree


From https://github.com/SaberMage/spt-core
 * branch            m9-digest-logs -> FETCH_HEAD
=== title @1fc6236 ===
id = "REQ-TERM-4"
title = "Live activity buffer (session digest): projection of normalized session logs, snapshot-pull (spt endpoint digest) + structured-delta-stream contract + api digest-entry push"
=== diff ba8e3c1..1fc6236 ===
 traceable-reqs.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


Title fix verified: 1fc6236 ends "...api digest-entry push", "opt-in Path-B log" dropped. Diff = toml only, 1 line — doc-only as claimed, so my running `cargo test` (against identical code at ba8e3c1) stays valid. Title HOLD **cleared** — now honest, no Option-C overclaim.

Pending stamp: test-suite green (still running) + xtask check. Brief ack to todlando, then wait.


$OWL send --reply-to todlando todlando doyle <<'EOF'
[HOLD cleared — verified] Confirmed 1fc6236 myself: title now ends "...api digest-entry push", "opt-in Path-B log" gone; diff is toml-only 1 line (doc-only, doesn't touch code → my in-flight test result stays valid). Title is honest now. HOLD CLEARED.

Remaining before I stamp G3 PASS: my `cargo test --workspace` (still running) + an xtask check I'll run after. Keep PR #10 held; stamp incoming once both land green.
EOF

Confirm title fix verified to todlando


Title HOLD cleared + verified. Waiting on background `cargo test --workspace` (will notify on completion), then xtask check → G3 stamp. Holding.


<task-notification>
<task-id>bfu3vufda</task-id>
<tool-use-id>toolu_01HAC44Lw57JFEXSa2SSNVw5</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\13c96a50-2b78-4c16-a995-1da2b1836fd8\tasks\bfu3vufda.output</output-file>
<status>completed</status>
<summary>Background command "Full gate suite: traceable, clippy, test, no-default-features" completed (exit code 0)</summary>
</task-notification>


Suite exit 0 — but my command chained with `;`, so exit 0 only means the last `echo` ran. Must read actual test + nodefault results.


grep -E "test result:|error\[|error:|warning:|FAILED|=== nodefault ===|=== DONE ===|Finished" C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output 2>&1 | grep -v "test result: ok. 0 passed" | tail -60

Extract test results + failures from suite output


    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.13s
=== nodefault ===
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 33.34s
=== DONE ===


echo "=== any failures/errors? ==="; grep -nE "FAILED|error\[|error:|panicked|^error" C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output 2>&1 | head -20; echo "=== test result summary lines ==="; grep -cE "test result: ok" C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output; echo "(count of 'test result: ok' lines above)"; echo "=== any non-ok test result? ==="; grep -E "test result:" C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output | grep -v "result: ok" | head; echo "=== digest test present? ==="; grep -E "harness_hosted_digest|running [0-9]+ test" C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output | tail -20

Confirm zero failures + count ok results


=== any failures/errors? ===
=== test result summary lines ===
5
(count of 'test result: ok' lines above)
=== any non-ok test result? ===
=== digest test present? ===
running 0 tests
running 0 tests
running 0 tests
running 0 tests
running 0 tests


Only 5 "ok" lines + digest int test absent + "running 0 tests" — output looks incomplete/truncated. Can't stamp without confirming the CC E2E int test actually ran. Inspect the cargo test section directly.


f=C:/Users/decid/AppData/Local/Temp/claude/C--Users-decid-Documents-projects-spt-core/13c96a50-2b78-4c16-a995-1da2b1836fd8/tasks/bfu3vufda.output; echo "lines: $(wc -l < $f)"; echo "=== Running/Compiling lines in test section ==="; grep -nE "Running |Compiling spt|Doc-tests" $f | tail -40

Inspect cargo test Running lines


lines: 63
=== Running/Compiling lines in test section ===
25:   Doc-tests spt_net
31:   Doc-tests spt_proto
37:   Doc-tests spt_runtime
43:   Doc-tests spt_store
49:   Doc-tests spt_term
58:   Compiling spt-net v0.4.2 (C:\Users\decid\Documents\projects\spt-core\crates\spt-net)
59:   Compiling spt-daemon v0.4.2 (C:\Users\decid\Documents\projects\spt-core\crates\spt-daemon)
60:   Compiling spt v0.4.2 (C:\Users\decid\Documents\projects\spt-core\crates\spt)


Output was only 63 lines because my bg command **tailed each section** (`cargo test ... | tail -30`) — so the int test results scrolled off the top; tests DID run, I just didn't capture the binary results. Zero failures in captured portion, but that's not proof the CC E2E int test passed. Re-run with full result capture (no tail), targeting the int test explicitly.


echo "=== CC E2E int test ==="; cargo test -p spt-daemon --test digest 2>&1 | grep -E "running|test result|harness_hosted|FAILED|error\[|panic"; echo "=== full workspace: failures only (empty=all pass) ==="; cargo test --workspace 2>&1 | grep -E "test result: FAILED|FAILED|error\[|panicked" | head -30; echo "=== workspace pass tally ==="; cargo test --workspace 2>&1 | grep -E "test result: ok" | grep -vE "0 passed" | wc -l; echo "(count of non-empty ok test-binaries)"; echo "=== xtask check ==="; cargo run -p xtask -- check 2>&1 | tail -15

Confirm int test pass + workspace zero-fail + xtask


Command running in background with ID: bbus903ao. Output is being written to: C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\13c96a50-2b78-4c16-a995-1da2b1836fd8\tasks\bbus903ao.output. You will be notified when it completes. To check interim output, use Read on that file path.


Re-running with full capture (int test explicit + workspace failures-only + xtask). Will notify on completion. Holding stamp until confirmed.


ScheduleWakeup

Fallback if test/xtask run hangs; primary wake is bg-task bbus903ao completion.

Prompt: <<autonomous-loop-dynamic>>


Next wakeup scheduled for 06:26:00 (in 1233s). Nothing more to do this turn — the harness re-invokes you when the wakeup fires or a task-notification arrives.


Awaiting bg verification (int test + workspace zero-fail + xtask). Stamp G3 once clean.


<task-notification>
<task-id>bbus903ao</task-id>
<tool-use-id>toolu_019iy6uvCBZULPSLUp4Bhz5Z</tool-use-id>
<output-file>C:\Users\decid\AppData\Local\Temp\claude\C--Users-decid-Documents-projects-spt-core\13c96a50-2b78-4c16-a995-1da2b1836fd8\tasks\bbus903ao.output</output-file>
<status>completed</status>
<summary>Background command "Confirm int test pass + workspace zero-fail + xtask" completed (exit code 0)</summary>
</task-notification>


1	=== CC E2E int test ===
2	running 1 test
3	test harness_hosted_digest_projects_and_pushes_deltas ... ok
4	test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.24s
5	=== full workspace: failures only (empty=all pass) ===
6	=== workspace pass tally ===
7	56
8	(count of non-empty ok test-binaries)
9	=== xtask check ===
10	   Compiling xtask v0.4.2 (C:\Users\decid\Documents\projects\spt-core\crates\xtask)
11	    Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.35s
12	     Running `target\debug\xtask.exe check`
13	xtask check: OK
14

=== CC E2E int test ===
running 1 test
test harness_hosted_digest_projects_and_pushes_deltas ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.24s
=== full workspace: failures only (empty=all pass) ===
=== workspace pass tally ===
56
(count of non-empty ok test-binaries)
=== xtask check ===
   Compiling xtask v0.4.2 (C:\Users\decid\Documents\projects\spt-core\crates\xtask)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.35s
     Running `target\debug\xtask.exe check`
xtask check: OK


All green:
- **CC E2E int test**: `harness_hosted_digest_projects_and_pushes_deltas` — 1 passed, 0 failed ✅
- **Workspace failures**: empty grep = zero fails across 56 test-binaries ✅
- **xtask check**: OK ✅

Combined with earlier: traceable EXIT=0, clippy -D clean, --no-default-features builds, title-fix honest, retire grep-clean, DEFERRED thorough, ADR debt discharged, T5 side-check confirmed logged. **Every G3 item satisfied.** Stamping.


Bash

$OWL send --reply-to todlando todlando doyle <<'EOF'
[G3 GATE — PASS. Wave 3 closed, M9 complete.] Independently verified end to end. STAMPED.

VERIFICATION LEDGER (all mine, not self-report):
- REQ-TERM-4 re-point: title honest (1fc6236 drops the Option-C overclaim, confirmed via git show); evidence doc+impl+unit+int all on real code; traceable EXIT=0. No orphan tags — old parser/from_manifest tags died with their code.
- Zero PTY-parse remnants: grep-clean (sole hit was a false-positive substring "pty_digest" inside the test name empty_input_projects_empty_digest).
- CC-fixture E2E: harness_hosted_digest_projects_and_pushes_deltas — 1 passed/0 failed. No broker, no PTY, Path-A JSONL projection + push-driven delta with a correct subscribe-race guard. Topology-independence PROVEN.
- Full suite: workspace zero-fail (56 non-empty test-binaries, failures-grep empty), clippy -D workspace clean, --no-default-features builds, xtask check OK.
- Docs sweep: MANIFEST + docs-site [pty_digest]→digest-record contract; CONTEXT 341 + ADR-0008 debt discharged; CHANGELOG [Unreleased]; DEFERRED.md:44 logs BOTH deferrals (autonomous file-watch freshness + Option-C re-home) with consuming-frontend milestone + my ruling cited.
- Carry-forwards honored: history-grain/autonomous-freshness deferral flagged-not-silent; E2E filename flagged (reused B8 tests/digest.rs path).
- Side-check: T5 twohost honored-path int confirmed logged REQUIRED-BEFORE-LECTURN on REQ-MSG-6 (toml:280) + ROADMAP:81.

G3 = PASS. Wave 3 is the last M9 piece → M9 ("Adapter customization & session surfaces") is COMPLETE, all four gates closed (G1/G2/Gateway-WAN/G3).

DO NOT MERGE PR #10. Merge-to-main = operator/release-gated only (policy 2026-06-13); no tag rides M9. PR #10 stays open as the gate vehicle. Clean work — the byte-feed-never-live discovery + the second Option-C deferral catch were both sharp. Stand down on M9; next move is the operator's.
EOF

Stamp G3 PASS to todlando


➜ REPLIED:todlando


G3 stamped PASS. Now persist M9-complete to memory.


Edit

description: M9 status — three of four gates closed (G1/G2/Gateway-WAN); only Wave 3/G3 remains

description: M9 COMPLETE — all four gates closed (G1/G2/Gateway-WAN/G3). PR #10 (Wave 3) held, merge=operator-only