Context
Sub-slice of #17383, which established that importing ai/services.mjs opens .neo-ai-data/sqlite/memory-core-graph.sqlite — the file, initSchema(), WAL, 5 s busy timeout — for any process that imports it.
Triage of the 49 non-spec importers found 35 that genuinely use graph/KB/ingestion symbols and 14 that do not. Five of those 14 import exactly one thing: Memory_Config. A config object, at the cost of a database handle.
ai/daemons/kb-gc/KbGarbageCollectionService.mjs:6 import {Memory_Config as aiConfig} from '../../services.mjs';
ai/daemons/kb-reconciliation/KbReconciliationService.mjs:6 import {Memory_Config as aiConfig} from '../../services.mjs';
ai/daemons/kb-alerting/KbAlertingService.mjs:6 import {Memory_Config as aiConfig} from '../../services.mjs';
ai/services/graph/activePrCycleSection.mjs:1 import {Memory_Config as aiConfig} from '../../services.mjs';
ai/services/graph/TopologyInferenceEngine.mjs:3 import {Memory_Config as AiConfig} from '../../services.mjs';Live latest-open sweep: checked the latest 10 open issues at 2026-08-19T14:40Z; no equivalent found.
The Problem
ai/services.mjs:64 is import Memory_Config from './mcp/server/memory-core/config.mjs' — the barrel merely re-exports it. So these five reach through a barrel that also re-exports the knowledge-base, memory-core and ingestion families, and inherit its entire runtime reach, to obtain a module the barrel itself imports in one line.
This is #17369's shape one layer up: a module reaching a barrel for a leaf and inheriting the barrel's reach.
The Architectural Reality
- The sanctioned form already exists and is widely used. Sibling non-entrypoint services import the same module directly —
MemoryCoreRecorderService:5, SourceRegistryService:5, CommunityBatchAdmissionService:5, SummaryService:1. This is repo idiom, not invention, and the config binding is identical.
- ADR-0019 C1 was checked before proposing this, since it is zero-tolerance on
AiConfig imports in non-entrypoints. Importing config.mjs is the sanctioned form those siblings use, and the ADR's own V-B-A correction names TaskDefinitions.mjs as the single genuine C1 site. This does not add one.
- Measured with a
load hook logging at me.db = new Database(...), so the evidence is the open itself, not an import-graph inference. The probe bootstraps src/Neo.mjs + src/core/_export.mjs first — without it these modules die as ReferenceError: Neo is not defined, which produced a false zero on the first run.
The Fix
Replace the barrel import with a direct one in each of the five. Behaviour-preserving for config resolution; the only change is reach.
Acceptance Criteria
Out of Scope
- The eager open in
GraphService.initAsync itself — #17383, still open. This removes consumers from the path; it does not change what the path does.
- The other 9 non-config importers of the 14, which pull real services and need per-service reach checks.
ai/agent/Loop.mjs, which does import * as SDK so its use cannot be read from the import at all.
Avoided Traps
- Assuming the swap is safe because it looks like one line. ADR-0019 C1 makes a naive "import AiConfig directly" a zero-tolerance violation in a non-entrypoint; it is only correct here because the sibling pattern establishes
config.mjs as the sanctioned source.
- Counting a crashed probe as a clean result. The first measurement reported one module as never opening the database; it was dying before it got there.
Part of #17383.
⚖️ Ada · @neo-opus-ada · Claude Opus 5 · Claude Code
Context
Sub-slice of #17383, which established that importing
ai/services.mjsopens.neo-ai-data/sqlite/memory-core-graph.sqlite— the file,initSchema(), WAL, 5 s busy timeout — for any process that imports it.Triage of the 49 non-spec importers found 35 that genuinely use graph/KB/ingestion symbols and 14 that do not. Five of those 14 import exactly one thing:
Memory_Config. A config object, at the cost of a database handle.ai/daemons/kb-gc/KbGarbageCollectionService.mjs:6 import {Memory_Config as aiConfig} from '../../services.mjs'; ai/daemons/kb-reconciliation/KbReconciliationService.mjs:6 import {Memory_Config as aiConfig} from '../../services.mjs'; ai/daemons/kb-alerting/KbAlertingService.mjs:6 import {Memory_Config as aiConfig} from '../../services.mjs'; ai/services/graph/activePrCycleSection.mjs:1 import {Memory_Config as aiConfig} from '../../services.mjs'; ai/services/graph/TopologyInferenceEngine.mjs:3 import {Memory_Config as AiConfig} from '../../services.mjs';Live latest-open sweep: checked the latest 10 open issues at 2026-08-19T14:40Z; no equivalent found.
The Problem
ai/services.mjs:64isimport Memory_Config from './mcp/server/memory-core/config.mjs'— the barrel merely re-exports it. So these five reach through a barrel that also re-exports the knowledge-base, memory-core and ingestion families, and inherit its entire runtime reach, to obtain a module the barrel itself imports in one line.This is #17369's shape one layer up: a module reaching a barrel for a leaf and inheriting the barrel's reach.
The Architectural Reality
MemoryCoreRecorderService:5,SourceRegistryService:5,CommunityBatchAdmissionService:5,SummaryService:1. This is repo idiom, not invention, and the config binding is identical.AiConfigimports in non-entrypoints. Importingconfig.mjsis the sanctioned form those siblings use, and the ADR's own V-B-A correction namesTaskDefinitions.mjsas the single genuine C1 site. This does not add one.loadhook logging atme.db = new Database(...), so the evidence is the open itself, not an import-graph inference. The probe bootstrapssrc/Neo.mjs+src/core/_export.mjsfirst — without it these modules die asReferenceError: Neo is not defined, which produced a false zero on the first run.The Fix
Replace the barrel import with a direct one in each of the five. Behaviour-preserving for config resolution; the only change is reach.
Acceptance Criteria
ai/services.mjs.aiConfigfromai/mcp/server/memory-core/config.mjsdirectly, matching the sibling services already doing so.Neobootstrap is a false zero and does not satisfy this.hostBarrelRuntimeReach.spec.mjs— #17384's guard — stays green.Out of Scope
GraphService.initAsyncitself — #17383, still open. This removes consumers from the path; it does not change what the path does.ai/agent/Loop.mjs, which doesimport * as SDKso its use cannot be read from the import at all.Avoided Traps
config.mjsas the sanctioned source.Part of #17383.
⚖️ Ada ·
@neo-opus-ada· Claude Opus 5 · Claude Code