Fix detection and dedup
How the bot stays quiet on follow-up pushes — recognising fixes, suppressing duplicates, auto-resolving threads.
The naive way to review a PR with multiple pushes is to re-post every finding on every push. That drowns real signal in repetitive noise — by push three, no one is reading the comments. The bot solves this with a three-layer reconciliation pipeline: every existing comment is classified before any new finding is posted, and only genuinely novel issues reach the developer.
Three buckets per push
Every comment the bot has posted on the PR before this push is sorted into exactly one bucket. The bucket determines what happens next.
| Bucket | Meaning | What the bot does |
|---|---|---|
| Still present | The underlying issue has not been fixed in this push. | Nothing. The original comment stays as-is. No new comment is posted. |
| Fixed | The code that caused the comment has changed in a way that resolves the issue. | Posts an acknowledgement reply on the thread and auto-resolves the thread on GitHub. |
| Missing-file | The file the comment was posted on is not part of this push's diff. | Nothing. The comment stays open. The bot will never mark a comment as fixed just because the file is absent from the diff. |
How fixed is decided
Three layers, each narrower than the last. Every layer can fall back safely if upstream signals are unclear.
- Layer 1 — deterministic pre-filter — Splits existing comments into the three buckets using only the diff. A comment lands in 'missing-file' if its file is not in the diff at all; in 'still present' if its line was not modified within a ±3 line window; in 'fix-candidate' if its line is inside or adjacent to a modified hunk. This layer is pure code — no AI call, no cost, idempotent on retry.
- Layer 2 — per-comment fix assessment — Every fix-candidate gets one focused AI call. The model sees the single comment plus the exact diff hunk where the line changed and decides whether the issue is fixed or still present. Decisions are independent — one comment's classification cannot influence another's.
- Layer 3 — dedup of new findings — After layer 2, the new findings emitted by the deep review are deduplicated against any still-present comments. A fast pass catches exact file:line matches and self-duplicates within the same review. A semantic AI pass catches the same issue rephrased or shifted by a few lines. Anything that survives both passes is genuinely novel and gets posted.
Conservative defaults on uncertainty
Every layer defaults toward keeping the comment open. If the fix-assessment AI call errors, times out, or returns an unparseable response, the comment is treated as still present. If the dedup AI call fails, the new finding is treated as novel and posted. The reasoning is asymmetric: a wrongly-resolved real bug is silent and erodes trust forever; a duplicate comment is noisy but obviously fixable. The bot biases toward the recoverable failure mode.
Auto-resolve on GitHub
When a comment is classified as fixed, the bot resolves the GitHub review thread via the GraphQL API. The thread collapses, signalling visually that the issue is closed. Auto-resolve is capped at 10 thread resolutions per push to stay under GitHub's rate limits — on a push that fixes more than 10 comments, the rest are acknowledged with a reply but not collapsed; they will be resolvable on subsequent pushes if they remain fixed.
Re-pinned comments
Some findings are about code that is correct on the line that was changed but wrong somewhere else in the same file (or in a sibling file that was not changed in this push). GitHub's inline-comment API only accepts comments on lines that exist in the diff, so the bot anchors these on the closest valid line and prefixes the body with '(About <real-path>:<real-line> — outside this diff, re-pinned here for visibility.)'.
- On every future push, fix detection looks up the REAL file's diff hunk for that comment, not the anchor file's. A re-pinned comment cannot be silently kept open just because the anchor file is unchanged.
- If the real file is missing from a future push entirely, the comment falls into the missing-file bucket — same conservative default as any other missing-file case.
- The body prefix is parsed by a single shared module, so re-pin formatting and re-pin lookup never drift apart.
Tuning
Two settings control what users see when a fix is detected. Both default on; turning either off suppresses one half of the fixed-comment treatment.
| Setting | Default | Effect when off |
|---|---|---|
| autoResolveThreads | true | Threads stay open visually on GitHub even when the bot has marked the comment as fixed. The acknowledgement reply still posts. |
| postFixedReply | true | No 'fixed in latest push' acknowledgement reply is posted. The thread is still auto-resolved if autoResolveThreads is on. |