feat(ci): add PR review agent via Claude Code Action - #402
Conversation
Claude Code reviews every pull request and posts severity-tagged findings as inline and summary comments. It never approves, requests changes, or merges; humans decide. Skips Dependabot, drafts and forks. Auth is the maintainer subscription through the CLAUDE_CODE_OAUTH_TOKEN repo secret. ADR-0008 records the decision. Closes #351
| on style that a linter already enforces. | ||
| claude_args: | | ||
| --max-turns 30 | ||
| --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" |
There was a problem hiding this comment.
[major] --allowedTools only grants mcp__github_inline_comment__create_inline_comment and three gh pr Bash subcommands. It does not grant Read, Glob, or Grep. The prompt (above) explicitly instructs the agent to "Read CLAUDE.md and agents.md first" and to check cross-file contracts, existing test coverage, and repo rules (migrations, Cypress specs) that live outside the diff. With no file-read tool allowed, the agent can only see whatever gh pr diff/gh pr view return, not the checked-out repo's CLAUDE.md/agents.md or any file not touched by the PR. This will silently degrade every review to "diff-only," missing the rules-check that's the stated purpose of this workflow.
Suggest adding Read, Glob, and Grep (or View) to the allowlist so the agent can actually open CLAUDE.md/agents.md and adjacent files.
There was a problem hiding this comment.
Fixed in 822b54d: Read, Glob and Grep added to --allowedTools.
| fetch-depth: 1 | ||
|
|
||
| - name: Review with Claude Code | ||
| uses: anthropics/claude-code-action@v1 |
There was a problem hiding this comment.
[nit] Third-party action pinned to the floating tag @v1 rather than a full commit SHA. v1 can move and change behavior/permissions under this workflow without review. Already called out as a "Watch" item in ADR-0008, so just a flag for future hardening (pin to SHA + Dependabot/renovate for updates), not blocking.
Review summary[major]
[nit]
Everything else checks out: conventional PR title and commit message, no em/en dashes, ADR-0008 present for the tooling decision and cross-linked in the ADR index, fork/Dependabot/draft PRs correctly excluded from secret-bearing runs, no schema changes (no migrations needed), no React/API contract changes (no Cypress updates needed), no secrets committed. |
Runs automatically only for PRs authored by the repository owner, whose subscription pays for it. Other authors opt in when the owner adds the claude-review label. Grants Read, Glob and Grep so the agent can read CLAUDE.md and agents.md as instructed (finding from the first review run).
Review finding: a floating v1 tag can change behaviour and permissions without review. Pinned to the current v1 commit; manual bumps until Dependabot covers github-actions (#310).
| contents: read | ||
| pull-requests: write | ||
| issues: write | ||
| id-token: write |
There was a problem hiding this comment.
[minor] id-token: write is granted but nothing in this job uses OIDC: auth is via claude_code_oauth_token (a static secret), not a cloud-provider OIDC exchange. Least-privilege suggests dropping it unless claude-code-action specifically needs it for this auth path; if it does, a one-line comment saying why would save the next reader from wondering.
There was a problem hiding this comment.
Fixed: id-token write removed; github_token is passed explicitly and auth is the OAuth token, so no OIDC exchange is needed.
| on style that a linter already enforces. | ||
| claude_args: | | ||
| --max-turns 30 | ||
| --allowedTools "Read,Glob,Grep,mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" |
There was a problem hiding this comment.
[nit] No timeout-minutes on this job. --max-turns 30 bounds Claude's own turn count but not wall-clock time (a slow tool call could still stall); a small job-level timeout would cap subscription usage on a hang instead of relying on the next push's concurrency cancellation.
There was a problem hiding this comment.
Fixed: timeout-minutes 30 on the review job (40 on the wait job).
A wait-for-checks job polls the PR head check runs and the review job runs only when every other check completed with success, neutral or skipped, so no subscription usage goes to PRs that still fail CI.
Review summary[minor] [minor] PR title "feat(ci): PR review agent via Claude Code Action" is a noun phrase, not imperative mood, and isn't lowercase ("PR", "Claude Code Action") per CLAUDE.md's Conventional Commits rule ("Subject: imperative mood, lowercase..."). Since squash merges use the PR title verbatim as the commit subject, worth retitling, e.g. [nit] Everything else checks out: fork PRs and non-owner/non-labeled PRs are correctly excluded from getting the secret, the action is pinned to a commit SHA, permissions are otherwise minimal, no em/en dashes were introduced, ADR-0008 is present and documents the tradeoffs, and no AI attribution appears in the commits or PR body. |
Review findings: id-token write is unused with OAuth token auth and an explicit github_token; max-turns bounds turns, not time, so add a 30 minute job timeout.
|
Retitled to imperative lowercase per the review: feat(ci): add PR review agent via Claude Code Action. |
Read earlier review summaries and never repeat findings, inline comments only for blocker and major, minors and nits capped in the summary, and an explicit 'Ready for human review' stop signal.
Review summaryThird pass. Prior [major]/[minor]/[nit] findings (missing Read/Glob/Grep tools, floating action pin, stray [minor]
[nit]
No blockers or majors. No em/en dashes introduced, PR title and branch name ( Ready for human review. |
Summary
claude-pr-review.yml:anthropics/claude-code-action@v1runs onpull_request(opened, synchronize, reopened, ready_for_review), skips Dependabot, drafts and forks, cancels superseded runs per PR.[blocker],[major],[minor]or[nit]. Never approves, requests changes, pushes or merges. Tools limited to inline comments andgh pr comment/diff/view,--max-turns 30.CLAUDE_CODE_OAUTH_TOKENrepo secret (maintainer subscription viaclaude setup-token); comments are posted withGITHUB_TOKEN, no GitHub App needed.Closes #351 (parent #356)
Test plan
Claude PR Reviewworkflow and gets a summary comment (and inline comments if it finds anything)