Domain 4 is worth 20% — roughly 12 questions out of 60. It's the one where candidates feel most comfortable, and that's precisely where they lose points: everyone "knows how to prompt", but the exam doesn't test the ability to write a good prompt. It tests whether you recognize when an instruction isn't enough — when you need a measurable criterion rather than an adjective, a schema rather than prose, a second instance rather than self-critique. The through-line is the same as in Domain 1: guarantee beats probability.
This lesson follows the 5 task statements of the official exam guide v1.0 (July 2026). The exam scenarios that lean on this domain are Code Generation with Claude Code (scenario 2), Document Processing Pipeline (scenario 6) and Claude Code for Continuous Integration (scenario 5).
tool_use / tool_choice from Domain 2, session isolation from Domain 3.The domain map
Domain 4 is a continuum of control: the further down you go, the more structure you impose on the model. 4.1 guides (prompt), 4.2 constrains (schema), 4.3 makes it reason (thinking), 4.4 shifts in time (batch), 4.5 multiplies viewpoints (instances).
4.1 — Guide: explicit criteria, few-shot, XML, system prompt
Question 3 of the official guide, the most emblematic: a review pipeline flags minor style changes as "critical" and misses a SQL injection. You add "be conservative, only flag real issues". It doesn't work. Why?
What you need to know
Adjectives can't be measured. "Conservative", "rigorous", "relevant" require interpretation, and interpretation varies from call to call. The expected answer is always explicit criteria: the list of what must be flagged (with severity level), the list of what must not, the expected format.
Few-shot with the reasoning. Examples of good and bad reviews aren't enough if they don't say why. Each example carries the reasoning ("this is critical because…", "this isn't flagged because…"). That's what allows generalization to cases not shown.
Positive framing. "Do X" guides better than "don't do Y". A list of "don'ts" leaves open everything not forbidden. You describe the expected behavior, and reserve the negative for precise exclusions ("don't flag formatting").
XML tags. To separate instructions, context and data to process without ambiguity: <instructions>, <code_to_review>, <previous_findings>. Without delimitation, injected code or a document can be read as an instruction.
Edge cases made explicit. When a transformation is ambiguous (does an empty field become undefined or a default value?), you decide in the prompt. Not doing so leaves the model to choose differently each time.
The system prompt for working style. "Ask your questions before implementing", "propose two approaches before coding": a behavior that must persist across the whole session goes in the system prompt (or CLAUDE.md in Claude Code), not in every message.
SYSTEM = """<role>Senior code reviewer. You ask clarifying questions BEFORE proposing a fix.</role>
<criteria>
Flag as CRITICAL: SQL/XSS/command injection, plain-text secrets, bypassable access control.
Flag as HIGH: unreleased resources, race conditions, swallowed exceptions.
Flag as MEDIUM: duplicated logic, missing input validation on a public API.
Do NOT flag: formatting, naming, import order, anything the linter covers.
Each finding: file, line, severity, evidence (exploitation path or failure scenario).
</criteria>
<examples>
<example>
<code>query = "SELECT * FROM users WHERE id = " + user_id</code>
<finding severity="critical">User input concatenated into a SQL query. Evidence: user_id = "1 OR 1=1" returns the whole table. Fix: parameterized query.</finding>
</example>
<example>
<code>import os, sys # non-alphabetical order</code>
<finding>NONE — import order = linter, out of scope.</finding>
</example>
</examples>
"""
messages = [{"role": "user", "content": f"<code_to_review>\n{diff}\n</code_to_review>"}]4.2 — Constrain: structured output, nullable, validate-retry
The technical core of the domain, anchored on scenario 6 (document pipeline). Question 11 of the guide is its archetype: invoices without a purchase order number get a plausible but invented number. What do you do?
What you need to know
tool_use + JSON schema, never parsing. To get structured output, you define a tool whose input_schema is the target schema. The model fills the schema; the application reads tool_use.input. Asking "answer in JSON" then parsing text is fragile (preambles, backticks, missing fields).
tool_choice guarantees the call. Forced on a specific tool when the schema is known; any when several extraction schemas exist (invoice, contract, delivery note) and the model must choose according to the document.
Validate, then retry with feedback. The output is validated programmatically (types, formats, business constraints). On failure, you send the precise error message back to the model for a targeted retry ("total_ht must be a number, received "1 250,00 €""). Not a blind retry, not a give-up.
Nullable against hallucination. This is question 11: if the schema demands a value, the model fabricates one. The fix is a strict schema where optional fields are explicitly nullable, with a description saying to return null when the value is absent. The distractors: "prompt instruction" (probabilistic), "post-processing that detects suspicious values" (too late, and how do you tell a real PO from a plausible fake?), "validation against the PO database" (unnecessary coupling, and an invented PO can exist by chance).
Enums for confidence. A free-text confidence level ("fairly sure", "probable") is unusable. A strict enum ("high" | "medium" | "low" | "not_found") enables programmatic routing: low → human review.
Distinguishing section types. In a mixed document (tables, prose, lists), a single extraction flattens everything. The prompt says how to handle each type and the schema has distinct fields (tables[], narrative, line_items[]), and you split on structure (one pass per section).
EXTRACT_INVOICE = {
"name": "extract_invoice",
"description": "Extracts invoice fields. Returns null for any field ABSENT from the document; never invents.",
"input_schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"po_number": {"type": ["string", "null"],
"description": "Purchase order number as written. null if none appears."},
"total_ht": {"type": "number", "description": "Decimal number, no currency or thousands separator"},
"currency": {"type": "string", "enum": ["EUR", "USD", "GBP"]},
"confidence": {"type": "string", "enum": ["high", "medium", "low", "not_found"]},
"line_items": {"type": "array", "items": {"type": "object",
"properties": {"label": {"type": "string"}, "qty": {"type": "number"}, "unit_price": {"type": "number"}},
"required": ["label", "qty", "unit_price"]}},
},
"required": ["invoice_number", "po_number", "total_ht", "currency", "confidence", "line_items"],
"additionalProperties": False,
},
}
def extract(doc: str, max_retries: int = 2) -> dict:
messages = [{"role": "user", "content": f"<document>\n{doc}\n</document>"}]
for attempt in range(max_retries + 1):
resp = client.messages.create(
model=M, max_tokens=2048, tools=[EXTRACT_INVOICE],
tool_choice={"type": "tool", "name": "extract_invoice"}, # guaranteed call
messages=messages,
)
block = next(b for b in resp.content if b.type == "tool_use")
errors = validate(block.input) # jsonschema + business rules
if not errors:
return block.input
messages += [{"role": "assistant", "content": resp.content},
{"role": "user", "content": [{"type": "tool_result", "tool_use_id": block.id,
"content": f"Validation failed: {errors}. Fix ONLY these fields.",
"is_error": True}]}] # retry with targeted feedback
raise ExtractionError(errors)tool_use + schema. Guaranteed → tool_choice. Absent → nullable + description. Confidence → enum. Invalid → retry with the error message.4.3 — Reason: when to enable extended thinking
A short section about proportion. The exam doesn't ask how extended thinking works, but when it's justified.
Extended thinking: yes or no?
| Enable | Don't enable | |
|---|---|---|
| Nature of the task | Multi-step reasoning, cross-file dependencies, architecture, trade-offs | Formatting, simple extraction, obvious classification |
| Example | Impact of a schema change on 12 modules, choice between two migration strategies | Rename a variable, extract a date, fix a typo |
| With tool_use | Reason over tool results between two calls (analysis → decision → call) | Direct tool call without arbitration |
| Audit | Make the reasoning visible to check WHY a decision was made | Only the result matters |
| Cost / latency | Accepted when the error costs more than the tokens | Unjustified on simple volume |
| Exam signal | "complex", "interdependent", "why", "audit" | "simple", "each document", "at scale" |
What you need to know
When. For complex, multi-step reasoning: analyzing cross-file dependencies before a refactor, arbitrating between two approaches, understanding the impact of a schema change. Not for a simple task where the extra cost and latency aren't justified.
With tool_use. Extended thinking combined with tools lets it reason over tool results before the next action: read three files, reason about their interactions, then decide what to modify.
For audit. When you must understand why a decision was made (an architecture recommendation, a classification), visible reasoning lets you verify the logic, not just the result.
4.4 — Defer: the Batch API
Scenario 6, volume and cost. Three numbers to know by heart and one contraindication.
What you need to know
The numbers. 50% discount on token cost. Results within 24 hours (often much less, but 24h is the commitment). Each request in the batch carries a custom_id to find it in the results, because output order isn't guaranteed.
The flow. Build the batch → submit → poll the status → retrieve results and match by custom_id. An entry can be succeeded or errored individually.
When. Non-urgent bulk processing: indexing 50,000 archive documents, classifying a backlog, generating product descriptions. When processing is expected in real time (PR review, customer reply), it's the standard API.
With prompt caching. Requests in a batch often share the same system prompt and extraction schema: marked with cache_control, they're cached and the batch discount applies on top.
# Batch: one item per document, custom_id for matching
requests = [
{"custom_id": f"doc-{d.id}",
"params": {"model": M, "max_tokens": 2048,
"system": [{"type": "text", "text": SYSTEM_EXTRACT,
"cache_control": {"type": "ephemeral"}}], # shared → cache
"tools": [EXTRACT_INVOICE],
"tool_choice": {"type": "tool", "name": "extract_invoice"},
"messages": [{"role": "user", "content": d.text}]}}
for d in documents
]
batch = client.messages.batches.create(requests=requests)
while client.messages.batches.retrieve(batch.id).processing_status != "ended":
time.sleep(60) # polling
for r in client.messages.batches.results(batch.id):
if r.result.type == "succeeded":
store(r.custom_id, r.result.message) # matching by custom_id
else:
requeue_or_flag(r.custom_id, r.result.error)custom_id is for.4.5 — Multiply: multi-instance review
A method question, bordering Domain 3 (session isolation). The idea: one instance = one perspective, plus one instance that consolidates.
What you need to know
One instance per perspective. Security, performance, maintainability: each instance receives the same diff with its own criteria. A single instance that "does everything" dilutes attention (cf. Domain 1, 1.6) and mixes severities.
Aggregation. A dedicated instance consolidates findings, deduplicates, prioritizes and arbitrates contradictions: when the performance review recommends inlining a function and the maintainability review recommends extracting it, the aggregator decides based on context (hot path or not) instead of leaving two opposite pieces of advice on the PR.
Independence. The instance that generated the code is never a reviewer (cf. Domain 3, 3.6).
Reducing false positives. Explicit criteria (4.1) and, above all, examples of what not to flag: false positives almost always come from an unbounded scope. A review that flags import order never received the instruction that it's out of scope.
Cross-cutting traps of Domain 4
- A quoted word is a signal. "Be conservative", "don't invent", "be less sensitive": the prompt shows you the probabilistic instruction that failed. The answer replaces it with structure.
- The schema is a contract, not a suggestion. Anything that can be imposed by the schema (nullable, enum, required, additionalProperties) wins over an instruction.
- Proportion, again. Extended thinking and multi-instance cost; they're justified by complexity or risk, not by default.
- Real time excludes batch. If someone is waiting, it's the standard API.
Night-before checklist
<instructions>, <code_to_review>, <document> — separate instructions and data.
- Ambiguous cases (empty → undefined or default?) decided in the prompt.
- Persistent working style ("ask your questions first") → system prompt.
- Structured → tool_use + input_schema; never "answer in JSON" + parsing.
- tool_choice forced (known schema) or any (several schemas).
- Absent fields → ["string", "null"] + description "null if absent" — not an instruction, not post-processing.
- Confidence → strict enum, programmatic routing (low → human).
- Programmatic validation → retry with the error message, never blind.
- Mixed sections → distinct fields in the schema + one pass per section.
- Extended thinking: complex, multi-step, audit, with tool_use; not on simple at-scale work.
- Batch API: -50%, ≤ 24h, custom_id, polling; never for real time; stackable with cache_control.
- Multi-instance: one per perspective + an aggregator that deduplicates and arbitrates; never the generating instance.
- False positives → criteria + examples of what not to flag.Five original scenario questions, corrected
Questions written by nAIvigate in the spirit of the exam, reproducing no real items.
Question 1 — CI scenario. Your automated review classifies "unused variable" as critical and lets a plain-text API token through. The prompt says "focus on what really matters". Most effective fix?
A. Replace with "be much stricter on security". B. Define explicit criteria per severity (secrets and injections = critical; unused variables = out of scope) with examples annotated with the reasoning. C. Switch to a bigger model. D. Enable extended thinking for the review.
📚Answer Q1
B. An adjective replaced by another adjective is still unmeasurable (A). C and D don't define what must be flagged. Explicit criteria + few-shot with reasoning is the expected answer.
Question 2 — Document scenario. A pipeline extracts contracts. Contracts without an end date receive a plausible end date. The prompt already says "don't invent dates". What do you do?
A. Repeat the instruction in capitals. B. Make end_date nullable in the schema with the description "null if not mentioned", and add confidence as an enum. C. Post-process to reject end dates after 2030. D. Compare with the existing contract database.
📚Answer Q2
B. The schema forces invention if the field is required; nullable gives a legitimate output for absence. A is probabilistic. C rejects by an arbitrary heuristic and misses plausible invented dates. D couples unnecessarily and doesn't detect an invented date that happens to coincide.
Question 3 — Document scenario. 80,000 archive reports to classify before month end, tight budget, no user waiting. Approach?
A. Standard API with maximum concurrency. B. Batch API with a custom_id per report, system prompt and schema marked cache_control, polling until ended. C. Extended thinking to improve accuracy. D. One instance per report category in real time.
📚Answer Q3
B. Volume, non-urgency, budget: the textbook Batch API case (-50%), stacked with caching of the shared context. A pays full price. C and D increase cost for no reason.
Question 4 — CI scenario. A structured extraction fails 6% of the time on a format validation (amount as string instead of number). Current logic retries the identical request up to 3 times. Improvement?
A. Raise to 5 attempts. B. Send the model the precise validation error message ("total_ht must be a number, received "1 250,00 €"") and ask for correction of that field only. C. Add "return numbers" to the system prompt. D. Convert all strings to numbers on the application side.
📚Answer Q4
B. Retry with targeted feedback is the expected pattern. A repeats the blind approach. C helps but isn't the recovery mechanism. D masks the problem and will break on unforeseen cases ("1.250,00" vs "1,250.00").
Question 5 — CI scenario. Your multi-instance review produces on the same PR "inline this function (perf)" and "extract this function (readability)". Developers now ignore both. What do you do?
A. Remove the performance instance. B. Add an aggregation instance that deduplicates, prioritizes and arbitrates contradictions based on context (hot path or not). C. Merge the two perspectives into a single instance. D. Let developers decide, it's their job.
📚Answer Q5
B. The aggregator is the expected component of the multi-instance pattern. A loses a perspective. C dilutes attention. D is what's already happening, and it doesn't work.
Validation quiz
"Be conservative" doesn't work. Expected answer?
📚Domain 4 glossary (expand)
Explicit criteria — Measurable list of what must be flagged, ignored and the expected format, replacing a behavioral adjective.
Few-shot with reasoning — Examples annotated with the why (why critical, why not flagged), a condition for generalization.
Positive framing — Describing the expected behavior ("do X") rather than the forbidden ("don't do Y").
XML tags — Delimiters (<instructions>, <document>) separating instructions, context and data so injected content isn't read as an instruction.
System prompt — Home of behaviors persisting across the whole session (working style, role, criteria).
Structured output — Response conforming to a schema, obtained via tool_use with input_schema.
input_schema — A tool's JSON schema; serves as the contract for structured output.
tool_choice — auto / any / forced; any and forced guarantee a tool call, hence structured output.
Nullable field — Type ["string", "null"] explicitly allowing absence of a value, against hallucination.
Confidence enum — Closed values (high / medium / low / not_found) enabling programmatic routing.
additionalProperties: false — Constraint forbidding fields outside the schema.
Validate-and-retry — Programmatic check of the output, then a new call with the precise error message.
Blind retry — Anti-pattern: replaying the same request without feedback.
Extended thinking — Extended reasoning for complex multi-step tasks; makes the logic auditable; combinable with tool_use.
Batch API — Asynchronous processing at -50%, results within 24h, for non-urgent volume.
custom_id — Per-request identifier in a batch, to match results (order not guaranteed).
Polling — Periodic status check of a batch until ended.
Prompt caching (cache_control) — Caching of shared prefixes (system prompt, schema); stackable with the batch discount.
Multi-instance review — One instance per perspective (security, performance, maintainability) on the same input.
Aggregation instance — Instance that consolidates, deduplicates, prioritizes and arbitrates contradictions between reviewers.
Review independence — The generating instance is never a reviewer (session isolation).
Non-finding examples — Examples of what not to flag, the main lever against false positives.
Going further
The last lesson is Domain 5 — Context Management & Reliability (15%), which picks up persistent memory, compaction, graceful degradation and source attribution — the reliability questions that complete the schemas and criteria seen here. To go deeper on structured output in production, our RAG in production guide shows extraction and citation schemas at scale.
If you want to certify a whole team — or industrialize a structured extraction pipeline (nullable schemas, validate-retry, batch + caching) on real documents — that's what nAIvigate Studio does in a Sprint.