{{ message }}
Repository navigation
Commit ddb0139
feat(review-pr): incremental reviews of new commits since last review (#37)
Implements Phase 2D (incremental review): on PRs with multiple review
cycles, re-reviews only the commits pushed since the last completed
review instead of the full PR diff. Saves tokens, avoids duplicate
comments, and skips unchanged code. Every unsafe or ambiguous state
falls back to a full review (fail-open), so a review is never blocked or
degraded.
## Issue mapping
| # | Item | Implementation |
| --- | --- | --- |
| 31 | Incremental review state tracking | Last reviewed SHA read from
the `commit_id` GitHub records on every posted review (`GET
/pulls/{n}/reviews`), which is the PR head SHA at posting time. Survives
across workflow runs with no extra writes and cannot be edited away like
an HTML comment marker. Only completed runs count (`### Assessment:` or
LGTM bodies); timeout and failure fallbacks leave commits unreviewed. |
| 32 | Commit-range diffing | `git diff <last-reviewed-sha>..HEAD`,
restricted at file level to the full PR diff so inline comments never
anchor outside what GitHub accepts (422). Full diff fallback when no
previous review exists or the base branch has been merged in (detected
by comparing merge-bases at last review vs now). |
| 33 | Rebases and force-pushes | `git cat-file -e` (SHA absent from the
clone) and `git merge-base --is-ancestor` (history rewritten) both force
a full review. |
| 34 | Finding deduplication across cycles | `src/dedupe-findings/`: new
comments matching an existing bot comment by file path + line proximity
(tolerance 3) + finding-heading Jaccard similarity (threshold 0.5) are
dropped before posting. Human comments and bot replies never suppress a
finding. Runs on every posting path (staged at
`/tmp/dedupe-findings.js`, existing comments pre-fetched by the action).
|
| 35 | `incremental` input | Added to `review-pr/action.yml` (default
`"true"`) and to the reusable workflow (`type: boolean`, default
`true`). `false` forces a full review. |
## Flow
```
pr.diff (full) -> incremental-review CLI
|- no previous review / rebase / force-push /
| base merged in / net-zero / any error -> full review (unchanged)
|- otherwise:
pr.diff <- git diff last-sha..HEAD (restricted to PR files)
pr_full.diff <- preserved full diff
(stale-thread resolution + suggestion validation)
```
## Fallback reasons (all covered by unit tests)
| Reason | Trigger |
| --- | --- |
| `no-previous-review` | no completed docker-agent review on the PR |
| `no-new-commits` | HEAD is the last reviewed commit (explicit
re-request) |
| `unknown-sha` | last reviewed SHA not in the local clone (force-push)
|
| `history-rewritten` | last reviewed SHA not an ancestor of HEAD
(rebase) |
| `base-unresolved` / `base-merged-in` | base ref missing, or merge-base
moved since last review |
| `net-zero-changes` | changes since last review cancel out against the
base |
| `disabled` / `error` / `no-diff` | input off, API/git failure, missing
pr.diff |
## Design notes
- Anchor safety: in incremental mode the original full diff is preserved
as `pr_full.diff`; stale-thread resolution and the suggestion validator
run against it (GitHub validates anchors against the full PR diff, not
the incremental range).
- Per AGENTS.md, all decision logic lives in TypeScript
(`src/incremental-review/`, `src/dedupe-findings/`) with Vitest
coverage; the action YAML only orchestrates CLI calls.
- The agent prompt gains an "Incremental Review" section instructing it
to review only `pr.diff` contents and to scope the assessment to the new
commits.
## Validation
| Check | Result |
| --- | --- |
| `pnpm build` (tsup) | pass |
| `tsc --noEmit` | pass |
| `biome ci .` | pass |
| `actionlint` | pass |
| Unit tests | 742 passed (61 new) |
| `tests/*.sh` shell integration | pass |
| Bundled CLI smoke tests (dedupe drop/keep, disabled and fail-open
paths) | pass |
---------
Signed-off-by: Sayt0 <138035894+Sayt-0@users.noreply.github.com>
Co-authored-by: Derek Misler <derek.misler@docker.com>1 parent bfd00a3 commit ddb0139
14 files changed
Lines changed: 1666 additions & 8 deletions
File tree
- .github/workflows
- review-pr
- agents
- refs
- src
- dedupe-findings
- __tests__
- incremental-review
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
42 | 46 | | |
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
46 | 54 | | |
47 | 55 | | |
48 | 56 | | |
| |||
172 | 180 | | |
173 | 181 | | |
174 | 182 | | |
| 183 | + | |
175 | 184 | | |
176 | 185 | | |
177 | 186 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
262 | 262 | | |
263 | 263 | | |
264 | 264 | | |
| 265 | + | |
265 | 266 | | |
266 | 267 | | |
267 | 268 | | |
| |||
285 | 286 | | |
286 | 287 | | |
287 | 288 | | |
| 289 | + | |
288 | 290 | | |
289 | 291 | | |
290 | 292 | | |
| |||
357 | 359 | | |
358 | 360 | | |
359 | 361 | | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
360 | 400 | | |
361 | 401 | | |
362 | 402 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
77 | 81 | | |
78 | 82 | | |
79 | 83 | | |
| |||
266 | 270 | | |
267 | 271 | | |
268 | 272 | | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
269 | 292 | | |
270 | 293 | | |
271 | 294 | | |
| |||
398 | 421 | | |
399 | 422 | | |
400 | 423 | | |
401 | | - | |
402 | | - | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
403 | 432 | | |
404 | 433 | | |
405 | 434 | | |
| |||
426 | 455 | | |
427 | 456 | | |
428 | 457 | | |
429 | | - | |
| 458 | + | |
430 | 459 | | |
431 | 460 | | |
432 | 461 | | |
| |||
709 | 738 | | |
710 | 739 | | |
711 | 740 | | |
| 741 | + | |
| 742 | + | |
712 | 743 | | |
713 | 744 | | |
714 | 745 | | |
| |||
740 | 771 | | |
741 | 772 | | |
742 | 773 | | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
743 | 783 | | |
744 | 784 | | |
745 | 785 | | |
| |||
786 | 826 | | |
787 | 827 | | |
788 | 828 | | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
789 | 857 | | |
790 | 858 | | |
791 | 859 | | |
| |||

0 commit comments