The post-admission half of parent #16677 has a deterministic source-level work amplifier even after closed #16767 removed the non-converging WAL-repair floor.
On the canonical one-CPU plane, fixed-window telemetry measured Fleet-bound list_messages calls averaging 10,642 ms and reaching 21,606 ms. In a separate fixed ingress window, 42/47 Memory Core POST reset intervals overlapped a Fleet list_messages execution interval. That overlap is correlation, not request-identity proof; this ticket owns only the independently verified list-projection cost below.
MailboxService.listMessages() scans every cached edge to find candidates, uses messages.find() for de-duplication, then scans every cached edge again for every matched message;
pagination is applied only after the complete result is built and sorted.
The incident store measured 93,765 edges and 10,704 MESSAGE nodes. limit:50 bounds the returned page, but the repeated projection work scales with the whole edge cache multiplied by the number of matches.
The Problem
MailboxService.listMessages() has an effectively quadratic projection phase.
For E cached edges and M matched messages, the current implementation performs:
one E-edge candidate scan;
an O(M²) linear-array duplicate check;
one additional E-edge metadata scan per matched message;
one additional E-edge related-ticket scan per matched message;
repeated full-edge broadcast-delivery filters on the legacy/current broadcast branch.
Pagination cannot protect the server because totalCount intentionally describes all filter matches and the slice happens last. The contract still needs the full match count; it does not need to rediscover the same edges for each row.
Closed #16767 is adjacent, not duplicate: it made graph/WAL repair convergent. This leaf starts after that repair and removes repeated graph projection scans from the ordinary list.
The Architectural Reality
The scoped structure map establishes the existing owners:
ai/services/fleet/fleetA2AActivityAdapter.mjs is a consumer of the existing list_messages contract;
GraphService.db.getAdjacentNodes() is still required to hydrate peer-process writes before projection;
ai/graph/Store.mjs already maintains source- and target-keyed secondary indexes, and Database.mjs constructs the edge store with both indexes;
the public MCP request/response authority remains ai/mcp/server/memory-core/openapi.yaml.
The fix belongs in the existing mailbox projection seam and must consume the existing graph indexes. It does not require a new MCP tool, a Fleet-private mailbox, a polling change, a second per-call edge index, or a GraphService-wide index.
The Fix
Replace the cache-wide and per-message scans with indexed graph reads:
After the current target/sentinel vicinity hydration, union db.edges.getByIndex('target', variant) for the caller's storage variants and AGENT:* where the box requires them.
Collect and de-duplicate candidate message ids with a Set, preserving the current direct, outbox, current-broadcast, and legacy-broadcast visibility rules.
Hydrate each candidate message's outbound vicinity so peer-process SENT_BY, SENT_TO, PART_OF_THREAD, TAGGED_CONCEPT, REFERENCES_TICKET, and DELIVERED_TO edges remain visible.
Read each candidate's metadata once through db.edges.getByIndex('source', messageId). Use that indexed slice for sender/recipient/thread/tag/ticket and broadcast-delivery resolution.
Preserve archive/read/retraction semantics, authorization, sorting, totalCount, truncated, nextOffset, and the existing public envelope.
Add a production-bound witness that fails if the full edge collection is enumerated after indexed vicinity hydration.
listMessages() no longer calls messages.find() to de-duplicate candidate rows.
Candidate discovery uses the existing target index for every canonical/legacy identity storage variant and the broadcast sentinel required by the selected box.
Message metadata, related-ticket lookup, and broadcast-delivery lookup use the existing source index rather than scanning the complete edge cache once per projected message.
A production-bound fixture fully hydrates target/sentinel/message vicinities, poisons full edge-store iteration while leaving secondary indexes intact, and still projects direct, broadcast, threaded, tagged, and related-ticket rows correctly.
Restoring a db.edges.items traversal makes the named work-bound fixture red.
Direct inbox, outbox, current per-recipient broadcast, and legacy shared-read broadcast rows remain visible to the same identities.
Sorting, totalCount, truncated, nextOffset, limit, and offset remain correct across at least three pages.
The Fleet activity adapter continues to consume the unchanged public envelope.
The canonical Neo unit command targets the affected mailbox and Fleet adapter specs explicitly; no default npx playwright test invocation is used.
Out of Scope
Replacing truthful totalCount with an estimate.
The graph/WAL repair contract delivered by closed #16767.
Changing Fleet's 15-second cadence or adding a Fleet-private mailbox store.
PAT admission caching, transport deadlines, load shedding, container restart policy, or generic GraphService indexing.
Claiming this one amplification explains every reset recorded on #16677.
Avoided Traps
Slice sooner and report less. That breaks the completeness contract; the repair removes repeated discovery, not truthful totalCount.
Build a second per-call edge index. The graph Store already owns maintained source/target indexes; duplicating them adds drift and retains an avoidable global scan.
Skip hydration because an index exists. Indexed reads before peer-process vicinity hydration would be fast and stale.
Move the workload into Fleet. Mailbox graph semantics and authorization remain Memory Core-owned.
Raise the timeout. More caller patience does not remove server work.
Call the correlation causation. The reset overlap motivates priority; the current-source nested scans independently establish this defect.
Live latest-open sweep: checked the newest 20 open issues created-descending immediately before filing; no equivalent ticket existed.
Recent A2A sweep: checked the latest 30 all-state messages immediately before filing; no competing claim overlapped this MailboxService projection scope.
Exact GitHub and repository searches for list_messages performance, Fleet mailbox saturation, MailboxService edge scan, and quadratic/full-graph mailbox projection found parent #16677 and closed #16767, not an open owner.
Semantic Memory Core query for #16677 Fleet list_messages full graph scan 15 second saturation returned no relevant prior artifact. The Knowledge Base semantic leg was unavailable because the identity-bound client could not obtain a canonical identity; current GitHub and source were used as the live authority.
Context
The post-admission half of parent #16677 has a deterministic source-level work amplifier even after closed
#16767removed the non-converging WAL-repair floor.On the canonical one-CPU plane, fixed-window telemetry measured Fleet-bound
list_messagescalls averaging 10,642 ms and reaching 21,606 ms. In a separate fixed ingress window, 42/47 Memory Core POST reset intervals overlapped a Fleetlist_messagesexecution interval. That overlap is correlation, not request-identity proof; this ticket owns only the independently verified list-projection cost below.At current
dev@82470ab084:fleetA2AActivityAdapter.mjsrequests{box:'all', status:'all', limit:50};MailboxService.listMessages()scans every cached edge to find candidates, usesmessages.find()for de-duplication, then scans every cached edge again for every matched message;getRelatedTicketsForMessage()adds another full-edge traversal for every projected message;The incident store measured 93,765 edges and 10,704 MESSAGE nodes.
limit:50bounds the returned page, but the repeated projection work scales with the whole edge cache multiplied by the number of matches.The Problem
MailboxService.listMessages()has an effectively quadratic projection phase.For
Ecached edges andMmatched messages, the current implementation performs:E-edge candidate scan;O(M²)linear-array duplicate check;E-edge metadata scan per matched message;E-edge related-ticket scan per matched message;Pagination cannot protect the server because
totalCountintentionally describes all filter matches and the slice happens last. The contract still needs the full match count; it does not need to rediscover the same edges for each row.Closed
#16767is adjacent, not duplicate: it made graph/WAL repair convergent. This leaf starts after that repair and removes repeated graph projection scans from the ordinary list.The Architectural Reality
The scoped structure map establishes the existing owners:
ai/services/memory-core/MailboxService.mjsowns mailbox authorization, graph hydration, filtering, projection, completeness, and pagination;ai/services/fleet/fleetA2AActivityAdapter.mjsis a consumer of the existinglist_messagescontract;GraphService.db.getAdjacentNodes()is still required to hydrate peer-process writes before projection;ai/graph/Store.mjsalready maintains source- and target-keyed secondary indexes, andDatabase.mjsconstructs the edge store with both indexes;ai/mcp/server/memory-core/openapi.yaml.The fix belongs in the existing mailbox projection seam and must consume the existing graph indexes. It does not require a new MCP tool, a Fleet-private mailbox, a polling change, a second per-call edge index, or a GraphService-wide index.
The Fix
Replace the cache-wide and per-message scans with indexed graph reads:
db.edges.getByIndex('target', variant)for the caller's storage variants andAGENT:*where the box requires them.Set, preserving the current direct, outbox, current-broadcast, and legacy-broadcast visibility rules.SENT_BY,SENT_TO,PART_OF_THREAD,TAGGED_CONCEPT,REFERENCES_TICKET, andDELIVERED_TOedges remain visible.db.edges.getByIndex('source', messageId). Use that indexed slice for sender/recipient/thread/tag/ticket and broadcast-delivery resolution.totalCount,truncated,nextOffset, and the existing public envelope.Contract Ledger
SettotalCount,truncated,nextOffset,limit, andoffsetexactlyfleetA2AActivityAdapterlist_messagesrequest and response shapedb.edges.itemsDecision Record impact
none— this enforces the existing mailbox read contract inside its current owner. It changes neither service ownership nor public protocol semantics.Acceptance Criteria
Implementation receipt: PR #16962 at
e25f972812; exact current-devmailbox + Fleet slice 168/168 green.listMessages()no longer callsmessages.find()to de-duplicate candidate rows.db.edges.itemstraversal makes the named work-bound fixture red.totalCount,truncated,nextOffset,limit, andoffsetremain correct across at least three pages.npx playwright testinvocation is used.Out of Scope
totalCountwith an estimate.#16767.Avoided Traps
totalCount.Related
Parent: #16677
Successor to #16767
Related: #16748
Duplicate and Collision Sweep
list_messages performance,Fleet mailbox saturation,MailboxService edge scan, and quadratic/full-graph mailbox projection found parent #16677 and closed #16767, not an open owner.#16677 Fleet list_messages full graph scan 15 second saturationreturned no relevant prior artifact. The Knowledge Base semantic leg was unavailable because the identity-bound client could not obtain a canonical identity; current GitHub and source were used as the live authority.Origin Session ID: 019fe5e8-b963-7e93-8762-c8e4af16bdec
Retrieval Hint: "#16677 Fleet list_messages 93,765 edges graph Store source target index pagination after projection"