feat(review-loop): loop review→fix→re-review until N consecutive rounds come back empty - #47
Open
jtomaszewski wants to merge 2 commits into
Open
feat(review-loop): loop review→fix→re-review until N consecutive rounds come back empty#47jtomaszewski wants to merge 2 commits into
jtomaszewski wants to merge 2 commits into
Conversation
…ds come back empty The naive loop — review, fix, review again — converges on the author's blind spots: the context holding the fix rationale checks the change against its own intent, not against the code. It catches self-contradiction and misses whatever it never thought of. review-loop keeps the loop and replaces the reviewer: each round fans out fresh read-only subagents across five lenses, none of which ever sees the rationale, the round number, or the ledger. Three mechanics make that terminate and stay cheap — dedupe against every finding ever raised (rejected ones included, or a rejected finding returns every round and the loop runs to the cap), refute each fresh finding before spending a code change on it, and exit on N *consecutive* empty rounds instead of a fixed iteration count. The report is the findings-per-round curve plus a claim scoped to what it proves: N independent passes under these lenses found nothing — not "a human reviewer will find nothing". Local only: posts nothing, merges nothing, hands off to deliver. version 1.2.2 -> 1.3.0 (new skill), README skill table row added.
…ted here The exit condition is "the next real review finds nothing", so the rounds have to run the review the change is actually headed for. Where the repo has an engine installed (om-code-review), each subagent runs that skill verbatim and reports in its severity scale, fanned out across its checklist sections; its validation gate is skipped inside the round, because a full gate per lens per round is what would make this too expensive to run. Where there is none — the usual client repo — the built-in lenses stand in, plus CODE_REVIEW.md and BACKWARD_COMPATIBILITY.md, which is the review deliver currently discovers one Copilot round at a time. The report names which standard ran; a loop that goes dry against a checklist nobody else applies has proven nothing.
Contributor
Author
|
Reworked the core after feedback: the rounds now run the review the change is actually headed for, instead of five lenses invented in this skill.
That makes the exit condition mean something: the next real review finds nothing, rather than my own checklist went quiet. New hard rule 3 says so, and the report names which standard ran. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
"Review it until nothing's left" is a loop we already run — but the loop as it exists converges on the wrong thing.
deliver's Phase 7 and the equivalent in other collections re-review after fixing. The re-review is performed by the context that wrote the fix, holding the reason the fix is right. That context checks the change against its own intent instead of against the code: reliably good at catching self-contradiction, structurally blind to whatever it never thought of.Field data from one run (a 2,400-line spec series, five prior human review rounds, each of which found something new): four self-review passes produced 3 → 3 → 1 → 0 findings. Round 1's best find was a real design hole — but it was a self-catch, an earlier fix claiming one mode inherited safety from another while the doc's own table already said it didn't. That's the shape of what a self-review finds. What it establishes is "no internal inconsistency survives four passes", not "the sixth reviewer finds nothing" — and the run's report had to say so.
Two smaller failures on the same run, both structural:
--max-iterations 3would have ended that run before the dry round — i.e. before there was any evidence of convergence at all.The fix
A new skill,
review-loop. It keeps the loop and replaces the reviewer.Each round fans out fresh read-only subagents across five lenses — correctness & data, security & tenancy, tests (would this fail against the pre-change code?), repo conventions, claims-vs-code — and none of them ever sees the fix rationale, the round number, or the ledger. The prompt-contamination list is explicit and includes the non-obvious one: telling a reviewer it's round 5 and that rounds 3–4 were empty is an instruction to find nothing.
Three mechanics keep that terminating and cheap, each written down with the failure it prevents:
--dry-rounds, default 2), capped by--max-rounds(default 6). Real curves decay; the exit condition should watch the decay, not a counter.The report is the findings-per-round curve plus a claim scoped to exactly what it proves — N consecutive independent passes under these lenses found nothing, naming the lenses and what they structurally can't see (no browser, no runtime, no production data). Never "this change is clean".
Judgement calls — design disagreements, scope questions, trade-offs the repo hasn't decided — get parked as
yoursrather than fixed or silently dropped. A PR target seeds the ledger with the review comments already on it asinheritedfindings, so the loop doesn't ignore the review that prompted it.Local only: posts nothing, pushes nothing, merges nothing, moves no tracker task. It leaves a branch and a report, and hands off to
deliver. No confidentiality gate, because it publishes nothing — the gate applies at thedeliverstep that follows.Prior art in this repo — and a question
#30 ("bound the review loop, and defer non-blocking findings") attacked the same area from the opposite end: it wanted
deliver's loop bounded, because each non-empty external review guarantees another 25-minute poll. It was closed with no comment and no review, and its content never landed —deliverstill polls ten minutes and has no Phase 7c.If that close meant "no review-loop machinery in this collection", this PR is in the same bucket and should be closed too. If it meant "not bolted onto
deliver", this is the separate skill.I took #30's one genuinely load-bearing rule: a round that changed a security boundary (auth, credentials, a trust or permission decision, what a sandbox allows, what leaves the machine) earns one more review even at the cap. Every other class of miss is correctable by a follow-up commit; that one is exposed the moment it merges.
Why standalone, not
deliver --until-drydeliver's loop waits on an external bot, is priced in 25-minute polls, and ends in a merge. This loop is local, subagent-driven, priced in tokens, and ends in a report. Same words, different economics and different terminal state — folding them together would mean one skill with two incompatible stop conditions.Name pairs with
review-queue: that one is many PRs, one pass each; this one is one change, many passes.Verification
review-loopadded to the repo-agnostic list.version-checkworkflow's condition checked by hand against this diff: touchesskills/**, head version strictly above base bysort -V.## Skill profile(check commands, review landmines, specs location, scratch dir) — no new profile knobs, so nothing for a consuming repo to add before this works.review-loopinvocation. Worth knowing before it's trusted unattended.🤖 Generated with Claude Code