From 80f726c982db39e6fe2e80d136a06c3bb249dea4 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 4 Aug 2026 13:43:35 -0500 Subject: [PATCH 1/3] claude-code-review.yml: enable track_progress for the review step The review step's `prompt:` input puts claude-code-action into automation mode, which by default posts nothing to the PR until the whole run finishes. Combined with the cost gate that waits for sibling CI, a review can look silently stuck for the better part of an hour with no visible progress. track_progress: true posts a live-updating tracking comment with a checklist instead. Pattern modeled on Postgres-Extensions/cat_tools PR #69. Note: because this workflow runs on pull_request_target, GitHub always executes the workflow file from the base branch (master), never a PR's own version -- so this PR's own claude-review check will still run the old workflow without track_progress. The new behavior can only be verified on a subsequent PR, after this one merges. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/claude-code-review.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 6585d24..2946ce5 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -95,6 +95,13 @@ jobs: # pull_request_target. GITHUB_TOKEN is repo/workflow-scoped (independent # of the actor's role) and has pull-requests: write here. github_token: ${{ secrets.GITHUB_TOKEN }} + # A `prompt:` input puts the action in "automation mode", which by + # default posts nothing until the whole run finishes -- there's no + # visibility into a review that runs long. track_progress forces a + # tracking PR comment with a live checklist that updates as Claude + # works, so a slow run is visible instead of silent. (Pattern + # modeled on Postgres-Extensions/cat_tools PR #69.) + track_progress: true # NOTE: plugin_marketplaces can't be pinned — it tracks the # marketplace repo's default branch (upstream anthropics/claude-code). plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' From 94c569c1d1b905827fb6ff60f1101aecd595e9c8 Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Tue, 4 Aug 2026 16:25:43 -0500 Subject: [PATCH 2/3] ci: allow-unsafe-pr-checkout for the review checkout step actions/checkout v4.4.0 (backported to all major-version tags) added a new default-on refusal for checking out a fork PR's head under pull_request_target, since it can't see that this job is already gated to the trusted jnasbyupgrade fork only (see the if: condition and SECURITY comment above) and never builds or executes the fetched code. Root-caused via the actual failed run logs on PRs #10/#14/#15, which all failed at this checkout step with: Refusing to check out fork pull request code from a 'pull_request_target' workflow. ... set 'allow-unsafe-pr-checkout: true' on the actions/checkout step. Without this, track_progress: true (added in the prior commit) never had a chance to matter -- the workflow was failing before the review step ever ran. --- .github/workflows/claude-code-review.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 2946ce5..843491e 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -84,6 +84,15 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 1 persist-credentials: false + # actions/checkout v4.4.0+ (backported to all major-version tags, + # https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/) + # refuses to check out a fork PR's head under pull_request_target + # unless this is explicitly set — the action can't see that the + # job-level `if:` above has already restricted this entire job to + # the trusted jnasbyupgrade fork. That gate, plus never building or + # executing the checked-out code (see SECURITY note above), is the + # safeguard this flag is asking us to confirm we have. + allow-unsafe-pr-checkout: true - name: Run Claude Code Review if: steps.gate.outputs.decision == 'run' From 8252558721282c2ccfe955624368ee7abddd08ef Mon Sep 17 00:00:00 2001 From: jnasbyupgrade Date: Wed, 5 Aug 2026 15:21:33 -0500 Subject: [PATCH 3/3] claude-code-review.yml: add a loud DO-NOT-REMOVE warning on the trust check The head.repo.owner.login check is the entire security boundary that makes allow-unsafe-pr-checkout: true safe on the checkout step below. Make that explicit and unmissable, not just implied by a comment on the checkout step referencing it. --- .github/workflows/claude-code-review.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index 843491e..95eeca1 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -22,7 +22,18 @@ concurrency: jobs: claude-review: # Trusted fork only, and skip drafts (don't spend API/CI on unfinished PRs). - # To add more trusted owners, extend the head-owner check. + # + # !!! SECURITY-CRITICAL -- DO NOT REMOVE OR WEAKEN THE head.repo.owner.login + # CHECK BELOW !!! It is the ONLY thing standing between an arbitrary external + # fork's PR and this job's write-capable GITHUB_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, + # and -- now that the checkout step below sets allow-unsafe-pr-checkout: true -- + # a checked-out copy of that fork's own code running in this trusted context. + # Drop or loosen this check and the checkout step's "safe because the job is + # already gated to a trusted fork" justification stops being true, turning this + # into a textbook "pwn request" vulnerability. To trust an additional fork, + # EXTEND this condition explicitly (e.g. `|| ... == 'other-trusted-account'`) -- + # never replace it with something broader (a wildcard, a check on PR author + # instead of head repo owner, etc.). if: >- github.event.pull_request.draft == false && github.event.pull_request.head.repo.owner.login == 'jnasbyupgrade'