Skip to content

claude-code-review.yml: add a claude-debug label to skip the cost gate - #64

Merged
jnasbyupgrade merged 4 commits into
Postgres-Extensions:masterfrom
jnasbyupgrade:claude-debug-label
Aug 2, 2026
Merged

claude-code-review.yml: add a claude-debug label to skip the cost gate#64
jnasbyupgrade merged 4 commits into
Postgres-Extensions:masterfrom
jnasbyupgrade:claude-debug-label

Conversation

@jnasbyupgrade

Copy link
Copy Markdown
Contributor

Debugging why the paid review isn't behaving as expected (e.g. the missing --comment flag that silently swallowed every review before #57, or the missing inline-comment --allowedTools flag before #62) previously meant either waiting 5-20+ min per iteration for the cost gate to clear, or hand-editing the workflow just to see the raw transcript.

Adding the claude-debug label to a PR now (a) skips the cost gate entirely, and (b) sets show_full_output: true so the full raw Claude Code JSON transcript, including tool results, lands in the job log. The label is checked live via gh pr view rather than the static event payload, so adding it and clicking "Re-run jobs" on an existing run picks it up without a new push.

labeled is added as a trigger type so applying the label alone kicks off a fresh run with no push/re-run needed; the job's if: scopes that to only the claude-debug label itself, so tagging a PR with anything else doesn't trigger another paid review.

Created the claude-debug label on the repo as part of this.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6a381c7e-f95c-4d0f-a1f7-d36375ec4089

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment on lines 18 to 24
# push/re-run needed. Scoped in the job's `if:` below to only actually
# proceed when the label added IS claude-debug -- otherwise every
# unrelated label added to a PR would trigger another paid review.
types: [opened, synchronize, reopened, ready_for_review, labeled]

concurrency:
group: claude-review-${{ github.event.pull_request.number }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: adding labeled here can silently cancel an in-progress review with nothing replacing it.

The workflow-level concurrency block just below (group: claude-review-${{ github.event.pull_request.number }}, cancel-in-progress: true on the next line) is unchanged by this PR and is keyed only on the PR number. on.pull_request_target.types has no way to filter by which label was added — that filtering only happens in the job's if: (line 42). So labeled fires a new workflow run for any label, not just claude-debug.

Concurrency cancellation is resolved at run-creation time, before job-level if: conditions are evaluated. So the sequence: a review is running (triggered by synchronize) → a maintainer adds an unrelated label (e.g. documentation) → GitHub creates a new run in the same concurrency group → the in-progress review is cancelled → the new run's claude-review job is then skipped by the if: (since the label isn't claude-debug) → no review ever posts for that push, and nothing automatically retriggers it.

Before this PR, labeled wasn't a trigger at all, so this failure mode didn't exist. A fix would need to keep non-claude-debug labeled events out of the shared concurrency group (e.g. fold github.event.label.name into the group key for labeled events), rather than relying solely on the job-level if: to no-op them.

# push/re-run needed. Scoped in the job's `if:` below to only actually
# proceed when the label added IS claude-debug -- otherwise every
# unrelated label added to a PR would trigger another paid review.
types: [opened, synchronize, reopened, ready_for_review, labeled]
concurrency:
group: claude-review-${{ github.event.pull_request.number }}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ac1ef09: labeled events for anything other than claude-debug now get their own per-label concurrency group, so they can never collide with (and thus never cancel) the real review's group. A claude-debug labeling event still deliberately shares the real group, since it's meant to supersede an in-progress review.

jnasbyupgrade added a commit to jnasbyupgrade/cat_tools that referenced this pull request Aug 2, 2026
…review

Caught by claude-review itself on this PR (review comment
Postgres-Extensions#64 (comment)):
concurrency-group cancellation is resolved when a run is admitted, before
the job's `if:` is ever evaluated -- the `if:` can only no-op the new run's
job, it can't un-cancel whatever the run's mere existence already
displaced. Since `labeled` became a trigger type, a group keyed only on PR
number meant ANY label (not just claude-debug) admitted a new run here,
cancelling a real in-progress review (triggered by `synchronize`) with
nothing to replace it.

Fix: only give a labeled event its own per-label group when the label is
NOT claude-debug, so it can never collide with (and thus never cancel) the
real review's group. A labeled+claude-debug event deliberately keeps the
plain group -- it's meant to supersede an in-progress real review, not run
alongside it.
jnasbyupgrade added a commit to jnasbyupgrade/cat_tools that referenced this pull request Aug 2, 2026
…review

Caught by claude-review itself on this PR (review comment
Postgres-Extensions#64 (comment)):
concurrency-group cancellation is resolved when a run is admitted, before
the job's `if:` is ever evaluated -- the `if:` can only no-op the new run's
job, it can't un-cancel whatever the run's mere existence already
displaced. Since `labeled` became a trigger type, a group keyed only on PR
number meant ANY label (not just claude-debug) admitted a new run here,
cancelling a real in-progress review (triggered by `synchronize`) with
nothing to replace it.

Fix: only give a labeled event its own per-label group when the label is
NOT claude-debug, so it can never collide with (and thus never cancel) the
real review's group. A labeled+claude-debug event deliberately keeps the
plain group -- it's meant to supersede an in-progress real review, not run
alongside it.
Comment on lines +24 to +36
# BUG (found by claude-review itself, PR #64 review comment): concurrency
# cancellation is resolved when the run is admitted, BEFORE the job's `if:`
# is ever evaluated -- a job's `if:` can only no-op that job, it can't
# un-cancel whatever the run's mere existence already displaced. Since
# `labeled` is now a trigger type (see the `on:` comment above), a group
# keyed only on PR number meant ANY label -- not just claude-debug -- would
# admit a new run here, cancelling a real in-progress review with nothing
# to replace it (the new run's job then no-ops via its `if:`, too late).
# Fix: only a labeled event whose label is NOT claude-debug gets its own
# per-label group, so it can never collide with (and thus never cancel)
# the real review's group. A labeled+claude-debug event deliberately keeps
# the plain group -- it's meant to supersede an in-progress real review.
group: claude-review-${{ github.event.pull_request.number }}${{ (github.event.action == 'labeled' && github.event.label.name != 'claude-debug') && format('-{0}', github.event.label.name) || '' }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment recounts the bug's full history (the old grouping, the specific labeled-trigger scenario, the exact cancellation consequence) rather than briefly stating the guard fact, per CLAUDE.md's "Bug Fixes" section:

Comment the fix where it isn't self-evident, but keep it concise — no novels. Do NOT recount the bug's history (what a past version got wrong) UNLESS the same mistake could realistically be made again; if it could, briefly state the guard fact that prevents it.

The guard fact ("concurrency cancellation is resolved when the run is admitted, before the job's if: is ever evaluated") is legitimate to keep since a future trigger type could hit the same pitfall — but the surrounding narration of the specific historical scenario and consequence goes beyond "briefly."

concurrency:
# BUG (found by claude-review itself, PR #64 review comment): concurrency
# cancellation is resolved when the run is admitted, BEFORE the job's `if:`
# is ever evaluated -- a job's `if:` can only no-op that job, it can't
# un-cancel whatever the run's mere existence already displaced. Since
# `labeled` is now a trigger type (see the `on:` comment above), a group
# keyed only on PR number meant ANY label -- not just claude-debug -- would
# admit a new run here, cancelling a real in-progress review with nothing
# to replace it (the new run's job then no-ops via its `if:`, too late).
# Fix: only a labeled event whose label is NOT claude-debug gets its own
# per-label group, so it can never collide with (and thus never cancel)
# the real review's group. A labeled+claude-debug event deliberately keeps
# the plain group -- it's meant to supersede an in-progress real review.
group: claude-review-${{ github.event.pull_request.number }}${{ (github.event.action == 'labeled' && github.event.label.name != 'claude-debug') && format('-{0}', github.event.label.name) || '' }}
cancel-in-progress: true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 761bc24: trimmed the comment to the guard fact only, dropping the incident narration.

Comment on lines +85 to +89
run: |
enabled=$(gh pr view "$PR" --repo "$REPO" --json labels \
--jq 'any(.labels[]; .name == "claude-debug")')
echo "enabled=$enabled" >> "$GITHUB_OUTPUT"
echo "claude-debug label present: $enabled"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step has no error tolerance for a transient gh pr view failure (API 5xx, rate limit, network blip). Under GitHub Actions' default bash -e, a non-zero exit inside $(...) fails the assignment and thus the step — and since this step is now unconditional and runs first, it will hard-fail the whole claude-review job on every ordinary (non-debug) PR review, not just debug runs.

This is inconsistent with the adjacent "Wait for CI; skip the paid review if any check failed" step, which explicitly tolerates the same class of failure (gh api ... 2>/dev/null || json='', then retries). Consider || enabled=false here so a transient failure degrades to "not in debug mode" instead of failing the run.

PR: ${{ github.event.pull_request.number }}
run: |
enabled=$(gh pr view "$PR" --repo "$REPO" --json labels \
--jq 'any(.labels[]; .name == "claude-debug")')
echo "enabled=$enabled" >> "$GITHUB_OUTPUT"
echo "claude-debug label present: $enabled"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 761bc24: added || enabled=false, matching the fallback style already used in the adjacent cost-gate step.

Debugging why the paid review isn't behaving as expected (e.g. the
missing --comment flag that silently swallowed every review before Postgres-Extensions#57)
previously meant either waiting 5-20+ min per iteration for the cost gate
to clear, or hand-editing the workflow just to see the raw transcript.

Adding the "claude-debug" label to a PR now (a) skips the cost gate
entirely, and (b) sets show_full_output: true so the full raw Claude Code
JSON transcript, including tool results, lands in the job log. The label
is checked live via `gh pr view` rather than the static event payload, so
adding it and clicking "Re-run jobs" picks it up without a new push.
`labeled` is added as a trigger type so applying the label alone kicks off
a fresh run; the job's `if:` scopes that to only the claude-debug label
itself, so tagging a PR with anything else doesn't trigger another paid
review.
…review

Caught by claude-review itself on this PR (review comment
Postgres-Extensions#64 (comment)):
concurrency-group cancellation is resolved when a run is admitted, before
the job's `if:` is ever evaluated -- the `if:` can only no-op the new run's
job, it can't un-cancel whatever the run's mere existence already
displaced. Since `labeled` became a trigger type, a group keyed only on PR
number meant ANY label (not just claude-debug) admitted a new run here,
cancelling a real in-progress review (triggered by `synchronize`) with
nothing to replace it.

Fix: only give a labeled event its own per-label group when the label is
NOT claude-debug, so it can never collide with (and thus never cancel) the
real review's group. A labeled+claude-debug event deliberately keeps the
plain group -- it's meant to supersede an in-progress real review, not run
alongside it.
This PR just made claude-code-review.yml's concurrency group conditional
(only a non-claude-debug labeled event gets its own per-label group), so
verify-cancel-on-close-coupling's expected literal string (added by Postgres-Extensions#63,
now on master) needs to match the new formula. Confirmed via
`gh pr checks 64` that this is exactly what's currently failing -- this
PR's own claude-code-review.yml change is compared, under the merge ref,
against master's not-yet-updated expectation.
Both caught by claude-review on this PR's own commit:

- The concurrency-group comment recounted the bug's full discovery history
  instead of briefly stating the guard fact, per this repo's Bug Fixes
  convention. Trimmed to the guard fact and the fix rationale.

- The claude-debug label check ran unconditionally with no fallback; under
  the default `bash -e`, a transient `gh pr view` failure (rate limit,
  blip) would hard-fail the whole claude-review job on every ordinary
  review, not just debug runs. Add `|| enabled=false`, matching the
  fallback style already used in the adjacent cost-gate step.
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Claude finished @jnasbyupgrade's task in 2m 33s —— View job


Claude Code Review

  • Gather context (PR body, prior review comments, changed files)
  • Read .github/workflows/claude-code-review.yml and .github/workflows/ci.yml diffs
  • Review for bugs, security issues, and CLAUDE.md compliance
  • Post review feedback

Review

I re-reviewed the current state of the diff (HEAD de7f925), after the two follow-up fix commits (f6efd1e, de7f925) that address the earlier review comments on this PR. Both are correctly resolved:

  • Concurrency-cancellation bug (f6efd1e): a labeled event for anything other than claude-debug now gets its own per-label group (.github/workflows/claude-code-review.yml:36), so it can never collide with/cancel the shared claude-review-<pr> group used by real reviews. I checked cancel-on-close.yml's hardcoded claude-review-${{ github.event.pull_request.number }} group too — it only needs to match the plain group (the one long-running/costly reviews use), which is unaffected by the new per-label suffix, so no update was needed there.
  • Coupling guard (05ff288): ci.yml's verify-cancel-on-close-coupling expected-string formula (lines 396-400) is a character-for-character match of the new concurrency.group expression in claude-code-review.yml:36. Confirmed by diffing both strings directly.
  • Comment conciseness (de7f925): the concurrency-group comment (lines 29-35) now states only the guard fact ("cancellation resolves at admission, before if: runs") without recounting the specific historical incident, per CLAUDE.md's Bug Fixes guidance.
  • Transient gh pr view failure (de7f925): line 85 now has || enabled=false, so a 5xx/rate-limit/network blip degrades to "not debug mode" instead of hard-failing the job for every ordinary PR review — consistent with the adjacent cost-gate step's own fallback style.

Other things I checked and found sound:

  • The job-level if: (lines 49-52) correctly gates so only a labeled event whose label is claude-debug proceeds; any other label short-circuits the whole job before the new "Check for claude-debug label" step ever runs, so there's no wasted API call.
  • steps.debug.outputs.enabled is read live via gh pr view rather than the static labeled event payload, so it also picks up a label already present on synchronize/reopened/etc. runs (not just the initial labeling event) — matches the PR description's intent.
  • Skipped steps' outputs resolve to empty string, not an error, so steps.debug.outputs.enabled == 'true' || steps.gate.outputs.decision == 'run' on the checkout/review steps is safe regardless of which upstream step ran.
  • show_full_output carries its own inline WARNING about secrets potentially landing in public logs — appropriately called out given this is a single-trusted-fork-owner workflow already gated by head.repo.owner.login.

No new issues found. This looks ready to merge from a workflow-logic and CLAUDE.md-compliance standpoint.

@jnasbyupgrade
jnasbyupgrade merged commit ddf08c5 into Postgres-Extensions:master Aug 2, 2026
37 checks passed
@jnasbyupgrade
jnasbyupgrade deleted the claude-debug-label branch August 2, 2026 23:54
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