Symptom
After a harness restart, MCP servers fail to start — neo-mjs-neural-link, neo-mjs-github-workflow, neo-mjs-memory-core: "Server disconnected." The preflight throws:
[Neo AI] Stale config overlay — a materialized config.mjs is missing template-owned config shape:
- neural-link/config.mjs: missing os:default
- github-workflow/config.mjs: missing path:default
The config.mjs files are not actually stale — they plainly import os from 'os' / import path from 'path' and use them. The guard is wrong.
Root cause
ai/scripts/setup/initServerConfigs.mjs:397 — projectSourceShape's import extractor:
for (const match of src.matchAll(/^import\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"]/gm)) {[\s\S]*? spans across lines. Every server config.mjs now begins with the bare Tier-1 participation import import '../../../config.mjs'; (added by #15294 / #15314), immediately above import os from 'os'. The bare import has no from, so the non-greedy [\s\S]*? bridges from its import to the next import's from 'os', swallowing import os into a malformed body — so the os:default binding is never extracted. assertConfigFresh then classifies os:default/path:default as missing crash-causing shape and throws at boot → the server process dies.
Empirically confirmed: projectSourceShape(realConfigSrc).imports.includes('os:default') returns false on a config that imports os. configBase.mjs starts with import os directly (no bare import first), so it projects correctly — the asymmetry is the whole bug, and it's why the tests missed it.
Blast radius
Every harness bricks on the next restart after #15314 — every real config.mjs has the bare-import-then-os/path shape.
Fix
initServerConfigs.mjs:397 — [\s\S]*? → [^;]*?. A bare import ends in ;, so [^;] cannot bridge into the next statement; multiline named imports contain no ; before from, so they still parse. Verified to restore every dropped binding and clear the boot guard for all five servers. A regression test with a bare-import-then-default fixture is added.
Decision Record
NOT_NEEDED — aligned with ADR 0019; this is a defect fix in the overlay-freshness tooling, no config-surface or SSOT semantics change.
Acceptance Criteria
Regression from: #15294 / #15314 (server config overlay base-extraction).
Symptom
After a harness restart, MCP servers fail to start —
neo-mjs-neural-link,neo-mjs-github-workflow,neo-mjs-memory-core: "Server disconnected." The preflight throws:The
config.mjsfiles are not actually stale — they plainlyimport os from 'os'/import path from 'path'and use them. The guard is wrong.Root cause
ai/scripts/setup/initServerConfigs.mjs:397—projectSourceShape's import extractor:for (const match of src.matchAll(/^import\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"]/gm)) {[\s\S]*?spans across lines. Every serverconfig.mjsnow begins with the bare Tier-1 participation importimport '../../../config.mjs';(added by #15294 / #15314), immediately aboveimport os from 'os'. The bare import has nofrom, so the non-greedy[\s\S]*?bridges from itsimportto the next import'sfrom 'os', swallowingimport osinto a malformed body — so theos:defaultbinding is never extracted.assertConfigFreshthen classifiesos:default/path:defaultas missing crash-causing shape and throws at boot → the server process dies.Empirically confirmed:
projectSourceShape(realConfigSrc).imports.includes('os:default')returnsfalseon a config that importsos.configBase.mjsstarts withimport osdirectly (no bare import first), so it projects correctly — the asymmetry is the whole bug, and it's why the tests missed it.Blast radius
Every harness bricks on the next restart after #15314 — every real
config.mjshas the bare-import-then-os/pathshape.Fix
initServerConfigs.mjs:397—[\s\S]*?→[^;]*?. A bare import ends in;, so[^;]cannot bridge into the next statement; multiline named imports contain no;beforefrom, so they still parse. Verified to restore every dropped binding and clear the boot guard for all five servers. A regression test with a bare-import-then-default fixture is added.Decision Record
NOT_NEEDED— aligned with ADR 0019; this is a defect fix in the overlay-freshness tooling, no config-surface or SSOT semantics change.Acceptance Criteria
projectSourceShapeextracts the:defaultbinding of an import that immediately follows a bare side-effect import.assertConfigFreshno longer throws for the five shipped serverconfig.mjsshapes (bare Tier-1 import +os/path).Regression from: #15294 / #15314 (server config overlay base-extraction).