Repository navigation
v0.4.0
This feature release adds a CI mode for headless reviews of GitHub pull requests, puts a copy of all findings into the PR summary comment, adds finding triage (accept, or dismiss with a reason) in the CLI, server and web UI, records token usage and how often findings get acted on, and adds --strict and --json to diffloupe doctor.
Changelog
CI Mode
DiffLoupe's headless mode was built for running a review from a terminal. Running it from a CI job that comments on the pull request needed a few more things: a fixed commit to review, a machine-readable result, distinct exit codes, and a guarantee that it reads the PR's actual comments instead of whatever runs happen to be saved on the machine.
diffloupe review --headless has five new options:
--githubreviews for a bot that posts the result. DiffLoupe still posts nothing itself. It writes the GitHub API calls topayloads.json, and a later job with a write token sends them.--head <sha>pins the review to the commit the workflow was triggered for, so a push during the run doesn't change what gets reviewed. It must be a full 40-character SHA in the PR's history.--out <dir>copies the run's files there, plus aresult.json.--jsonprintsresult.jsonon stdout instead of the summary.--fail-on <severity>exits 3 when a posted finding is at or above that severity.
diffloupe review '#123' --headless --github --head "$HEAD_SHA" --out review-out --max-cost 10With --github:
- Earlier rounds come only from the PR's comments on GitHub. Runs saved on the CI machine are ignored, so a cached data directory can't make the bot reply to comments that aren't on the PR.
- If
ghfails while reading the PR description or comments, the run fails. Before, it reviewed without them and could post the same findings again. - Every round reviews the whole PR. The summary comment is rewritten on each push and holds the findings, so a round that only looked at changed files would drop the open findings on the others.
result.json has the run's status (complete, failed, cancelled or skipped), the reviewed base and head, cost and wall time, whether the cost cap stopped it early, finding counts by severity, the round, the path to payloads.json, and the artifact name the summary comment links to.
Headless runs now use distinct exit codes:
| Code | Meaning |
|---|---|
| 0 | Reviewed, or skipped because there were no code files |
| 1 | The run failed |
| 2 | The run could not start, including bad flags and unknown options |
| 3 | --fail-on matched a posted finding |
| 130 | Cancelled |
A headless run that fails to start now exits 2 instead of 1. If a script checks for exit code 1, update it.
The README has a new "Running in CI" section with the result.json shape, the files in --out, and a gate job pattern. --fail-on and the gate job count only the findings posted in that round, so a finding posted in an earlier round no longer fails the check. To block merges until every finding is dealt with, use branch protection's "Require conversation resolution before merging".
The summary comment's marker now records the head it reviewed. We also removed the leftover auto-pause text and the /ai-review resume hint from the summary. DiffLoupe never implemented auto-pause.
Copy All Findings From the Summary Comment
The PR summary comment now has a collapsed "Copy all findings as Markdown" section. It holds every finding in a fenced block, so GitHub's copy button copies them all at once, for example to paste into an agent session.
When the full text would push the comment past GitHub's size limit, findings are cut to their heading and location, starting from the end of the list, until it fits. A note then points at findings.md in the run's artifact for the full text. When even that doesn't fit, a single line points at the artifact instead. The next review round leaves this section out of what it shows the model, so earlier findings aren't fed back to the reviewers in full.
Finding Triage
Findings can now be accepted, or dismissed with a reason: wrong, not-worth-fixing, already-handled, duplicate or other, plus an optional note.
- CLI:
diffloupe runs dismiss <run> <ids...> --reason <reason> [--note <text>],runs acceptandruns reopen.runs showprints finding ids and their triage status, and--jsonincludes it. - Web UI: each finding card has Accept and Dismiss buttons. Each file has a Viewed checkbox (or press
r) that collapses it, and a "show dismissed" toggle brings dismissed findings back. Dismissed findings are left out of the verdict, the counts,n/pnavigation and Copy all. - Server:
GET /api/runs/:id/state,PUT /api/runs/:id/state/findings/:fidandPUT /api/runs/:id/state/files. They return 409 while the run is still active.
runs stats splits dismissals by reason.
Token Usage and Acted-On Rates
Each run now records token usage per review slice and per stage (coordinator, walkthrough, intent draft), including how much of each session's first request came from the prompt cache. runs show and runs stats print it. For runs saved before this release, they read it from raw/record.json.
A re-review also records which of the previous run's findings had their code edited before it ran. A control span 35 lines away is checked the same way. runs stats uses both to report an acted-on rate next to a baseline, so you can tell whether findings lead to fixes more often than unrelated nearby edits.
diffloupe doctor --strict and --json
--strict makes warnings fail too. Use it in CI: a model that reports no cost is only a warning, but --max-cost can't be enforced without one. --json prints the checks as JSON.
Other Changes
Runs now record the DiffLoupe version they ran with. Installed copies report diffloupe@0.4.0. A git checkout reports the version plus the commit, such as diffloupe@0.4.0+1a2b3c4d5e6f. Before, an installed copy recorded only diffloupe, or the commit hash of whatever git repository it happened to be installed in.
What's Changed
- Record token usage and acted-on rates, and add finding triage by @markerikson in #1
- Add CI mode for headless reviews, plus a self-review workflow by @markerikson in #2
Full Changelog: v0.3.0...v0.4.0