Skip to content

fix(ci): claude-review cannot submit formal reviews or clean up comments - #465

Merged
kolatts merged 3 commits into
mainfrom
kolatts/464-claude-review-tool-denials
Sep 15, 2026
Merged

kolatts merged 3 commits into
mainfrom
kolatts/464-claude-review-tool-denials

Conversation

@kolatts

@kolatts kolatts commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Closes #464

Every automated review since early September ended with 5–28 permission denials, no formal APPROVE/REQUEST_CHANGES, and stale tracking comments left in place (see the process note on #462). Two independent causes, both confirmed by running the workflow on this PR with show_full_output on:

  1. mcp__github__pull_request_review_write does not exist in the GitHub MCP server that claude-code-action v1.0.213 pins (github-mcp-server v0.17.1). Replaced with the real tools: create_and_submit_pull_request_review, create_pending_pull_request_review, submit_pending_pull_request_review, get_pull_request_reviews, get_issue_comments, get_pull_request_diff. The first run on this PR submitted a formal CHANGES_REQUESTED review through that path, so it works.
  2. Every gh api repos/*/...:* Bash rule was dead. Claude Code treats :* as a wildcard only on an otherwise-literal prefix; a * earlier in the rule plus the :* suffix matches nothing, so each call was denied with "This command requires approval" while plain-prefix rules like gh pr view:* worked. Reproduced locally on 2.1.261: Bash(gh api repos/*/issues/*/comments:*) denies, Bash(gh api repos/*/issues/*/comments *) allows. Switched those rules to the trailing-space form and added the reviews/*/dismissals and graphql endpoints the cleanup steps need.

The prompt now names the MCP review tool as the primary way to submit, with gh pr review as fallback, and spells out the list/dismiss commands for stale reviews.

Verification: the review run on this PR's final commit should (a) delete the previous run's tracking comment, (b) dismiss the earlier CHANGES_REQUESTED review, and (c) end with a formal review state and zero permission denials. no-auto-fix is set on this PR so the feedback loop doesn't push here. Once merged, reopening or pushing to #462 re-runs the review there with the fixed workflow.

🤖 Generated with Claude Code

mcp__github__pull_request_review_write does not exist in the GitHub MCP
server pinned by claude-code-action v1.0.213, so the agent never had a
working path to a formal review. Also enable show_full_output to surface
the remaining Bash gh permission denials.

Refs #464

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread .github/workflows/claude-review.yml Outdated
Do not leave a PR without a formal APPROVE or REQUEST_CHANGES state.
claude_args: |
--model sonnet --allowedTools "mcp__github__pull_request_review_write,mcp__github__add_comment_to_pending_review,mcp__github__add_issue_comment,mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr review:*),Bash(gh api repos/*/issues/*/comments:*),Bash(gh api repos/*/issues/comments/*:*),Bash(gh api repos/*/pulls/*/reviews:*),Bash(gh api repos/*/pulls/*/comments:*)"
--model sonnet --allowedTools "mcp__github__get_issue_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_diff,mcp__github__create_and_submit_pull_request_review,mcp__github__create_pending_pull_request_review,mcp__github__add_comment_to_pending_review,mcp__github__submit_pending_pull_request_review,mcp__github__add_issue_comment,mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr review:*),Bash(gh api repos/*/issues/*/comments:*),Bash(gh api repos/*/issues/comments/*:*),Bash(gh api repos/*/pulls/*/reviews:*),Bash(gh api repos/*/pulls/*/comments:*)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The MCP tool names here are now correct, but the four Bash(gh api repos/*/...) patterns are still non-functional, and I confirmed this empirically while running this very review: gh api repos/kolatts/pncli/issues/465/comments and gh api repos/kolatts/pncli/pulls/465/reviews were both denied with "This command requires approval," while gh pr view ... and gh pr diff ... (patterns with no wildcard before the trailing :*) succeeded.

Claude Code's Bash allowlist only treats the trailing :* as a wildcard suffix on an otherwise-literal prefix — a * embedded earlier in the pattern (as in repos/*/issues/*/comments) is not expanded against the real repo/issue-number, so it can never match an actual invocation. That means:

  • Bash(gh api repos/*/issues/*/comments:*)
  • Bash(gh api repos/*/issues/comments/*:*)
  • Bash(gh api repos/*/pulls/*/reviews:*)
  • Bash(gh api repos/*/pulls/*/comments:*)

are all dead rules, and every gh api repos/... call in the prompt (fetching prior comments, deleting them, listing existing reviews) is denied exactly as the PR description says. Worth confirming whether this repo's Claude Code build supports mid-pattern globs at all before relying on this shape again — if not, these need a different approach (e.g. pinning the literal repos/kolatts/pncli/... prefix, since fork PRs are already excluded by the job's if: condition, and finding another way to parameterize the issue/PR number).

Separately: there's no MCP delete_issue_comment-equivalent tool in this environment, so the "clean up stale comments" step in the prompt (deleting previous comments by ID) currently has no working path at all, Bash or MCP — that step needs to either get a working Bash rule or be dropped/reworked until one exists.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The MCP tool-name fix is correct and verified (create_pending_pull_request_review, add_comment_to_pending_review, submit_pending_pull_request_review, create_and_submit_pull_request_review, get_issue_comments, get_pull_request_reviews, get_pull_request_diff are all real, currently-available tools).

However, the four Bash(gh api repos/*/...) allowlist patterns are still non-functional. I confirmed this empirically while running this very review: gh api repos/kolatts/pncli/issues/465/comments and gh api repos/kolatts/pncli/pulls/465/reviews were both denied ("This command requires approval"), while gh pr view ... / gh pr diff ... (patterns with no wildcard before the trailing :*) succeeded. Claude Code's Bash allowlist appears to only honor a trailing :* wildcard on an otherwise-literal prefix — a * embedded mid-pattern (repos/*/issues/*/comments) never matches against the real repo/issue number, so these four rules are dead:

  • Bash(gh api repos/*/issues/*/comments:*)
  • Bash(gh api repos/*/issues/comments/*:*)
  • Bash(gh api repos/*/pulls/*/reviews:*)
  • Bash(gh api repos/*/pulls/*/comments:*)

Every gh api repos/... call the prompt relies on — fetching prior comments, deleting them, listing existing reviews — is still denied exactly as this PR's title describes. There's also currently no MCP tool equivalent to deleting an issue comment in this environment, so the "clean up stale comments" step has no working path at all right now, Bash or MCP.

Left the detail as an inline comment on .github/workflows/claude-review.yml:121. Given the PR description already flags this as WIP with a "final fix to follow," requesting changes so this diagnostic data feeds directly into that follow-up rather than merging a fix that only resolves the review-submission half of the reported problem.

@imagile-bot

imagile-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Feedback round 1

Feedback Verdict What changed
The four Bash(gh api repos/*/...) allowlist patterns are dead (mid-pattern * before a literal segment doesn't match), so fetching/deleting prior comments and listing reviews are still denied. No MCP equivalent exists for deleting an issue comment, so "clean up stale comments" has no working path at all. Declined This PR's only changed file is .github/workflows/claude-review.yml, and the fix requested here is a further edit to that same file. I'm a PR-feedback agent running with a token that has no workflows permission — any push touching .github/workflows/ is rejected by GitHub outright, and my own operating rules also forbid modifying anything under .github/ regardless. This needs a maintainer to push the allowlist/cleanup fix directly. Nothing else in the feedback was actionable outside that file.

No commits were pushed this round — the only requested change is out of reach for this agent (workflow file, requires a maintainer push). The PR description already flags this as WIP with a "final fix to follow," which matches the reviewer's own framing.

@kolatts kolatts added the no-auto-fix Claude PR Feedback will not push to this branch label Sep 15, 2026
Claude Code only treats the `:*` suffix as a wildcard on a literal prefix,
so every rule with a mid-pattern `*` (gh api repos/*/issues/*/comments:*)
silently matched nothing and the agent could never fetch, delete or
dismiss its own stale feedback. Switch those rules to the trailing-space
form, add the dismissals and graphql endpoints the cleanup steps need,
and point the prompt at the MCP review tool that does exist.

Closes #464

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions
github-actions Bot dismissed their stale review September 15, 2026 21:11

Superseded by a newer automated review

Comment thread .github/workflows/claude-review.yml Outdated
gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews --jq '.[] | select(.user.login == "github-actions[bot]" and (.state == "APPROVED" or .state == "CHANGES_REQUESTED")) | .id'
and dismiss each with:
gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews/<id>/dismissals -X PUT -f message="Superseded by a newer automated review"
5. Resolve any outdated inline review comment threads from previous reviews (use `gh api graphql` with the resolveReviewThread mutation).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The two root causes named in the PR title are fixed and I verified both directly while running this review: gh api repos/kolatts/pncli/issues/comments/<id> -X DELETE deleted the stale tracking comment from the previous run, and gh api repos/kolatts/pncli/pulls/465/reviews/<id>/dismissals -X PUT -f message=... dismissed the earlier CHANGES_REQUESTED review — both using the exact trailing-space-form patterns this PR added.

One more gap, found while executing this step: the resolveReviewThread GraphQL mutation is rejected by the workflow's GITHUB_TOKEN itself — FORBIDDEN: Resource not accessible by integration — regardless of the pull-requests: write permission already granted in the job. This is a platform-level restriction on Actions installation tokens, not a Claude Code Bash-allowlist issue like the other four rules this PR fixed, so adding Bash(gh api graphql *) doesn't actually unlock it. Not a regression (step 5 never worked before this PR either, it just failed with a different error), and out of the PR's stated scope (title/verification criteria only cover review submission and comment cleanup), so not blocking — but worth tracking as a fast follow: either drop step 5 or mark it explicitly best-effort so a future run doesn't burn a turn on a call that can never succeed.

github-actions[bot]
github-actions Bot previously approved these changes Sep 15, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Both root causes named in the title are fixed, and I verified each one empirically while running this review rather than just reading the diff:

  1. MCP tool namesmcp__github__pull_request_review_write (which doesn't exist) is replaced with the real split tools. Confirmed again this run: create_and_submit_pull_request_review is what's submitting this very review.
  2. Dead gh api repos/*/... Bash rules — the previous two review rounds on this PR independently reproduced the :*-suffix rules as dead (mid-pattern * never matches a real repo/issue number). This commit switches them to the trailing-space form and adds the reviews/*/dismissals and graphql endpoints. I exercised the fixed rules directly as part of this run's required stale-feedback cleanup: gh api repos/kolatts/pncli/issues/comments/5688063020 -X DELETE removed the previous run's tracking comment, and gh api repos/kolatts/pncli/pulls/465/reviews/5215761576/dismissals -X PUT -f message=... dismissed the earlier CHANGES_REQUESTED review. Both succeeded.

One residual gap, left as an inline comment on .github/workflows/claude-review.yml:101: step 5 of the cleanup instructions (resolve outdated inline review threads via the resolveReviewThread GraphQL mutation) still doesn't work — not because of a Claude Code Bash-allowlist issue like the other four rules, but because the workflow's GITHUB_TOKEN itself gets FORBIDDEN: Resource not accessible by integration on that mutation regardless of the pull-requests: write permission already granted. I hit this directly attempting the same mutation this run. It's not a regression (that step never worked before this PR either) and it's outside what the PR title/verification criteria cover, so not blocking — but worth a fast follow so a future run doesn't spend a turn on a call that can't succeed.

Also note: I was unable to resolve the one pre-existing outdated review thread on this PR for the same reason (GraphQL FORBIDDEN on resolveReviewThread), so it remains open despite being outdated — a maintainer with broader GraphQL scope will need to resolve it manually.

Stale feedback cleanup performed this run: deleted 1 stale tracking comment (5688063020), dismissed 1 stale CHANGES_REQUESTED review (5215761576). The imagile-bot[bot] "Feedback round 1" comment was left in place — it's out of scope for this cleanup (the fetch filter only targets claude[bot]/github-actions[bot] comments).

…rompt

The workflow token is refused on the resolveReviewThread mutation, so the
step cannot be made reliable; stop the agent from reporting it as a failure.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions
github-actions Bot dismissed their stale review September 15, 2026 21:17

Superseded by a newer automated review

github-actions[bot]
github-actions Bot previously approved these changes Sep 15, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the final state of .github/workflows/claude-review.yml (commits 249c4b3, be90eed, 3114c8f). Both root causes named in the PR title are fixed, and — unusually for a workflow-only change — every fix has already been exercised live across the four prior review rounds on this same PR, not just read as a diff:

  1. MCP tool names: mcp__github__pull_request_review_write (nonexistent) → the real split tools (create_and_submit_pull_request_review, create_pending_pull_request_review, add_comment_to_pending_review, submit_pending_pull_request_review, get_issue_comments, get_pull_request_reviews, get_pull_request_diff). This review itself is being submitted via create_and_submit_pull_request_review, so that path is confirmed again.
  2. Dead gh api repos/*/... Bash rules: switched from the :*-suffix form (which never matches once a * appears earlier mid-pattern) to the trailing-space form. I exercised these directly during this run's cleanup: gh api repos/.../issues/comments/<id> -X DELETE and gh api repos/.../pulls/<n>/reviews/<id>/dismissals -X PUT -f message=... both succeeded using exactly the rule shapes this PR added (Bash(gh api repos/*/issues/comments/* *), Bash(gh api repos/*/pulls/*/reviews/*/dismissals *)).

The residual gap flagged by the previous approval (step 5, resolveReviewThread rejected by the workflow's GITHUB_TOKEN with FORBIDDEN: Resource not accessible by integration — a platform-level token restriction, not a Bash-allowlist issue) is now addressed by commit 3114c8f: step 5 is explicitly marked best-effort and instructs the agent not to report the expected failure as a problem. I re-verified the mutation still gets the same FORBIDDEN response with this run's token, so marking it best-effort rather than trying to "fix" it further is the right call — there's no allowlist change that unlocks a platform permission.

No other issues found. The new Bash(gh api repos/*/pulls/*/reviews/*/dismissals *) and Bash(gh api graphql *) rules are appropriately scoped to the issues/pulls endpoints the prompt actually needs, and the explanatory comment above claude_args correctly documents the wildcard-matching gotcha for future maintainers.

Stale feedback cleanup performed this run: deleted 1 stale tracking comment (#5688135817), dismissed 1 stale APPROVED review (#5215832729). Attempted best-effort resolution of the 2 outdated inline threads via resolveReviewThread — still FORBIDDEN as expected, left open per the now-documented best-effort behavior.

@kolatts
kolatts marked this pull request as ready for review September 15, 2026 21:19
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @kolatts's task in 2m 7s —— View job


Reviewing this PR

  • Clean up stale Claude feedback from previous runs
  • Gather context (diff, CLAUDE.md, workflow file)
  • Review the change for correctness, security, and adherence to patterns
  • Post review feedback and submit formal GitHub review

Cleanup performed

  • Deleted 1 stale tracking comment from the previous run (#5688208240)
  • Dismissed 1 stale APPROVED review from github-actions[bot] (#5215863964, superseded)
  • The imagile-bot[bot] "Feedback round 1" comment was left in place — out of scope for this cleanup (filter only targets claude[bot]/github-actions[bot])
  • Attempted best-effort resolution of the 2 outdated inline review threads via resolveReviewThread — still FORBIDDEN: Resource not accessible by integration (expected platform-token limitation, not blocking, per the now-documented best-effort step 5)

Review — APPROVE

Both root causes named in the PR title are fixed, and I independently re-verified each one live in this run rather than just reading the diff:

  1. MCP tool names: mcp__github__pull_request_review_write (nonexistent) → the real split tools. This review is submitted via create_and_submit_pull_request_review, and the cleanup steps used get_issue_comments/get_pull_request_reviews/get_pull_request_diff successfully.
  2. Dead gh api repos/*/... Bash rules: switched from the :*-suffix form to the trailing-space form. I exercised the fixed rules directly:
    • gh api repos/kolatts/pncli/issues/comments/5688208240 -X DELETE — deleted the stale tracking comment, matching Bash(gh api repos/*/issues/comments/* *).
    • gh api repos/kolatts/pncli/pulls/465/reviews/5215863964/dismissals -X PUT -f message=... — dismissed the stale review, matching Bash(gh api repos/*/pulls/*/reviews/*/dismissals *).
    • gh api graphql -f query=... — succeeded for both the read query and the (platform-rejected) resolveReviewThread mutation, matching Bash(gh api graphql *).
    • As a negative control, an unrelated command outside the allowlist (git diff origin/main...HEAD) was correctly denied with "This command requires approval" — confirming the allowlist is doing real work here, not just passing everything through.

No blocking issues found. The permissions: block (issues: write, pull-requests: write, contents: read, actions: write) lines up with exactly what the new Bash rules need, and the explanatory comment above claude_args correctly documents the wildcard-matching gotcha for future maintainers. Bash(gh api repos/*/pulls/*/comments *) is unused by the current prompt steps but harmless (scoped to the same PR-comments endpoint family) — not worth blocking on.

Formal review submitted: APPROVE.

@github-actions
github-actions Bot dismissed their stale review September 15, 2026 21:20

Superseded by a newer automated review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the final state of .github/workflows/claude-review.yml. Both root causes named in the PR title are fixed, and I independently re-verified each one live in this run rather than just reading the diff:

  1. MCP tool names: mcp__github__pull_request_review_write (nonexistent) → the real split tools. This review is submitted via create_and_submit_pull_request_review, and the cleanup steps used get_issue_comments/get_pull_request_reviews/get_pull_request_diff successfully.
  2. Dead gh api repos/*/... Bash rules: switched from the :*-suffix form to the trailing-space form. I exercised the fixed rules directly:
    • gh api repos/kolatts/pncli/issues/comments/5688208240 -X DELETE — deleted the stale tracking comment, matching Bash(gh api repos/*/issues/comments/* *).
    • gh api repos/kolatts/pncli/pulls/465/reviews/5215863964/dismissals -X PUT -f message=... — dismissed the stale review, matching Bash(gh api repos/*/pulls/*/reviews/*/dismissals *).
    • gh api graphql -f query=... — succeeded for both the read query and the (platform-rejected) resolveReviewThread mutation, matching Bash(gh api graphql *).
    • As a negative control, an unrelated command outside the allowlist (git diff origin/main...HEAD) was correctly denied with "This command requires approval" — confirming the allowlist is doing real work, not just passing everything through.

No blocking issues found. The permissions: block (issues: write, pull-requests: write, contents: read, actions: write) lines up with exactly what the new Bash rules need, and the explanatory comment above claude_args correctly documents the wildcard-matching gotcha. Bash(gh api repos/*/pulls/*/comments *) is unused by the current prompt steps but harmless — not worth blocking on.

Stale feedback cleanup performed this run: deleted 1 stale tracking comment (#5688208240), dismissed 1 stale APPROVED review (#5215863964). Attempted best-effort resolution of the 2 outdated inline threads via resolveReviewThread — still FORBIDDEN as expected, left open per the documented best-effort behavior.

@kolatts
kolatts merged commit c0bc02f into main Sep 15, 2026
5 checks passed
@kolatts
kolatts deleted the kolatts/464-claude-review-tool-denials branch September 15, 2026 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-auto-fix Claude PR Feedback will not push to this branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: claude-review cannot delete stale comments or submit formal reviews (tools denied)

1 participant