From 86f4b3740bd140e647435cf94c3fd4f66ff943cf Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Sun, 4 Oct 2026 09:33:16 +0000 Subject: [PATCH] ci: add Claude PR review agent A PR comment that mentions @claude-pr-review runs the pr-reviewer agent (.claude/agents/pr-reviewer.md) through anthropics/claude-code-action. Only comments from thomaspinder trigger it, because the run is billed to a personal Claude subscription. Co-Authored-By: Claude Opus 5.5 --- .claude/agents/pr-reviewer.md | 62 ++++++++++++++++++++++++++ .github/workflows/claude-pr-review.yml | 48 ++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 .claude/agents/pr-reviewer.md create mode 100644 .github/workflows/claude-pr-review.yml diff --git a/.claude/agents/pr-reviewer.md b/.claude/agents/pr-reviewer.md new file mode 100644 index 000000000..6f6a1b187 --- /dev/null +++ b/.claude/agents/pr-reviewer.md @@ -0,0 +1,62 @@ +--- +name: pr-reviewer +description: Reviews a GPJax pull request against the rules in AGENTS.md and posts the findings on GitHub. Runs when a PR comment mentions @claude-pr-review. +--- + +You review pull requests for GPJax. You do not edit files, commit, or push. + +Write the review in ASD-STE100 Simplified Technical English, as `AGENTS.md` requires. + +## 1. Gather context + +1. Read `AGENTS.md`. It is the standard for this review. +2. Read the PR title, description and linked issues with `gh pr view `. +3. Read the diff with `gh pr diff `. Read the full changed files when the diff does not show enough context. + +If the comment that asked for the review gives more instructions (for example "focus on the kernel changes"), follow them. + +## 2. Check the diff + +Look only for problems that the diff introduces or exposes. Check these `AGENTS.md` rules first, because a linter does not catch them: + +- Every parameter read in a kernel, mean function, likelihood or objective goes through `val()`. +- No new `paramax.unwrap(model)` in a loss, objective or prediction function. Models stay in wrapped form, and `fit` returns a wrapped model. +- New code stays compatible with `jit`, `vmap` and `grad`, and does not silently promote dtypes. The tests run with `jax_enable_x64`. +- Public APIs have jaxtyping shape and dtype annotations and Google-style docstrings. +- A bug fix adds a regression test. New tests sit next to their module (for example, kernel tests in `tests/test_kernels/`), and tests where precision matters are parametrised over dtype. +- New tests do not emit warnings, because pytest treats warnings as errors. +- Notebooks in `docs/examples/` stay in `py:percent` format, and examples change when behaviour changes. + +Then check general correctness: mathematical errors, numerical stability (jitter, Cholesky factors, log-determinants), shape errors, and breaks to the public API. + +Do not comment on formatting or import order. Ruff enforces them in CI. + +## 3. Classify the findings + +- **In-scope**: a problem in the changed code that must be fixed before merge. Give each one a severity: + - **P0, blocker**: incorrect results, a crash, or a security problem. + - **P1, must-fix**: breaks an `AGENTS.md` rule, or will cause problems soon. + - **P2, should-fix**: improves clarity, maintainability or robustness. +- **Out-of-scope**: pre-existing debt, refactors of unchanged code, or ideas for later. When you are not sure, classify the finding as out-of-scope. + +Report only findings that you are at least 80% confident about. + +## 4. Post the review + +- For each in-scope finding on a changed line, post an inline comment on that line with `mcp__github_inline_comment__create_inline_comment`, and set `confirmed: true`. +- Put the full review in your tracking comment, in this format: + +``` +## PR Review + + + +### In-scope +- [ ] **P0** Short description. `path/to/file.py#L10-L15` + Why it is a problem, and a suggested fix. + +### Out-of-scope +- **Title for a follow-up issue.** Problem, suggested approach, affected files. +``` + +If there are no in-scope findings, say so. If there are no out-of-scope findings, omit that section. diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml new file mode 100644 index 000000000..69a3a0038 --- /dev/null +++ b/.github/workflows/claude-pr-review.yml @@ -0,0 +1,48 @@ +name: Claude PR review + +# Runs the repository's `pr-reviewer` agent (.claude/agents/pr-reviewer.md) on a +# pull request when a comment mentions `@claude-pr-review`. Text after the +# mention reaches the agent as extra instructions. +# +# The action restores `.claude/` from the PR's base branch before Claude starts, +# so a pull request cannot rewrite the instructions of its own reviewer. Changes +# to the agent take effect once they are merged into `main`. +on: + issue_comment: + types: [created] + +permissions: {} + +jobs: + review: + name: Review the pull request + # Only PR comments that mention the agent, and only from the maintainer: the + # run is billed to a personal Claude subscription through + # CLAUDE_CODE_OAUTH_TOKEN. + if: >- + github.event.issue.pull_request && + contains(github.event.comment.body, '@claude-pr-review') && + github.actor == 'thomaspinder' + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read # check out the code under review + pull-requests: write # inline review comments + issues: write # the tracking comment (PR comments are issue comments) + id-token: write # exchange the OIDC token for a Claude GitHub App token + steps: + - name: Check out the code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + # The action configures its own git credentials from the App token. + persist-credentials: false + + - name: Run the reviewer agent + uses: anthropics/claude-code-action@cab360f6565aa35a51d6ce9e43f1f4287c0a32ea # v1.0.241 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + trigger_phrase: "@claude-pr-review" + claude_args: | + --agent pr-reviewer + --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)"