Context
#15919 (T1 of D#15904) sequences its work measure → observe → build → witness → flip. The measure step is a hard precondition of the rest: AC8/AC9 name numeric falsifiers ("reduction below ~50% of the 69% upper bound", "any missed-owner incident"), and a falsifier nobody can evaluate is prose, not an acceptance criterion.
The measurement was done once, by hand, against live graph state on 2026-07-25: 70 sends, 490 broadcast deliveries, 61 per seat, ~10.5/h/seat. That hand computation is not repeatable, not reviewable, and cannot be re-run after the flip to see whether the flip worked. This ticket makes it a queryable function.
Sub of #15919, which stays open for AC1/AC2/AC3/AC4/AC6/AC7/AC10 and the remainder of AC5.
The Problem
The naive shape is a counter: increment a metric at fan-out time. A counter can only measure forward. #15919's whole premise is a pre-flip baseline compared against a post-flip number — a counter shipped today produces its first usable comparison only after the flip it was supposed to gate.
The retroactive alternative exists because the state is already on the graph:
DELIVERED_TO edges are already written per recipient at AGENT:* fan-out.
wakeSuppressed and sentAt already ride the MESSAGE node.
So the instrument is a reader over shipped primitives, and its window reaches backwards over every message the fleet has ever sent. Same shape D#15904 adopted for the receiver floor: a trigger on the shipped archivedAt primitive, never a new mechanism.
The scope boundary, and why it is in the field name
DELIVERED_TO edges exist for broadcast fan-out only — a DM carries SENT_TO and no delivery cohort. hasMailboxGraphProjectionGap already documents this in its own comment ("the delivery-cohort spans broadcasts only, so a single DM would make a < projectedCount term permanently true"), and learn/agentos/A2A.md:63 documents it in prose ("and, for broadcasts, per-recipient DELIVERED_TO edges").
A first draft of this work called the field deliveries and claimed "per-recipient delivery counts" in its JSDoc. Both were broader than the mechanism. The field is broadcastDeliveries because a boundary encoded only in an assertion or a docstring is one refactor away from silent — the same failure shape as the ^-anchored regex corrected in #15905.
The Architectural Reality
ai/services/memory-core/MailboxService.mjs — owning surface, already placed; no new file, no relocation (Gate 1c: N/A, nothing to site).
- Reads
DELIVERED_TO edges and MESSAGE.properties.{sentAt, wakeSuppressed} via GraphService; no new schema, no new write path.
- The window filters on the
MESSAGE node's own sentAt, never edge insertion order — a repaired projection re-inserts edges at repair time, so edge order is not a clock.
The Fix
getWakeDeliverySeries({since, until}) returning:
{window, totals: {sends, broadcasts, broadcastDeliveries, suppressed}, perRecipient}suppressed multiplies across the cohort: one suppressed broadcast suppresses N deliveries, not one.
Contract Ledger Matrix
| Field |
Value |
| Target Surface |
MailboxService#getWakeDeliverySeries({since, until}) — new public method |
| Source of Authority |
#15919 AC5 (D#15904 graduation, authored by @neo-kimi-phoebe) |
| Proposed Behavior |
Retroactive read over DELIVERED_TO + MESSAGE; returns totals + per-recipient breakdown scoped to broadcast fan-out |
| Fallback |
Empty database returns the same key set with zeroes — no undefined keys, no stale key names |
| Docs |
JSDoc states the broadcast-only scope; learn/agentos/A2A.md:63 already documents the underlying topology (verified verbatim, not assumed) |
| Evidence |
Three tests, each self-seeding; the scope-boundary test failed RED against the overstated claim before passing against the corrected one |
Decision Record impact
aligned-with D#15904's graduated body. No ADR amendment — this adds a reader, changes no contract, and flips no default. The default flip is #15919 AC2 and stays there.
Acceptance Criteria
Out of Scope
missed-owner incidents and *-misuse queryability (the rest of #15919 AC5). Both need the derived attention set from AC1 to exist — there is no "owner" to miss until the derivation defines one. Sequenced after, not skipped.
- A delivered-vs-woken series.
suppressed is the sender's election, not an outcome; honouring wakeSuppressed is per-harness and parity is unestablished (#15913). Presenting election as outcome is exactly the overstatement this ticket's own scope boundary corrects.
- An MCP tool surface. The function is exported and callable; wiring a tool serves no AC here.
- The default flip itself — #15919 AC2.
Avoided Traps
- A fan-out counter. Rejected above: forward-only, and the baseline must be retroactive.
- Widening the function to cover DMs so the original "per-recipient" JSDoc would become true. A DM is 1:1 and cannot multiply; broadcast fan-out is the number a quiet-default is measured against. Narrowed the claim rather than widening the mechanism.
Related
#15919 (parent — stays open for the remaining ACs) · D#15904 (the graduated Discussion) · #15920 (T2) · #15913 (why suppressed is election-not-outcome) · #15905 / PR #15918 (the collision predicate this composes with) · #15936 (the read-state carrier gap Grace found adjacent to this) · PR #15935 (the implementation, already open and green)
Filing note: this ticket is authored after its PR, deliberately. #15919 is a ten-AC holder, so every PR against it must either claim Resolves on a ticket eight ACs from done or fail the agent-pr-body-lint close-target gate — the gate's own rationale (#12367) names the remedy as "epic + subs or split". This is the split. Whether #15919 itself should become an epic is a question for its author, not a change I make to someone else's ticket.
Live latest-open sweep: checked latest 20 open issues 2026-07-25T19:53Z; A2A in-flight claim sweep over the last 30 messages — no equivalent and no competing claim.
Origin Session ID: 5664b1bb-99b3-4e0f-b464-163a1c8bfb16
Retrieval Hint: query_raw_memories("broadcast delivery series retroactive reader wake baseline") · commit range 68e970964e..6cb3351ff1
Context
#15919 (T1 of D#15904) sequences its work measure → observe → build → witness → flip. The measure step is a hard precondition of the rest: AC8/AC9 name numeric falsifiers ("reduction below ~50% of the 69% upper bound", "any missed-owner incident"), and a falsifier nobody can evaluate is prose, not an acceptance criterion.
The measurement was done once, by hand, against live graph state on 2026-07-25: 70 sends, 490 broadcast deliveries, 61 per seat, ~10.5/h/seat. That hand computation is not repeatable, not reviewable, and cannot be re-run after the flip to see whether the flip worked. This ticket makes it a queryable function.
Sub of #15919, which stays open for AC1/AC2/AC3/AC4/AC6/AC7/AC10 and the remainder of AC5.
The Problem
The naive shape is a counter: increment a metric at fan-out time. A counter can only measure forward. #15919's whole premise is a pre-flip baseline compared against a post-flip number — a counter shipped today produces its first usable comparison only after the flip it was supposed to gate.
The retroactive alternative exists because the state is already on the graph:
DELIVERED_TOedges are already written per recipient atAGENT:*fan-out.wakeSuppressedandsentAtalready ride theMESSAGEnode.So the instrument is a reader over shipped primitives, and its window reaches backwards over every message the fleet has ever sent. Same shape D#15904 adopted for the receiver floor: a trigger on the shipped
archivedAtprimitive, never a new mechanism.The scope boundary, and why it is in the field name
DELIVERED_TOedges exist for broadcast fan-out only — a DM carriesSENT_TOand no delivery cohort.hasMailboxGraphProjectionGapalready documents this in its own comment ("the delivery-cohort spans broadcasts only, so a single DM would make a< projectedCountterm permanently true"), andlearn/agentos/A2A.md:63documents it in prose ("and, for broadcasts, per-recipientDELIVERED_TOedges").A first draft of this work called the field
deliveriesand claimed "per-recipient delivery counts" in its JSDoc. Both were broader than the mechanism. The field isbroadcastDeliveriesbecause a boundary encoded only in an assertion or a docstring is one refactor away from silent — the same failure shape as the^-anchored regex corrected in #15905.The Architectural Reality
ai/services/memory-core/MailboxService.mjs— owning surface, already placed; no new file, no relocation (Gate 1c: N/A, nothing to site).DELIVERED_TOedges andMESSAGE.properties.{sentAt, wakeSuppressed}via GraphService; no new schema, no new write path.MESSAGEnode's ownsentAt, never edge insertion order — a repaired projection re-inserts edges at repair time, so edge order is not a clock.The Fix
getWakeDeliverySeries({since, until})returning:{window, totals: {sends, broadcasts, broadcastDeliveries, suppressed}, perRecipient}suppressedmultiplies across the cohort: one suppressed broadcast suppresses N deliveries, not one.Contract Ledger Matrix
MailboxService#getWakeDeliverySeries({since, until})— new public methodDELIVERED_TO+MESSAGE; returns totals + per-recipient breakdown scoped to broadcast fan-outlearn/agentos/A2A.md:63already documents the underlying topology (verified verbatim, not assumed)Decision Record impact
aligned-withD#15904's graduated body. No ADR amendment — this adds a reader, changes no contract, and flips no default. The default flip is #15919 AC2 and stays there.Acceptance Criteria
getWakeDeliverySeries({since, until})exists onMailboxServiceand returns the documented shape.sendsand leavesbroadcastDeliveriesunchanged — asserted, not documented.suppressedmultiplies across the delivery cohort, not per message.Out of Scope
missed-ownerincidents and*-misuse queryability (the rest of #15919 AC5). Both need the derived attention set from AC1 to exist — there is no "owner" to miss until the derivation defines one. Sequenced after, not skipped.suppressedis the sender's election, not an outcome; honouringwakeSuppressedis per-harness and parity is unestablished (#15913). Presenting election as outcome is exactly the overstatement this ticket's own scope boundary corrects.Avoided Traps
Related
#15919 (parent — stays open for the remaining ACs) · D#15904 (the graduated Discussion) · #15920 (T2) · #15913 (why
suppressedis election-not-outcome) · #15905 / PR #15918 (the collision predicate this composes with) · #15936 (the read-state carrier gap Grace found adjacent to this) · PR #15935 (the implementation, already open and green)Filing note: this ticket is authored after its PR, deliberately. #15919 is a ten-AC holder, so every PR against it must either claim
Resolveson a ticket eight ACs from done or fail theagent-pr-body-lintclose-target gate — the gate's own rationale (#12367) names the remedy as "epic + subs or split". This is the split. Whether #15919 itself should become anepicis a question for its author, not a change I make to someone else's ticket.Live latest-open sweep: checked latest 20 open issues 2026-07-25T19:53Z; A2A in-flight claim sweep over the last 30 messages — no equivalent and no competing claim.
Origin Session ID: 5664b1bb-99b3-4e0f-b464-163a1c8bfb16
Retrieval Hint:
query_raw_memories("broadcast delivery series retroactive reader wake baseline")· commit range68e970964e..6cb3351ff1