Repository navigation
This feature release adds a diffloupe gate command that fails a check while findings are still open on the pull request, adds --max-rounds to stop reviewing a PR after a set number of rounds, lets authors and bots dispute a finding and get a ruling, adds a post setting to keep low-severity findings out of inline comments, blocks review agents from reaching the network, and recovers agent answers that were given in the reply instead of the output file.
Changelog
diffloupe gate: Fail on Findings Still Open on the PR
review --fail-on only counts the findings posted in that round. If a finding was posted in round 1 and the author pushed something unrelated, round 2 passed. That's not much of a gate, especially for PRs written by agents that will happily push until the check goes green.
diffloupe gate reads the PR's live state from GitHub and fails when an open finding is at or above the threshold:
diffloupe gate 'owner/repo#123' --fail-on blocker --status "$HEAD_SHA"A finding counts as open when it is:
- a DiffLoupe inline thread that isn't resolved and hasn't been closed out as fixed by a later review (outdated threads still count), or
- a finding in the latest summary comment.
Severity comes from a new sev field in the comment markers. Comments from older versions fall back to the severity badge in the text. Findings marked pre-existing are listed but don't fail the gate.
Options:
--jsonprints the result as JSON.--status [sha]sets adiffloupe/gatecommit status on that commit (default: the PR head).--override-label <name>passes the gate when the PR has that label.--count-author-resolvedtreats threads the PR author resolved as closed. By default, a thread the author resolved keeps counting, so an author (or their agent) can't clear a blocker by clicking "Resolve".--review-missingfails when no review has ever reached the PR. Use it when the review job failed, so a PR whose first review broke doesn't pass as clean.
The gate exits 3 when it fails and 0 when it passes.
Because the summary comment is rewritten every round, a re-review now carries forward earlier summary findings that it didn't post again (files it didn't read, or findings cut by the cap) in a "Still open from earlier reviews" section. That keeps them visible to the gate.
--max-rounds
diffloupe review --max-rounds <n> skips the review once a PR has already had n rounds. It stops before any model call. A headless run exits 4 and result.json has status skipped with skipReason: "round-limit". With --github, the payloads put a "Reviews stopped" notice at the top of the existing summary comment, saying why it stopped and that the remaining findings are left for a human.
This is meant for agent-driven PRs: review and block a few times so the agent has a chance to fix things, then stop blocking instead of holding the PR forever. The example workflow labels the PR ai-review-max-rounds and passes the gate with --override-label, so the leftover findings stay on the PR and can be tracked later.
Round Progress in result.json
result.json has a new progress object showing where the bot's earlier findings stand after this review: open, fixed, withdrawn, upheld, fixedThisRound, withdrawnThisRound and new. The same data is in run.json as roundProgress.
Answering and Disputing Findings
Authors and bots can now answer a finding with a marker in a comment:
<!-- ai-review-response fp=3f9a1c2b verdict=disputed -->
This path is only reached after the lock is held; see `acquire()` above.verdict is disputed or fixed, and action works as an alias for verdict. One comment can answer several findings. Answers are read from any account, including other bots, in inline replies and top-level comments.
The coordinator rules on each dispute once:
- Withdrawn: the inline thread gets a reply that closes it, and the finding is never raised again on that PR, in any wording.
- Upheld: the thread gets a reply saying the finding still stands and stays open for a human reviewer.
Rulings are kept in a "Disputed findings" section of the summary comment, so later rounds keep them. A fixed answer is not a ruling. It is shown to the coordinator next to the finding, and the normal close-out decides. run.json has a new disputes list (withdrawn, upheld, or unjudged), and runs stats counts them.
post Setting: Choose What Gets Posted Inline
diffloupe.json has a new post setting:
{
"post": { "minSeverity": "should-fix", "questions": "drop" }
}Findings below minSeverity get no inline comment. They go into a collapsed "Minor findings" section of the summary comment and still count toward the verdict. questions: "drop" leaves questions out of the review entirely, before the finding cap, so they don't take up slots. result.json reports below-threshold findings as findings.minor instead of findings.posted.
Settings now reset to their defaults when a key is missing, so a long-running server no longer carries one repo's surfaces, extraExcludes or post rules into the next repo's run.
Review Agents Can't Reach the Network
We saw cheaper models run curl to fetch upstream sources mid-review. The governor now refuses shell commands that reach the network: curl, wget, ssh, nc and similar; gh api/gh pr/gh issue and other gh subcommands that call the API; git fetch/pull/push/clone/ls-remote; and network code inside node -e, bun -e, python -c and other inline interpreters. It looks through pipes, $(...), sh -c/bash -lc, timeout/env/xargs/nohup wrappers, and absolute paths like /usr/bin/curl.
This check reads command lines, so it can be worked around. In CI, also restrict outbound traffic to your model provider and GitHub, and keep write tokens out of the review step.
Reading Answers From the Final Reply
Some models give their JSON answer in the final chat reply instead of writing the output file, and the stage failed. When there's no output file, DiffLoupe now reads the answer from the reply, but only if it has the fields that stage expects. Across about 3,000 saved agent runs, this would have recovered 23 of 25 intent failures, 21 of 22 grouping failures and 9 of 10 walkthrough failures.
Example Workflow
examples/github-actions/review.yml is a copyable workflow with review, post and gate jobs, --max-rounds 3, the round-limit label, and the gate as the job to make required. DiffLoupe's own review.yml now runs the same review → post → gate flow. The README's "Running in CI" section documents the gate, answers to findings, exit code 4 and the new result.json fields.
What's Changed
- Add CI gate support for agent-driven PRs: severity posting rules, dispute handling, round limits, and a
gatecommand by @markerikson in #6
Full Changelog: v0.4.1...v0.5.0