Acceptance Criteria
fieldMetas' spec is data: a bare string, or { from: [...], default } — no entry accepts a function- The spec round-trips through
JSON.parse(JSON.stringify(...)) unchanged from resolves only the declared roots (attrs.*, file.*); an unknown root is rejected at call time rather than resolving to empty- The five runes reading
config.variables.file express their created / modified fallback without a closure - A property-and-ref name collision is still rejected with the ADR-008 error when the properties object comes from
fieldMetas fieldMetas is adopted where a rune's metas are all plain attrs reads mapped into properties; runes needing a meta outside properties, or conditionally, keep the explicit formgroupByHeading is adopted at the seven loop sites, with each rune's per-item parser left rune-specific- No lint rule or contract assertion makes either utility mandatory
- The utility is named
fieldMetas; nothing in the codebase or docs introduces a second metaFields, and RuneConfig.metaFields is unchanged refrakt contracts --check and npm run seo:baseline:check report no driftnpm test passes unchanged
Approach
fieldMetas writes into the same flat key space as refs (ADR-008), so the collision check has to keep working against a computed object rather than a literal — worth a test, since the current check reads Object.keys of both and a generated object is the case nobody has exercised.
Key order matters for data-rune-fields, which is JSON.stringifyd: iterate the spec in declaration order so the bag's key order stays stable and the contracts diff stays empty.
groupByHeading shares only the traversal. parseColorEntry, parseNameValue, parseFontEntry and parseLocationItem stay where they are — the loop is the duplication, not the parsing.
Blocked by
- WORK-598 —
fieldMetas produces the properties object, so it lands after the children emission is gone rather than having to reproduce it
References
- SPEC-140 — Tier 3, D5
- ADR-008 — the flat namespace
properties and refs share