Context
Operator challenge, 2026-08-23: "the team is using high prio wake broadcasts for pr reviews and lane claims. this should be normal non-wake. and it is a regression." Clarified: targeted 1:1 wakes are fine — the problem is AGENT:*.
Measured over the last 40 A2A messages: ~23 broadcasts, 18 unsuppressed, 4 of those priority: high. Every one of the 18 woke every active seat.
The Problem
Two distinct failures wear the same symptom, and separating them is the whole point of this ticket.
1. Only claim-class has a mechanical default — and it is one class of four
MailboxService.mjs:2425 is the acceptance-layer default (:2417 before #17649 added the source comment above it):
wakeSuppressed = wakeSuppressed ?? (operatorSteering || (to === 'AGENT:*' && !!collisionPreventionTag({subject, taggedConcepts})));collisionPreventionTag recognises exactly four tags: lane-claim, review-claim, claim-corrected, drive-claimed. Everything else broadcasts loud unless the author remembers the flag — and the routine broadcast vocabulary this fleet actually uses is much wider:
| observed broadcast subject |
matcher result |
woke every seat |
[pr-merged][PR #17623 …] |
null |
yes, at priority: high |
[merge-readiness-uncertified][…PR #17624] → @tobiu |
null |
yes, at priority: high |
[PR-opened][PR #17639 → #17619] |
null |
yes |
[PR opened][draft][PR #17636] |
null |
yes, at priority: high |
[ideation][D#17644][divergence open] |
null |
yes |
[pre-cut disposition posted][Epic #17500] |
null |
yes |
Verified by running collisionPreventionTag against the live subjects, not by reading it.
The → @tobiu rows are the sharpest case. A merge-readiness broadcast's only actionable reader is the operator, who is not an agent seat and does not consume A2A wakes at all. It wakes every seat by construction, for an action none of them can take. Three of those are mine, all at priority: high — I am not reporting someone else's habit.
2. Claim-class is covered, and three still shipped loud
The matcher fires correctly on all three lane-claim broadcasts in the window — I ran it:
"lane-claim" ← [lane-claim][#17317][self-assigned] reveal overlay inside-click…
"lane-claim" ← [ticket-created][lane-claim][#17640][self-assigned][Epic #17500…
"lane-claim" ← [lane-claim][#17619][self-assigned] withdrawn-route visibility
All three were to: AGENT:*, so :2417 should have suppressed them. All three arrived unsuppressed, one at priority: high.
Two hypotheses I refuted before filing, so nobody re-runs them:
The MCP schema default neutralises the ??. openapi.yaml:2135 declares wakeSuppressed: {default: false}, and false ?? x is false. But wakeSuppressed appears nowhere in ai/mcp/server/, and no useDefaults/applyDefaults path exists — the schema default is documentation, not injection.
⚠️ THIS REFUTATION WAS WRONG, and it was the actual root cause. Corrected 2026-08-24 (#17648 / PR #17649, merged 7f608560b0). My search was for a consumer of the field name and for an explicit defaults-application step, and both came back empty — so I concluded the declared default was inert. It was not. buildZodSchema compiles a declared OpenAPI default through zodSchema.default(), and Zod applies a default even under .optional() — so the value materialized into the parsed request before addMessage ever ran, wakeSuppressed was never nullish, and ?? could not reach either branch. Silently, because the injected value is a legal one. Grepping for the field name could never have found this: the injection is generic, in the schema compiler, and names no field. #17648 removed the declared defaults for wakeSuppressed and priority; the rule now recorded at the seam is that a request field may carry a schema default OR a service-side contextual default, never both.
Verified before and after with the identical probe — my own [lane-claim] broadcast with the flag omitted: unsuppressed before, wakeSuppressed: true after deployedRevision: 7f608560b0.
- The deployed plane predates the fix.
healthcheck reports deployedRevision: 0e072f05cd… (2026-08-22); #15987 introduced :2417 in 7d6b8c0ffb (2026-07-26). git merge-base --is-ancestor confirms the deployed revision includes it.
So the mechanism is present, deployed, and correct in isolation. The remaining explanation is that senders are passing wakeSuppressed: false explicitly — which would make this a discipline regression on top of the coverage gap, not a broken mechanism.
That is measured by this ticket's own claim broadcast, which is deliberately sent with the flag omitted; whatever the server stores is the answer. Result recorded below on filing.
The Architectural Reality
learn/agentos/A2A.md:58-59 states the intent exactly right:
quiet is the default for status broadcasts, and a wake is reserved for action the recipient must take
The implementation delivers that sentence for four tags and leaves the rest to memory. peer-role-mode.md:122 then carries the full discipline in prose — "Use priority: 'high' only for act-now wakes… other unsuppressed traffic stays normal" — which is correct guidance and unenforced.
This is the shape #15987 already diagnosed once. Its own commit message says the predecessor was /^\s*\[lane-claim\]/i and "the fleet's own [ticket-created][lane-claim][#N] convention walked past it — 8 of 15 live claims unguarded." The fix made the matcher structural rather than lexical, which was right, and left its vocabulary narrow. The same failure recurred one level up: not a matcher too literal, but a tag set too small.
The Fix
Invert the default for broadcasts. to === 'AGENT:*' is already the strongest available signal that a message is status rather than instruction. Broadcasts are presumed quiet; an all-hands interrupt is an explicit wakeSuppressed: false. (a broadcast cannot be action-required for everyone, or it would be addressed to someone — corrected 2026-08-24: that read as a universal and is not one. The presumption is rebuttable per message, which is what the election is for.)
- Quiet by default for every
AGENT:* broadcast, not only the four collision tags. A sender who genuinely needs to interrupt the whole fleet elects it with explicit wakeSuppressed: false — the same escape hatch a contested-lane resolution already uses, and the same polarity A2A.md already describes.
- Reject
priority: 'high' on AGENT:* only when the sender says nothing about the wake. unless wakeSuppressed: false is explicitly set — narrowed 2026-08-24 in review: explicit true + high is durable-high (top of the queue, nobody woken) and is accepted, because that is what a critical audit alert means. Only silence plus high is the pair nobody chose. Scoped to the agent classes; operator steering carries high as drain-ordering.
getWakeSuppressionRisk already permits broadcast suppression: at :380 it returns null — no risk — for any to that does not start with @, and 'AGENT:*' does not. (Corrected 2026-08-24: this line previously said it "returns true ... at :339", which is wrong twice — the function returns a reason string or null, never true, and the guard sits at :380 after #17649 shifted the file. The conclusion is unchanged and now checkable: the permission exists, only the default is missing.)
Contract Ledger Matrix
| # |
Target surface |
Source of authority |
Before |
After |
Fallback |
Evidence |
| 1 |
addMessage broadcast wake default |
MailboxService.mjs:2425 |
quiet only for 4 collision tags |
quiet for every AGENT:* |
explicit wakeSuppressed: false |
18/23 broadcasts unsuppressed |
| 2 |
priority on AGENT:* (agent classes only) |
MailboxService.mjs acceptance |
high accepted freely |
high requires the sender to STATE the wake — false (interrupt) or true (durable-high) |
none — only SILENCE + high is rejected |
4 high-priority broadcasts observed |
| 3 |
openapi.yaml:2133 description |
MCP tool contract |
"Default: omit (= false)" |
must state broadcast-quiet inversion |
none |
the description is what authors read |
| 4 |
A2A.md:61 / peer-role-mode.md:122 |
prose authority |
claim-class-scoped |
broadcast-scoped |
none |
prose already asserts the wider intent |
Acceptance Criteria
Out of Scope
- Targeted 1:1 wake behaviour — operator-confirmed correct. Review requests,
REQUEST_CHANGES, evidence requests and lane-unblocks addressed to one peer should wake, and this ticket must not touch them.
- The
getWakeSuppressionRisk rejection path for actionable direct lifecycle subjects — that guard is right and stays.
- Wake coalescing / digest behaviour (
wakeCoalescePolicy.mjs) — downstream of this decision.
- The heartbeat/pulse evaluator's own
wakeSuppressed read.
Avoided Traps
- Reading this as a discipline problem and writing a stronger rule.
peer-role-mode.md:122 is already precise and already ignored at scale; a fourth prose statement of the same intent is the anti-pattern, not the fix. The three surfaces that already say it are the evidence that saying it again will not work.
- Widening
COLLISION_PREVENTION_TAGS instead. That set means "this message prevents a claim collision" — a real, separate concept with its own consumers (fleetA2AActivityAdapter.mjs:278 reads it to identify lane claims). Stuffing pr-merged into it to get quiet would corrupt a working abstraction to reach an unrelated default.
- Suppressing broadcasts unconditionally. A contested-lane resolution genuinely must interrupt;
A2A.md:64 already reserves explicit wakeSuppressed: false for exactly that, and removing the election would trade one failure for its mirror.
- Assuming the mechanism is broken because the symptom is present. Two plausible mechanical explanations — schema-default injection and a stale deployed plane — were both refuted before filing. The coverage gap is real; a broken
:2417 is not established.
Decision Record impact
none — implements the polarity A2A.md already documents. If review concludes the broadcast-wide inversion is an architectural change rather than a completion of #15987, it becomes amends and says so.
Structure-map gate: N/A for placement — the change lands in the existing MailboxService acceptance seam.
Live latest-open sweep: checked latest 20 open issues at 2026-08-23T19:20Z; no equivalent found. A2A in-flight claim sweep: latest 40 all-state messages — that sweep is this ticket's own dataset; no overlapping claim.
Related
#15987 / PR #15989 (made the matcher structural and left the vocabulary narrow — the direct predecessor) · #12635 · #14576 (the wake-control lineage peer-role-mode.md:122 cites) · #15639
Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84
Retrieval Hint: query_raw_memories("A2A broadcast wake default AGENT:* quiet collisionPreventionTag priority high status broadcasts")
Context
Operator challenge, 2026-08-23: "the team is using high prio wake broadcasts for pr reviews and lane claims. this should be normal non-wake. and it is a regression." Clarified: targeted 1:1 wakes are fine — the problem is
AGENT:*.Measured over the last 40 A2A messages: ~23 broadcasts, 18 unsuppressed, 4 of those
priority: high. Every one of the 18 woke every active seat.The Problem
Two distinct failures wear the same symptom, and separating them is the whole point of this ticket.
1. Only claim-class has a mechanical default — and it is one class of four
MailboxService.mjs:2425is the acceptance-layer default (:2417before #17649 added the source comment above it):wakeSuppressed = wakeSuppressed ?? (operatorSteering || (to === 'AGENT:*' && !!collisionPreventionTag({subject, taggedConcepts})));collisionPreventionTagrecognises exactly four tags:lane-claim,review-claim,claim-corrected,drive-claimed. Everything else broadcasts loud unless the author remembers the flag — and the routine broadcast vocabulary this fleet actually uses is much wider:[pr-merged][PR #17623 …]nullpriority: high[merge-readiness-uncertified][…PR #17624] → @tobiunullpriority: high[PR-opened][PR #17639 → #17619]null[PR opened][draft][PR #17636]nullpriority: high[ideation][D#17644][divergence open]null[pre-cut disposition posted][Epic #17500]nullVerified by running
collisionPreventionTagagainst the live subjects, not by reading it.The
→ @tobiurows are the sharpest case. A merge-readiness broadcast's only actionable reader is the operator, who is not an agent seat and does not consume A2A wakes at all. It wakes every seat by construction, for an action none of them can take. Three of those are mine, all atpriority: high— I am not reporting someone else's habit.2. Claim-class is covered, and three still shipped loud
The matcher fires correctly on all three lane-claim broadcasts in the window — I ran it:
All three were
to: AGENT:*, so:2417should have suppressed them. All three arrived unsuppressed, one atpriority: high.Two hypotheses I refuted before filing, so nobody re-runs them:
The MCP schema default neutralises the??.openapi.yaml:2135declareswakeSuppressed: {default: false}, andfalse ?? xisfalse. ButwakeSuppressedappears nowhere inai/mcp/server/, and nouseDefaults/applyDefaultspath exists — the schema default is documentation, not injection.healthcheckreportsdeployedRevision: 0e072f05cd…(2026-08-22); #15987 introduced:2417in7d6b8c0ffb(2026-07-26).git merge-base --is-ancestorconfirms the deployed revision includes it.So the mechanism is present, deployed, and correct in isolation. The remaining explanation is that senders are passing
wakeSuppressed: falseexplicitly — which would make this a discipline regression on top of the coverage gap, not a broken mechanism.That is measured by this ticket's own claim broadcast, which is deliberately sent with the flag omitted; whatever the server stores is the answer. Result recorded below on filing.
The Architectural Reality
learn/agentos/A2A.md:58-59states the intent exactly right:The implementation delivers that sentence for four tags and leaves the rest to memory.
peer-role-mode.md:122then carries the full discipline in prose — "Usepriority: 'high'only for act-now wakes… other unsuppressed traffic staysnormal" — which is correct guidance and unenforced.This is the shape #15987 already diagnosed once. Its own commit message says the predecessor was
/^\s*\[lane-claim\]/iand "the fleet's own[ticket-created][lane-claim][#N]convention walked past it — 8 of 15 live claims unguarded." The fix made the matcher structural rather than lexical, which was right, and left its vocabulary narrow. The same failure recurred one level up: not a matcher too literal, but a tag set too small.The Fix
Invert the default for broadcasts.
to === 'AGENT:*'is already the strongest available signal that a message is status rather than instruction. Broadcasts are presumed quiet; an all-hands interrupt is an explicitwakeSuppressed: false. (a broadcast cannot be action-required for everyone, or it would be addressed to someone— corrected 2026-08-24: that read as a universal and is not one. The presumption is rebuttable per message, which is what the election is for.)AGENT:*broadcast, not only the four collision tags. A sender who genuinely needs to interrupt the whole fleet elects it with explicitwakeSuppressed: false— the same escape hatch a contested-lane resolution already uses, and the same polarityA2A.mdalready describes.priority: 'high'onAGENT:*only when the sender says nothing about the wake.unless— narrowed 2026-08-24 in review: explicitwakeSuppressed: falseis explicitly settrue+highis durable-high (top of the queue, nobody woken) and is accepted, because that is what acriticalaudit alert means. Only silence plushighis the pair nobody chose. Scoped to the agent classes; operator steering carrieshighas drain-ordering.getWakeSuppressionRiskalready permits broadcast suppression: at:380it returnsnull— no risk — for anytothat does not start with@, and'AGENT:*'does not. (Corrected 2026-08-24: this line previously said it "returnstrue... at:339", which is wrong twice — the function returns a reason string ornull, nevertrue, and the guard sits at:380after #17649 shifted the file. The conclusion is unchanged and now checkable: the permission exists, only the default is missing.)Contract Ledger Matrix
addMessagebroadcast wake defaultMailboxService.mjs:2425AGENT:*wakeSuppressed: falsepriorityonAGENT:*(agent classes only)MailboxService.mjsacceptancehighaccepted freelyhighrequires the sender to STATE the wake —false(interrupt) ortrue(durable-high)highis rejectedopenapi.yaml:2133descriptionA2A.md:61/peer-role-mode.md:122Acceptance Criteria
AGENT:*broadcast withwakeSuppressedomitted is stored suppressed, whatever its subject — asserted on at least one subject thatcollisionPreventionTagreturnsnullfor, since a claim-class subject would pass under the current code and prove nothing.wakeSuppressed: falseon a broadcast still wakes — the sender election survives, verified red against a change that made broadcasts unconditionally quiet.wakeSuppressedomitted is unchanged: still wakes. The inversion is broadcast-scoped, and an arm proves 1:1 traffic is untouched (operator: "targeted 1:1 wakes are fine").priority: 'high'on anAGENT:*broadcast with nowakeSuppressedat all is rejected with a message naming both knobs. Narrowed 2026-08-24 in review (@neo-gpt-emmy):without explicit— explicitwakeSuppressed: falsewakeSuppressed: true+highis durable-high (top of the queue, nobody woken) and must be ACCEPTED.KbAlertingService.dispatchA2Aproduces exactly that shape for acriticalseverity withdeliveryMode: 'audit'on ana2a:AGENT:*channel, and its dispatcher catches and logs — so rejecting it would have LOST the durable alert rather than surfacing an error.highas turn-start drain-ordering metadata, so it is exempt — asserted, not assumed.openapi.yaml'swakeSuppresseddescription states the broadcast-quiet default;A2A.mdandpeer-role-mode.mddrop the claim-class scoping.Out of Scope
REQUEST_CHANGES, evidence requests and lane-unblocks addressed to one peer should wake, and this ticket must not touch them.getWakeSuppressionRiskrejection path for actionable direct lifecycle subjects — that guard is right and stays.wakeCoalescePolicy.mjs) — downstream of this decision.wakeSuppressedread.Avoided Traps
peer-role-mode.md:122is already precise and already ignored at scale; a fourth prose statement of the same intent is the anti-pattern, not the fix. The three surfaces that already say it are the evidence that saying it again will not work.COLLISION_PREVENTION_TAGSinstead. That set means "this message prevents a claim collision" — a real, separate concept with its own consumers (fleetA2AActivityAdapter.mjs:278reads it to identify lane claims). Stuffingpr-mergedinto it to get quiet would corrupt a working abstraction to reach an unrelated default.A2A.md:64already reserves explicitwakeSuppressed: falsefor exactly that, and removing the election would trade one failure for its mirror.:2417is not established.Decision Record impact
none— implements the polarityA2A.mdalready documents. If review concludes the broadcast-wide inversion is an architectural change rather than a completion of #15987, it becomesamendsand says so.Structure-map gate: N/A for placement — the change lands in the existing
MailboxServiceacceptance seam.Live latest-open sweep: checked latest 20 open issues at 2026-08-23T19:20Z; no equivalent found. A2A in-flight claim sweep: latest 40 all-state messages — that sweep is this ticket's own dataset; no overlapping claim.
Related
#15987/ PR#15989(made the matcher structural and left the vocabulary narrow — the direct predecessor) ·#12635·#14576(the wake-control lineagepeer-role-mode.md:122cites) ·#15639Origin Session ID: eb671e6e-ca17-4a53-8069-64fd5885ce84
Retrieval Hint:
query_raw_memories("A2A broadcast wake default AGENT:* quiet collisionPreventionTag priority high status broadcasts")