Skip to content

The Claude review check skips pull requests from forks instead of failing on them - #82

Merged
webdevcody merged 1 commit into
mainfrom
skip-review-on-fork-prs
Sep 17, 2026
Merged

webdevcody merged 1 commit into
mainfrom
skip-review-on-fork-prs

Conversation

@webdevcody

@webdevcody webdevcody commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Every pull request from a fork has carried a red claude-review check since #77; the review job now skips fork PRs, so the check reads "skipped" instead of "failure" and same-repo branches are reviewed exactly as before.

Contents: 🐛 Symptom · 🔍 Cause · ✅ Fix · 📸 Before / After · 🔁 State · ⚠️ Risk · 🔧 Technical overview · 🧪 Proof · 📝 Notes

🐛 Symptom

Three contributor PRs in a row (#77, #79, #81) showed a failing claude-review check, while PRs opened from branches in this repository (#75, #80) passed the same day. Each failing run died about 25 seconds in, at the "Run Claude Code Review" step, before Claude had read a line of the diff. Re-running the job did not help: #81 failed three times in five minutes.

🔍 Cause

The workflow runs on pull_request. When the head branch lives in a fork, GitHub runs that workflow with a read-only token, no repository secrets, and no OIDC token: the id-token: write permission in the file is simply not honoured for foreign code. anthropics/claude-code-action asks for an OIDC token first, to mint its GitHub App token, and gives up after three attempts with Could not fetch an OIDC token. Nothing in .github/workflows/ had changed since 2026-08-28; what changed on 2026-09-15 is that the first fork PRs arrived.

✅ Fix

  • Fork PRs skip the review. The job carries if: github.event.pull_request.head.repo.full_name == github.repository, so a PR whose head is in another repository shows the check as skipped, not failed.
  • Unchanged. Same-repo branches are reviewed as before; the @claude comment workflow and the release workflow are not touched.

📸 Before / After

There is no screen to shoot: the change is a condition on a CI job. The terminal shows it instead.

Before — the tail of run 35062940588 on #81, identical on #77 and #79:

Attempt 3 failed: Could not fetch an OIDC token. Did you remember to add `id-token: write` to your workflow permissions?
error: Error message: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable
Operation failed after 3 attempts
##[error]Action failed with error: Could not fetch an OIDC token. ...
gh pr list --state all --limit 5 --json number,isCrossRepository,statusCheckRollup
#81  cross=true   claude-review  FAILURE
#80  cross=false  claude-review  SUCCESS
#79  cross=true   claude-review  FAILURE
#77  cross=true   claude-review  FAILURE
#75  cross=false  claude-review  SUCCESS

After — this PR's own run (35103634141) gets past the step that killed the fork runs, then stops on purpose because the PR edits the review workflow itself:

Requesting OIDC token...
OIDC token successfully obtained
Exchanging OIDC token for app token...
##[warning]Skipping action due to workflow validation: The workflow file must exist and have identical content to the version on the repository's default branch.
Action skipped due to workflow validation error. This is expected ... on PRs with workflow changes.

The next fork PR will list the check as SKIPPED instead of FAILURE.

🔁 State

flowchart LR
  PR[pull_request event] --> Q{head repo == this repo?}
  Q -- yes --> RUN[Run Claude Code Review]
  RUN --> OIDC[fetch OIDC token] --> REVIEW[inline review comments]
  Q -- "no, before" --> RUN2[Run Claude Code Review]
  RUN2 --> FAIL["no OIDC token → ❌ failure"]
  Q -- "no, after" --> SKIP["job skipped → ⏭ skipped"]
  style FAIL stroke:#c0392b,stroke-width:2px
  style SKIP stroke:#27ae60,stroke-width:2px
Loading

⚠️ Risk

Verdict: 🟢 Low risk — the change removes a job run and adds nothing that executes.

Level Why
🔒 Security & production Low No new surface: fork code still never receives secrets or a token; the job just no longer starts for it. The alternative that would review fork PRs, pull_request_target, was rejected because it runs with this repository's secrets against untrusted PR content.
Performance Low Off every hot path: a job-level if: in a workflow file; nothing in the daemon or TUI changes.
🧩 Fit with the codebase Low The workflow was generated from the action's template, which ships this exact if: slot as a commented "Filter by PR author" block; this fills it with the condition the template anticipated.

Rollback: git revert of the merge restores the red check on fork PRs; there is no protocol, store or pushed branch to undo.

🔧 Technical overview

  • The line that mattered. .github/workflows/claude-code-review.yml — the claude-review job had no if:, so it ran for every pull_request event; it now runs only when github.event.pull_request.head.repo.full_name equals github.repository, the standard GitHub Actions test for "not a fork".
  • Why it was missed. Every PR before 2026-09-15 came from a branch in this repository, where OIDC and secrets are available, so the fork path was never exercised.
  • Why not pull_request_target. It runs with the base repository's secrets, so a fork PR could steer the reviewer at the CLAUDE_CODE_OAUTH_TOKEN; the action's own security docs warn against checking out untrusted refs under it.
  • Why not github_token: ${{ secrets.GITHUB_TOKEN }}. On a fork PR that token is read-only and the OAuth secret is absent anyway, so the run would fail one step later instead.

🧪 Proof

  • Same-repo path. This PR's own claude-review check is green, but trivially: the action refuses to review a PR that changes its own workflow file, so it exits 0 after the OIDC exchange succeeds. The exchange succeeding is the part the fork runs never reached.
  • Full review run. The idle reaper spares an agent whose backgrounded tool call is still running, and restarts its idle clock when the job ends #80 (same-repo, 2026-09-16 00:57) ran the review end to end: 8 minutes, one inline comment. A follow-up PR that leaves the workflow alone repeats that check after this one.
  • Fork path. Only a fork PR exercises the skip; the next contributor PR should show claude-review as skipped rather than failed.
  • Gate. make ci not run: the diff is one YAML file, no Rust is touched.

📝 Notes

🤖 Generated with Claude Code

…ling on them

GitHub gives a `pull_request` run of fork code neither the repository secrets nor an OIDC token, so anthropics/claude-code-action fails inside its GitHub-token setup ("Could not fetch an OIDC token") before Claude ever starts. Every contributor PR since #77 has carried a red claude-review check for that reason alone; the workflow itself has not changed since August. Gate the job on the head repository being this one, so fork PRs show the check as skipped and same-repo branches keep their review.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@webdevcody
webdevcody merged commit 82ff590 into main Sep 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant