Domain 3 · Task 3.6
CI/CD Integration
Integrate Claude Code into CI/CD pipelines.
Running Claude Code in CI means making it headless and machine-parseable, and structuring reviews so they are trustworthy. The three load-bearing facts: -p for non-interactive mode (a bare claude "..." hangs), --output-format json with --json-schema for structured output, and the insight that the session which generated code is worse at reviewing it than an independent instance.
Key concept
CI must be headless (-p) and machine-parseable (--json-schema). Review needs an independent instance plus prior-findings context to report only new or unaddressed issues. CLAUDE_HEADLESS and --batch do not exist.
What you need to know
Headless mode: -p and structured output
A bare claude "..." call hangs waiting for interactive input, which stalls the pipeline. The fix is -p / --print: process the prompt, print to stdout, and exit. For output CI can act on, combine --output-format json with --json-schema so results are schema-validated and machine-parseable — ready to post as inline PR comments. Note the anti-patterns: CLAUDE_HEADLESS=true and a --batch flag do not exist, and stdin redirection does not fix the hang. The one correct fix for a hanging CI call is adding -p.
# WRONG — hangs waiting for interactive input
claude "review the changed files"
# RIGHT — headless + machine-parseable
git diff main --name-only \
| claude -p "review these changed files for security issues" \
--output-format json \
--json-schema ./review-schema.json \
| jq '.structured_output'Independent review and avoiding duplicate comments
Session-context isolation: the session that generated code is less effective at reviewing its own changes — it carries its own reasoning context and will not challenge itself. Use a separate, independent instance for review. When re-running after new commits, include the prior findings and instruct Claude to report only new or still-unaddressed issues, so the pipeline does not spam duplicate comments across pushes. Provide the existing test files too, so test generation does not re-cover scenarios that are already tested.
# .github snippet — headless review as a build step
- name: Claude review
run: |
gh pr diff "${{ github.event.pull_request.number }}" \
| claude -p "You are an independent reviewer. Report ONLY new or unaddressed issues." \
--output-format json --json-schema ./review-schema.json \
> findings.jsonCLAUDE.md as CI project context
CI-invoked Claude Code still reads CLAUDE.md, which is how you supply project context to a headless run: testing standards, fixtures, and review criteria. Documenting testing standards and fixtures there raises test quality and cuts low-value output — the CI instance writes tests that match your conventions and skips scenarios already covered. In short, the pipeline flags (-p, --json-schema) make Claude Code a deterministic build step, and CLAUDE.md makes that build step aware of the project it is running against.
Exam traps
| The trap | The reality |
|---|---|
A claude "..." call hanging in CI is fixed by setting CLAUDE_HEADLESS=true or adding a --batch flag. | Neither exists. The bare call hangs on interactive input; add -p / --print to run non-interactively. |
| Redirecting stdin into the command resolves the CI hang. | The problem is interactive-mode invocation, not missing input. -p is the correct fix. |
| The session that wrote the code is the most efficient one to review it, since it already has all the context. | That session is worse at reviewing its own changes — it will not challenge itself. Use an independent instance. |
| Re-running review after each push should just re-report everything it finds. | Include prior findings and report only new or unaddressed issues, or the PR fills with duplicate comments. |
Practice scenario
Real questions from the bank that test this topic — the correct answer is highlighted.
Your company runs Claude Code in CI for automated code review. Constraints:
- Must be non-interactive (no TTY prompts)
- Output must be parseable as JSON for downstream processing
- The agent should only be allowed to use Read , Glob , and Grep
- The CI worker has a 5-minute timeout
Which invocation is correct?
--ci --readonly --timeout 300 --format json--output-format stream-json --allowed-tools Read,Glob,GrepCorrect--headless --tools=read,glob,grep --format json --max-time 300--batch --output json --tools "Read,Glob,Grep"Why: Headless invocation: claude -p "prompt" (the -p is "print" / non-interactive). --output- format supports text , json , stream-json . --allowed-tools takes a comma-separated list. The other invocation patterns (A, C, D) mix invented flag names with plausible-looking syntax.
A startup deploys Claude Code in CI to review every PR. They have these constraints:
The CI job has a hard 6-minute wall-clock budget
The agent must only use Read , Glob , Grep , and Bash(gh:*) — never anything that could mutate the repo
Output must be machine-parseable JSON so it can be posted as a structured PR comment
The CI runner has no TTY
A junior engineer's first attempt is:
claude --interactive --tools "Read,Glob,Grep,Bash(gh:*)" --timeout 360 --format json
The senior reviewer rejects it. What are the specific problems?
--interactive is wrong (should be -p for headless print mode); --tools is the wrong flag name (should be --allowed-tools ); --format is wrong (should be --output-format ); --timeout isn't a real Claude Code flag — wall-clock budgeting must be enforced by the CI runner itself (e.g., GitHub Actions timeout-minutes )Correctsettings.json--format json is fine but the agent needs an additional --ci-mode flag for CI environmentsWhy: This question tests headless mode flags concretely. The correct invocation: claude -p "review the diff" --output-format json --allowed-tools "Read Glob Grep Bash(gh:*)" with wall-clock budgeting handled by the CI runner (e.g., GitHub Actions timeout-minutes: 6 ). The junior's mistakes: - --interactive is wrong; -p (print) is headless mode - --tools doesn't exist as a flag; --allowed-tools is correct - --format is wrong; --output-format is correct - --timeout isn't a Claude Code flag — there's no built-in wall-clock timeout; CI runners handle that B is wrong because Bash(gh:*) patterns are valid in --allowed-tools . C invents --ci- mode . D invents a constraint that CI must use the API directly.
Build exercise
Wire a headless Claude Code review into CI
~45 min- 1Reproduce the hang: run a bare
claude "..."in a non-interactive shell and watch it wait forever.Why: Seeing the hang cements why headless mode is mandatory in CI.
- 2Re-run with
-pand confirm it processes the prompt, prints to stdout, and exits.You should see: The command completes without waiting for input.
- 3Add
--output-format jsonand--json-schemapointing at a review schema, and parse the result withjq.You should see: Machine-parseable, schema-conforming findings you can post as inline PR comments.
- 4Pipe the PR diff into a fresh, independent Claude Code instance for review rather than the generating session.
Why: An independent instance reviews more effectively than the one that wrote the code.
- 5On a second push, pass the prior findings and instruct it to report only new or unaddressed issues; add a
CLAUDE.mdwith your testing standards.You should see: No duplicate comments across pushes, and generated tests follow the documented standards.
Sources
Drill Claude Code Configuration & Workflows
Practice only this domain’s questions, untimed, with instant explanations.