V0.30.1
Name:v0.30.1Status:planning

v0.30.1 — Transform engine facet decomposition

House-cleaning on the most central package in the project. transformRune in packages/transform/src/engine.ts resolves every universal rune axis — tint, density, width, spacing, reading, elevation, prominence, motion, background, frame, substrate, cover — in one ~800-line function whose steps are numbered 1, 1b, 1c … 1h, 2 … 9. This milestone replaces that section with a facet registry (SPEC-124): one module per axis, dependencies declared rather than implied by statement order, and a driver that merges their contributions.

No feature work. Every step is behaviour-preserving, and the governing rule is that the 630 pre-existing transform tests never change — they run through the public entry point, so an edit to accommodate a migration is a bug report, not a fix.

Not enough history yet — burndown needs at least one completed day of activity.

Progress 11/11 work items

Work Items

Done 11
WORK-517 main
Facet registry foundation and first universal axes
Realize the foundation of SPEC-124: the facet interface, the driver, and the first two axes migrated out of transformRune. elevation and prominence are chosen deliberately as the easy case — both are independent scalars with no cross-axis dependency — so the foundation lands against low-risk axes before WORK-518 tests it against the hardest one.
high moderate
10/10 criteria
WORK-518 main
Migrate the cover/scrim coupling — two-phase facets and seeded dependencies
Test the facet interface against the engine's hardest cross-axis dependency: SPEC-089 cover mode, whose isCover branch has seven call sites spanning ~600 lines of transformRune. Migrating cover and content-place is the real trial of whether declared after ordering can replace ordering-by-statement-adjacency — and it is expected to correct the interface from WORK-517, not merely consume it.
high moderate
9/9 criteria
WORK-519 main
Migrate the tint axis to a facet
Move SPEC-053 tint resolution — preset lookup, inline token overrides, lockMode, dark-mode tokens, the --tinted modifier and the --tint-* custom properties — out of step 1d of transformRune into a tint facet. Tint goes before background because bg's scrim polarity checks whether tint already claimed data-color-scheme; migrating tint first turns that from a seeded read into a real facet dependency.
high moderate
8/8 criteria
WORK-520 main
Migrate the background axis to a facet and exercise the layers channel
Move the ~200-line background block (step 1f) into a bg facet. This is the first facet that builds its own element tree, so it is the first real use of FacetResult.layers — declared since WORK-517 and unexercised. If layers cannot express the bg layer's construction and placement, the channel needs redesigning before the remaining migrations depend on it.
high complex
10/10 criteria
WORK-521 main
Migrate the remaining scalar axes to facets
Move the remaining lettered sub-steps of transformRune into facets: density (1e), width / spacing / inset (1f), reading and dropcap (1f-bis), content-measure, and reveal / stagger. Individually these are small; together they are most of what remains between step 1 and step 2, and clearing them is what lets the lettered numbering go away.
medium moderate
8/9 criteria
WORK-522 main
Migrate frame and substrate chrome to postAssemble facets
Move SPEC-086 frame chrome (step 1g) and SPEC-087 substrate fills (step 1h) into facets. Both resolve early but apply late, and to a target chosen at resolve time — the rune root or the [data-section="media"] zone, which does not exist until assembly. They are the canonical two-phase facets, and the second real test of postAssemble after cover.
medium complex
9/9 criteria
WORK-523 main
Migrate the config-modifier loop and retire seedAxes
Make step 1's generic config-modifier resolution — plus contextModifiers (1b) and staticModifiers (1c) — facets, and delete FacetInput.seedAxes once nothing needs it. This is the last structural migration and the one that removes the migration scaffolding: every after reference resolves to a real facet, and the registry's dependency graph becomes complete rather than partly seeded.
medium complex
9/9 criteria
WORK-524 main
Consolidate warn-once diagnostics onto the facet collector
Retire the eight module-level *_WARNED sets in engine.ts, route every engine diagnostic through the facet WarningCollector, and settle the dedupe-scope question the migration deliberately deferred. Also convert the ten test files that currently spy on console.warn into assertions on returned warning data.
medium moderate
7/8 criteria
WORK-525 main
Derive structure contracts from facets
packages/transform/src/contracts.ts independently re-derives what the identity transform emits, from config — it imports types, helpers and merge, and never the engine. So refrakt contracts --check validates the contracts file against its own derivation, not against engine output: any engine change contracts does not mirror produces confidently wrong contracts while CI stays green. Now that the axes are facets, investigate whether each facet can declare its own contract contribution and collapse the two implementations into one.This is the spec's principal open question, and it may not pan out. Time-box the investigation and record the answer either way.
high complex
6/6 criteria
WORK-526 main
Let facets declare their own author attributes
transformRune still strips consumed author attributes from pass-through output using a fixed list naming eight facet-owned axes:
low simple
6/6 criteria
WORK-527 main
Universal axes are absent from structure contracts
refrakt contracts is documented as describing "the complete HTML structure the identity transform produces for every rune". It does not. Measured across the checked-in contracts/structures.json (132 runes), not one universal axis appears:
medium moderate
8/8 criteria