model-v2: anchor — land the v2 engine as the crates/n0 family#5
Conversation
The full redesign workbench for node geometry/layout/transform, scoped to the Rust engine (crates/grida) and the format spec (format/grida.fbs): - phase 1-2 research corpus: problems, harnesses, peer study, candidate models (anchor / sheet / bake / wire) — anchor decided by survey - the 114-test Rust lab (model-v2/a/lab): four-phase resolver (Taffy flex, oriented envelopes), typed ops incl. cross-zero flip resize (E-A14), canonical text IR, node-arena + SOA storage (11.5x resolver speedup) - the decision register (a/DECISIONS.md) with DEC-0 LOCKED: visual-only rotation (the CSS framing), CSS-pure sizing (a/dec0-visual-only.md) - peer-compat matrix (COMPAT), ship-readiness census (LIMITS), report - demo pages (a/.preview): proof, model walkthrough, edge cases, DEC-0 fork A/B, free editing - E10: the native skia interactive spike (a/spike-canvas) — feel pass + TEXTBOOK.md, the reference for the legacy migration Isolated: nothing outside model-v2/ is touched; no production code paths.
…tests, branch note); fix MODEL.md dec0 link
…ontracts (ENG-0…ENG-5), validity-tagged
…nce the snapshot) The lab's accumulated evolution on the anchor branch since the textbook snapshot, across two threads — the seams anchor-engine consumes and the DEC-0 visual-only feel study — plus a crate-wide rustfmt pass. 121-test suite green. Engine seams (additive; serde stays out of default deps): - ops.rs: typed `Op` + pure-dispatch `apply` (journal/replay/dirty land on it; free fns kept, `apply ≡ free_fn` differential per variant) + `DirtyClass` parameterized on rotation-in-flow [ENG-1.2]. - model.rs: per-slot `generations` column + `gen_of` (semantic PartialEq ignores it — a storage artifact) [ENG-2.3]. - resolve.rs: non-panicking `box_opt`/`local_opt`/`aabb_opt`/`slot_count`. - serde feature (default OFF) — the op-log wire; new tests accessors, generations, op_apply. Visual-only exploration: rotation scenes added to the edge/fork probes (grow × rotation, rotated text/frame/group) and expanded edge_census + visual_only corpora anchoring DEC-0.
…spike re-hosted anchor-engine: document → resolve → drawlist → paint, plus the read tier (query), time-as-data (journal/replay), and the sockets every future optimization plugs into (damage, ident, oracle). It consumes anchor-lab as a library — the relationship the crates/grida migration will have with the model crate. Every ENG-0…ENG-5 contract has a code socket and a guarding test; skia is confined to paint.rs so a later core/paint split is mechanical. The spike is re-hosted onto it (proof, not just scaffolding): the scene painter is deleted → drawlist::build + paint::execute (4 goldens byte-identical, incl. the rotated and cross-zero-flip scenes); pick/hover via query; gesture ops via apply, recorded in the journal (undo stays document snapshots — ENG-5.5); `--record` writes .replay corpus. The scene-cache compositor (ENG-2) rasters once into a backend-matched offscreen and blits camera pans O(1), re-rastering only on doc-change / zoom / pan-beyond-margin. Live per-stage frame instrumentation (a glFinish GPU-vs-vsync split) plus render-on-demand (draw only on real input) land the felt-latency fix. gate (ENG-0.2 oracle law): shots 4/4 IDENTICAL · replays deterministic · scene-cache differential MATCHES fresh (integer pan) · bench within budgets. Docs: ENGINE.md SETUP ground contracts (S-1…S-7), engine README (contract → module → guarding-test map), model-v2 + a/README phase-4 status.
Performance is not one question, so it is not one tool — it splits on a hard line between what a machine can measure alone and what needs a human. This is the doctrine that stops us measuring the wrong axis (as a session's detour did: render *work* was green while the felt lag lived in the *present path*). MEASURE.md — the four axes and which tool owns each: - work + correctness are automated & headless (probe / probe_gpu / gate) — the default loop, no human. - feel & pacing need a real window: the opt-in ANCHOR_FRAMELOG channel (human runs + interacts + kills; the reviewer reads the per-frame gap/stage log). An on-demand bridge, not the iteration loop. - input→photon latency is NOT software-measurable — the lag accrues in the OS compositor/display after swap_buffers returns; measuring it needs a photodiode rig or a human glance. Named so a green gate is never mistaken for good feel. The automated work axis it documents: - bin/probe — deterministic CPU raster, per-stage distributions, JSON baselines, panicking audit-guards. The always-on regression detector. - bin/probe_gpu — headless surfaceless-GL, real flush_and_submit timing, for the wins whose value is a persistent GPU texture (CPU raster reports the wrong sign there). Worked example folded in: render-on-demand (idle frames 466 → 2, throughput 125fps median — the symptom was pacing/latency, not work).
…L doctrine Two gate-verified data-layout moves aligned to browser prior art, plus the decisions doc that grounds them: - Color: fill `Option<String>` → `model::Color(u32)` (0xAARRGGBB), read straight by the drawlist. No per-node heap String, no per-build hex parse; text-IR/SVG convert at the boundary via from_hex/to_hex. Parse byte-identical to the old resolve_color (pixels unchanged, gate 4/4 IDENTICAL); build −31% at 100k nodes. - Resolve caches: HashMap<NodeId,_> → Vec<Option<_>> (union_cache, ops_cache). NodeId is a dense arena index — hashing it is the cc anti-pattern. Correctness preserved (gate PASS, 121 lab tests). - spike-canvas: `pages` stress scene (realistic nested flex cards, ANCHOR_SCENE selected) + --bench rows; goldens still use starter(). - engine/DATA-MODEL.md: six data-layout details decided against cc/Blink/Stylo/ Skia prior art, validity-tagged (ALIGNED/ADOPTED/SOCKET), with honesty corrections to the record.
Package renames (anchor-engine -> n0, anchor-lab -> n0-model, anchor-spike -> n0_dev) with mechanical use-path renames across the three crates; root workspace members += n0-model, n0_dev; path deps rewired; compile-checked include fixes (lab tests' engine/rig includes -> ../../n0/rig, spike font fixture depth); gate.rs re-pointed at the n0_dev binary and ../n0_dev/shots goldens. Patrol ledger (patrol-before-drop) — deliberately dropped as obsolete: - the three [workspace] breakouts and "standalone on purpose" rationale comments (workspace membership is what promotion IS), - [profile.release] debug=true x3 (ignored in a workspace; profiling uses CARGO_PROFILE_RELEASE_DEBUG=true), - both .cargo/config.toml target-dir shims and every "shared target dir reuse" comment clause (automatic in-workspace), - the three standalone Cargo.locks (the root lock takes over; its diff is additions-only — no existing dep version moved), - the engine's phase-4 description (replaced by the stub's "A 2D graphics engine." + categories/keywords/readme metadata). Kept/rewritten per the same ledger: the serde op-log-wire rationale, the trace zero-cost-observability rationale, the native-gl-context GL-free-default rationale, the skia-confined-to-paint.rs invariant, and the shell-dep version-parity constraint (reason updated from target-dir reuse to workspace type unification). Crate-doc (//!) and README framing updated from phase-4/standalone future tense to the landed topology; archived-workbench references repointed to model-v2/ paths; 15 forbidden grida.co/docs/wg links converted to same-repo relative per the links discipline. Part of landing #5; tracking #9.
git rm src/bin/{e1,e3,edge,fork}.rs — the archive-coupled experiment
harnesses whose outputs are frozen under model-v2/a/
(e1-rotation-in-flow/, e3-text-ir/, edge-cases/, dec0-fork/). The
arg-driven e4 (resolver benchmarks) and e5scan (SVG corpus scan) stay.
Patrol ledger (patrol-before-drop) — essence captured into the archive
BEFORE deletion, in this commit:
- e3's truth protocol emits a world-AABB line only for rotated
NON-GROUP nodes — the group exclusion was undocumented (inert on the
frozen corpus); now stated in model-v2/a/e3-text-ir/README.md.
- edge's flipzero scene: only the flip arm is real resize_drag output;
wall and slide are analytic stand-ins — now stated in
model-v2/a/DECISIONS.md DEC-9 (was recoverable from nowhere else).
- fork's demo-meter definitions (escape = max sibling world-AABB
overrun past the container's ORIENTED painted rect, 0 = contained;
overlap = max pairwise sibling world-AABB intersection area) — now
stated alongside rule V-3 in model-v2/a/dec0-visual-only.md; escape
was defined nowhere else.
- e1 carries no at-risk essence (fully covered by
model-v2/a/e1-rotation-in-flow/ README + verdict).
Deliberately dropped: edge.rs's stale s_flipzero doc comment
(v=80/anchor-95 parameterization) — it contradicts the shipped
code and frozen frames.json (v=60/anchor-75); wrong, not essence.
Part of landing #5; tracking #9.
The anchor_lab -> n0_model / anchor_engine -> n0 renames shortened import paths past rustfmt wrap thresholds. No semantic change. Part of landing #5.
Opt n0 and n0-model into [lints] workspace = true — the workspace table's too_many_arguments allow covers all 9 findings (the documented house pattern: geometry/cache passes many scalar params). Fence e5scan's ASCII decision table as a text block so its '+' rows stop parsing as markdown list items. n0_dev's opt-in is deferred (manifest comment): unreachable_pub flags 46 internal pub items in the binary crate; demoting them to pub(crate) is follow-up hygiene, not landing scope. cargo clippy --no-deps --workspace --exclude grida-canvas-wasm -- -D warnings and cargo fmt --all --check both green. Part of landing #5.
model-v2/README.md and a/README.md become the archive's navigational surfaces: archive banner with the promotion map (a/lab -> crates/ n0-model, engine/ -> crates/n0, a/spike-canvas -> crates/n0_dev), the declared convention that frozen papers' relative paths refer to the pre-promotion layout, the stale gridaco/grida#957 tracking note fixed to #9, run commands updated to workspace form, and the bare "PR #437" reference fixed to gridaco/grida#437 per cross-repo link discipline. Both RFDs' v2 evidence citations flip from cited-by-branch-in-prose to real in-tree links, as their own footnotes promised at landing: paint-model.md -> crates/n0-model/src/model.rs, display-list-contract.md -> crates/n0/src/drawlist.rs (the branch-links footnotes retire). AGENTS.md and README.md: crates/n0 is no longer "reserved" — the project-structure table and workspace list gain the n0 family and the model-v2 archive rows; check/test commands extended with the new crates. Part of landing #5; tracking #9.
… code) All four shots (default, crosszero, rot45, ungroup) diffed against their goldens by 11-50 bytes out of 4.9M pixels with max channel delta 1 — sub-LSB antialiasing drift on edge pixels. Etiology, established by A/B experiment: the PRE-promotion standalone build (worktree at the merge commit 80c60ad, original manifests and standalone Cargo.locks) produces byte-identical output to the promoted workspace build in today's environment — all four shot SHA1s equal across the two builds. The promotion is therefore pixel-exact; the goldens were stale against this machine's current rendering stack (recorded in an earlier environment). A stale always-red byte gate catches nothing; re-blessed from the verified-identical output. The bench budgets (rig/baselines.json) are deliberately NOT re-blessed: the same A/B showed today's shell measures both builds uniformly ~1.7-2.5x over the recorded baselines (the old build even slower than the new), i.e. environmental timing variance, not a promotion regression. Loosening budgets from a slow context would blind the gate. CI-hosted gate baselines remain the filed follow-up. Part of landing #5.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 244 files, which is 144 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (27)
📒 Files selected for processing (244)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI's first-ever linux run of the v2 suites exposed a pre-existing
platform sensitivity: paint::read_pixels requested native-N32
(RGBA on Apple Silicon, BGRA on x86 linux), so byte-order assertions
in animation_profile5_paints failed on ubuntu. Pin the readback to
explicit RGBA8888 premul — the same rule tests/grida_xml.rs already
applies ("so the assertions do not depend on native N32"). Byte
output on Apple Silicon is unchanged: n0 suites and the gate's four
shot goldens re-verified IDENTICAL after the fix.
oxfmt (node.yml fmt:check): the frozen model-v2/ archive joins the
formatter's ignore list (kept verbatim per its tombstone); the live
crates/n0 docs are formatted.
Part of landing #5.
The experiment-ledger codes (E1/E3/E4/E5/E10) were plan-sequence names, not names of things; live artifacts now carry names per their end-state role: - bins: e4 -> resolve_bench, e5scan -> svg_transform_scan - the "historical E3 TextIr" dialect -> "the historical TextIr dialect" (doc comments, error strings, and the test asserting on them, in lockstep) - window/HUD titles and bin headers drop "E10"/"E4" for descriptive names; crate docs describe the experiments instead of citing their ledger ids (the ledger id survives only as a parenthetical archive citation where provenance matters) Deliberately KEPT: registry citations (R-E1, G-E2, L-E1, D-E1, E-A*, DEC-*, V-*, ENG-*) — these cite the archived conformance edge registries, spec-delta register, and decision/contract catalogues; scrubbing them would sever rule-to-registry traceability. SVG animation Profile 0-6 identifiers are spec profile ids, not plan codes. Part of landing #5.
…n-slots
Renames (63 tracked renames, content byte-identical):
- models/{a,b,c,d}.md -> models/{anchor,sheet,bake,wire}.md — the
files now carry the working identifiers their own README declared
- model-v2/a/ -> model-v2/anchor/ — the winner's workbench named for
what it is
- experiment dirs drop their eN- ledger prefixes: rotation-in-flow/,
format-roundtrip/, text-ir/, resolver-spike/, svg-corpus/,
pluggable-layout/, shape-points-scale/ (dec0-* keeps its decision-
register id)
- crates/n0_dev/SPIKE.md -> model-v2/anchor/spike-readout.md — a
frozen session read-out belongs in the archive, not a live crate
(TEXTBOOK.md stays: living migration doctrine)
Every repo reference rewritten (live crates, archive papers, demo
pages); the E1-E10 ids remain the experiment ledger's register keys in
prose and in anchor/README.md's table, which now also carries the
old-name -> new-name map. The archive convention note is amended:
content verbatim, names canonicalized at landing, frozen papers'
relative paths still mean the pre-promotion layout. One frozen
historical command quote in proof.html deliberately keeps the old bin
name (it records what was run).
Part of landing #5.
The XML source format is v2-native, and v2 is n0 — the format now carries the engine's name (owner directive): - modules/APIs: grida_xml -> n0_xml, grida_xml_source -> n0_xml_source, Dialect::GridaXml -> N0Xml; bin grida_xml_render -> n0_xml_render; all n0/n0-model test files renamed in kind - file extension: .grida.xml -> .n0.xml (rig examples + fixtures renamed; every reference updated) - docs/wg RFD family: format/grida-xml*.md -> format/n0-xml*.md with repo-wide link updates; the main RFD gains a "Formerly" continuity note; frontmatter keeps "grida xml" as a search keyword - the term "Grida XML" -> "n0 XML" repo-wide, including the archive (the archive convention note now records the term rename as one of the two canonicalizations applied at landing) Deliberately unchanged: the Draft-0 wire grammar — the <grida version="0"> root element is NOT renamed by this commit; a grammar rename is a spec decision, flagged as an open item in the RFD rather than smuggled through a term sweep. Legacy crates: zero references, zero touch. Part of landing #5; tracking #9.
…nvention Per the fixtures doctrine (deterministic test inputs live under fixtures/<domain>/), the engine's rig corpus moves out of the crate: - crates/n0/rig/corpus/*.replay -> fixtures/n0-replay/ - crates/n0/rig/examples/*.n0.xml -> fixtures/test-n0-xml/examples/ (incl. social-feed/ with its webp assets) - crates/n0/rig/fixtures/*.n0.xml -> fixtures/test-n0-xml/cases/ (incl. component-program/, slot-program/) - all 14 SVG animation profile sources (showcases + boundaries) -> fixtures/test-svg/animation/ — joining the existing SVG fixture domain, and dissolving the cross-crate include (n0-model tests no longer reach into n0's tree; both crates now include from fixtures/) crates/n0/rig/ keeps only baselines.json + probe-baseline.json — gate state (machine-local budget/probe records), not fixtures. Authored relative resource paths inside the moved .n0.xml documents are rebased to their new location (../../images/...); every code and doc reference updated; the gate re-verified green from the new corpus location (shots IDENTICAL, replays DETERMINISTIC, differential MATCHES). Part of landing #5.
The post-landing program (phases 0-6 with entry/exit gates, first three PRs, the owner decision registry, known unknowns) — synthesized from the three-lens panel + adversarial judge and ratified by the owner's review. Git-persisted so it survives agent sessions (named consolidation.md, not *.plan.md — that suffix is the repo's untracked working-plan convention); the umbrella tracking issue mirrors it. Part of landing #5; tracking #9.
The frozen v2 workbench record moves from the repo root (model-v2/) to archive/model-v2/ — a dedicated home for concluded-program records (owner directive: the workbench should not sit at the repo root on main). archive/README.md declares the convention; future concluded programs land as archive/<program>/. Every live reference updated (crate docs, WG docs, AGENTS/README, issue links follow separately); the oxfmt ignore generalizes from model-v2/ to archive/. The tombstone banner records the relocation; archive-internal relative links are unaffected (the tree moved wholesale). Part of landing #5.
Adopt the complete review-only candidate from GitHub Actions run 29700301719, artifact 8446425713 (zip SHA-256 469e3fbf2a526611f61f1d43220dc966bcc414ba290595dd0e97d6b2f3fed978). The manifest records source ed0f310, ubuntu-24.04 x86_64, Rust 1.92.0, and skia-safe 0.93.1. Captured essence: preserve all four named 1360x900 RGBA states, paired shot/timing ownership, same-host determinism, replay determinism, the ENG-0.2 differential, scene-cache equality, and the generous max(1.5x, +50us) timing rule. The candidate gate passed every portable check before installing and uploading exactly five files. Etiology: the outgoing images came from 7c101b9 on aarch64-macos. Main commit abe0305 then deliberately changed the footer from the plan-code name anchor spike — E10 to n0_dev — anchor spike without updating the shots; that source change accounts for exactly 4,010 pixels in each current macOS render. Comparing current source across macOS and Ubuntu changes 2,627-3,992 pixels per shot (0.215-0.326%); visual diffs are confined to text rasterization, while geometry and colors align. The timing numbers move from incomparable macOS measurements to CI-owned min-of-11 measurements: starter 15.639/1.654us and flat10k 1076.711/269.591us for resolve/build. This is an authority transfer, not a performance claim. Deliberate drop: retire only aarch64-macos baseline authority and the stale E10 footer pixels. Preserve their provenance here and in #5/#44. format/grida.fbs, the published canvas-wasm freeze, probe-baseline.json, renderer behavior, and fixture content remain untouched.
Lands the v2 anchor stack as workspace crates. This PR carries the research branch's full 33-commit history (relocated from gridaco/grida#970 at the engine split) plus the landing commits, and merges
--no-ff— never squash.Tracking: #9 (anchor — the v2 node model). Sequencing per the legacy seam program (#27), which completed first: with the v2 engine in-tree, the extraction rule — a module becomes a workspace crate when its second consumer appears in the workspace — is now armed for every seam M4 reviewed.
The move map
model-v2/a/labcrates/n0-modelanchormodel (node records, ops, resolve, pick, n0_xml, textir)model-v2/enginecrates/n0resolve → drawlist → paint, query, journal/replay, damage — takes the reserved public namemodel-v2/a/spike-canvascrates/n0_devmodel-v2/(rest)Promotion is a
git mv, not a rewrite — the engine's own contract (ENGINE.md S-1, archived). The dependency-direction law survives as cargo boundaries: model (no skia) ← engine (skia inpaint.rsonly) ← host.Nobody absorbs anybody at landing. This PR is topology-only. Absorption runs afterward in the promotion program: n0 consumes crates extracted from
crates/grida(cg vocabulary first — M4 ruled it extraction-ready); grida is decomposed, not absorbed, and remains the executable conformance bar until the v1-vs-n0 scoreboard says otherwise. The freeze contract (@grida/canvas-wasm@0.91.0-canary.22) is untouched.Landing commits (on top of the research history)
git mvof the three trees (the reservedcrates/n0stub retires).anchor-*→n0family), manifests rewritten against a patrol ledger (every caveat comment kept/rewritten, named-obsolete, or re-homed; nothing silently dropped), standalone locks/.cargoshims removed, include paths fixed, 15 forbiddengrida.co/docs/wglinks converted to same-repo relative.e1/e3/edge/forkretired; their unrecorded essence (e3's group-AABB exclusion, edge's flipzero stand-in provenance, fork'sescape/overlapmetric definitions) was captured into the archive docs before deletion.n0_dev's opt-in deferred (46unreachable_pubin a binary crate = follow-up hygiene).n0.e4→resolve_bench,e5scan→svg_transform_scan; "historical E3 TextIr" → "the historical TextIr dialect"; window/HUD titles and crate docs describe things instead of citing ledger ids. The archive canonicalizes too:models/{a,b,c,d}.md→{anchor,sheet,bake,wire}.md,model-v2/a/→model-v2/anchor/, experiment dirs dropeN-prefixes, the spike's frozen read-out moves to the archive. Registry citations (R-E1,E-A*,DEC-*,V-*,ENG-*) are deliberately kept — they cite the archived conformance/decision registers; the E1–E10 ids remain the ledger's register keys, with the old→new map inmodel-v2/anchor/README.md..grida.xml→.n0.xml, modules/APIsgrida_xml→n0_xml, the RFD familydocs/wg/format/grida-xml*.md→n0-xml*.md(with a "Formerly" continuity note), the term swapped repo-wide, and issue anchor — the v2 node model #9's body updated in kind (including its stale pre-splitgridaco/gridalinks → landed in-tree paths). The Draft-0 wire grammar — the<grida version="0">root element — is deliberately NOT renamed; that's flagged as an open RFD item, not smuggled through a sweep. Legacy crates: zero references, zero touch.rig/corpus→fixtures/n0-replay/,rig/examples+rig/fixtures→fixtures/test-n0-xml/{examples,cases}/, and all 14 SVG animation profile sources →fixtures/test-svg/animation/— dissolving the cross-crate include (n0-model tests no longer reach into n0's tree).crates/n0/rig/keeps only the machine-local gate baselines. Authored resource paths inside the moved documents rebased; the gate re-verified green from the new corpus.Gates (all run at the head commit)
cargo test --workspace --exclude grida-canvas-wasm— green (legacy + v2 suites).cargo clippy --no-deps --workspace --exclude grida-canvas-wasm -- -D warningsandcargo fmt --all --check— green (the exact CI commands).git diff main...HEADovercrates/grida*,math2,csscascade,fonts,grida-canvas-wasm,format/,packages/is empty; the rootCargo.lockdiff is additions-only (no existing dep version moved).L0.exact— 0 byte diffs, 65/65.just build(the wasm package) succeeds off the shared lock.Deferred (filed as follow-ups)
WG graduation of the anchor spec (normative rewrite of
models/anchor.md); the extraction cascade (cg first);n0_xmlextraction at its second consumer;math2vsn0-model/mathconvergence; fonts-backed text oracle; the v1-vs-n0 reftest scoreboard; CI-hosted gate with CI-blessed baselines; ENGINE.md contract graduation;n0_devpub-hygiene + workspace-lints opt-in; edition-2024 bump.