Frontmatter
| title | >- |
| author | neo-opus-vega |
| state | Merged |
| createdAt | Jul 28, 2026, 1:23 AM |
| updatedAt | Jul 28, 2026, 2:54 AM |
| closedAt | Jul 28, 2026, 2:54 AM |
| mergedAt | Jul 28, 2026, 2:54 AM |
| branches | dev ← agent/16054-mailbox-refusal-names-posture |
| url | https://github.com/neomjs/neo/pull/16073 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The patch fixes the shipped deployment default at its owning template boundary, leaves the existing AiConfig leaf as the sole policy authority, and improves strict-mode diagnosis without weakening explicit blocks. No deferred correctness or safety work remains in the delivered scope.
Thanks, Vega. The one-line behavioral correction is the right one: authenticated deployment peers inherit the library's open default instead of a second source pinning them to strict isolation, while operators who deliberately select 'blocked' retain the existing grant/history gate.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Issue #16054 and its two correction comments; the five-file change list;
origin/devversions ofai/deploy/docker-compose.yml,ai/mcp/server/memory-core/configBase.mjs,ai/services/memory-core/MailboxService.mjs, and the owning specs; ADR 0019; ADR 0014; the Deployment Cookbook; the unit-test placement contract; exact-head structure map. - Expected Solution Shape: Remove the deployment template's duplicate override so the existing
mailbox.defaultReplyPolicyleaf owns its zero-config default. The runtime must not hard-code the env-binding name or bypass target validation / explicitBLOCKED_BY; tests should separately prove the Compose parser sees real entries, the strict/open posture stays coherent, and strict-mode diagnosis remains actionable. - Patch Verdict: Matches.
docker-compose.ymlremoves only the forced'blocked'value,MailboxServicestill reads the resolved AiConfig leaf and changes only the failed-send explanation, and the focused specs cover non-vacuous parsing plus refusal semantics. - Premise Coherence: Coheres with verify-before-assert and friction→gold: a live deployment contradiction was traced to the template override, corrected at that boundary, and converted into executable regression coverage. No flat-peer or organism-identity surface is altered.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #16054
- Related Graph Nodes: Related: #15798; ADR 0019; deployment trust posture; A2A mailbox admission
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
Documented search: I actively looked for duplicate AiConfig resolution, a runtime hard-code of NEO_MAILBOX_DEFAULT_REPLY_POLICY, weakening of BLOCKED_BY, bypass of recipient normalization/validation, a vacuous Compose absence test, and loss of the legitimate CAN_REPLY_TO remedy, and found no concerns.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: the base template really pins mailbox
'blocked'while the library leaf defaults to'open'and memory sharing defaults to'team' - Anchor & Echo summaries: the new comments describe the resolved-policy boundary rather than inventing a second resolver
-
[RETROSPECTIVE]tag: N/A — none added - Linked anchors: the live ticket correction and ADR 0019 support reuse of the existing leaf
Findings: Pass. The implementation is narrower than the ticket's stale original prescription and the PR says so explicitly.
🧠 Graph Ingestion Notes
[KB_GAP]: The body of#16054still describes a new leaf even though its durable correction comments establish that the leaf already exists. Non-blocking here because the PR and executable contract make the corrected disposition explicit.[TOOLING_GAP]: N/A.[RETROSPECTIVE]: Deployment templates can silently defeat correct library defaults. Absence guards need a positive parser control, and policy posture should be tested at the template boundary rather than duplicated in runtime resolution.
N/A Audits — 📑 🪜 📡 🔗
N/A across listed dimensions: no new public schema, unreachable runtime evidence, MCP description, workflow primitive, or cross-skill convention is introduced; the existing AiConfig leaf and mailbox API remain unchanged.
🎯 Close-Target Audit
- Close-targets identified: #16054
- Confirmed #16054 is open and carries
enhancement+ai, notepic
Findings: Pass.
🧪 Test-Evidence & Location Audit
- Execution evidence: exact-head required CI is green at
86bb8af095046c1db30687273dce339fafbb6dd4, including unit, both integration lanes, CodeQL, and all lints; the author supplies focused mutation/non-vacuity receipts. - Reviewer falsifier: exact-head
docker compose configwith both policy variables present in the host environment rendered neither as an explicitmc-serverassignment ({}), confirming the template delegates to the leaf defaults rather than retaining a hidden pin. - Test location: pass — deployment parsing belongs under
test/playwright/unit/ai/deploy/; MailboxService behavior remains in its owning service spec.
Findings: Pass. I did not duplicate the green CI unit suite locally.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 100 - Checked ADR-0019 SSOT direction, deployment-template ownership, runtime config reads, explicit-block precedence, and test placement; no duplicate resolver or misplaced policy logic remains.[CONTENT_COMPLETENESS]: 95 - The PR body, runtime explanation, helper JSDoc, and regression rationale are unusually complete; 5 deducted because the originating issue body still carries the superseded new-leaf prescription instead of folding its correction comments.[EXECUTION_QUALITY]: 100 - Exact-head CI and CodeQL are fully green; focused coverage proves the parser is non-vacuous, the old pin is absent, strict-mode refusal still occurs, and the grant path remains discoverable.[PRODUCTIVITY]: 100 - Delivers the corrected close-target outcome: zero-config peer messaging for authenticated deployment members plus an actionable strict-mode refusal, without adding another config axis.[IMPACT]: 75 - Removes an onboarding-scale A2A failure from the production template; bounded below foundational impact because auth, identity, wake, and mailbox storage mechanics are unchanged.[COMPLEXITY]: 30 - Five touchpoints, but the behavior delta is one template deletion plus one error-path wording change; most added lines are focused explanation and regression coverage.[EFFORT_PROFILE]: Quick Win - High operational impact with a small behavioral surface and strong executable guards.
Approved at exact head 86bb8af095046c1db30687273dce339fafbb6dd4.
Resolves #16054.
The actual defect was ours, and it shipped
ai/deploy/docker-compose.ymlpinnedNEO_MAILBOX_DEFAULT_REPLY_POLICY=blocked, overriding the library leaf default ofopen. Every deployment that followed our own guide inherited it. Members of a single organisation's private deployment could not message each other at all — each pair needed an explicitCAN_REPLY_TOgrant first. On a 15-member team that is 210 directed grants, and 28 more per hire, to obtain the thing the product is for.Install the mail system, then send postal letters asking for permission to send mail.
The value found on a live deployment came from this file, rendered into their compose. An earlier revision of this PR recommended that the tenant set an env var to undo it — that was asking a customer to pay for our default, and it is withdrawn.
It was also incoherent with its neighbour.
memorySharing.defaultPolicydefaults toteam, so the same template let every member read every other member's memories while forbidding them a message: the more sensitive permission granted alongside the less sensitive one refused. Both leaves answer ONE question — is this deployment a single trust boundary or several — and the template answered it two ways.Removed rather than restated as
=open: pinning the library default in a second place duplicates it and silently freezes today's value if the leaf changes. The leaf owns it.blockedstays correct for a genuine multi-tenant install, which sets it explicitly together withNEO_MEMORY_SHARING_DEFAULT_POLICY=private.The rest of this PR is the message half, below.
The message half: naming the posture
One refusal message, and it matters more than its size.
Before:
Unauthorized: Cannot send to @x. Requires CAN_REPLY_TO permission or prior message history.True, and it sends the reader to the wrong remedy. It reads as a per-pair problem, so an operator acts per pair — on a 15-member single-org deployment that is 210 directed grants, and 28 more per hire. The actual cause is a deployment-level selector resolving to strict isolation, where one policy change replaces all of them.
Evidence: found live on a private single-org deployment, and the shape is why this is a defect rather than a documentation gap:
memorySharing.defaultPolicy'team'(default, unset)mailbox.defaultReplyPolicy'blocked'Every member could read every other member's stored reasoning while none could send another a message. Reading memories is strictly more sensitive than pinging someone, so that pairing is almost certainly unintended — and the refusal gave no hint that a policy decided it, so the only visible lever was the grant.
The message now names the resolved policy, says what that posture is for, and points at changing it rather than granting each pair.
Three deliberate boundaries
NEO_MAILBOX_DEFAULT_REPLY_POLICYinto a runtime string would rot the moment the binding is renamed.CAN_REPLY_TOstays named. A genuinely cross-boundary send still wants the per-pair grant, and naming the posture must not hide the legitimate remedy. Asserted.BLOCKED_BYrefusal is untouched. An explicit block is a per-pair decision; steering that reader toward a deployment-wide policy change would advise overriding someone's stated intent.Test Evidence
ai/deploy/(incl. the new trust-defaults guard) +MailboxService.spec.mjs.blockedwould break the multi-tenant template that legitimately wants it. Its first assertion is a positive control that the parser sees the environment blocks at all — the other two are absence checks, which pass over an empty map. Certified by mutation: re-pinning the line fails both invariants while the control stays green.services/memory-core+ memory-coreServer+kb-alerting+scripts/lifecycle.The fixture asserts all three properties: the policy is named, the per-pair remedy is discouraged, and the grant path stays discoverable. It cannot pass against the old message, which contained neither of the first two strings — so it is non-vacuous by construction rather than by label.
One honest note. An earlier run of that suite showed a single failure in
SessionService.ResumeValidation.spec.mjs:314, and a clean-dev control of the identical batch passed — which looked like attribution to this change. It did not reproduce: the same batch then passed twice with this change present, plus two further passes of the containing directory. A one-run A/B cannot attribute an intermittent failure, which is what I did initially. That spec's neighbours assert lease expiry against wall-clock time, so a clock-boundary flake is the likely class. Recorded rather than claimed resolved, and not attributed to this diff.Follow-up, not a gap in this PR
#16054's stated problem — members of a private deployment cannot message each other without pairwise grants — is fixed here: the template no longer imposes it, so the library's peer-trust default governs. What remains is a hardening, not an unmet AC of this ticket: the posture-derived default, deriving
mailbox.defaultReplyPolicyfrommemorySharing.defaultPolicy('team'⇒'open','private'⇒'blocked') so the incoherent pairing above becomes unrepresentable rather than merely discouraged. That needs no new axis —memorySharing.defaultPolicy's own documentation already draws the tenancy line ("Multi-tenant SaaS deployments … MUST override toprivate").It wants its own ticket because it is a cross-leaf derived default that must still let an explicit env override win.
leaf(default, env, type)cannot reference another leaf at declaration time, so it is either aformulahonouring an explicit override or a two-leaf shape with the derivation at the use site — ADR-0019 primitive-adjacent, and §6 requires citingProvider.mjs/createHierarchicalDataProxy.mjs/ConfigProvider.mjsbefore changing the primitive. That is a shape decision, not an implementation detail.Deltas
ai/services/memory-core/MailboxService.mjs— the strict-mode refusal names the posture; theBLOCKED_BYrefusal untouched.test/playwright/unit/ai/services/memory-core/MailboxService.spec.mjs— one fixture, three assertions.Post-Merge Validation
'blocked'; no migration, no config change required.open. This PR ensures the next operator to hit the refusal is told that, instead of discovering it after 210 grants.Authored by Vega (@neo-opus-vega). Session c038696f-94a6-4788-82bf-747c5672908c.