Resolves #17056
Filesystem projection now verifies authoritative CONTAINS topology without replaying the reinforcing linkNodes() operation. A new GraphService structural-edge primitive creates missing relations, leaves equivalent persisted rows byte-stable, surfaces property or tenancy drift, and reconciles stale RAM state against SQLite before a replacement write. Filesystem receipts now separate created, verified, drifted, culled, and unavailable edges from path-node upserts.
Evidence: L2 (real SQLite-backed production-entrypoint matrix; repository-wide baseline classified separately) → L2 required (all close-target ACs are internal graph mutation semantics exercised in-process). No residuals.
Deltas from ticket
- Kept
linkNodes() byte-for-byte unchanged so intentional learning/reinforcement remains intact.
- Made the structural operation own its transaction and fail loud under an outer Graph Database transaction; a partial transaction overlay would misclassify queued node and edge mutations.
- Bound verification to SQLite tenancy authority and preserved historically reinforced weights.
- Added stale-cache reconciliation for peer-deleted edges so SQLite and RAM converge on the same replacement identity.
- Named the node counter
pathNodesUpserted because the existing project-root anchor remains an unconditional separate upsert.
Test Evidence
npm run test-unit -- test/playwright/unit/ai/services/memory-core/GraphService.spec.mjs test/playwright/unit/ai/services/memory-core/FileSystemIngestor.spec.mjs — 57/57 passed at rebased head be0795fbc0.
npm run test-unit -- --reporter=dot — pre-rebase full-suite baseline at fdc967a778: 13,015 passed, 39 failed, 11 skipped, 59 did not run. Playwright's last-run ledger placed all failures in 22 untouched files; neither modified spec was present.
npx playwright test -c test/playwright/playwright.config.unit.mjs --last-failed --reporter=dot — isolated baseline rerun: 6 recovered, 35 remained across 19 untouched deploy/harness/health files. Observed failures include local .neo-ai-data permission/artifact contamination and live MCP-health dependencies, not the structural-edge path. Exact-head GitHub CI owns the repository-wide merge gate.
npm run agent-preflight -- --change-class capability --commit-subject "feat(ai): make structural edges write-idempotent (#17056)" ai/services/memory-core/GraphService.mjs ai/services/memory-core/FileSystemIngestor.mjs test/playwright/unit/ai/services/memory-core/GraphService.spec.mjs test/playwright/unit/ai/services/memory-core/FileSystemIngestor.spec.mjs — passed.
FileSystemIngestor: real SQLite first/unchanged/add sync matrix proves full receipts, byte-stable prior edges, and zero unchanged edge GraphLog writes.
GraphService: real SQLite matrix proves create/verify/drift, RLS mismatch, intentional reinforcement, outer-transaction refusal, and stale-RAM peer-delete recovery.
Post-Merge Validation
- None required beyond ordinary CI; no operator-only surface is involved.
Evolution
The implementation initially tried to share more of the reinforcing path and to support surrounding graph transactions. Adversarial source review showed both directions widened authority: the shared path could still reinforce after a race, while partial transaction support could not represent queued node and edge removals. The final shape is additive and narrower: direct creation, SQLite-authoritative verification, one owned transaction, and explicit fail-loud behavior outside that contract.
🪡
Authored by Emmy (GPT-5.6 Sol Ultra, Codex). Session 019fe0b3-53bc-7ef2-8665-41a0ef3f7b62.
Resolves #17056
Filesystem projection now verifies authoritative
CONTAINStopology without replaying the reinforcinglinkNodes()operation. A new GraphService structural-edge primitive creates missing relations, leaves equivalent persisted rows byte-stable, surfaces property or tenancy drift, and reconciles stale RAM state against SQLite before a replacement write. Filesystem receipts now separate created, verified, drifted, culled, and unavailable edges from path-node upserts.Evidence: L2 (real SQLite-backed production-entrypoint matrix; repository-wide baseline classified separately) → L2 required (all close-target ACs are internal graph mutation semantics exercised in-process). No residuals.
Deltas from ticket
linkNodes()byte-for-byte unchanged so intentional learning/reinforcement remains intact.pathNodesUpsertedbecause the existing project-root anchor remains an unconditional separate upsert.Test Evidence
npm run test-unit -- test/playwright/unit/ai/services/memory-core/GraphService.spec.mjs test/playwright/unit/ai/services/memory-core/FileSystemIngestor.spec.mjs— 57/57 passed at rebased headbe0795fbc0.npm run test-unit -- --reporter=dot— pre-rebase full-suite baseline atfdc967a778: 13,015 passed, 39 failed, 11 skipped, 59 did not run. Playwright's last-run ledger placed all failures in 22 untouched files; neither modified spec was present.npx playwright test -c test/playwright/playwright.config.unit.mjs --last-failed --reporter=dot— isolated baseline rerun: 6 recovered, 35 remained across 19 untouched deploy/harness/health files. Observed failures include local.neo-ai-datapermission/artifact contamination and live MCP-health dependencies, not the structural-edge path. Exact-head GitHub CI owns the repository-wide merge gate.npm run agent-preflight -- --change-class capability --commit-subject "feat(ai): make structural edges write-idempotent (#17056)" ai/services/memory-core/GraphService.mjs ai/services/memory-core/FileSystemIngestor.mjs test/playwright/unit/ai/services/memory-core/GraphService.spec.mjs test/playwright/unit/ai/services/memory-core/FileSystemIngestor.spec.mjs— passed.FileSystemIngestor: real SQLite first/unchanged/add sync matrix proves full receipts, byte-stable prior edges, and zero unchanged edge GraphLog writes.GraphService: real SQLite matrix proves create/verify/drift, RLS mismatch, intentional reinforcement, outer-transaction refusal, and stale-RAM peer-delete recovery.Post-Merge Validation
Evolution
The implementation initially tried to share more of the reinforcing path and to support surrounding graph transactions. Adversarial source review showed both directions widened authority: the shared path could still reinforce after a race, while partial transaction support could not represent queued node and edge removals. The final shape is additive and narrower: direct creation, SQLite-authoritative verification, one owned transaction, and explicit fail-loud behavior outside that contract.
🪡
Authored by Emmy (GPT-5.6 Sol Ultra, Codex). Session 019fe0b3-53bc-7ef2-8665-41a0ef3f7b62.