miki-review-loop
What this does
Runs /review → addresses issues → re-runs, up to 3 iterations. Exits when only nits remain, a judgment call surfaces, or the cap is hit. Ends with a written summary of every change and commit.
Preconditions
Verify in order; stop and report on first failure:
- PR identification.
- Argument given (bare
84, #84, PR 84, PR #84, or full PR URL): normalize to a bare number, run gh pr view <n> --json number,title,headRefName,baseRefName,state, announce Processing PR #N — "<title>" on <head> → <base>, proceed.
- No argument: derive from current branch via
gh pr view --json number,title,headRefName,baseRefName,state. Announce Detected PR #N — ... and ask to confirm. If ambiguous, list candidates and ask.
- No PR or not OPEN: stop.
- Clean working tree (
git status --porcelain empty).
- Not on
main / master.
- Verification command available (see below). If not, follow the prompt-to-create flow before iteration 1.
Verification command
The loop runs the repo's verification command between iterations to confirm fixes don't break the build. Detect on each run:
package.json scripts — try in order: check, verify, validate, ci, test. For each, prefer the :ci variant when present (e.g. check:ci over check) — tuned for non-interactive runs. First match wins.
- Other stacks —
Cargo.toml → cargo check && cargo test; Makefile with check target → make check; pyproject.toml with [tool.<runner>] → obvious check/test task.
CLAUDE.md — grep for a documented verification command (e.g. under a "Verification" header or a fenced block tagged as the canonical check). If present, use it.
One candidate found → use it, tell the user. Multiple plausible → list and ask.
If none can be found
Stop before iteration 1 and tell the user:
I couldn't find a verification command for this repo. The loop runs one between iterations to catch broken intermediate commits before they compound.
Options:
(a) Add a check script to package.json aggregating lint + typecheck + test.
(b) Document the command in CLAUDE.md under a "Verification" section.
(c) Give me the command now — for this run only, won't persist.
(d) Skip verification — not recommended; risky on iteration 2+ (broken intermediate commits compound).
For (a) or (b), draft the addition and show it to the user before writing; persist as a separate atomic commit via the user's normal flow.
Auto-merge handling
After preconditions pass, check gh pr view <n> --json autoMergeRequest,reviewDecision. If auto-merge is enabled, disable it (gh pr merge <n> --disable-auto) before iteration 1 and record the merge method (SQUASH/MERGE/REBASE) — otherwise the merge can fire on the pre-fix SHA the moment CI greens. Announce auto-merge paused for loop duration; also mention if reviewDecision is APPROVED (that's why the pause matters).
Restore via gh pr merge <n> --auto --<method> after the final push, and on every exit path: clean (only nits or no findings at all, no commits made), judgment call surfaced, iteration cap reached, build verification failed, push failed. The trivial-clean exit where no commits were made still requires restoration — don't conflate "nothing to push" with "nothing to restore". Restoration is the last action before writing the summary, not something the user has to trigger by saying "please merge". If disable or restore fails, stop and report — don't iterate without the pause, don't leave auto-merge in a different state than you found.
Severity bands
Classify each /review finding:
- Blocker — breaks correctness, security, or build. Fix.
- Major — clear defect or convention violation, single right answer (e.g. missing null check on documented-non-null field). Fix.
- Minor — single obvious right answer, no behavior change (e.g. unused import, typo). Fix.
- Nit — style/preference, multiple valid choices. Skip.
- Judgment call — depends on intent, tradeoffs, or info not in the diff (architecture, naming, public-API contracts, behavior changes, anything needing a new test). Stop and ask.
When in doubt between Minor and Judgment call, treat as judgment.
The loop
For iteration i in 1..3:
- Run
/review.
- Classify findings; show the grouped list to the user before acting.
- Any judgment calls → stop, ask one by one. Don't silently skip.
- No Blocker/Major/Minor remain → exit successfully (only nits, or nothing). Restore auto-merge before writing the summary (see Auto-merge handling).
- Address Blocker + Major + Minor. If a finding turns out more ambiguous while fixing it, stop and ask.
- Run the verification command. If it fails, fix the failure and re-verify before committing. If you can't fix cleanly within the same logical change, surface as a judgment call.
- Commit atomically (see below).
- Track every change (file path + one-line) and commit (sha + message) in a running log.
If iteration 3 finishes with non-nit issues remaining, stop and report.
Commit and push cadence
- Atomic commits per project convention (Conventional Commits in most projects — check
git log or CLAUDE.md). Two unrelated findings = two commits.
- Amend only when the new edit is part of the same atomic change as the previous commit — verification fix-the-fix, a follow-up iteration completing an incomplete fix. Never across two separate findings.
- Never amend a commit already pushed (prior loop run or anything not made by this loop). Treat existing history as immutable.
- Push once, at the end of the loop. Single
git push, no matter how the loop terminates. Never force-push.
Final message format
## Review loop summary
**PR**: #<number> — <title>
**Iterations run**: <n>/3
**Exit reason**: <clean (only nits remain) | iteration cap reached | judgment call surfaced | precondition failed | build verification failed | no verification command>
### Changes made
- <file:line> — <one-line description>
- ...
### Commits in this review cycle
- <sha> — <commit message> (new | amended)
- ...
### Remaining issues
- **Nits** (skipped): <count + one-line list>
- **Judgment calls** (need input): <list with the question, or "none">
- **Unaddressed non-nits** (only if cap hit): <list>
### Pushed
- <yes / no — if no, why>
- **Auto-merge**: <paused → restored as <method> | unchanged | restore failed: <reason>>
List every commit produced, including ones later amended (mark as amended). If no changes were made, say so plainly rather than printing empty lists.
Operating rules
- Never resolve findings by deleting tests, weakening assertions, or adding
eslint-disable / @ts-ignore. Those are judgment calls.
- Never broaden scope. If
/review flags untouched code, surface as a judgment call ("fix here, separate PR, or skip?") rather than silently expanding the diff.
- Stay in the diff. Clean up what this PR introduced, not the surrounding area.
- If
/review fails or returns nothing parseable, stop and report — don't guess what it would have said.
1---2name: miki-review-loop3description: Miki's personal pre-self-review loop. Iteratively runs /review on the current PR and addresses obvious issues until only minor nits remain or 3 iterations have run. Surfaces judgment calls back to the user, makes atomic commits per project convention, pushes once at the end. Invoke when the user says "/miki-review-loop", "loop the review", "iterate on the review", or similar after a PR has been created.4---56# miki-review-loop78## What this does910Runs `/review` → addresses issues → re-runs, up to 3 iterations. Exits when only nits remain, a judgment call surfaces, or the cap is hit. Ends with a written summary of every change and commit.1112## Preconditions1314Verify in order; stop and report on first failure:15161. **PR identification.**17 - **Argument given** (bare `84`, `#84`, `PR 84`, `PR #84`, or full PR URL): normalize to a bare number, run `gh pr view <n> --json number,title,headRefName,baseRefName,state`, announce `Processing PR #N — "<title>" on <head> → <base>`, proceed.18 - **No argument**: derive from current branch via `gh pr view --json number,title,headRefName,baseRefName,state`. Announce `Detected PR #N — ...` and **ask to confirm**. If ambiguous, list candidates and ask.19 - **No PR or not OPEN**: stop.202. **Clean working tree** (`git status --porcelain` empty).213. **Not on `main` / `master`.**224. **Verification command available** (see below). If not, follow the prompt-to-create flow before iteration 1.2324## Verification command2526The loop runs the repo's verification command between iterations to confirm fixes don't break the build. Detect on each run:27281. **`package.json` scripts** — try in order: `check`, `verify`, `validate`, `ci`, `test`. For each, prefer the `:ci` variant when present (e.g. `check:ci` over `check`) — tuned for non-interactive runs. First match wins.292. **Other stacks** — `Cargo.toml` → `cargo check && cargo test`; `Makefile` with `check` target → `make check`; `pyproject.toml` with `[tool.<runner>]` → obvious `check`/`test` task.303. **`CLAUDE.md`** — grep for a documented verification command (e.g. under a "Verification" header or a fenced block tagged as the canonical check). If present, use it.3132One candidate found → use it, tell the user. Multiple plausible → list and ask.3334### If none can be found3536Stop before iteration 1 and tell the user:3738> I couldn't find a verification command for this repo. The loop runs one between iterations to catch broken intermediate commits before they compound.39>40> Options:41> (a) **Add a `check` script** to `package.json` aggregating lint + typecheck + test.42> (b) **Document the command in CLAUDE.md** under a "Verification" section.43> (c) **Give me the command now** — for this run only, won't persist.44> (d) **Skip verification** — not recommended; risky on iteration 2+ (broken intermediate commits compound).4546For (a) or (b), draft the addition and show it to the user before writing; persist as a separate atomic commit via the user's normal flow.4748## Auto-merge handling4950After preconditions pass, check `gh pr view <n> --json autoMergeRequest,reviewDecision`. If auto-merge is enabled, disable it (`gh pr merge <n> --disable-auto`) before iteration 1 and record the merge method (`SQUASH`/`MERGE`/`REBASE`) — otherwise the merge can fire on the pre-fix SHA the moment CI greens. Announce `auto-merge paused for loop duration`; also mention if `reviewDecision` is `APPROVED` (that's why the pause matters).5152Restore via `gh pr merge <n> --auto --<method>` after the final push, **and on every exit path**: clean (only nits or no findings at all, no commits made), judgment call surfaced, iteration cap reached, build verification failed, push failed. The trivial-clean exit where no commits were made still requires restoration — don't conflate "nothing to push" with "nothing to restore". Restoration is the **last action before writing the summary**, not something the user has to trigger by saying "please merge". If disable or restore fails, stop and report — don't iterate without the pause, don't leave auto-merge in a different state than you found.5354## Severity bands5556Classify each `/review` finding:5758- **Blocker** — breaks correctness, security, or build. Fix.59- **Major** — clear defect or convention violation, single right answer (e.g. missing null check on documented-non-null field). Fix.60- **Minor** — single obvious right answer, no behavior change (e.g. unused import, typo). Fix.61- **Nit** — style/preference, multiple valid choices. **Skip.**62- **Judgment call** — depends on intent, tradeoffs, or info not in the diff (architecture, naming, public-API contracts, behavior changes, anything needing a new test). **Stop and ask.**6364When in doubt between Minor and Judgment call, treat as judgment.6566## The loop6768For iteration `i` in `1..3`:69701. Run `/review`.712. Classify findings; show the grouped list to the user before acting.723. **Any judgment calls** → stop, ask one by one. Don't silently skip.734. **No Blocker/Major/Minor remain** → exit successfully (only nits, or nothing). Restore auto-merge before writing the summary (see *Auto-merge handling*).745. Address Blocker + Major + Minor. If a finding turns out more ambiguous while fixing it, stop and ask.756. Run the verification command. If it fails, fix the failure and re-verify before committing. If you can't fix cleanly within the same logical change, surface as a judgment call.767. Commit atomically (see below).778. Track every change (file path + one-line) and commit (sha + message) in a running log.7879If iteration 3 finishes with non-nit issues remaining, stop and report.8081## Commit and push cadence8283- **Atomic commits per project convention** (Conventional Commits in most projects — check `git log` or CLAUDE.md). Two unrelated findings = two commits.84- **Amend** only when the new edit is part of the same atomic change as the previous commit — verification fix-the-fix, a follow-up iteration completing an incomplete fix. Never across two separate findings.85- **Never amend a commit already pushed** (prior loop run or anything not made by this loop). Treat existing history as immutable.86- **Push once, at the end** of the loop. Single `git push`, no matter how the loop terminates. Never force-push.8788## Final message format8990```91## Review loop summary9293**PR**: #<number> — <title>94**Iterations run**: <n>/395**Exit reason**: <clean (only nits remain) | iteration cap reached | judgment call surfaced | precondition failed | build verification failed | no verification command>9697### Changes made98- <file:line> — <one-line description>99- ...100101### Commits in this review cycle102- <sha> — <commit message> (new | amended)103- ...104105### Remaining issues106- **Nits** (skipped): <count + one-line list>107- **Judgment calls** (need input): <list with the question, or "none">108- **Unaddressed non-nits** (only if cap hit): <list>109110### Pushed111- <yes / no — if no, why>112- **Auto-merge**: <paused → restored as <method> | unchanged | restore failed: <reason>>113```114115List every commit produced, including ones later amended (mark as amended). If no changes were made, say so plainly rather than printing empty lists.116117## Operating rules118119- **Never resolve findings** by deleting tests, weakening assertions, or adding `eslint-disable` / `@ts-ignore`. Those are judgment calls.120- **Never broaden scope.** If `/review` flags untouched code, surface as a judgment call ("fix here, separate PR, or skip?") rather than silently expanding the diff.121- **Stay in the diff.** Clean up what this PR introduced, not the surrounding area.122- **If `/review` fails** or returns nothing parseable, stop and report — don't guess what it would have said.