Problem
Since #622, both Claude review lanes skip every event whose github.actor ends in [bot]:
!endsWith(github.actor, '[bot]')
(claude-review.yml at v0.29.1, line 113; the security lane carries the same clause.)
That condition keys on the pusher, not the PR author. When a GitHub App such as the Cursor agent (cursor[bot]) pushes to a same-repo PR a human org member opened, every synchronize and ready_for_review event has github.actor = cursor[bot], so neither lane ever reviews the PR. #622's own commit message notes this consequence: it stopped reviewing cursor[bot] pushes that #441/#443 had admitted.
Observed impact in melodic-software/claude-code-plugins on 2026-09-28: 182 open PRs, nearly all Cursor-driven, and every review lane run on them was SKIPPED. Local reviews then found destructive-command guard bypasses in 2 of the 3 guard-changing PRs (claude-code-plugins #5068, #4788) that no lane had seen.
Proposed fix
Gate the PR author on not being a bot, and admit a bot pusher only by explicit name, with the same list passed to the action:
# job if:
${{ vars.CLAUDE_LANES_DISABLED != 'true'
&& (github.event_name != 'pull_request'
|| (github.event.pull_request.head.repo.full_name == github.repository
&& github.event.pull_request.draft == false
&& !endsWith(github.event.pull_request.user.login, '[bot]')))
&& (!endsWith(github.actor, '[bot]')
|| contains(format(',{0},', inputs.allowed-bots), format(',{0},', github.actor))) }}
# claude-code-action step:
with:
allowed_bots: ${{ inputs.allowed-bots }} # e.g. 'cursor[bot]'; never '*'
Why each clause:
!endsWith(github.event.pull_request.user.login, '[bot]') keeps Dependabot-authored PRs skipped, including when a human pushes to one. GitHub's own Dependabot automation examples key on user.login.
- The comma-wrapped
contains on github.actor avoids substring matches and checks the same identity claude-code-action checks (GITHUB_ACTOR), so the workflow gate and the action cannot disagree, re-runs included.
allowed_bots is required: at v1.0.235, checkHumanActor throws for any non-User actor not in allowed_bots, so admitting cursor[bot] in if: alone makes the job fail instead of review.
- The existing same-repo and draft guards stay. A same-repo head branch means the pusher had write access.
- A new
allowed-bots input, empty by default, keeps every current consumer's behaviour unchanged.
Deliberately not proposed: an author_association clause
#443 gated on OWNER,MEMBER,COLLABORATOR. Org members with private membership are reported to appear as CONTRIBUTOR in Actions payloads (flutter/flutter#101012, apache/arrow#34381), and nobody has captured what this org's payload reports. The clause could silently skip exactly the PRs it is meant to review. If it is wanted, settle it first with a one-off toJSON(github.event.pull_request.author_association) echo in a same-repo PR run.
Acceptance criteria
Related
Problem
Since #622, both Claude review lanes skip every event whose
github.actorends in[bot]:!endsWith(github.actor, '[bot]')(
claude-review.ymlat v0.29.1, line 113; the security lane carries the same clause.)That condition keys on the pusher, not the PR author. When a GitHub App such as the Cursor agent (
cursor[bot]) pushes to a same-repo PR a human org member opened, everysynchronizeandready_for_reviewevent hasgithub.actor = cursor[bot], so neither lane ever reviews the PR. #622's own commit message notes this consequence: it stopped reviewingcursor[bot]pushes that #441/#443 had admitted.Observed impact in
melodic-software/claude-code-pluginson 2026-09-28: 182 open PRs, nearly all Cursor-driven, and every review lane run on them was SKIPPED. Local reviews then found destructive-command guard bypasses in 2 of the 3 guard-changing PRs (claude-code-plugins #5068, #4788) that no lane had seen.Proposed fix
Gate the PR author on not being a bot, and admit a bot pusher only by explicit name, with the same list passed to the action:
Why each clause:
!endsWith(github.event.pull_request.user.login, '[bot]')keeps Dependabot-authored PRs skipped, including when a human pushes to one. GitHub's own Dependabot automation examples key onuser.login.containsongithub.actoravoids substring matches and checks the same identityclaude-code-actionchecks (GITHUB_ACTOR), so the workflow gate and the action cannot disagree, re-runs included.allowed_botsis required: at v1.0.235,checkHumanActorthrows for any non-User actor not inallowed_bots, so admittingcursor[bot]inif:alone makes the job fail instead of review.allowed-botsinput, empty by default, keeps every current consumer's behaviour unchanged.Deliberately not proposed: an
author_associationclause#443 gated on
OWNER,MEMBER,COLLABORATOR. Org members with private membership are reported to appear asCONTRIBUTORin Actions payloads (flutter/flutter#101012, apache/arrow#34381), and nobody has captured what this org's payload reports. The clause could silently skip exactly the PRs it is meant to review. If it is wanted, settle it first with a one-offtoJSON(github.event.pull_request.author_association)echo in a same-repo PR run.Acceptance criteria
allowed-botsinput (default empty) and use the condition above.allowed_bots.cursor[bot]push to a human-owned same-repo PR runs both lanes, and a Dependabot PR still skips. This live run is the independent check; the claims above rest on the action's source at v1.0.235 and GitHub's docs only.melodic-software/standardsrunner-policy contract permits callers to passallowed-botsat the new pin.node --testsuites cover the four actor/author combinations: human/human, bot-listed/human, bot-unlisted/human, any/dependabot.Related
allowed_botsfix.cursor[bot]was never deliberated there.claude[bot]andmelodic-ai[bot]belong inallowed-bots.