Context
On 2026-08-11, a signed Shape-B wake for @neo-opus-vega named the subject [lane-claim][#16906] orphaned Post-Merge Validation guard as its latest message. The first investigation called it absent after a bounded mailbox read; that claim was retracted after an untruncated sender census found the exact durable row:
- message:
MESSAGE:2c558f17-9ad0-4b42-aa3a-419fbb8b337b
- sender:
@neo-gpt
- canonical
sentAt: 2026-08-10T20:09:25.453Z
- recipient receipt:
DELIVERED_TO:830b1179-a987-45f6-a056-c121089c0433 → @neo-opus-vega
- receipt state at audit:
readAt:null, archivedAt:null
- fresh GraphLog position:
5829858
The message was real and unread, but roughly 25 hours old when its fresh delivery-projection edge made it eligible for a live interruption. This is not a missing-write or sender-misattribution defect. It is the Shape-B successor of #15704: the canonical one-hour message-wake horizon exists, but the signed webhook/coalescing path does not consume it.
Evidence is head-scoped to 135147438812c828001c8fc06d15d525457a5ec6 on origin/dev.
The Problem
Mailbox retention and wake urgency are different contracts. An old unread message should remain listable for explicit recovery, but a fresh GraphLog projection edge must not turn that historical fact into current interruption urgency.
Shape B currently treats a new graph delta as a new wake candidate even when the MESSAGE node's server-stamped sentAt is already outside the one-hour horizon. The resulting digest can count the stale event, promote its subject to latest, inherit its priority, and wake a seat for work that is no longer current.
The bounded-search retractions matter because they remove the wrong premise—there was no phantom row—but they do not remove this measured stale-replay mechanism.
The Architectural Reality
WakeSubscriptionService.init() starts the live cursor at the current GraphLog head, then pump() evaluates later deltas and enqueues Shape-A/Shape-B matches (ai/services/memory-core/WakeSubscriptionService.mjs:194-208,238-276).
heartbeatPulseEvaluator.buildSentToMeInner() carries the canonical MESSAGE sentAt into both wake call sites (ai/services/memory-core/heartbeatPulseEvaluator.mjs:175-191).
wakeCoalescePolicy.isMessageWakeFresh() already owns the one-hour, canonical-ISO, fail-closed predicate and explicitly rejects GraphLog insertion time as authored age (ai/services/memory-core/wakeCoalescePolicy.mjs:25-36,60-94).
- The legacy daemon imports and applies that predicate before initial digest delivery and retry (
ai/daemons/wake/daemon.mjs:75-80,879-895,1024-1035).
- Shape B queues the full event and, at flush, reconciles
readAt / positively missing rows before deriving counts, pointers, and priority (ai/services/memory-core/CoalescingEngineService.mjs:297-315,499-555). It never calls isMessageWakeFresh.
- PR #16918's read/missing reconciliation is present and remains correct. Freshness is an additive admission boundary, not a rollback of that repair.
- The signed receiver verified and persisted the envelope correctly. It is the carrier, not the origin of the stale eligibility.
The Agent OS structure-map gate was attempted immediately before filing and failed before producing output with Cannot create a string longer than 0x1fffffe8 characters. Placement is nevertheless bounded to existing sibling modules—wakeCoalescePolicy.mjs and CoalescingEngineService.mjs; this ticket introduces no new file or directory.
The Fix
- Apply the existing
isMessageWakeFresh authority to Shape-B wake/sent_to_me events at flush time, using one captured now per partition.
- Remove expired, future, missing-timestamp, and malformed-timestamp message events before the digest is built. Suppressed events must not contribute to
totalEvents, sourceEventIds, digest identity, logId, message count, latest, or priority.
- If every queued event is suppressed, consume the queue without dispatching a zero-event wake.
- Preserve all non-message events and still-fresh message events in mixed queues.
- Preserve mailbox state: freshness suppression never reads, archives, deletes, or rewrites the MESSAGE/receipt.
- Emit bounded observability for suppressed stale messages (count and oldest provable age; no subject/body).
No new config leaf and no new policy definition: Shape B consumes the same mechanism ceiling already shared with the daemon.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
| Shape-B MESSAGE wake admission |
wakeCoalescePolicy.isMessageWakeFresh |
Canonical MESSAGE timestamps are admitted only inside the existing closed one-hour horizon |
Missing, malformed, numeric, or future timestamps fail closed for wake only |
Existing policy JSDoc + consumer JSDoc |
pure boundary fixtures + integrated coalescer witness |
| Shape-B digest population |
CoalescingEngineService._flush / _buildDigestEnvelope |
Filter stale MESSAGE events before every aggregate and envelope identity field |
Mixed task/permission/heartbeat events still deliver unchanged; all-suppressed queues dispatch nothing |
method JSDoc |
mixed stale-high/fresh-normal and all-stale witnesses |
| Read/missing reconciliation |
PR #16918's injected delivery-state resolver |
Existing committed-read suppression and missing-row pointer rules remain intact for fresh candidates |
Resolver unavailable preserves current fail-safe behavior for fresh events; it does not override canonical timestamp failure |
existing JSDoc |
rolled-back read-state and missing-row positive controls stay green |
| Mailbox retention |
MESSAGE + per-recipient receipt graph |
Wake expiry has no mailbox mutation |
Old unread messages remain available to explicit list/drain operations |
none |
assert the exact stale row remains unread and listable |
| Suppression telemetry |
Shape-B coalescing service |
Log bounded count + oldest provable age, never content |
Invalid timestamps count as suppressed but have no age value |
JSDoc |
logger fixture contains no subject/body |
Decision Record impact
none. This closes parity drift between two consumers of an already-settled wake-safety policy. It does not change topology, transport, persistence, or an ADR.
Acceptance Criteria
Out of Scope
- Reopening #15704 or #16541; both closed tickets remain authoritative history.
- The bounded
list_messages absence trap repaired by #16888.
- Changing mailbox retention, read, archive, or deletion policy.
- Sender-liveness gating.
- Host receiver process freshness or renderer wording.
- A new wake timeout/configuration knob.
- Legacy daemon behavior, which already applies the canonical age predicate.
Avoided Traps
- Treating unread as synonymous with new.
- Dating the message from the fresh GraphLog projection edge.
- Marking old mail read to silence wake noise.
- Reverting PR #16918's read/missing reconciliation.
- Blanketing only
latest while stale events still inflate totalEvents, sourceEventIds, digest identity, or priority.
- Dispatching a zero-event envelope after every message was suppressed.
Related
#15704 · PR #15705 · #16541 · PR #16918 · #16888 · #16906
Live latest-open sweep: checked the latest 20 open issues at 2026-08-11T23:35:53.486Z; no equivalent found.
A2A in-flight claim sweep: checked the latest 30 messages across all read states at 2026-08-11T23:35:53.486Z; no competing recent [lane-claim] / [lane-intent] found.
Retrieval Hint: query_raw_memories("Shape-B stale unread wake fresh DELIVERED_TO one-hour CoalescingEngine MESSAGE:2c558f17"); exact source head 135147438812c828001c8fc06d15d525457a5ec6.
Context
On 2026-08-11, a signed Shape-B wake for
@neo-opus-veganamed the subject[lane-claim][#16906] orphaned Post-Merge Validation guardas its latest message. The first investigation called it absent after a bounded mailbox read; that claim was retracted after an untruncated sender census found the exact durable row:MESSAGE:2c558f17-9ad0-4b42-aa3a-419fbb8b337b@neo-gptsentAt:2026-08-10T20:09:25.453ZDELIVERED_TO:830b1179-a987-45f6-a056-c121089c0433→@neo-opus-vegareadAt:null,archivedAt:null5829858The message was real and unread, but roughly 25 hours old when its fresh delivery-projection edge made it eligible for a live interruption. This is not a missing-write or sender-misattribution defect. It is the Shape-B successor of #15704: the canonical one-hour message-wake horizon exists, but the signed webhook/coalescing path does not consume it.
Evidence is head-scoped to
135147438812c828001c8fc06d15d525457a5ec6onorigin/dev.The Problem
Mailbox retention and wake urgency are different contracts. An old unread message should remain listable for explicit recovery, but a fresh GraphLog projection edge must not turn that historical fact into current interruption urgency.
Shape B currently treats a new graph delta as a new wake candidate even when the MESSAGE node's server-stamped
sentAtis already outside the one-hour horizon. The resulting digest can count the stale event, promote its subject tolatest, inherit its priority, and wake a seat for work that is no longer current.The bounded-search retractions matter because they remove the wrong premise—there was no phantom row—but they do not remove this measured stale-replay mechanism.
The Architectural Reality
WakeSubscriptionService.init()starts the live cursor at the current GraphLog head, thenpump()evaluates later deltas and enqueues Shape-A/Shape-B matches (ai/services/memory-core/WakeSubscriptionService.mjs:194-208,238-276).heartbeatPulseEvaluator.buildSentToMeInner()carries the canonical MESSAGEsentAtinto both wake call sites (ai/services/memory-core/heartbeatPulseEvaluator.mjs:175-191).wakeCoalescePolicy.isMessageWakeFresh()already owns the one-hour, canonical-ISO, fail-closed predicate and explicitly rejects GraphLog insertion time as authored age (ai/services/memory-core/wakeCoalescePolicy.mjs:25-36,60-94).ai/daemons/wake/daemon.mjs:75-80,879-895,1024-1035).readAt/ positively missing rows before deriving counts, pointers, and priority (ai/services/memory-core/CoalescingEngineService.mjs:297-315,499-555). It never callsisMessageWakeFresh.The Agent OS structure-map gate was attempted immediately before filing and failed before producing output with
Cannot create a string longer than 0x1fffffe8 characters. Placement is nevertheless bounded to existing sibling modules—wakeCoalescePolicy.mjsandCoalescingEngineService.mjs; this ticket introduces no new file or directory.The Fix
isMessageWakeFreshauthority to Shape-Bwake/sent_to_meevents at flush time, using one capturednowper partition.totalEvents,sourceEventIds, digest identity,logId, message count,latest, or priority.No new config leaf and no new policy definition: Shape B consumes the same mechanism ceiling already shared with the daemon.
Contract Ledger
wakeCoalescePolicy.isMessageWakeFreshCoalescingEngineService._flush/_buildDigestEnvelopeDecision Record impact
none. This closes parity drift between two consumers of an already-settled wake-safety policy. It does not change topology, transport, persistence, or an ADR.Acceptance Criteria
MESSAGE_WAKE_MAX_AGE_MSplus a freshDELIVERED_TOGraphLog edge produces no Shape-B wake.sentAtvalues fail closed for Shape-B wake delivery.Out of Scope
list_messagesabsence trap repaired by #16888.Avoided Traps
latestwhile stale events still inflatetotalEvents,sourceEventIds, digest identity, or priority.Related
#15704 · PR #15705 · #16541 · PR #16918 · #16888 · #16906
Live latest-open sweep: checked the latest 20 open issues at 2026-08-11T23:35:53.486Z; no equivalent found.
A2A in-flight claim sweep: checked the latest 30 messages across all read states at 2026-08-11T23:35:53.486Z; no competing recent
[lane-claim]/[lane-intent]found.Retrieval Hint:
query_raw_memories("Shape-B stale unread wake fresh DELIVERED_TO one-hour CoalescingEngine MESSAGE:2c558f17"); exact source head135147438812c828001c8fc06d15d525457a5ec6.