LearnNewsExamplesServices
Frontmatter
id14123
titleDestructiveOperationGuard guards a stale graph collection name — fail-open on the live neo-native-graph
stateClosed
labels
bugaiarchitecturesecurity
assigneesneo-opus-grace
createdAtJun 26, 2026, 6:43 PM
updatedAtJun 26, 2026, 11:46 PM
githubUrlhttps://github.com/neomjs/neo/issues/14123
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJun 26, 2026, 11:46 PM

DestructiveOperationGuard guards a stale graph collection name — fail-open on the live neo-native-graph

Closed v13.1.0/archive-v13-1-0-chunk-7 bugaiarchitecturesecurity
neo-opus-grace
neo-opus-grace commented on Jun 26, 2026, 6:43 PM

Context

Found 2026-06-26 while reviewing PR #14117 (the data-integrity sweep's audited collection set). The destructive-operation guard that refuses to drop canonical Chroma collections protects a phantom collection name and leaves the live Memory Core graph collection unprotected.

The Bug (verified)

ai/mcp/server/shared/services/DestructiveOperationGuard.mjs defines:

export const GUARDED_CANONICAL_COLLECTION_NAMES = Object.freeze(new Set([
    'neo-agent-memory', 'neo-agent-sessions', 'neo-agent-graph', 'neo-knowledge-base'
]));

used at :48 (isCanonical = GUARDED_CANONICAL_COLLECTION_NAMES.has(name)) to throw CanonicalCollectionGuardError (:117) and refuse a deleteCollection({name}). This is the collection-name-layer guard that exists (per its own JSDoc :88-94) to backstop cases where the path-target guard is bypassed.

But the live MC graph collection is neo-native-graph, NOT neo-agent-graph. Verified against the SSOT:

  • ai/mcp/server/memory-core/config.mjs:267graph: leaf('neo-native-graph', 'NEO_GRAPH_COLLECTION_NAME', 'string')
  • ai/scripts/maintenance/knowledgeBaseArtifact.mjs:49MEMORY_CORE_COLLECTION_PREFIXES = ['neo-agent-memory','neo-agent-sessions','neo-native-graph']
  • ai/scripts/maintenance/purgeTestCollections.mjs:54 PROTECTED set uses neo-native-graph; #14084's live recovery repaired 941/942 neo-native-graph rows

grep -rln 'neo-agent-graph' ai/ src/ → only DestructiveOperationGuard.mjs + ChromaManager.mjs (both JSDoc/lists). So neo-agent-graph is purely stale.

Impact: GUARDED_CANONICAL_COLLECTION_NAMES.has('neo-native-graph') is false → a destructive drop of the real ~900-row graph collection is not blocked at this layer (fail-open).

The Fix

  1. DestructiveOperationGuard.mjs: neo-agent-graphneo-native-graph in GUARDED_CANONICAL_COLLECTION_NAMES + the JSDoc at :19.
  2. ai/services/memory-core/managers/ChromaManager.mjs:331: same stale name in JSDoc → correct it.
  3. Drift-catch test (the durable defense): assert the guarded set's MC entries match the live config collection names (memory/session/graph), so a future rename can't silently re-open the gap. The guard's JSDoc deliberately hardcodes (not config-derived) so a caller without the isolation layer still gets the canonical name blocked — so the test asserts hardcoded-set ⊇ config-names (parity), rather than making the guard config-derived.

Acceptance Criteria

  • GUARDED_CANONICAL_COLLECTION_NAMES contains neo-native-graph and not the stale neo-agent-graph.
  • The ChromaManager JSDoc names the live neo-native-graph.
  • A unit test pins parity between the hardcoded guard set and the live MC config collection names (drift-catch), failing if they diverge again.
  • No other live reference to a neo-agent-graph collection exists (verified — only the two guard files).

Avoided Traps

  • Do NOT make the guard config-derived (it deliberately hardcodes for the no-isolation-layer case); add the parity TEST instead.
  • Confirm neo-knowledge-base + neo-agent-memory + neo-agent-sessions are still the live names before touching them (only the graph name drifted).

Related

Refs #14117 (where it surfaced), #14084 (the recovery work that touches neo-native-graph). Lesson: derive identity sets from config, never a hand-maintained literal — [[verify-identifiers-against-runtime-ssot]].

Authored by Grace (Claude Opus 4.8, Claude Code).

tobiu closed this issue on Jun 26, 2026, 11:46 PM
tobiu referenced in commit 060b7f1 - "fix(ai): guard the live neo-native-graph collection name in DestructiveOperationGuard (#14123) (#14125) on Jun 26, 2026, 11:46 PM