Skip to content

cursor-review: skip fork PRs; degrade to job summary on read-only token#12

Merged
mattmillerai merged 1 commit into
mainfrom
fix/cursor-review-fork-pr-403
Jun 22, 2026
Merged

cursor-review: skip fork PRs; degrade to job summary on read-only token#12
mattmillerai merged 1 commit into
mainfrom
fix/cursor-review-fork-pr-403

Conversation

@mattmillerai

Copy link
Copy Markdown
Contributor

What

Two complementary fixes so cursor-review stops failing red on PRs it fundamentally can't review.

Why

A fork PR (Comfy-Desktop#973) failed the cursor-review check. Tracing it:

  • The linked panel cell (adversarial (gemini-3.1-pro)) actually passed — every panel cell did.
  • The run went red in Consolidate panel → Post review:
    No-findings review POST failed: gh: Resource not accessible by integration (HTTP 403)
    

Root cause: on a PR from a fork, the pull_request event gives the workflow no secrets and a read-only GITHUB_TOKEN. So:

  1. CURSOR_API_KEY is empty → cursor-agent errors Authentication required on all 8 cells → every cell produces empty output → judge skipped → "no findings".
  2. The read-only token can't POST a review → HTTP 403 → job fails.

The review can neither analyze nor post on a fork PR, yet it still ran the full 8-cell matrix + judge and failed the check on every external contribution.

Changes

1. Gate — skip fork PRs (cursor-review.yml)
Skip when head.repo.full_name != github.repository (the cross-repo signal, == isCrossRepository). No wasted matrix, no red X — the check is simply skipped, the standard outcome for a workflow that needs secrets a fork PR can't have.

2. post-review.py — degrade to job summary on a read-only-token 403 (defense-in-depth)
If a review POST fails with Resource not accessible by integration / HTTP 403, render the review into the job step summary and exit 0 instead of failing. Covers same-repo runs that can still hit a read-only token (read-only default workflow permissions, token-downgrading events). Genuine non-permission failures (e.g. HTTP 422) still exit 1 — no silent swallow.

Testing

  • python3 -m py_compile post-review.py and YAML parse — both clean.
  • Smoke-tested post-review.py with a fake gh:
    • read-only 403 (the #973 case) → writes review to $GITHUB_STEP_SUMMARY, exit 0
    • non-permission 422 → still exit 1 (inline + fallback both fail) ✓
    • happy path (POST succeeds) → exit 0, nothing written to summary ✓

Not done (and why)

pull_request_target would grant a write token on fork PRs, but cursor-review checks out and runs an agent over untrusted PR code — handing that a privileged token is the textbook fork-PR footgun. The proper "review forks too" path is a separate workflow_run job that posts from base-repo context; out of scope here.

🤖 Generated with Claude Code

…oken

Fork PRs can't run this review: the pull_request event withholds secrets
(CURSOR_API_KEY is empty, so every panel cell produces empty output) and
grants a read-only GITHUB_TOKEN (posting the consolidated review returns
HTTP 403 "Resource not accessible by integration"). The review can neither
analyze nor post, yet it still ran the 8-cell matrix + judge and failed the
check red on every external contribution (e.g. Comfy-Desktop#973).

Two complementary fixes:

1. Gate: skip when head.repo.full_name != github.repository (the cross-repo
   signal). No wasted matrix, no red X — the check is simply skipped, the
   standard outcome for a workflow that needs secrets a fork PR can't have.

2. post-review.py: when a review POST fails with a read-only-token 403,
   render the review into the job step summary and exit 0 instead of failing.
   Defense-in-depth for same-repo runs that can still hit a read-only token
   (read-only default workflow permissions, token-downgrading events). Genuine
   non-permission failures (e.g. HTTP 422) still exit 1 — no silent swallow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1a6e247d-93ab-423e-a604-86dd8d40e660

📥 Commits

Reviewing files that changed from the base of the PR and between dc65b8b and eebcd8a.

📒 Files selected for processing (2)
  • .github/cursor-review/post-review.py
  • .github/workflows/cursor-review.yml

📝 Walkthrough

Walkthrough

The workflow adds an early-exit fork-PR guard in the gate job. The Python review poster gains four new helpers (is_read_only_token_error, write_step_summary, post_or_degrade, render_findings_markdown) and all direct gh_post_review call sites are replaced with post_or_degrade, which degrades delivery to the GitHub Actions step summary when a read-only token 403 is detected.

Changes

Fork detection and read-only token degradation

Layer / File(s) Summary
New degradation helpers
.github/cursor-review/post-review.py
Adds import os, is_read_only_token_error (403/resource-not-accessible stderr matching), write_step_summary (GITHUB_STEP_SUMMARY writer with stdout fallback), post_or_degrade (wraps gh_post_review returning bool with step-summary degradation on read-only token errors), and render_findings_markdown (merges review body and inline comments into a single markdown blob) — no pun intended, these helpers really degrade gracefully!
Call site updates to post_or_degrade
.github/cursor-review/post-review.py
post_error_review, the no-findings path, and both retry/fallback paths in main replace direct gh_post_review calls with post_or_degrade; read-only token failures in the inline retry path degrade directly to the step summary via render_findings_markdown; all paths exit SystemExit(1) only on unrecoverable delivery failures.
Fork PR early-exit gate
.github/workflows/cursor-review.yml
Adds HEAD_REPO/BASE_REPO env vars and a shell guard in the gate job's "Check trigger label" step that sets should_run=false and exits early for fork PRs (head repo ≠ base repo), before any label-based gating — it really forks over all the responsibility!
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cursor-review-fork-pr-403
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/cursor-review-fork-pr-403

Comment @coderabbitai help to get the list of available commands and usage tips.

@mattmillerai mattmillerai merged commit 31a2c3e into main Jun 22, 2026
3 checks 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.

2 participants