Loop Review — phase 3 of loop-run
Input: an ISSUE-ID. State dir: $LOOP_STATE_DIR/<ISSUE-ID>/ (default ~/.claude/loop-state/<ISSUE-ID>/). Requires spec.md, plan.md and state.md (worktree path). If missing, stop and say which phase to run first. The worktree: path is all this skill needs — every command below is the same in both layouts.
Clean context: this skill runs with NO knowledge of how the code was written (loop-run dispatches it in a fresh subagent). Judge only what spec.md, plan.md and the diff say. Never assume good intent from the build phase.
Nothing is fetched. spec.md carries the ticket verbatim (## Ticket), its sources' decisive content, and each premise's verification. Do not call Linear or Notion: the orchestrator already paid for that, and a reviewer that re-reads the ticket from the source drifts from the spec the builder was judged against.
Steps
Get the diff. In the worktree from state.md:
git -C <worktree> add -N . && git -C <worktree> diff origin/main --stat
git -C <worktree> diff origin/main
(add -N only marks new files so they appear in the diff — it is part of this pipeline's git exception.)
Gates — scripts, not judgment. Run them in the worktree before reading a line of code; a red gate is a FAIL issue on its own and the build phase's claim is never trusted:
pnpm lint && pnpm types && pnpm translations:inspect && pnpm translations:ensure-consistency
<front>/scripts/diff-hygiene.sh origin/main <worktree> <state dir>/plan.md # every added comment declared, none in a refused position
<front>/scripts/loop-plan-check.sh <worktree> <state dir>/plan.md # new files / exports outside plan.md
loop-plan-check.sh exit 3 means new files/exports exist and plan.md declares them: each declared item is a question for check 2 below, and loop-run runs the adversarial pass on the same signal. Exit 1 (undeclared) should not survive the build gate; if it does, it is a FAIL issue tagged [gate:plan].
The whole diff first: read spec.md ## Ticket and ## Acceptance criteria, then the diff end to end, and answer before any check: does this diff, as a whole, make sense for the ticket's objective, and is it the smallest change that meets the criteria? A diff can pass every check below and still miss the point, or solve around a premise spec.md marked unverified — both are FAIL issues.
Checks. Seven, capped by scripts/skill-budget.sh: a new one enters only by deleting one.
- Every acceptance criterion is met: map each criterion to the code that satisfies it. A criterion with no code behind it, or code that renders the state the criterion describes as fact before the data answers (a badge claiming "none" while
loading), is a FAIL.
- Scope is the plan: nothing outside spec.md "Files to touch" and plan.md without a note in plan.md
## Deviations. For each new file or export loop-plan-check.sh listed, apply the inline test — write the diff without the abstraction; if that version is smaller or equal, the abstraction is a FAIL issue. A feature flag, prop, or callback left half-wired (rendered nowhere, or read but never distinguished from its absence) is scope that was started, not finished.
- Nothing is reimplemented: no new component/hook/util that replicates
lago-design-system, a shared module, or a global handler (the Apollo error link, the toast layer, the router wrappers). Open the candidate sibling and say why it did not fit before accepting the new one.
- Conventions and translations: neighboring code style,
.agents/docs/frontend-coding-styleguide.md, codegen output consistent; new keys in translations/base.json only where no existing label fit (search for one), no dead keys.
- Tests exist and test the branch: make-tests output is in the diff; no fixture default switches off the branch a test claims to cover (
.agents/docs/testing-practices.md → "Fixture Defaults"); with more than one navigating path, assertions pin the destination with toHaveBeenCalledWith and assert the routes NOT taken; every callback the component passes to a mocked hook (onCompleted, onError, ...) is captured and invoked.
- Follow the calls out of the diff: when the diff passes an existing hook/util an argument that used to be constant, open that implementation and verify it honours it; when it reuses a form component on a new surface, walk every optional callback prop the new caller omits and name the user action that omission disables; when it redirects to a route constant, confirm the tab it resolves to; for each persisted field, state what the read path puts back and what the next save sends.
- Declared comments earn their category, and nothing is left behind:
diff-hygiene.sh already refused undeclared and mis-positioned comments; read only plan.md ## Comments kept and FAIL any line whose comment does not do what its category claims — a constraint with no identifier a reader could grep, a why-not where the alternative was never viable, a trap nothing actually trips — or that justifies the diff or answers a review round. No dead code, no unused exports.
Second pass with the code-review skill — inline. Run /code-review in THIS session on the worktree diff, never through the Agent tool: a subagent copy has stalled past its bound on every recorded run and returned later against a stale tree. Fold confirmed findings into the issues list and state in review.md which pass produced each.
Write review.md in the state dir. Every issue names the check that produced it so loop-run can journal it:
PASS format:
Verdict: PASS
## Criteria mapping
<one line per acceptance criterion: criterion → file/code that satisfies it>
FAIL format:
Verdict: FAIL
## Issues
1. [review#<check> | gate:<name> | code-review] <file:line — problem — what to change>
2. ...
Issues must be concrete and actionable — file, line, problem, fix direction. No style nitpicks that don't change meaning.
Report the verdict and (if FAIL) the issue list to the operator.
Hard rules
- Review is read-only on the code: never fix issues yourself, only report them.
- Uncertain whether something is a real problem → it is not an issue; note it as a remark below the Issues list instead.
- Never fetch Linear or Notion. Never run the full jest suite.
- Two communication registers: messages to humans (chat report, notifications) = short, direct, plain language, no deep-tech jargon. Internal state files (spec.md, review.md, histories, working notes) = written for the AI of a later iteration: dense, precise, full paths/symbols/error strings — optimize for machine effectiveness, not human readability.
1---2name: loop-review3description: Phase 3 of the loop pipeline for lago-front. Takes an ISSUE-ID, reviews the worktree diff against spec.md and plan.md with clean context, and writes a PASS/FAIL verdict to review.md in the run state dir. Use when user says "/loop-review <ISSUE-ID>" or the loop-run orchestrator dispatches the review phase in a fresh subagent.4---56# Loop Review — phase 3 of loop-run78**Input:** an ISSUE-ID. State dir: `$LOOP_STATE_DIR/<ISSUE-ID>/` (default `~/.claude/loop-state/<ISSUE-ID>/`). Requires `spec.md`, `plan.md` and `state.md` (worktree path). If missing, stop and say which phase to run first. The `worktree:` path is all this skill needs — every command below is the same in both layouts.910**Clean context:** this skill runs with NO knowledge of how the code was written (loop-run dispatches it in a fresh subagent). Judge only what spec.md, plan.md and the diff say. Never assume good intent from the build phase.1112**Nothing is fetched.** spec.md carries the ticket verbatim (`## Ticket`), its sources' decisive content, and each premise's verification. Do not call Linear or Notion: the orchestrator already paid for that, and a reviewer that re-reads the ticket from the source drifts from the spec the builder was judged against.1314## Steps15161. **Get the diff.** In the worktree from state.md:1718 ```bash19 git -C <worktree> add -N . && git -C <worktree> diff origin/main --stat20 git -C <worktree> diff origin/main21 ```2223 (`add -N` only marks new files so they appear in the diff — it is part of this pipeline's git exception.)24252. **Gates** — scripts, not judgment. Run them in the worktree before reading a line of code; a red gate is a FAIL issue on its own and the build phase's claim is never trusted:2627 ```bash28 pnpm lint && pnpm types && pnpm translations:inspect && pnpm translations:ensure-consistency29 <front>/scripts/diff-hygiene.sh origin/main <worktree> <state dir>/plan.md # every added comment declared, none in a refused position30 <front>/scripts/loop-plan-check.sh <worktree> <state dir>/plan.md # new files / exports outside plan.md31 ```3233 `loop-plan-check.sh` exit 3 means new files/exports exist and plan.md declares them: each declared item is a question for check 2 below, and loop-run runs the adversarial pass on the same signal. Exit 1 (undeclared) should not survive the build gate; if it does, it is a FAIL issue tagged `[gate:plan]`.34353. **The whole diff first**: read spec.md `## Ticket` and `## Acceptance criteria`, then the diff end to end, and answer before any check: does this diff, as a whole, make sense for the ticket's objective, and is it the smallest change that meets the criteria? A diff can pass every check below and still miss the point, or solve around a premise spec.md marked `unverified` — both are FAIL issues.36374. **Checks.** Seven, capped by `scripts/skill-budget.sh`: a new one enters only by deleting one.3839 <!-- checks:start -->40 1. **Every acceptance criterion is met**: map each criterion to the code that satisfies it. A criterion with no code behind it, or code that renders the state the criterion describes as fact before the data answers (a badge claiming "none" while `loading`), is a FAIL.41 2. **Scope is the plan**: nothing outside spec.md "Files to touch" and plan.md without a note in plan.md `## Deviations`. For each new file or export `loop-plan-check.sh` listed, apply the inline test — write the diff without the abstraction; if that version is smaller or equal, the abstraction is a FAIL issue. A feature flag, prop, or callback left half-wired (rendered nowhere, or read but never distinguished from its absence) is scope that was started, not finished.42 3. **Nothing is reimplemented**: no new component/hook/util that replicates `lago-design-system`, a shared module, or a global handler (the Apollo error link, the toast layer, the router wrappers). Open the candidate sibling and say why it did not fit before accepting the new one.43 4. **Conventions and translations**: neighboring code style, `.agents/docs/frontend-coding-styleguide.md`, codegen output consistent; new keys in `translations/base.json` only where no existing label fit (search for one), no dead keys.44 5. **Tests exist and test the branch**: make-tests output is in the diff; no fixture default switches off the branch a test claims to cover (`.agents/docs/testing-practices.md` → "Fixture Defaults"); with more than one navigating path, assertions pin the destination with `toHaveBeenCalledWith` and assert the routes NOT taken; every callback the component passes to a mocked hook (`onCompleted`, `onError`, ...) is captured and invoked.45 6. **Follow the calls out of the diff**: when the diff passes an existing hook/util an argument that used to be constant, open that implementation and verify it honours it; when it reuses a form component on a new surface, walk every optional callback prop the new caller omits and name the user action that omission disables; when it redirects to a route constant, confirm the tab it resolves to; for each persisted field, state what the read path puts back and what the next save sends.46 7. **Declared comments earn their category, and nothing is left behind**: `diff-hygiene.sh` already refused undeclared and mis-positioned comments; read only plan.md `## Comments kept` and FAIL any line whose comment does not do what its category claims — a `constraint` with no identifier a reader could grep, a `why-not` where the alternative was never viable, a `trap` nothing actually trips — or that justifies the diff or answers a review round. No dead code, no unused exports.47 <!-- checks:end -->48495. **Second pass with the code-review skill — inline.** Run `/code-review` in THIS session on the worktree diff, never through the Agent tool: a subagent copy has stalled past its bound on every recorded run and returned later against a stale tree. Fold confirmed findings into the issues list and state in review.md which pass produced each.50516. **Write `review.md`** in the state dir. Every issue names the check that produced it so loop-run can journal it:5253 PASS format:5455 ```markdown56 Verdict: PASS5758 ## Criteria mapping59 <one line per acceptance criterion: criterion → file/code that satisfies it>60 ```6162 FAIL format:6364 ```markdown65 Verdict: FAIL6667 ## Issues68 1. [review#<check> | gate:<name> | code-review] <file:line — problem — what to change>69 2. ...70 ```7172 Issues must be concrete and actionable — file, line, problem, fix direction. No style nitpicks that don't change meaning.73747. **Report** the verdict and (if FAIL) the issue list to the operator.7576## Hard rules7778- Review is read-only on the code: never fix issues yourself, only report them.79- Uncertain whether something is a real problem → it is not an issue; note it as a remark below the Issues list instead.80- Never fetch Linear or Notion. Never run the full jest suite.81- **Two communication registers**: messages to humans (chat report, notifications) = short, direct, plain language, no deep-tech jargon. Internal state files (spec.md, review.md, histories, working notes) = written for the AI of a later iteration: dense, precise, full paths/symbols/error strings — optimize for machine effectiveness, not human readability.