LearnNewsExamplesServices
Frontmatter
id17646
titleStatus broadcasts wake every seat: the quiet default covers one class of four
stateClosed
labels
bugaimodel-experienceagent-os
assigneesneo-opus-grace
createdAtAug 23, 2026, 9:20 PM
updatedAtAug 24, 2026, 3:35 AM
githubUrlhttps://github.com/neomjs/neo/issues/17646
authorneo-opus-grace
commentsCount6
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 24, 2026, 3:35 AM

Status broadcasts wake every seat: the quiet default covers one class of four

Closed Backlog/active-chunk-19 bugaimodel-experienceagent-os
neo-opus-grace
neo-opus-grace commented on Aug 23, 2026, 9:20 PM

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

  • An AGENT:* broadcast with wakeSuppressed omitted is stored suppressed, whatever its subject — asserted on at least one subject that collisionPreventionTag returns null for, since a claim-class subject would pass under the current code and prove nothing.
  • Explicit wakeSuppressed: false on a broadcast still wakes — the sender election survives, verified red against a change that made broadcasts unconditionally quiet.
  • A direct message with wakeSuppressed omitted 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 an AGENT:* broadcast with no wakeSuppressed at all is rejected with a message naming both knobs. Narrowed 2026-08-24 in review (@neo-gpt-emmy): without explicit wakeSuppressed: false — explicit wakeSuppressed: true + high is durable-high (top of the queue, nobody woken) and must be ACCEPTED. KbAlertingService.dispatchA2A produces exactly that shape for a critical severity with deliveryMode: 'audit' on an a2a:AGENT:* channel, and its dispatcher catches and logs — so rejecting it would have LOST the durable alert rather than surfacing an error.
  • The rejection is scoped to the agent sender classes. Operator steering is durable-quiet by design (#15376 / PR #15378) and carries high as turn-start drain-ordering metadata, so it is exempt — asserted, not assumed.
  • openapi.yaml's wakeSuppressed description states the broadcast-quiet default; A2A.md and peer-role-mode.md drop the claim-class scoping.

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")

tobiu referenced in commit 7f60856 - "fix(mcp): drop the schema defaults that shadow add_message's computed ones (#17648) (#17649) on Aug 23, 2026, 10:14 PM
tobiu closed this issue on Aug 24, 2026, 3:35 AM
tobiu referenced in commit 3a71e06 - "fix(memory-core): every AGENT:* broadcast is quiet by default (#17646) (#17657) on Aug 24, 2026, 3:35 AM