What You Need to Know
Claude Code is normally an interactive developer tool. In interactive mode it expects keyboard input and shows a conversational interface. A CI/CD pipeline is an automated job that runs when an event triggers it, for example a code change. A run with no person present is called headless. In a pipeline, Claude Code becomes an automated tool for review and generation.
This lesson uses one made-up example throughout: an off-licence chain with an online shop. Each pull request (PR) on the shop's code starts a pipeline. The team wants Claude Code to review each PR in that pipeline.
The -p Flag: Non-Interactive Mode
A CI pipeline has no keyboard. Without the -p flag, the job hangs forever. It waits for input that never arrives. The off-licence's first review job hung this way.
# WRONG — hangs in CI
claude "Analyse this pull request for security issues"
# CORRECT — runs non-interactively
claude -p "Analyse this pull request for security issues"The -p flag (also --print) switches Claude Code to print mode. Claude Code processes the prompt, writes the result to stdout and exits, without waiting for user input. Running Claude Code in CI with -p prevents interactive input hangs. The off-licence adds -p, and its job finishes.
Key Concept
For CLI calls in CI, use claude -p. Logs that show Claude waiting for input mean that the flag is missing.
Structured Output for CI
In CI, no human reads the output. Automated systems process the output to post inline PR comments, update dashboards or trigger downstream workflows. So the output has to be machine-parseable. The off-licence wants each finding posted on the exact line that the finding refers to. Two flags work together to do this.
--output-format json: returns the output of the run as a JSON envelope, which is one JSON object, not as human-readable text. The envelope holds result text, session ID, and cost and usage metadata.--json-schema: validates the agent's final output against a JSON Schema (print mode only)
claude -p \
--output-format json \
--json-schema '{"type":"object","properties":{"findings":{"type":"array","items":{"type":"object","properties":{"file":{"type":"string"},"line":{"type":"integer"},"severity":{"type":"string"},"message":{"type":"string"}}}}}}' \
"Review this PR for security issues"The data that matches the schema appears in the envelope's structured_output field. Extract the data with jq '.structured_output', not from the top level. You can use the validated findings in four ways.
- Parse programmatically
- Post as inline PR comments at the exact file and line
- Filter by severity for different notification channels
- Track across review runs
Session Context Isolation
When Claude writes code in a session, it collects reasoning context: why it chose this approach, which trade-offs it considered, which alternatives it rejected. A review in the same session keeps all of that context. So the review is less likely to question decisions that Claude already justified to itself. That makes the generating session less effective at reviewing its own changes, and the effect is measurable.
The fix is a separate Claude Code call for the review. That call has no access to the reasoning context of the generation session. The review call judges only the code, without the bias of prior justification. At the off-licence, one claude -p call writes a new age check for checkout. A second, independent call reviews that age check.
# Step 1: Generate code (session A)
claude -p "Implement the authentication middleware"
# Step 2: Review code (session B — independent, no shared context)
claude -p "Review the authentication middleware for security issues, error handling gaps, and edge cases"Session context isolation connects to Domain 4 (multi-instance review architectures) and Domain 5 (context management).
Incremental Review Context
Automated reviews run on every push. Without context about earlier reviews, each run analyses the whole PR from the start and reports the same findings again. A fixed issue disappears from the findings automatically, because the changed code no longer triggers it. The issues that keep returning are the issues that the developer saw and chose not to change. A re-scan with no context cannot distinguish those issues from new problems, so it flags them on every push.
Suppose an off-licence developer keeps a hard-coded postcode list on purpose. Every review flags the list again. The fix is to include prior findings in context and tell Claude to report only new or still-unaddressed issues.
claude -p \
--output-format json \
"Review this PR. Here are the findings from the previous review:
${PREVIOUS_FINDINGS}
Report ONLY:
1. New issues not in the previous findings
2. Issues from the previous findings that are still present
Do NOT re-report previous findings the developer has already reviewed and chosen not to act on."Duplicate comments reduce developer trust. If every push brings the same five comments whether or not the issues were fixed, developers stop reading the comments. Incremental review context keeps the share of useful comments high.
CLAUDE.md for CI Context
In CI, Claude Code reads the project's CLAUDE.md files exactly as it does interactively. So CLAUDE.md is how you give a CI run project-specific context.
- Testing standards: what makes a good test, what patterns to follow, what to avoid
- Available fixtures: which test fixtures exist, how to use them, what data they contain
- Review criteria: what constitutes a critical finding vs a minor style issue
- Existing test coverage: what is already covered, to avoid suggesting duplicate tests
Without this context, test generation in CI produces low-value boilerplate. With this context, generated tests follow the team's patterns and add genuine coverage. If the off-licence's CLAUDE.md names its test factories, CI runs use them.
# .claude/CLAUDE.md — CI-relevant section ## Testing Standards - Tests must use the factory pattern from test/factories/ for data creation - Integration tests connect to the test database via test/setup/db.ts - Do not test private implementation details — test public API contracts - Coverage target: 80% branch coverage for new code - Available fixtures: test/fixtures/users.json, test/fixtures/orders.json
CLI Flags Reference
Other flags besides -p control a headless run: the output format, the system prompt, and the scope of permissions and tools. These flags work with claude -p in CI and with the interactive claude command.
System prompt flags. Claude Code has four.
| Flag | Effect |
|---|---|
--system-prompt "<text>" | Replaces the entire default system prompt |
--system-prompt-file <path> | Replaces the default prompt with a file's contents |
--append-system-prompt "<text>" | Appends text to the default prompt |
--append-system-prompt-file <path> | Appends a file's contents to the default prompt |
The default system prompt holds Claude Code's tool guidance, safety instructions and coding conventions. Appending keeps all of that content, so you only supply what differs. Use appending when Claude should stay a coding assistant that also follows your extra rules. Replacing drops the entire default prompt, so you are responsible for everything that the task still needs. Use replacing when the identity or permission model differs from Claude Code's, such as a non-coding agent in a pipeline that no human watches. The off-licence's reviewer stays a coding assistant, so it appends.
Headless output and limits (print mode).
| Flag | Effect |
|---|---|
--output-format text|json|stream-json | Output shape for -p; json and stream-json are machine-parseable |
--input-format text|stream-json | Input shape for -p |
--json-schema '<schema>' | Schema-validated output for -p; with --output-format json it lands in the envelope's structured_output field |
--max-turns <n> | Cap the number of agentic turns, then exit |
Permissions, tools, and context.
| Flag | Effect |
|---|---|
--permission-mode <mode> | Start in default, acceptEdits, plan, auto, dontAsk, bypassPermissions, or manual (an alias for default, v2.1.200+) |
--allowedTools "<rules>" | Tools that run without a permission prompt, e.g. "Bash(git diff *)" "Read" |
--disallowedTools "<rules>" | Deny rules; a bare tool name removes the tool from context entirely |
--tools "Bash,Edit,Read" | Restrict which built-in tools are available at all |
--add-dir <path> | Add a directory Claude may read and edit (grants file access, not configuration discovery) |
--model <alias|name> | Set the session model (sonnet, opus, or a full model name) |
Session and start-up. -c / --continue resumes the most recent conversation in the current directory, and -r / --resume <id|name> resumes a specific session. --bare is minimal mode. It skips auto-discovery of hooks, skills, plugins, MCP servers, auto memory and CLAUDE.md, so scripted calls start faster. Claude keeps only the Bash and file read/edit tools. Use it for a fast, predictable scripted run that needs no project configuration. The off-licence's review needs its CLAUDE.md, so it skips --bare.
GitHub Actions: Claude Code as a Workflow Step
Beyond the guide
Task Statement 3.6 describes CI/CD integration through the -p flag, output formats and session isolation. It never names GitHub Actions. Verified against the GitHub Actions page of the Claude Code docs on 16 September 2026.
Anthropic's pipeline integration is a GitHub Action, anthropics/claude-code-action@v1. The action runs Claude Code non-interactively inside a workflow job. It is built on the Agent SDK and accepts the same CLI flags as a headless claude -p run. Two settings control how a workflow behaves.
Mode follows the prompt input. With no prompt, the action runs in interactive mode. It waits for the trigger phrase (@claude by default) in an issue or PR comment and answers there. With a prompt, the action runs in automation mode without waiting for a mention. It runs on whatever event started the workflow, such as a PR opening or a cron schedule. The action writes its result to the workflow run log, unless the prompt tells it to post somewhere and it has a tool for posting. With a plain-text prompt, Claude has no shell or GitHub API access until the workflow grants the tools it needs. The workflow grants them with --allowedTools in claude_args or a permissions.allow rule in the settings input. The off-licence wants a review on every new PR. So it sets a prompt that asks for comments and grants a tool that can post them.
CLI flags go through claude_args. There is no separate action input for the system prompt, the tool allowlist or the turn cap. You pass the flags from the reference tables as one string: --append-system-prompt, --allowedTools, --max-turns, --model, --mcp-config. So to add review criteria without replacing the default behaviour, use --append-system-prompt inside claude_args, as on the command line. Of the pre-v1 inputs, custom_instructions became --append-system-prompt and direct_prompt became prompt. The mode input disappeared because the action now infers the mode.
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--append-system-prompt "Review against the checklist in CLAUDE.md. Report only critical findings."
--allowedTools "Read,Grep,Glob,Bash(git diff *)"
--max-turns 10How Claude gets the files: actions/checkout puts the repository on the runner, so without that step there is nothing on disk to read. Claude reads the PR itself through the GitHub tools that the action provides. The permissions block limits what Claude can write back. The action needs id-token: write for its default GitHub App authentication. actions: read lets Claude see CI results on the PR. The credential is a repository secret, never a literal in the file. The secret is anthropic_api_key for an API key or claude_code_oauth_token for a subscription token.
Parallel Sessions with Git Worktrees
Beyond the guide
Verified against the worktrees page of the Claude Code docs on 16 September 2026.
A git worktree is a second working directory for the same repository. It has its own branch and shares the same history. If each Claude Code session runs in its own worktree, edits in one session never change files in another session. So three sessions can extract three services from one monolith at the same time. They can commit to the same repository without overwriting each other's files. Each session also gets its own full context budget. The off-licence could split its shop this way into stock, orders and payments, with one session for each part.
claude --worktree <name> creates a worktree under .claude/worktrees/<name>/ on a branch called worktree-<name>. Then it starts the session inside that worktree. git worktree add ../project-feature -b feature does the same manually, with a path and a branch that you choose.
Nothing locks a shared file. Sessions coordinate by order: the first session merges, and the second session rebases onto the result before it changes the shared file. A custom subagent can run in its own worktree with isolation: worktree in its frontmatter. So edits from parallel subagents do not overwrite each other either.
Providing Existing Tests to Avoid Duplication
When test generation runs in CI, include the existing test files in context. Without the existing test files, Claude Code may suggest tests that already exist. Those duplicate tests waste developer review time. With the existing test files, Claude can find coverage gaps instead of duplicating existing scenarios. If the off-licence already tests its basket totals, the run looks for gaps elsewhere.
Batch API vs Real-Time for CI Workflows
The Message Batches API offers 50% cost savings, but processing takes up to 24 hours with no guaranteed latency SLA. This trade-off gives a clear rule for which API to choose.
| Workflow type | API choice | Reason |
|---|---|---|
| Pre-merge checks (blocking) | Real-time (synchronous) | Developers wait for results |
| Overnight technical debt reports | Batch API | Not time-sensitive, 50% savings |
| Weekly code audit | Batch API | Scheduled, latency-tolerant |
| Nightly test generation | Batch API | Runs overnight, reviewed next morning |
Pre-merge checks are blocking workflows: developers cannot merge until the check completes. The Batch API gives no latency guarantee, so it is unsuitable for pre-merge checks. The off-licence's PR review runs in real time, while its weekly code audit can use the Batch API.
In the exam
The exam tests five concepts under Task Statement 3.6. The -p flag is the single most directly tested fact in Domain 3. It is Question 10 in the official sample questions. The question shows a CI job that hangs while Claude waits for input. The question asks for the fix. The answer is always -p: pure memorisation. CLAUDE_HEADLESS=true and --batch do not exist. Redirecting stdin from /dev/null does not properly address Claude Code's interactive mode.
The exam tests session isolation specifically in CI/CD scenarios. The exam also expects you to know the headless flags, and it tests append versus replace. Sample Question 11 tests the Batch API decision directly.
GitHub Actions and worktrees are outside the guide, but they are worth knowing. Three readers who sat the exam report four or five questions on GitHub Actions. The guide does not mention worktrees. But large refactors run on parallel Claude Code sessions, so the practice questions include worktrees.
Exam Traps
Choosing CLAUDE_HEADLESS=true, --batch or stdin redirection to stop a CI job waiting for input
CLAUDE_HEADLESS and --batch do not exist, and redirecting stdin is a Unix workaround that does not properly address Claude Code's command syntax. The -p flag processes the prompt, writes the result to stdout and exits without waiting for user input.
Assuming self-review in the same session is as effective as independent review
The same session retains reasoning context from code generation, making it less likely to question its own decisions. An independent review instance without that context is more effective at finding issues.
Using the Batch API for pre-merge CI checks
The Message Batches API has up to 24-hour processing time with no latency SLA. Pre-merge checks are blocking workflows where developers wait for results. Use real-time API for blocking checks; batch API for overnight or weekly non-blocking analysis.
Reviewing only the new commit's diff, or resuming the first run's session, to stop repeated findings
An incremental diff hides findings the contributor has not yet addressed, and a resumed session carries the first run's stale tool results over code that has since changed. Pass the previous findings into a fresh whole-PR run and ask for only new or unaddressed issues.
Practice Scenario
A CI pipeline script runs 'claude “Analyse this PR”' but the job hangs indefinitely. Logs show Claude Code is waiting for interactive input. What is the correct fix?
Build Exercise
Set Up a CI/CD Pipeline with Claude Code
What you'll learn
- Use the -p flag for non-interactive Claude Code execution in CI pipelines
- Configure structured JSON output with --output-format json and --json-schema
- Implement session context isolation between code generation and review
- Set up incremental review to eliminate duplicate findings across runs
- Provide project context via CLAUDE.md for CI-invoked Claude Code
- Write a CI script that runs Claude Code with the -p flag for non-interactive PR analysis
Why: The -p flag is the single most directly testable fact in Domain 3. Without it, the CI job hangs indefinitely waiting for interactive input. This is Question 10 in the official sample questions.
You should see: A CI script (GitHub Actions YAML, GitLab CI, or similar) that invokes claude -p with a review prompt. The job completes successfully without hanging. The output is printed to stdout and captured by the CI system.
- Add --output-format json and --json-schema to produce structured findings with file, line, severity, and message fields
Why: CI output must be machine-parseable. Automated systems need structured JSON to post inline PR comments, filter by severity, and track findings across runs. Human-readable text output cannot be reliably parsed by downstream tools.
You should see: The Claude Code output is a JSON envelope whose structured_output field conforms to the specified schema. Each finding has file, line, severity, and message fields. Piping the output to jq .structured_output extracts the validated data without errors.
- Configure the pipeline to parse the JSON output and post findings as inline PR comments
Why: Inline PR comments at exact file and line numbers provide actionable feedback. Generic PR-level comments are ignored. Structured JSON output makes precise inline commenting possible.
You should see: Each finding from the JSON output appears as an inline comment on the PR at the exact file and line number. Severity levels are visible. Developers can see the finding in context alongside the code it references.
- Add a section to CLAUDE.md documenting testing standards, available fixtures, and review criteria for CI-invoked Claude Code
Why: Claude Code reads CLAUDE.md in CI just as in interactive mode. Without project context, CI-invoked test generation produces low-value boilerplate. With testing standards and fixture documentation, generated tests follow team patterns.
You should see: The CLAUDE.md file contains a clearly marked CI-relevant section with testing standards, available fixture paths, and review severity criteria. CI-invoked Claude Code produces tests using the documented factories and fixtures rather than generic boilerplate.
- Set up two separate Claude Code invocations: one for code generation and an independent one for review (no shared session context)
Why: The same session that generated code is less effective at reviewing it because it retains reasoning context that biases it toward its own decisions. Independent review instances judge only the code itself, without the bias of earlier justifications.
You should see: Two distinct claude -p invocations in the CI script: one for generation and one for review. They share no session context. The review invocation analyses the generated code independently. The review findings are more thorough than self-review in the same session.
- Implement incremental review: store previous findings, include them in the next review run, and instruct Claude to report only new or still-unaddressed issues
Why: Without incremental context, each review run analyses the entire PR from scratch and produces duplicate comments. Duplicate comments reduce developer trust — when the same five issues appear on every push regardless of fixes, developers stop reading them.
You should see: The first review run produces findings and stores them (as a JSON artifact or file). Subsequent runs include the previous findings in context. The output contains only new issues or issues that remain unaddressed. Previously fixed issues do not reappear as comments.
Sources
- Claude Code CLI Reference — Anthropic
- Claude Code GitHub Actions — Anthropic
- Claude Code: Run parallel sessions with worktrees — Anthropic
- Claude Certified Architect Foundations Exam Guide — Task Statement 3.6 — Anthropic
- Claude Certified Architect Foundations Exam Guide — Sample Questions 10 and 11 — Anthropic
- Anthropic Message Batches API Documentation — Anthropic
- CCAR-F Exam Guide v1.0: Sample Question 10 — Anthropic