Review UI against prototype
SkillAI & modelsCompare the live UI of the web app against its HTML prototype in .ai-engineering/workflow/prototypes/ and report the visual and behavioral gaps. Screenshots both, then two parallel reviewers (subagents, or `claude -p`) diff them, one for visuals and one for behavior. Use when the user asks to review, check, or diff the implemented UI against a prototype (e.g. "/ai-review-ui order-list", "does the settings page match the prototype?").
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the Review UI against prototype skill
What this skill tells your AI
The instructions your AI receives, as published by arcasilesgroup/ai-engineering in skills/ai-review-ui/SKILL.md and read by ahel’s review.
This skill runs in one of two modes. Work out which one you're in first.
- Standalone (a human ran
/ai-review-ui): run steps 1–5. The output is a findings report. Don't fix anything; step 5 ends by offering fixes, and fixing happens only if the user accepts. - Gate (
/ai-orchestratorGate 2): run steps 2–4 only, with the checkpoint'sui.scope. Before capturing, check thatroutein.ai-engineering/workflow/prototypes/<name>.states.jsonequals the checkpoint'sui.route; if not, fix the scenario file to useui.route, since the capture script only reads the scenario. ReturnPASSorFAILand the merged findings to the orchestrator. Never offer or apply fixes; failures go through the orchestrator's failure path. The gate passes when every in-scope, live-reproducible state ismatchand there are nohighormedfindings, other than ones tagged[intentional?].
1. Resolve the pair
- Prototype:
.ai-engineering/workflow/prototypes/<name>.html. Match it from the request; if more than one could fit, ask. - Live route: the page that implements it (see
FILEMAP.md, and Project config → Pages inAGENTS.md, which says where signed-in and public pages live). Public pages don't need the saved sign-in session. - Source files: that route's page file, plus any components it uses (Project config → Components).
If the route isn't obvious from the prototype name, send an Explore subagent to find the page that implements it and its component files. Have it return only the paths.
2. Make sure the app is reachable
- Run
curl -s -o /dev/null -w '%{http_code}' <web URL>(Project config → Web URL). If nothing answers, run the Dev server command from the repo root in the background and wait until it responds. - Signed-in pages need a saved session. If
.ai-engineering/workflow/playwright/auth.jsonis missing, stop and ask the user to run the command below, sign in, and close the window:! npx -y playwright codegen --save-storage=.ai-engineering/workflow/playwright/auth.json <web URL><sign-in path>.ai-engineering/workflow/playwright/is gitignored. If the screenshots come back showing the login page, the session has expired; ask the user to run the same command again.
3. Capture both sides in the same state
Never compare a prototype in one state with the live page in another, like an empty form against a submitted one. Every comparison is driven by a state scenario, .ai-engineering/workflow/prototypes/<name>.states.json, which /ai-prototype writes next to each prototype. If it's missing, write it first from the prototype's states:
{
"route": "/discounts",
"login": "/login",
"now": "2026-10-01T10:00:00",
"regions": ["page-header", "discount-list", "discount-form"],
"states": [
{ "name": "overview", "regions": ["page-header", "discount-list"], "steps": [] },
{ "name": "empty-form", "regions": ["discount-form"], "steps": [] },
{ "name": "validation-error", "regions": ["discount-form"], "steps": [{ "click": "Save discount" }] },
{ "name": "filled", "regions": ["discount-form"], "steps": [{ "select": "Type", "value": "Percentage" }, { "fill": "Amount", "value": "15" }] },
{ "name": "saved", "regions": ["discount-form", "discount-list"], "mutates": true, "steps": [{ "select": "Type", "value": "Percentage" }, { "fill": "Amount", "value": "15" }, { "click": "Save discount" }, { "waitFor": "Discount saved" }] },
{ "name": "no-discounts", "regions": ["discount-list"], "proto_query": "state=empty", "live": false, "live_note": "needs a store with no discounts" }
]
}
-
Regions and scope: a checkpoint often builds only part of a screen, while its prototype shows the finished screen. The prototype marks each part with
data-region="<name>".regionslists them, and each state says which regions it exercises. With--scope page-header,balances:- the script captures only the states whose regions are all in scope;
- it fingerprints only the prototype elements inside in-scope regions, so parts that aren't built yet can't cause a MISMATCH;
- the reviewers judge only in-scope regions.
Region names match the checkpoint's
ui.scope. -
loginis the sign-in path from Project config (default/login). Landing there means the saved session expired. Without--scope, every state runs and the whole screen is compared (standalone mode, or the final checkpoint for a screen). -
Steps:
fill/select/checktarget a field by its visible label.clicktargets a button by its name (add"role"for links or tabs). There's alsopress,waitFor(text) andwait(ms). The same steps run on both sides, so the prototype and the live page must use the same labels and button text; a step that works on only one side is a finding in itself. -
Data states: loading, error and empty lists are reached on the prototype with
proto_query, and on live withlive_queryor a different role's session ("auth": ".ai-engineering/workflow/playwright/auth-<role>.json"). If live can't reproduce the state, set"live": false. That state is then reviewed on the prototype alone and never compared. -
Mutating states (
"mutates": true) run on live only when the base URL is localhost. List them last. -
Viewports: both sides use the same viewports (default 1440×900 and 375×812), the same frozen clock (
now), reduced motion, and no animations.
Run from the repo root:
npx -y -p playwright sh -c 'NODE_PATH="$(dirname "$(command -v playwright)")/.." node skills/ai-review-ui/capture.cjs <name> <web URL> [--scope a,b]'
The script writes .ai-engineering/workflow/playwright/review/<name>/<state>-<width>-{proto,live}.png, plus states.json.
Before taking each pair of screenshots, it fingerprints the UI state on both sides: field values by label, open dialogs, alert or status messages, and invalid fields. Each pair is marked match, MISMATCH (with the reasons) or proto-only.
- A MISMATCH means the two sides aren't in the same state, so their pixels aren't comparable. Report each mismatch as a
highfinding, e.g. "after submitting empty, the live page shows no validation", and exclude that pair from the visual review. redirected to <sign-in path>means the sign-in session has expired; see step 2.
4. Independent review
Hand the comparison to reviewers with fresh context, so they judge the screenshots and not your memory of building them.
Default: two agents in parallel. Launch ui-visual-reviewer and ui-behavior-reviewer in a single message. Their method, output format and rules are in their agent definitions (.claude/agents/), which also:
- give them a fixed model (visual:
opus; behavior:sonnet); - tell them to ignore
AGENTS.md, so they judge against the prototype and.ai-engineering/DESIGN.mdwithout the implementer's reasoning. Don't pass them the checkpoint's goal, tasks or implementation notes either.
Give each one only:
- the capture folder
.ai-engineering/workflow/playwright/review/<name>/; - the scope (
ui.scope, or "whole screen"); .ai-engineering/workflow/prototypes/<name>.html, plus.ai-engineering/workflow/prototypes/<name>.states.jsonfor the behavior reviewer;- the live source paths (the page and its components).
The visual reviewer judges sizes proportionally against each screenshot's own dimensions, not by eye. The behavior reviewer owns the MISMATCH findings.
Alternative: claude -p. Use this when the user asks for it, or for a fully separate process. Run both from the repo root, in parallel:
claude -p --agent ui-visual-reviewer "<inputs above>"
claude -p --agent ui-behavior-reviewer "<inputs above>"
5. Report (standalone only)
Merge the two lists and drop duplicates, meaning the same element and the same difference. Put the state mismatches from states.json first. Relay the findings verbatim, grouped as high, then med, then low. Add the paths to the screenshots. Offer to fix the high-severity findings.
Lifecycle
Lane: standard, full Writes: nothing Read by: ai-orchestrator, humans Dies: on completion Next: none
Signals
- GitHub stars
- 59
- Forks
- 3
- Last commit
- Oct 2026
Advanced
- Item type
- skill
- Key
ai-review-ui- Source
- github.com/arcasilesgroup/ai-engineering
github.com/arcasilesgroup/ai-engineering
Related picks
Skill · handsontable
The pick for End-to-end testingmstar-e2e
Skill · btspoony
The pick for End-to-end testingcompare-screenshots
Skill · dzhng
The pick for Screenshotsagent-first-screenshots
Skill · devin-axis
The pick for Screenshotsskill-creator
Skill · anthropics
More in AI & modelswayfinder
Skill · mattpocock
More in AI & models