Acceptance Criteria
packages/transform/src/facets/types.ts defines Facet, FacetContext, FacetInput, FacetResult, FacetWarning, FacetLayerfacets/driver.ts provides orderFacets (deterministic topological sort, registration order preserved for independents), runFacets (merge into a FacetResolution), and WarningCollector (per-key dedupe, immediate emission)orderFacets throws on a dependency cycle, a dangling after, or a duplicate facet name — at import, via a module-level ORDERED_FACETS, not per transformelevation and prominence are facets owning their own vocabularies (ELEVATION_VALUES, PROMINENCE_VALUES, HEADER_SECTION_ROLES); the inline blocks and their stranded constants are gone from engine.ts- The engine wires all merge sinks — axes, classes,
dataAttrs, styles, consumes — so migrating a further axis needs no engine change - Registry order preserves
modifierValues key insertion order, so output attribute order is unchanged - Warning strings are byte-identical and emitted at the same point in the run
- All 630 pre-existing transform tests pass unmodified
- New unit tests cover facets in isolation and the driver's failure modes (cycle, dangling
after, duplicate name), which have no counterpart today npm run build, the full repo suite, and refrakt contracts --check pass
Approach
Both axes are pure attribute ?? config default → data-* scalars, so the migration is mechanical. The care goes into two things.
Ordering fidelity. Registration order must match the order these axes resolved inline, because modifierValues key insertion order determines data-* attribute order in the serialized output. Keep the facet pass at the same point in transformRune that the inline blocks occupied.
Warning fidelity. Neither axis dedupes today — both warn per instance — so their FacetWarnings carry no dedupeKey. Ten test files spy on console.warn and assert on message text, so the collector must still print, with the same strings, at the same point. Converting those spies into assertions on returned FacetWarning[] is deliberately not part of this item; it belongs with WORK-524, after the refactor has proven itself.
Note ELEVATION_VALUES was dead code before this — declared, never read. The engine performs no validation on elevation; the closed set is enforced only at parse time by the schema's matches. Preserve that (unknown values pass through); do not add validation as a drive-by.
References
- SPEC-124 — facet registry (the spec this work item realizes)
- SPEC-107 — elevation & prominence, the axes migrated here