visual-verification
Verify UI-facing changes by running a screenshot-analyze-verify loop across configured viewports, with a browser-tool priority cascade (Playwright MCP → Chrome DevTools MCP → CLI fallback → external s
SKILL.mdplugins/flow/skills/visual-verification/SKILL.md
GitHubVisual Verification
Contract
Iron law: UI changes are visually verified or explicitly skipped with a structured result — a
passing build does not prove the page renders, and a screenshot does not prove an interaction
works. Invoked with runtime-verification by /flow:start Phase 4 (retried at step 8) and
/flow:pr Phase 4 via Agent(integration-verifier) when the diff has UI files or criteria
mention UI. Returns the Visual Verification and Visual Evidence tables, one
Viewport/Screenshot/Result/Observed: block per viewport for ### Visual analysis, a
Step:-carrying block per step on an interaction criterion, and P1/P2/P3 findings
(category=visual), labeled PASS/FAIL/SKIP/SKIP_WARN/SKIP_USER_APPROVED/MANUAL/BLOCKED.
Permitted skips: SKIP when no UI signal fires or the dev server is unavailable; SKIP_WARN,
SKIP_USER_APPROVED, MANUAL only via the cascade.
UI Relevance
Activates when changed files match \.(tsx|jsx|vue|html|css|scss|svelte)$, or criteria contain
UI, page, display, render, visual, layout, responsive, component or style. Neither: SKIP — no UI-relevant changes detected. requireVisualVerification (default false) controls escalation only; an unattempted
UI change still yields SKIP_WARN.
Browser Tool Cascade
Only an interactive tool can run a flow: a picture-only tool cannot click, and treating it as
if it could reports a flow that performed none. Playwright MCP and Chrome DevTools MCP are
interactive; the npx playwright screenshot CLI and compound-engineering browser skills are
not.
Playwright MCP exposes browser_navigate, browser_snapshot, browser_click, browser_type,
browser_take_screenshot, browser_console_messages. Take names from the server's docs, not
memory: a call to a tool that does not exist finds nothing and says nothing, so the check never
runs and nothing reports it.
No tool: SKIP_WARN, or BLOCKED when requireVisualVerification is true (escalate per
references/escalation-format.md). Never install silently.
Screenshot-Analyze-Verify Loop
Needs the dev server URL from runtime-verification; if it failed to start, return SKIP ("dev
server unavailable") — that failure is primary. Bounded by maxIterations (default 3). Per page
and viewport in visualVerification.viewports: screenshot, analyze, write the block, read
browser_console_messages. Findings per references/finding-schema.md, prefix VIS- or INT-,
located at URL path and viewport.
Interaction Flows
Governed by visualVerification.flows ("on" | "off", default "on"); off, or no interactive
tool, write Flows: none — {reason} into ### Visual analysis instead of step blocks.
A UI criterion gets one to three scenarios when it describes an action a user performs — cue
verbs: click, submit, type, select, toggle, open, navigate, drag — or a risk-map row names a UI
state. Neither: write Flows: none — no interaction. Each scenario is at most
visualVerification.maxFlowSteps (default 8) steps:
navigate <route> · click <element> · type <element> <text> · expect <element or text>
Targets come from browser_snapshot, never a selector guessed from the criterion's text.
Drive with browser_click and browser_type; after every step screenshot and write a block
carrying the Step: {n}/{m} {action} line shown below.
Observed: describes the page AFTER that step. Desktop by default, every viewport when the
criterion mentions responsive or mobile. Step blocks are additional to viewport blocks, never
a replacement. A step whose expect is not met is a category=visual finding. When
browser_start_video is available, cite the path; its absence is not a skip, warning or
finding.
Output Format
One block per page and viewport in ### Visual analysis, plus one per step:
Viewport: {name} {width}x{height}
Screenshot: {path}
Step: {n}/{m} {action} (interaction steps only)
Result: PASS|FAIL
Observed: {two to four plain sentences}
The judge has no file tools and reads only Observed:, so name the element the criterion asks
for. Result: FAIL when a block has a P1/P2 finding; only FAIL and BLOCKED fail the gate.
Result meanings, cascade roster, severity classification, the flow procedure, a worked example and
task tracking: visual-verification-output.md.