Evidence-based rating and iteration
PLAn evidence-based rating loop scores a handoff artifact, such as a visual direction, prototype, design contract, or spec, against written acceptance criteria. A reviewer that did not produce the artifact rates it, every point cites reproducible evidence, the lowest dimension is the handoff score, and the loop ends at an explicit threshold or a named stop condition.
The agent revised the checkout design contract four times and reported 7/10, then 8/10, 8.5/10, and finally 9.5/10. You open version four and the declined-card state is still missing, a new colour has appeared, and nobody can say which of the earlier findings were fixed or quietly dropped. This page is for developers and tech leads who hand plan-stage artifacts between people and agents, and want a score they can gate on instead of a number the author gave itself.
What you get from an evidence-based rating loop
Section titled “What you get from an evidence-based rating loop”- A four-dimension rubric where the minimum wins, and a JSON Schema that forces every rating into the same shape.
- A read-only review command for Claude Code and Codex, and the equivalent routine in Cursor.
- A
jqgate that computes the score itself instead of trusting the reviewer’s arithmetic. - A findings ledger with a disposition for every finding, so fixes are tracked rather than remembered.
- Three copy-paste prompts: rate, fix, and re-rate.
- Stop conditions, a calibration routine for the reviewer, and recovery steps for the ways the loop goes wrong.
When is a rating loop worth running?
Section titled “When is a rating loop worth running?”The loop costs a review run per iteration plus a human decision at the end. Use it where a wrong handoff means rework downstream, not on every artifact.
| Situation | Use |
|---|---|
| An artifact in the four-artifact pipeline came back from review more than once | Full loop |
A design contract or spec.md that several implementation tasks will read | Full loop, threshold 9 |
| A customer-facing flow with accessibility or legal exposure | Full loop plus a named human acceptance for those dimensions |
| An internal screen built only from existing components | One rating pass, no iteration unless a blocker appears |
| A copy or spacing fix | No loop; normal review of the change |
The loop rates artifacts that humans accept. It assumes the criteria already exist; if they do not, write them first with executable acceptance criteria. Rating the code the artifact produces is a different job, covered by model-graded checks and agent PR review.
How the rubric scores a handoff
Section titled “How the rubric scores a handoff”Four dimensions cover what goes wrong in plan-stage handoffs. Each scores 0 to 10, and the handoff score is the minimum, not the average: a perfect visual system does not compensate for a missing error state.
| Dimension | Scores 10 when | Typical evidence |
|---|---|---|
| Intent fidelity | Every acceptance criterion and non-goal in intent.md or spec.md is met, each cited by ID | Criterion ID, the section or frame that satisfies it |
| State completeness | Every required state, breakpoint, content variant, keyboard path, and failure is present | Row in states.md, fixture name, screenshot path |
| System coherence | Only canonical tokens, components, interaction patterns, and terms are used | Token gate output, component list, glossary term |
| Evidence quality | Another reviewer can reproduce each finding from the route, state, viewport, and check named | The command or route to rerun, artifact version |
A 10 does not mean “looks excellent”. It means every declared criterion has reproducible proof and no blocker is open. For normal work, set the ship threshold at 9 with no open blocker, no criterion left fail or not-tested, and a human acceptance recorded.
Save this schema as design/rating.schema.json. It makes the reviewer return the same structure every time, so a script can gate on it. Every object sets additionalProperties: false and lists all its properties as required, because Codex’s --output-schema expects a strict schema. Keep the 0 to 10 range in the prompt, and let the jq gate in step 4 reject any score outside it, so the range is enforced by the script rather than trusted.
{ "type": "object", "additionalProperties": false, "required": ["artifact_version", "criteria", "dimensions", "findings"], "properties": { "artifact_version": { "type": "string" }, "criteria": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "result", "evidence"], "properties": { "id": { "type": "string" }, "result": { "type": "string", "enum": ["pass", "fail", "not-tested"] }, "evidence": { "type": "string" } } } }, "dimensions": { "type": "object", "additionalProperties": false, "required": ["intent", "states", "system", "evidence"], "properties": { "intent": { "type": "integer" }, "states": { "type": "integer" }, "system": { "type": "integer" }, "evidence": { "type": "integer" } } }, "findings": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "criterion", "severity", "evidence", "fix"], "properties": { "id": { "type": "string" }, "criterion": { "type": "string" }, "severity": { "type": "string", "enum": ["blocker", "major", "minor"] }, "evidence": { "type": "string" }, "fix": { "type": "string" } } } } }}Run the rating loop
Section titled “Run the rating loop”-
Freeze the version you rate.
Commit the artifact, or pin the design-tool link to a version, and record that identifier in
design/checkout/VERSION. A rating that cannot name the exact version it scored cannot be compared with the next one. Keep all ratings for one artifact indesign/<feature>/ratings/, numberedr1.json,r2.json, and so on. Increment the round number on every run and never write over an existing rating file: the re-rate compares against the previous one. -
Run the deterministic checks first.
Anything a machine can decide, a machine decides: the Playwright state spec, the axe scan, and the token gate from the four-artifact pipeline, or a spec linter for text artifacts. Put the commands in
design/checkout/checks/run.shso every round, and the fixer, runs exactly the same checks, and have it write its output todesign/checkout/checks/. The reviewer cites that output as evidence; a failing check caps its dimension, whatever the reviewer thinks of the rest. -
Rate the artifact in a fresh, read-only context.
The reviewer must not be the session that produced the artifact, and it must not be able to edit it. Save the prompt below as
design/checkout/rate-prompt.md.Run it headless so the output lands in a file:
Terminal window claude -p --permission-mode dontAsk \--allowedTools "Read Grep Glob" \--disallowedTools "Edit Write NotebookEdit Bash" \--output-format json \--json-schema "$(cat design/rating.schema.json)" \"$(cat design/checkout/rate-prompt.md)" \| jq '.structured_output' > design/checkout/ratings/r1.jsondontAskdenies every tool call that would prompt (reads in the working directories and read-only commands still run) and--disallowedToolsremoves the write tools even if a settings file allows them, so the reviewer can read but not write; with--json-schema, the validated object arrives in thestructured_outputfield (checked on Claude Code 2.1.283).Terminal window codex exec --ephemeral \-c 'default_permissions=":read-only"' \--output-schema design/rating.schema.json \-o design/checkout/ratings/r1.json \< design/checkout/rate-prompt.md:read-onlyis a built-in permission profile (beta in Codex CLI 0.157.1; do not combine it with the legacy-s read-onlyflag, because OpenAI says the two systems do not compose),-owrites the final schema-shaped message to the file, and--ephemeralpersists no session files, so the review cannot be resumed into the fix task by mistake.Start a new Agent chat, not the one that produced the artifact. Paste the prompt,
@-reference the artifact, the criteria, the check output, anddesign/rating.schema.json, and ask for the JSON in one code block. Save that block asdesign/checkout/ratings/r1.jsonyourself. The review only has to read, so reject any file edit the chat proposes; an edit means the rating is void and has to run again.In Claude Code, a read-only custom subagent gives the same separation for interactive work inside a session.
-
Gate on the score with a script, not on the reviewer’s summary.
The reviewer supplies judgements; the script does the arithmetic. This gate exits non-zero unless the rating names the frozen version, every dimension is between 0 and 10, the minimum dimension is at least 9, no blocker is open, and every criterion passes. Run it on the newest rating file:
Terminal window jq -e --arg v "$(cat design/checkout/VERSION)" '{version_ok: (.artifact_version == $v),in_range: ([.dimensions[]] | all(. >= 0 and . <= 10)),score: ([.dimensions[]] | min),blockers: ([.findings[] | select(.severity == "blocker")] | length),not_passing: ([.criteria[] | select(.result != "pass")] | length)}| ., (.version_ok and .in_range and .score >= 9 and .blockers == 0 and .not_passing == 0)' design/checkout/ratings/r1.jsonIt prints the five values and then
trueorfalse;-esets the exit code from that last value, so the same command works in a terminal, a pre-merge job, or a loop script. A rating of the wrong version, or one with a score outside 0 to 10, fails the gate instead of passing quietly. -
Give every finding a disposition in the ledger.
Copy the findings into
design/checkout/findings.md, one row each, and have the artifact owner set the disposition. Findings are never deleted; they change status.| ID | Round | Criterion | Severity | Status | Owner | Note || F-1 | r1 | AC-4 | blocker | fixed | design lead | fixed in r2, verified r3 || F-2 | r1 | AC-7 | major | accepted | product | ships without offline state || F-3 | r2 | AC-4 | minor | open | design lead | |acceptedmeans a named human chose to ship with the gap;duplicatepoints at another ID;fixedis set by the artifact owner fromfix-notes.md, and stays provisional until the next re-rate confirms it with evidence;openis everything else. -
Fix in a separate task.
The fixer gets the ledger, not the whole conversation with the reviewer. It may change the artifact, and nothing else.
-
Re-rate in a new context, with the ledger.
A fresh reviewer rates the new version and also checks the previous ledger, so old findings are verified as well as new ones found. Save the prompt below as
design/checkout/rerate-prompt.md.Run it like step 3, with two changes. The new rating is written to a temporary file and moves into
ratings/only once it is valid JSON, and the previous round is named on the prompt’s last line. A plain> r2.jsonwould not be safe here: the shell creates the empty target before the reviewer starts, so a reviewer told to read “the newest file” would compare against an emptyr2.jsonand find no regressions. For the first re-rate,previsr1.jsonandnextisr2.json:Terminal window prev=design/checkout/ratings/r1.jsonnext=design/checkout/ratings/r2.jsontmp=$(mktemp)test -s "$prev" && test ! -e "$next" \&& claude -p --permission-mode dontAsk \--allowedTools "Read Grep Glob" \--disallowedTools "Edit Write NotebookEdit Bash" \--output-format json \--json-schema "$(cat design/rating.schema.json)" \"$(cat design/checkout/rerate-prompt.md)Previous rating file: $prev" \| jq -e '.structured_output' > "$tmp" \&& mv "$tmp" "$next"Terminal window prev=design/checkout/ratings/r1.jsonnext=design/checkout/ratings/r2.jsontmp=$(mktemp)test -s "$prev" && test ! -e "$next" \&& { cat design/checkout/rerate-prompt.mdprintf 'Previous rating file: %s\n' "$prev"; } \| codex exec --ephemeral \-c 'default_permissions=":read-only"' \--output-schema design/rating.schema.json \-o "$tmp" - \&& jq -e . "$tmp" > /dev/null \&& mv "$tmp" "$next"The trailing
-makescodex execread the whole prompt, including the appended line, from stdin.Start another new Agent chat, paste the re-rate prompt and add a last line
Previous rating file: design/checkout/ratings/r1.json,@-reference the same files plusfindings.md,fix-notes.md, and that previous rating file, and save the JSON asdesign/checkout/ratings/r2.jsononly after the chat has answered.The chain fails closed and changes nothing in
ratings/if the previous file is missing or empty, if the next file already exists, or if the reviewer exits with an error or returns no rating (jq -eexits non-zero on empty ornulloutput). The next round setsprevtor2.jsonandnexttor3.json, and so on. Then run the step 4 gate on the new file and update the ledger. -
Stop, and hand the decision to a human.
When the gate passes, or a stop condition below applies, the loop ends. A named person accepts the artifact and the ledger, and both travel with it into
spec.md,plan.md, and the pull request’s evidence bundle.
Let the reviewer collect browser evidence
Section titled “Let the reviewer collect browser evidence”A reviewer that reads files can only cite the check output you saved. To let it open the running app and collect state and viewport evidence itself, load the Playwright MCP server (@playwright/mcp, 0.0.82 on npm on 2026-09-26) for the review run only. Pin that version so every round collects evidence with the same browser tooling.
Replace the --allowedTools line of the step 3 command with these three options. --strict-mcp-config loads only this server and ignores the MCP servers in your user and project configuration, some of which may write; mcp__playwright allows every tool of that one server.
--strict-mcp-config \--mcp-config '{"mcpServers":{"playwright":{"command":"npx","args":["@playwright/mcp@0.0.82"]}}}' \--allowedTools "Read Grep Glob mcp__playwright"Add these configuration overrides before --output-schema in the step 3 command. codex mcp add would work too, but it writes the server into ~/.codex/config.toml for every future session, not just this review.
-c 'mcp_servers.playwright.command="npx"' \-c 'mcp_servers.playwright.args=["@playwright/mcp@0.0.82"]' \When should the rating loop stop?
Section titled “When should the rating loop stop?”Stop iterating and hand control to a human when any of these holds:
- The gate passes and every remaining finding has a disposition.
- Two rounds in a row do not improve the same failing criterion. More rounds rarely help; the criterion, the artifact, or the tool is wrong.
- A product, brand, legal, privacy, or accessibility decision is missing. An agent cannot make it, however many times it is asked.
- Fixing a finding would change accepted intent, a non-goal, or the architecture. That is a new decision, not an iteration.
- The reviewer cannot reproduce the artifact or collect the evidence a criterion needs.
- You reach your round cap. Set one before starting; three rounds is a reasonable default for one artifact.
Who signs off, and how do you trust the reviewer?
Section titled “Who signs off, and how do you trust the reviewer?”The rating is evidence for a human decision, never the decision itself. The agent that produced the artifact can critique its own work as a first pass, but it cannot certify it, because it can miss the same defect it created.
| Decision | Made by | Based on |
|---|---|---|
| Criteria and threshold | Artifact owner, before round 1 | acceptance.md, risk of the change |
| Each finding’s disposition | Artifact owner; product for scope, design-system owner for tokens | Ledger evidence |
accepted for an accessibility, legal, or privacy gap | The named owner of that area | Finding evidence and the reason recorded |
| Handoff acceptance | Artifact owner | Passing gate, ledger, last rating file |
Check the reviewer as you would check any test. Two routines keep it honest:
- Seed a known defect. Before relying on a new rubric or prompt, remove one required state from a copy of an accepted artifact and rate it. If the reviewer does not report a blocker against that criterion, the prompt or the criteria are too weak.
- Compare against a human rating. For a sample of handoffs, have a person rate the same version with the same rubric before reading the agent’s rating. Where the two disagree by more than a point on a dimension, tighten that dimension’s criteria. Evals for coding agents cover turning these samples into a regression suite.
What breaks in a rating loop?
Section titled “What breaks in a rating loop?”The score rises but the artifact does not. The reviewer grades the fixer’s notes instead of the artifact. Recover by rating from the frozen version only, diffing the version identifiers between rounds, and treating any fixed finding without evidence in the current version as a blocker, as the re-rate prompt does.
Everything receives 9 or 10. The criteria are too loose to fail. Recover by running the seeded-defect check, adding negative states and failure paths to acceptance.md, and lowering the cap for unsupported claims in the prompt.
The reviewer edits the artifact. The review ran with write access, usually in the producing session. Recover by discarding the edits, rerunning the rating with the read-only command, and keeping fix work in its own task.
Findings disappear between rounds. A fresh reviewer does not know what the last one found. Recover by always passing the ledger into the re-rate, and by never deleting a ledger row.
The loop never converges. Each round fixes one finding and creates another. Recover by stopping at the round cap, reading the last two rating files side by side, and asking whether the criteria contradict each other or the artifact needs a decision from its owner.
Taste outweighs function. Rounds go on spacing and wording while a state is missing. Recover by fixing blockers on intent, states, and accessibility first, and deferring minor system findings to a later round.
Check the rating before the handoff
Section titled “Check the rating before the handoff”- The rating file names the exact artifact version it scored.
- Every
failandnot-testedcriterion cites evidence another reviewer can reproduce. - The
jqgate passes on the last rating file. - Every finding in the ledger has a status, and every
acceptedrow names its owner. - The last rating ran in a fresh, read-only context.
- The accepted rating and ledger are attached to the next stage’s artifact.
Where to go next from the rating loop
Section titled “Where to go next from the rating loop”- Rate a visual direction before implementation starts with visual acceptance before frontend implementation.
- Apply the loop at each handoff of the four-artifact design-to-code pipeline.
- Write criteria worth rating against with executable acceptance criteria.
- Keep the checks the reviewer relies on out of the agent’s reach with protecting the oracle.
- Run the same pattern on pull requests with a bounded PR review-fix loop.