Context
Leaf of #17225 — slice PR2 per the posted slice plan (issuecomment-5304989519). PR1 (leaf #17248, merged PR #17249) landed the honest-surface unit: plane declaration, unobservable axes as unknown, imported presence vocabulary. This leaf adds the one observation the parent's diagnosis showed nothing models: re-review load.
The premise reproduced live at 2026-08-16T22:06Z while routing a review: the tool read Ada idle (stale add_memory 20:51Z) minutes after she posted a review round, and my own row read idle mid-turn. The presence inversion is PR1's declaration; the LOAD inversion is this leaf — a reviewer holding N open CHANGES_REQUESTED loops reads as queue-depth 0 on every GitHub-derived signal, because reviewRequests empties the moment the review posts.
The Problem
Parent AC4: "Load is added and counts re-review obligations — a peer holding N CHANGES_REQUESTED reads as N, not 0. Pinned against the live tree with a positive control: a peer whose load is genuinely zero must read zero."
Vega's sharpening on the parent: the re-review obligation after an author response is invisible everywhere — no reviewRequests, no notification, no query surfaces it (#17218 was finished, green, and silent until hand-diffed).
The Architectural Reality
The swarm's review protocol makes lifecycle A2A pings mandatory (AGENTS.md §critical_gates 6), so the plane-resident mailbox holds the canonical trail: MESSAGE nodes carry from / to / subject / sentAt on their properties (MailboxService.mjs:2361-2379), queryable through the same SQLite json_extract pattern the roster read already uses (WakeSubscriptionService._listAgentIdentityNodes). The derivation itself is pure over already-read rows — new helper ai/services/memory-core/helpers/reviewLoadProjection.mjs, sibling precedent mailboxReadStateClassifier.mjs (same folder, same no-I/O contract).
The classifier's robustness rule is the first-bracket-tag anchor: prose that NAMES a disposition without being one — a stale-approval warning quoting APPROVED, an author response quoting the CHANGES_REQUESTED it answers — must neither open nor retire a loop. And PR refs come from the SUBJECT only: review pings list the tickets they resolve in relatedTickets, so counting every ref would open phantom loops on issues nobody reviewed.
The Fix
ai/services/memory-core/helpers/reviewLoadProjection.mjs (new, pure): deriveReviewLoad(messages) → per-reviewer {open, returned, loops[]} (oldest-first); 30-day trail horizon as a module constant, declared in the envelope reason.
WakeSubscriptionService.whoIsOnline: axes.load envelope on both shapes (container plane, wired/observed, signal a2a-review-lifecycle); terse gains the sparse reviewLoad map (identity → open count; absent IS the counted zero); verbose rows gain per-row reviewLoad.
ai/mcp/server/memory-core/openapi.yaml: description + schema for the new fields (the declared-schema pin test reads them).
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
who_is_online axes surface — WakeSubscriptionService._composedAxesEnvelope (ai/services/memory-core/WakeSubscriptionService.mjs) |
this ticket (parent AC4) |
adds the load capability envelope: container-plane, wired/observed, signal a2a-review-lifecycle, blind class named in reason |
trail store absent or read failure → the envelope degrades (degraded/none + reason): absence of observation, never a counted zero |
method JSDoc + openapi description |
AC4 spec pins |
Terse payload reviewLoad |
parent AC4 positive control |
sparse map identity → open loop count |
absent entry = counted zero; the whole key is OMITTED when the trail is unreadable |
openapi schema |
terse pin test |
Verbose per-row reviewLoad |
parent AC4 |
{open, returned, loops[]}, loops oldest-first (stalest obligation at the head) |
{open: 0, returned: 0, loops: []}; null when the trail is unreadable |
openapi schema |
verbose pin tests |
Derivation — ai/services/memory-core/helpers/reviewLoadProjection.mjs (new) |
parent AC4 + the mailbox write contract (MailboxService.mjs:2361) |
first-tag-anchored disposition classifier over MESSAGE rows; subject-only PR refs; 30d horizon |
non-conforming subjects invisible — declared in the envelope reason |
module docstring |
robustness pin + 3 mutation controls |
Decision Record impact
aligned-with the FM capability-envelope grammar as declared by PR #17249's superset. No ADR amended; no AiConfig touch (the horizon is a module constant, not a config leaf — ADR-0019 not engaged).
Acceptance Criteria
Out of Scope
- The fleet-publish slice (parent AC6-8, forked to @neo-fable-clio Tier-2.5): the host-originated axes stay unobserved until then.
- Live GitHub review state at read time (see Avoided Traps).
- Human/external reviewer coverage — the trail is the swarm protocol's; the envelope declares the blind class.
Avoided Traps
- Live GitHub reads at call time.
who_is_online is a fast routing glance; per-call GraphQL is seconds and credential-bound, and reviewRequests-based queue depth is the exact signal the parent ticket falsified. The plane-resident trail is instant and canonical.
relatedTickets as the PR source. Review pings list the tickets they resolve; counting every ref would open loops on ISSUES nobody reviewed. The subject's PR #N is the protocol surface.
- A config leaf for the horizon. Module constant with the declaration in the envelope reason; no AiConfig surface.
Related
Parent #17225 · sibling leaf #17248 (PR1, merged PR #17249) · the fleet-publish fork rides the parent's AC6-8.
Live latest-open sweep: latest 20 open issues checked live via gh at 2026-08-16T22:10Z, no equivalent (the only who_is_online items are this chain's own). A2A claim sweep (last hour, all read-states): no competing claim on the load-axis scope; the umbrella claim is mine from the parent intake.
Origin Session ID: ade8fd5d-4732-49ad-9763-b1c8b6772826
Retrieval Hint: query_raw_memories("who_is_online load axis review lifecycle trail A2A CHANGES_REQUESTED derivation")
Context
Leaf of #17225 — slice PR2 per the posted slice plan (issuecomment-5304989519). PR1 (leaf #17248, merged PR #17249) landed the honest-surface unit: plane declaration, unobservable axes as
unknown, imported presence vocabulary. This leaf adds the one observation the parent's diagnosis showed nothing models: re-review load.The premise reproduced live at 2026-08-16T22:06Z while routing a review: the tool read Ada
idle(staleadd_memory20:51Z) minutes after she posted a review round, and my own row readidlemid-turn. The presence inversion is PR1's declaration; the LOAD inversion is this leaf — a reviewer holding N openCHANGES_REQUESTEDloops reads as queue-depth 0 on every GitHub-derived signal, becausereviewRequestsempties the moment the review posts.The Problem
Parent AC4: "Load is added and counts re-review obligations — a peer holding N
CHANGES_REQUESTEDreads as N, not 0. Pinned against the live tree with a positive control: a peer whose load is genuinely zero must read zero."Vega's sharpening on the parent: the re-review obligation after an author response is invisible everywhere — no
reviewRequests, no notification, no query surfaces it (#17218was finished, green, and silent until hand-diffed).The Architectural Reality
The swarm's review protocol makes lifecycle A2A pings mandatory (AGENTS.md §critical_gates 6), so the plane-resident mailbox holds the canonical trail:
MESSAGEnodes carryfrom/to/subject/sentAton their properties (MailboxService.mjs:2361-2379), queryable through the same SQLitejson_extractpattern the roster read already uses (WakeSubscriptionService._listAgentIdentityNodes). The derivation itself is pure over already-read rows — new helperai/services/memory-core/helpers/reviewLoadProjection.mjs, sibling precedentmailboxReadStateClassifier.mjs(same folder, same no-I/O contract).The classifier's robustness rule is the first-bracket-tag anchor: prose that NAMES a disposition without being one — a stale-approval warning quoting
APPROVED, an author response quoting theCHANGES_REQUESTEDit answers — must neither open nor retire a loop. And PR refs come from the SUBJECT only: review pings list the tickets they resolve inrelatedTickets, so counting every ref would open phantom loops on issues nobody reviewed.The Fix
ai/services/memory-core/helpers/reviewLoadProjection.mjs(new, pure):deriveReviewLoad(messages)→ per-reviewer{open, returned, loops[]}(oldest-first); 30-day trail horizon as a module constant, declared in the envelope reason.WakeSubscriptionService.whoIsOnline:axes.loadenvelope on both shapes (container plane, wired/observed, signala2a-review-lifecycle); terse gains the sparsereviewLoadmap (identity → open count; absent IS the counted zero); verbose rows gain per-rowreviewLoad.ai/mcp/server/memory-core/openapi.yaml: description + schema for the new fields (the declared-schema pin test reads them).Contract Ledger
who_is_onlineaxes surface —WakeSubscriptionService._composedAxesEnvelope(ai/services/memory-core/WakeSubscriptionService.mjs)loadcapability envelope: container-plane, wired/observed, signala2a-review-lifecycle, blind class named inreasondegraded/none+ reason): absence of observation, never a counted zeroreviewLoadreviewLoad{open, returned, loops[]}, loops oldest-first (stalest obligation at the head){open: 0, returned: 0, loops: []};nullwhen the trail is unreadableai/services/memory-core/helpers/reviewLoadProjection.mjs(new)MailboxService.mjs:2361)Decision Record impact
aligned-with the FM capability-envelope grammar as declared by PR #17249's superset. No ADR amended; no AiConfig touch (the horizon is a module constant, not a config leaf — ADR-0019 not engaged).
Acceptance Criteria
CHANGES_REQUESTEDloops reads N (verbose per-rowopen+loops; terse sparse map), red-proved by fixture.APPROVEDand author responses quotingCHANGES_REQUESTEDneither open nor retire loops — pinned with the author rostered, so the assertion cannot be vacuous.returned, distinct from a fresh open.reason.reviewLoadfor a seat with a known open loop matches hand-counted GitHub state.Out of Scope
Avoided Traps
who_is_onlineis a fast routing glance; per-call GraphQL is seconds and credential-bound, andreviewRequests-based queue depth is the exact signal the parent ticket falsified. The plane-resident trail is instant and canonical.relatedTicketsas the PR source. Review pings list the tickets they resolve; counting every ref would open loops on ISSUES nobody reviewed. The subject'sPR #Nis the protocol surface.Related
Parent #17225 · sibling leaf #17248 (PR1, merged PR #17249) · the fleet-publish fork rides the parent's AC6-8.
Live latest-open sweep: latest 20 open issues checked live via
ghat 2026-08-16T22:10Z, no equivalent (the only who_is_online items are this chain's own). A2A claim sweep (last hour, all read-states): no competing claim on the load-axis scope; the umbrella claim is mine from the parent intake.Origin Session ID: ade8fd5d-4732-49ad-9763-b1c8b6772826
Retrieval Hint:
query_raw_memories("who_is_online load axis review lifecycle trail A2A CHANGES_REQUESTED derivation")