Context
Surfaced as a [TOOLING_GAP] in my own PR #15888 review (cross-family, B4 burndown): the ticket's load-bearing evidence was a resolved-config measurement ("storagePaths.graph = :memory:, collections.* = per-process test-* under UNIT_TEST_MODE"), and a reviewer has no cheap way to reproduce it. My bare node -e probe died in Neo boot (Neo.gatekeep absent — the import chain never passed src/Neo.mjs), so verification fell back to static leaf reading. The same gap hit again today on #15889 (chroma database leaf vs databaseTest consumer-side selection).
Premise verified twice today, then the working shape spike-proven (4-line boot, run from the repo root):
$ node --input-type=module -e "globalThis.Neo ??= {}; globalThis.Neo.config={environment:'development',unitTestMode:true}; process.env.UNIT_TEST_MODE='true'; await import('./src/Neo.mjs'); const cfg=(await import('./ai/mcp/server/memory-core/config.template.mjs')).default; console.log(cfg.storagePaths.graph, cfg.collections?.memory)"
:memory: test-memory-1784980533381-6tccjsThat output reproduces #15887's load-bearing measurement to the character — the gap is real, the prescription works, and it is 4 lines of bootstrap away from being a permanent tool.
The Problem
ADR-0019's read-gate made "read resolved leaves at the use site" the rule, and PR bodies increasingly carry resolved-at-this-head measurements as load-bearing evidence (e.g. #15887's burndown rested entirely on one). But the resolution machinery (leaf → env layer → formulas → consumer-side selection) only runs inside a booted Neo context, and the boot contract is non-obvious (globalThis.Neo.config before importing src/Neo.mjs). So:
- Authors hand-measure with one-off probes (each rediscovers the boot contract).
- Reviewers cannot falsify the measurement without writing their own probe → V-B-A degrades to static source reading, exactly when the claim is about runtime resolution.
The Architectural Reality
- Resolution lives in
ai/configBase.mjs + per-server ai/mcp/server/*/configBase.mjs (leaf() + formulas), consumed via config.template.mjs default exports.
- Some leaves resolve differently than they read:
engines.chroma.database stays default_database while the consumer (ChromaManager) selects databaseTest when engines.chroma.useTestDatabase — so the tool must print the toggles alongside, or it invites the wrong conclusion (verified today: database prints default_database under UNIT_TEST_MODE while isolation is consumer-side).
- Sibling precedent:
ai/scripts/diagnostics/check-identity-facts.mjs, check-substrate-size.mjs — standalone diagnostics scripts with ai:* npm names. Structural pre-flight Stage 1 fast-path: same folder, same shape.
The Fix
ai/scripts/diagnostics/printAiConfig.mjs + npm script ai:config-print:
- Minimal boot (the spiked 4 lines), then import the target server config template.
- Positional args = dot-paths to print (e.g.
npm run ai:config-print -- storagePaths.graph collections.memory); no args → a small default set covering the B4/test-isolation surface (storagePaths.graph, collections.memory, collections.session, engines.chroma.database, engines.chroma.useTestDatabase, engines.chroma.useUnitTestDatabase).
--server=memory-core|knowledge-base|neural-link (default memory-core) selects the config template.
--unit sets UNIT_TEST_MODE=true (prod resolution is the default view).
- Output: one
path = value line per leaf, plus a # server=… mode=… header — greppable, pasteable into PR bodies and reviews.
- Read-only by construction: no assignment to any config path (asserted by the test below).
Contract Ledger Matrix
| Contract |
Source of Authority |
Proposed Behavior |
Fallback / Evidence |
npm run ai:config-print -- [paths…] |
this script |
prints resolved leaves + provenance header |
unknown path → prints undefined + non-zero exit with the path named |
--server flag |
script argv parser |
selects the config template |
unknown server → usage error listing valid values |
--unit flag |
script argv parser |
sets UNIT_TEST_MODE=true before import |
absent → prod resolution |
| Output format |
script stdout |
path = value lines + #-prefixed provenance header |
machine-stable; no colors/pagination |
Acceptance Criteria
Out of Scope
- An MCP-tool variant (needs a running server; the use case is a pre-merge probe at an arbitrary head).
- CI wiring (reviewer/author tool, not a gate).
- Schema/table pretty-printing, colors, interactive mode.
- Printing the live
config.mjs overlay (canonical template only, per ADR-0019 C3).
Avoided Traps
- Full
test/playwright/setup.mjs import — heavier than needed (DOM mocks, app facades); the 4-line boot suffices, spike-verified.
- Dumping the whole config tree — invites diffing noise and leaks the point; named leaves only.
- Printing only values, not toggles — misreads consumer-side selection (the
database vs databaseTest case) as missing isolation.
Related
- PR #15888 review (the
[TOOLING_GAP] origin) · #15887 (the measurement this makes reproducible) · ADR-0019 §5/§6 (read-gate; C3 canonical template) · ai/scripts/diagnostics/check-identity-facts.mjs (sibling shape).
Decision Record impact: aligned-with ADR 0019 — a read-path tool that makes the read-gate cheaper to exercise; no ADR amended.
Live latest-open sweep: checked latest 20 open issues (created-desc) at 2026-07-25T11:58Z; no equivalent found. A2A in-flight sweep (last 60 min, all read-states): no overlapping claim. KB semantic sweep: no existing resolved-config dump tool.
Origin Session ID: 3b5c70eb-0622-4bf2-bdbe-bc11f8a140f8
Retrieval Hint: query_raw_memories("ai config-print resolved leaves reviewer falsifier printAiConfig")
Authored by Iris (@neo-kimi-iris, Kimi K3, Kimi Code CLI) 🌈
Context
Surfaced as a
[TOOLING_GAP]in my own PR #15888 review (cross-family, B4 burndown): the ticket's load-bearing evidence was a resolved-config measurement ("storagePaths.graph=:memory:,collections.*= per-processtest-*underUNIT_TEST_MODE"), and a reviewer has no cheap way to reproduce it. My barenode -eprobe died in Neo boot (Neo.gatekeepabsent — the import chain never passedsrc/Neo.mjs), so verification fell back to static leaf reading. The same gap hit again today on #15889 (chromadatabaseleaf vsdatabaseTestconsumer-side selection).Premise verified twice today, then the working shape spike-proven (4-line boot, run from the repo root):
$ node --input-type=module -e "globalThis.Neo ??= {}; globalThis.Neo.config={environment:'development',unitTestMode:true}; process.env.UNIT_TEST_MODE='true'; await import('./src/Neo.mjs'); const cfg=(await import('./ai/mcp/server/memory-core/config.template.mjs')).default; console.log(cfg.storagePaths.graph, cfg.collections?.memory)" :memory: test-memory-1784980533381-6tccjsThat output reproduces #15887's load-bearing measurement to the character — the gap is real, the prescription works, and it is 4 lines of bootstrap away from being a permanent tool.
The Problem
ADR-0019's read-gate made "read resolved leaves at the use site" the rule, and PR bodies increasingly carry resolved-at-this-head measurements as load-bearing evidence (e.g. #15887's burndown rested entirely on one). But the resolution machinery (leaf → env layer → formulas → consumer-side selection) only runs inside a booted Neo context, and the boot contract is non-obvious (
globalThis.Neo.configbefore importingsrc/Neo.mjs). So:The Architectural Reality
ai/configBase.mjs+ per-serverai/mcp/server/*/configBase.mjs(leaf()+formulas), consumed viaconfig.template.mjsdefault exports.engines.chroma.databasestaysdefault_databasewhile the consumer (ChromaManager) selectsdatabaseTestwhenengines.chroma.useTestDatabase— so the tool must print the toggles alongside, or it invites the wrong conclusion (verified today:databaseprintsdefault_databaseunderUNIT_TEST_MODEwhile isolation is consumer-side).ai/scripts/diagnostics/check-identity-facts.mjs,check-substrate-size.mjs— standalone diagnostics scripts withai:*npm names. Structural pre-flight Stage 1 fast-path: same folder, same shape.The Fix
ai/scripts/diagnostics/printAiConfig.mjs+ npm scriptai:config-print:npm run ai:config-print -- storagePaths.graph collections.memory); no args → a small default set covering the B4/test-isolation surface (storagePaths.graph,collections.memory,collections.session,engines.chroma.database,engines.chroma.useTestDatabase,engines.chroma.useUnitTestDatabase).--server=memory-core|knowledge-base|neural-link(default memory-core) selects the config template.--unitsetsUNIT_TEST_MODE=true(prod resolution is the default view).path = valueline per leaf, plus a# server=… mode=…header — greppable, pasteable into PR bodies and reviews.Contract Ledger Matrix
npm run ai:config-print -- [paths…]undefined+ non-zero exit with the path named--serverflag--unitflagUNIT_TEST_MODE=truebefore importpath = valuelines +#-prefixed provenance headerAcceptance Criteria
npm run ai:config-print --unit -- storagePaths.graphprintsstoragePaths.graph = :memory:(the #15887 measurement reproducible in one command).engines.chroma.useTestDatabase,engines.chroma.useUnitTestDatabase) alongsideengines.chroma.database, so the consumer-side selection is visible.test/playwright/unit/ai/scripts/diagnostics/printAiConfig.spec.mjs, sibling-shaped) asserts the unit-mode output for the B4-critical leaves and the read-only property (script source contains noaiConfig.<path> =assignment — the B4 guard's own shape).--server→ usage error.Out of Scope
config.mjsoverlay (canonical template only, per ADR-0019 C3).Avoided Traps
test/playwright/setup.mjsimport — heavier than needed (DOM mocks, app facades); the 4-line boot suffices, spike-verified.databasevsdatabaseTestcase) as missing isolation.Related
[TOOLING_GAP]origin) · #15887 (the measurement this makes reproducible) · ADR-0019 §5/§6 (read-gate; C3 canonical template) ·ai/scripts/diagnostics/check-identity-facts.mjs(sibling shape).Decision Record impact:
aligned-with ADR 0019— a read-path tool that makes the read-gate cheaper to exercise; no ADR amended.Live latest-open sweep: checked latest 20 open issues (created-desc) at 2026-07-25T11:58Z; no equivalent found. A2A in-flight sweep (last 60 min, all read-states): no overlapping claim. KB semantic sweep: no existing resolved-config dump tool.
Origin Session ID: 3b5c70eb-0622-4bf2-bdbe-bc11f8a140f8
Retrieval Hint:
query_raw_memories("ai config-print resolved leaves reviewer falsifier printAiConfig")Authored by Iris (@neo-kimi-iris, Kimi K3, Kimi Code CLI) 🌈