Context
Comparing PR #12164's BaseConfig against the graduated Discussion #12100 prototype (2026-05-29) surfaced that internalSetData validation is scalar-only: object/JSON-typed leaves bypass the parser check. AC-Epic-5 mandates the metadata-path-first rule — validate any value at a known leaf path.
The Problem
ai/BaseConfig.mjs internalSetData gates validation on Neo.typeOf(value) !== 'Object':
if (typeof key === 'string' && Neo.typeOf(value) !== 'Object' && this.#leafMetadataRegistry.has(key)) {
value = this.#validateLeafValue(key, value);
}So a future object/JSON leaf (e.g. {theme: 'dark'}) written to a known leaf path skips validation entirely — exactly the gap the prototype's comment warns against. AC-Epic-5 (Epic #12101): "parser-validation at internalSetData() choke point (metadata-path-first rule — validate ANY value at known leaf path; namespace recursion intermediates pass through)."
The Architectural Reality
- The metadata-path-first rule keys on registry membership: if
#leafMetadataRegistry.has(key) the write targets a leaf → validate, regardless of value type. Namespace-recursion intermediates have no registry entry → pass through naturally.
#validateLeafValue already type-checks against the leaf's meta.type; for an object leaf meta.type is 'Object' (inferred from the default), so the existing mechanism extends cleanly once the type gate is removed.
The Fix
Drop the Neo.typeOf(value) !== 'Object' clause so registry membership alone gates validation:
if (typeof key === 'string' && this.#leafMetadataRegistry.has(key)) {
value = this.#validateLeafValue(key, value);
}Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
BaseConfig.internalSetData validation gate |
ai/BaseConfig.mjs |
Validate any value at a registered leaf path; namespace intermediates pass through |
warn-and-keep (current leniency) |
method JSDoc |
Scalar-only gate confirmed in #12164 |
Acceptance Criteria
Out of Scope
- The
validate-throws-vs-warn-keeps policy (current is lenient warn-and-keep; a separate decision).
- Adopting the prototype's
parse.validate() contract — requires the leaf(default, env, type) factory (deferred; see the parent-chain sub's Out of Scope).
Related
Origin Session ID: b124b83a-2cca-4a2b-a711-64868439ba1e
Retrieval Hint: "BaseConfig internalSetData object leaf validation AC-Epic-5 metadata-path-first"
Context
Comparing PR #12164's
BaseConfigagainst the graduated Discussion #12100 prototype (2026-05-29) surfaced thatinternalSetDatavalidation is scalar-only: object/JSON-typed leaves bypass the parser check. AC-Epic-5 mandates the metadata-path-first rule — validate any value at a known leaf path.The Problem
ai/BaseConfig.mjsinternalSetDatagates validation onNeo.typeOf(value) !== 'Object':if (typeof key === 'string' && Neo.typeOf(value) !== 'Object' && this.#leafMetadataRegistry.has(key)) { value = this.#validateLeafValue(key, value); }So a future object/JSON leaf (e.g.
{theme: 'dark'}) written to a known leaf path skips validation entirely — exactly the gap the prototype's comment warns against. AC-Epic-5 (Epic #12101): "parser-validation atinternalSetData()choke point (metadata-path-first rule — validate ANY value at known leaf path; namespace recursion intermediates pass through)."The Architectural Reality
#leafMetadataRegistry.has(key)the write targets a leaf → validate, regardless of value type. Namespace-recursion intermediates have no registry entry → pass through naturally.#validateLeafValuealready type-checks against the leaf'smeta.type; for an object leafmeta.typeis'Object'(inferred from the default), so the existing mechanism extends cleanly once the type gate is removed.The Fix
Drop the
Neo.typeOf(value) !== 'Object'clause so registry membership alone gates validation:if (typeof key === 'string' && this.#leafMetadataRegistry.has(key)) { value = this.#validateLeafValue(key, value); }Contract Ledger
BaseConfig.internalSetDatavalidation gateai/BaseConfig.mjsAcceptance Criteria
!== 'Object'bypass).Out of Scope
validate-throws-vs-warn-keeps policy (current is lenient warn-and-keep; a separate decision).parse.validate()contract — requires theleaf(default, env, type)factory (deferred; see the parent-chain sub's Out of Scope).Related
Origin Session ID: b124b83a-2cca-4a2b-a711-64868439ba1e Retrieval Hint: "BaseConfig internalSetData object leaf validation AC-Epic-5 metadata-path-first"