LearnNewsExamplesServices
Frontmatter
id16098
titlePin mailbox identity comparison as an authorization contract
stateClosed
labels
documentationenhancementaitestingsecurity
assigneesneo-gpt
createdAtJul 28, 2026, 5:49 PM
updatedAtJul 28, 2026, 6:43 PM
githubUrlhttps://github.com/neomjs/neo/issues/16098
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 28, 2026, 6:43 PM

Pin mailbox identity comparison as an authorization contract

Closed Backlog/active-chunk-10 documentationenhancementaitestingsecurity
neo-gpt
neo-gpt commented on Jul 28, 2026, 5:49 PM

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, 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

  1. 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.
  2. Update sameMailboxIdentity() JSDoc at the production seam so a caller audit sees that normalization changes affect authorization, visibility, retraction, and Task transitions.
  3. 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.
  4. 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.
  • Editing the already-merged PR #16094 body.
  • Extending the read-state diagnostic contract.
  • 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.

Related

Duplicate Sweep

  • Semantic ticket/Memory Core sweep: no equivalent authorization-contract follow-up found; nearest historical authority is closed #15027.
  • Exact source/content sweep: no existing ticket or Discussion names normalizeMailboxIdentityForComparison or the post-#16094 dual-audience dependency.
  • Live latest-open and A2A in-flight sweeps will be refreshed immediately before creation.

Origin Session ID: 019fa904-9d8c-7f12-94fe-346ae8e54046

Retrieval Hint: PR #16094 mailbox identity normalizer production authorization KB_GAP sameMailboxIdentity

Authored by Euclid (@neo-gpt, Codex Desktop).

tobiu referenced in commit a1b2af7 - "chore(ai): pin mailbox authorization contract (#16098) (#16099)" on Jul 28, 2026, 6:43 PM
tobiu closed this issue on Jul 28, 2026, 6:43 PM