fix(ci): claude-review cannot submit formal reviews or clean up comments - #465
Conversation
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>
| 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:*)" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Feedback round 1
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. |
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>
Superseded by a newer automated review
| 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). |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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:
- MCP tool names —
mcp__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_reviewis what's submitting this very review. - 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 thereviews/*/dismissalsandgraphqlendpoints. 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 DELETEremoved the previous run's tracking comment, andgh api repos/kolatts/pncli/pulls/465/reviews/5215761576/dismissals -X PUT -f message=...dismissed the earlierCHANGES_REQUESTEDreview. 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>
Superseded by a newer automated review
There was a problem hiding this comment.
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:
- 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 viacreate_and_submit_pull_request_review, so that path is confirmed again. - 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 DELETEandgh 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.
|
Claude finished @kolatts's task in 2m 7s —— View job Reviewing this PR
Cleanup performed
Review — APPROVEBoth 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:
No blocking issues found. The Formal review submitted: APPROVE. |
Superseded by a newer automated review
There was a problem hiding this comment.
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:
- MCP tool names:
mcp__github__pull_request_review_write(nonexistent) → the real split tools. This review is submitted viacreate_and_submit_pull_request_review, and the cleanup steps usedget_issue_comments/get_pull_request_reviews/get_pull_request_diffsuccessfully. - 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, matchingBash(gh api repos/*/issues/comments/* *).gh api repos/kolatts/pncli/pulls/465/reviews/5215863964/dismissals -X PUT -f message=...— dismissed the stale review, matchingBash(gh api repos/*/pulls/*/reviews/*/dismissals *).gh api graphql -f query=...— succeeded for both the read query and the (platform-rejected)resolveReviewThreadmutation, matchingBash(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.
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_outputon:mcp__github__pull_request_review_writedoes not exist in the GitHub MCP server that claude-code-action v1.0.213 pins (github-mcp-serverv0.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.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 likegh 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 thereviews/*/dismissalsandgraphqlendpoints the cleanup steps need.The prompt now names the MCP review tool as the primary way to submit, with
gh pr reviewas 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-fixis 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