CI Reviews & Handoff Automation
Run Claude Code in GitHub Actions to review POC PRs, summarize metadata deltas between demos, and generate handoff docs on every tag. Zero-click artifacts for the customer.
The last SE mile of any POC is handoff. Customer's architect wants a metadata inventory. Customer's admin wants a release-notes doc. Customer's security team wants a permission diff. Internally, your delivery team wants a clean PR review before you even demo. Claude Code running in CI (GitHub Actions, GitLab CI, Bitbucket Pipelines) closes all of that without eating your Friday.

Why CI and not just local
Local scripts (the Headless Automation page) work for you. CI works for everyone: the customer's fork, the internal delivery team, the archived POC branch three months later. Same prompts, reproducible environment, no "works on my machine".
The anatomy of a Claude Code GitHub Action
The setup is boring and short:
# .github/workflows/claude-pr-review.yml
name: Claude PR Review
on:
pull_request:
branches: [main]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # need full history for diff context
- name: Install Claude Code
run: |
curl -fsSL https://claude.ai/install.sh | bash
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install sf CLI
run: npm install -g @salesforce/cli
- name: Run review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: ./scripts/ci-review.sh ${{ github.event.pull_request.number }}One secret (ANTHROPIC_API_KEY), one shell script, and you have a Claude-powered reviewer on every PR.
The review script
#!/usr/bin/env bash
# scripts/ci-review.sh
set -euo pipefail
PR_NUMBER="${1:?PR number required}"
# Get the diff
git diff --unified=5 origin/main...HEAD > /tmp/pr.diff
# Review it
REVIEW=$(claude -p "$(cat <<'EOF'
You are reviewing a pull request on a Salesforce POC repository.
The diff is in /tmp/pr.diff. Check for:
1. Profiles or permission sets that widen access inappropriately
2. Apex without test coverage
3. SOQL in loops or other governor-limit anti-patterns
4. Agent metadata (.genAiPlugin, .genAiPromptTemplate) changes that widen scope
5. Hardcoded IDs or credentials
6. Data 360 DLO/DMO changes that could break existing segments
Output as markdown. Each finding: severity (blocker/warning/nit), file:line, one-sentence description, one-sentence fix.
Return a short "Looks good" line if nothing found.
EOF
)" \
--allowedTools "Read" \
--dangerously-skip-permissions \
--model sonnet)
# Post to the PR
gh pr comment "$PR_NUMBER" --body "## Claude review
$REVIEW"Two things to notice. --allowedTools "Read" is the seatbelt: Claude can see files but cannot edit or run anything in CI. --dangerously-skip-permissions is fine because CI is ephemeral and has no sf auth to a real org.
Four ready-to-use Actions
Permission-diff reviewer
Fires on any PR that touches .profile-meta.xml or .permissionset-meta.xml:
on:
pull_request:
paths:
- 'force-app/main/default/profiles/**'
- 'force-app/main/default/permissionsets/**'Script:
git diff origin/main...HEAD -- 'force-app/main/default/profiles/**' \
'force-app/main/default/permissionsets/**' \
| claude -p "$(cat <<'EOF'
Analyze this permission delta. For each change:
- Which profile/permset is affected
- What access is gained or lost
- Risk level for a customer POC about to move to production
Flag any 'Modify All' or 'View All' additions as blockers.
EOF
)" --allowedTools "Read"Post as a PR comment. Customer security teams love this. Your delivery lead loves it more.
Agent metadata reviewer
Triggered when .genAiPlugin or .genAiPromptTemplate changes:
git diff origin/main...HEAD -- 'force-app/main/default/genAiPlugins/**' \
'force-app/main/default/genAiPromptTemplates/**' \
| claude -p "$(cat <<'EOF'
Review these Agentforce metadata changes. Check:
- Topic descriptions are customer-safe (no internal codenames)
- Prompt templates don't leak system instructions in user-visible output
- New actions have appropriate invocation guards
- Topic overlaps that could cause routing confusion
Output markdown. Flag blockers that would be visible in the customer demo.
EOF
)" --allowedTools "Read"Release-notes generator on tag
Every time you tag a POC milestone (v0.3.0-demo2), generate release notes:
on:
push:
tags: ['v*']
jobs:
release-notes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- # install claude, sf...
- run: |
PREV=$(git describe --tags --abbrev=0 HEAD^)
git log "$PREV..HEAD" --pretty=format:'%s (%an)' > /tmp/commits.txt
claude -p "Convert these commits into customer-facing release notes. Group by theme (Agent, Data 360, UI, Integrations). Plain English, no jargon." \
--allowedTools "Read" \
< /tmp/commits.txt \
> RELEASE_NOTES.md
gh release create "${GITHUB_REF#refs/tags/}" RELEASE_NOTES.md --notes-file RELEASE_NOTES.mdPOC handoff doc writes itself.
Nightly regression-sweep-to-Slack
Runs the Agentforce test suite against a shared UAT nightly and posts a triage to Slack. This one needs sf auth, so it uses a JWT connected app and encrypted secrets:
on:
schedule:
- cron: '0 13 * * 1-5' # 6 AM PT weekdays
jobs:
sweep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- # install claude, sf
- name: Auth to UAT
env:
SF_JWT_KEY: ${{ secrets.SF_JWT_KEY }}
run: |
echo "$SF_JWT_KEY" > /tmp/server.key
sf org login jwt \
--username poc-ci@acme.com.uat \
--jwt-key-file /tmp/server.key \
--client-id ${{ secrets.SF_CONSUMER_KEY }} \
--alias acme-uat --set-default
- name: Run sweep
run: ./scripts/nightly-agent-sweep.sh
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}See Connected Apps for the JWT flow setup.
Cost and runtime discipline
CI minutes and Claude turns both cost real money. Four rules:
Every CI job that calls claude -p should log the cost:
RESULT=$(claude -p "..." --output-format json)
COST=$(echo "$RESULT" | jq -r .total_cost_usd)
echo "::notice::Claude turn cost: \$$COST"That shows up in the Actions summary. Check it weekly. A PR review costing $0.30 is fine. $3.00 means your prompt or input is too big.
paths: filters on your workflow are free cost discipline. Don't run the permission-diff reviewer on every PR, only on PRs that touch permission files.
Same with git diff inside the script. Pipe only the relevant paths, not the whole diff.
Reviews that find blockers should fail the check:
if echo "$REVIEW" | grep -q "blocker"; then
echo "::error::Blockers found in Claude review"
exit 1
fiDon't let the CI check go green while the comment says "blocker". The merge button shouldn't be available.
If your prompt references large fixed context (e.g., an architecture doc), put it in the repo and reference it in the prompt as a file. Claude Code reads the file once per turn. Caching the setup steps (uses: actions/cache@v4 for ~/.local/bin) also saves 30+ seconds per run.
Secrets hygiene
Three ground rules for Claude-Code-in-CI:
ANTHROPIC_API_KEYin Actions secrets only. Never in the repo. Never in a public Gist. Rotate quarterly.sfJWT keys in Actions secrets, never in plaintext. Write the key to/tmpat runtime, and the runner is ephemeral.- Limit
--allowedToolsruthlessly. CI almost always only needsRead. NoEdit, noBash, noWriteunless the job's entire purpose is to write one file and commit it.
Never attach production org auth to CI
CI jobs are a fine target for automation, but they should only ever reach scratch orgs, UAT sandboxes, or read-only integrations. Never give CI a way to write to customer production. Use an environment variable scoped to a non-prod environment, gated by environment: rules that require manual approval for anything production-shaped.
Full PR review + auto-fix flow
The most advanced shape: Claude reviews the PR, and on minor findings (nits), proposes a fix as a follow-up commit. This requires write permissions on contents: write and careful --allowedTools:
permissions:
contents: write
pull-requests: write# After the review runs and posts the comment, if no blockers:
if ! echo "$REVIEW" | grep -q "blocker"; then
claude -p "Apply the nits from this review as minimal commits. Don't touch anything flagged warning or blocker." \
--allowedTools "Read,Edit,Write,Bash(git:*)" \
< /tmp/review.md
git push origin HEAD
fiUse with discretion. The more you let CI write back to the branch, the more you need a human reviewer on the result.
What this unlocks for the customer
By the end of a POC that used this pattern from week one:
- Every PR comment thread is a searchable record of Claude's review + the human's response. Great for handoff.
- Every tag has a generated release-notes doc. Great for customer's release manager.
- Every merge to main triggers a metadata-inventory doc update. Great for customer's architect.
- Nightly sweeps caught regressions 8 hours before the morning standup. Great for your team's sleep.
None of that is possible with Cursor alone. It needs a terminal, a shell, and an API key. That is the Claude Code advantage.
Next
- Headless Automation is the local-machine counterpart.
- CLI Composition is the primitive everything here is built on.
- Hooks & Commands for team-shared slash commands that mirror your CI jobs.
Checkpoints & Time Travel
Resume sessions by ID, rewind to any prior turn, branch a POC session into parallel experiments, and recover from demo-day disasters without losing context.
Agent vs. Builder vs. Script
A decision page for the most common Agentforce question. Agentforce Builder, Agent Script, or Prompt Builder?