Skip to content
Merged
Show file tree
Hide file tree
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
87 changes: 52 additions & 35 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ name: Claude Code Review (reusable)
# claude.yml can re-dispatch a review after an @claude run pushes commits.
# The stub passes the dispatch's pr_number through as the `pr-number` input.
#
# This workflow is intentionally read-only: its tools grant no git push, so it
# never pushes a fix and `cancel-in-progress: true` is safe.
# This workflow is read-only: it always runs claude-code-action in agent mode
# (track_progress: false) so git write tools are never in ALLOWED_TOOLS.
# Tag mode hardcodes git write tools regardless of --disallowedTools, which
# caused unauthorized commits (serodynamics PR #175, commit 02af72b). Agent
# mode fixes this; cancel-in-progress: true is safe because no push can occur.

on:
workflow_call:
Expand Down Expand Up @@ -42,6 +45,16 @@ on:
if you loosen that gate to review bot-authored pushes directly.
type: string
default: 'github-actions[bot]'
track-progress:
description: >-
Enable tag mode (track_progress: true) for pull_request events so the
reviewer posts a live tracking comment and can use the inline-comment
tool. Defaults to false (agent mode), which is safe but produces
summary-only reviews. Set to true only after anthropics/claude-code-action#1415
ships a read_only input — until then, tag mode grants git write tools
and the reviewer may push commits to the branch.
type: boolean
default: false
show-full-output:
description: >-
Show the full JSON output from Claude Code (passed through to
Expand Down Expand Up @@ -323,10 +336,10 @@ jobs:
# allowlist gap, not a GitHub-App permission scope issue — the tool
# ships with the action; it just has to be enabled). Mirrors
# anthropics/claude-code-action's own pr-review workflows. Additive:
# the CLI merges this with the action's base allowlist, so tag mode's
# tracking-comment tool is preserved. The tool is only initialized in
# tag mode (pull_request); dispatched/agent-mode runs still fall back
# to the summary, per the prompt below (claude-code-action #635).
# the CLI merges this with the action's base allowlist. The
# inline-comment tool is not initialized in agent mode
# (claude-code-action #635); the prompt falls back to a
# summary-only review when it is unavailable.
# ...and keep this a REVIEW-ONLY run. Even with a read-only contents
# token, tag mode hands the reviewer git add/commit/rm and the action's
# default prompt tells it to commit+push, so it flails through
Expand Down Expand Up @@ -437,15 +450,22 @@ jobs:
explicit `### Verdict` status line required above — restating that
the prior verdict stands is not itself the status line.
${{ inputs.prompt-addendum }}
# `track_progress` forces tag mode, which guarantees a tracking
# comment even when the plugin scores the PR below its post
# threshold. The action REJECTS `track_progress` for
# workflow_dispatch, so gate it on event_name: tag mode for
# pull_request, agent mode (possibly silent) for dispatched runs.
# Tag mode also initializes the inline-comment tool; agent mode does
# not (claude-code-action #635), so the prompt above falls back to a
# summary-only review on dispatched runs.
track_progress: ${{ github.event_name == 'pull_request' && 'true' || 'false' }}
# Agent mode (false) by default; tag mode (true) when the caller
# opts in via the track-progress input on pull_request events.
# The action rejects track_progress on workflow_dispatch regardless.
#
# WARNING: tag mode hardcodes git write tools into ALLOWED_TOOLS
# regardless of --disallowedTools (the action's TypeScript sets the
# env var directly, overriding the CLI flag). This caused the
# reviewer to push commits to PR branches (serodynamics PR #175,
# commit 02af72b). Only enable tag mode after
# anthropics/claude-code-action#1415 ships a read_only input.
# In agent mode, ALLOWED_TOOLS comes solely from claude_args so
# no git write tools are ever available; the trade-off is
# summary-only reviews (no live tracking comment or inline-comment
# tool — see claude-code-action #635). The "Post review comment"
# step below compensates when running in agent mode.
track_progress: ${{ inputs.track-progress && github.event_name == 'pull_request' && 'true' || 'false' }}
# Fresh tracking comment per run so each push surfaces as new PR
# activity; the collapse step below folds priors up as OUTDATED.
use_sticky_comment: 'false'
Expand Down Expand Up @@ -540,21 +560,20 @@ jobs:
gh pr comment "$PR_NUMBER" --repo "$REPO" --body "> [!WARNING]
> **Claude review skipped — API quota exhausted.** The \`CLAUDE_CODE_OAUTH_TOKEN\` account's API quota is exhausted. Re-trigger the review by pushing a new commit or re-running the workflow once the quota resets. [View run]($RUN_URL)"

# In agent mode (workflow_dispatch), display_report writes Claude's output
# to the GitHub Actions step summarynot to the PR. Tag mode
# (pull_request) creates a tracking comment automatically; agent mode does
# not. Post the review manually here so dispatched reviews are visible on
# the PR instead of silently discarded. Closes #87.
- name: Post review comment for dispatched run
# In agent mode, display_report writes Claude's output to the step
# summary, not to the PR. Post it manually here. Tag mode auto-posts
# the tracking comment on pull_request events, so skip this step then
# (it would duplicate the comment). Always run on workflow_dispatch
# since the action rejects track_progress there regardless. Closes #87.
- name: Post review comment
if: >-
steps.claude-review.outcome == 'success' &&
steps.fail-check.outputs.quota_exhausted != 'true' &&
github.event_name == 'workflow_dispatch' &&
inputs.pr-number != ''
!(inputs.track-progress && github.event_name == 'pull_request')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr-number }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr-number }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
EXECUTION_FILE: ${{ steps.claude-review.outputs.execution_file }}
run: |
Expand Down Expand Up @@ -595,8 +614,8 @@ jobs:
# pushes' review comments too, or they linger now that dispatched runs can
# succeed (allowed_bots, above). The run_id→path match already scopes this
# to this workflow's comments, so running it on dispatched reviews is safe.
# The "Post review comment for dispatched run" step above ensures that
# dispatched runs always leave a comment with an `actions/runs/<id>` link,
# The "Post review comment" step above ensures that dispatched and
# pull_request runs always leave a comment with an `actions/runs/<id>` link,
# so this collapse step can match and fold it in future rounds.
- name: Collapse previous Claude review comments
if: >-
Expand Down Expand Up @@ -633,16 +652,14 @@ jobs:
done

# When this run is canceled (e.g. cancel-in-progress fires because a newer
# commit — or a re-dispatched review of the same PR — superseded it), tag
# mode's tracking comment is left stuck showing an unfinished "review in
# progress". Explain the cancellation in the
# comment, then fold it (minimize as OUTDATED) so the PR doesn't carry a
# perpetual in-progress review. Tag mode only runs on pull_request, so a
# tracking comment only exists there. The tracking comment is THIS run's
# claude[bot] comment, identified by the run it links to — the same
# matcher the collapse step uses.
# commit superseded it), any tracking comment left by a tag-mode run
# (track-progress: true) would be stuck showing "review in progress".
# In agent mode (the default), no tracking comment is created, so this
# step is a no-op — kept as a safety net for tag-mode opt-in callers.
# The comment is identified by its run ID link — the same matcher the
# collapse step uses.
- name: Explain and fold tracking comment when canceled
if: cancelled() && github.event_name == 'pull_request'
if: cancelled()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ below with migration steps.
eol=lf`, explicit for `*.sh`) and union-merges `CHANGELOG.md` so two
sibling PRs appending entries under `## [Unreleased]` merge cleanly
instead of conflicting on adjacent insertions (mirrors `d-morrison/ai-config`).
- **`claude-code-review` gains a `track-progress` input** (#134). Consumers
can now opt into tag mode (live tracking comment + inline-comment tool) by
setting `track-progress: true`. The default remains `false` (agent mode),
which is safe — agent mode never grants git write tools, preventing the
review bot from pushing commits to PR branches. Only set `track-progress:
true` once [anthropics/claude-code-action#1415](https://github.com/anthropics/claude-code-action/issues/1415)
ships a `read_only` input; until then, tag mode exposes git write tools.

### Changed

Expand Down Expand Up @@ -91,6 +98,18 @@ below with migration steps.
(ambiguity, meaning-changing grammar, broken links or markup, factual errors).
This cuts review-round churn from cosmetic wrapping nits.

### Fixed

- **`claude-code-review` no longer pushes unauthorized commits to PR
branches** (#134). Tag mode (`track_progress: true`) in `claude-code-action`
hardcodes git write tools into `ALLOWED_TOOLS` regardless of `--disallowedTools`,
causing the review bot to commit and push during review runs (confirmed:
serodynamics PR #175, commit `02af72b`). The workflow now defaults to agent
mode, which builds `ALLOWED_TOOLS` solely from `claude_args` with no git write
tools. Trade-off: reviews are summary-only (no inline comments) in the default
mode; opt into tag mode with the new `track-progress` input when the upstream
fix is available.

## [2.0.0] - 2026-06-25

### Breaking
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Pin to `@v1` (a moving major tag updated as fixes land). Do not reference
| `check-news.yml` | Enforce a `NEWS.md` changelog entry on PRs (wraps `UCD-SERG/changelog-check-action`) | `changelog` |
| `test-coverage.yml` | Measure R-package test coverage with `covr` and upload the Cobertura report to Codecov | `path`, `install-quarto`, `extra-packages`, `fail-ci-if-error` |
| `claude.yml` | Agent-mode Claude Code bot: responds to `@claude` mentions, edits files, opens/updates PRs | `setup-r`, `install-quarto`, `use-renv`, `apt-packages`, `pip-packages`, `checkout-submodules`, `link-skills`, `eager-pr`, `prompt-addendum`, `webfetch-allowlist-url`, `reviewer` |
| `claude-code-review.yml` | Read-only Claude PR review (runs the `code-review` plugin; inline findings on `pull_request` runs, consolidated summary on dispatched runs) | `pr-number`, `prompt-addendum`, `checkout-submodules`, `allowed-bots`, `apt-packages`, `pip-packages` |
| `claude-code-review.yml` | Read-only Claude PR review (runs the `code-review` plugin; inline findings when `track-progress: true`, consolidated summary otherwise) | `pr-number`, `prompt-addendum`, `checkout-submodules`, `allowed-bots`, `track-progress`, `apt-packages`, `pip-packages` |
| `quarto-publish.yml` | Render a Quarto site and deploy it to GitHub Pages | `path`, `setup-r`, `r-packages`, `use-renv`, `tinytex`, `apt-packages`, `output-dir`, `checkout-submodules`, `pre-render-artifact`, `pre-render-artifact-path`, `deploy` |
| `preview.yml` | Build half of the PR-preview family: render a Quarto site in the (possibly fork) PR context and upload it + PR metadata as an artifact (read-only) | `path`, `r-version`, `apt-packages`, `use-renv`, `install-package`, `setup-chrome`, `submodules`, `render-profile` |
| `preview-deploy.yml` | Deploy half: on `workflow_run` completion of the build, publish the artifact to `gh-pages` and comment the preview link (base-repo context) | — |
Expand Down Expand Up @@ -134,7 +134,7 @@ says so.

| Feature | Action argument | Caller-configurable? |
|---|---|---|
| Live progress tracking comment on the PR | `track_progress` | Nohardcoded to `'true'` on `pull_request` events in `claude-code-review.yml` (`'false'` otherwise); not a `workflow_call` input, so callers cannot override it. Not used in `claude.yml` (agent mode manages its own progress comments). |
| Live progress tracking comment on the PR | `track_progress` | Yesdriven by the `track-progress` input of `claude-code-review.yml` (default `false`; tag mode with tracking comment and inline-comment tool when `true`, agent/summary-only mode when `false`). Not used in `claude.yml`. See `track-progress` warning in the inputs table: tag mode exposes git write tools until anthropics/claude-code-action#1415 lands. |
| Full Claude SDK output in the job log | `show_full_output` | Yes — driven by the `show-full-output` input of `claude-code-review.yml` (note the hyphen; off by default, turn on to diagnose silent auth / quota failures). Not surfaced in `claude.yml`. |
| Resume a prior session | `session_id` (internal step output of `anthropics/claude-code-action`) + `--resume` in `claude_args` | No — neither reusable workflow declares `session_id` as a `workflow_call` output, so session resume is not available to consumers of `claude.yml` or `claude-code-review.yml`. |

Expand Down
2 changes: 2 additions & 0 deletions website/reference/claude-code-review.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ keep the stub named `claude-code-review.yml` (or set `claude.yml`'s `review-work
| `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](https://github.com/anthropics/claude-code-action/issues/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. |
Expand Down Expand Up @@ -73,6 +74,7 @@ jobs:
# 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)
# 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
# prompt-addendum: |
Expand Down
2 changes: 1 addition & 1 deletion website/workflows.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ each workflow links to its reference page, with full input tables and a copy-pas
| [`check-equation-renders.yml`](reference/check-equation-renders.qmd) | Crawl the build artifact with a headless browser and fail on equations MathJax can't render | `fail` |
| [`cleanup-pr-previews.yml`](reference/cleanup-pr-previews.qmd) | Housekeeping: delete preview directories for closed PRs | `preview-dir` |
| [`claude.yml`](reference/claude.qmd) | Agent-mode Claude Code bot: responds to `@claude`, edits files, opens/updates PRs | `setup-r`, `install-quarto`, `use-renv`, `apt-packages`, `pip-packages`, `checkout-submodules`, `reviewer` |
| [`claude-code-review.yml`](reference/claude-code-review.qmd) | Read-only Claude PR review | `pr-number`, `prompt-addendum`, `checkout-submodules`, `allowed-bots`, `apt-packages`, `pip-packages` |
| [`claude-code-review.yml`](reference/claude-code-review.qmd) | Read-only Claude PR review | `pr-number`, `prompt-addendum`, `checkout-submodules`, `allowed-bots`, `track-progress`, `apt-packages`, `pip-packages` |
| [`bump-submodule.yml`](reference/bump-submodule.qmd) | Update a submodule to upstream HEAD and open a PR when the pointer moves | `submodule-path`, `remote-branch`, `base-branch`, `pr-branch` |
| [`sync-shared-fragments.yml`](reference/sync-shared-fragments.qmd) | Vendor files from an upstream repo (pinned to a commit) and open a PR when they change | `source-repo`, `source-ref`, `source-paths`, `dest-dir`, `manifest-path` |

Expand Down
Loading