Context
ai/mcp/server/memory-core/config.template.mjs collections.memory + collections.session are inline-process.env leaf defaults (ADR 0019 §3 A4) — the 2 remaining baselined by the SSOT lint (#12474):
memory : leaf(process.env.UNIT_TEST_MODE === 'true' ? `test-memory-${Date.now()}-${Math.random()...}` : 'neo-agent-memory', 'NEO_MEMORY_COLLECTION_NAME', 'string'),
session: leaf(process.env.UNIT_TEST_MODE === 'true' ? `test-session-${Date.now()}-${Math.random()...}` : 'neo-agent-sessions', 'NEO_SESSION_COLLECTION_NAME', 'string')The dynamic sub-case of #12451 (chroma was #12480/#12481; graph was #12491/#12494). The test values are per-worker-unique (${Date.now()}-${Math.random()}) so parallel playwright workers (fullyParallel: true, multi-worker locally) don't collide on a shared collection.
The Fix — the #12494 formula pattern + a module-const per-worker-unique test name
The per-worker uniqueness is preserved by a module-const generated at config-load (per worker process); only the inline process.env.UNIT_TEST_MODE check (the A4 antipattern) is removed — not the generation:
const testMemoryCollection = `test-memory-${Date.now()}-${Math.random().toString(36).slice(2)}`;
const testSessionCollection = `test-session-${Date.now()}-${Math.random().toString(36).slice(2)}`;
collections: {
memory : leaf('neo-agent-memory', 'NEO_MEMORY_COLLECTION_NAME', 'string'),
memoryTest : leaf(testMemoryCollection, 'NEO_MEMORY_COLLECTION_NAME_TEST', 'string'),
session : leaf('neo-agent-sessions', 'NEO_SESSION_COLLECTION_NAME', 'string'),
sessionTest: leaf(testSessionCollection, 'NEO_SESSION_COLLECTION_NAME_TEST', 'string'),
useTestDatabase: leaf(false, 'UNIT_TEST_MODE', 'boolean'),
graph : leaf('neo-native-graph', 'NEO_GRAPH_COLLECTION_NAME', 'string')
}
formulas: {
'collections.memory' : data => data.collections.useTestDatabase ? data.collections.memoryTest : data.collections.memory,
'collections.session': data => data.collections.useTestDatabase ? data.collections.sessionTest : data.collections.session
}The ~3 consumers (HealthService, ChromaManager, defragChromaDB) read collections.memory/session unchanged (the formula resolves them). A4-clean (no inline process.env in any leaf default — the generation is a module const, the selection is the toggle+formula). Per-worker uniqueness preserved (the module const generates per config-load = per worker process). Maintenance (defragChromaDB, no UNIT_TEST_MODE) → prod names. This is the #12494 formula precedent — NOT the env-relocation the #12451 umbrella note suggests (superseded; see #12451 comment).
Contract Ledger
| Surface |
Type |
Contract |
collections.memory / .session |
config leaf |
Prod names (static literals); env NEO_MEMORY_COLLECTION_NAME / NEO_SESSION_COLLECTION_NAME. |
collections.memoryTest / .sessionTest |
config leaf |
Per-worker-unique test names (module-const default); env …_TEST. |
collections.useTestDatabase |
config leaf |
Toggle. Default false; env UNIT_TEST_MODE. |
collections.memory / .session |
formula |
useTestDatabase ? *Test : * (prod). Consumers read unchanged. |
testMemoryCollection / testSessionCollection |
module const |
Generated at config-load (per worker process) for per-worker uniqueness. |
Acceptance Criteria
Scope — completes #12451
The 2 dynamic collection-names (memory + session). This is the last #12451 reshape: chroma (#12481) + graph (#12494) + this = all instances declarative → the SSOT lint baseline empties → fully-enforcing. (The lint's lintConfigTemplateSsot.spec baselined-fixture already derives from BASELINE[0] per #12494, but an empty BASELINE needs the suppression test guarded/revisited — flag during impl.)
Refs #12451 (reshape umbrella). Part of Epic #12456 (AiConfig reactive Provider SSOT cleanup). Design per the #12494 precedent + ADR 0019 §3 (A4) / §5.
Authored by Claude Opus 4.8 (Claude Code), /lead-role. MC session f5432f03-d2b6-4bc0-a7b5-9fbdafe4b7b9.
Context
ai/mcp/server/memory-core/config.template.mjscollections.memory+collections.sessionare inline-process.envleaf defaults (ADR 0019 §3 A4) — the 2 remaining baselined by the SSOT lint (#12474):memory : leaf(process.env.UNIT_TEST_MODE === 'true' ? `test-memory-${Date.now()}-${Math.random()...}` : 'neo-agent-memory', 'NEO_MEMORY_COLLECTION_NAME', 'string'), session: leaf(process.env.UNIT_TEST_MODE === 'true' ? `test-session-${Date.now()}-${Math.random()...}` : 'neo-agent-sessions', 'NEO_SESSION_COLLECTION_NAME', 'string')The dynamic sub-case of #12451 (chroma was #12480/#12481; graph was #12491/#12494). The test values are per-worker-unique (
${Date.now()}-${Math.random()}) so parallel playwright workers (fullyParallel: true, multi-worker locally) don't collide on a shared collection.The Fix — the #12494 formula pattern + a module-const per-worker-unique test name
The per-worker uniqueness is preserved by a module-const generated at config-load (per worker process); only the inline
process.env.UNIT_TEST_MODEcheck (the A4 antipattern) is removed — not the generation:// module-level (config-load, per worker process — no process.env CHECK; like the existing neoRootDir/cwd consts): const testMemoryCollection = `test-memory-${Date.now()}-${Math.random().toString(36).slice(2)}`; const testSessionCollection = `test-session-${Date.now()}-${Math.random().toString(36).slice(2)}`; collections: { memory : leaf('neo-agent-memory', 'NEO_MEMORY_COLLECTION_NAME', 'string'), // prod, static memoryTest : leaf(testMemoryCollection, 'NEO_MEMORY_COLLECTION_NAME_TEST', 'string'), // per-worker-unique session : leaf('neo-agent-sessions', 'NEO_SESSION_COLLECTION_NAME', 'string'), sessionTest: leaf(testSessionCollection, 'NEO_SESSION_COLLECTION_NAME_TEST', 'string'), useTestDatabase: leaf(false, 'UNIT_TEST_MODE', 'boolean'), // toggle (env-arg) graph : leaf('neo-native-graph', 'NEO_GRAPH_COLLECTION_NAME', 'string') // unchanged (static) } formulas: { 'collections.memory' : data => data.collections.useTestDatabase ? data.collections.memoryTest : data.collections.memory, 'collections.session': data => data.collections.useTestDatabase ? data.collections.sessionTest : data.collections.session }The ~3 consumers (HealthService, ChromaManager, defragChromaDB) read
collections.memory/sessionunchanged (the formula resolves them). A4-clean (no inlineprocess.envin any leaf default — the generation is a module const, the selection is the toggle+formula). Per-worker uniqueness preserved (the module const generates per config-load = per worker process). Maintenance (defragChromaDB, noUNIT_TEST_MODE) → prod names. This is the #12494 formula precedent — NOT the env-relocation the #12451 umbrella note suggests (superseded; see #12451 comment).Contract Ledger
collections.memory/.sessionNEO_MEMORY_COLLECTION_NAME/NEO_SESSION_COLLECTION_NAME.collections.memoryTest/.sessionTest…_TEST.collections.useTestDatabasefalse; envUNIT_TEST_MODE.collections.memory/.sessionuseTestDatabase ? *Test : *(prod). Consumers read unchanged.testMemoryCollection/testSessionCollectionAcceptance Criteria
collections.memory/sessiondeclarative (no inlineprocess.env); selected by-construction via the toggle+formula.fullyParallel).NEO_MEMORY_COLLECTION_NAME+NEO_SESSION_COLLECTION_NAMErows removed from the SSOT-lintBASELINE→ BASELINE empties → the lint flips to fully-enforcing (no grandfathered instances).config.template.specproves the formula resolves the per-worker-unique test name under the toggle.Scope — completes #12451
The 2 dynamic collection-names (memory + session). This is the last #12451 reshape: chroma (#12481) + graph (#12494) + this = all instances declarative → the SSOT lint baseline empties → fully-enforcing. (The lint's
lintConfigTemplateSsot.specbaselined-fixture already derives fromBASELINE[0]per #12494, but an empty BASELINE needs the suppression test guarded/revisited — flag during impl.)Refs #12451 (reshape umbrella). Part of Epic #12456 (AiConfig reactive Provider SSOT cleanup). Design per the #12494 precedent + ADR 0019 §3 (A4) / §5.
Authored by Claude Opus 4.8 (Claude Code), /lead-role. MC session
f5432f03-d2b6-4bc0-a7b5-9fbdafe4b7b9.