Context
Operator surfaced a concrete config-substrate cleanup gap on 2026-05-25 during the #11982 correction cycle.
Current live evidence:
ai/config.template.mjs still contains an idempotent Playwright-worker collision workaround around Neo.ai.Config registration:
- comment block beginning
Idempotent registration: ai/config.template.mjs and the operator-overlay...
const instance = Neo.ns('Neo.ai.Config') ?? Neo.setupClass(Config);
ai/mcp/server/memory-core/config.template.mjs still defines applyLegacyEnv() with fallback support for SSE_PORT, NEO_CHROMA_EMBEDDING_PROVIDER, NEO_KB_CHROMA_HOST, and NEO_KB_CHROMA_PORT.
ai/mcp/server/knowledge-base/config.template.mjs still defines applyLegacyEnv() with fallback support for SSE_PORT and NEO_MEMORY_CORE_DB_PATH.
- Exact grep also shows many Playwright unit tests import repo-local gitignored
config.mjs files directly instead of the tracked templates.
Duplicate sweep:
- #10823 is the closed predecessor for Phase 1 legacy alias deletion. It removed earlier helper-level aliases, but the current template-level
applyLegacyEnv() methods still exist, so this is residual cleanup rather than a duplicate.
- Live open-issue search for
config.template applyLegacyEnv Neo.ai.Config returned no open duplicate.
- Knowledge Base semantic search was attempted but unavailable because the local embedding endpoint refused connection.
The Problem
The current shape preserves two wrong testing and compatibility escape hatches:
The top-level ai/config.template.mjs masks test pollution by reusing an existing Neo.ai.Config singleton when both ai/config.template.mjs and the operator overlay ai/config.mjs load in the same Playwright worker. That workaround hides the real contract violation: tests must not import the gitignored operator overlay when they need canonical defaults.
Memory Core and Knowledge Base config templates still carry legacy env fallback methods after the config cleanup line already moved to canonical env names. These methods reintroduce alias semantics and keep old names alive inside the source of truth.
Both issues make cloud-deployment config behavior harder to reason about. The template should be the test source of truth; the gitignored overlay is operator-local runtime state, not a unit-test fixture.
The Architectural Reality
ai/config.template.mjs owns the top-level Tier-1 AI config singleton Neo.ai.Config.
ai/config.mjs is gitignored operator overlay generated from the template by initServerConfigs.mjs; it is not safe test input because it can drift per checkout.
ai/mcp/server/memory-core/config.template.mjs and ai/mcp/server/knowledge-base/config.template.mjs are tracked per-server templates and should expose canonical env bindings only.
- Playwright unit workers reuse process-global
Neo namespaces, so importing both a template and a gitignored overlay can collide under unitTestMode. The fix is import discipline, not idempotent singleton reuse.
- Closed #10823 established the cleanup direction: no released-version compatibility burden for these legacy aliases.
The Fix
- In
ai/config.template.mjs, delete the idempotent registration comment and replace the fallback registration with strict class setup:
const instance = Neo.setupClass(Config);
- Audit Playwright unit tests and fixtures so canonical config defaults come from tracked
config.template.mjs, not repo-local gitignored config.mjs overlays.
- Delete
applyLegacyEnv() from both:
ai/mcp/server/memory-core/config.template.mjs
ai/mcp/server/knowledge-base/config.template.mjs
- Remove or rewrite tests that assert legacy fallback behavior.
- Add/adjust regression coverage proving template imports are deterministic and no longer need the
Neo.ns(...) ?? collision workaround.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
ai/config.template.mjs class registration |
Operator directive + Neo unitTestMode namespace discipline |
Strict Neo.setupClass(Config); duplicate imports should fail loudly instead of silently reusing stale singleton state |
None |
PR body notes test import contract |
Unit/import audit |
| Playwright unit config imports |
Operator directive: tests use templates only |
Tests needing canonical defaults import tracked config.template.mjs, never repo-local gitignored config.mjs |
Synthetic temp config.mjs files may exist only inside tests for config-copy tooling, not as runtime config imports |
Test comments where needed |
rg "config.mjs" test/playwright/unit/ai reduced to allowed tooling fixtures only |
MC/KB applyLegacyEnv() |
#10823 cleanup direction + operator directive |
Delete template-level legacy env fallback hooks |
None; canonical env names only |
PR body migration note if needed |
Unit coverage no longer references legacy env vars |
Decision Record Impact
Aligned with the accepted #10822 / #10823 config hard-cut direction. No ADR amendment expected.
Acceptance Criteria
Out Of Scope
- Deleting gitignored runtime
config.mjs overlays from operator checkouts.
- Changing production services that intentionally import their runtime
config.mjs overlay.
- Reworking the entire three-tier config architecture.
- Cleaning unrelated config templates such as GitHub Workflow or Neural Link unless they directly block the ACs above.
Avoided Traps
- Do not replace
Neo.ns(...) ?? Neo.setupClass(...) with another collision-suppression helper. The collision is the signal that tests imported the wrong config layer.
- Do not preserve legacy env aliases for backwards compatibility. This is unreleased v13 Agent OS substrate and #10823 already established the hard-cut direction.
- Do not broaden this into a cloud-deployment config epic. This is a narrow residual cleanup ticket.
Related
- Predecessor cleanup: #10823
- Parent config cleanup direction: #10822
- Operator-surfaced during #11982 correction cycle.
Retrieval Hint: "ai config template strict setupClass tests use config.template only delete applyLegacyEnv memory-core knowledge-base"
Context
Operator surfaced a concrete config-substrate cleanup gap on 2026-05-25 during the #11982 correction cycle.
Current live evidence:
ai/config.template.mjsstill contains an idempotent Playwright-worker collision workaround aroundNeo.ai.Configregistration:Idempotent registration: ai/config.template.mjs and the operator-overlay...const instance = Neo.ns('Neo.ai.Config') ?? Neo.setupClass(Config);ai/mcp/server/memory-core/config.template.mjsstill definesapplyLegacyEnv()with fallback support forSSE_PORT,NEO_CHROMA_EMBEDDING_PROVIDER,NEO_KB_CHROMA_HOST, andNEO_KB_CHROMA_PORT.ai/mcp/server/knowledge-base/config.template.mjsstill definesapplyLegacyEnv()with fallback support forSSE_PORTandNEO_MEMORY_CORE_DB_PATH.config.mjsfiles directly instead of the tracked templates.Duplicate sweep:
applyLegacyEnv()methods still exist, so this is residual cleanup rather than a duplicate.config.template applyLegacyEnv Neo.ai.Configreturned no open duplicate.The Problem
The current shape preserves two wrong testing and compatibility escape hatches:
The top-level
ai/config.template.mjsmasks test pollution by reusing an existingNeo.ai.Configsingleton when bothai/config.template.mjsand the operator overlayai/config.mjsload in the same Playwright worker. That workaround hides the real contract violation: tests must not import the gitignored operator overlay when they need canonical defaults.Memory Core and Knowledge Base config templates still carry legacy env fallback methods after the config cleanup line already moved to canonical env names. These methods reintroduce alias semantics and keep old names alive inside the source of truth.
Both issues make cloud-deployment config behavior harder to reason about. The template should be the test source of truth; the gitignored overlay is operator-local runtime state, not a unit-test fixture.
The Architectural Reality
ai/config.template.mjsowns the top-level Tier-1 AI config singletonNeo.ai.Config.ai/config.mjsis gitignored operator overlay generated from the template byinitServerConfigs.mjs; it is not safe test input because it can drift per checkout.ai/mcp/server/memory-core/config.template.mjsandai/mcp/server/knowledge-base/config.template.mjsare tracked per-server templates and should expose canonical env bindings only.Neonamespaces, so importing both a template and a gitignored overlay can collide underunitTestMode. The fix is import discipline, not idempotent singleton reuse.The Fix
ai/config.template.mjs, delete the idempotent registration comment and replace the fallback registration with strict class setup:const instance = Neo.setupClass(Config);config.template.mjs, not repo-local gitignoredconfig.mjsoverlays.applyLegacyEnv()from both:ai/mcp/server/memory-core/config.template.mjsai/mcp/server/knowledge-base/config.template.mjsNeo.ns(...) ??collision workaround.Contract Ledger
ai/config.template.mjsclass registrationNeo.setupClass(Config); duplicate imports should fail loudly instead of silently reusing stale singleton stateconfig.template.mjs, never repo-local gitignoredconfig.mjsconfig.mjsfiles may exist only inside tests for config-copy tooling, not as runtime config importsrg "config.mjs" test/playwright/unit/aireduced to allowed tooling fixtures onlyapplyLegacyEnv()Decision Record Impact
Aligned with the accepted #10822 / #10823 config hard-cut direction. No ADR amendment expected.
Acceptance Criteria
ai/config.template.mjsno longer contains the idempotent registration comment orNeo.ns('Neo.ai.Config') ?? Neo.setupClass(Config).Neo.setupClass(Config)registration.config.template.mjs, not repo-local gitignoredconfig.mjsoverlays.config.mjsmentions in unit tests are limited to synthetic temp files or tests whose subject is config-file copying/migration, not runtime default consumption.applyLegacyEnv()is deleted fromai/mcp/server/memory-core/config.template.mjs.applyLegacyEnv()is deleted fromai/mcp/server/knowledge-base/config.template.mjs.SSE_PORT,NEO_CHROMA_EMBEDDING_PROVIDER,NEO_KB_CHROMA_HOST,NEO_KB_CHROMA_PORT, andNEO_MEMORY_CORE_DB_PATHare removed or rewritten to canonical-name expectations.rg "applyLegacyEnv|NEO_KB_CHROMA_HOST|NEO_KB_CHROMA_PORT|NEO_CHROMA_EMBEDDING_PROVIDER|SSE_PORT|NEO_MEMORY_CORE_DB_PATH" ai/mcp/server/{memory-core,knowledge-base}/config.template.mjs test/playwright/unit/aishows no remaining legacy fallback contract except historical issue/discussion resources.Out Of Scope
config.mjsoverlays from operator checkouts.config.mjsoverlay.Avoided Traps
Neo.ns(...) ?? Neo.setupClass(...)with another collision-suppression helper. The collision is the signal that tests imported the wrong config layer.Related
Retrieval Hint: "ai config template strict setupClass tests use config.template only delete applyLegacyEnv memory-core knowledge-base"