Originally published at heycc.cn. This is a mirrored copy — the canonical version is kept up to date at the source.
Using AI in GitHub Actions CI in 2026: The Claude Code Playbook
Putting an AI agent inside your CI pipeline is no longer a novelty experiment. In 2026 it is a concrete pattern: a GitHub Actions job that reads a pull request, reviews the diff, flags security issues, and sometimes pushes a fix commit — all before a human opens the PR. The catch is that the official Claude Code GitHub Action reached its v1 GA release, and that release renamed enough of the input surface that most blog snippets you will find still use the dead @beta form. What follows is the current configuration, the breaking-change mapping, and the security posture that keeps an AI agent with repository write access from becoming a liability — all checked against Anthropic's and GitHub's official docs (dates and links at the end).
What "AI in CI" actually means in 2026
There are three distinct jobs people lump together. Keeping them separate is the first decision you make.
| Pattern | Trigger | What it does | Write access needed |
|---|---|---|---|
| Interactive agent |
@claude mention in an issue/PR comment |
Implements a feature, fixes a bug, answers a question, opens a PR | Yes (contents, PRs, issues) |
| Automated review |
pull_request opened/synchronize |
Reviews the diff and posts inline comments | Only pull-requests: write
|
| Security review | pull_request |
AI-driven vulnerability scan of the diff |
contents: read + pull-requests: write
|
The interactive agent is the most powerful and the most dangerous: it can modify files and push branches. The two review patterns are read-mostly and are where most teams should start. You can run all three in the same repository as separate workflow files.
The v1 action: minimal working workflow
The action is anthropics/claude-code-action@v1. The smallest workflow that does something useful responds to @claude mentions in comments:
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Responds to @claude mentions in comments
That is the entire file. v1 auto-detects its mode: if you supply a prompt, it runs immediately in automation mode; if you omit it on a comment event, it waits for the trigger_phrase (default @claude) and runs interactively. There is no longer a mode: input.
The v1 input surface is deliberately small:
-
prompt— plain-text instructions or a skill invocation like/code-review:code-review. -
claude_args— a passthrough for any Claude Code CLI flag (--max-turns,--model,--append-system-prompt,--allowedTools,--mcp-config). -
anthropic_api_key— required for the direct Claude API path. -
github_token,trigger_phrase,use_bedrock,use_vertex,plugin_marketplaces,plugins.
If you are upgrading from @beta
The GA release introduced breaking changes. If you copied a workflow before 2025, you must edit it. The mapping:
| Old beta input | v1 replacement |
|---|---|
@beta |
@v1 |
mode: "tag" / "agent"
|
removed — auto-detected |
direct_prompt |
prompt |
custom_instructions |
claude_args: --append-system-prompt |
max_turns |
claude_args: --max-turns |
model |
claude_args: --model |
allowed_tools |
claude_args: --allowedTools |
So a beta block with max_turns: "10" and model: "claude-sonnet-5" becomes:
- uses: anthropics/claude-code-action@v1
with:
prompt: "Review this PR for security issues"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--append-system-prompt "Follow our coding standards"
--max-turns 10
--model claude-sonnet-5
Pattern 2: automated review on every PR
To review every pull request without a human typing @claude, trigger on pull_request and pass a skill in prompt. The official example installs the code-review plugin from a marketplace and runs its namespaced skill:
name: Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
For a repo-local skill in .claude/skills/, run actions/checkout first, then pass /skill-name in prompt. If you have already adopted Claude Code locally — see our Claude Code best practices guide — the same skills and CLAUDE.md conventions carry straight into CI.
Pattern 3: AI security review
anthropics/claude-code-security-review is a dedicated action that scans the diff for vulnerabilities and posts findings as PR comments. The documented configuration:
name: Security Review
on: [pull_request]
permissions:
contents: read
pull-requests: write
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 2
- uses: anthropics/claude-code-security-review@main
with:
claude-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
comment-pr: true
It exposes a findings-count output and writes a results JSON file, so you can fail the job on any finding, or read the JSON and gate only on certain severities. Note that the documented example pins to @main — for production you should pin to a commit SHA instead (covered below).
Gating merges on AI findings
A common mistake is assuming the AI check blocks the merge. Anthropic's managed Code Review (research preview for Team/Enterprise) deliberately does not: its check run, named "Claude Code Review," always completes with a neutral conclusion so it never blocks a merge on its own. Findings are posted as inline comments with severity markers — Important, Nit, Pre-existing.
If you want a hard gate, you read the machine-readable severity counts out of the check-run output in your own CI step and decide. A non-zero normal (Important) count means a real bug worth fixing before merge:
- name: Gate on Important findings
run: |
CHECK_RUN_ID=$(gh api \
repos/${{ github.repository }}/commits/${{ github.event.pull_request.head.sha }}/check-runs \
--jq '.check_runs[] | select(.name=="Claude Code Review") | .id')
SEVERITY=$(gh api repos/${{ github.repository }}/check-runs/$CHECK_RUN_ID \
--jq '.output.text | split("bughunter-severity: ")[1] | split(" -->")[0] | fromjson')
IMPORTANT=$(echo "$SEVERITY" | jq '.normal')
[ "$IMPORTANT" -eq 0 ] || exit 1
You also steer the reviewer with two repo files. CLAUDE.md supplies project context, and newly introduced violations of it are flagged as nits. REVIEW.md is review-only and injected as highest-priority instructions — use it to redefine severity, cap nit volume, set skip rules, and add repo-specific checks like "new API routes must have an integration test." That file is the single highest-leverage thing you can add to tune an AI reviewer to your codebase.
Authentication without a static key
The old way: store ANTHROPIC_API_KEY as a repository secret and reference ${{ secrets.ANTHROPIC_API_KEY }}. It works and is fine for getting started. Never hardcode it in the workflow file.
The 2026 way: Workload Identity Federation (WIF), GA on the Claude Platform since June 17, 2026. The action exchanges the workflow's GitHub Actions OIDC token for a short-lived Anthropic access token, so there is no ANTHROPIC_API_KEY secret to create, store, or rotate. Bedrock and Vertex workflows use the same OIDC pattern against your cloud — no static cloud keys. It is not just a permission line, though — you register a federation issuer, service account, and federation rule once in the Claude Console (Settings → Workload identity), then point the action at them:
permissions:
contents: write
pull-requests: write
id-token: write # required to fetch the GitHub OIDC token
jobs:
claude:
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_federation_rule_id: fdrl_xxxxxxxxxxxx
anthropic_organization_id: 00000000-0000-0000-0000-000000000000
anthropic_service_account_id: svac_xxxxxxxxxxxx
# anthropic_workspace_id is optional when the rule targets one workspace
Do not also set anthropic_api_key or claude_code_oauth_token — a static credential takes precedence and the federation inputs are ignored.
Model IDs differ by backend. Direct API's current flagship is claude-sonnet-5. Bedrock prepends a region for cross-region inference (us.anthropic.claude-sonnet-4-6 in Anthropic's own Bedrock example, still on the Sonnet 4.6 generation there). Vertex uses the dated form (claude-sonnet-4-5@20250929). Pass whichever via claude_args: --model ....
The security checklist that actually matters
An AI agent in CI inherits whatever permissions the workflow has. Lock them down with GitHub's secure-use guidance.
| Control | Why it matters | How |
|---|---|---|
Least-privilege GITHUB_TOKEN
|
Default-deny blast radius | Set default token to read-only for contents, then grant the minimum via the permissions: key; anything you omit is set to none
|
| SHA-pin every action | Immutability against a backdoored tag | Replace @v1/@main with a full-length commit SHA from the action's own repo (not a fork) — a SHA-1 collision would be required to forge it |
Never check out untrusted code on pull_request_target
|
That trigger runs privileged, with write access and secrets | Use pull_request for fork PRs; if you must use pull_request_target, do not check out the PR's head |
| Org-level SHA-pinning policy | Stops drift back to mutable tags | Since Aug 2025, admins can enforce SHA pinning via the allowed-actions policy; unpinned workflows fail |
The SHA-pinning point is worth internalizing. Pinning to a full-length commit SHA is currently the only way to use an action as a truly immutable release. @v1 is a moving tag; if the action's repo is compromised, @v1 can be repointed at malicious code, and your AI agent runs it with whatever permissions you granted. The Anthropic actions are reputable, but the principle is the same as any third-party dependency.
The pull_request_target row deserves the concrete version, because the mistake is easy to make by accident. Per GitHub's own security docs, a pull_request_target workflow "runs with elevated trust: the job receives the base repository's GITHUB_TOKEN and access to repository and organization secrets" — unlike plain pull_request, where GitHub "restricts these events to a read-only GITHUB_TOKEN, withholds access to other secrets." Cache access does not follow the same elevated pattern: the same docs note that pull_request_target workflows "have read-only access to the cache in the default branch's scope" and "can restore existing cache entries but cannot create or overwrite them." The exploit path, per GitHub Security Lab's writeup: if that privileged workflow then checks out the PR's head and runs anything from it — a build script, a test command, even a dependency install that executes a Makefile — an attacker who opened the PR from a fork controls what runs, with your repo's secrets and write token available to it. This is exactly the shape an AI-review workflow can fall into if it's adapted from a pull_request example without checking the trigger: the fix, per the same docs, is to use pull_request unless you specifically need the elevated token, and if you do need pull_request_target, treat the checked-out PR content as data to inspect, never as code to execute.
We tested the "omit it, get nothing" claim ourselves
The permissions: table above rests on one specific claim: a scope you don't list is set to none, not to whatever the repo's default happens to be. Rather than take that on faith from the docs, we built a two-workflow test and ran it on a real PR (github-actions-token-permissions-demo, public, reproducible by opening a PR against it yourself):
-
no-perms-test.ymlsetspermissions: {}and tries to post a PR comment with${{ github.token }}. -
write-perms-test.ymlsetspermissions: pull-requests: writeand attempts the identical comment.
Both fired on the same pull request, seconds apart. The zero-permissions run failed with the token rejected before it ever reached the write itself:
GraphQL: Resource not accessible by integration (addComment)
##[error]Process completed with exit code 1.
(run log)
The explicit-write run succeeded, and the resulting PR comment is still visible on PR #1 (run log):
posted by write-perms-test (expected to succeed) — GITHUB_TOKEN with explicit pull-requests:write worked.
Same repo, same event, same commit — the only variable was the permissions: block. That's the least-privilege default in practice, not just in the docs.
When to call the API directly instead
Sometimes you do not want the full agent — you want a single, deterministic LLM call inside a script step (classify a PR, draft release notes). Call the Messages API directly:
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-sonnet-5","max_tokens":1024,"messages":[{"role":"user","content":"Summarize this diff"}]}'
The body requires model, max_tokens, and messages. max_tokens can be set to 0 solely to pre-warm the prompt cache without generating a response; otherwise it is the generation cap. Use the action when you want tool use, multi-turn iteration, and repo awareness; use the raw API when you want one bounded call with predictable cost.
A rollout order that fails safe
- Start read-only. Add the security-review and PR-review patterns first. They cannot break your repo.
-
Add the interactive agent only once permissions are scoped and you trust the
REVIEW.md/CLAUDE.mdguardrails. -
Gate explicitly. The AI check is neutral by design; if you want merges blocked, write the
gh api --jqstep yourself. - Pin SHAs before you scale across repos, ideally enforced by org policy.
- Prefer OIDC/WIF over a long-lived API key once you are past the prototype.
The pattern that wins in 2026 is not "let the AI merge code." It is a tightly scoped agent that reviews, flags, and proposes — running under least privilege, pinned to a SHA, authenticated without a stored key, and gated by a check you control. Reviewing the AI's suggestions before merging is still in the official best-practices list for a reason.
What backs each claim
Read on 2026-06-28 against the documentation below, and re-verified on 2026-07-16 against the live docs and the claude-code-action repo's action.yml/docs/setup.md. The anthropics/claude-code-action@v1 inputs (prompt, claude_args, anthropic_api_key, github_token, trigger_phrase, use_bedrock, use_vertex) and the beta-to-v1 mapping come from the Claude Code GitHub Actions docs; the neutral check-run conclusion, severity-parsing jq filter, and REVIEW.md behavior from the Code Review docs; the security-review inputs from the claude-code-security-review repo; the WIF/federation inputs (anthropic_federation_rule_id, anthropic_organization_id, anthropic_service_account_id) from the action's own docs/setup.md and the Claude Platform WIF-for-GitHub-Actions guide; and the SHA-pinning, least-privilege GITHUB_TOKEN, and pull_request_target guidance from GitHub's secure-use reference and the August 2025 policy changelog. The 2026-07-16 pass corrected a pull_request_target/cache-access quote that had drifted from GitHub's current wording, refreshed the direct-API model ID to the current flagship (claude-sonnet-5; claude-sonnet-4-6 is now a legacy model per the models overview), replaced a guessed severity-gating gh api snippet with the documented one, corrected the max_tokens minimum (0 is valid, for cache pre-warming), and added the WIF setup steps that the auth section had glossed over. The Bedrock/Vertex model IDs (us.anthropic.claude-sonnet-4-6, claude-sonnet-4-5@20250929) still match Anthropic's own current GitHub Actions examples for those backends even though direct API has moved on — worth confirming against the model docs for your account before copying. Token rates are not quoted; only the Messages API request shape is. The "omit it, get nothing" permissions: behavior is not just cited from GitHub's docs — it's independently verified on 2026-07-08 in a real two-workflow test (see above), with both Actions run logs left public rather than pasted as unlinked text.
Sources
- Claude Code GitHub Actions (official docs)
- Use WIF with GitHub Actions (Claude Platform docs)
- Claude models overview — current and legacy model IDs
- GitHub Code Review — severity gating and REVIEW.md (official docs)
- anthropics/claude-code-security-review (GitHub)
- GitHub Actions secure-use reference: SHA pinning, GITHUB_TOKEN permissions, pull_request_target
- GitHub Docs — Securely using pull_request_target
- GitHub Security Lab — Preventing pwn requests
- GitHub Actions policy now supports SHA pinning (changelog, Aug 2025)
- Claude Messages API reference


Top comments (0)