Title
claude-code-action pushes commits when only a review was requested; workflow permissions: contents: read does not constrain it
Body
Summary
I observed anthropics/claude-code-action@v1 make and push a commit to a PR branch under the claude[bot] identity, in response to a comment that requested review and verification only — no implementation was requested. Additionally, the workflow YAML's permissions: contents: read block did not prevent this push, because the action uses the GitHub App's own credentials (granted at App install time) rather than the workflow's auto-minted GITHUB_TOKEN.
This combination — proactive code-pushing behavior + workflow permissions that don't actually constrain the bot — surprised me as the repo owner. Filing this so the team is aware and so other users can decide whether the behavior matches their expectations.
Reproduction context (anonymized)
- A multi-round PR review loop where the bot iteratively reviews a single PR and the human pushes fixes between rounds.
- The bot is invoked via
@claude mentions in PR comments.
- Workflow file declares only
permissions: contents: read, pull-requests: read, issues: read, id-token: write, actions: read.
- The PR contained code changes addressing security findings the bot had raised in earlier rounds.
Sequence that produced the surprise commit
- I (human) made local edits implementing fixes the bot had requested.
- I ran
git commit -m "...".
- My local
pre-commit hook ran ruff format, which modified some files and exited non-zero. The commit was therefore rejected, but the staged changes remained.
- My subsequent
git push reported Everything up-to-date — because no commit had actually been created. From the developer's perspective the workflow looked successful. This is a known git foot-gun but worth naming as the trigger.
- I posted a PR comment to
@claude saying (paraphrased) "addressed all items from your last review, please re-verify."
- The bot ran. It checked out the branch, observed the claimed fixes were NOT present in the remote.
- Instead of flagging the discrepancy and asking the human to push the missing changes, the bot implemented and committed the fixes itself, author/committer =
claude[bot]. Commit message included the line "Implement missing changes (committed <sha>, pushed)".
- The bot then posted its review of its own commit, declaring the PR ready to merge.
Two distinct concerns
Concern 1: scope expansion from "review" to "implement" without explicit invocation
My PR comment did not request implementation. It asked for re-verification of fixes I had (incorrectly) claimed to have pushed. A reasonable response would have been: "The fixes you claim to have pushed are not in the current branch HEAD <sha> — please verify your push succeeded and re-trigger the review."
Instead the bot interpreted the discrepancy as a directive to make my comment true by implementing the missing fixes. This is a reasonable design choice for some users, but it should be opt-in rather than the default, especially because:
- The bot's commits bypass local pre-commit hooks (the bot's commit broke CI's
ruff format --check step because the bot does not run pre-commit before committing).
- The bot's commits land in
git blame as claude[bot], with the human listed as Co-authored-by. For attribution and review-trail purposes this matters.
- A bot acting on a misinterpretation of a developer's comment can compound errors silently.
Concern 2: workflow YAML permissions: block does not constrain the action
This is the more important point for documentation/clarity. The workflow declared:
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read
A naive reader (including me) interprets this as "the workflow can only read repo contents, not write." This is true for the workflow's auto-minted GITHUB_TOKEN, but the action does not use that token for code modifications. It uses the GitHub App's own installation token, which has whatever permissions were granted at App install time — typically including contents: write.
Result: the workflow YAML's permission block is misleading. The expressed intent ("read-only") is not what the bot can actually do.
I'd recommend either:
- A clear documentation note in the README that workflow permissions do NOT constrain the action's push capability — that requires App-level permission revocation in repo settings.
- A
mode: input on the action (e.g., mode: review, mode: suggest, mode: implement) that the action self-enforces, regardless of the App's available permissions. Default to review or suggest.
- An explicit
claude_args: example in the README showing how to disable code-modification capability via the prompt or via tool restrictions.
Suggested concrete fixes
- README clarification about App-level vs workflow-level permissions for this action.
- Add a
mode input with values like review, suggest_diff, implement. Default review or suggest_diff. implement requires explicit opt-in.
- Detect the missing-claimed-changes case and prefer to flag it back to the developer ("the fixes you claim are not in the branch HEAD") rather than implementing them speculatively.
- If the bot DOES commit, run pre-commit hooks (or at least
ruff format / prettier where present in .pre-commit-config.yaml) before pushing, so its commits don't break CI for trivial format issues.
Workaround for now
For users who want the bot to be review-only:
- GitHub UI: Settings → Integrations → Claude Code → Configure → repo permissions → change Contents from "Read & write" to "Read-only".
- Bot can still post reviews/comments (uses
pull-requests and issues permissions, separate from contents).
- Any push attempt returns 403, surfacing the constraint clearly.
What I'd love to see in response
- Confirmation of the App-vs-workflow permission semantics so I can document it correctly for my team.
- Any plan to add a self-enforced
mode input or similar.
- Confirmation of whether the "implement when claimed-but-missing" behavior is by design or an emergent property.
Thanks for the action — overall I find the review capability extremely valuable and want to give clear feedback on this one rough edge.
Title
claude-code-action pushes commits when only a review was requested; workflow
permissions: contents: readdoes not constrain itBody
Summary
I observed
anthropics/claude-code-action@v1make and push a commit to a PR branch under theclaude[bot]identity, in response to a comment that requested review and verification only — no implementation was requested. Additionally, the workflow YAML'spermissions: contents: readblock did not prevent this push, because the action uses the GitHub App's own credentials (granted at App install time) rather than the workflow's auto-mintedGITHUB_TOKEN.This combination — proactive code-pushing behavior + workflow permissions that don't actually constrain the bot — surprised me as the repo owner. Filing this so the team is aware and so other users can decide whether the behavior matches their expectations.
Reproduction context (anonymized)
@claudementions in PR comments.permissions: contents: read, pull-requests: read, issues: read, id-token: write, actions: read.Sequence that produced the surprise commit
git commit -m "...".pre-commithook ranruff format, which modified some files and exited non-zero. The commit was therefore rejected, but the staged changes remained.git pushreportedEverything up-to-date— because no commit had actually been created. From the developer's perspective the workflow looked successful. This is a known git foot-gun but worth naming as the trigger.@claudesaying (paraphrased) "addressed all items from your last review, please re-verify."claude[bot]. Commit message included the line "Implement missing changes (committed<sha>, pushed)".Two distinct concerns
Concern 1: scope expansion from "review" to "implement" without explicit invocation
My PR comment did not request implementation. It asked for re-verification of fixes I had (incorrectly) claimed to have pushed. A reasonable response would have been: "The fixes you claim to have pushed are not in the current branch HEAD
<sha>— please verify your push succeeded and re-trigger the review."Instead the bot interpreted the discrepancy as a directive to make my comment true by implementing the missing fixes. This is a reasonable design choice for some users, but it should be opt-in rather than the default, especially because:
ruff format --checkstep because the bot does not runpre-commitbefore committing).git blameasclaude[bot], with the human listed asCo-authored-by. For attribution and review-trail purposes this matters.Concern 2: workflow YAML
permissions:block does not constrain the actionThis is the more important point for documentation/clarity. The workflow declared:
A naive reader (including me) interprets this as "the workflow can only read repo contents, not write." This is true for the workflow's auto-minted
GITHUB_TOKEN, but the action does not use that token for code modifications. It uses the GitHub App's own installation token, which has whatever permissions were granted at App install time — typically includingcontents: write.Result: the workflow YAML's permission block is misleading. The expressed intent ("read-only") is not what the bot can actually do.
I'd recommend either:
mode:input on the action (e.g.,mode: review,mode: suggest,mode: implement) that the action self-enforces, regardless of the App's available permissions. Default torevieworsuggest.claude_args:example in the README showing how to disable code-modification capability via the prompt or via tool restrictions.Suggested concrete fixes
modeinput with values likereview,suggest_diff,implement. Defaultrevieworsuggest_diff.implementrequires explicit opt-in.ruff format/prettierwhere present in.pre-commit-config.yaml) before pushing, so its commits don't break CI for trivial format issues.Workaround for now
For users who want the bot to be review-only:
pull-requestsandissuespermissions, separate fromcontents).What I'd love to see in response
modeinput or similar.Thanks for the action — overall I find the review capability extremely valuable and want to give clear feedback on this one rough edge.