Skip to content
Merged
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
84 changes: 78 additions & 6 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ on:
# - "src/**/*.js"
# - "src/**/*.jsx"

# One review per PR at a time: a new push cancels the in-flight review so the
# only verdict that lands is for the latest commit (implementbot keys off the
# verdict's SHA, so stale verdicts must not race the current one).
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
claude-review:
# Optional: Filter by PR author
Expand All @@ -21,8 +28,8 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
pull-requests: write
issues: write
id-token: write

steps:
Expand All @@ -36,9 +43,74 @@ jobs:
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
prompt: |
Review this pull request as an experienced engineer on this
codebase. This repo's CLAUDE.md is your rubric — follow it, and where
a more specific guideline applies, it wins.

Context for posting (do not review these values, just use them):
- Repository: ${{ github.repository }}
- PR number: ${{ github.event.pull_request.number }}
- Head commit SHA: ${{ github.event.pull_request.head.sha }}
- Opened by implementbot: ${{ contains(github.event.pull_request.body, 'implementbot:issue=') }}

Priorities, most important first — this is what this team cares about:
1. Make invalid states unrepresentable. Flag nullable/optional fields
with no real "absent" case, tri-state "omitted vs explicit null"
confusion in partial-update schemas, mode/boolean flags that should
be two functions, and fields only valid together but typed apart.
2. Tests for new or changed behaviour — especially the negative and
edge cases (invalid input, missing record, empty input, the
default/omitted argument). A missing test for new behaviour is a
finding even when the code looks correct.
3. Encapsulation — anything exposed that could be private (_-prefixed).
4. Domain types over bare primitives for ids/paths/urls/tokens/counts,
applied where the value is created, not as casts at call sites.
5. Reuse over duplication — logic a model, helper, or library already owns.
6. Simplicity and placement — speculative abstraction, code in the wrong
module, dead code, and comments that state "what" instead of "why".

Rules of engagement:
- Only comment when you are confident the issue is real and worth the
author's attention. Prefer a few high-value, design-level comments
over many small nits.
- Classify each finding as BLOCKING (a real correctness, spec, or
guideline violation that should not merge as-is) or OPTIONAL (a
nice-to-have or minor improvement). Only blocking findings gate.
- A clean PR is a success. Never invent findings or restate the diff to
look thorough.
- Do not report anything a formatter, linter, or type checker catches;
CI runs those. Do not report pre-existing issues on lines this PR
did not change.

How to post (the PR branch is already checked out here):
- Leave each finding as an inline comment on the specific line with
`mcp__github_inline_comment__create_inline_comment` (pass
`confirmed: true`). Say whether it is BLOCKING or OPTIONAL, then what
is wrong, why it matters (cite CLAUDE.md where relevant), and what
would resolve it.
- Then handle the top-level verdict comment by whether implementbot
opened this PR (see the "Opened by implementbot" value above):

* If TRUE — always post a verdict, even when the PR is clean, because
implementbot waits for it. Build the body with this exact first line:
`<!-- claude-review sha=<HEAD_SHA> verdict=<clean|changes> -->`
using the Head commit SHA above, and `changes` if you left any
BLOCKING finding else `clean`; follow it with one or two sentences
(and "details are inline" when there are findings). Post it as a
single STICKY comment — never a second one: first look for your
previous verdict with
`gh api "repos/<REPO>/issues/<PR>/comments" --jq 'map(select(.body|startswith("<!-- claude-review")))|.[0].id // empty'`
if it returns an id, update that comment in place with
`gh api --method PATCH "repos/<REPO>/issues/comments/<id>" -f body='<body>'`
otherwise create it with `gh pr comment <PR> --body '<body>'`.

* If FALSE (a human PR) — do not post any verdict comment. If there
are no findings at all, post nothing and stay silent.

- Post feedback only through these GitHub tools; do not return review
text as your final message.
claude_args: |
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh api:*)"
# 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

Loading