* Add agent-approval-check composite action Require N human approvals on PRs that contain agent-authored commits. Posts an agent-approval-check commit status that repos mark as a required check on protected branches. This is a sanitized port of the check Anthropic runs internally on every agent-authored PR — same detection rules, /approve <sha> comment flow, sibling-PR-same-SHA guard, and fail-closed semantics, with the Anthropic-specific path exemptions and kill-switch removed and config moved to action inputs. Co-Authored-By: Claude <noreply@anthropic.com> * Drop stray internal acronym from comment * agent-approval-check: require write-access approvers, pin deps, pagination + doc fixes 🏠 Remote-Dev: homespace * agent-approval-check: prettier 🏠 Remote-Dev: homespace * agent-approval-check: verify approver write permission via REST; commits(last:100); docstring 🏠 Remote-Dev: homespace * agent-approval-check: use headRefOid; drop pull_request_review trigger and correct threat-model docs 🏠 Remote-Dev: homespace * agent-approval-check: stale-notification wording, no-retry-on-4xx, docstring API-call count 🏠 Remote-Dev: homespace * agent-approval-check: fail-closed sibling guard on commits-ordering edge; drop stale 'reviewed' from README 🏠 Remote-Dev: homespace * agent-approval-check: drop hardcoded API-call counts from logs; clarify author write-access requirement in README 🏠 Remote-Dev: homespace * agent-approval-check: count all agent-email commits (close-reopen bypass); validate REQUIRED_APPROVALS>=1; exempt_head_branches warning 🏠 Remote-Dev: homespace --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Octavian Guzu <oct@anthropic.com>
Agent Approval Check
Require N human approvals on any pull request that contains commits authored by an AI agent (Claude, Claude Code, or any bot identity you configure). PRs without agent activity are unaffected.
This is the same gate Anthropic runs internally on every agent-authored PR.
What it does
When a PR is opened, pushed to, or commented on, this action:
- Scans the PR's commits, author, and reviews for the configured agent
identities (committer email, bot login, or an
APPROVEDreview from a bot). If none are found it postssuccess: No agent activityand stops. - Counts distinct human approvals: the latest
APPROVEDreview per login, plus any/approve <head-sha>comment whose SHA matches the current head. Only users with write access to the repo count (verified per-user via the collaborators permission API); agent and excluded-bot logins never count. - Posts an
agent-approval-checkcommit status (successonce the count reachesrequired_approvals, otherwisepending) and a sticky PR comment explaining what's still needed. - Re-evaluates on every new push or comment. A push moves the head SHA,
so earlier
/approve <old-sha>comments are flagged stale. Approving reviews still count toward the threshold — they're picked up the next time the workflow runs (on push or/approve); they just don't trigger a run on their own.
Mark agent-approval-check as a required status check on your protected
branches and GitHub will refuse to merge until it's green.
Setup
Copy examples/agent-approval-check.yml
into .github/workflows/ in your repo, then add agent-approval-check to the
required status checks on your protected branch.
This action is designed to run alongside GitHub's native branch protection, not replace it. On the same protected branch you should also:
- Require at least 1 approving review from someone with write access.
- Enable Dismiss stale pull request approvals when new commits are pushed.
name: agent-approval-check
on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
statuses: write
jobs:
check:
if: github.event_name != 'issue_comment' || github.event.issue.pull_request
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action/agent-approval-check@main
with:
required_approvals: 2
agent_emails: noreply@anthropic.com
agent_logins: claude[bot],claude-code[bot]
Inputs
| Input | Default | Meaning |
|---|---|---|
required_approvals |
2 |
Distinct human approvals needed. |
agent_emails |
noreply@anthropic.com |
Committer emails that mark a commit agent-authored. |
agent_logins |
claude[bot],claude-code[bot] |
Logins treated as agents (PR author or approving reviewer). |
excluded_approvers |
(empty) | Logins whose approvals never count. |
exempt_head_branches |
(empty) | Head-branch globs that auto-pass. ⚠️ Leave empty — branch names are attacker-controlled, so this is not a safe place to encode trust. |
exempt_path_prefixes |
(empty) | PRs touching only these prefixes auto-pass. |
protected_bases |
(default branch) | Base branches this check gates (see threat model). |
config_file |
(empty) | Path to an agent-identities YAML replacing the inline inputs. See the warning below. |
docs_url |
this README | Link in the PR comment footer. |
github_token |
${{ github.token }} |
Needs statuses:write + pull-requests:write. |
⚠️
config_fileand checkout: if you setconfig_file, your workflow must check out the base branch to read it (the default behaviour ofactions/checkoutunderpull_request_target). Never check out the PR head ref — doing so would let the PR author control the config and bypass this check.
Approving
A human counts as an approver by either:
- submitting a normal GitHub Approve review, or
- commenting
/approve <sha>where<sha>is the current head commit (12–40 hex chars). This path lets the PR author — who can't approve their own PR in GitHub's UI — vouch for commits an agent pushed on their behalf. The author's/approveis subject to the same write-access verification as any other approver, so a fork-PR author without write access on the base repository cannot self-count. The author counts as one approval; the remaining approvals must come from other reviewers with write access.
Threat model
- Tamper-proof triggers.
pull_request_targetandissue_commentrun the workflow file from the base/default branch, so the PR under review cannot edit this check.pull_request_reviewdoes not share this property — it runs from the merge ref — so the example workflow omits it; native Approve reviews are picked up on the next synchronize or/approvecomment. This tamper-resistance assumes the workflow file itself is protected: an actor who can push workflow changes to the default branch can spoof any required status check, including this one, so protect.github/workflows/via branch protection or CODEOWNERS. - Fail-closed. Any unhandled error exits non-zero; the required status stays non-success and the PR stays blocked. PRs with >100 commits are treated as agent-authored because the full commit list can't be verified.
- Sibling-PR guard. Commit statuses attach to a SHA, not a PR. The
action refuses to post a status on a PR whose base isn't in
protected_bases, and withholdssuccesswhile another open PR to a protected base shares the same head commit — otherwise a green status on one PR would also unblock the other. - No checkout of PR code. The action never checks out the PR's branch;
it reads PR metadata via the GitHub API, so the usual
pull_request_targetcode-execution risk does not apply.