Agent docs

14 · Validating a UI change

The pass a UI change goes through before it is called done, the skills that run it, and when each one fires.

A UI change is done when someone has run it in the app, in the state that matters, and read the result. Typechecking is not that. That sentence is the repo's bar (CLAUDE.md, "The bar for done") and this doc is the pass that meets it. It exists because the reliance on a handful of review skills was invisible. Agents ran them heavily on speedway, walmart and this playground, they caught things a green build never would, and nothing named them or said when each one fires. Owner ask, 2026-08-17 (doc 59 V1): document the process, so the reliance is not left invisible.

The pass, in order#

  1. Typecheck the layers you touched. Kit: npm run typecheck in packages/ui, read bare, then the browser. Playground: npx tsc --noEmit in apps/playground. A clean run says the code parses, nothing more. It is the gate to the browser, never a substitute for it.
  2. Exercise it live, in both themes, at two widths. The playground runs on :5104 (pm2 name playground-dev). Dark is the literal theme name versable-dark, light is versable-light. The toggle writes pg-theme to localStorage and the root layout applies it before paint (apps/playground/src/app/layout.tsx). Check 1280 wide and 375 wide. A dark-only or desktop-only sign-off is the recurring lapse this step exists to stop. Scope the claim to what you saw ("verified in light only") if you cannot see the rest.
  3. Measure the categorical classes. Behaviour checks ("the modal opens", "the filter filters") miss a whole class of defects that only measuring catches: a transparent floating layer, sibling controls at different type sizes, a height chain that scrolls the page instead of the pane, an action with no toast, text that silently ellipsizes, numbers that disagree on one screen, a dark-mode-blind sign-off. /ui-categorical-check <url> runs the catalog of those classes, mined from the owner's own complaints, against the live page: computed-style diffs of sibling controls, opacity of floating layers while they overlap content, scrollWidth against clientWidth, contrast ratios, the counts on the page against the counts on the pages it links. It then delegates an adversarial pass to a second agent with the browser, because the author cannot see their own blind spots. Its findings outrank the author's self-check.
  4. Ask why a screen feels wrong, when it does. /ui-gripe <url> is the confusion audit: what is fighting the reader, ranked by damage, each tied to visible evidence and a fix. Run it when a page "feels off" and nobody can say why yet, or before a redesign. It answers a different question from the categorical check (why is this confusing, versus what is measurably inconsistent).
  5. Review the code that produced it. /skeptical-review reads the diff against the surrounding tree (siblings, conventions, existing helpers to reuse, the contract docs) and flags, never auto-fixes. For a kit API change the repo also runs a codex review of the diff and records the dispositions beside the report. Both are review lanes, not the same lane twice.
  6. Read the contract before adopting a component. Every kit contract doc under packages/ui/docs/ carries a "Before you adopt this" checklist and a Banned combinations section; docs/DO-NOTS.md is the regenerated lens over all of them (node scripts/build-do-nots.mjs). A change that adds a component reads its checklist first. A change that ships a new do-not writes it into the contract doc and the canon so the lens picks it up.
  7. Cite what you checked. A doc or a report that says "verified" names the URL, the theme, the width and the measurement or the screenshot. Every canon or contract change re-runs node scripts/check-citations.mjs docs.

When each skill fires#

SituationInstrument
Any UI change, before "done"steps 1 and 2 above, always
A change to a shared surface (a shell, a card frame, a title, a strip of controls) or anything the owner has flagged for inconsistency/ui-categorical-check on the changed page, then the adversarial pass it dispatches
A page that feels confusing, dated or off, with no named defect yet/ui-gripe
A kit API change, or any diff touching more than a few files/skeptical-review; kit diffs also get a codex review with dispositions
A new component or pattern adopted on a pagethe contract doc's "Before you adopt this" checklist, then docs/DO-NOTS.md
A screen that must match a reference (a mock, a recreation)/vis-compare on the pair of images
Not sure which of these fits/ui routes the request and enforces the preconditions

What each pass has caught here#

Each step has already earned its place on this repo, which is why the pass is written down. The categorical check on the landing after wave 2b (2026-08-17) found the strip's three demo tiles hand-rolled at one padding while the section cards used the kit Card at another, four uppercase labels typed as three different class recipes, and View buttons whose position moved with the length of the subtitle beside them. The fix was one card frame, one Kicker primitive and the toolbar slot (doc 60 §2 and §6, doc 59 V1 C1 C2). The codex review of the kit diff in the same wave found a hook declared after an early return (a hook-order crash the typecheck cannot see) and a topbar grid whose title column could push the actions off a phone screen. Both were fixed before the commit (.claude/output/20260817-w2b-kit-review/codex.md, local). The earlier rounds live in the review docs (docs/plan/55- and docs/plan/59-showcase-feedback-*.md) and in the do-nots those rounds produced.

What the record shows#

The guidebook's retrospective (docs/GUIDEBOOK.md §7, built 2026-08-17 from the commit histories, the atone ledger, the runtime notes and the owner's own messages, every citation checked by a second agent) is this doc's evidence base. Three readings of its 28 lapses matter here.

Who noticed. Ten of the 28 were caught by the owner looking at the page, and three of those ten only after the same ask had been made twice or three times. Seven were self-caught by the building agent, usually late. Eight came from a review that was neither: four from the usability audit's facets (doc 50), two from a peer session reading the diff, one from the conventions sweep (doc 46), one from the walmart tech-debt review. Two surfaced while doing something else (writing the contract docs; an unrelated verification pass), and one from the toolkit RCA. Read that against the pass above: the reviews found the third of the record that neither the builder nor the owner did, and the owner's channel is the most expensive one, because it costs a sitting.

Which checks caught what. All 28 typechecked. The measurable classes (a grid overflowing at phone width, small text under AA in light, a class a component's own colour rule defeats, a hand-typed glyph where the kit has an icon) fell to the categorical pass and its adversary once those existed (rows 8, 9, 14, 17, 18 predate /ui-categorical-check being reached for; the wave-2b landing pass then found six of the same class in one sitting). The "does not look restored" class (rows 2, 3, 10, 26) fell to nothing but the owner, because no check compares a rebuild against the reference the owner remembers unless the reference is rendered beside it, which is what /vis-compare and a parity ledger exist to do and were not used for.

What a fresh agent got wrong first. Five families, in the guidebook's §7.3: a rebuild replaces accumulated UX without a parity audit; a local surface diverges from the mock or the canon and ships unverified against it; the instrument was absent, not the judgment; a new surface skips the sibling or naming sweep; and a process is declared complete without its own steps having run. The last is the one the atone ledger records most often across every project, and it is the reason this doc's pass ends in a browser, not a typecheck.

See also#

docs/app-patterns/13-when-a-solution-is-unsuitable.md (the tells that the request itself is wrong, which no validation pass fixes), docs/app-patterns/01-documenting-a-surface.md and 02-deriving-e2e.md (how a surface doc becomes the test suite the pass runs against), packages/ui/docs/tooltip.md "The rich preset" (the header standard the categorical check measures against), CONTRIBUTING.md (how to run the playground the pass exercises).

@versable-git/ui · reference, canon, and method, read in place