Context
The Data Sync Pipeline has been failing on dev since the last success at 2026-08-02T20:13:14Z — 9 consecutive runs, tracked by the standing alarm #16428 (auto-maintained, self-closing on recovery; not a close-target).
Run 30861274995, stage content indexes and SEO:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'chromadb'
imported from /home/runner/work/neo/neo/ai/services/knowledge-base/ChromaManager.mjs
Live latest-open sweep: latest 20 open issues at 2026-08-03T23:27:44Z; no equivalent found. A2A in-flight claim sweep at the same minute (15 messages, all read-states): no overlapping [lane-claim].
The Problem
A docs/SEO job requires a vector-database client, and it does so to read GitHub labels.
The chain, verified by reading each hop:
buildScripts/dataSyncPipeline.mjs stage "content indexes and SEO"
└─ buildScripts/docs/rebuildContentIndexesAndSeo.mjs --include-labels
└─ :66 await import('./index/labels.mjs') ← only this flag reaches it
└─ buildScripts/docs/index/labels.mjs:5
import {GH_LabelService} from '../../../ai/services.mjs'
└─ ai/services.mjs ← 66-import EAGER barrel
└─ ai/services/knowledge-base/ChromaManager.mjs
└─ 'chromadb'The stage needs one method — GH_LabelService.listLabels() — and pays for the entire Brain services graph.
The over-import is old; #16389 only stopped paying for it. The two-path install tier (#16364 / #16389, merged 2026-08-02T21:42:15Z, i.e. after the last green run) moved chromadb into package.brain.json. The Body tier declares zero dependencies and 38 devDependencies, and already ships everything this script genuinely needs — fs-extra, commander, gray-matter, fast-glob, js-yaml, dotenv. Nothing about the split is wrong; it exposed a latent coupling.
The Architectural Reality
ai/services.mjs is a barrel with 66 top-level imports spanning every MCP server. Importing any one export constructs all of them.
ai/services/github-workflow/LabelService.mjs imports only src/core/Base.mjs, GraphqlService.mjs, ai/mcp/server/github-workflow/config.mjs and queries/labelQueries.mjs — nothing Brain-tier.
- The barrel wraps services in
makeSafe(service, spec) (ai/services.mjs:114), which Zod-validates and marshals input arguments from an object into positional params.
makeSafe is a provable no-op at this call site. list_labels in ai/mcp/server/github-workflow/openapi.yaml:135 declares no parameters and no requestBody, and the raw method is async listLabels() — zero params, reads config internally. The call is await GH_LabelService.listLabels() with no arguments, so the wrapper resolves to zodSchema.parse({}) → {}, paramNames → [], then currentMethod.call(service). Identical to the unwrapped call. No validation is lost by importing directly.
But a bare direct import does not work, and this is the part worth recording:
ReferenceError: Neo is not defined
at src/core/Compare.mjs:166
The barrel was also performing the Neo namespace bootstrap. LabelService is a Neo.setupClass class, so the entry point must populate globalThis.Neo first. Verified: import Neo from 'src/Neo.mjs' + import * as core from 'src/core/_export.mjs' is sufficient — InstanceManager is not required here — and yields className = Neo.ai.services.github-workflow.LabelService with listLabels callable.
The Fix
In buildScripts/docs/index/labels.mjs, replace the barrel import with the narrow one plus the entry-point bootstrap:
import Neo from '../../../src/Neo.mjs';
import * as core from '../../../src/core/_export.mjs';
import GH_LabelService from '../../../ai/services/github-workflow/LabelService.mjs';
LabelService.mjs default-exports Neo.setupClass(LabelService), so this is a default import, not a named one.
Acceptance Criteria
Out of Scope
- Restructuring
ai/services.mjs. Making the barrel lazy would fix this class everywhere and is the better long-term answer, but it changes a surface every MCP server imports. Narrow this consumer first; file the barrel work separately if the pattern recurs.
- The install-tier split.
#16389 is correct and is not being reverted or softened.
- Whether other Body-tier consumers import the barrel. Worth a census, but not this ticket — see Related.
Avoided Traps
- Do not add
chromadb to the Body tier. That reverses #16389 to satisfy an import that should not exist, and every Body install then carries a vector-DB client.
- Do not drop the Neo bootstrap on the assumption a direct import is self-sufficient. It is not — measured above, it throws at
src/core/Compare.mjs:166.
- Do not assume
makeSafe is always removable. It is a no-op here because the operation takes no arguments. For a method with parameters it also marshals object → positional, so a naive narrowing would change the calling convention.
Related
#16428 — standing data-sync alarm (auto-maintained; do not use as a close-target).
#16364 / #16389 — the two-path install tier that exposed this.
Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c
Retrieval Hint: query_raw_memories("services.mjs barrel eager import chromadb data-sync SEO labels Body tier")
Context
The Data Sync Pipeline has been failing on
devsince the last success at2026-08-02T20:13:14Z— 9 consecutive runs, tracked by the standing alarm#16428(auto-maintained, self-closing on recovery; not a close-target).Run 30861274995, stage
content indexes and SEO:Live latest-open sweep: latest 20 open issues at
2026-08-03T23:27:44Z; no equivalent found. A2A in-flight claim sweep at the same minute (15 messages, all read-states): no overlapping[lane-claim].The Problem
A docs/SEO job requires a vector-database client, and it does so to read GitHub labels.
The chain, verified by reading each hop:
buildScripts/dataSyncPipeline.mjs stage "content indexes and SEO" └─ buildScripts/docs/rebuildContentIndexesAndSeo.mjs --include-labels └─ :66 await import('./index/labels.mjs') ← only this flag reaches it └─ buildScripts/docs/index/labels.mjs:5 import {GH_LabelService} from '../../../ai/services.mjs' └─ ai/services.mjs ← 66-import EAGER barrel └─ ai/services/knowledge-base/ChromaManager.mjs └─ 'chromadb'The stage needs one method —
GH_LabelService.listLabels()— and pays for the entire Brain services graph.The over-import is old;
#16389only stopped paying for it. The two-path install tier (#16364/#16389, merged2026-08-02T21:42:15Z, i.e. after the last green run) movedchromadbintopackage.brain.json. The Body tier declares zerodependenciesand 38devDependencies, and already ships everything this script genuinely needs —fs-extra,commander,gray-matter,fast-glob,js-yaml,dotenv. Nothing about the split is wrong; it exposed a latent coupling.The Architectural Reality
ai/services.mjsis a barrel with 66 top-level imports spanning every MCP server. Importing any one export constructs all of them.ai/services/github-workflow/LabelService.mjsimports onlysrc/core/Base.mjs,GraphqlService.mjs,ai/mcp/server/github-workflow/config.mjsandqueries/labelQueries.mjs— nothing Brain-tier.makeSafe(service, spec)(ai/services.mjs:114), which Zod-validates and marshals input arguments from an object into positional params.makeSafeis a provable no-op at this call site.list_labelsinai/mcp/server/github-workflow/openapi.yaml:135declares noparametersand norequestBody, and the raw method isasync listLabels()— zero params, reads config internally. The call isawait GH_LabelService.listLabels()with no arguments, so the wrapper resolves tozodSchema.parse({})→{},paramNames→[], thencurrentMethod.call(service). Identical to the unwrapped call. No validation is lost by importing directly.But a bare direct import does not work, and this is the part worth recording:
The barrel was also performing the Neo namespace bootstrap.
LabelServiceis aNeo.setupClassclass, so the entry point must populateglobalThis.Neofirst. Verified:import Neo from 'src/Neo.mjs'+import * as core from 'src/core/_export.mjs'is sufficient —InstanceManageris not required here — and yieldsclassName = Neo.ai.services.github-workflow.LabelServicewithlistLabelscallable.The Fix
In
buildScripts/docs/index/labels.mjs, replace the barrel import with the narrow one plus the entry-point bootstrap:import Neo from '../../../src/Neo.mjs'; import * as core from '../../../src/core/_export.mjs'; import GH_LabelService from '../../../ai/services/github-workflow/LabelService.mjs';LabelService.mjsdefault-exportsNeo.setupClass(LabelService), so this is a default import, not a named one.Acceptance Criteria
buildScripts/docs/index/labels.mjsno longer importsai/services.mjs, and its transitive closure does not reachchromadb.node ./buildScripts/docs/rebuildContentIndexesAndSeo.mjs --include-labelsproduces the same label index as before the change — the output is what matters, not merely that it runs.package.brain.jsondeps present). This is the acceptance shape: a green run on a machine that already haschromadbproves nothing.GH_LabelService.listLabels()still returns the same shape; themakeSafeno-op equivalence is asserted rather than assumed.content indexes and SEOstage withoutERR_MODULE_NOT_FOUND, and#16428self-closes on recovery.Out of Scope
ai/services.mjs. Making the barrel lazy would fix this class everywhere and is the better long-term answer, but it changes a surface every MCP server imports. Narrow this consumer first; file the barrel work separately if the pattern recurs.#16389is correct and is not being reverted or softened.Avoided Traps
chromadbto the Body tier. That reverses#16389to satisfy an import that should not exist, and every Body install then carries a vector-DB client.src/core/Compare.mjs:166.makeSafeis always removable. It is a no-op here because the operation takes no arguments. For a method with parameters it also marshals object → positional, so a naive narrowing would change the calling convention.Related
#16428— standing data-sync alarm (auto-maintained; do not use as a close-target).#16364/#16389— the two-path install tier that exposed this.Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c
Retrieval Hint:
query_raw_memories("services.mjs barrel eager import chromadb data-sync SEO labels Body tier")