--- name: qa-verify description: Verify built UI against this project's hard rules and against the design — runs the mechanical check script, then the judgement checks a script cannot make (visual fidelity vs Figma, token naming, motion choice, semantics, contrast through every frame of an entrance, responsive behaviour including live resizes, phone overflow and dark mode), and fixes what it finds in a loop until clean. Use after building or changing any page, view, section or component, before committing, and whenever the user says "QA this", "check my work", or "is this ready to ship". allowed-tools: Bash, Read, Grep, Glob, Edit, Write --- # QA & verification Two layers. The script decides what is decidable; you decide the rest. Never report "done" on the script alone — it cannot see a design. Browser checks below use `tools/qa/` against a running `yarn build && yarn start` (`yarn qa:setup` once; every tool takes `--url`; see `tools/qa/README.md`). Probes need a person's UA — the default puppeteer UA gets the robot form, which has no motion, no cursor and no scene, and "proves" a bug gone that a person still sees. **Look at every screenshot a tool writes** (Read the image) — numbers flag, the eye decides. ## Layer 1 — mechanical (always run first) ```bash .claude/scripts/verify.sh # whole src/ .claude/scripts/verify.sh src/views/about.tsx # scoped yarn lint yarn build # must pass before anything ships ``` Every **FAIL** must be fixed. **WARN**s are judgement calls: fix or justify in your summary, do not silently ignore them. ## Layer 2 — judgement checks Work section by section. For each one: ### Design fidelity (only when a design exists) Re-fetch the Figma node — do not QA from memory or from your own earlier summary. `get_design_context` for values, `get_screenshot` for layout. Then compare: - **Copy** — character for character. Flag anything paraphrased, shortened or invented. - **Layout** — column count, flex direction, alignment, order, positioning. - **Spacing** — margins, padding, gaps against the design values. - **Typography** — size, weight, line-height, letter-spacing. Never assume a heading is bold; designs often use 400. - **Colour** — exact values, resolved through tokens rather than matched by eye. - **Images** — aspect ratio, crop, radius, overlap. No effects the design lacks. If an image looks invisible or wrong, check the **container** first — an invented wrapper background is the usual cause, not the asset itself. ### Tokens - Every colour/spacing/radius/type value resolves to a token. - New tokens follow the three-tier grammar and carry a comment naming their origin. - No literal reached `@theme inline` or a Tier 2 token. - A value that had to be invented because the design has no token for it is **flagged to the user for design review**, not quietly added. ### Motion - Everything scroll-driven, revealing, staggered or layout-affecting is a spring. - CSS `transition-*` appears only for hover/focus/discrete state, with `duration-[var(--duration-*)]` and a token ease. - Each primitive is the right one — `Inview` for reveals, `SpringTrigger` scrub for parallax, `Hover` for hover, text through the text engine. - `tag` is semantic on every animation component. - With `prefers-reduced-motion`, content is present and readable — and the page still responds: a `loop:` spring or `while (alive) await …` loop not gated on `useMotionOff()` restarts in the same tick forever under `skipAnimation` and hangs the tab (found on 6+ production sites). `node tools/qa/check-motion.mjs --url …` checks reduced motion and the robot form. - Motion speed doesn't follow the refresh rate: per-frame increments are scaled by `dt` (a 120 Hz phone otherwise runs them 2×) — `mobile-device-qa` §120 Hz. - Hover springs exist only where a mouse can hover; text reveals blur once per line, never per letter (`optimize-performance` §3). ### Semantics & a11y - One `