Skip to content

refbrowser: cg-reftest skill + Playwright producer + suite manifests#680

Merged
softmarshmallow merged 6 commits intomainfrom
feature/zealous-pasteur-8d9158
Apr 22, 2026
Merged

refbrowser: cg-reftest skill + Playwright producer + suite manifests#680
softmarshmallow merged 6 commits intomainfrom
feature/zealous-pasteur-8d9158

Conversation

@softmarshmallow
Copy link
Copy Markdown
Member

@softmarshmallow softmarshmallow commented Apr 22, 2026

Summary

New HTML/CSS reftest harness: refbrowser. Renders fixtures in Playwright Chromium and cg's htmlcss pipeline in parallel, diffs via @grida/reftest. Suite JSONs gate the comparison; L0.exact must pass 100.00% byte-exact.

Pipeline:

fixtures/test-html/suites/<name>.json
  ├── refbrowser_render.ts (Playwright)   →  expected/<n>.png
  └── cargo run -p cg --example golden_htmlcss --suite
                                          →  actual/<n>.png
                                                  ↓
                                @grida/reftest --threshold 0
                                          →  target/reftests/<name>/report.json

What's included

Producers (both read the same suite JSON):

  • .agents/skills/cg-reftest/scripts/refbrowser_render.ts — new Playwright producer.
  • crates/grida-canvas/examples/golden_htmlcss.rs — accepts --suite <path>; applies extra_css via htmlcss::with_extra_stylesheets so the cascade is symmetric with Chromium.

cg API addition:

  • htmlcss::with_extra_stylesheets(html, &[css]) (crates/grida-canvas/src/htmlcss/mod.rs). Agnostic — any caller with HTML + extra author rules can use it, not test-specific.

Fixture scaffolding:

  • fixtures/test-html/suites/L0.exact.json — byte-exact gate (1 fixture today).
  • fixtures/test-html/suites/L0.coverage.json — aspirational scope (5 fixtures).
  • fixtures/test-html/_reftest/hide-text.css — shared helper; zeros glyph coverage + pins line-height: 1 so Blink-vs-Skia text divergence doesn't dominate non-text diffs.
  • fixtures/test-html/README.md — viewport presets (canvas-md 600×800 default, plus sm/mobile/tablet/desktop), paint-vs-layout authoring rule, promotion workflow.
  • 3 paint fixtures updated with the min-height preset pattern; layout fixtures kept natural-cull.

Docs:

  • .agents/skills/cg-reftest/SKILL.md — full refbrowser pipeline section, WPT see-also, "Reading the score" warning (content-area-relative scoring pitfall), known divergence surfaces as backlog items, and two "future work" heuristic sections:
    • Subtree bisection (aka diff attribution, culprit isolation) — delta-debugging for rendering, TODO.
    • Viewport sweep — width-matrix for layout fixtures, TODO.
  • .agents/skills/fixtures/SKILL.md — cross-ref to the test-html README for the paint-vs-layout rule.

Deps:

  • @playwright/test + tsx added as devDeps of @grida/reftest.

Scores

Verified end-to-end with strict diff (--threshold 0, floor 1.0):

Suite Result
L0.exact 100.00% (1/1) — gate passes
L0.coverage avg 98.49%, min 96.30%, max 100% (5 fixtures)

The sub-100 scores in L0.coverage surface three concrete cg backlog items:

  1. % border-radius not honored (paint-border-radius 98.94%) — border-radius: 50% and H / V forms render as square; fixed-length radii work.
  2. Alpha-compositing rounds differently (paint-background-solid 97.92%, paint-opacity 96.30%) — cg rounds rgba(255,0,0,0.7) over white to 77, Chromium to 76. 1-unit channel delta but real policy divergence.
  3. Layout math under asymmetric padding (box-padding 99.32%) — inner bar's resolved width off by 2-3px across every card.

Each is documented in SKILL.md under "Known divergence surfaces." Not tolerance-excused; the score carries the truth.

Why strict (--threshold 0)

We're building a renderer from scratch. Every pixel delta from Chromium is a decision cg made differently — worth surfacing, not absorbing. The skill walks through the tradeoff: tuning threshold up hides real bugs (see the rgba rounding discovery). Floor is 100.00% byte-exact for L0.exact; L0.coverage is informational.

Not in scope (follow-ups)

  • @grida/reftest --suite <path> flag to pull gate config from the suite JSON and auto-enforce floor. Today CI/dev asserts tests[].similarity_score ≥ gate.floor by reading report.json themselves.
  • Subtree bisection tooling + viewport sweep tooling (both called out as TODO in SKILL.md).
  • Fixing the 3 cg bugs the harness found.

Test plan

  • cargo check -p cg --example golden_htmlcss clean
  • pre-commit hooks pass (oxlint, oxfmt, cargo-fmt, clippy)
  • refbrowser_render.ts --suite L0.exact.json renders expecteds
  • cargo run -p cg --example golden_htmlcss -- --suite L0.exact.json renders actuals
  • @grida/reftest diff on L0.exact returns 100.00% for box-dimensions
  • Same flow on L0.coverage returns avg 98.49% with expected sub-100 on 4 fixtures
  • CI: this branch's changes don't touch any existing test harness; CI should be green on unrelated checks. Reviewer: confirm.

Summary by CodeRabbit

  • New Features

    • Headless HTML/CSS reftest pipeline and CLI-driven renderer: suite-driven manifests, Playwright Chromium PNG outputs, configurable viewports, and support for injecting extra styles during renders.
  • Documentation

    • Expanded pipeline guide: oracle types, scoring/gate interpretation, fixture-authoring rules, known divergence surfaces, and manual gate-check workflow.
  • Fixtures

    • New fixture conventions and README, consolidated suite manifests (per-fixture sidecars removed), and a global helper stylesheet to suppress text for visual tests.
  • Examples

    • Added a true reftest example and suite-driven rendering examples.

New HTML/CSS reftest harness. Pairs the existing @grida/reftest diff
tool with a Playwright-Chromium producer for expecteds and the cg
`golden_htmlcss` example for actuals, driven by suite JSONs under
`fixtures/test-html/suites/`.

What landed:
- `.agents/skills/cg-reftest/scripts/refbrowser_render.ts` — new
  Playwright producer. Reads a `--suite <path>` JSON, iterates
  fixtures, renders each under a fixed viewport with optional
  extra-CSS injection.
- `crates/grida-canvas/src/htmlcss/mod.rs` — new public helper
  `htmlcss::with_extra_stylesheets(html, &[css])` that injects a
  `<style>` block before `</head>` (case-insensitive). Agnostic —
  any caller with HTML + extra rules can use it.
- `crates/grida-canvas/examples/golden_htmlcss.rs` — now accepts
  `--suite <path>`, resolves extra_css relative to the suite file,
  calls `with_extra_stylesheets` so the cascade is symmetric with
  Chromium. FontRepository and CSS reads are hoisted/cached once
  per run.
- `fixtures/test-html/_reftest/hide-text.css` — shared stylesheet
  that zeros glyph coverage (`color: transparent`) and pins
  line-box height (`line-height: 1`) so Blink-vs-Skia text
  divergence doesn't dominate diffs on non-text fixtures.
- `fixtures/test-html/suites/{L0.exact,L0.coverage}.json` — two
  suites. `exact` is the byte-exact gate (floor 1.0); `coverage`
  tracks aspirational scope. Promotion rule: fixture reaches
  100.00% → move entry from coverage to exact.
- `fixtures/test-html/README.md` — viewport preset list and the
  paint-vs-layout authoring rule (paint fixtures force preset size
  via `min-height`; layout fixtures measure natural cull).
- `.agents/skills/cg-reftest/SKILL.md` — refbrowser pipeline,
  "Reading the score" warning (content-area-relative scoring),
  known divergence surfaces (alpha-comp rounding, layout math,
  text, AA on curves, gradients, filters/shadows, `%` radius),
  WPT see-also, and two "future work" technique sections:
  subtree bisection (diff attribution) and viewport sweep
  (width-matrix for layout fixtures).
- `.agents/skills/fixtures/SKILL.md` — one-line cross-ref to
  `fixtures/test-html/README.md` for the paint-vs-layout rule.
- `packages/grida-reftest/package.json` — devDeps bump for
  `@playwright/test` and `tsx`.

Verified end-to-end. L0.exact gates at 100.00% on box-dimensions.
L0.coverage averages 98.49% strict (`--threshold 0 --aa off`) and
surfaces three concrete cg backlog items via sub-100 scores:
`%`-radius not honored (paint-border-radius), alpha-compositing
rounds differently (paint-background-solid, paint-opacity), and
inner-bar width-resolution under asymmetric padding (box-padding).
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Apr 22, 2026 1:51pm
docs Ready Ready Preview, Comment Apr 22, 2026 1:51pm
grida Ready Ready Preview, Comment Apr 22, 2026 1:51pm
viewer Ready Ready Preview, Comment Apr 22, 2026 1:51pm
3 Skipped Deployments
Project Deployment Actions Updated (UTC)
backgrounds Ignored Ignored Preview Apr 22, 2026 1:51pm
code Ignored Ignored Apr 22, 2026 1:51pm
legacy Ignored Ignored Apr 22, 2026 1:51pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a Playwright Chromium–based refbrowser reftest pipeline: new TypeScript CLI renderer, suite-driven rendering support in Rust example, HTML/CSS extra-stylesheet injection helper, fixture suite manifests and fixture sizing updates, reftest helper CSS, dev deps, and expanded documentation and prompts for workflow and authoring.

Changes

Cohort / File(s) Summary
Documentation & Prompts
.agents/skills/cg-reftest/SKILL.md, .agents/skills/fixtures/SKILL.md, .agents/prompts/cg-htmlcss-feature.md, fixtures/test-html/README.md
Extensive docs and prompt template added/updated: terminology, refbrowser reftest pipeline, suite JSON schema, fixture authoring rules (paint vs layout), viewport presets, gating rules, known divergence surfaces, and workflow steps.
Refbrowser CLI Renderer
.agents/skills/cg-reftest/scripts/refbrowser_render.ts
New TypeScript CLI: supports --suite/--fixture, suite parsing/validation, per-fixture overrides, resolves and caches extra_css, launches deterministic Playwright Chromium, renders per-fixture isolated contexts/pages, injects CSS, screenshots to <out-dir>, logs per-fixture status, and sets non-zero exit on errors.
Rust rendering & extra-CSS support
crates/grida-canvas/examples/golden_htmlcss.rs, crates/grida-canvas/src/htmlcss/mod.rs
Example updated for --suite rendering and per-fixture viewport resolution; render pipeline refactored to accept explicit width/height and external font repo; added with_extra_stylesheets() to inject concatenated CSS; introduced per-run CSS cache and extras-aware render helpers.
Fixture suite manifests & HTML changes
fixtures/test-html/suites/L0.coverage.json, fixtures/test-html/suites/L0.exact.json, fixtures/test-html/L0/*.html
Added two suite manifests and updated L0 paint fixtures to enforce html, body { min-height: 800px; box-sizing: border-box; }; some fixtures include per-fixture viewport overrides to stabilize rendering.
Reftest utilities & deps
fixtures/test-html/_reftest/hide-text.css, packages/grida-reftest/package.json
Added hide-text.css to suppress glyphs in reftests; added devDependencies @playwright/test and tsx to reftest package.json.
Small guidance note
.agents/skills/fixtures/SKILL.md
Added explicit requirement: paint/visual-property fixtures must size root element to a preset viewport; references fixtures/test-html/README.md.

Sequence Diagram(s)

sequenceDiagram
participant CLI as CLI
participant Renderer as Refbrowser_Render.ts
participant FS as Filesystem
participant Playwright as Playwright/Chromium
participant Reporter as report.json

CLI->>Renderer: invoke (--suite or --fixture, --out-dir)
Renderer->>FS: read suite JSON or fixture HTML
Renderer->>FS: read extra_css files (cache)
Renderer->>Playwright: launch Chromium
loop per fixture
    Renderer->>Playwright: create context/page (deterministic settings)
    Renderer->>FS: navigate to file://<fixture.html>
    Playwright-->>Renderer: network/fonts ready events
    Renderer->>Playwright: inject <style> (extra_css)
    Playwright-->>FS: screenshot -> save <out-dir>/<stem>.png
    Renderer->>Reporter: record per-fixture status
end
Playwright-->>Renderer: close browser
Renderer-->>CLI: exit (process.exitCode set if failures)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

cg, enhancement, css, canvas

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The linked issue #39 requests a typo fix in README.md, but this PR introduces new documentation without addressing the noted typo. Either fix the typo mentioned in issue #39 or clarify that it is out of scope for this PR and should be addressed separately.
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: introduction of refbrowser reftest harness with Playwright producer and suite manifests.
Out of Scope Changes check ✅ Passed The PR includes changes beyond the refbrowser core: fixture updates (paint-*.html files), documentation (README.md, SKILL.md), and prompt templates that extend the scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/zealous-pasteur-8d9158

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: efd779f173

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

htmlcss::with_extra_stylesheets(&html, &extras)
};

render_to_png(&html, 600.0, &name, out_dir, fonts);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor suite viewport width when rendering cg actuals

Suite manifests now carry per-fixture viewport.width, but the cg producer still renders every fixture at a hard-coded 600.0 width. Any suite entry using a non-600 preset (for example the documented mobile/tablet presets) will generate mismatched actuals even when rendering logic is correct, producing false regressions and making those presets unusable in refbrowser comparisons.

Useful? React with 👍 / 👎.

const cssCache = new Map<string, string>();
try {
browser = await chromium.launch();
ctx = await browser.newContext({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate fixtures by creating context per render

The browser context is created once and reused for the whole suite, so cookies/localStorage/service worker state can leak between fixtures. That makes screenshots order-dependent when fixtures include any script-driven state, which can cause flaky diffs and mask real renderer changes. Creating a fresh incognito context per fixture avoids cross-test contamination.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
.agents/skills/cg-reftest/scripts/refbrowser_render.ts (1)

218-240: Pair-wise i += 2 arg parser is brittle.

The loop assumes every arg comes in --key value pairs. A stray positional, a boolean-like flag, or a swapped order silently produces garbage in args (e.g. --out-dir foo --suite bar parses, but foo --out-dir bar --suite baz does not). Low risk today because only three flags exist and all take values, but worth hardening — iterate one token at a time and match known flags, like golden_htmlcss.rs::parse_args does on the Rust side.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/cg-reftest/scripts/refbrowser_render.ts around lines 218 -
240, The parseArgs function's for-loop (using i += 2) is brittle; change it to
iterate tokens one-by-one over argv, inspect each token (argv[i]) and match
known flags "--out-dir", "--suite", and "--fixture", consume the next token as
the value only for those flags (error if value missing), ignore or error on
unknown tokens/flags, and set args["out-dir"], args["suite"], args["fixture"]
accordingly; keep the existing validation and return structure but use
path.resolve on args["out-dir"] as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/cg-reftest/scripts/refbrowser_render.ts:
- Around line 184-186: The file URL is built incorrectly for Windows by using
`file://${path.resolve(htmlPath)}` which yields invalid backslashes and missing
extra slash and encoding; replace this manual construction by converting the
resolved path to a proper file URL using `pathToFileURL` from `node:url` (the
same module already importing `fileURLToPath`), assign that result to `fileUrl`,
and pass it to `page.goto(fileUrl, { waitUntil: "load" })` so paths, slashes and
percent-encoding are handled correctly.
- Around line 191-193: The current check in the block handling config.wait_for
"fonts" calls page.evaluate(() => document.fonts.ready) which returns an in-page
Promise that Playwright cannot serialize; update the call in the
refbrowser_render logic so the promise is awaited inside the browser context
(e.g., use page.evaluate with an async function or .then to resolve
document.fonts.ready) so the evaluate returns a serializable value and actually
waits before proceeding.

In @.agents/skills/cg-reftest/SKILL.md:
- Around line 391-393: The shell snippet uses the non-portable concatenation
`"$TMPDIR"grida-htmlcss-goldens`; change it to use an explicit separator and a
fallback so it works when TMPDIR is empty (Linux/CI). Replace occurrences of
`"$TMPDIR"grida-htmlcss-goldens` with `"${TMPDIR:-/tmp}/grida-htmlcss-goldens"`
(update both the shown mkdir/cp lines and the repeated occurrence at the other
location) so the path is absolute and portable across platforms.
- Around line 1008-1037: The example invocation for refbrowser_render.ts uses a
non-existent flag (--fixture-dir) and omits the required --out-dir; update the
example to call the script with the supported flags (either --suite or --fixture
as appropriate) and include --out-dir, matching the earlier correct suite-based
invocation shown around lines 366-379; specifically, replace `--fixture-dir
fixtures/test-html/L0` with the supported flag (e.g., `--suite L0` or `--fixture
<name>`) and add `--out-dir target/refbrowser/expected` so the call uses only
the implemented options of refbrowser_render.ts.

In `@crates/grida-canvas/examples/golden_htmlcss.rs`:
- Around line 187-209: In parse_args, unknown long options (a.starts_with("--"))
currently advance i by only 1 which causes the following token (often a flag
value) to be treated as a positional; update the logic in parse_args to either
(A) maintain an explicit known flag set (e.g., include "--suite") and only treat
those as flags, or (B) when encountering a token starting with "--", peek
argv.get(i+1) and if it exists and does not start with '-' treat it as that
flag's value and advance i by 2 (otherwise advance by 1); adjust handling of
variables a, i, argv, suite, and positional accordingly to ensure flag values
are not leaked into positional.

---

Nitpick comments:
In @.agents/skills/cg-reftest/scripts/refbrowser_render.ts:
- Around line 218-240: The parseArgs function's for-loop (using i += 2) is
brittle; change it to iterate tokens one-by-one over argv, inspect each token
(argv[i]) and match known flags "--out-dir", "--suite", and "--fixture", consume
the next token as the value only for those flags (error if value missing),
ignore or error on unknown tokens/flags, and set args["out-dir"], args["suite"],
args["fixture"] accordingly; keep the existing validation and return structure
but use path.resolve on args["out-dir"] as before.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f0df2a5a-a319-4723-ad6a-227ec3d57620

📥 Commits

Reviewing files that changed from the base of the PR and between 37ec479 and efd779f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (13)
  • .agents/skills/cg-reftest/SKILL.md
  • .agents/skills/cg-reftest/scripts/refbrowser_render.ts
  • .agents/skills/fixtures/SKILL.md
  • crates/grida-canvas/examples/golden_htmlcss.rs
  • crates/grida-canvas/src/htmlcss/mod.rs
  • fixtures/test-html/L0/paint-background-solid.html
  • fixtures/test-html/L0/paint-border-radius.html
  • fixtures/test-html/L0/paint-opacity.html
  • fixtures/test-html/README.md
  • fixtures/test-html/_reftest/hide-text.css
  • fixtures/test-html/suites/L0.coverage.json
  • fixtures/test-html/suites/L0.exact.json
  • packages/grida-reftest/package.json

Comment thread .agents/skills/cg-reftest/scripts/refbrowser_render.ts
Comment thread .agents/skills/cg-reftest/scripts/refbrowser_render.ts
Comment thread .agents/skills/cg-reftest/SKILL.md
Comment thread .agents/skills/cg-reftest/SKILL.md
Comment thread crates/grida-canvas/examples/golden_htmlcss.rs
Addresses PR #680 review:

- [P1] golden_htmlcss.rs now parses `viewport.{width,height}` from
  suite defaults and per-fixture entries, passes them through to
  `htmlcss::render`. Previously the cg side rendered every fixture
  at a hard-coded 600×600 regardless of what the suite specified,
  so any non-600 preset (mobile/tablet/desktop) would produce
  mismatched actuals and false regressions. Widths now inherit
  defaults → per-fixture override → DEFAULT_WIDTH, same as extras.

- [P2] refbrowser_render.ts creates a fresh incognito
  `browser.newContext()` per fixture instead of reusing one across
  the whole suite. Previously any cookie / localStorage /
  service-worker state set by fixture N could leak into N+1 and
  make diffs order-dependent. The doc comment already claimed
  per-fixture isolation; the code now matches it.
Addresses PR #680 CodeRabbit review:

- [Major] refbrowser_render.ts: build page URL via `pathToFileURL`
  instead of `file://${path}`. The concatenation produces invalid URLs
  on Windows (backslashes) and mishandles spaces / non-ASCII paths.

- [Major] refbrowser_render.ts: await `document.fonts.ready` inside
  the evaluate callback rather than returning the Promise out. The
  returned `FontFaceSet` can't serialize across Playwright's
  boundary; awaiting it in-page makes the wait explicit.

- [Minor] golden_htmlcss.rs: when `parse_args` encounters an unknown
  `--flag`, peek the next token and consume it as the flag's value
  if it doesn't start with `-`. Previously an unknown `--foo bar`
  would leak `bar` into the positional stream and be treated as a
  file path.

- [Minor] SKILL.md: replace `"$TMPDIR"grida-htmlcss-goldens` with
  `"${TMPDIR:-/tmp}/grida-htmlcss-goldens"` — portable across macOS
  (TMPDIR ends with `/`, got lucky) and Linux/CI (TMPDIR unset).

- [Major] SKILL.md example 1008-1037: drop the nonexistent
  `--fixture-dir` flag and bring the invocation in line with the
  suite-based example earlier in the doc.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/grida-canvas/examples/golden_htmlcss.rs`:
- Around line 146-176: render_with_extras currently panics if reading html_path
fails; change that to skip the failing fixture with a warning (same style as
ensure_css_cached) instead of aborting the whole suite: replace the
std::fs::read_to_string(...).unwrap_or_else(...) call in render_with_extras with
a match or if let that on Err logs a warning including html_path and the error
and then returns early (so it doesn't call htmlcss::with_extra_stylesheets or
render_to_png); keep success path unchanged so html, name, extras handling and
render_to_png remain the same.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01923602-83e8-4c8e-825f-3bb97ed57930

📥 Commits

Reviewing files that changed from the base of the PR and between 58cf798 and b482a57.

📒 Files selected for processing (3)
  • .agents/skills/cg-reftest/SKILL.md
  • .agents/skills/cg-reftest/scripts/refbrowser_render.ts
  • crates/grida-canvas/examples/golden_htmlcss.rs
✅ Files skipped from review due to trivial changes (1)
  • .agents/skills/cg-reftest/scripts/refbrowser_render.ts

Comment thread crates/grida-canvas/examples/golden_htmlcss.rs
…uite

Addresses PR #680 CodeRabbit review:

- A missing / misnamed entry in a suite (common for the aspirational
  L0.coverage.json) previously panicked in render_with_extras and
  killed every subsequent fixture. Match the style of
  ensure_css_cached: print a warning with the path + error and
  continue with the next fixture.
@vercel vercel Bot temporarily deployed to Preview – blog April 22, 2026 13:09 Inactive
@vercel vercel Bot temporarily deployed to Preview – backgrounds April 22, 2026 13:09 Inactive
@vercel vercel Bot temporarily deployed to Preview – grida April 22, 2026 13:09 Inactive
@vercel vercel Bot temporarily deployed to Preview – viewer April 22, 2026 13:09 Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
crates/grida-canvas/examples/golden_htmlcss.rs (2)

244-271: Consider --suite=PATH form and a friendlier missing-value message.

Two small usability gaps in the current parser:

  1. --suite=fixtures/.../L0.exact.json (the GNU long-option form) isn’t accepted — only the space-separated form works. A quick split_once('=') handles both without adding dependencies.
  2. --suite with no following token panics via unwrap_or_else(|| panic!(...)). A plain eprintln! + std::process::exit(2) gives a cleaner CLI UX (no Rust backtrace noise on RUST_BACKTRACE=1).

Neither is blocking — --suite is the only flag and the current behavior is documented — filing as optional.

♻️ Proposed tweak
     while i < argv.len() {
         let a = &argv[i];
-        if a == "--suite" {
+        if let Some(v) = a.strip_prefix("--suite=") {
+            suite = Some(v.to_string());
+            i += 1;
+        } else if a == "--suite" {
             let v = argv
                 .get(i + 1)
-                .unwrap_or_else(|| panic!("--suite requires a path argument"));
+                .unwrap_or_else(|| {
+                    eprintln!("error: --suite requires a path argument");
+                    std::process::exit(2);
+                });
             suite = Some(v.clone());
             i += 2;
         } else if a.starts_with("--") {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/grida-canvas/examples/golden_htmlcss.rs` around lines 244 - 271, The
parse_args function should accept both `--suite PATH` and `--suite=PATH` forms
and emit a friendly error instead of panicking when no value is provided: in
parse_args, detect if a token starts_with("--suite") and if so try
split_once('=') to extract the path when present, otherwise look at
argv.get(i+1); if no value is found print a concise message with
eprintln!("--suite requires a path argument") and call std::process::exit(2)
instead of unwrap_or_else panic; ensure you set suite = Some(value.clone()) and
advance i appropriately in each branch so the positional stream remains correct.

84-102: Minor: viewport inheritance is correct but the unwrap_or(defaults.viewport) step is dead code.

When entry.viewport is None, vp becomes defaults.viewport, so vp.width.or(defaults.viewport.width) is a no-op (same value on both sides). When entry.viewport is Some(v), the per-field .or(defaults.viewport.*) chain does the real work. The net effect is correct — entries get per-field inheritance — but Line 92 can be dropped without changing semantics:

♻️ Simplification
-    let vp = entry.viewport.unwrap_or(defaults.viewport);
-    let width = vp
-        .width
-        .or(defaults.viewport.width)
-        .unwrap_or(DEFAULT_WIDTH);
-    let height = vp
-        .height
-        .or(defaults.viewport.height)
-        .unwrap_or(DEFAULT_HEIGHT);
+    let vp = entry.viewport.unwrap_or_default();
+    let width = vp.width.or(defaults.viewport.width).unwrap_or(DEFAULT_WIDTH);
+    let height = vp.height.or(defaults.viewport.height).unwrap_or(DEFAULT_HEIGHT);

Also worth noting for future doc updates: extra_css inheritance is all-or-nothing (an explicit "extra_css": [] on a fixture overrides defaults rather than merging), which differs from viewport’s per-field merge. Probably intentional for list-typed fields, but a one-line note in the file-level doc comment (Lines 30-32) would prevent surprises.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/grida-canvas/examples/golden_htmlcss.rs` around lines 84 - 102, The
code in resolve_entry binds vp by value with
entry.viewport.unwrap_or(defaults.viewport), which makes the subsequent
per-field .or(defaults.viewport.*) a no-op; change vp to a reference so
per-field inheritance works as intended by using
entry.viewport.as_ref().unwrap_or(&defaults.viewport) (reference the
resolve_entry function and the vp, width, height variables), leaving the
width/height lines unchanged so they can call
vp.width.or(defaults.viewport.width). Also consider adding a short doc-note
about extra_css being an all-or-nothing override elsewhere in the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/grida-canvas/examples/golden_htmlcss.rs`:
- Around line 244-271: The parse_args function should accept both `--suite PATH`
and `--suite=PATH` forms and emit a friendly error instead of panicking when no
value is provided: in parse_args, detect if a token starts_with("--suite") and
if so try split_once('=') to extract the path when present, otherwise look at
argv.get(i+1); if no value is found print a concise message with
eprintln!("--suite requires a path argument") and call std::process::exit(2)
instead of unwrap_or_else panic; ensure you set suite = Some(value.clone()) and
advance i appropriately in each branch so the positional stream remains correct.
- Around line 84-102: The code in resolve_entry binds vp by value with
entry.viewport.unwrap_or(defaults.viewport), which makes the subsequent
per-field .or(defaults.viewport.*) a no-op; change vp to a reference so
per-field inheritance works as intended by using
entry.viewport.as_ref().unwrap_or(&defaults.viewport) (reference the
resolve_entry function and the vp, width, height variables), leaving the
width/height lines unchanged so they can call
vp.width.or(defaults.viewport.width). Also consider adding a short doc-note
about extra_css being an all-or-nothing override elsewhere in the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53653858-ba9c-48f1-8c16-0c28697e6f3c

📥 Commits

Reviewing files that changed from the base of the PR and between b482a57 and 5d88025.

📒 Files selected for processing (1)
  • crates/grida-canvas/examples/golden_htmlcss.rs

Pastable template for driving a single CSS feature through
audit → ground → fixture → implement → verify, with reftest-backed
gate policy so every landing has objective Chromium-parity proof.

Shipped as a prompt (not a skill) because it's an orchestrator over
existing /research, /fixtures, /cg-reftest skills — opt-in is the
right invocation model for a heavy 5-phase loop.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/prompts/cg-htmlcss-feature.md:
- Around line 24-30: The unlabeled fenced block containing the ASCII phase
diagram (the multi-line box diagram starting with "┌──────────┐  ┌──────────┐ 
┌──────────┐") should be given an explicit fence language to satisfy
markdownlint MD040; update the opening triple-backtick to include a language
token (e.g., "text") so the block becomes a labeled fence and leave the closing
triple-backticks unchanged.
- Around line 292-316: The unlabeled fenced code block that starts with "Drive
the htmlcss feature loop for: <property or behavior>." must be labeled to
satisfy MD040; update that fenced block’s opening fence to include a language
tag (use "text") so it becomes ```text and leave the closing fence as-is; locate
the block by searching for the exact string "Drive the htmlcss feature loop
for:" in .agents/prompts/cg-htmlcss-feature.md and change the fence label
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 164ef97c-ff58-4c47-91d6-156c932c41ef

📥 Commits

Reviewing files that changed from the base of the PR and between 5d88025 and 46a4537.

📒 Files selected for processing (1)
  • .agents/prompts/cg-htmlcss-feature.md

Comment thread .agents/prompts/cg-htmlcss-feature.md Outdated
Comment thread .agents/prompts/cg-htmlcss-feature.md Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant