From a295e1ac4e9afa0c8ceac31423da778419ddf663 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Sat, 15 Aug 2026 19:14:58 -0500 Subject: [PATCH] revert(ci): drop the automatic PR review trigger, keep the author gate The pull_request trigger added earlier today fired on every PR and never posted a single review. Three obstacles in sequence: the action's anti-tampering guard, then insufficient token permissions, then 15 tool permission denials that remain unexplained. Every run concluded "success" while producing nothing, at $0.20-$1.12 each. The comment-triggered path was never broken, and is what /create-pr uses. A working control invoked by the command beats a broken one wired to an event. Reverted: the pull_request trigger, the concurrency group (it existed to de-dupe draft/ready toggles on that trigger, and on comment-driven runs it would cancel a legitimate in-progress review), the automation prompt, and the token permission bump that turned out not to be the fix. KEPT deliberately: the author_association gate. Several of these workflows had no gate at all and would run for any commenter on a public repository. That hole was real and predates today's work. --- .github/workflows/claude.yml | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index dc40ddb..a7a94ad 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -1,8 +1,6 @@ name: Claude Code Review on: - pull_request: - types: [opened, ready_for_review] issue_comment: types: [created] pull_request_review_comment: @@ -12,29 +10,13 @@ on: pull_request_review: types: [submitted] -# One review per PR at a time. A rapid draft->ready->draft toggle would otherwise -# stack runs and burn Claude usage on states nobody is waiting for. -concurrency: - group: claude-review-${{ github.event.pull_request.number || github.event.issue.number || github.run_id }} - cancel-in-progress: true - jobs: claude: # Defense-in-depth author gate: only run when the triggering actor is a repo - # OWNER/MEMBER/COLLABORATOR, so a drive-by comment or a fork PR from an - # outside account cannot invoke Claude or drain Claude usage. This repo is - # public, so the gate is load-bearing, not decorative. - # - # The pull_request arm reviews every PR opened by an org member — the - # compensating control for CC8.1, since a sole maintainer cannot approve - # their own PR. It is `pull_request`, never `pull_request_target`, so a fork - # PR carries no secrets even in the case where it somehow ran. + # OWNER/MEMBER/COLLABORATOR, so a drive-by comment from an outside account + # cannot invoke Claude or drain Claude usage. This repo is public, so the + # gate is load-bearing, not decorative. if: | - ( - github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository && - contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.pull_request.author_association) - ) || (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)) || @@ -63,14 +45,8 @@ jobs: additional_permissions: | actions: read - # On the automatic pull_request trigger there is no comment to act on, so - # supply the review brief. Left empty for comment-driven events so Claude - # follows the instructions in the comment that tagged it. - # - # Deliberately does NOT approve: org policy sets - # can_approve_pull_request_reviews=false, and an unconditional bot approval - # on every PR is a rubber stamp - worse audit evidence than none. - prompt: ${{ github.event_name == 'pull_request' && 'Review this pull request. This repository is a published SDK on a post-1.0 semver contract with external integrators, maintained by a single engineer who cannot approve their own PRs, so this review is the compensating change-management control - be substantive rather than cursory. Focus on correctness bugs, whether anything touches the stable tier (SDK facades, root exports, error classes, auth config, and the symbols the integration template imports) which would require a coordinated client major, and whether the PR description matches the diff. Post your findings as a comment. Do not approve the pull request.' || '' }} + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' # Optional: Add claude_args to customize behavior and configuration # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md