- name
- dev-render-htmlcss-svg-feature
- description
- Manual-invocation only. Five-phase feature loop (audit → ground → fixture → implement → verify) for driving a single SVG feature to Chromium parity in the grida `htmlcss::svg` renderer. Sibling to `dev-render-htmlcss-feature` (HTML/CSS path); same loop shape, different corpus (resvg-test-suite + Chrome bake) and different scoring (multi-oracle: consensus / disputed / UB).
# grida-htmlcss::svg — feature loop
> **Status (2026-07-24): the target module is a frozen donor.** Per
> [D-N, the SVG engine of record](../../../docs/wg/consolidation/svg-engine-of-record.md),
> no new behavior lands in `htmlcss::svg`; SVG capability evolves as rungs
> on the engine of record (`websem → rframe → n0`), Chromium-baked under
> `fixtures/web-first/`. Use this loop only for owner-directed wire-back or
> donor study — not for new feature work.
**What this is.** A heavy, manually-invoked loop for driving a single
SVG feature forward in `crates/htmlcss/src/svg/`. Do not
auto-trigger; load only when the user explicitly runs it.
**Sister skill.** [`dev-render-htmlcss-feature`](../dev-render-htmlcss-feature/SKILL.md)
covers the HTML/CSS path (different code surface, different fixture
suite, single oracle). This SKILL covers the SVG path: same five-phase
shape, but different corpus and different scoring policy.
| Aspect | HTML/CSS sister skill | This (SVG) skill |
| --------------- | -------------------------------------------- | ------------------------------------------------------------------- |
| Renderer module | `crates/htmlcss/src/` (excl. `svg/`) | `crates/htmlcss/src/svg/` |
| Corpus | `fixtures/test-html/L0/` (we author) | `fixtures/local/resvg-test-suite/` (vendored, 1679 fixtures) |
| Oracle | Playwright Chromium (refbrowser, on-the-fly) | `expected.png` (suite author) **+** baked Chrome PNG |
| Scoring | Single oracle, gate=`L0.exact` floor 1.0 | Multi-oracle: consensus / disputed / UB; gate = consensus pass-rate |
| Tooling | `cargo run -p grida_wpt -- render --suite …` | `cargo run -p grida_dev -- reftest <run\|bake\|inspect\|summary>` |
---
## Why multi-oracle (read this before phase 1)
The resvg-test-suite ships one `expected.png` per fixture — but that
PNG is the **suite author's** read of the spec, not a browser
oracle. For ~12% of fixtures Chrome diverges from `expected.png`
(disputed) and ~4% have no defined behavior at all (UB). The harness
ingests the suite's `results.csv` (a 9-renderer status matrix) to
classify each fixture:
- **Consensus** (`chrome=PASSED` in csv): `expected.png` is
authoritative. Optimize against this set — it is the headline
parity number.
- **Disputed** (`chrome=FAILED/CRASHED`): Chrome diverges from
`expected.png`. The harness scores against a baked Chrome PNG too;
effective score = max(`vs_expected`, `vs_chrome`). Passing on
either oracle counts.
- **UB** (`chrome=UNKNOWN`): excluded from headline parity entirely.
Don't optimize, don't regress, don't celebrate a pass.
When you read a score, always read its bucket. A 0.30 score on a
disputed fixture is meaningless without `vs_chrome`; a 0.30 score on
a consensus fixture is a real bug.
---
## The five phases
```text
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 1. AUDIT │→ │2. GROUND │→ │3. FIXTURE│→ │ 4. IMPL │→ │5. VERIFY │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │
└───── ← ─── loop ← ─── score < floor ← ─── diff ← ───────┘
```
### 1. Audit — "what's the actual state of this feature?"
**Question.** Where does the feature live in the htmlcss::svg pipeline
today? Which resvg fixtures touch it, and where do they fall in the
oracle buckets?
**Actions.**
- Scan `crates/htmlcss/src/svg/` for the property/element name
in dom/parse, style/cascade, layout, paint. SVG features can be
parsed-but-dropped, computed-but-misrouted, or just unhandled —
each has a different fix shape.
- Get the suite-wide picture and the worst-fail shortlist:
```sh
cargo run --release -p grida_dev -- reftest summary
```
Headline = consensus pass-rate. The `worst consensus failures` block
is the real-bug shortlist.
- For each fixture relevant to your feature, run `inspect`:
```sh
cargo run --release -p grida_dev -- reftest inspect <fixture>
```
This prints oracle status, all 9 renderer flags, last-run scores
vs both oracles, and the four PNG paths. **This is the agent's
primary diagnostic tool.** Use `--json` for machine consumption.
- Filter the run to just the feature's category:
```sh
cargo run --release -p grida_dev -- reftest run \
--suite-dir fixtures/local/resvg-test-suite \
--renderer htmlcss --threshold 0.1 \
--filter 'filters_feSpecularLighting'
```
- Cross-reference [`crates/htmlcss/src/svg/README.md`](../../../crates/htmlcss/src/svg/README.md)
for the Blink module map and any prior decision.
**Deliverable.** A short audit note (PR draft / task prompt):
- _Pipeline state_: not-parsed / parsed-but-dropped / partial /
Chromium-parity-except-X. Cite the file:line.
- _Affected fixtures, partitioned by oracle_:
- `consensus` (must fix): list with `vs_expected` scores.
- `disputed` (situational): list with `vs_expected` and
`vs_chrome`. If we already match Chrome, this is a non-issue.
- `ub` (ignore): list count only.
- _Priority bucket_: easy-and-important / easy-low-value /
hard-important / hard-low-value.
**Exit when.** You can name the broken pipeline stage **and** point
at a specific consensus fixture that demonstrates it. If your only
failing fixture is disputed-but-we-match-chrome, there is no bug to
fix.
### 2. Ground — "how do real engines solve this?"
**Question.** What's the canonical implementation strategy in a
mature SVG engine? We adapt; we don't invent.
**Actions.** Invoke `/research`. The references for SVG features:
- **Blink (`third_party/blink/renderer/core/svg/`,
`core/layout/svg/`, `core/paint/svg/`)** — authoritative for the
layout/paint divergence calls; the engine our chrome-baseline is
rendered against.
- **WebKit (`Source/WebCore/svg/`)** — third voice. Useful when
Blink has Chrome-specific behavior (the disputed bucket often
contains Chrome quirks).
- **resvg (`crates/resvg/`, `crates/usvg/`)** — Rust, readable.
Useful for parsing and computed-value rules. But remember: when we
diverge from resvg toward Chrome, that's correct for the disputed
bucket — resvg authored most of the contested expecteds.
- **Spec** (SVG 1.1 / 2 / specific module). Read this first.
**Deliverable.** A research note with:
- The spec section(s) that govern behavior.
- 3–6 lines on Blink's structure, plus contrast with resvg's read
when relevant.
- Explicit deviation, if any, and which oracle motivates it (Chrome
vs. resvg/expected). Cite the disputed fixture(s) by name.
**Exit when.** You can defend the implementation shape by pointing
at Blink, **and** if disputed fixtures are involved, you can name
the oracle the implementation is targeting and why.
### 3. Fixture — "which test proves it?"
**Different from the HTML/CSS path: we don't author SVG fixtures —
we pick them.** The 1679 resvg-test-suite fixtures already cover
nearly every SVG primitive. Authoring new ones is reserved for
genuine gaps in the upstream coverage.
**Actions.**
- Identify 1–3 fixtures that exercise the feature unambiguously.
`reftest inspect <fixture>` gives you the rel path, oracle status,
and current score in one shot.
- Prefer **consensus** fixtures as your primary target — they have an
unambiguous oracle. A consensus fixture going from 0.3 → 1.0 is
a clean win.
- For features whose semantics Chrome interprets differently, you
may need a **disputed** fixture as your target. That is fine, but
the bake oracle (Chrome PNG) must be present. Run:
```sh
cargo run --release -p grida_dev -- reftest bake --filter <category>
```
Verify with `inspect` that `chrome.png` is now resolved.
- If genuinely no upstream fixture covers the case, consider whether
the feature is in scope. Authoring a new SVG fixture should be
rare; check WPT (`fixtures/local/wpt/svg/`) before authoring.
**Deliverable.**
- A list of 1–3 target fixtures with their rel paths, oracle status,
and current scores.
- For disputed targets: confirmation that the chrome-baseline PNG
exists for each.
**Exit when.** Each target fixture renders through `reftest run`
(filtered), produces all four PNGs in the result dir, and has a
known starting score against the chosen oracle.
### 4. Implement — "what code change realizes the behavior?"
**Question.** What is the minimum diff in
`crates/htmlcss/src/svg/` to make the targets pass?
**Actions.**
- Touch the smallest surface possible. Don't combine refactor +
feature; the multi-oracle scoring already adds noise to the
signal — adding refactor noise on top is a debugging nightmare.
- Trace the SVG pipeline end-to-end:
parse (`dom/`) → style cascade (`style/`) → geometry/layout
(`geometry/`, `layout/`) → paint (`paint/`). A feature can fail
at any stage.
- Add unit tests where behavior is data-assertable (computed paint
server, resolved length, geometry). The Rust tests catch
regressions the reftest cannot, especially for SVG's lots of
"value resolved correctly but ended up at the wrong z-position"
bugs.
- Mirror the Blink structure when in doubt; the htmlcss::svg module
map docs are explicit about Blink anchors.
**Deliverable.**
- Code change scoped to the feature.
- Any new data tests.
- A one-line behavior summary in the PR description, written in
spec terms (e.g. "feSpecularLighting now treats specularExponent=0
as the default 1, matching Chrome / SVG 1.1 §15.21.4").
**Exit when.** `cargo check -p grida -p grida_dev` is clean,
existing unit tests pass, and the targeted fixtures render via
`reftest run` without panic.
### 5. Verify — "does it actually match Chromium?"
**Actions.**
1. Run the targeted slice:
```sh
cargo run --release -p grida_dev -- reftest run \
--suite-dir fixtures/local/resvg-test-suite --renderer htmlcss \
--threshold 0.1 --filter <category>
```
2. Read the consensus pass-rate and worst-N:
```sh
cargo run --release -p grida_dev -- reftest summary --report \
target/reftests/resvg-test-suite.htmlcss/report.json
```
3. For every target fixture, `reftest inspect <name>`. Read both
`vs_expected` and `vs_chrome`. If the oracle is `consensus`, both
should match. If `disputed`, at least one must clear `pass_floor`
(default 0.95).
4. **Read the diff PNG.** `inspect` prints its path. A high
similarity score on a sparse fixture can mask a completely
broken feature. Open the PNG.
5. Run the **full suite** at least once before declaring done. New
features routinely regress neighbors via shared codepaths
(cascade ordering, paint server resolution, etc.):
```sh
cargo run --release -p grida_dev -- reftest run \
--suite-dir fixtures/local/resvg-test-suite --renderer htmlcss \
--threshold 0.1
cargo run --release -p grida_dev -- reftest summary
```
Compare the headline consensus pass-rate to the pre-change number.
It must not drop. If it dropped, something regressed — diff the
`worst_consensus` lists before vs. after.
**Close the loop.**
- Headline rose, no consensus regressions? Done.
- Headline rose but disputed bucket got worse? Investigate. We may
have over-fit to the resvg interpretation and broken Chrome
alignment.
- Headline dropped on consensus? Stop. Either revert or fix; do
**not** lower `pass_floor`.
**Deliverable.** PR description with:
- Before/after consensus pass-rate (from `reftest summary`).
- Per-fixture before/after scores for the targets, with both
`vs_expected` and `vs_chrome` when relevant.
- Diff PNG review for any score < 1.0.
- Specific divergence-surface note for any residual gap.
**Exit when.** Someone reading the PR description without the
context can tell exactly which fixtures moved, which oracle they
moved against, and whether anything regressed.
---
## Handoffs and artifacts
| Phase | Artifact | Location |
| --------- | --------------------------------------------------------- | --------------------------------------------------------------- |
| Audit | Current-state note, oracle-partitioned fixture list | PR description / task prompt |
| Ground | Research note (spec + Blink/resvg cross-ref) | PR description or `docs/wg/feat-2d/` |
| Fixture | Target fixture list with oracle status; baked chrome.png | `fixtures/local/resvg-test-suite/chrome-baseline/` (gitignored) |
| Implement | Code change, data tests, spec-language behavior summary | `crates/htmlcss/src/svg/` |
| Verify | Before/after summary, per-fixture scores, diff PNG review | PR description |
---
## Gate policy
The gate is the **consensus pass-rate** — `oracle_buckets.consensus.passing
/ oracle_buckets.consensus.total` from `report.json` (aka the headline
in `reftest summary`).
- The consensus pass-rate must not drop. Period.
- Disputed bucket: track but do not gate. Improvements here are good;
small regressions are tolerable if `vs_chrome` improved overall.
- UB bucket: never gate, never optimize, never report.
- `pass_floor` (default 0.95) is the per-fixture passing threshold.
Don't relax it to "make a fixture pass." If a fixture is slipping
by 0.001, fix the renderer or eat the score.
### What "destructive" means here
A change is destructive if it:
- Lowers the consensus pass-rate.
- Lowers `pass_floor` in `reftest.toml`.
- Removes `[test.oracles]` config or detaches the chrome baseline.
- Increases `--threshold` to absorb real divergence.
- Reclassifies a UB fixture as consensus by editing `results.csv` to
fit our renderer.
None are acceptable without explicit human approval.
---
## Anti-patterns
| Anti-pattern | Why it fails | Instead |
View on GitHub