Context
During this session's deep exploration of BaseConfig extends Neo.state.Provider (10-subagent facet analysis + operator dialogue, 2026-05-29), it became clear that PR #12164 reuses the Provider's reactive data plumbing but not its hierarchy. Operator-directed: model the aiConfig realm as a real Provider parent chain — Tier-1 = parent holding realm-wide data, each MCP-server config = child inheriting it. This maps the framework's component-tree provider hierarchy (Body) onto the Brain's config tiers, where there is no component tree.
Substantive rationale / provenance: operator-directed across multiple turns this session (the getParent() override mechanism was operator-proposed); grounded in the graduated Epic #12101 "extend Provider, don't reinvent" mandate. Not premature convergence — the shape was converged in operator dialogue. Open to cross-family peer review at PR time.
The Problem
No config sets a parent. KB + MC import AiConfig and snapshot shared leaves via default: AiConfig.x (~30 leaves in MC, ~11 in KB); the 3 workflow/inspection servers hand-copy literals. Result: massive sibling duplication, stale-snapshot risk (values frozen at module-eval), and N independent owners of every shared leaf. The Provider parent chain that would eliminate this already exists and is unused.
The Architectural Reality
Provider#getParent() (src/state/Provider.mjs:396) resolves _parent, else falls back to the component tree. The Brain has no component tree → override getParent() to return the Tier-1 singleton.
- Raw-instance access is
globalThis.Neo.ai.Config (placed by applyToGlobalNs, Neo.mjs:141 / singleton path :972) — not Neo.get('Neo.ai.Config'), which is id-keyed and returns nothing for a className (operator-corrected + V-B-A'd 2026-05-29).
getOwnerOfDataProperty (:376) recurses to the parent → reads resolve up the chain. Enumeration asymmetry: getTopLevelDataKeys (:460) is LOCAL-only — ownKeys / spread / Object.keys / JSON.stringify over a namespace do NOT merge parent leaves (facet-9 audit: no current consumer enumerates a shared namespace; all reads are keyed/dotted/destructured).
- Split rule (operator): "as soon as ≥2 children need a leaf ⇒ move it up to Tier-1." Same env var + same default = shared; differing default ⇒ stays local.
The Fix
BaseConfig.getParent() override: const root = Neo.ai?.Config; return root && root !== this ? root : null — identity guard prevents the root self-parenting (no recursion); not-yet-loaded ⇒ null ⇒ resolves locally. BaseConfig cannot statically import the root (circular — root extends BaseConfig), so it reads the global ns; lazy per-access resolution means the root only needs loading before the first realm-leaf read, not before a server constructs.
- Realm split: move shared leaves (auth.*, engines.chroma.*, model/graph/embedding providers, vectorDimension, backupPath, dummyEmbeddingFunction per the dummy-EF SSOT sub) to Tier-1; delete from servers (drop
import AiConfig + default: AiConfig.x snapshots). Servers keep only genuinely-own leaves (ports, log prefixes, collections, behavior flags).
- Cross-tier write validation:
internalSetData resolves getOwnerOfDataProperty(key) and validates against the owner's registry (facet-8: a child write to a parent-owned leaf currently bypasses validation on both tiers).
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
BaseConfig.getParent() |
ai/BaseConfig.mjs (new override) |
Returns Tier-1 Neo.ai.Config raw singleton; null for root/unloaded |
local resolution |
JSDoc |
Provider seam verified Provider.mjs:376/396 |
| Shared realm leaves (auth.*, chroma.*, providers…) |
server config.template.mjs → Tier-1 |
Declared once at Tier-1; servers inherit via chain |
local re-declare to override |
JSDoc |
Duplication confirmed (facet-5) |
internalSetData owner-registry validation |
ai/BaseConfig.mjs |
Validate against owner's #leafMetadataRegistry |
— |
— |
Cross-tier bypass confirmed (facet-8) |
Acceptance Criteria
Out of Scope
data + afterSetData reshape and the leaf(default, env, type) factory — separate, elegance-not-correctness (file only if pursued).
- Enumeration-merge override for
ownKeys/getTopLevelDataKeys — DEFERRED; no current consumer needs it (document as a latent hazard).
- The
dummyEmbeddingFunction definition-consolidation — the dummy-EF SSOT sub (this ticket makes it chain-inherited).
internalSetData object-leaf validation completeness — the validation-completeness sub.
Avoided Traps
- Per-child
parent: config wiring (rejected): forces a static import edge → transitive unitTestMode className co-instantiation in every spec worker; proxy-vs-raw footgun; runtime-parent-must-be-overlay ambiguity. The getParent() override dissolves all three (no import edge; returns the raw globalThis.Neo.ai.Config; one global singleton resolves to the overlay at runtime).
Decision Record impact
none — extends Epic #12101's "extend Provider, don't reinvent" mandate; operator-directed this session. Downstream amendment note: if peer review at PR time reshapes the wiring, this body's Fix section may need refresh.
Related
- Epic #12101; Discussion #12100 (graduated source); PR #12164 (merged Stage 1); the dummy-EF SSOT sub (becomes chain-inherited).
Origin Session ID: b124b83a-2cca-4a2b-a711-64868439ba1e
Retrieval Hint: "aiConfig parent chain getParent override realm split globalThis Neo.ai.Config"
Context
During this session's deep exploration of
BaseConfig extends Neo.state.Provider(10-subagent facet analysis + operator dialogue, 2026-05-29), it became clear that PR #12164 reuses the Provider's reactive data plumbing but not its hierarchy. Operator-directed: model the aiConfig realm as a real Provider parent chain — Tier-1 = parent holding realm-wide data, each MCP-server config = child inheriting it. This maps the framework's component-tree provider hierarchy (Body) onto the Brain's config tiers, where there is no component tree.Substantive rationale / provenance: operator-directed across multiple turns this session (the
getParent()override mechanism was operator-proposed); grounded in the graduated Epic #12101 "extend Provider, don't reinvent" mandate. Not premature convergence — the shape was converged in operator dialogue. Open to cross-family peer review at PR time.The Problem
No config sets a parent. KB + MC
import AiConfigand snapshot shared leaves viadefault: AiConfig.x(~30 leaves in MC, ~11 in KB); the 3 workflow/inspection servers hand-copy literals. Result: massive sibling duplication, stale-snapshot risk (values frozen at module-eval), and N independent owners of every shared leaf. The Provider parent chain that would eliminate this already exists and is unused.The Architectural Reality
Provider#getParent()(src/state/Provider.mjs:396) resolves_parent, else falls back to the component tree. The Brain has no component tree → overridegetParent()to return the Tier-1 singleton.globalThis.Neo.ai.Config(placed byapplyToGlobalNs,Neo.mjs:141/ singleton path:972) — notNeo.get('Neo.ai.Config'), which is id-keyed and returns nothing for a className (operator-corrected + V-B-A'd 2026-05-29).getOwnerOfDataProperty(:376) recurses to the parent → reads resolve up the chain. Enumeration asymmetry:getTopLevelDataKeys(:460) is LOCAL-only —ownKeys/ spread /Object.keys/JSON.stringifyover a namespace do NOT merge parent leaves (facet-9 audit: no current consumer enumerates a shared namespace; all reads are keyed/dotted/destructured).The Fix
BaseConfig.getParent()override:const root = Neo.ai?.Config; return root && root !== this ? root : null— identity guard prevents the root self-parenting (no recursion); not-yet-loaded ⇒null⇒ resolves locally. BaseConfig cannot statically import the root (circular — root extends BaseConfig), so it reads the global ns; lazy per-access resolution means the root only needs loading before the first realm-leaf read, not before a server constructs.import AiConfig+default: AiConfig.xsnapshots). Servers keep only genuinely-own leaves (ports, log prefixes, collections, behavior flags).internalSetDataresolvesgetOwnerOfDataProperty(key)and validates against the owner's registry (facet-8: a child write to a parent-owned leaf currently bypasses validation on both tiers).Contract Ledger
BaseConfig.getParent()ai/BaseConfig.mjs(new override)Neo.ai.Configraw singleton; null for root/unloadedProvider.mjs:376/396config.template.mjs→ Tier-1internalSetDataowner-registry validationai/BaseConfig.mjs#leafMetadataRegistryAcceptance Criteria
BaseConfig.getParent()override + identity guard; unit test: a server config resolves a Tier-1-only leaf via the chain; root returns null; a bare instance with no root registered resolves locally.import AiConfigfordefault: AiConfig.xsnapshots.AiConfig.X.Yunchanged (facet-9: every call-site is keyed/dotted/destructured).JSON.stringifyover a realm namespace (enumeration stays local-only).Out of Scope
data+afterSetDatareshape and theleaf(default, env, type)factory — separate, elegance-not-correctness (file only if pursued).ownKeys/getTopLevelDataKeys— DEFERRED; no current consumer needs it (document as a latent hazard).dummyEmbeddingFunctiondefinition-consolidation — the dummy-EF SSOT sub (this ticket makes it chain-inherited).internalSetDataobject-leaf validation completeness — the validation-completeness sub.Avoided Traps
parent:config wiring (rejected): forces a staticimportedge → transitiveunitTestModeclassName co-instantiation in every spec worker; proxy-vs-raw footgun; runtime-parent-must-be-overlay ambiguity. ThegetParent()override dissolves all three (no import edge; returns the rawglobalThis.Neo.ai.Config; one global singleton resolves to the overlay at runtime).Decision Record impact
none— extends Epic #12101's "extend Provider, don't reinvent" mandate; operator-directed this session. Downstream amendment note: if peer review at PR time reshapes the wiring, this body's Fix section may need refresh.Related
Origin Session ID: b124b83a-2cca-4a2b-a711-64868439ba1e Retrieval Hint: "aiConfig parent chain getParent override realm split globalThis Neo.ai.Config"