Skip to content

ci(claude-review): stop silent no-comment review runs #344

ci(claude-review): stop silent no-comment review runs

ci(claude-review): stop silent no-comment review runs #344

name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs:
claude-review:
# Skip PRs we shouldn't (or can't) review:
# - Dependabot / forks: no CLAUDE_CODE_OAUTH_TOKEN, so the action would fail.
# - release-please release PRs: just version bumps + changelog — nothing to
# review, and it must never block a release.
# - Back-merge PRs (production -> dev, opened by release-please.yml): only
# carry code already reviewed on its way through dev, and the action
# rejects their bot actor ("Workflow initiated by non-human actor"),
# which blocks the merge since claude-review is a required check.
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && !startsWith(github.head_ref, 'release-please--') && !startsWith(github.head_ref, 'chore/back-merge-') }}
runs-on: ubuntu-latest
permissions:
contents: read
# write so the review agent can post its inline comments — with read it
# reviews successfully but every posting attempt is denied and the PR
# gets nothing (the action still exits 0).
pull-requests: write
issues: read
id-token: write
steps:
- name: Record start time
run: echo "REVIEW_STARTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
# --comment is required for the review to be posted to the PR: the
# code-review plugin's spec says "If --comment was NOT provided,
# stop here. Do not post any GitHub comments." Without it the review
# runs and is discarded (the log doesn't even show it unless
# show_full_output is enabled).
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }} --comment'
# Explicitly allow the posting tools (mirrors the plugin command's
# allowed-tools frontmatter). In headless mode the frontmatter grant
# doesn't reach the inline-comment MCP tool: PR #9's review hit 46
# permission denials and posted nothing despite finding issues.
claude_args: |
--model claude-opus-5-5 --allowedTools "mcp__github_inline_comment__create_inline_comment,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:*)" --append-system-prompt "You are running non-interactively. Never run subagents in the background and never schedule wakeups: ending your turn ends the session. Wait for every reviewer to finish, then post the review comment before you stop."
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
# The action exits green even when Claude ends its turn before posting
# anything (e.g. it backgrounded its reviewer subagents and stopped to
# "wait" for them). Fail loudly instead of leaving a silent green check.
- name: Check a review comment was posted
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
count=$(
{
gh api "repos/$REPO/issues/$PR/comments" --paginate \
--jq ".[] | select(.user.login == \"claude[bot]\" and .created_at >= \"$REVIEW_STARTED_AT\") | .id"
gh api "repos/$REPO/pulls/$PR/comments" --paginate \
--jq ".[] | select(.user.login == \"claude[bot]\" and .created_at >= \"$REVIEW_STARTED_AT\") | .id"
} | wc -l
)
if [ "$count" -eq 0 ]; then
echo "::error::Claude finished without posting a review comment on this PR."
exit 1
fi
echo "Claude posted $count comment(s)."