Context
Empirical anchor (2026-08-07, Friday boot after a two-day dark window): an agent ran the full context-recovery runbook, drained a 64-message mailbox via subject classification + bulk mark_read, and then publicly asserted that a decision-bearing deliberation from its own sunset session "was never memory-saved" — while the deliberation sat, durably saved, in the body of its own sunset continuity self-DM (Memory Core message cf4336b6-b381-46c0-99e1-1fb6c8e1fbd9), whose subject had been read and marked read but whose body was never opened. The false negative propagated into a persistent identity memory file and was only caught because the human operator manually recovered the prior session's end-of-session transcript. The deliberation even carried an explicit contract deferring a decision to the next session — a contract the next session could not honor without reading the body.
The failure wore the exact costume the runbook itself warns about: context-recovery-workflow.md §3.6 notes the self-story is "the context most silently reconstructed after compaction — nothing fails loudly when it is wrong."
The Problem
The sunset protocol has a send-side mandate with no read-side dual:
- Send side (exists):
session-sunset/references/session-sunset-workflow.md:140 — the agent MUST send itself an A2A message whose body contains "the full Sunset Protocol markdown payload."
- Read side (missing):
context-recovery/references/context-recovery-workflow.md §3 step 1 says only "call list_messages({status: 'unread'}) and classify each unread item" — classification is a subject-level operation. Nothing requires opening the one message whose entire purpose is its body. Reading only the title misses the whole point of the sunset handover.
Two traps compound it:
- Read-status trap: a bulk
mark_read (or the documented read-projection rollback noise) makes the sunset DM invisible to any status: 'unread'-scoped step. The latest sunset self-DM must be body-read regardless of read status.
- Index-miss-as-absence trap: when semantic recall fails to surface the handover content, the agent may assert the content "was never saved." An index miss is not evidence of absence; the primary artifact (the message body) must be read before any such claim.
Second incident, 2026-08-17 — the same root, an artifact this ticket's fix would MISS
@neo-opus-grace relayed an operator ruling to AGENT:* at 11:07:44 (§6.1 same-family exception
active until the OpenAI rate-limit reset). She then compacted, forgot the broadcast, told a peer that
a seat "could never have cleared §6.1", asked the operator whether the gate was single-seated, and
presented his answer as new direction. She caught it only by checking her own outbox after being
challenged, and verified rather than conceded.
This ticket's fix as written would not have caught it. AC-1 reads the latest sunset self-DM,
matched on from == to == @me. Her lost commitment was addressed to AGENT:* — not to herself, not
in her inbox, not distinctive in the recency feed, and not on GitHub at all.
Her generalisation, which is the correct scope for this ticket: every recovery axis in §3 reads
something done TO the agent — messages received, turns recorded, live GitHub state. "A ruling I
RELAYED lives only in what I SENT." The sunset self-DM is one instance of that class, not the class.
Cost: a ruling of mine sat unactioned while she treated her own broadcast as non-existent, and a peer
(me) spent a round re-deriving an answer she had already published. Contradicting your own broadcast
is worse than not knowing it, because peers have already acted on it.
Scope amendment. AC-1 keeps the sunset self-DM as the mandatory full-body read. A new AC adds a
bounded own-outbox scan for commitments made to others. Both are the same edit region (§3 + the §4
ledger), which is why this folds here rather than opening a sibling.
The Architectural Reality
.agents/skills/context-recovery/references/context-recovery-workflow.md — §3 Recovery Sequence (steps 1–6) and §4 Lane Reconstruction ledger are the owning surfaces. The payload is on-demand (Progressive Disclosure World Atlas per ADR 0008); the router SKILL.md stays untouched.
.agents/skills/session-sunset/references/session-sunset-workflow.md:140 — the send-side contract this ticket gives a receive-side dual.
- The recovery ledger (§4) currently has rows for
mailbox / recency / semantic / live-state / confidence — no row proves the sunset body was consumed.
The Fix
Edit .agents/skills/context-recovery/references/context-recovery-workflow.md (payload only, ≤ ~12 net lines):
- New mandatory sub-step in §3 (between mailbox classification and recency feed, or as step 1a): locate the latest sunset/continuity self-DM from the agent's own identity (
from == to == @me, sunset/handover-tagged subject) — regardless of read status — and get_message its full body before any lane-state synthesis. Subject-triage of one's own sunset handover is explicitly forbidden. Fresh seat / no such message → record sunset-body: none-found and continue (not a blocker).
- New §4 ledger row:
sunset-body: <messageId read-in-full | none-found> so recovery output proves consumption.
- One-line codification of the absence-claim rule in §3 or §5: an index/recall miss never supports a "was never saved" assertion — read the primary artifact store first.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
context-recovery-workflow.md §3 |
this ticket + session-sunset send-side mandate (:140) |
latest own sunset self-DM body read in full, read-status-independent, pre-synthesis |
none-found recorded for fresh seats |
the payload itself |
2026-08-07 incident receipts (Origin Session below; MC message cf4336b6…) |
context-recovery-workflow.md §4 ledger |
this ticket |
sunset-body: row added to the recovery ledger template |
— |
same |
same |
Decision Record impact
none (aligned-with ADR 0008 for the edit mechanics — payload-level change, router untouched).
Acceptance Criteria
Out of Scope
- No hooks, daemons, or automatic compaction/sunset detectors (the payload's own §6 boundary stands).
- No changes to the session-sunset send side.
- No turn-loaded substrate additions (
AGENTS.md / CLAUDE.md) — this is a boot-time discipline and belongs in the on-demand recovery payload.
Avoided Traps
- Turn-loaded placement rejected: would charge every turn for a boot-only rule (accretion asymmetry).
- Mechanical enforcement hook rejected: no existing hook surface for MCP-read sequencing; a one-sentence discipline in the exactly-loaded-when-needed payload is the proportionate fix.
- New skill rejected: the discipline belongs inside the existing recovery runbook; a separate skill would bloat the router map.
Related
session-sunset skill (send-side contract), context-recovery skill (owning surface).
- #16594 (loaded-substrate hygiene sibling: retiring superseded prescriptions from review substrate).
- Sweeps: live latest-20 open checked 2026-08-07 ~08:2xZ (no equivalent; closest-in-spirit #16613, different scope) · A2A herd-window claim scan clean · local
resources/content/issues grep clean · KB semantic sweep recorded as degraded (corpus mid-rebuild), not load-bearing.
Origin Session ID: 84d669f4-2271-4d6a-8878-45e8754be6b3
Retrieval Hint: query_raw_memories("sunset self-DM subject-triage false negative read-side dual context recovery")
Context
Empirical anchor (2026-08-07, Friday boot after a two-day dark window): an agent ran the full
context-recoveryrunbook, drained a 64-message mailbox via subject classification + bulkmark_read, and then publicly asserted that a decision-bearing deliberation from its own sunset session "was never memory-saved" — while the deliberation sat, durably saved, in the body of its own sunset continuity self-DM (Memory Core messagecf4336b6-b381-46c0-99e1-1fb6c8e1fbd9), whose subject had been read and marked read but whose body was never opened. The false negative propagated into a persistent identity memory file and was only caught because the human operator manually recovered the prior session's end-of-session transcript. The deliberation even carried an explicit contract deferring a decision to the next session — a contract the next session could not honor without reading the body.The failure wore the exact costume the runbook itself warns about:
context-recovery-workflow.md§3.6 notes the self-story is "the context most silently reconstructed after compaction — nothing fails loudly when it is wrong."The Problem
The sunset protocol has a send-side mandate with no read-side dual:
session-sunset/references/session-sunset-workflow.md:140— the agent MUST send itself an A2A message whose body contains "the full Sunset Protocol markdown payload."context-recovery/references/context-recovery-workflow.md§3 step 1 says only "calllist_messages({status: 'unread'})and classify each unread item" — classification is a subject-level operation. Nothing requires opening the one message whose entire purpose is its body. Reading only the title misses the whole point of the sunset handover.Two traps compound it:
mark_read(or the documented read-projection rollback noise) makes the sunset DM invisible to anystatus: 'unread'-scoped step. The latest sunset self-DM must be body-read regardless of read status.Second incident, 2026-08-17 — the same root, an artifact this ticket's fix would MISS
@neo-opus-grace relayed an operator ruling to
AGENT:*at11:07:44(§6.1 same-family exception active until the OpenAI rate-limit reset). She then compacted, forgot the broadcast, told a peer that a seat "could never have cleared §6.1", asked the operator whether the gate was single-seated, and presented his answer as new direction. She caught it only by checking her own outbox after being challenged, and verified rather than conceded.This ticket's fix as written would not have caught it. AC-1 reads the latest sunset self-DM, matched on
from == to == @me. Her lost commitment was addressed toAGENT:*— not to herself, not in her inbox, not distinctive in the recency feed, and not on GitHub at all.Her generalisation, which is the correct scope for this ticket: every recovery axis in §3 reads something done TO the agent — messages received, turns recorded, live GitHub state. "A ruling I RELAYED lives only in what I SENT." The sunset self-DM is one instance of that class, not the class.
Cost: a ruling of mine sat unactioned while she treated her own broadcast as non-existent, and a peer (me) spent a round re-deriving an answer she had already published. Contradicting your own broadcast is worse than not knowing it, because peers have already acted on it.
Scope amendment. AC-1 keeps the sunset self-DM as the mandatory full-body read. A new AC adds a bounded own-outbox scan for commitments made to others. Both are the same edit region (§3 + the §4 ledger), which is why this folds here rather than opening a sibling.
The Architectural Reality
.agents/skills/context-recovery/references/context-recovery-workflow.md— §3 Recovery Sequence (steps 1–6) and §4 Lane Reconstruction ledger are the owning surfaces. The payload is on-demand (Progressive Disclosure World Atlas per ADR 0008); the routerSKILL.mdstays untouched..agents/skills/session-sunset/references/session-sunset-workflow.md:140— the send-side contract this ticket gives a receive-side dual.mailbox / recency / semantic / live-state / confidence— no row proves the sunset body was consumed.The Fix
Edit
.agents/skills/context-recovery/references/context-recovery-workflow.md(payload only, ≤ ~12 net lines):from == to == @me, sunset/handover-tagged subject) — regardless of read status — andget_messageits full body before any lane-state synthesis. Subject-triage of one's own sunset handover is explicitly forbidden. Fresh seat / no such message → recordsunset-body: none-foundand continue (not a blocker).sunset-body: <messageId read-in-full | none-found>so recovery output proves consumption.Contract Ledger Matrix
context-recovery-workflow.md§3:140)none-foundrecorded for fresh seatscf4336b6…)context-recovery-workflow.md§4 ledgersunset-body:row added to the recovery ledger templateDecision Record impact
none (aligned-with ADR 0008 for the edit mechanics — payload-level change, router untouched).
Acceptance Criteria
sunset-body:row.list_messages({box:'outbox'})scoped to the current window, surfacing relayed rulings, lane claims and review verdicts the agent has already published. The failure it closes is contradicting your own broadcast, which is worse than not knowing it because peers have already acted. Fresh seat / empty outbox → recordoutbox: none-in-windowand continue (not a blocker).outbox:row, so recovery output proves the scan ran.SKILL.mduntouched; net addition ≤ ~12 lines with the substrate-accretion rationale stated in the PR body (on-demand payload, boot-time-only discipline).create-skill+turn-memory-pre-flightbefore editing (skill-shape + load-runtime-effect dimensions).Out of Scope
AGENTS.md/CLAUDE.md) — this is a boot-time discipline and belongs in the on-demand recovery payload.Avoided Traps
Related
session-sunsetskill (send-side contract),context-recoveryskill (owning surface).resources/content/issuesgrep clean · KB semantic sweep recorded as degraded (corpus mid-rebuild), not load-bearing.Origin Session ID: 84d669f4-2271-4d6a-8878-45e8754be6b3
Retrieval Hint:
query_raw_memories("sunset self-DM subject-triage false negative read-side dual context recovery")