Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 96 additions & 4 deletions .github/workflows/claude-code-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ on:
required: false

jobs:
# Interactive mode: PR reviews and comments from trusted contributors
# Interactive mode: PR reviews and @claude mentions from trusted human contributors.
# Bot review comments (CodeRabbit, Copilot, Gemini) are handled by claude-fix-review-comments below.
claude:
if: >-
(github.event_name == 'pull_request' &&
Expand All @@ -25,9 +26,7 @@ jobs:
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' &&
github.event.comment.user.login != 'claude[bot]' &&
(contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) ||
(github.event.pull_request.head.repo.full_name == github.repository &&
contains(fromJson('["coderabbitai[bot]","Copilot","copilot-pull-request-reviewer[bot]","gemini-code-assist[bot]"]'), github.event.comment.user.login))))
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
Expand All @@ -51,6 +50,99 @@ jobs:
actions: read
checks: read

# Automation mode: bot review-comment responder — address all open threads, fix CI, repeat
claude-fix-review-comments:
if: >-
github.event_name == 'pull_request_review_comment' &&
github.event.comment.user.login != 'claude[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository &&
contains(fromJson('["coderabbitai[bot]","Copilot","copilot-pull-request-reviewer[bot]","gemini-code-assist[bot]"]'), github.event.comment.user.login)
concurrency:
group: claude-review-comments-${{ github.event.pull_request.number }}
cancel-in-progress: true
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
id-token: write
pull-requests: write
issues: write
actions: read
checks: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
token: ${{ secrets.GH_PAT_WORKFLOWS || github.token }}
- name: Run Claude Code
uses: anthropics/claude-code-action@476e359e6203e73dad705c8b322e333fabbd7416 # v1.0.119
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GH_PAT_WORKFLOWS || github.token }}
Comment thread
don-petry marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
don-petry marked this conversation as resolved.
# yamllint disable rule:line-length
prompt: |
A reviewer has left a comment on PR #${{ github.event.pull_request.number }} (${{ github.event.pull_request.html_url }}).

Your job: work through ALL open (unresolved) review threads on this PR and bring it to a passing, fully-reviewed state. Repeat the cycle below until CI is green and every addressable thread is resolved.

## Cycle

### 1. Check out the PR branch and rebase onto latest main
```
gh pr checkout ${{ github.event.pull_request.number }}
git fetch origin ${{ github.event.pull_request.base.ref }}
git rebase origin/${{ github.event.pull_request.base.ref }}
git push --force-with-lease
```
If the rebase has conflicts, resolve them, then `git rebase --continue` before pushing.

### 2. Fetch all open review threads (collect node IDs — you need them to resolve threads later)
```
gh api graphql -f query='query { repository(owner:"${{ github.repository_owner }}", name:"${{ github.event.repository.name }}") { pullRequest(number:${{ github.event.pull_request.number }}) { reviewThreads(first:100) { nodes { id isResolved comments(first:10) { nodes { path line body author { login } } } } } } } }'
Comment thread
don-petry marked this conversation as resolved.
Outdated
```
Comment thread
don-petry marked this conversation as resolved.

### 3. Address each unresolved thread
For each thread where `isResolved` is false:
- Read the comment body and understand the concern.
- Apply the appropriate fix to the file. If the reviewer included a `suggestion` block, apply it unless you have a clear reason not to.
- If a comment needs a human decision (architectural choice, ambiguous requirement), reply to the thread explaining what decision is needed and skip resolving it — leave it unresolved for the human.

### 4. Commit and push all fixes in one commit
```
git add -A
git commit -m "fix: address review comments"
Comment thread
don-petry marked this conversation as resolved.
Outdated
git push
Comment thread
don-petry marked this conversation as resolved.
Outdated
```

### 5. Resolve each thread you addressed via GraphQL (use the node IDs from step 2)
```
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_NODE_ID"}) { thread { isResolved } } }'
```
Replace THREAD_NODE_ID with the actual `id` value for each thread.

### 6. Wait for CI and fix any failures
```
gh pr checks ${{ github.event.pull_request.number }} --watch --interval 30
```
If any check fails:
- Read the logs: `gh run view <run-id> --log-failed`
- Fix the issue, commit, push, and loop back to step 6.
- Do NOT give up after a single CI failure — keep fixing until all checks pass.

### 7. Check for newly opened threads
After pushing, re-run step 2 to check for any new review threads created in response to your changes. Address them if present.

### 8. Post a summary comment on the PR
When CI is green and all addressable threads are resolved, post a comment summarising:
- What changes were made and why
- Which review threads were resolved
- Any threads left unresolved and why they need human input
# yamllint enable rule:line-length
additional_permissions: |
actions: read
checks: read

# Automation mode: CI failure response — diagnose and fix failing checks on PRs
claude-ci-fix:
if: >-
Expand Down