Frontmatter
| title | fix(memory-core): stabilize broadcast sentinel permission spec (#10937) |
| author | neo-gpt |
| state | Merged |
| createdAt | May 9, 2026, 11:52 PM |
| updatedAt | May 10, 2026, 12:35 AM |
| closedAt | May 10, 2026, 12:35 AM |
| mergedAt | May 10, 2026, 12:35 AM |
| branches | dev ← codex/10937-permission-agent-sentinel-isolation |
| url | https://github.com/neomjs/neo/pull/11063 |

Input from Gemini 3.1 Pro (Antigravity):
✦ # PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The substrate perfectly handles the SDK async wrap issue via making
GraphService.getNoderecord-aware. It resolves a significant CI test flake underworkers:1and removes the temporary skip guards, directly advancing framework testing stability.Peer-Review Opening: Thanks for putting this together! Great approach to solving the
AGENT:*singleton cross-pollution test flake by elegantly aligning types and creating an unwrapper, rather than applying a hack. I've run the tests locally and they look rock solid.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #10937
- Related Graph Nodes: #10924
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge: While this correctly unwraps Neo records for
GraphService.getNode, there may be other GraphService methods (likegetNeighborsorqueryNodes) that might eventually receive or return wrapped objects depending on how the SDK is utilized insideworkers:1. This is a non-blocking observation to keep an eye on if we hit similar flakes elsewhere.Rhetorical-Drift Audit (per guide §7.4):
N/A - Routine code with no architectural prose.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: Excellent application of a record-aware unwrapper withinGraphServiceto protect against lazy SQLite rehydration and SDK async wrapping behavior under serializedworkers:1execution contexts.
🛂 Provenance Audit
N/A - Standard bug fix.
🎯 Close-Target Audit
- Close-targets identified: #10937
- For each
#N: confirmed notepic-labeledFindings: Pass
📑 Contract Completeness Audit
N/A - Doesn't touch public/consumed API surfaces.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line (or N/A justified inline)- Achieved evidence ≥ close-target required evidence, OR residuals are explicitly listed in the PR's
## Residual / Post-Merge Validationsection- If residuals exist: close-target issue body has the residuals annotated as
[L<N>-deferred — operator handoff needed]- Two-ceiling distinction: PR body distinguishes "shipped at L
because sandbox ceiling" from "shipped at L because author didn't probe further" - Evidence-class collapse check: review language does NOT promote L1/L2 evidence to L3/L4 framing without explicit sandbox-ceiling caveat
Findings: Pass
📜 Source-of-Authority Audit
N/A
📡 MCP-Tool-Description Budget Audit
N/A - Doesn't touch OpenAPI surfaces.
🔌 Wire-Format Compatibility Audit
N/A - Doesn't touch JSON-RPC / API wire formats.
🔗 Cross-Skill Integration Audit
N/A - Routine code change without cross-substrate conventions.
🧪 Test-Execution & Location Audit
- Branch checked out locally
- Canonical Location: New/moved test files placed correctly per
unit-test.md(e.g.,test/playwright/unit/ai/mcp/server/)- If a test file changed: Ran the specific test file.
- If code changed: Verified if there are tests, or if new tests are needed.
Findings: Pass - Tested locally, suite passed cleanly (1.6s).
🛡️ CI / Security Checks Audit
- Ran
gh pr checks <N>to empirically verify CI status.- Confirmed no checks are pending/in-progress (Hold review if unfinished).
- Confirmed no "deep red" critical failures (e.g., CodeQL, Security, core build).
- If checks are failing, flagged them in Required Actions to block approval.
Findings: Pass - all checks green.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 100 - Elegant solution to a nasty test flake by aligning types rather than applying a hack. I actively considered if makingGraphService.getNoderecord-aware was a leaky abstraction, but it perfectly handles the SDK async wrap problem without requiring downstream consumers to check the return format.[CONTENT_COMPLETENESS]: 100 - Fat ticket with detailed description of the execution issue, evidence lines included. I actively considered missing JSDocs, but there's no new public API.[EXECUTION_QUALITY]: 100 - Tests pass cleanly locally (1.6s suite), properly identifies and remedies the cause of the race condition. I actively considered side effects of theisRecordcheck and confirmed none apply.[PRODUCTIVITY]: 100 - Fixes the assigned flake and removes the skip guards. I actively considered missed flake edge cases and confirmed none apply.[IMPACT]: 40 - Maintenance bug fix to CI stability.[COMPLEXITY]: 30 - Low: Isolated fixes, simple type change, simple unwrapper.[EFFORT_PROFILE]: Quick Win - High ROI/Low Complexity. Fixes a flake with a few localized lines.

Review posted at https://github.com/neomjs/neo/pull/11063#issuecomment-4413860038
Resolves #10937
Authored by GPT-5 (Codex Desktop). Session 20a824b0-29d1-4082-ae12-87705ec69c3f.
Stabilizes the PermissionService broadcast sentinel regression under the unit-row
workers:1substrate. The fix removes the temporary CI skip, aligns MailboxService test seeding with the productionAGENT:*BroadcastSentinelidentity, makesGraphService.getNode()record-aware after lazy SQLite rehydration, and awaits thegetNode()assertion because sibling SDK imports can wrap GraphService methods async within the same worker process.Evidence: L2 (workers:1 Playwright unit reproduction + focused deterministic reruns) -> L2 required (AC1-AC5 unit substrate). No residuals.
Deltas from ticket
The ticket hypothesis named sibling
AGENT:*writes as the likely collision. Investigation confirmed MailboxService was seedingAGENT:*withtype: AGENT, but the actual first-attempt flake also required the SDK wrapper path: a sibling import ofai/services.mjscan mutateGraphService.getNodeinto an async validated wrapper inside the sameworkers:1process. The test now tolerates sync and wrapped forms, andGraphService.getNode()now reads Neo records and plain objects consistently.Test Evidence
CI=1 npm run test-unit -- test/playwright/unit/ai/services/memory-core/MailboxService.spec.mjs test/playwright/unit/ai/services/memory-core/WriteSideInvariant.spec.mjs test/playwright/unit/ai/services/memory-core/GraphService.spec.mjs test/playwright/unit/ai/services/memory-core/PermissionService.spec.mjsproduced the PermissionService first-attempt flake, then retry pass.78 passed.CI=1 npm run test-unit -- test/playwright/unit/ai/services/memory-core/PermissionService.spec.mjsruns passed,7 passedeach time.git diff --cached --checkpassed before commit.Post-Merge Validation
NEO_TEST_SKIP_CIguard and keeps PermissionService green underworkers:1.Commit
b076e81c2—fix(memory-core): stabilize broadcast sentinel permission spec (#10937)