# Browser Verify UI Changes

> Use whenever a task has a user-visible outcome — chart, overlay, UI state, route change. Before marking the task complete, take a chrome-devtools MCP screenshot (or evaluate_script against actual render state) and visually diff against the operator's reference. Type-check, lint, unit tests, and e2e all passing is NECESSARY but NOT SUFFICIENT for canvas/UI work — paint-time bugs hide from synthetic tests.

- Skill: `traderspost/browser-verify-ui-changes` (Agent Skill)
- Install (CLI): `npx skillmds@latest add traderspost/browser-verify-ui-changes`
- Raw SKILL.md: https://api.skillmd.com/api/skills/traderspost/browser-verify-ui-changes/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: traderspost (https://skillmd.com/u/traderspost)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/traderspost/browser-verify-ui-changes

---


# Browser-verify UI changes before marking done

This is the single most consequential discipline lesson from canvas-heavy UI work with an agent. It was learned the hard way after an agent shipped a broken chart indicator **three times in a row** with all gates green:

1. Type-check passed
2. Biome / ESLint passed
3. Unit tests passed
4. e2e (Playwright) passed
5. The chart was still wrong

The bugs only manifested at paint time:
- A chart lib's `isWhitespace=true` stripping rows from data without warning
- NaN sentinels for off-screen positions silently dropping draws
- Array-index misalignment between primary and overlay series

Synthetic tests literally cannot catch these. The fix has to be at the same layer the bug manifests: **read the actual rendered pixels**.

## When to use

Reach for this skill **for every task** that:

- Changes a chart, overlay, indicator, or any visible chart element
- Changes a layout, toolbar button, popover, menu, page
- Changes a route or URL behavior the operator interacts with
- Adds a visual state (active, hover, disabled, loading, empty)

Reach for it ESPECIALLY when:

- The task seems "simple enough" to skip verification (this is the trap)
- You've already burned an iteration on a similar bug
- The reference is a screenshot or an existing tool you're matching

## The pattern

Treat the edit and the visual verification as **one atomic unit**. The moment you think "I'm done" is the moment you MUST screenshot and diff before saying so.

```ts
// After every UI-affecting commit/edit, run via chrome-devtools MCP:

await mcp__chrome-devtools__navigate_page({ type: "reload" })
await mcp__chrome-devtools__take_screenshot()
// Compare to the operator's reference at the PIXEL LEVEL:
//   - shape, position, color, breaks, alignment
// If anything is off, the task is NOT done.

// For chart state that doesn't show clearly in a screenshot, use
// evaluate_script to interrogate the chart's actual data:
await mcp__chrome-devtools__evaluate_script({
  function: `() => {
    const hook = window.__myDebugHook;
    const series = hook.chart.panes()[0].getSeries()[0];
    return { data: series.data(), options: series.options() };
  }`
})
```

If browser access is unavailable in the session, the task is `in_progress` and you say so explicitly — NEVER `completed` on assumed-correctness.

## Why synthetic tests fall short

| Test class | Catches | Misses |
|---|---|---|
| TypeScript | type errors, missing fields | render-time data shape issues |
| Lint | code-style, banned patterns | runtime behavior |
| Unit | pure-function math, hook reducers | canvas pixel correctness, time-axis layout |
| e2e (Playwright) | "the canvas exists with N candles" | "the candles are drawn at the right Y position" |
| Visual screenshot diff (chrome-devtools MCP) | pixel-level correctness ✓ | (this is the floor) |

## The agentic-loop rule

Codify in your project's `CLAUDE.md` (or equivalent) so the agent reads it before every UI task:

```
**No browser-affecting task is "done" until verified in the browser via
chrome-devtools MCP.** For any change with a user-visible outcome (chart,
UI, route, overlay, popover, etc.), reload the page, exercise the
specific behavior end-to-end through the live app (click the button,
drag the chart, toggle the setting), and screenshot or programmatically
confirm the expected state. tsc + biome + unit + e2e passing is
necessary but not sufficient — synthetic tests don't catch what only
manifests at paint time. Acceptable verification = a chrome-devtools
take_screenshot, take_snapshot, or evaluate_script inspecting actual
chart state. If browser access is unavailable in the session, mark the
task in_progress and say so explicitly rather than completing on
assumed-correctness.
```

## Gotchas

- **The rule loaded in context is not a checklist.** When this rule first lands, the agent often ignores it on the NEXT iteration because "context I've read" ≠ "checklist I run before declaring done." Make verification a forcing-function step in the task workflow, not just a documented rule.
- **"It looks right" isn't verification.** Compare against the explicit reference. The operator's screenshot, the Pine source render, the previous-version screenshot — there's always a reference.
- **Screenshots aren't always enough.** Some bugs (off-by-one OHLC, wrong volume, hidden state) show up in `series.data()` but not in pixels. Use `evaluate_script` for those.
- **Don't claim "verified" if the dev server might be stale.** Force a navigate-reload before screenshotting.
- **Browser-verification doesn't replace tests.** It catches paint-time bugs the tests can't. Keep the tests; add the verification.

## Reference implementation

The rule above was added to a project's `CLAUDE.md` mid-session after an indicator port shipped wrong three times in a row despite green CI. The exact incident — a "trend fill" implementation that recolored the wrong layer, then drew the fill at the wrong y-range, then connected lines across session gaps — was each time invisible to type-check + unit + e2e, and each time obvious in a screenshot.

The lesson was written up as a long-form memory note `feedback_browser_verify_before_done.md` in the project's memory store, then codified as a numbered hard rule (`#9`) in `CLAUDE.md` so future agent sessions load it as context.

## Related skills

- `lightweight-charts-integration` — the chart-specific paint-time bugs that escape synthetic tests
- `react-canvas-race-conditions` — another class of bug invisible to unit tests
- `read-reference-source-first` — its companion discipline ("don't guess; read the source")

