Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
81 changes: 81 additions & 0 deletions .github/workflows/claude-autofix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Claude Auto-Apply Review Suggestions

on:
pull_request_review:
types: [submitted]

concurrency:
group: claude-autofix-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
Comment on lines +6 to +11
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💭 Consider: Add concurrency group to prevent race conditions

Issue: If Claude submits multiple reviews in quick succession (e.g., due to retries or separate review events), the workflow could run multiple times concurrently on the same branch, potentially causing race conditions with commits.

Why: While unlikely, concurrent autofix runs could conflict when pushing commits.

Fix: Add a concurrency group after the on: block:

concurrency:
  group: claude-autofix-${{ github.event.pull_request.number }}
  cancel-in-progress: true

Refs:

autofix:
# Only run when claude[bot] submits a review on a non-fork PR
if: github.event.review.user.login == 'claude[bot]' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
Comment on lines +12 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Minor: Missing timeout-minutes on job

Issue: The job has no timeout-minutes specified. If Claude hangs or enters an infinite loop, the job will run until GitHub's default 6-hour timeout.

Why: Explicit timeouts prevent runaway execution and wasted compute/API credits.

Fix: Add timeout-minutes: 15 to the job definition (same as auto-format.yml):

Suggested change
autofix:
# Only run when claude[bot] submits a review - skip human reviews
if: github.event.review.user.login == 'claude[bot]'
runs-on: ubuntu-latest
autofix:
# Only run when claude[bot] submits a review - skip human reviews
if: github.event.review.user.login == 'claude[bot]'
runs-on: ubuntu-latest
timeout-minutes: 15

Refs:

timeout-minutes: 15
permissions:
contents: write
pull-requests: read

steps:
- name: Check iteration limit
id: guard
env:
GH_TOKEN: ${{ github.token }}
run: |
MAX=3
COUNT=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits \
--jq '[.[].commit.message | select(test("chore: apply Claude review suggestions \\[\\d+/'"$MAX"'\\]"))] | length')
Comment on lines +28 to +29
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Minor: Add error handling for gh api failure

Issue: If gh api fails (network error, rate limit, transient GitHub issue), COUNT will be empty or contain an error message. The subsequent [ "$COUNT" -ge "$MAX" ] comparison will fail with a shell error like [: -ge: unary operator expected.

Why: While the failure is visible (workflow errors out), adding a default makes the behavior more predictable and matches patterns used elsewhere in this repo (e.g., ci-maintenance.yml:145-147).

Fix: Add fallback handling:

COUNT=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits \
  --jq '[.[].commit.message | select(test("chore: apply Claude review suggestions \\[\\d+/'"$MAX"'\\]"))] | length' 2>/dev/null || echo "0")
COUNT=${COUNT:-0}

Refs:

echo "count=$COUNT" >> $GITHUB_OUTPUT
if [ "$COUNT" -ge "$MAX" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "skip=false" >> $GITHUB_OUTPUT
echo "iteration=$((COUNT + 1))" >> $GITHUB_OUTPUT
fi

- name: Generate app token
if: steps.guard.outputs.skip != 'true'
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.INTERNAL_CI_APP_ID }}
private-key: ${{ secrets.INTERNAL_CI_APP_PRIVATE_KEY }}

- name: Checkout PR branch
if: steps.guard.outputs.skip != 'true'
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 1
token: ${{ steps.app-token.outputs.token }}

- name: Apply review suggestions
if: steps.guard.outputs.skip != 'true'
# Pinned to SDK 0.2.25 (last working version) due to AJV validation crash in 0.2.27+
# Tracking: https://github.com/anthropics/claude-code-action/issues/892
# Remove pin when issue is resolved and @v1 is stable again
uses: anthropics/claude-code-action@01e756b34ef7a1447e9508f674143b07d20c2631
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ steps.app-token.outputs.token }}
allowed_bots: "claude"
claude_args: '--max-turns 20 --allowedTools "Read,Edit,Write,Bash(gh api:*),Bash(gh pr view:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(git diff:*)"'
prompt: |
Apply the code suggestions from Claude's PR review on PR #${{ github.event.pull_request.number }} in repo ${{ github.repository }}.

Steps:
1. Fetch Claude's inline review comments:
gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments \
--jq '[.[] | select(.user.login == "claude[bot]")]'
2. For each comment, check if it contains a concrete code suggestion (e.g. a code block showing a proposed change, a specific line replacement, etc.)
3. Apply each concrete suggestion to the corresponding file and location
4. Skip any comment that is purely informational, raises an architectural concern, asks a question, or requires a product decision
5. If any changes were made:
git add -A
git commit -m "chore: apply Claude review suggestions [${{ steps.guard.outputs.iteration }}/3]"
git push
6. If no actionable code suggestions exist, do nothing - no commit needed

IMPORTANT: Only apply clear, unambiguous code-level changes. Do not implement new features, refactor beyond what is explicitly suggested, or make any judgment calls.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,6 @@ progress.txt

.pnpm-store/
.vercel

# Git worktrees (created by Claude Code / git worktree add)
.claude/worktrees/