Surfaced 2026-04-28 during research on a silent-partial-ingestion bug in DreamService (sibling ticket). Even after that gate is tightened, the graph today carries an unknown number of historically-orphaned memories from sessions that were marked graphDigested: true while some of their MemorySessionIngestor upserts had silently failed. There is currently no substrate that can detect or report this divergence.
This ticket proposes a read-only graph-integrity audit — a periodic sweep that, for every SESSION graph node, queries Chroma for the expected raw-memory count and compares with the graph's ORIGINATES_IN-edge count. Mismatches surface in a structured report consumable by humans and downstream agents (Memory Core, Sandman handoff).
The Problem
The Native Edge Graph and Chroma memory collection are kept in sync by MemorySessionIngestor. The synchronization is one-shot per session — once the session is marked digested, no integrity check ever revisits it. Conditions that produce silent drops include:
Transient SQLite parameter overflow (sibling ticket — was the trigger today)
Disk-space exhaustion mid-write
Lock contention from concurrent processes
Schema-mismatch errors caught by the per-memory try/catch
Future unknown-unknown failures in any upstream service
Without a substrate to detect divergence, these silent drops accumulate. The Memory Core's effective recall fidelity decays, but the system reports green status. Sandman's handoff and Golden Path synthesis run on partial data without indication. The longer the delay between drop and detection, the harder root-cause attribution becomes.
A periodic integrity audit is the substrate-level safety net — it doesn't prevent drops, it surfaces them.
The Architectural Reality
Files involved (read-only — this is an audit, not a mutator):
ai/scripts/ — entry-point script for one-shot audit
ai/services.mjs — SDK aggregation for both reads, with KB_Config.data.autoSync = false and GH_Config.data.syncOnStartup = false already in place (precedent: buildScripts/ai/backup.mjs)
What the audit emits:
A structured report (JSON or markdown) with per-session entries:
actualMemoryCount (from graph ORIGINATES_IN edges into session:X)
divergence (positive = missing in graph; negative = orphaned in graph)
severity (clean / soft drop ≤5% / hard drop >5%)
Consumer identity: primary consumer is the maintainer (operator). Secondary consumer is downstream automation that could trigger lazy back-fill on detected gaps. Tertiary is the swarm — the report could feed into Sandman's golden-path synthesis as a coverage-quality signal.
The Fix
Net-new substrate. Phased implementation:
Phase A (this ticket — minimum viable): standalone npm run script.
New file: ai/scripts/auditGraphIntegrity.mjs (or buildScripts/ai/auditGraphIntegrity.mjs — pick the directory whose conventions match; buildScripts/ai/backup.mjs is the precedent for aggregation-via-ai/services.mjs).
Reads all SESSION nodes from graph, all memory rows from Chroma grouped by sessionId, computes divergence per session.
Writes report to .neo-ai-data/audits/graph-integrity-<ISO-timestamp>.json (or .md for human-readable).
Exit code 0 (clean), 1 (soft drops), 2 (hard drops) so CI / cron can branch.
New package.json script: "ai:audit-integrity": "node ./buildScripts/ai/auditGraphIntegrity.mjs".
Phase B (future ticket): integrate audit output into Sandman's synthesizeGoldenPath so coverage-quality signals appear in sandman_handoff.md.
Phase C (future ticket): auto-trigger lazy back-fill on detected gaps via MemorySessionIngestor.ingestSingleRow.
This ticket is scoped to Phase A.
Acceptance Criteria
npm run ai:audit-integrity runs end-to-end on the current Memory Core + graph DB without crashes
Report file is written under .neo-ai-data/audits/ with ISO-timestamped filename
Per-session entry includes sessionId, expectedMemoryCount, actualMemoryCount, divergence, severity
Severity classification: clean (divergence === 0), soft (1-5% drop), hard (>5% drop) — thresholds configurable via env or config keys
Exit code reflects worst severity: 0 for clean-only, 1 for soft, 2 for hard
Read-only: no graph-DB mutations, no Chroma writes, no service-side side-effects
Unit test: synthetic fixture with 3 sessions (one clean, one soft, one hard) verifies report shape and exit code
Empirical: dry-run on the live Memory Core after the sibling ticket fixes ship; report surfaces any historical silent drops
Commit subject ends with (#TICKET_ID) per AGENTS.md §3 Gate 1; type=feat, scope=ai
Out of Scope
Phase B (Sandman integration) and Phase C (auto-backfill). Filed as future tickets after Phase A's report shape is empirically validated.
Cross-tenant scoping. The audit operates on the current userId context. Multi-tenant isolation belongs to the broader #9999 epic.
Real-time streaming integrity check. This is a periodic audit, not a real-time invariant. Streaming integrity would be a separate substrate.
Graph→Chroma direction (orphaned graph nodes with no Chroma source). This ticket is scoped to "missing memories." Chroma rows referenced by graph but absent in Chroma is a different failure pattern (Chroma data loss) — file separately if observed.
Avoided Traps
Embedding the audit in the runSandman finally block. Rejected. RunSandman is already too long-lived (~10 min); adding a full graph traversal at the tail compounds the time. Standalone script is independently runnable, separable, and operator-controlled.
MCP tool surface for the audit at this stage. Rejected for Phase A. MCP tools imply schema closure (per MCP output category split pattern); audit output is exploratory at this point — let the report shape stabilize empirically before pinning a closed contract.
Per-memory hash verification (audit each individual memory's payloadHash). Rejected for Phase A. Count divergence is the cheap signal that catches the failure mode this ticket targets. Hash-level audit would catch a different class of corruption (mutated memory content) and is substantially more expensive — file separately if needed.
Audit-time auto-correction. Rejected. Phase A is observation only. Mixing detection with correction in the same script bypasses the operator's review of severity. Phase C is the correct home for auto-correction, after operators trust the detection signal.
Related
Detects historical drops from: sibling ticket on DreamService graphDigested gate (which prevents future drops but doesn't backfill past ones)
Trigger source for past drops: sibling ticket on SQLite IN-clause overflow (and any other transient ingestion failure mode)
Adjacent: #10153 (lazy back-fill — audit could feed it; out of scope for Phase A)
Context
Surfaced 2026-04-28 during research on a silent-partial-ingestion bug in
DreamService(sibling ticket). Even after that gate is tightened, the graph today carries an unknown number of historically-orphaned memories from sessions that were markedgraphDigested: truewhile some of theirMemorySessionIngestorupserts had silently failed. There is currently no substrate that can detect or report this divergence.This ticket proposes a read-only graph-integrity audit — a periodic sweep that, for every
SESSIONgraph node, queries Chroma for the expected raw-memory count and compares with the graph'sORIGINATES_IN-edge count. Mismatches surface in a structured report consumable by humans and downstream agents (Memory Core, Sandman handoff).The Problem
The Native Edge Graph and Chroma memory collection are kept in sync by
MemorySessionIngestor. The synchronization is one-shot per session — once the session is marked digested, no integrity check ever revisits it. Conditions that produce silent drops include:try/catchWithout a substrate to detect divergence, these silent drops accumulate. The Memory Core's effective recall fidelity decays, but the system reports green status. Sandman's handoff and Golden Path synthesis run on partial data without indication. The longer the delay between drop and detection, the harder root-cause attribution becomes.
A periodic integrity audit is the substrate-level safety net — it doesn't prevent drops, it surfaces them.
The Architectural Reality
Files involved (read-only — this is an audit, not a mutator):
ai/mcp/server/memory-core/services/GraphService.mjs— read SESSION nodes +ORIGINATES_INedge countsai/mcp/server/memory-core/services/MemoryService.mjs(orStorageRouter.getMemoryCollection()) — read Chroma memory-collection counts grouped bysessionIdai/scripts/— entry-point script for one-shot auditai/services.mjs— SDK aggregation for both reads, withKB_Config.data.autoSync = falseandGH_Config.data.syncOnStartup = falsealready in place (precedent:buildScripts/ai/backup.mjs)What the audit emits:
A structured report (JSON or markdown) with per-session entries:
sessionId,sessionNodeId,chromaSessionIdexpectedMemoryCount(from Chromawhere: {sessionId: X}count)actualMemoryCount(from graphORIGINATES_INedges intosession:X)divergence(positive = missing in graph; negative = orphaned in graph)severity(clean / soft drop ≤5% / hard drop >5%)Consumer identity: primary consumer is the maintainer (operator). Secondary consumer is downstream automation that could trigger lazy back-fill on detected gaps. Tertiary is the swarm — the report could feed into Sandman's golden-path synthesis as a coverage-quality signal.
The Fix
Net-new substrate. Phased implementation:
Phase A (this ticket — minimum viable): standalone
npm runscript.ai/scripts/auditGraphIntegrity.mjs(orbuildScripts/ai/auditGraphIntegrity.mjs— pick the directory whose conventions match;buildScripts/ai/backup.mjsis the precedent for aggregation-via-ai/services.mjs).sessionId, computes divergence per session..neo-ai-data/audits/graph-integrity-<ISO-timestamp>.json(or.mdfor human-readable).package.jsonscript:"ai:audit-integrity": "node ./buildScripts/ai/auditGraphIntegrity.mjs".Phase B (future ticket): integrate audit output into Sandman's
synthesizeGoldenPathso coverage-quality signals appear insandman_handoff.md.Phase C (future ticket): auto-trigger lazy back-fill on detected gaps via
MemorySessionIngestor.ingestSingleRow.This ticket is scoped to Phase A.
Acceptance Criteria
npm run ai:audit-integrityruns end-to-end on the current Memory Core + graph DB without crashes.neo-ai-data/audits/with ISO-timestamped filenamesessionId,expectedMemoryCount,actualMemoryCount,divergence,severityclean(divergence === 0),soft(1-5% drop),hard(>5% drop) — thresholds configurable via env or config keys(#TICKET_ID)per AGENTS.md §3 Gate 1; type=feat, scope=aiOut of Scope
userIdcontext. Multi-tenant isolation belongs to the broader #9999 epic.Avoided Traps
MCP output category splitpattern); audit output is exploratory at this point — let the report shape stabilize empirically before pinning a closed contract.payloadHash). Rejected for Phase A. Count divergence is the cheap signal that catches the failure mode this ticket targets. Hash-level audit would catch a different class of corruption (mutated memory content) and is substantially more expensive — file separately if needed.Related
graphDigestedgate (which prevents future drops but doesn't backfill past ones)Origin Session ID:
4bb6859b-860f-440d-9055-320e20b0ee22Retrieval Hint:
graph-integrity audit SESSION ORIGINATES_IN edge count completeness check session-to-memory divergence orphaned-memory detection