claude-code-review.yml

Run a read-only Claude review of a pull request (the code-review plugin). By default, callers run it only on dispatched runs (for @claude review via claude.yml), which posts a consolidated summary. If the caller also enables a pull_request trigger, it can run automatically on PR updates. A direct /review path is available too, but only if the caller also enables issue_comment. claude.yml dispatches it via workflow_dispatch; keep the stub named claude-code-review.yml (or set claude.yml’s review-workflow-file to match) so the dispatch resolves.

If you have disabled claude.yml, enable issue_comment in the stub and set the repository variable CLAUDE_AGENT_DISABLED to true, so its dispatch-on-comment job also accepts an @claude review mention. Both are required: that job only ever runs on an issue_comment event, so the variable on its own changes nothing. Without them a repo with the agent off has no mention path, and the phrasing people reach for silently does nothing. Leave the variable unset while the agent is live, or the mention is answered twice.

Inputs

Input Type Default Description
pr-number string '' PR number to review (set by claude.yml’s workflow_dispatch; empty on pull_request runs).
prompt-addendum string '' Repo-specific review guidance appended to the plugin prompt.
checkout-submodules boolean false Check out submodules so the reviewer can read their contents. Private submodules require the SUBMODULES_TOKEN secret.
allowed-bots string 'github-actions[bot]' Comma-separated bot actors the reviewer accepts, so a dispatched review is not rejected as non-human.
track-progress boolean false Enable tag mode: live tracking comment + inline-comment tool on pull_request events. Default false (agent/summary-only mode) is safe. Only set true after anthropics/claude-code-action#1415 ships a read_only input - until then, tag mode exposes git write tools and the reviewer may push commits to the branch.
show-full-output boolean false Show the full JSON output from Claude Code. Turn it on to diagnose a review that fails inside the model call.
apt-packages string '' Space-separated system packages to apt-get install before the review runs (e.g. maxima for a computer algebra system). Empty to skip.
pip-packages string '' Space-separated pip packages to install with pip3 --break-system-packages before the review runs (e.g. sympy). Empty to skip.
lab-manual boolean true Hold the PR to the SERG lab manual (coding style/practices, repository conventions) during review, in addition to correctness. Default true; set false for repos where the R/tidyverse/Quarto-focused manual doesn’t apply (e.g. a GitHub Actions, shell, or docs repo), so the reviewer isn’t handed guidance irrelevant to the diff.
check-latex-macros boolean false Check PR diffs for LaTeX math simplifiable via an existing macro from the d-morrison/macros submodule, and for nontrivial expressions repeated 3+ times that are candidates for a new one. Off by default. The macro checks read definitions only from a locally checked-out submodule (the repo’s pinned version is authoritative), so this only finds real macro definitions when the repo vendors the d-morrison/macros submodule and checkout-submodules: true is also set.
report-cost boolean true Post a follow-up PR comment with the review’s dollar cost (total_cost_usd, summed across the initial attempt and any stub-review retry). See README.md’s feature-parity table for the upstream source citation. Set false to suppress it.
model string '' Model for the reviewer to run on, passed through as the Claude Code CLI’s --model flag (e.g. claude-opus-4-8). Empty (default) falls through to claude-code-action’s own default. Unlike claude.yml, this workflow has no generic claude-args passthrough, so this is the only way to influence the model.
use-ai-config boolean true Install the Morrison-Lab/ai-config plugin (ai-config@Morrison-Lab) alongside the built-in code-review@claude-code-plugins, so the reviewer applies the lab’s shared review conventions rather than only the calling repo’s own CLAUDE.md. Set false to opt out.
plugin-marketplaces string '' Extra newline-separated plugin marketplace Git URLs, added on top of the two this workflow already uses (anthropics/claude-code.git for the code-review plugin, and Morrison-Lab/ai-config.git unless use-ai-config is false). Pair with plugins below; empty adds none.
plugins string '' Extra newline-separated plugin refs to install (<plugin>@<marketplace-name>), added on top of the built-in code-review@claude-code-plugins and ai-config@Morrison-Lab. Each marketplace name must match the name that marketplace declares in its own .claude-plugin/marketplace.json, not the URL given in plugin-marketplaces.

Secrets

Secret Required Description
CLAUDE_CODE_OAUTH_TOKEN no OAuth token for Claude Code (Claude Max plan).
ANTHROPIC_API_KEY no Direct Anthropic API key (or GitHub App via /install-github-app), used instead of CLAUDE_CODE_OAUTH_TOKEN.
SUBMODULES_TOKEN no Read access to private submodules.

Permissions

Grant contents: read, pull-requests: write, issues: write, and id-token: write, and add either the CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret.

The require-review gate

Put review / require-review in branch protection’s required checks, not review / claude-review: the review job reports success in cases where no verdict was produced, and this gate job exists to distinguish them.

Read its result as follows.

Result What it attests
Green A reviewer ran and the job did not fail. Not that the reviewer approved — read the posted verdict for that.
Gray (skipped) No review ran. Does not block merging.
Red The review job did not succeed. Blocks merging.

One red result is not a review failure at all: a claude-review run canceled by a newer run of the same PR (cancel-in-progress) also turns this gate red. Check whether claude-review’s conclusion is cancelled rather than failure before investigating the diff, and trust the surviving run’s review.

The gate skips gray whenever no review was attempted. That covers anything that made the claude-review job’s own if: false — a draft, fork, or bot-authored PR, a dispatch blocked by the fork/Dependabot guard, or the context-gathering job having been cancelled — plus two cases where the job ran and stopped early:

  • the API quota was exhausted before any review work happened;
  • the PR edits the caller’s own review workflow file.

That last case deserves care. claude-code-action requires the calling workflow to match the default branch, so a PR editing it cannot be reviewed by this workflow at all, and no re-run or re-dispatch will change that — the skip lifts only if the PR stops editing the file, which is rarely what the PR is for. The review job posts a PR comment saying so. Merge such a PR on a self-review or a human review; do not read the gray gate as an all-clear.

Concurrency

Do not declare a top-level concurrency: block in your caller workflow. claude-code-review.yml manages per-PR concurrency internally on its claude-review job (group: claude-review-<PR>). Adding a top-level concurrency: block in the caller with a PR-scoped group name causes GitHub Actions to detect a deadlock between the top-level workflow and the nested job, cancelling the run immediately (gha#437).

Example

# Copy to .github/workflows/claude-code-review.yml in your repo.
# NOTE: requires either CLAUDE_CODE_OAUTH_TOKEN (Claude Max plan) or
# ANTHROPIC_API_KEY (direct API / GitHub App via /install-github-app). Pass
# secrets explicitly (below) rather than via `secrets: inherit`: GitHub only
# inherits secrets into a reusable workflow owned by the same org/user, so a
# cross-owner caller (e.g. a UCD-SERG-org repo calling this d-morrison
# user-owned workflow) inherits an empty token and the run fails env-validation.
# Default: mention-triggered only. claude.yml re-dispatches this workflow on an
# `@claude review` mention (and after an @claude run pushes commits) via
# workflow_dispatch. Keep the file named claude-code-review.yml (or set
# claude.yml's review-workflow-file to match) so the dispatch resolves.
# Optional: uncomment pull_request for automatic review, and uncomment
# issue_comment to allow trusted collaborators to trigger `/review` directly.
# If you have DISABLED claude.yml, do BOTH of these, or nothing happens: uncomment
# the issue_comment trigger below, and set the repository variable
# CLAUDE_AGENT_DISABLED to `true`. dispatch-on-comment only ever runs on an
# issue_comment event, so the variable on its own changes nothing.
name: Claude Code Review

on:
  # Optional: enable automatic review on PR activity.
  # pull_request:
  #   types: [opened, synchronize, ready_for_review, reopened]
  # Optional: direct on-demand `/review` path (independent of @claude), plus
  # the opt-in `@claude review` fallback for repos with the agent disabled.
  # issue_comment:
  #   types: [created]
  workflow_dispatch:
    inputs:
      pr_number:
        description: "Pull request number to review"
        required: true
        type: string

jobs:
  review:
    permissions:
      contents: read
      pull-requests: write
      issues: write
      id-token: write
    uses: Morrison-Lab/gha/.github/workflows/claude-code-review.yml@v2
    secrets:
      CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # Max-plan OAuth; empty when using API key
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} # direct API key; empty when using OAuth
      SUBMODULES_TOKEN: ${{ secrets.SUBMODULES_TOKEN }} # optional; empty when unset
    with:
      # Wires the workflow_dispatch input through so claude.yml can re-dispatch
      # a review on Claude's commits; empty (and ignored) on pull_request runs.
      pr-number: ${{ inputs.pr_number }}
    # with:
    #   checkout-submodules: true   # SUBMODULES_TOKEN secret only for private submodules
    #   allowed-bots: 'github-actions[bot],claude'   # accept more bot actors (default: github-actions[bot])
    #   track-progress: true        # tag mode: live tracking comment + inline comments (unsafe until anthropics/claude-code-action#1415 ships)
    #   lab-manual: false           # skip the R/tidyverse SERG lab manual (default true; set false for non-R/Quarto repos)
    #   apt-packages: maxima         # system packages for the reviewer's Bash tool, e.g. a CAS
    #   pip-packages: sympy          # pip packages, e.g. a computer-algebra system for math-heavy repos
    #   check-latex-macros: true    # flag LaTeX simplifiable via d-morrison/macros; also needs checkout-submodules: true
    #   report-cost: false          # suppress the dollar-cost comment (default true)
    #   model: claude-opus-4-8      # override the reviewer's model (default: claude-code-action's own default)
    #   use-ai-config: false        # skip the Morrison-Lab/ai-config plugin (installed by default)
    #   plugin-marketplaces: https://github.com/<owner>/<repo>.git   # further plugin sources
    #   plugins: <plugin>@<marketplace-name>
    #   prompt-addendum: |
    #     Repo-specific review guidance (e.g. Quarto/R conventions to enforce).

See examples/claude-code-review.yml for the full caller stub.