Skip to content

feat(ci): add PR review agent via Claude Code Action - #402

Merged
kauereinbold merged 6 commits into
mainfrom
feat/pr-review-agent
Aug 22, 2026
Merged

feat(ci): add PR review agent via Claude Code Action#402
kauereinbold merged 6 commits into
mainfrom
feat/pr-review-agent

Conversation

@kauereinbold

Copy link
Copy Markdown
Owner

Summary

  • New workflow claude-pr-review.yml: anthropics/claude-code-action@v1 runs on pull_request (opened, synchronize, reopened, ready_for_review), skips Dependabot, drafts and forks, cancels superseded runs per PR.
  • Prompt reviews against CLAUDE.md and agents.md (correctness, security, repo rules, tests) and posts findings as inline comments plus one summary comment, each tagged [blocker], [major], [minor] or [nit]. Never approves, requests changes, pushes or merges. Tools limited to inline comments and gh pr comment/diff/view, --max-turns 30.
  • Auth: CLAUDE_CODE_OAUTH_TOKEN repo secret (maintainer subscription via claude setup-token); comments are posted with GITHUB_TOKEN, no GitHub App needed.
  • ADR-0008 documents the decision and alternatives.

Closes #351 (parent #356)

Test plan

  • actionlint clean locally
  • This PR triggers the Claude PR Review workflow and gets a summary comment (and inline comments if it finds anything)
  • A follow-up push cancels the previous run and reviews again
  • No approve/request-changes review is submitted, only comments

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
Comment thread .github/workflows/claude-pr-review.yml Outdated
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:*)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 822b54d: Read, Glob and Grep added to --allowedTools.

Comment thread .github/workflows/claude-pr-review.yml Outdated
fetch-depth: 1

- name: Review with Claude Code
uses: anthropics/claude-code-action@v1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 530a8f3: pinned to commit dcb5774 (v1); manual bumps until Dependabot covers github-actions (#310).

@github-actions

Copy link
Copy Markdown

Review summary

[major]

  • .github/workflows/claude-pr-review.yml:73: --allowedTools grants only the inline-comment MCP tool plus gh pr comment/diff/view. No Read/Glob/Grep is allowed, so the agent cannot actually open CLAUDE.md or agents.md as the prompt instructs, or inspect any file the diff doesn't touch. This undercuts the workflow's stated purpose (rules/cross-file review), reducing it to diff-only review.

[nit]

  • .github/workflows/claude-pr-review.yml:38: anthropics/claude-code-action@v1 is pinned to a floating tag, not a commit SHA. Already noted as a "Watch" item in ADR-0008, so just flagging for future hardening.

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).
Comment thread .github/workflows/claude-pr-review.yml Outdated
contents: read
pull-requests: write
issues: write
id-token: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

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:*)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

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.
@github-actions

Copy link
Copy Markdown

Review summary

[minor] .github/workflows/claude-pr-review.yml:34 - id-token: write is granted at job level but auth uses claude_code_oauth_token (a static secret), not OIDC. Suggest dropping it unless the action genuinely needs it for this auth path.

[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. feat(ci): add PR review agent via Claude Code Action.

[nit] .github/workflows/claude-pr-review.yml:77 - no timeout-minutes on the job; --max-turns 30 bounds turns, not wall-clock time.

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.
@kauereinbold kauereinbold changed the title feat(ci): PR review agent via Claude Code Action feat(ci): add PR review agent via Claude Code Action Aug 22, 2026
@kauereinbold

Copy link
Copy Markdown
Owner Author

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.
@github-actions

Copy link
Copy Markdown

Review summary

Third pass. Prior [major]/[minor]/[nit] findings (missing Read/Glob/Grep tools, floating action pin, stray id-token: write, non-imperative title) are all addressed: allowedTools now includes Read,Glob,Grep, claude-code-action is pinned to a commit SHA, id-token is gone from both jobs, and the title was retitled to feat(ci): add PR review agent via Claude Code Action.

[minor]

  • .github/workflows/claude-pr-review.yml:31 - wait-for-checks has timeout-minutes: 40. If the PR's other CI (e.g. e2e/docker builds) legitimately runs longer, this job times out as a non-success, and review (which needs: wait-for-checks) is silently skipped with no comment explaining why no review appeared. Consider a longer timeout or a fallback comment on timeout.
  • .github/workflows/claude-pr-review.yml:45-53 - the poll loop does one sleep 30 before its first check-run read. If a third-party/slow-to-register check hasn't posted any check run yet at that point, the grep -Eq '^(queued|in_progress...)' filter finds nothing pending and the loop exits immediately, so all-green=true could fire before that check has even started. Low likelihood for native Actions jobs (they register as queued almost immediately), but worth a comment noting the assumption if intentional.

[nit]

  • docs/adr/0008-pr-review-agent.md references issues #351/#356; not verified against the tracker as part of this pass, worth a manual check that they're the intended numbers.

No blockers or majors. No em/en dashes introduced, PR title and branch name (feat/pr-review-agent) follow convention, ADR-0008 present and matches the template, CLAUDE.md label table update is consistent with the workflow's opt-in behavior, no secrets committed, no schema/React/API contract changes so migrations and Cypress specs don't apply, no AI attribution in the commit or PR body.

Ready for human review.

@kauereinbold
kauereinbold merged commit 2d56a58 into main Aug 22, 2026
8 checks passed
@kauereinbold
kauereinbold deleted the feat/pr-review-agent branch August 22, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ci): PR review agent via Claude Code Action

1 participant