v2.10.0

Added

  • Dismissing/marking-false-positive/wont-fixing a static-analyzer finding now points at the durable suppression mechanism (issue #775). A verdict on a finding sourced from a native analyzer only ever suppresses that exact fingerprint: the same false positive recurring in a different file was never affected by any number of prior verdicts, and nothing in the bot’s reply said so. The confirmation reply now appends a note directing the user to .github/ai-pr-review/suppressions.json when the finding’s source is a static analyzer. See Suppressions.

Fixed

A chain of bugs surfaced while investigating why one PR stayed blocked despite what its reporter believed was a correct false-positive dismissal:

  • dismiss/false-positive/wont-fix/fixed verdicts could be silently discarded whenever the canonical review being patched was also the one that first rendered the finding, the common case, not an edge case (issue #771). A self-consistency guard added for an earlier, unrelated issue treated this ordinary shape as corruption and dropped the verdict; the guard is removed, with a construction-time guard and an INFO-level log as the remaining defenses. See docs/adr/0003.
  • A human resolving a bot-owned thread via GitHub’s native “Resolve conversation” button, with no verdict command, reappeared on a later rescan as an apparently-brand-new duplicate comment with no explanation (issue #779). This was the actual root cause of the PR that started the investigation. The bot correctly can’t suppress a resolved-without-verdict finding forever (that would risk silently losing a live finding), but it now explains why the comment came back and names the commands that would suppress it for good, and carefully distinguishes a human’s resolve from the bot’s own routine stale-thread cleanup, which produces the identical shape for an unrelated reason.
  • A malformed /ai-pr-review slash command failed completely silently, with no signal beyond an “eyes” reaction (issue #772). A comment with inverted argument order (F2 dismiss <reason> instead of dismiss F2 <reason>) parsed as an unknown command and produced no reaction or reply, leaving the reporter with no way to tell their command had been ignored. Malformed commands now get a distinct reaction and a reply naming the problem.
  • The /ai-pr-review feedback command’s reply read as confirmation that a finding was handled, when it only writes an advisory note (issue #773). The reply now states plainly that the finding stays open and blocking, and names the commands that would actually clear it.
  • GitHub lacked GitLab’s same-run keep-alive registration for freshly created review threads (issue #774). Confirmed real via code reading, but not the cause of the duplicate-thread incident above: that turned out to be issue #779.
  • A corroborated finding’s source could be mis-attributed to an LLM agent instead of the static analyzer that also flagged it (issue #776). A finding independently confirmed by both an LLM agent and a static analyzer now correctly attributes the analyzer in reply text and the learning-store entry, rather than whichever name happened to sort first alphabetically.
  • A /ai-pr-review wont-fix/false-positive/dismiss slash command could post two bot replies and duplicate a feedback-store entry (issue #769). Two workflow jobs were both firing for the same comment; one is now the sole owner of the reply and the store write.
  • A run with no new findings but a still-open bot-owned thread from an earlier run could post a headline claiming a nonzero finding count with nothing in the body to back it (issue #766), and the same shape recurred for a recurred-with-thread finding via a different code path (issue #767). Both are now counted and, where applicable, rendered.

See CHANGELOG.md for full technical detail on every change above.