Skip to content
Browse all prompts
adversarial-design-review.md Markdown · 146 lines Raw
Take one UI surface from competent to well-crafted. Not "changed", better, and you have to prove it with pixels.

The surface is: **[name the page, dialog or component, and the URL it renders at]**

## The rule that makes this work

You do not review your own work. You wrote it, so you will like it. A second model is the reviewer and the only thing that gives a score. You fix what it finds, and you decide which of its findings are actually right.

Send each round to Codex: `codex exec` in a read-only sandbox, or the Codex MCP server if you have it connected. Attach every screenshot as an image (`-i /abs/path.png`, one flag per file), name each one in the prompt so findings can cite it, and ask the reviewer to be hostile. Every round is a fresh call with a self-contained prompt, never a continuation of the last one. Each review has to stand on its own.

The score is the engine, not decoration. It forces the reviewer to commit and rank instead of handing you a flat pile of observations, it shows progress without anyone reading a diff, and it tells the loop when to stop.

Work through to the end without stopping to ask me things. When you hit a judgement call, make it, put a price on it and write it down. I would rather reverse a recorded decision than answer a question halfway through.

## 1. Learn what this surface is meant to be

Before you look at my version, name the element generically. Not what this product calls it, but what it is: "a pricing card grid", "a multi-select combobox", "a settings dialog with grouped sections". The generic name is what finds the specification. The product's name finds nothing.

Then research it briefly, one or two searches rather than a literature review, for four things:

1. **The established pattern** and the names of its parts, so the rest of the run has a vocabulary.
2. **The hard requirements**, each with the specification that states it and its URL: WAI-ARIA Authoring Practices, WCAG, the platform's own guidelines. A rule with no source is a convention, not a requirement.
3. **The conventions** most well-built versions follow, and why.
4. **The known failure modes**: what this element is habitually got wrong. This is the most valuable of the four, and the one a reviewer looking only at pixels will never supply.

Two rules for using what you find. **My project's rules outrank the standard.** Where a documented house rule and an outside convention disagree, the house rule wins and you note the conflict instead of arguing it. **A standard is a floor and a vocabulary, not a redesign mandate.** "The current best practice is a bottom sheet" is not a finding about a desktop dialog.

## 2. Write the brief

Write it once and reuse it verbatim every round. Every score is given against this brief, not against anyone's taste in the moment. It needs:

1. **What the surface is and who is looking at it.** One paragraph.
2. **The questions it must answer, ranked.** Questions, not features: _is anything asking me for something, is anything stuck, is anything finished and waiting._ This is what turns "improve the hierarchy" into something you can test.
3. **How it is driven.** Keyboard-first or pointer-first changes almost every affordance judgement.
4. **The house rules, with their reasons.** Colour restraint, motion, icon vocabulary, microcopy, contrast floors, every theme it has to survive. Tell the reviewer that a proposal breaking one of these is not an improvement here: argue the rule if you think it is wrong, but score against it. A rule without its reason gets reinterpreted into something else.
5. **The standards from step 1**, with the precedence line stated.
6. **Settled decisions.** Anything already decided in an issue, an ADR or an earlier review. The reviewer cannot see that history and will happily walk you into relitigating it.
7. **A file map.** Which files hold which decisions, and an instruction to read only those.
8. **The rubric.** Named dimensions and an anchored top mark.

Start from these dimensions and adapt them to the surface: information hierarchy, scannability, visual language consistency, typography and rhythm, density and spacing, affordance and discoverability, states (empty, loading, error, edge), accessibility, microcopy, craft and polish. Then give it the anchor, word for word:

> 10 means a top-tier product design team could not name a change worth making. 9 means one or two genuinely marginal nits remain. Do not inflate, and do not deflate to seem rigorous.

Without the anchor the score drifts between rounds and stops being comparable.

## 3. Build a screenshot harness

A script I can re-run, not a one-off. Playwright, headless, capturing the surface at 375px and 1440px, in every theme the site has.

- **Every state that carries design weight.** Rest, populated, an open menu, a validation error, loading, empty, long content. Half the findings land on states nobody has looked at in months.
- **Realistic fixtures, pushed through the data path the app actually uses**, not UI-level mocks. Sparse fixtures hide exactly the defects worth finding: truncation is invisible until a label is long enough to clip.
- **Never trust the exit code. Count the files.** A harness that swallows an error reports success and writes nothing, or writes a plausible empty state over the real one. Verify after the page settles, and throw rather than write.
- **Captures live outside the repo** and never get committed.

Two things that will bite you:

- Headless Chromium hides scrollbars, which changes layout. Launch with `ignoreDefaultArgs: ['--hide-scrollbars']` if scrollbars are part of what you are judging.
- For a hover state, hover the element itself and then assert the hovered style before you capture. A screenshot that silently shows the resting state is the easy mistake here. If hover will not take in a headless run, activating the frame before moving the mouse is worth trying, but check that doing so has not changed anything else in the shot.

Capture a baseline now, before you change anything.

### Check that the reviewer can actually see

A model that cannot view images, handed an image path, will describe the image it imagines rather than admit it. That failure is silent, and it poisons every round after it. So before round 1, copy one screenshot alone into an empty directory, with nothing beside it that describes the surface, and point the reviewer at it. Ask three or four questions you already know the answers to: the exact heading text, the button label, whether the button is light on dark. End with _"If you cannot view the image at all, say exactly CANNOT VIEW IMAGE and do not guess."_ One invented detail means it is blind, and from then on you describe the screenshots to it in words.

## 4. Loop

Each round:

1. **Capture** the current state, and count the files against the state list. A missing state is a harness bug to fix now.
2. **Look** at every screenshot yourself. You are the one implementing, so you need the pixels in your own head, not just the reviewer's summary of them.
3. **Review.** One fresh call to the reviewer with the brief, the screenshots, the file map and the must-not-change list, asking for the four outputs below.
4. **Verify.** The reviewer will be confidently wrong, and its tone will not tell you when. It will call a generated design-token utility a hardcoded value, or ask for an ARIA role that breaks something else. For every factual claim you are not sure of, read the code before you concede. Discard what you cannot reproduce, and say which ones you discarded. When its reading of a rule would condemn the whole codebase, suspect the reading. Hold it symmetrically: most findings will be right, and those you fix without arguing.
5. **Triage** every finding into one of the three buckets below, and keep a record of all of them, the rejects included.
6. **Fix** everything in the fix bucket. If the cause is in a shared component or on a neighbouring screen, fix it there instead of patching this one surface.
7. **Pin it.** For anything that could silently come back, write a test. Assert the rule, not the value: "no two status marks share both shape and colour", not "done uses the check icon". If changing the design forces the identical edit in the test, the test is worthless. Then break the code on purpose and confirm the test fails.
8. **Check and commit.** Run the surface's tests and lint, keep them green every round, and commit the round with a note of what actually landed.

### What to ask the reviewer for

- **An overall score to one decimal place**, justified in two or three sentences, plus what would have to change to reach 10.0.
- **A score for each rubric dimension.**
- **A ranked defect list.** Each item says what is wrong, what it costs the user, and the specific change in a named file. "Feels cramped" is not a finding; "the 8px gap between the heading and the first row reads as one block" is.
- **What must not change.** What this version gets right that the next round could break. Carry the list forward and add to it every round.

Then tell it when to stop: _"Name only what is genuinely still missing. No padding, nothing invented to avoid giving a top mark. If what remains is below the threshold that would change the score, say that instead of listing it."_ Without this it finds something forever, because returning nothing feels to it like failing to review.

### Triage

- **Fix it.** Most findings. A breach of a hard requirement from step 1 always lands here.
- **Push back.** The reviewer is wrong, or wants to reverse a settled decision. Put the counter-argument and its source in the next round's notes, and make the reviewer rule on it.
- **Decide it yourself.** A product call, or a fix big enough to be a project. Price it, take the smaller change unless the reasoning clearly says otherwise, and record which way you went.

### Later rounds

**From round 2,** replace the open review request with notes on the last round: what you fixed for each item, what you deliberately did not do and why, and _"check the code, do not take my word for it."_ Account for every finding, the rejects included, or the reviewer spends the next round raising them again. Scope the review to what changed, plus anything else genuinely blocking a higher score. A full re-review of a growing diff always finds something, and the score wanders instead of climbing.

**From round 3,** ask it to price every remaining item: the file, the change, and what fixing it is worth in points. Triage becomes arithmetic. When one of my house rules is what caps the score, ask what the rule costs in points and what the alternative would have been. "0.1 points, because two status marks share a shape in forced-colours mode" is a decision anyone can make in ten seconds. A paragraph of argument is not.

### How many rounds

Three rounds, then up to two more that the score has to earn:

| Round | Runs when                                         |
| ----- | ------------------------------------------------- |
| 1-3   | Always, unless a stop condition below fires first |
| 4     | Round 3 scored below 9.8                          |
| 5     | Round 4 scored below 9.5                          |

Stop early when a round's score gains less than 0.3 on the one before (that is the plateau), or when the reviewer names nothing that would change the score. Five rounds is the ceiling.

The bar drops as the rounds go on because each late round costs more and buys less. In our runs, round 2 bought more than any round after it, and past round 3 a reviewer asked for a design review drifts into accessibility and state correctness. That work can be real, but buying it on a design budget should be a decision rather than momentum.

**A score drop means a fix was wrong.** Do not carry on past it. Find the change that caused it and revert or redo it. The one exception is a change of reviewer: if round 1 used a stronger model and later rounds a cheaper one, a drop at the switch is a regression only if the new reviewer names something that got worse. Keep the same model for the rounds whose scores decide whether another round runs, so the numbers stay comparable.

**Remember what a score measures.** Each round's score is the review of the surface as it stood _entering_ that round. The last round's fixes are never scored, so the final score is the mark from before them. Say so in the report.

## 5. Things a reviewer will push you toward that you should refuse

- **Deleting content to raise the score.** An emptier screen scores better and serves the reader worse. Tighten the design, keep the substance.
- **Restructuring a familiar layout to beat the score.** A reviewer that sees a conventional structure will call it generic and propose something unusual. Keep the familiar structure and polish it until it is clearly better than the usual version.
- **Hover effects on things that are not clickable.** Hover belongs where a click lands, and nowhere else.
- **Decorative underlines and redundant rules.** If the hierarchy needs a horizontal rule to be legible, the hierarchy is the problem. This is not licence to strip a link of its underline in running prose, where the underline is how a reader finds it, or to flatten a rule that is genuinely separating rows of data.
- **Centring everything.** Rows of roughly equal length usually want to be left-aligned.

## 6. Before you tell me it is done

Two gates the loop cannot do for itself, because it only ever sees one surface.

**Consistency.** For every pattern this run introduced or changed (a colour role, a button treatment, a spacing rhythm, a glyph, a microcopy formula), count how many other surfaces still do it the old way. A surface that scores 10 on its own can be the only one of sixty-five dialogs with that button, and then it is not an improvement, it is an inconsistency. Then decide:

- The new pattern lives in a shared component or token, or the list of places is short and the change is mechanical: **roll it out now**, and commit the roll-out separately so it can be reverted on its own.
- Every other place would need a rewrite: **revert this surface to the existing convention**, and write down what the roll-out would have cost.
- The new pattern is already the majority: **keep it** and bring the stragglers over.

Wherever a shared value changed, check every place it lands, other route groups included.

**Full test run.** The whole unit suite, plus lint, typecheck and any repo-wide guard tests. Not just the tests for this surface. Design changes touch copy, class names and shared primitives, and the assertion that breaks usually lives a directory above the one you edited.

Then report with a table, one row per round and one for the gates:

| Round | Score | Δ   | What the review found | What landed |
| ----- | ----- | --- | --------------------- | ----------- |

Under it: what you fixed, what you pushed back on and whether the reviewer conceded, the calls you made yourself with their price in points, the consistency count and what you decided, where I can open the surface myself (the URL or click path, not a server you left running), and the before-and-after screenshots side by side.