---
id: 12499
title: 'config.template collection-names leaf: declarative via toggle+formula with per-worker-unique test names (memory-core)'
state: CLOSED
labels:
  - enhancement
  - ai
assignees:
  - neo-claude-opus
createdAt: '2026-06-04T12:53:30Z'
updatedAt: '2026-06-04T18:15:08Z'
githubUrl: 'https://github.com/neomjs/neo/issues/12499'
author: neo-claude-opus
commentsCount: 1
parentIssue: null
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-06-04T18:15:08Z'
---
# config.template collection-names leaf: declarative via toggle+formula with per-worker-unique test names (memory-core)

## 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):

```js
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:

```js
// 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`/`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

- [ ] `collections.memory` / `session` declarative (no inline `process.env`); selected by-construction via the toggle+formula.
- [ ] **Per-worker uniqueness preserved** — parallel workers resolve **distinct** test collections (verify under `fullyParallel`).
- [ ] `NEO_MEMORY_COLLECTION_NAME` + `NEO_SESSION_COLLECTION_NAME` rows removed from the SSOT-lint `BASELINE` → **BASELINE empties** → the lint flips to fully-enforcing (no grandfathered instances).
- [ ] Consumers (HealthService / ChromaManager / defragChromaDB) unchanged; maintenance resolves prod names.
- [ ] `config.template.spec` proves 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.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`.


## Timeline

- 2026-06-04T12:53:31Z @neo-claude-opus added the `enhancement` label
- 2026-06-04T12:53:32Z @neo-claude-opus assigned to @neo-claude-opus
- 2026-06-04T12:53:32Z @neo-claude-opus added the `ai` label
- 2026-06-04T13:06:46Z @neo-claude-opus cross-referenced by PR #12500
- 2026-06-04T13:50:00Z @neo-claude-opus referenced in commit `d9b0f08` - "test(ai): pin config formula selection for both toggle states; extract formula fns for testability (#12499)

Review feedback (@tobiu): the prior tests pinned only the formulas' initial VALUES (the test branch — the unit suite always runs UNIT_TEST_MODE=true), not the test/prod SELECTION. Extract the three reactive selectors (graph + collections.memory/session) as named exports and assert both toggle states directly, covering the prod branch the suite never exercises. The toggle is bounded (re-resolved at construct/load, not live-per-read), so the formula is effectively a construct-time derivation; the Provider's reactive re-invocation is covered by its own specs. Verified: config.template.spec 11 passed, SSOT lint OK-0, ChromaManager + chroma-isolation + HealthService consumers green against the regenerated overlay."
- 2026-06-04T16:03:25Z @neo-claude-opus referenced in commit `b866f82` - "refactor(ai): inline memory-core config formulas, drop test-only exports (#12499)

Review (@tobiu): the 3 exported formula fns (graphPathFormula/collectionsMemoryFormula/collectionsSessionFormula) were test-only exports in the SSOT config — pollution for trivial toggle-ternaries. Inline them in the formulas block (zero exports) + drop the export-based both-branches test. The value-test (real resolved config, test branch) stays; the prod branch is the verified ternary's trivial else (a direct prod-branch test would need shared-singleton mutation [B4] or a fresh Provider [formulas not in _data] — not worth it for a one-line else). Verified: config.template.spec 10 passed; SSOT lint OK-0."
### @neo-gpt - 2026-06-04T16:23:16Z

**Input from GPT-5.5 (Codex Desktop):**

> ● ## Contract Ledger Addendum — PR #12500 shipped inline-formula surface
> 
> Reviewer addendum from @neo-gpt for PR #12500 at head `b866f8284605d94b50b1000216555cca5d4e8f1e`. This addendum supersedes the older ticket-body ledger rows where `collections.memory` / `.session` were described as prod leaves and any stale notes implying exported selector functions or env-relocation.
> 
> | Surface | Type | Contract |
> | :--- | :--- | :--- |
> | `collections.memoryProd` / `.sessionProd` | config leaf | Static prod collection names; env overrides `NEO_MEMORY_COLLECTION_NAME` / `NEO_SESSION_COLLECTION_NAME`. |
> | `collections.memoryTest` / `.sessionTest` | config leaf | Per-worker-unique test names generated from module const defaults; env overrides `NEO_MEMORY_COLLECTION_NAME_TEST` / `NEO_SESSION_COLLECTION_NAME_TEST`. |
> | `collections.useTestDatabase` | config leaf | Toggle leaf; default `false`; env override `UNIT_TEST_MODE`. |
> | `collections.memory` / `.session` | formula output | Inline reactive formulas resolve `useTestDatabase ? *Test : *Prod`; existing consumers read these active names unchanged. |
> | `testMemoryCollection` / `testSessionCollection` | module const implementation detail | Generated at config-load per worker process to preserve `fullyParallel` isolation; not exported as public API. |
> | formula helper exports | none | No `graphPathFormula`, `collectionsMemoryFormula`, or `collectionsSessionFormula` exports ship in the final patch. |
> | `BASELINE` in `lint-config-template-ssot.mjs` | lint substrate | Empty; SSOT lint is fully enforcing for new inline-env leaf defaults. |
> 
> Evidence checked before posting this addendum:
> 
> - PR #12500 current head: `b866f8284605d94b50b1000216555cca5d4e8f1e`; CI all green.
> - Diff shows inline formulas for `collections.memory` / `collections.session` and no exported selector functions.
> - Local verification on the checked-out PR branch passed: `npm run ai:lint-config-template-ssot` and `npm run test-unit -- test/playwright/unit/ai/mcp/server/memory-core/config.template.spec.mjs test/playwright/unit/ai/scripts/lint/lintConfigTemplateSsot.spec.mjs` (`19 passed`).

- 2026-06-04T18:15:08Z @tobiu referenced in commit `7cf3f26` - "refactor(ai): resolve memory-core collection-names declaratively via toggle+formula (#12499) (#12500)

* refactor(ai): resolve memory-core collection-names declaratively via toggle+formula (#12499)

Replace the inline process.env.UNIT_TEST_MODE ? test-unique : prod leaf ternaries in collections.memory/session with declarative *Prod/*Test leaves + a useTestDatabase toggle; resolve the active collections.memory/session by construction via reactive Provider formulas (the graph-reshape precedent). Per-worker-unique test names are module consts generated per worker process at config-load → fullyParallel isolation preserved, A4-clean. Empties the SSOT-lint BASELINE → the lint is now fully-enforcing (all config.template inline-process.env instances reshaped). Adapts lintConfigTemplateSsot.spec for the empty baseline (synthetic-baseline suppression test + drop the row-count assertion).

* test(ai): pin config formula selection for both toggle states; extract formula fns for testability (#12499)

Review feedback (@tobiu): the prior tests pinned only the formulas' initial VALUES (the test branch — the unit suite always runs UNIT_TEST_MODE=true), not the test/prod SELECTION. Extract the three reactive selectors (graph + collections.memory/session) as named exports and assert both toggle states directly, covering the prod branch the suite never exercises. The toggle is bounded (re-resolved at construct/load, not live-per-read), so the formula is effectively a construct-time derivation; the Provider's reactive re-invocation is covered by its own specs. Verified: config.template.spec 11 passed, SSOT lint OK-0, ChromaManager + chroma-isolation + HealthService consumers green against the regenerated overlay.

* refactor(ai): inline memory-core config formulas, drop test-only exports (#12499)

Review (@tobiu): the 3 exported formula fns (graphPathFormula/collectionsMemoryFormula/collectionsSessionFormula) were test-only exports in the SSOT config — pollution for trivial toggle-ternaries. Inline them in the formulas block (zero exports) + drop the export-based both-branches test. The value-test (real resolved config, test branch) stays; the prod branch is the verified ternary's trivial else (a direct prod-branch test would need shared-singleton mutation [B4] or a fresh Provider [formulas not in _data] — not worth it for a one-line else). Verified: config.template.spec 10 passed; SSOT lint OK-0.

---------

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-06-04T18:15:09Z @tobiu closed this issue

