PR #16094 merged the shared normalizeMailboxIdentityForComparison() helper introduced by #16086. The extraction correctly removed the duplicate matcher identified during PR #16085: the explicit-path diagnostic, the in-process read-state diagnostic, and MailboxService now consume one comparison rule.
Vega's exact-head review of PR #16094 verified the exported helper against the former production chain across 22 identity-shaped inputs and found zero behavioral divergences. The review also identified a narrower [KB_GAP]: the helper now serves two audiences with different change discipline, but its JSDoc describes only stored-target and diagnostic semantics. It does not state that production authorization depends on the same function.
This ticket turns that approved-review follow-up into durable code-local knowledge. The merged PR body cannot be the point-of-edit warning for future changes.
The Problem
MailboxService.sameMailboxIdentity() delegates both operands to normalizeMailboxIdentityForComparison(). Current origin/dev uses that comparison throughout production mailbox decisions, including:
explicit-block and strict CAN_REPLY_TO admission;
reachable-counterparty trust;
inbox/read/mark/archive recipient checks;
sender-only message retraction;
A2A Task originator, assignee, and transition authority.
The exported helper lives in ai/services/memory-core/helpers/mailboxReadStateClassifier.mjs, whose module summary presents it as a read-state classifier. Its own function summary says it canonicalizes stored mailbox targets, but neither location records the authorization dependency. A future editor optimizing a diagnostic spelling rule could therefore alter who may send, read, retract, or transition a Task without seeing that blast radius at the edit site.
The focused helper spec currently covers representative direct spellings, the family alias, broadcast sentinel, and one role address. Vega's review falsifier additionally exercised padded and multi-@ direct spellings, an AGENT: wrapper combined with padding, human:, @me, the empty string, and non-string values. That broader behavior was verified once in review but is not fully durable in the test matrix.
The Architectural Reality
ai/graph/normalizeAgentIdentityNodeId.mjs owns canonical direct AgentIdentity spelling.
normalizeMailboxIdentityForComparison() adds mailbox-specific preservation for AGENT:*, family/model aliases, and non-direct address kinds.
MailboxService.sameMailboxIdentity() is the production comparison seam and is intentionally private.
mailboxReadStateClassifier.mjs exports the shared normalization primitive because both the diagnostic classifier and MailboxService consume it.
#15027 established the single-canonicalizer direction; PR #16094 completed the shared mailbox comparison extraction without changing behavior.
The structure map identifies ai/services/memory-core/helpers/ as the existing owner for the pure classifier; no file is added or relocated.
The Fix
Update the helper module and normalizeMailboxIdentityForComparison() JSDoc to state that the export is shared by diagnostics and production authorization. Name the affected decision classes without implying a new public API.
Update sameMailboxIdentity() JSDoc at the production seam so a caller audit sees that normalization changes affect authorization, visibility, retraction, and Task transitions.
Expand the pure helper's normalization matrix to preserve the behavior classes already exercised during review, including direct spelling tolerance and fail-closed pass-through for non-direct or non-string inputs.
Keep production behavior and all public MCP contracts unchanged.
Contract Ledger
Target Surface
Source of Authority
Proposed Behavior
Fallback
Docs
Evidence
normalizeMailboxIdentityForComparison()
#15027 canonical identity contract; current MailboxService consumers
Preserve exact normalization behavior while declaring both diagnostic and authorization audiences
non-direct and non-string values remain unchanged
helper module + function JSDoc
expanded pure normalization matrix
MailboxService.sameMailboxIdentity()
current mailbox authorization/read/retraction/Task call sites
Continue compare-after-normalize with explicit authorization-critical status
strict equality after canonicalization, unchanged
private helper JSDoc
existing MailboxService suite
normalization regression matrix
PR #16085 Cycle-2 differential and PR #16094 exact-head review
Make the verified behavior classes durable
no production fallback added
test title/case labels
focused unit spec plus unchanged integration suite
Decision Record impact
none — this records and tests an existing production dependency. It does not change identity authority, mailbox policy, permissions, transports, or service boundaries.
Acceptance Criteria
The helper module and exported function JSDoc explicitly state that production mailbox authorization consumes the normalization rule.
sameMailboxIdentity() documents the authorization, visibility, retraction, and Task-transition blast radius of normalization changes.
The focused unit matrix covers canonical, padded, multi-@, AGENT:-wrapped, combined wrapper/padding, broadcast, family alias, role, human, @me, empty, and representative non-string inputs.
Every new expectation preserves current origin/dev behavior; no normalization branch changes.
The focused helper spec and the full MailboxService unit spec pass.
No new canonicalizer, MCP field/tool, config leaf, or production dependency is introduced.
Out of Scope
Changing mailbox identity normalization behavior.
Changing CAN_REPLY_TO, CAN_READ_INBOX_OF, block, retraction, or Task-transition policy.
General identity canonicalization outside the mailbox layer.
Avoided Traps
A standalone prose note outside the code: rejected because it would not appear where a future diagnostic-driven edit changes authorization.
Duplicating production authorization tests for every call site: rejected because existing MailboxService integration coverage already owns those behaviors; this ticket pins the shared primitive's input contract.
Exporting sameMailboxIdentity(): rejected because consumers need the shared normalizer, not a new public comparison surface.
Changing behavior while documenting it: rejected; the review already proved current behavior equivalent, and this ticket makes that proof durable.
Context
PR #16094 merged the shared
normalizeMailboxIdentityForComparison()helper introduced by #16086. The extraction correctly removed the duplicate matcher identified during PR #16085: the explicit-path diagnostic, the in-process read-state diagnostic, andMailboxServicenow consume one comparison rule.Vega's exact-head review of PR #16094 verified the exported helper against the former production chain across 22 identity-shaped inputs and found zero behavioral divergences. The review also identified a narrower
[KB_GAP]: the helper now serves two audiences with different change discipline, but its JSDoc describes only stored-target and diagnostic semantics. It does not state that production authorization depends on the same function.This ticket turns that approved-review follow-up into durable code-local knowledge. The merged PR body cannot be the point-of-edit warning for future changes.
The Problem
MailboxService.sameMailboxIdentity()delegates both operands tonormalizeMailboxIdentityForComparison(). Currentorigin/devuses that comparison throughout production mailbox decisions, including:CAN_REPLY_TOadmission;The exported helper lives in
ai/services/memory-core/helpers/mailboxReadStateClassifier.mjs, whose module summary presents it as a read-state classifier. Its own function summary says it canonicalizes stored mailbox targets, but neither location records the authorization dependency. A future editor optimizing a diagnostic spelling rule could therefore alter who may send, read, retract, or transition a Task without seeing that blast radius at the edit site.The focused helper spec currently covers representative direct spellings, the family alias, broadcast sentinel, and one role address. Vega's review falsifier additionally exercised padded and multi-
@direct spellings, anAGENT:wrapper combined with padding,human:,@me, the empty string, and non-string values. That broader behavior was verified once in review but is not fully durable in the test matrix.The Architectural Reality
ai/graph/normalizeAgentIdentityNodeId.mjsowns canonical direct AgentIdentity spelling.normalizeMailboxIdentityForComparison()adds mailbox-specific preservation forAGENT:*, family/model aliases, and non-direct address kinds.MailboxService.sameMailboxIdentity()is the production comparison seam and is intentionally private.mailboxReadStateClassifier.mjsexports the shared normalization primitive because both the diagnostic classifier andMailboxServiceconsume it.ai/services/memory-core/helpers/as the existing owner for the pure classifier; no file is added or relocated.The Fix
normalizeMailboxIdentityForComparison()JSDoc to state that the export is shared by diagnostics and production authorization. Name the affected decision classes without implying a new public API.sameMailboxIdentity()JSDoc at the production seam so a caller audit sees that normalization changes affect authorization, visibility, retraction, and Task transitions.Contract Ledger
normalizeMailboxIdentityForComparison()MailboxServiceconsumersMailboxService.sameMailboxIdentity()MailboxServicesuiteDecision Record impact
none— this records and tests an existing production dependency. It does not change identity authority, mailbox policy, permissions, transports, or service boundaries.Acceptance Criteria
sameMailboxIdentity()documents the authorization, visibility, retraction, and Task-transition blast radius of normalization changes.@,AGENT:-wrapped, combined wrapper/padding, broadcast, family alias, role, human,@me, empty, and representative non-string inputs.origin/devbehavior; no normalization branch changes.MailboxServiceunit spec pass.Out of Scope
CAN_REPLY_TO,CAN_READ_INBOX_OF, block, retraction, or Task-transition policy.Avoided Traps
MailboxServiceintegration coverage already owns those behaviors; this ticket pins the shared primitive's input contract.sameMailboxIdentity(): rejected because consumers need the shared normalizer, not a new public comparison surface.Related
Duplicate Sweep
normalizeMailboxIdentityForComparisonor the post-#16094 dual-audience dependency.Origin Session ID: 019fa904-9d8c-7f12-94fe-346ae8e54046
Retrieval Hint:
PR #16094 mailbox identity normalizer production authorization KB_GAP sameMailboxIdentityAuthored by Euclid (@neo-gpt, Codex Desktop).