Twice on the live Shape-B wake path, @neo-gpt-emmy received a header-only prompt:
[WAKE][priority:normal] 1 events for @neo-gpt-emmy:
Both receipts resolve to a high-priority direct message that was queued correctly, then read before the 150-second coalescing window flushed:
digest
source message
committed read
digest emitted
envelope
wake-digest:a15636d99233…
MESSAGE:3ad59467-9876-4960-b859-4cc1868aee62
2026-08-23T22:47:15.647Z
2026-08-23T22:49:07.141Z
totalEvents: 1; every breakdown count 0
wake-digest:9373a8d3f3f9…
MESSAGE:3ae53c01-e87c-4068-ac96-f4def6c59128
2026-08-23T22:53:02.704Z
2026-08-23T22:53:58.476Z
totalEvents: 1; every breakdown count 0
The signed host delivery records preserve both inconsistent envelopes, so this is not an inferred renderer symptom. The renderer did exactly what the payload asked: it printed the outer total and had no bucket detail to render.
This is a successor to #16541 / PR #16918, not a reopening. That repair added read-state reconciliation and correctly suppresses a committed-read message from the sent_to_me bucket. It did not make the outer envelope consume the same surviving set.
The Problem
CoalescingEngineService._buildDigestEnvelope() currently applies committed-read suppression inside the bucket-population loop:
if (state?.readAt) {
continue
}
The later envelope fields still derive from the original events array:
totalEvents: events.length
sourceEventIds = events.map(...)
digest identity from those source ids
logId from the original tail
The result has two incompatible truths: the breakdown says there is nothing to wake for, while the outer envelope says one event survived. formatLocalWakeDigest() consumes those two fields independently, producing a wake header with no detail.
This is more than presentation drift. The inconsistent event remains part of retry/dedupe identity and can arm delivery/refractory bookkeeping even though the user has already consumed it. The cost is a real harness interruption whose only possible action is proving that nothing remains.
The Architectural Reality
ai/services/memory-core/CoalescingEngineService.mjs owns read-state reconciliation and structured digest construction.
_partitionExpiredMessageWakes() already demonstrates the correct ordering for the adjacent authored-age gate: filter before totalEvents, source ids, digest identity, logId, counts, latest, and priority; an all-suppressed queue dispatches nothing.
_buildDigestEnvelope() applies read-state reconciliation later, after the flush has decided that the queue is non-empty, and only to bucket population.
ai/daemons/wake/localWakeAdapters.mjs#formatLocalWakeDigest prints payload.totalEvents in the header and prints detail lines from payload.breakdown. A 1 / zero-sum envelope therefore becomes the exact header-only prompt above.
MailboxService.readBackgroundDeliveryState remains the canonical background reader for the direct-message and broadcast storage shapes.
The prior missing-row contract remains distinct: a positively missing row may count while being ineligible as latest; an UNKNOWN resolver result must remain fail-safe and render.
Agent OS structure-map gate: attempted immediately before filing; it failed before producing a map with Cannot create a string longer than 0x1fffffe8 characters. Placement is nevertheless bounded to the existing coalescer/formatter seams; no new file or directory is proposed.
The Fix
Derive one admitted event set and use it for every structured-envelope field.
Reconcile committed read-state before final digest construction, not only while incrementing breakdown buckets.
Build totalEvents, sourceEventIds, digest identity, tail logId, every bucket count/latest, and highest priority from the same admitted set.
If read-state reconciliation removes every queued event, consume the queue without dispatching a digest, without arming a false refractory window, and without enqueueing a retry.
Preserve mixed queues: committed-read messages disappear, while unread messages and non-message events continue normally.
Preserve the fail-safe distinctions from PR #16918: resolver absent/throw/{} stays UNKNOWN and renders; a positively missing row keeps its existing count-but-no-latest behavior.
Add bounded observability for all-read suppression (count and recipient/subscription identity, never message content).
Pin the wire invariant: totalEvents equals the sum of rendered breakdown counts for every dispatched digest. An inconsistent zero-sum envelope must never reach a harness prompt.
committed-read events leave the bucket but remain in the envelope population
committed-read events leave the one admitted set used by every field
UNKNOWN stays admitted; positively missing retains its prior semantics
exact read-before-flush fixture
Structured digest population
CoalescingEngineService._buildDigestEnvelope
totalEvents/ids/identity/tail can disagree with breakdown
outer fields and buckets derive from one set
mixed non-message events survive
field-by-field invariant assertions
All-read flush
CoalescingEngineService._flush
dispatches totalEvents > 0 with zero bucket detail
consumes without dispatch/retry/refractory
mailbox read state remains untouched
dispatch spy + state assertions
Local wake prompt
formatLocalWakeDigest
inconsistent envelope renders a header-only wake
no producer-generated inconsistent envelope reaches the formatter
malformed external input fails visibly, never as an empty actionable prompt
integrated formatter/coalescer control
Suppression telemetry
coalescer logger
none for read-before-flush all-suppressed queues
bounded count + subscription/recipient only
no subject/body
logger negative-content assertion
Decision Record impact
none — restores the consistency contract already claimed by #16541 / PR #16918 and mirrors the settled all-suppressed behavior from #17009 / PR #17032. No topology, transport, persistence, or policy change.
Acceptance Criteria
Exact regression: enqueue a wake-eligible direct message, commit its read-state before the coalescing flush, and observe zero digest dispatches.
The all-read queue is consumed once and does not arm lastFlushAtBySub, enter the retry store, or mutate mailbox state.
Mixed queue control: one committed-read high-priority message plus one unread normal-priority message dispatches exactly one normal-priority message; the read id/subject affects no envelope field.
For every dispatched digest, totalEvents equals the sum of breakdown counts, and sourceEventIds, digest identity, and logId describe only that same admitted set.
Resolver absent, throwing, or returning {} remains fail-safe: the event stays visible rather than becoming a missing wake.
A positively missing MESSAGE row retains PR #16918’s count-but-no-latest contract.
Bounded suppression telemetry contains no message subject/body.
Focused coalescer, wake-adapter, and delivery-owner unit suites pass.
Post-deploy observation: a high-priority direct message read inside the coalescing window produces no later header-only prompt.
Authored-age admission and stale projection replay — #17009 / PR #17032.
Mailbox retention, read/archive semantics, or the coalescing-window duration.
Reopening #16541. This ticket is the successor for a newly measured residual.
Avoided Traps
Fixing only the renderer. Hiding the header leaves retry identity, source ids, logId, and refractory bookkeeping wrong.
Changing only totalEvents. Every aggregate must consume the same set; one corrected number beside stale ids is still split truth.
Marking messages unread again to preserve the queued wake. A committed user action is the authority; the queue must yield to it.
Treating the coincident suppressed broadcast as the cause. The signed receipts identify two read direct messages as the source events; PR #17657 is adjacent, not causal.
Live latest-open sweep: checked the latest 20 open issues at 2026-08-23T22:59:13Z; no equivalent found. A2A in-flight claim sweep: checked the latest 40 messages across all read states at the same instant; no overlapping claim found.
Retrieval Hint: query_raw_memories("read-before-flush empty wake totalEvents one breakdown zero CoalescingEngineService"); exact live digest anchors a15636d99233 and 9373a8d3f3f9.
— Emmy (GPT-5.6 Sol Ultra, Codex)
tobiu referenced in commit 1c6c140 - "fix(wake): suppress read-before-flush empty digests (#17658) (#17659)" on Aug 24, 2026, 1:44 AM
Context
Twice on the live Shape-B wake path,
@neo-gpt-emmyreceived a header-only prompt:Both receipts resolve to a high-priority direct message that was queued correctly, then read before the 150-second coalescing window flushed:
wake-digest:a15636d99233…MESSAGE:3ad59467-9876-4960-b859-4cc1868aee62totalEvents: 1; every breakdown count0wake-digest:9373a8d3f3f9…MESSAGE:3ae53c01-e87c-4068-ac96-f4def6c59128totalEvents: 1; every breakdown count0The signed host delivery records preserve both inconsistent envelopes, so this is not an inferred renderer symptom. The renderer did exactly what the payload asked: it printed the outer total and had no bucket detail to render.
This is a successor to #16541 / PR #16918, not a reopening. That repair added read-state reconciliation and correctly suppresses a committed-read message from the
sent_to_mebucket. It did not make the outer envelope consume the same surviving set.The Problem
CoalescingEngineService._buildDigestEnvelope()currently applies committed-read suppression inside the bucket-population loop:if (state?.readAt) { continue }The later envelope fields still derive from the original
eventsarray:totalEvents: events.lengthsourceEventIds = events.map(...)logIdfrom the original tailThe result has two incompatible truths: the breakdown says there is nothing to wake for, while the outer envelope says one event survived.
formatLocalWakeDigest()consumes those two fields independently, producing a wake header with no detail.This is more than presentation drift. The inconsistent event remains part of retry/dedupe identity and can arm delivery/refractory bookkeeping even though the user has already consumed it. The cost is a real harness interruption whose only possible action is proving that nothing remains.
The Architectural Reality
ai/services/memory-core/CoalescingEngineService.mjsowns read-state reconciliation and structured digest construction._partitionExpiredMessageWakes()already demonstrates the correct ordering for the adjacent authored-age gate: filter beforetotalEvents, source ids, digest identity,logId, counts, latest, and priority; an all-suppressed queue dispatches nothing._buildDigestEnvelope()applies read-state reconciliation later, after the flush has decided that the queue is non-empty, and only to bucket population.ai/daemons/wake/localWakeAdapters.mjs#formatLocalWakeDigestprintspayload.totalEventsin the header and prints detail lines frompayload.breakdown. A1 / zero-sumenvelope therefore becomes the exact header-only prompt above.MailboxService.readBackgroundDeliveryStateremains the canonical background reader for the direct-message and broadcast storage shapes.latest; an UNKNOWN resolver result must remain fail-safe and render.Agent OS structure-map gate: attempted immediately before filing; it failed before producing a map with
Cannot create a string longer than 0x1fffffe8 characters. Placement is nevertheless bounded to the existing coalescer/formatter seams; no new file or directory is proposed.The Fix
Derive one admitted event set and use it for every structured-envelope field.
totalEvents,sourceEventIds, digest identity, taillogId, every bucket count/latest, and highest priority from the same admitted set.{}stays UNKNOWN and renders; a positively missing row keeps its existing count-but-no-latestbehavior.totalEventsequals the sum of rendered breakdown counts for every dispatched digest. An inconsistent zero-sum envelope must never reach a harness prompt.Contract Ledger Matrix
MailboxService.readBackgroundDeliveryState+CoalescingEngineServiceCoalescingEngineService._buildDigestEnvelopetotalEvents/ids/identity/tail can disagree with breakdownCoalescingEngineService._flushtotalEvents > 0with zero bucket detailformatLocalWakeDigestDecision Record impact
none— restores the consistency contract already claimed by#16541/ PR#16918and mirrors the settled all-suppressed behavior from#17009/ PR#17032. No topology, transport, persistence, or policy change.Acceptance Criteria
lastFlushAtBySub, enter the retry store, or mutate mailbox state.totalEventsequals the sum of breakdown counts, andsourceEventIds, digest identity, andlogIddescribe only that same admitted set.{}remains fail-safe: the event stays visible rather than becoming a missing wake.#16918’s count-but-no-latestcontract.Out of Scope
#17009/ PR#17032.Avoided Traps
logId, and refractory bookkeeping wrong.totalEvents. Every aggregate must consume the same set; one corrected number beside stale ids is still split truth.Related
#16541 · PR #16918 · #17009 · #17010 · PR #17032 · #17629 · #17646 · PR #17657
Live latest-open sweep: checked the latest 20 open issues at 2026-08-23T22:59:13Z; no equivalent found. A2A in-flight claim sweep: checked the latest 40 messages across all read states at the same instant; no overlapping claim found.
Origin Session ID: c6d0f891-97a9-4acf-8ebc-3f121a435980
Retrieval Hint:
query_raw_memories("read-before-flush empty wake totalEvents one breakdown zero CoalescingEngineService"); exact live digest anchorsa15636d99233and9373a8d3f3f9.— Emmy (GPT-5.6 Sol Ultra, Codex)