LearnNewsExamplesServices
Frontmatter
id11417
titleReject or resolve invalid to: in add_message instead of silent null-storage
stateClosed
labels
bugdocumentationaiarchitecturemodel-experience
assigneesneo-opus-4-7
createdAtMay 15, 2026, 1:38 PM
updatedAtMay 19, 2026, 8:36 AM
githubUrlhttps://github.com/neomjs/neo/issues/11417
authorneo-opus-4-7
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 19, 2026, 8:36 AM

Reject or resolve invalid to: in add_message instead of silent null-storage

Closedbugdocumentationaiarchitecturemodel-experience
neo-opus-4-7
neo-opus-4-7 commented on May 15, 2026, 1:38 PM

Context

Empirical regression surfaced 2026-05-15: ~17 A2A messages this session sent with to: "AGENT:<family>/<model>" (alias format) silently stored as to: null in DB. Result:

  • SENT_TO edge target = null → wake-emit guard fails at WakeSubscriptionService line 684 → recipient never wakes
  • get_message returns Unauthorized: message <id> was not sent to or from <recipient> per Phase 3 read-path → recipient cannot read the message at all
  • Mailbox is populated with orphan records inaccessible to the intended recipient

Empirical confirmation: probe MESSAGE:85c58e46-35cb-444e-8b5c-eaac785920ef sent with canonical to: "@neo-gpt" → correctly stored + wake fired + readable. Same content sent with to: "AGENT:openai/gpt"to: null orphan. GPT's [Unauthorized] denial confirmed via MESSAGE:aaa17ecc-d97c-434c-990e-62587327062d.

The Problem

add_message accepts any to: string and silently stores null if the value doesn't resolve to a registered AgentIdentity node. This produces:

  1. Orphan messages invisible to the intended recipient
  2. Cross-skill silent failure/lead-role, /peer-role, /pull-request, /pr-review all rely on A2A handoffs; alias-format misuse silently breaks the coordination loop
  3. Post-compaction failure mode — agents re-deriving the to: format from the tool schema after context loss have no concrete example except 'AGENT:*' (broadcast); confabulating AGENT:<family>/<model> from that single example is the empirically-observed extrapolation

The Architectural Reality

Three substrate surfaces involved:

  • ai/mcp/server/memory-core/openapi.yaml line ~988: to: field description = "Recipient Agent Identity Node ID, or 'AGENT:' for broadcast"* — no concrete canonical example, only the broadcast special case
  • ai/services/memory-core/MailboxService.mjs: identity-resolution path that produces to: null instead of rejecting OR resolving aliases
  • .agents/skills/** (lead-role, peer-role, pull-request, etc.): zero worked-examples demonstrating concrete to: value patterns

Per AGENTS.md §13 substrate-accretion-defense, the framework-level surfaces (openapi.yaml + skill substrate) must remain team-agnostic — they ship with neo to forks, client projects, and downstream deployments where the agent identities are NOT @neo-opus-4-7 / @neo-gemini-3-1-pro / @neo-gpt.

The Fix

Three independent improvements; each lands independently.

1. openapi.yaml description (team-agnostic + names the failure mode)

to:
  type: string
  description: "Recipient Agent Identity Node ID (e.g., `@some-agent` — must match
    a registered AgentIdentity graph node), or `'AGENT:*'` for broadcast. Aliases like
    `AGENT:<family>/<model>` are NOT resolved and currently silently store as
    `to: null`, producing orphan messages inaccessible to the recipient."

2. Skill substrate worked-examples

Add concrete A2A code-block examples to lead-role-mode.md + peer-role-mode.md + review-response-protocol.md using team-agnostic placeholders (@<peer-agent> / @reviewer-agent / @<your-agent>).

3. MailboxService failure-mode (deepest fix; catches all future agents)

Replace silent to: null storage with one of:

  • Reject with explicit error: "Unrecognized 'to' format: '<value>'. Expected '@<identity>' canonical form or 'AGENT:*' for broadcast."
  • Resolve known alias patterns (e.g., AGENT:<family>/<model> → matching @<identity>) when an unambiguous mapping exists
  • Both: resolve known aliases; reject unknown formats

Option C (both) is highest-value: tolerant of common alias confabulation while still fail-loud on truly invalid input.

Acceptance Criteria

  • openapi.yaml add_message.to description updated (team-agnostic + failure-mode named)
  • At least 3 skill files have concrete A2A worked-examples with team-agnostic placeholders
  • add_message with unresolvable to: either rejects with clear error OR resolves to canonical identity (no silent to: null)
  • Existing alias-format messages (orphan records) handled per data-migration plan (audit + reject OR rewrite)
  • Spec coverage for the new behavior (reject path + resolve path)

Out of Scope

  • Implementing a full alias registry beyond simple AGENT:<family>/<model> resolution
  • Migration of historical orphan messages (separate consideration; may want forensic preservation)
  • Cross-harness wake-delivery infrastructure (different substrate concern)

Avoided Traps

  • Hardcoding @neo-X identities in framework surfaces — neo is forkable; client projects + downstream deployments have different identities. Framework-level docs MUST be team-agnostic.
  • Documentation-only fix — docs alone don't catch confabulation after context-compaction. The MailboxService-level guard is the only lasting prevention.
  • Symbolic DSL conversion — discussion #10732 + #11341 already rejected schema-form conversion. The to: description stays prose-shaped with concrete examples.

Related

  • Empirical anchor: ~17 alias-format messages this session producing orphan records
  • Phase 3 read-path validation: catches the issue at get_message but only after the orphan is already created
  • Sibling: #11406 (retired-primitive grep-fail CI check) — both belong to mechanical-enforcement-layer-for-discipline-only-substrate-guards family

Origin Session

Origin Session ID: 656c0935-0b3e-4b06-9b14-548524275859

Retrieval Hint

Search for add_message to null orphan alias canonical identity wake-emit.

tobiu referenced in commit 3748019 - "fix(memory-core/mailbox): reject or resolve invalid to: in add_message instead of silent null-storage (#11417) (#11615) on May 19, 2026, 8:36 AM
tobiu closed this issue on May 19, 2026, 8:36 AM