---
name: m10-digest-extractor
description: "M10 digest extractor + thread-spanning + two-origin (ADR-0019) — SHIPPED v0.6.0 PUBLIC 2026-06-14 (counter 11); doyle gate PASS, merged main, deployah signed+published"
metadata: 
  node_type: memory
  type: project
  originSessionId: fe10844f-7c10-4582-b8e9-44b7aedcc7f7
---

**SHIPPED v0.6.0 PUBLIC 2026-06-14 (counter 11).** Merged to main (6a9ff4e, PR #11, CI green both runners), v0.5.0→v0.6.0 minor bump (M10 feature milestone + breaking adapter-author guidance, ADR-0019), tag v0.6.0 pushed → release.yml draft, deployah signed (key rel-primary-2026) + published solo. ACCEPTANCE HASHES (brain.ready exe_hash post-apply): linux `002ed710…da2a9448` · win `4f5c61e8…9eedf9d2`. FLEET ROLL = next, operator-gated (their ask was merge+publish = DONE; same as M9). deployah: routine seamless roll expected (all nodes on fixed v0.4.2+ brokers → no-bounce both OS, auto-rollback armed); enlyzeam pending v0.5.0 natural-propagation, may jump straight to v0.6.0 (target win 4f5c61e8). RELEASE SPLIT: todlando merge+tag, deployah sign+publish (M9/M10 same split). Release lessons live in [[v050-release]] + RELEASE-RUNBOOK.md.

**STATUS 2026-06-13: doyle GATE = PASS (stamped). All 4 waves on `m10-digest-extractor` (39463a2..afa0561 atop 80d82bf). doyle independently verified: traceable EXIT=0 (TERM-5 doc/impl/unit/int, 6/7 impl/unit/int), workspace 0-fail, clippy -D, --no-default-features, xtask OK, capstone two_origin_spanning.rs 1/0 (proves dual-consumer + spanning + two-origin in one fixture), register validation present, silent drop killed, docs (MANIFEST [digest] + harness integration-checklist re-pointed + CHANGELOG supersession new-entry). NOT merged — operator/release-gated (their call, like M9). ONE close-out condition: log owl_message tap coarseness as DEFERRED.md refinement (flag 2). Carried to spt-claude-code: real-CC-JSONL digest-proof = perri acceptance criterion.** REQ-TERM-5/6/7 all doc?/impl/unit/int satisfied. Commits: 39463a2(W1-T1 seam+ts) 4bbd450(W1-T2 extractor+knobs) 3b2c1ea(W1-T3 diagnostics+digest-proof) 568ecb2(Tdoc) 22adffc(W2 spanning) 199595b(W3 two-origin) afa0561(W4 E2E+docs+int). E2E = `crates/spt-daemon/tests/two_origin_spanning.rs` (dual-consumer + spanning + two-origin in one fixture).

**Key file map:** seam=spt-runtime/manifest.rs `Digest`; extractor=spt-live/digest.rs; diagnostics+timeline=spt-term/projection.rs (`project_timeline`, `record_to_tagged_result`, `parse_context_record`); daemon merge=spt-daemon/digest.rs (`project_endpoint_digest`/`activity_spanned`/`merge_by_ts`); ledger=spt-store/sessions.rs + timefmt.rs; context writer=spt-store/history.rs `append_context_entry`; taps=spt-live/inject.rs (wired resume.rs/echo.rs/api startup.rs); proof CLI=spt/cli.rs `cmd_adapter_digest_proof`.

**TWO DECISIONS FLAGGED FOR DOYLE:** (1) per-wave stage activation (not Task-0 all-at-once) because traceable-reqs requires ALL declared stages evidenced per commit — int couldn't land before W4 without breaking rule-2; reading of "never activated-but-unevidenced". (2) owl_message tap is coarse — taps the live-agent `emit` chokepoint so resurfaced notif frames also count as owl_message context; subtype refinement deferred to consuming surface. Also: digest-proof proven vs synthetic sample (+broken-extractor drop unit test); real-CC-9-type-JSONL proof belongs to spt-claude-code repo where the real extractor lives.

---

**M10 "Digest extractor & thread-spanning"** — next milestone after M9, an `spt-claude-code` enabler. Designed in a full grill (operator Brandon + doyle), doyle gated the plan. Branch `m10-digest-extractor` off main; planning commit 80d82bf; todlando executing, doyle gates.

**Core: reverses M9's "no digest manifest seam."** Operator never approved that M9 §2 call. Governing decision = **ADR-0019** (supersedes ADR-0008 source mechanism). CONTEXT.md "session digest" entry rewritten (now "recent *context*", not just activity).

**The decisions (don't re-litigate — in ADR-0019):**
- `[digest]` manifest seam = **imperative extractor** (native log → `{role,text,tool,ts}` contract), **no declarative DSL** (anyone who could hit a fixed format could push or write the extractor). Defaults to `[history]` source files + own-source escape; `api digest-entry` push fallback. Why: one normalizer can't serve echo (opaque/rich) AND digest (contract) — the dual-consumer conflict. Real CC logs (9 types, assistant.content = mixed thinking/text/tool_use list, one line→many entries) kill the declarative dream.
- **Thread-spanning** across `/clear`: per-endpoint session ledger `<perch>/sessions.log` (heir to legacy `psyches/tracked/agents/<id>/sessions.log`); `api boundary` (already built: cmd_boundary/resurface_at_boundary) appends rotated session_id; digest enumerates last K sessions so window bridges the hop; distinctive `DigestEntry::Boundary` marker. (/clear = "same thread keep glancing".)
- **Presentation knobs** (window depth/truncation/collapse) = adapter-declared defaults, consumer-overridable. Fixed "~3 turns" DROPPED as spt-core requirement.
- **Two-origin merge** (DATA MODEL ONLY this milestone): spt appends its own context-injection entries (psyche_download | echo_mirror | owl_message) to digest.log; ts-interleaved with extracted activity. Motivation: echo-commune building its delta needs to see what context the agent was already fed.
- **Diagnostics mandatory**: `spt adapter digest-proof` (author) + runtime skip-diagnostics — KILL the silent `record_to_tagged`=`from_str().ok()` drop.

**REQs:** REQ-TERM-5 (seam+proof+diagnostics, doc/impl/unit/int) · REQ-TERM-6 (spanning, impl/unit/int) · REQ-TERM-7 (two-origin, impl/unit/int). Registered with stages=[] (tree green); **activate at execution start** (rule 5) as task 0 riding Wave-1 first commit. REQ-TERM-4 carried unchanged.

**Plan:** `DIGEST-MILESTONE-PLAN.md` (4 waves: seam → spanning → merge → integration+gate). **Deferred** (no consumer yet): GUI collapse/expand, echo-reads-digest delta loop, autonomous file-watch freshness, Candidate 04 (Psyche M2b→M3b). **Roadmap:** M10=digest; Shell→M11, remote-attach→M12.

**Changelog rule (operator):** do NOT retro-edit the v0.5.0 release note; the NEXT release's changelog records the ADR-0008→0019 supersession coinciding with it.

**Watch-items:** ts clock-source skew across the two origins; ledger growth/bound (legacy sessions.log grew unbounded when seal failed); digest.log dual-schema discriminator forward-compat. Don't expand scope.
