LearnNewsExamplesServices
Frontmatter
id17497
titleA review request at default priority never wakes an idle reviewer
stateClosed
labels
bugaiagent-os
assignees[]
createdAtAug 21, 2026, 9:09 PM
updatedAtAug 21, 2026, 9:10 PM
githubUrlhttps://github.com/neomjs/neo/issues/17497
authorneo-fable-clio
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 21, 2026, 9:10 PM

A review request at default priority never wakes an idle reviewer

Closed Backlog/active-chunk-18 bugaiagent-os
neo-fable-clio
neo-fable-clio commented on Aug 21, 2026, 9:09 PM

A review request at default priority never wakes an idle reviewer

Context

Live incident 2026-08-21, operator-observed: PR #17491's review request to @neo-gpt (MESSAGE:a1845f64, 18:30:29Z, direct 1:1, not suppressed) sat unread while the reviewer stayed idle — the operator asked "did not get a wake. what went wrong?". The reviewer had gone idle at 17:57:40Z, before the send; a wake was the only path back, and none fired. A manual resend at priority: high (MESSAGE:578c6332, 19:06:43Z) was the workaround.

Evidence chain, verified not inferred: the sender's own wake subscription (recipe-bootstrapped 2026-08-02, same shape across seats) declares trigger: SENT_TO_ME with filters: {priority: "high"} — and the inverse case proves the filter live: the reviewer's earlier priority: high 1:1 (17:57Z) woke the sender mid-turn, while the sender's priority: normal request in the other direction woke nobody.

The Problem

The platform already encodes "actionable direct lifecycle messages must be able to wake": the mailbox acceptance guard rejects wakeSuppressed: true on exactly that class. But wake delivery has a second, independent drop point — the subscription priority filter — and nothing routes the same classification through it. A review request sent at the default priority is actionable-lifecycle by the server's own definition, passes acceptance un-suppressed, emits SENT_TO_ME, and then dies at a filter that only asks "is it high?". The suppression-rejection intent is defeated one layer downstream, silently: the sender sees a successful send, the recipient sees nothing, and review latency (the fleet's named bottleneck) absorbs the loss until a human notices.

The Architectural Reality

  • ai/services/memory-core/MailboxService.mjs:374getWakeSuppressionRisk({wakeSuppressed, to, subject, priority, taggedConcepts, task, senderPrincipalClass}): the acceptance-side classifier that names the protected class ("known-actionable DIRECT messages — actionable lifecycle subjects, high-priority", per its own docblock at :349) and rejects suppression on it. It runs ONLY when a sender sets wakeSuppressed: true.
  • ai/services/memory-core/MailboxService.mjs:2404priority = priority ?? (operatorSteering ? 'high' : 'normal'): agent sends default to normal; only operator steering elevates. The docblock (:2400) frames priority as "turn-start drain-ordering metadata" — but the wake substrate ALSO consumes it as a delivery gate, an unstated second consumer.
  • ai/services/memory-core/MailboxService.mjs:2411 — the suppression default: direct messages default un-suppressed, so the class arrives at the wake substrate correctly; the loss is purely the filter mismatch.
  • Wake subscriptions (manage_wake_subscription, WAKE_SUB rows): per-seat filters.priority is the recipe's shipped shape; a subscription filtering on high is legitimate throttle hygiene — the defect is that the SENDER side has no mechanism to mark lifecycle sends as filter-passing, and no substrate line tells authors to.
  • The routing substrate is silent: grep -rn "priority" .agents/skills/pull-request/references/*.md → zero matches. The review-request format (ci-green-review-routing.md, review-response-protocol.md §14) specifies role lines and wake-required classes but never the priority a waking send must carry.

The Fix

Elevate at acceptance, where the classification already lives: in addMessage, when the message matches the SAME actionable-direct-lifecycle classification getWakeSuppressionRisk protects (direct recipient + lifecycle subject/task shape) and the sender did not explicitly set a priority, default priority to 'high' instead of 'normal' — one code path, the classifier consulted once at one seam, and the class of silent drops disappears for every current and future seat without touching subscription semantics. An explicit priority: 'normal'/'low' from the sender stays a sender election (mirror of the wakeSuppressed: false election rule).

Companion one-liner in ci-green-review-routing.md: waking review-lifecycle sends carry priority: high — stated for the window until the mechanical default lands, and as reader documentation for why their sends wake.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
add_message priority default (MCP tool + MailboxService.addMessage) MailboxService.mjs:2404 default expression Unset priority on actionable-direct-lifecycle messages defaults high (classification per the :374 guard's subject/task shape); explicit sender priority always wins Non-lifecycle and broadcast sends keep the normal default unchanged This ticket + the ci-green-review-routing.md line Unit spec on the default expression: lifecycle-direct unset → high; explicit normal preserved; broadcast unset → normal
Wake subscription filters.priority WAKE_SUB rows (per-seat) Unchanged — filters keep their semantics; the class now passes them by carrying the priority they ask for n/a n/a The incident pair above (high wakes, normal does not) stays the manual reproduction

Decision Record impact

none — no ADR governs mailbox priority semantics; the change is aligned-with the acceptance guard's own stated intent (MailboxService.mjs:349-360).

Acceptance Criteria

  • An unset-priority direct message with a lifecycle subject/task shape (the :374 classification) is stored and emitted at priority: 'high'; an explicit sender priority is never overridden.
  • Broadcast (AGENT:*) and non-lifecycle direct sends keep the current normal default (spec-pinned).
  • The owning unit tree covers the three-way matrix (lifecycle-direct unset / explicit normal / broadcast unset).
  • ci-green-review-routing.md carries the one-line priority statement for waking review-lifecycle sends.

Out of Scope

#16526 (the wake daemon's host-path read — a sibling delivery-layer defect, claimed by @neo-opus-grace; this ticket's fix is necessary but not sufficient while that one stands) · subscription-filter semantics redesign (filters consulting classifications directly) · per-seat subscription repair or audit tooling · retroactive re-delivery of dropped wakes.

Related

#16526 (sibling delivery-layer defect) · PR #17491 (the incident's review lane) · #15987 (the claim-class quiet-by-default precedent this mirrors on the opposite class).

Live latest-open sweep: latest 20 open checked 2026-08-21T19:09Z, no equivalent; A2A herd-window sweep (last ~90 min, all read-states) clean — nearest neighbor is #16526, a different layer.

Origin Session ID: 8947f450-e0c3-424b-8aa1-1e52ea33c03f

Retrieval Hint: query_raw_memories("review request wake priority filter subscription normal high idle reviewer")

tobiu
tobiu Aug 21, 2026, 9:10 PM

NOT planned. on purpose.

tobiu closed this issue on Aug 21, 2026, 9:10 PM