Summary
There is no repeatable way to boot the real app and look at it. scripts/check-page.mjs screenshots a static page load; it cannot drive the UI, so nothing verifies that a screen renders correctly with live data.
This gap has cost something concrete: the centre-aligned category headers shipped in #506 passed 1313 tests and were caught only by an ad-hoc browser session. That session was rebuilt from scratch each time and then discarded.
Why not just goldens
Goldens are the right fix for layout regressions and are tracked separately. They cannot tell you the app boots, that the real feed parses, or that CanvasKit and fonts load — all of which are exactly what a browser check covers. The two are complementary.
What to add
A scripts/visual-check.mjs alongside the existing check-page.mjs / screenshot-batch.mjs, taking a URL and a screenshot prefix, that boots the app, waits for first render, and drives a scripted sequence with a screenshot per step.
Environment notes worth recording with it, all discovered the hard way:
- Chromium path is
/opt/pw-browsers/chromium-1194/chrome-linux/chrome. Playwright's default chromium/chrome-linux/chrome does not exist in the managed image and fails with "executable doesn't exist".
- Flutter web builds no DOM semantics tree until accessibility is enabled. Clicking
flt-semantics-placeholder programmatically turns it on. Even then it exposed only the nav bar in practice, so coordinate-driven clicks were the workable approach — worth stating so the next attempt doesn't sink time into the semantics route.
- Egress-blocked hosts. In the sandbox,
www.gstatic.com and fonts.gstatic.com were refused, which breaks three things at once: CanvasKit (blank page), Firebase (firebasejs module import), and every webfont including Flutter's Roboto fallback — so no glyphs render at all and the UI looks empty. Workarounds if still blocked: set "useLocalCanvasKit":true in build/web/flutter_bootstrap.js to use the bundled copy, stub **/firebasejs/**, and serve a TTF from the Flutter SDK for **fonts.gstatic.com**.
- The font substitution invalidates any typography judgement — every Google font collapses to one face, so typeface questions cannot be answered from these screenshots.
- Preview
*.pages.dev URLs were blocked; serving build/web on localhost worked.
The allow-list has since been widened, so the first step should be to try it clean and treat the stubs above as fallbacks.
Where it belongs
Script in scripts/, notes in the diagnostics-and-tooling skill, which already owns headless page checks and ships helper scripts.
Impact
Low, but it is the only route to answering "does this actually render on a real device" for an agent, and today that answer is re-derived from zero every time.
Summary
There is no repeatable way to boot the real app and look at it.
scripts/check-page.mjsscreenshots a static page load; it cannot drive the UI, so nothing verifies that a screen renders correctly with live data.This gap has cost something concrete: the centre-aligned category headers shipped in #506 passed 1313 tests and were caught only by an ad-hoc browser session. That session was rebuilt from scratch each time and then discarded.
Why not just goldens
Goldens are the right fix for layout regressions and are tracked separately. They cannot tell you the app boots, that the real feed parses, or that CanvasKit and fonts load — all of which are exactly what a browser check covers. The two are complementary.
What to add
A
scripts/visual-check.mjsalongside the existingcheck-page.mjs/screenshot-batch.mjs, taking a URL and a screenshot prefix, that boots the app, waits for first render, and drives a scripted sequence with a screenshot per step.Environment notes worth recording with it, all discovered the hard way:
/opt/pw-browsers/chromium-1194/chrome-linux/chrome. Playwright's defaultchromium/chrome-linux/chromedoes not exist in the managed image and fails with "executable doesn't exist".flt-semantics-placeholderprogrammatically turns it on. Even then it exposed only the nav bar in practice, so coordinate-driven clicks were the workable approach — worth stating so the next attempt doesn't sink time into the semantics route.www.gstatic.comandfonts.gstatic.comwere refused, which breaks three things at once: CanvasKit (blank page), Firebase (firebasejsmodule import), and every webfont including Flutter's Roboto fallback — so no glyphs render at all and the UI looks empty. Workarounds if still blocked: set"useLocalCanvasKit":trueinbuild/web/flutter_bootstrap.jsto use the bundled copy, stub**/firebasejs/**, and serve a TTF from the Flutter SDK for**fonts.gstatic.com**.*.pages.devURLs were blocked; servingbuild/webon localhost worked.The allow-list has since been widened, so the first step should be to try it clean and treat the stubs above as fallbacks.
Where it belongs
Script in
scripts/, notes in thediagnostics-and-toolingskill, which already owns headless page checks and ships helper scripts.Impact
Low, but it is the only route to answering "does this actually render on a real device" for an agent, and today that answer is re-derived from zero every time.