What you need to know
Non-interactive mode
A CI job has no one to answer prompts. Run Claude Code with -p (--print): it processes the prompt, prints the result and exits. Without it, the job waits for terminal input until the pipeline times out.
claude -p "Review this pull request for security issues" \
--output-format json \
--json-schema "$(cat review-schema.json)"
Structured output for automation
--output-format jsonreturns machine-readable output instead of prose.--json-schemamakes the final output match a schema you supply.
Together they produce findings a script can post as inline PR comments without fragile text parsing:
{"findings": [{"file": "src/pay.ts", "line": 88, "severity": "high",
"category": "security", "issue": "Card number logged in plain text",
"fix": "Log only the last four digits"}]}
Project context comes from CLAUDE.md
CI-invoked Claude Code reads the project's CLAUDE.md. That is where to document testing standards, the fixture library, what makes a valuable test, and the review criteria. Generated tests that ignore fixtures or test trivial getters usually mean this context is missing.
Avoid repeats and duplicates
- When a review re-runs after new commits, include the previous findings in context and ask Claude to report only new issues or earlier ones still unaddressed. Otherwise it re-posts everything, and developers mute it.
- When generating tests, include the existing test files so Claude does not suggest scenarios already covered.
Independent review
A session that generated code keeps the reasoning that produced it, and is less likely to question its own decisions. Review generated code with a separate instance that has no access to the generator's reasoning (lesson 4.6 goes further).
In the exam
Scenario 5 (CI) carries these: a hanging job, unparseable findings, duplicate comments after each commit, low-value generated tests, duplicate test scenarios, and self-review that approves its own bugs.
Check your understanding
A CI job running claude "Analyze this PR" hangs until it times out, waiting for input. What fixes it?
Show answer
Answer: A. -p runs Claude Code non-interactively. The other options work around the symptom without changing the mode.
Your reviewer re-posts the same 12 findings after every commit. What should change?
Show answer
Answer: B. Giving the reviewer its earlier findings lets it skip what was fixed and avoid duplicates while still catching new issues.
About 80% of generated tests are rejected for ignoring fixtures and testing trivial code. Which TWO changes help most? Select TWO.
Show answer
Answer: A, C. CLAUDE.md gives CI-invoked Claude Code the standards and fixtures; existing tests show what is covered and the house style. Volume and model size do not address the cause.
Apply it: Fix Keystone's pipeline
Keystone's review job hangs, posts prose, repeats itself, and approves fixes written by the same session. Redesign the CI integration.
- Write the review command with its flags.
- Sketch the findings schema fields the comment poster needs.
- Describe how re-runs avoid duplicates.
- Say what goes into CLAUDE.md to improve generated tests.
- Separate generation from review.
Show model answer
1. Command
claude -p "$(cat .ci/review-prompt.md)" --output-format json \
--json-schema "$(cat .ci/findings.schema.json)"
2. Schema fields. file, line, category (security, correctness, performance, style), severity (critical, high, medium, low), issue, fix, and an id stable across runs.
3. Re-runs. The job fetches the bot's open findings for the PR and passes them into the prompt with the instruction: "Report only issues that are new, or listed below and still present. Mark listed ones that are fixed as resolved." The poster updates or resolves comments by id.
4. CLAUDE.md. Test conventions (one behaviour per test, naming), the fixture library and how to use it, what not to test (trivial getters and setters, generated code), and the review criteria per category. The generation job also receives the existing test files for the changed modules.
5. Separation. Test and fix generation run in one job; review runs in a separate claude -p invocation that receives only the diff and the CLAUDE.md context, never the generator's conversation.