LearnNewsExamplesServices
Frontmatter
title>-
authorneo-opus-ada
stateMerged
createdAtMay 29, 2026, 2:33 PM
updatedAtMay 29, 2026, 4:48 PM
closedAtMay 29, 2026, 4:48 PM
mergedAtMay 29, 2026, 4:48 PM
branchesdevagent/12168-baseconfig-prototype-shape
urlhttps://github.com/neomjs/neo/pull/12172
Merged
neo-opus-ada
neo-opus-ada commented on May 29, 2026, 2:33 PM

Resolves #12168 Related: #12101, #12166

Authored by Claude Opus 4.8 (Claude Code 1M). Session b124b83a-2cca-4a2b-a711-64868439ba1e.

FAIR-band: over-target [21/30] — operator-direction (operator "wrap up the epic; a half-finished version is dangerous"; @neo-gpt did not claim #12168 after the offer). Rebalance: #12166 (parent-chain, sequences after this) offered to under-target @neo-gpt.

Conforms BaseConfig to the graduated Discussion #12100 prototype shape — the shape #12164 diverged from (a re-derived metaTree config + construct-time compile). This is the foundation the parent-chain realm inheritance (#12166) builds on.

What shipped

ai/BaseConfig.mjs (core):

  • Meta-leaf tree now lives in the standard data config (the parallel metaTree config is gone), compiled in an afterSetData override via the Provider's own processDataObject seam — reactive + AC-Epic-1-aligned ("extend, don't reinvent").
  • leaf(default, env, type) factory replaces {env, default, parse} literals. Reuses the name-based Env parsers (typeParsers) + a typeValidators map. Explicit type survives a null default (e.g. leaf(null, 'NEO_PUBLIC_URL', 'url') stays validatable — the old Neo.typeOf-inference lost the type on null).
  • internalSetData validates any value at a known leaf path — object/JSON leaves no longer bypass (closes what #12167 targeted; #12167 closed as superseded).
  • setEnvOverride(envName, value) keyed by env-var name (one var → N leaves); new refreshEnv() for bounded on-demand re-resolution.
  • Bounded env (not live-per-read), deliberately. A live env value decoupled from a coordinated restart is dangerous — a changed NEO_CHROMA_PORT would make new reads target a port the DB isn't listening on. Rationale baked into #applyEnvLayer's JSDoc. True live-env-with-coordinated-restart is a separate Body-tier topic.
  • Guard: Neo.typeOf returns undefined for an object with an own constructor key (the dummy-EF duck-type) — both inference sites guard it.

6 config.template.mjs converted metaTree+literals → data+leaf(). Shape only — the AiConfig.x snapshots are kept (the realm-split that removes them is #12166). 3 leaves with custom (non-Env) parsers — logLevel (github + gitlab) and memorySharing.defaultPolicy (memory-core) — kept as explicit {env, default, parse} literals (the valid escape hatch; compileMetaLeaves honors any {default} object).

7 specs adapted: {metaTree: cfg.metaTree}{data: cfg._data}, text-asserts metaTree:data:, daemon.spec template-text default:\s*falseleaf\(false (the leaf() conversion changed the asserted source), env-name-keyed setEnvOverride.

Changed config keys (MCP config-template change guide)

  • All 6 config.template.mjs reshaped (metaTreedata slot + leaf() leaves). No leaf VALUES changed (defaults preserved).
  • Local config.mjs follow-up REQUIRED after merge: regenerate each clone's gitignored config.mjs from the updated templates + restart MCP servers + orchestrator. No config.mjs is committed.

Deltas from ticket

  • #12167 (object-leaf validation) is subsumed here (the leaf-factory typed validation does it properly) — #12167 closed as superseded.
  • 3 custom-parser leaves kept as literals (the leaf() factory covers Env parsers only; a custom-parser escape hatch is out of scope).

Evidence

Evidence: L1 (standalone Neo-bootstrapped verification — core 16/16 checks; 6/6 templates compile+resolve; 7 specs node --check clean; shape-sensitive spec tail assessed safe) → L2 full Playwright unit suite in CI (the authoritative full-suite gate; the worktree has no node_modules + a parse5-build prerequisite locally). Verified: no runtime consumer of setEnvOverride or .metaTree exists, so the signature/shape changes break zero production code; the consumer read API (aiConfig.X.Y) is unchanged.

Test Evidence

  • Standalone harness (node): leaf() factory, afterSetData compile, nested + object leaves, bounded env, setEnvOverride (env-name), refreshEnv, validate-any, typeOf-guard — all pass.
  • All 6 converted templates import + resolve.
  • 7 affected specs adapted + syntax-clean; an 11-file broad sweep resolved to safe (false-positives / value-preserving compiled-config reads / SourcePathsConfig toContain(string) checks preserved by the conversion / already-stale comments).
  • Full unit suite runs in CI.

Post-Merge Validation

  • Regenerate each clone's config.mjs from the templates; restart MCP servers + orchestrator.
  • Confirm orchestrator boots + MCP servers start on the new shape.

Commits

  • 8afa5ce7c — BaseConfig core (data + afterSetData seam + leaf() factory + bounded env + typeOf-safe inference)
  • 019fcb43c — 6 templates → data+leaf() + typeOf guard
  • a90549839 — 7 specs adapted to the new shape