Frontmatter
| id | 12331 |
| title | Isolate unit-test chroma + sqlite-daemon stores + orphan-purge |
| state | Closed |
| labels | bugaitesting |
| assignees | neo-opus-ada |
| createdAt | Jun 2, 2026, 1:29 AM |
| updatedAt | Jun 7, 2026, 7:18 PM |
| githubUrl | https://github.com/neomjs/neo/issues/12331 |
| author | neo-opus-ada |
| commentsCount | 6 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [x] 10384 Revalidate full-suite Neo.setupClass contamination after test isolation lands, [x] 12180 Orphan test-* Chroma collections accumulate (incomplete test cleanup), [x] 12143 Test runs leak test-* collections into the production unified Chroma store |
| closedAt | Jun 6, 2026, 1:02 AM |
Isolate unit-test chroma + sqlite-daemon stores + orphan-purge

ran it:
tobiasuhlig@819fe61f-83c8-4461-95d7-a3ac14b840ba neo % npm run ai:purge-test-collections -- --apply> neo.mjs@12.1.0 ai:purge-test-collections
> node ./ai/scripts/maintenance/purgeTestCollections.mjs --apply
🧹 Purge test collections — APPLY (destructive)
🔌 Chroma: localhost:8000
Collection schema deserialization was created with the dummy_embedding_function embedding function. However, the @chroma-core/dummy_embedding_function package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/dummy_embedding_function package.
Collection schema deserialization was created with the dummy_embedding_function embedding function. However, the @chroma-core/dummy_embedding_function package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/dummy_embedding_function package.
Collection neo-knowledge-base was created with the dummy_embedding_function embedding function. However, the @chroma-core/dummy_embedding_function package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/dummy_embedding_function package.
📚 1302 collections total → 1296 test orphans, 6 retained.
✨ Retained: test-where, neo-agent-memory, test-where-in, neo-knowledge-base, neo-agent-sessions, neo-native-graph
🔥 Deleting 1296 test orphans:
…deleted 100/1296
…deleted 200/1296
…deleted 300/1296
…deleted 400/1296
…deleted 500/1296
…deleted 600/1296
…deleted 700/1296
…deleted 800/1296
…deleted 900/1296
…deleted 1000/1296
…deleted 1100/1296
…deleted 1200/1296
🧽 SQLite-daemon residue under /Users/Shared/github/neomjs/neo/.neo-ai-data:
✅ none found.
Collection schema deserialization was created with the dummy_embedding_function embedding function. However, the @chroma-core/dummy_embedding_function package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/dummy_embedding_function package.
Collection schema deserialization was created with the dummy_embedding_function embedding function. However, the @chroma-core/dummy_embedding_function package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/dummy_embedding_function package.
Collection neo-knowledge-base was created with the dummy_embedding_function embedding function. However, the @chroma-core/dummy_embedding_function package is not installed. 'add' and 'query' will fail unless you provide them embeddings directly, or install the @chroma-core/dummy_embedding_function package.
🎉 Purge complete: deleted 1296 collections (0 failed); 6 remain (0 test orphans left).
Context
Operator-directed (2026-06-02), after exploring the unit-test isolation: the chroma prod-wipe risk is genuinely resolved (process-isolated
test-*collection names +afterAll cleanupChromaManager+ the FATALUNIT_TEST_MODEguard protect the real collections), but the orphan accumulation is not. Empirical, live store 2026-06-02:test-*orphan collections indefault_tenant/default_databasevs only 4 real (neo-agent-memory,neo-agent-sessions,neo-knowledge-base,neo-native-graph) — up from #12180's 1,263 (2026-05-29).test/playwright/unit/ai/daemons/bridge/daemon.spec.mjswrites its db and daemon dir into the prod data dir (DB_PATH = .neo-ai-data/sqlite/test-daemon-${testId}.sqlite,DAEMON_DIR = .neo-ai-data/wake-daemon-test-${testId}) — the SQLite sibling of the chroma leak; 12 leaked dbs were just cleaned but bleed back on every run.This is the unified test-store-isolation gap that lets #12143/#12180 (and the daemon leak) actually close.
The Problem
Unit tests write to the live stores. Process-isolated names +
afterAllcleanup protect prod from wipes and handle the happy path, but interrupted/Ctrl-C/CI-cancel runs and bare-npxbypasses still leak, and there's no ephemeral isolation (so crashes can touch prod-adjacent namespaces) and no purge for the existing backlog. The graph store already does this right (config:112→:memory:underUNIT_TEST_MODE); chroma + the daemon db do not.The Architectural Reality
ai/mcp/server/memory-core/config.template.mjs:119-120—test-memory-${ts}-${rand}/test-session-…underUNIT_TEST_MODE, but the samedefault_tenant/default_databaseas prod.ai/services/memory-core/managers/ChromaManager.mjs#connect()→chromaConnect({client})(ai/services/shared/vector/chromaClientPrimitives.mjs); client is injectable;ChromaManager.deleteCollection({name, confirmation})(+DestructiveOperationGuard) is the safe delete path.daemon.spec.mjsDB_PATH/DAEMON_DIRresolve under.neo-ai-data/(prod).cleanupChromaManager(util.mjs) has the FATALUNIT_TEST_MODEguard.:memory:underUNIT_TEST_MODE(config:112).defragChromaDB.mjscleans orphaned segment directories only, NOT thetest-*collection entries.The Fix
UNIT_TEST_MODE, route the chroma client to an isolated namespace so test collections never land indefault_tenant/default_database. Recommended mechanism: a dedicated test tenant/database (chroma multi-tenancy), whole-namespace-droppable. (Mechanism — tenant/db vs throwaway persist-dir vs in-memory — confirm at implementation against the actual client-creation site; prod path staysdefault_tenant/default_database.)daemon.spec.mjsDB_PATH+DAEMON_DIR→os.tmpdir()-based paths +afterEach/afterAllcleanup. Off the prod data dir by construction.ai/scripts/maintenance/purgeTestCollections.mjs: lists chroma collections, deletestest-memory-*/test-session-*(via theChromaManager/guarded delete path; or drops the test tenant/db) + the.neo-ai-data/sqlite/test-daemon-*residue. Runnable on demand (clears the existing 1,281 backlog) and optionally at suite start.Contract Ledger
UNIT_TEST_MODEChromaManager#connect+config.template.mjsdefault_tenant/default_database)test-*names + mandatory purge-at-suite-startconfigJSDocdefault_*; test path asserts the isolated namespacepurgeTestCollections.mjs(new maintenance surface)ai/scripts/maintenance/siblingstest-memory-*/test-session-*collections +test-daemon-*.sqliteresidue; refuses to touch the 4 real collectionstest-*name (hard guard)npm runentrytest-*deleted; real collections untouchedAcceptance Criteria
UNIT_TEST_MODE, chroma test collections resolve to a namespace distinct fromdefault_tenant/default_database(statically verifiable); by construction no test run — including crashed/npx-bypassed — can create collections in the prod namespace.daemon.spec.mjswrites its db + daemon dir underos.tmpdir(), not.neo-ai-data/(statically verifiable);afterEach/afterAllcleanup present.purgeTestCollections.mjsdeletestest-*chroma collections +test-daemon-*sqlite residue, with a hard guard that never deletes a non-test-*name; running it against the live store leaves only the 4 real collections (verified by a follow-up count).default_tenant/default_database; the prod-wipe guards (DestructiveOperationGuard, FATALUNIT_TEST_MODEguard) remain intact.#12143(leak) +#12180(accumulation) are resolved (the daemon leak too).Out of Scope
Related
daemon.spec.mjsSQLite leak (no standalone ticket; folded here)Origin Session ID: da9a6007-1250-4363-8c15-dff69eccb3be Retrieval Hint: "unit-test chroma sqlite-daemon ephemeral isolation orphan purge test-* collections"