You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Umbrella for the legacy seam program: reorganizing crates/grida into modular, model-agnostic shape so the v2 engine promotion (#9) consumes modules, not forks. Migration by extraction, not reimplementation.
All work lands on main. The model-v2-anchor branch is locked — design reference only. Its WG spec overlay is landed on main by #26; those specs are what the seams build against.
Thesis
The legacy engine is already mostly IR-mediated. v1-model entanglement is confined to three choke points:
crates/grida/src/import/svg/pack.rs — walks the existing IRSVG IR but emits v1 node schema records directly.
crates/grida/src/import/html/mod.rs — walks Stylo ComputedValues and emits v1 records directly (no IR; duplicates the htmlcss cascade front-end verbatim).
The painter's model→display-list compilation (painter/layer.rs, geometry.rs, painter_debug_node.rs) — the draw loop itself has zero model references.
Already agnostic: the htmlcss/ renderer (own StyledElement→LayoutBox→Skia pipeline), the cg/ leaf vocabulary (one skia From impl from standalone), Skia-textlayout shaping, and the foundation crates (math2, csscascade, fonts).
Each seam gets: a name first, an architecture test locking the boundary, behavior-preserving refactors, and pixel/snapshot gates. The v2 engine is treated as a foreign consumer we cannot rewrite — contracts stay agnostic, never legacy-shaped or v2-shaped.
The deciding rule (standing doctrine for this program)
A module becomes a workspace crate when its second consumer appears in the workspace — and not before. Until then it stays an in-crate module whose boundary is locked by an architecture test enforcing the same import rules cargo would enforce after extraction, so extraction later is a git mv plus a Cargo.toml, not a design event.
Naming exercises are per-extraction gates (the name comes first), scheduled at extraction time — not now.
Milestones
Milestone
Contents
Status
M0 — bootstrap
WG spec overlay on main (#26); this issue + seam issues; gate baseline recorded (comment below)
No wasm-surface changes — @grida/canvas-wasm publishing is untouched throughout.
No type reshaping of Paint/Stroke/Color/TextStyleRec — vocabulary convergence is spec-first work whose implementation belongs to the promotion program.
No geometry-IR convergence (v2 PathArtifact vs legacy d: String) — no legacy-side choke point exists; doing it now would be v2 promotion in disguise.
No tree-level HTML convergence (importer consuming collected StyledNode trees / LayoutBox) — baked positions and inline-merge semantics are intentional pixel changes, out of scope.
Status — 2026-07-18
Umbrella for the legacy seam program: reorganizing
crates/gridainto modular, model-agnostic shape so the v2 engine promotion (#9) consumes modules, not forks. Migration by extraction, not reimplementation.All work lands on
main. Themodel-v2-anchorbranch is locked — design reference only. Its WG spec overlay is landed on main by #26; those specs are what the seams build against.Thesis
The legacy engine is already mostly IR-mediated. v1-model entanglement is confined to three choke points:
crates/grida/src/import/svg/pack.rs— walks the existingIRSVGIR but emits v1 node schema records directly.crates/grida/src/import/html/mod.rs— walks StyloComputedValuesand emits v1 records directly (no IR; duplicates the htmlcss cascade front-end verbatim).painter/layer.rs,geometry.rs,painter_debug_node.rs) — the draw loop itself has zero model references.Already agnostic: the
htmlcss/renderer (own StyledElement→LayoutBox→Skia pipeline), thecg/leaf vocabulary (one skiaFromimpl from standalone), Skia-textlayout shaping, and the foundation crates (math2,csscascade,fonts).Each seam gets: a name first, an architecture test locking the boundary, behavior-preserving refactors, and pixel/snapshot gates. The v2 engine is treated as a foreign consumer we cannot rewrite — contracts stay agnostic, never legacy-shaped or v2-shaped.
The deciding rule (standing doctrine for this program)
Naming exercises are per-extraction gates (the name comes first), scheduled at extraction time — not now.
Milestones
Seam issues (each carries its exit criterion and gate commands): filed as follow-ups and linked in the checklist below.
docs/wg/feat-painting/)Gate baseline
Recorded in a comment below after the baseline run. Every seam's exit criterion appends "…and the M0 baseline is unchanged."
cargo test -p gridacargo run --release -p grida_dev -- reftest run --suite-dir fixtures/local/resvg-test-suite --renderer iosvg --threshold 0.1(iosvg, not thereftest-run.shdefaulthtmlcss— the htmlcss renderer bypassespack.rsentirely)grida_wptfixtures/test-html/suites/L0.exact.jsonat 100% vs the Chromium oracle@grida/canvas-wasmbuilds (crates/grida-canvas-wasm && just build)Non-goals (locked for the whole program)
format/grida.fbschanges (theSCHEMA_VERSIONlockstep with gridaco/grida holds; see The engine migration — history & the consolidation guideline (residue from gridaco/grida) #1).@grida/canvas-wasmpublishing is untouched throughout.Paint/Stroke/Color/TextStyleRec— vocabulary convergence is spec-first work whose implementation belongs to the promotion program.PathArtifactvs legacyd: String) — no legacy-side choke point exists; doing it now would be v2 promotion in disguise.StyledNodetrees /LayoutBox) — baked positions and inline-merge semantics are intentional pixel changes, out of scope.model-v2/**— the branch is locked.References