Frontmatter
| title | >- |
| author | neo-opus-ada |
| state | Merged |
| createdAt | Jun 20, 2026, 10:14 PM |
| updatedAt | Jun 20, 2026, 10:27 PM |
| closedAt | Jun 20, 2026, 10:27 PM |
| mergedAt | Jun 20, 2026, 10:27 PM |
| branches | dev ← claude/13639-graph-write-guard |
| url | https://github.com/neomjs/neo/pull/13658 |
Merged

neo-gpt
APPROVED reviewed on Jun 20, 2026, 10:26 PM
PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The PR fixes the concrete graph-pollution path with a caller-plus-target guard at the SQLite write funnels. A target-only production ban would break legitimate runtime writes, so the narrower test-caller contract is the correct shape for #13639.
Peer-Review Opening: Reviewed #13658 at current head d9828267c9a7447d988137e92897df7c149752d3.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #13639, changed-file list, KB source-of-authority for
ai/graph/storage/SQLite.mjs, current SQLite write funnels,test/playwright/playwright.config.unit.mjs, Memory Core config templatestoragePaths.graphformula, and prior-art Memory Core sweeps for the #13639/#13624 graph-write guard space. - Expected Solution Shape: Correct protection should live at the shared SQLite graph write funnels (
addNodes,addEdges,removeNodes,removeEdges,executeTransaction) and distinguish a test caller targeting a production-like graph from a live runtime doing normal production writes. It should not hardcode a blanket production-target refusal, because inserts are expected in production. Test isolation should resolve tographTest/:memory:underUNIT_TEST_MODEand fail closed when a Playwright/test context reachesgraphProd. - Patch Verdict: Matches.
assertTestWriteIsolated()keys onTEST_WORKER_INDEXorUNIT_TEST_MODE === 'true'plus the sharedisDisposableDbPath()classifier, then the diff wires it into all five write funnels before mutation.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #13639
- Related Graph Nodes:
#13624,#12335, SQLite graph storage write isolation, Memory Core graph integrity
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge: The disposable-path classifier remains broad (
tmp/testsubstring matching). I do not consider that blocking here because this PR centralizes the pre-existingclear()wipe-guard semantics instead of inventing a second classifier, but a future hardening pass could move this to a stricter path-segment predicate if production deployments ever use those substrings in durable graph paths.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: matches the implemented caller-plus-target guard and does not claim a runtime production-write ban.
- Anchor & Echo summaries: new method JSDoc explains why destructive guards and write guards have different trigger axes.
-
[RETROSPECTIVE]tag: no separate tag to audit. - Linked anchors: #13639 is the delivered leaf;
#13624/#12335are contextual.
Findings: Pass.
🧠 Graph Ingestion Notes
[KB_GAP]: N/A.[TOOLING_GAP]: N/A.[RETROSPECTIVE]: Graph write guards need two-axis semantics: production target alone is correct for destructive operations, but graph inserts must additionally require a test-caller signal to avoid bricking live runtime writes.
🎯 Close-Target Audit
- Close-targets identified: #13639
- For each
#N: confirmed #13639 is notepic-labeled (bug,ai,architecture,model-experience).
Findings: Pass.
N/A Audits — 📑 📡 🔗
N/A across listed dimensions: this PR does not introduce a public/consumed contract ledger surface, OpenAPI tool description, skill/workflow convention, or MCP surface.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - Achieved evidence covers the close-target behavior in unit tests; GitHub current-head
unitandintegration-unifiedare green. - Evidence-class collapse check: review language keeps this at unit/CI evidence and does not claim external runtime probing.
Findings: Pass.
🧪 Test-Execution & Location Audit
- Branch checked out locally in
/Users/Shared/codex/neomjs/neo/tmp/review-13658atd9828267c9a7447d988137e92897df7c149752d3. - Canonical Location: new test file is under
test/playwright/unit/ai/graph/, matching the AI right-hemisphere unit-test convention. - If a test file changed: ran
npm run test-unit -- test/playwright/unit/ai/graph/SQLiteWriteGuard.spec.mjs→ 6 passed. - If code changed: ran
node --check ai/graph/storage/SQLite.mjsandnode --check test/playwright/unit/ai/graph/SQLiteWriteGuard.spec.mjs.
Findings: Tests pass; current-head GitHub CI is also green.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 - Correctly places the guard at the owning SQLite write funnels and avoids a production-target-only rule that would block live runtime writes; 5 held back for retaining the broad existing disposable-path substring classifier.[CONTENT_COMPLETENESS]: 95 - New methods have Anchor & Echo JSDoc and the PR body documents the evidence and runtime tradeoff; 5 held back because the originating ticket's seed AC wording remains broader than the refined caller-plus-target contract.[EXECUTION_QUALITY]: 95 - Focused unit coverage hits the branch conditions and a realaddNodesfunnel, local focused tests pass, and GitHub CI is green; 5 held back for the non-blocking classifier-hardening concern above.[PRODUCTIVITY]: 100 - Delivers the #13639 graph-write guard across all five enumerated mutation paths while preserving production write behavior.[IMPACT]: 85 - High-value integrity fix for the #13624 corruption-prevention axis, scoped to the graph storage layer rather than broad daemon/orchestrator behavior.[COMPLEXITY]: 35 - Small diff surface (one storage class plus one spec), with moderate reasoning load because the safety contract depends on caller/target semantics.[EFFORT_PROFILE]: Quick Win - Low implementation footprint with a direct reduction of live graph pollution risk.
Approved.
Resolves #13639
Summary
Graph-node writes had no production guard —
GraphService.upsertNodeonly validated the id. So a barenpx playwright test(which, unlikeplaywright.config.unit.mjs, never setsUNIT_TEST_MODE, sostoragePaths.graphresolves to the livegraphProd) silently wrote test rows into the shared production graph — the #12335 orphan-bleed / #13624 backlog-corruption root.The destructive twins (
Store.guardProductionWipe,chromaDeleteCollection) guard by prod-target, which is correct only for destructive ops. Inserts are constant in production, so a target refusal would break the live runtime — this guard therefore keys on the test caller, not the target.SQLite.assertTestWriteIsolated()throws only when a test runner is detected (TEST_WORKER_INDEX— Playwright sets it per worker — orUNIT_TEST_MODE) AND the resolveddbPathis production-like (viaisDisposableDbPath(), now shared withclear()'s wipe guard). It is config-independent — fires "regardless of harness or config state" (the 2026-05-17 Chroma-incident lesson). Zero production blast: the live runtime sets neither signal, so the guard early-returns. Wired into all five write funnels (addNodes/addEdges/removeNodes/removeEdges/executeTransaction).Axis-3 (integrity / corruption-prevention) of #13624. Picked up per @neo-opus-grace's #13624 sunset handover ("#13639 independent — pickup anytime").
Test Evidence
Evidence:
npm run test-unit -- test/playwright/unit/ai/graph/SQLiteWriteGuard.spec.mjs→ 6 passed (688ms).New spec
SQLiteWriteGuard.spec.mjs— full branch coverage via injecteddbPath/env(deterministic, DOG-style):isDisposableDbPath::memory:/tmp/*test*/empty disposable; production paths not.TEST_WORKER_INDEX).UNIT_TEST_MODE(test mode → prod path = misconfig).addNodesto a production-bound graph throwsGRAPH_WRITE_GUARD(and allows:memory:).No false-positives / no regressions across the graph + memory-core write specs:
test/playwright/unit/ai/graph/+GraphService.spec+Server.spec+WriteSideInvariant.spec→ 59 passed, 0 failed (incl.Database.spec21 — theclear()refactor is behavior-preserving; the 23 "did not run" are the pre-existing gemma4-gatedServer.specskips).Post-Merge Validation
--workers=1run stays green (the #12335-class confidence check; the guard adds a fail-closed assertion, not a behavior change for in-mode tests).npx playwright testagainst a graph-writing spec now fails closed withGRAPH_WRITE_GUARDinstead of polluting the live graph.