Visual Verification
Contract
Iron law: UI changes are visually verified or explicitly skipped with a structured result — a passing build and test suite do not prove the page renders. Invoked alongside runtime-verification by /flow:start Phase 4 step 2 (retried at step 8) and /flow:pr Phase 4 via Agent(integration-verifier) when the diff has UI files or acceptance criteria mention UI. Returns the Visual Verification table, Visual Evidence table, one Viewport/Screenshot/Result/Observed: block per viewport for the bundle's ### Visual analysis, 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
Either activates:
git diff --name-only HEAD~1..HEAD | grep -iE '\.(tsx|jsx|vue|html|css|scss|svelte)$'- Acceptance criteria containing: UI, page, display, render, visual, layout, responsive, component, style.
Neither: SKIP — no UI-relevant changes detected.
visualVerification.requireVisualVerification (default false) controls escalation only; an unattempted UI change always yields at least SKIP_WARN.
Browser Tool Cascade (first available)
- Playwright MCP (
browser_navigate,browser_take_screenshot,browser_console_logs) - Chrome DevTools MCP
- CLI:
npx playwright screenshot http://localhost:$PORT/ $SCREENSHOT_DIR/page.png(installing chromium first if needed) Skill(compound-engineering:test-browser)(if installed)Skill(compound-engineering:agent-browser)- Nothing:
SKIP_WARNwith install guidance ("Install Playwright MCP or use /flow:setup") whenrequireVisualVerificationis false;BLOCKEDwhen true, escalated perreferences/escalation-format.md.
Never install Playwright 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 visualVerification.maxIterations (default 3); iterate only after fixes. $SCREENSHOT_DIR = visualVerification.screenshotDir (default .screenshots).
For each page (root + key routes) and viewport in visualVerification.viewports (defaults desktop 1280×720, tablet 768×1024, mobile 375×812):
- Navigate; screenshot to
$SCREENSHOT_DIR/{page}-{viewport}-{timestamp}.png. - Analyze the screenshot (Read tool); write its
Observed:block (shape below). - Classify: blank page or render-blocking console error P1; layout break or missing content P2; minor styling P3.
- With MCP tools, grep
browser_console_logsfor JS errors, React warnings, CSP violations. - Put the block in the task result; the bundle producer copies it into
### Visual analysis.
Findings use the Finding | Suggested Fix table (references/finding-schema.md), prefix VIS- standalone or INT- from integration-verifier, location = URL path plus viewport.
Track three tasks (Visual verification, Browser tool discovery, Responsive check), closed with the result strings in visual-verification-output.md; TaskList confirms all resolved.
Result Vocabulary
| Result | Meaning | Passes gate? |
|---|---|---|
PASS |
Ran and passed | Yes |
FAIL |
Ran and found P1 issues | No |
SKIP |
No UI changes, or dev server unavailable | Yes |
SKIP_WARN |
UI changed, no tools, requireVisualVerification false |
Yes, with warning |
SKIP_USER_APPROVED |
User skipped via escalation | Yes |
MANUAL |
User verifies manually | Yes |
BLOCKED |
requireVisualVerification true, no tools |
No; escalate |
Output Format
### Visual Verification
| Check | Status | Details |
|---|---|---|
| Browser tools | {tool name or NONE} | Cascade result |
| Visual check | PASS/FAIL/SKIP/SKIP_WARN/SKIP_USER_APPROVED/MANUAL/BLOCKED | {pages checked, findings} |
| Responsive | PASS/FAIL/SKIP/SKIP_WARN/MANUAL | {viewports tested} |
| Console errors | PASS/FAIL/SKIP | {error count} |
### Visual analysis
Viewport: {name} {width}x{height}
Screenshot: {path}
Result: PASS|FAIL
Observed: {two to four plain sentences: element present, text, state, layout, console errors}
One block per page and viewport; Result: FAIL when it has a P1/P2 finding. The verdict-judge has no file tools and reads only Observed:, so name the element the criterion asks for. Then ### Visual Evidence and ### Visual Findings tables (template in the reference).