Skip to content

Commit 546d2bf

Browse files
ci(claude-review): use built-in /code-review and assert it ran (#19)
Replace the free-form prompt that was posting "No issues found" after a few seconds with the built-in high-effort review, full history, and a check that the run actually did work. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 2c8d361 commit 546d2bf

1 file changed

Lines changed: 71 additions & 26 deletions

File tree

‎.github/workflows/claude-code-review.yml‎

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,101 @@ name: Claude Code Review
22

33
on:
44
pull_request:
5-
types: [opened, synchronize]
5+
types: [opened, synchronize, ready_for_review, reopened]
66
# Optional: Only run on specific file changes
77
# paths:
88
# - "src/**/*.ts"
99
# - "src/**/*.tsx"
1010
# - "src/**/*.js"
1111
# - "src/**/*.jsx"
1212

13+
# A push while a review is still running replaces it, rather than stacking a
14+
# second review of a diff that is already stale.
15+
concurrency:
16+
group: claude-review-${{ github.event.pull_request.number }}
17+
cancel-in-progress: true
18+
1319
jobs:
1420
claude-review:
15-
# Optional: Filter by PR author
16-
# if: |
17-
# github.event.pull_request.user.login == 'external-contributor' ||
18-
# github.event.pull_request.user.login == 'new-developer' ||
19-
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
21+
# Drafts are reviewed once they are marked ready. Bot PRs (release-please)
22+
# need no review and only queue up as action_required.
23+
if: github.event.pull_request.draft == false && github.event.pull_request.user.type != 'Bot'
2024

2125
runs-on: ubuntu-latest
2226
permissions:
2327
contents: read
24-
pull-requests: read
25-
issues: read
28+
pull-requests: write
29+
issues: write
2630
id-token: write
31+
checks: read
32+
statuses: read
33+
actions: read # Required for Claude to read CI results on PRs
2734

2835
steps:
2936
- name: Checkout repository
30-
uses: actions/checkout@v4
37+
uses: actions/checkout@v7
3138
with:
32-
fetch-depth: 1
39+
# Full history, so the review can diff, log and blame against the base
40+
# branch instead of piecing the PR together from `gh pr diff`.
41+
fetch-depth: 0
42+
43+
# - name: Trust workspace directory
44+
# run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
3345

3446
- name: Run Claude Code Review
3547
id: claude-review
3648
uses: anthropics/claude-code-action@v1
3749
with:
3850
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39-
prompt: |
40-
REPO: ${{ github.repository }}
41-
PR NUMBER: ${{ github.event.pull_request.number }}
42-
43-
Please review this pull request and provide feedback on:
44-
- Code quality and best practices
45-
- Potential bugs or issues
46-
- Performance considerations
47-
- Security concerns
48-
- Test coverage
49-
50-
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
51-
52-
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
53-
51+
# Print Claude's messages to the job log. Without it a run that stops
52+
# early or skims gives no reason.
53+
show_full_output: true
54+
# anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
55+
# The built-in /code-review, not the code-review@claude-code-plugins
56+
# command. The plugin's procedure (eligibility check, four parallel
57+
# reviewers, validators) was routinely skipped by its Sonnet main loop:
58+
# it would skip the subagents the review steps describe, read a few
59+
# line ranges of a diff too big for one tool result, and post
60+
# "No issues found" after a few seconds. Since Claude Code 2.1.x the
61+
# built-in takes a PR number, an effort level and --comment, and
62+
# verifies its findings before posting them.
63+
prompt: '/code-review high ${{ github.event.pull_request.number }} --comment'
5464
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
5565
# or https://code.claude.com/docs/en/cli-reference for available options
56-
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
66+
additional_permissions: |
67+
actions: read
68+
claude_args: |
69+
--allowedTools "Bash(gh:*),Bash(git:*),mcp__github_inline_comment__create_inline_comment"
5770
71+
# The action reports success whenever the SDK returns is_error: false, even
72+
# if Claude never did anything (unresolved slash command, missing plugin,
73+
# empty prompt). That failure mode hid behind months of green runs, so
74+
# assert that work actually happened.
75+
#
76+
# Check cost as well as turns: a real review delegates to subagents and can
77+
# legitimately report num_turns 0 while spending several dollars. Only a run
78+
# that is zero on BOTH counts truly did nothing.
79+
#
80+
# A run that did work but never delegated is the quieter failure: one
81+
# agent skimming the diff and calling it clean. That only warns, because a
82+
# tiny diff may not need subagents.
83+
- name: Assert the review actually ran
84+
if: always() && steps.claude-review.conclusion == 'success'
85+
run: |
86+
file="${{ steps.claude-review.outputs.execution_file }}"
87+
if [ -z "$file" ] || [ ! -f "$file" ]; then
88+
echo "::warning::No execution file — the action exited before starting Claude. Expected on PRs that modify this workflow (the action requires it to match the default branch); investigate otherwise."
89+
exit 0
90+
fi
91+
result=$(jq -r '[.. | objects | select(.type == "result")] | last' "$file")
92+
turns=$(jq -r '.num_turns // 0' <<<"$result")
93+
cost=$(jq -r '.total_cost_usd // 0' <<<"$result")
94+
delegations=$(jq -r '[.. | objects | select(.type == "tool_use" and (.name == "Agent" or .name == "Task" or .name == "Workflow"))] | length' "$file")
95+
echo "Claude turns: $turns, cost: \$$cost, subagent launches: $delegations"
96+
if [ "$turns" -eq 0 ] && [ "$(jq -rn --argjson c "$cost" '$c > 0')" = "false" ]; then
97+
echo "::error::Claude did no work — 0 turns and \$0 spent. The review was a no-op (bad prompt or unresolved slash command)."
98+
exit 1
99+
fi
100+
if [ "$delegations" -eq 0 ]; then
101+
echo "::warning::The review launched no subagents. On anything but a tiny diff that means one agent skimmed it, so treat a clean result with suspicion."
102+
fi

0 commit comments

Comments
 (0)