Skip to content

Stop PR comments from cancelling the Claude review - #210

Merged
ljwharbers merged 1 commit into
mainfrom
patch
Oct 1, 2026
Merged

ljwharbers merged 1 commit into
mainfrom
patch

Conversation

@ljwharbers

Copy link
Copy Markdown
Collaborator

Why

Every Claude review run ends as cancelled (red ✗), even when it posted its full review: #207 (run 36825421844, both attempts) and #202 (run 36720540789).

Annotation: "Canceling since a higher priority waiting request for claude-review-207 exists".

concurrency is set at workflow level, and GitHub evaluates it before the job if:. So every comment on a PR starts a run in claude-review-<N> and cancels the active review. That run's own job is then skipped. The last thing a review does is post its summary with gh pr comment, so each review cancels itself 2–4 s later:

#207 attempt summary comment skipped issue_comment run review job ended
1 06:36:43 06:36:45 06:36:48 cancelled
2 06:49:50 06:49:53 06:49:54 cancelled

A human comment during the 3–4 min review would also kill it mid-run.

Change

Comments that don't start with @claude review go into a per-run group (claude-review-<N>-<run_id>), so they can't cancel anything. PR open/reopen/ready events and @claude review keep the shared per-PR group, so a new review request still replaces a running one.

Rollout

The action only runs when the PR's claude-review.yml is byte-identical to main's. This change goes in twice, with the identical file: patch → main and ci/claude-review-concurrency-dev → dev. Neither PR will get a real review run before the main one is merged.

Checks

  • prek (prettier, whitespace) passes; YAML parses.
  • After merge: @claude review on a PR should end success. The summary comment's run should be skipped in its own group, and a plain comment mid-review should leave the review running.

🤖 Generated with Claude Code

Workflow-level concurrency is evaluated before the job's if, so every
comment on a PR joined claude-review-<N> and cancelled the running
review. Claude's own summary comment did this at the end of every
review, so each run on #202 and #207 ended as cancelled (red) even
though its comments were posted. A human comment mid-review would kill
it outright. Comments that are not '@claude review' now get a
per-run group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ljwharbers
ljwharbers marked this pull request as ready for review October 1, 2026 08:30
@ljwharbers
ljwharbers merged commit 36fdd6e into main Oct 1, 2026
9 of 11 checks passed
@ljwharbers
ljwharbers deleted the patch branch October 1, 2026 08:30
# `@claude review` get a group of their own, so they cannot cancel a review.
concurrency:
group: claude-review-${{ github.event.pull_request.number || github.event.issue.number }}
group: claude-review-${{ github.event.pull_request.number || github.event.issue.number }}${{ github.event_name == 'issue_comment' && !startsWith(github.event.comment.body, '@claude review') && format('-{0}', github.run_id) || '' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concurrency key only looks at the comment text. The job if (lines 32–34) also requires github.event.issue.pull_request and an OWNER/MEMBER/COLLABORATOR author_association. Because concurrency is applied before the job if, an @claude review comment from anyone without those roles still joins the shared claude-review-<N> group. That includes a fork PR author or any user on this public repo. With cancel-in-progress: true, the comment cancels a running review, and its own run is then skipped. So the self-cancel bug is fixed, but anyone can still cancel a review just by commenting. Use the same author_association check in the concurrency condition, so only comments that would actually start a review share the group. For example:

  group: claude-review-${{ github.event.pull_request.number || github.event.issue.number }}${{ github.event_name == 'issue_comment' && !(startsWith(github.event.comment.body, '@claude review') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) && format('-{0}', github.run_id) || '' }}

A smaller version of the same problem: pull_request reopened/ready_for_review events on fork PRs still use the shared group, so they can cancel a maintainer-requested review even though their own job is skipped by the head.repo.full_name check.

@claude

claude Bot commented Oct 1, 2026

Copy link
Copy Markdown

Claude review: 1 finding. The concurrency key checks only the comment text, not the job's author_association/PR checks. So an @claude review comment from a non-maintainer (or a fork PR reopen/ready event) can still cancel a running review (inline comment on .github/workflows/claude-review.yml:22). This is a CI-only change with no nf-test impact. After merge, someone needs to check that a review run ends success and that a plain comment posted mid-review leaves it running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant