Skip to content
CCAR-FAcademy CCAR-F

Domain 4 — Prompt Engineering & Structured Output

Domain 4 measures whether you can turn a requirement into prompt text plus API configuration that produces reliable, machine-consumable output. It covers writing explicit categorical criteria instead of vague or confidence-based instructions, using few-shot examples to pin down output format and ambiguous-case judgment, enforcing schema compliance through tool use with JSON schemas, and closing the loop with validation, retry-with-error-feedback and self-correction fields. It also covers the operational side: matching the synchronous API or the Message Batches API to a workload’s latency requirements, and designing multi-instance and multi-pass review architectures. Its two anchor scenarios are Claude Code in CI/CD (actionable feedback, minimal false positives) and structured data extraction from unstructured documents.
20% of the exam 6 statements 18 examples 7 diagrams ~17 min read Take the 10-item quiz
4.1

Design prompts with explicit criteria to improve precision and reduce false positives

What you need to know

  • Replace vague quality instructions with decidable categorical criteria the model can apply the same way twice.
  • "Be conservative" and "only report high-confidence findings" are not precision levers — they change tone, not the decision boundary.
  • Precision is per-category but trust is global: one noisy category undermines confidence in the accurate ones.
  • Temporarily disabling a high-false-positive category is a valid way to restore trust while you improve its prompt.
  • Anchor every severity level to a concrete code example so classification is repeatable.
  • State both axes separately: what counts as a finding, and how severe it is. Neither implies the other.
Vague instruction vs explicit categorical criteriaShow that precision comes from replacing the decision rule with a decidable predicate, not from asking the model to be more confident or conservative — and show the downstream trust consequence of each path.Vague instructionVagueinstructionExplicit categorical criteriaExplicitcategoricalcriteriaModel invents its own barModel invents itsown barModel applies a fixed testModel applies afixed testInconsistent classificationInconsistentclassificationConsistent classificationConsistentclassificationHigh false positive rateHigh falsepositive rateFindings acted onFindings acted onTrust erodes across all categoriesTrust erodesacross allcategoriescheck comments are accurate,no decidable predicateflag only on codecontradiction, decidablepredicatevaries per runrepeatablenoisesignalspillover
Vague instruction vs explicit categorical criteria

Show that precision comes from replacing the decision rule with a decidable predicate, not from asking the model to be more confident or conservative — and show the downstream trust consequence of each path.

Worked examples

Rewriting a comment-accuracy check for a CI review bot
Scenario 5 · Claude Code for Continuous Integration

The first version of the prompt produced comments on every docstring that was merely terse or slightly out of date, and developers stopped reading the bot. The rewrite does not add "be careful" — it replaces the criterion with a contradiction test plus an explicit skip list.

markdown Before / after criterion for the comment-accuracy category

BEFORE (vague — the model invents the bar)
  Check that comments are accurate.

AFTER (explicit categorical criterion)
  Flag a comment ONLY when the behavior it claims contradicts the behavior of
  the code it documents. A contradiction means: the comment states a return
  value, side effect, precondition, or error case that the code does not
  implement.

  Report:
    - comment says "returns null on failure", code throws
    - comment says "thread-safe", method mutates shared state without a lock
    - comment documents a parameter that no longer exists

  Skip:
    - comments that are terse, informal, or stylistically inconsistent
    - missing comments
    - comments that are incomplete but not contradictory
    - TODO/FIXME notes
Severity levels anchored to concrete code examples
Scenario 5 · Claude Code for Continuous Integration

Qualitative labels drift between runs. Giving one short code example per level turns severity into a matching task instead of a judgment call, which is what makes the classification consistent enough to drive routing (block the merge vs post a comment).

markdown Severity rubric fragment

BLOCKING — a defect that can corrupt data or bypass authorization.
  Example:
    query = "SELECT * FROM users WHERE id = " + req.params.id

HIGH — a defect that produces incorrect behavior on a reachable input path.
  Example:
    for (let i = 0; i <= items.length; i++) total += items[i].price

MEDIUM — a defect that only manifests under a documented edge case.
  Example:
    parseInt(userInput)   // no radix, no NaN check

Anything that does not match one of the examples above is NOT reported.
Temporarily disabling a high-false-positive category
Scenario 5 · Claude Code for Continuous Integration

Telemetry shows developers dismiss 80% of findings in the race_condition category, and dismissal rates are rising in security too — the noise is bleeding across categories. The fix is to switch the noisy category off in the pipeline config while its criteria are rewritten, then re-enable it behind a measured dismissal rate. Turning the whole bot off, or leaving the category on and asking the model to be more careful, are both worse.

yaml CI review configuration during remediation

review_categories:
  correctness:      { enabled: true }
  security:         { enabled: true }
  race_condition:
    enabled: false
    # Disabled 2026-02-11: 80% dismissal rate was eroding trust in the
    # correctness and security categories. Re-enable when the rewritten
    # criteria hold dismissal under 20% on the replay corpus.
  style:            { enabled: false }   # explicitly out of scope

Anti-patterns

  • Confidence-based filtering: instructing the model to "only report high-confidence findings" instead of naming which categories to report and which to skip because an instruction about certainty gives the model no decision rule and leaves the boundary exactly where it was. Note the scope of this ban: it forbids confidence as an upstream filter deciding what counts as a finding. Attaching self-reported confidence to an already-generated finding as downstream routing metadata is a different and credited technique (4.6).
  • Vague quality directives: asking the model to "check that comments are accurate" instead of defining the contradiction test because the model must then invent the bar and will invent a different one on each run.
  • Unanchored severity labels: naming levels critical/major/minor without a concrete code example per level because the model reclassifies the same defect differently across files and the routing built on top of it becomes unreliable.
  • Shipping a known-noisy category: leaving a high-false-positive category enabled while you iterate instead of disabling it temporarily because its noise erodes developer trust in the categories that are accurate.

How it is examined

  • Scenario 5 stems typically describe a review bot whose findings are being dismissed and ask what to change. Options that adjust how the model should feel about a finding (be conservative, be careful, only high confidence) are distractors; the credited option changes what counts as a finding.
  • When a stem says one category is noisy and overall trust is falling, the expected answer pairs a temporary disable of that category with prompt improvement — not accepting the noise, and not disabling review entirely.
  • If an option offers "add explicit severity definitions" versus "add explicit severity definitions with a code example per level", the version with concrete examples is the credited one — the guide names consistent classification as the goal.
Why this is true

Explicit criteria are a decision rule; vague instructions are a wish

The exam's canonical contrast is "check that comments are accurate" versus "flag comments only when claimed behavior contradicts actual code behavior". The second one is not merely more words — it is a decidable predicate. The model can apply it to a comment/code pair and get the same answer twice. The first one forces the model to invent its own bar for "accurate", and it will invent a slightly different bar on every file, every run, and every PR. Inconsistent classification is the direct consequence of a criterion the model has to define for itself.

Why "be conservative" does not improve precision

Instructions like "be conservative" or "only report high-confidence findings" are meta-instructions about the model's internal certainty. They do not change what counts as a finding; they only ask the model to feel differently about findings it was already going to produce. There is no calibrated confidence threshold behind such a request, so the observable effect is usually tone and hedging rather than a moved decision boundary. The lever that actually moves precision is categorical: name the issue classes that are in scope (bugs, security vulnerabilities, correctness defects) and the classes that are explicitly out of scope (minor style, formatting, project-local patterns the team has deliberately adopted).

False positives are contagious

The reason precision matters more than raw recall in a CI review bot is a trust effect: a category with a high false positive rate undermines developer confidence in the categories that are accurate. Once a reviewer has dismissed eight of ten "possible null dereference" comments, they stop reading the security comments too. Precision is therefore a per-category property with a global blast radius.

That gives you two distinct engineering moves:

  • Temporarily disable the offending category while you improve its prompt. You trade coverage you were not getting value from anyway for restored trust in the remaining categories. This is a legitimate, exam-relevant answer — not a cop-out.
  • Define explicit severity criteria with a concrete code example for each level. Naming levels "critical / major / minor" is another vague criterion. Anchoring each level to a short code sample ("critical: an unchecked index into a caller-supplied array") makes classification consistent across runs and reviewers.

Reporting criteria (report vs skip) and severity criteria (how bad) are separate axes. Both need to be explicit, and neither should be delegated to the model's sense of confidence.

Exam guide, verbatim — what is measured

Knowledge of

  • The importance of explicit criteria over vague instructions (e.g., "flag comments only when claimed behavior contradicts actual code behavior" vs "check that comments are accurate")
  • How general instructions like "be conservative" or "only report high-confidence findings" fail to improve precision compared to specific categorical criteria
  • The impact of false positive rates on developer trust: high false positive categories undermine confidence in accurate categories

Skills in

  • Writing specific review criteria that define which issues to report (bugs, security) versus skip (minor style, local patterns) rather than relying on confidence-based filtering
  • Temporarily disabling high false-positive categories to restore developer trust while improving prompts for those categories
  • Defining explicit severity criteria with concrete code examples for each severity level to achieve consistent classification
4.2

Apply few-shot prompting to improve output consistency and quality

What you need to know

  • When detailed instructions still produce inconsistent output, few-shot examples are the highest-leverage next move — not longer instructions.
  • Use 2–4 targeted examples chosen at the ambiguous boundary; a pile of easy cases teaches the model nothing about where the line is. 2–4 is the few-shot count; the 2–3 count belongs to iterative-refinement input/output examples (3.5).
  • An ambiguous-case example teaches nothing without the reasoning that rejected the plausible alternative.
  • Demonstrate the exact output fields (location, issue, severity, suggested fix) instead of describing them in prose.
  • Pair a genuine issue with a superficially similar acceptable pattern to cut false positives while preserving generalization.
  • In extraction, one example per document structure variant is the stated fix for hallucination and for empty/null required fields.

Worked examples

Few-shot for an ambiguous tool-selection decision
Scenario 1 · Customer Support Resolution Agent

A support request that mentions both a charge and a delivery problem plausibly maps to lookup_order or process_refund. Instructions alone ("pick the most appropriate tool") leave it a coin flip. The example below shows the choice and the reasoning that rejected the alternative, which is what lets the model handle a novel ambiguous request the same way.

text One of 2–4 ambiguous-case examples

User: "I was charged twice for order 4471 and it still hasn't arrived."

Reasoning: Two issues are present. The duplicate charge is asserted, not
verified — process_refund would act on an unverified claim and is not
reversible. lookup_order returns both the shipment state and the charge
history, so it resolves which issue is real before any irreversible action.
Choose the read before the write.

Action: lookup_order(order_id="4471")

---

User: "Cancel my subscription and refund last month."

Reasoning: Two requests, both explicit and both write actions. Refund policy
depends on subscription state at time of cancellation, so cancellation must
be sequenced first. No read is needed to disambiguate intent.

Action: cancel_subscription(...) then process_refund(...)
Format-pinning plus an acceptable-pattern negative example
Scenario 5 · Claude Code for Continuous Integration

The review bot's output shape was drifting: sometimes a paragraph, sometimes a bullet list, sometimes without a line number, which broke the code that turned findings into PR comments. Two examples fix the shape, and the second doubles as the exclusion boundary — it shows a pattern that looks like the flagged one but must be skipped, so precision improves without hard-coding an allowlist.

markdown Format demonstration + negative example

EXAMPLE 1 — report
  location: src/billing/invoice.ts:88
  issue: total is computed from items before the discount is applied, so
    discounted invoices overcharge by the discount amount
  severity: HIGH
  suggested_fix: compute subtotal, apply discount, then sum tax

EXAMPLE 2 — skip (looks similar, is not a defect)
  code: const total = items.reduce(sum, 0)   // discount applied upstream
  finding: none
  why: the discount is applied by the caller in applyPromotion(); flagging
    this would be a false positive. Only flag when no caller in the diff
    applies the discount.
Examples covering varied document structures
Scenario 6 · Structured Data Extraction

The extraction pipeline was returning null for citations on roughly a third of papers, and occasionally inventing a reference list. The papers were not malformed — they simply put citations in different places. Adding one example per structural variant addressed both the nulls and the fabrication, because each example demonstrates where the information lives in that layout.

text Structure-variant examples for a citation extractor

VARIANT A — inline citations, no bibliography
  Source: "...consistent with earlier work (Okonkwo & Reyes, 2019)."
  Extract: citations = [{ authors: "Okonkwo & Reyes", year: 2019,
                          title: null }]
  Note: title is absent from the document -> null, do NOT infer one.

VARIANT B — numeric markers plus a References section
  Source: "...as shown in [3]."  /  "[3] Okonkwo A. Signal drift. 2019."
  Extract: citations = [{ authors: "Okonkwo A.",
                          title: "Signal drift", year: 2019 }]
  Note: resolve the marker against the References section before extracting.

VARIANT C — methodology embedded in the results narrative
  Source: "Samples were run in triplicate at roughly room temperature..."
  Extract: method.replicates = 3,
           method.temperature_c = null,
           method.temperature_note = "roughly room temperature"
  Note: informal measurements go in the note field; never convert them to
        a number.

Anti-patterns

  • Easy-case few-shot: filling the example block with unambiguous cases instead of the boundary cases the model actually gets wrong because examples teach where the line is and easy examples put the line nowhere useful.
  • Answer-only examples: showing the chosen action without the reasoning that rejected the plausible alternative because the model then copies the surface of the example instead of generalizing the judgment to novel patterns.
  • Describing the format instead of demonstrating it: specifying "include location, issue, severity and fix" in prose rather than showing a formatted example because prose specifications drift across runs while a demonstrated shape does not.
  • Positive-only examples: never showing an acceptable pattern that must not be flagged because the model cannot infer the exclusion boundary and keeps producing false positives on look-alike code.

How it is examined

  • A stem that says "detailed instructions produce inconsistent output" is signaling few-shot. Distractors will offer more explicit instructions, a longer rules list, or a stricter tone — the credited answer adds 2–4 examples.
  • Watch the example count and the example choice. "Add 20 examples covering every case" and "add one example of the typical case" are both wrong shapes; the guide asks for 2–4 targeted examples of the ambiguous cases.
  • If a stem describes empty or null values in required extraction fields across differently formatted documents, the credited fix is adding examples of correct extraction from those formats — often combined with making the fields nullable (task statement 4.3).
Why this is true

Few-shot is the fix when instructions alone are inconsistent

The guide is unusually direct here: few-shot examples are described as the most effective technique for getting consistently formatted, actionable output when detailed instructions alone produce inconsistent results. The mechanism is that an example demonstrates the target simultaneously along every axis you care about — field set, field order, granularity, tone, length, and what a "good" value looks like — whereas prose has to describe each axis separately and each description is itself open to interpretation. If your first instinct on inconsistent output is "write longer instructions", the exam expects the second instinct: show two to four examples.

Examples earn their place at the ambiguous boundary

The target count is small — 2 to 4 targeted examples — which means selection matters more than volume. Keep this count attached to its scope: 2–4 is the few-shot prompting number, for examples that teach ambiguous-case judgment inside a prompt. The sibling number you will also see on the exam is 2–3, which belongs to iterative refinement (3.5): concrete input/output pairs supplied to pin down a transformation the model keeps interpreting differently. Same idea, different statement and different job, so read which one the stem is describing before you pick a count. The examples that pay for themselves are the ambiguous ones: which tool to select for a request that plausibly maps to two tools; whether a partially covered branch counts as a test coverage gap; whether an unusual-looking code pattern is a genuine defect or an accepted project convention. Crucially, an ambiguous-case example should include the reasoning for why one action was chosen over a plausible alternative. That reasoning is the part that transfers: it teaches the model the principle behind the boundary rather than a single mapping, which is why well-chosen few-shot examples let the model generalize judgment to novel patterns instead of only matching the cases you pre-specified.

Format pinning and false positive reduction

Two concrete jobs show up repeatedly:

  • Format consistency. Demonstrate the exact output shape you want — location, issue, severity, suggested fix — in the example, rather than describing those fields in prose.
  • False positive reduction with generalization. Include an example of an acceptable pattern that must not be flagged alongside a superficially similar genuine issue. This teaches the exclusion boundary. Without a negative example the model can only guess where the line sits, and the guide notes that this pairing reduces false positives while still allowing generalization — you are not enumerating an allowlist.

Extraction: examples reduce hallucination

In extraction tasks, few-shot examples are the stated remedy for hallucination and for empty or null values in required fields. Documents differ structurally: citations may be inline or collected in a bibliography; methodology may have its own section or be buried in a results paragraph; measurements may be informal ("about a tablespoon"). An example per structural variant shows the model where to look and how to normalize, instead of letting it either invent a plausible value or give up and return nothing. Pair this with a nullable schema (4.3) so "genuinely absent" has a legal representation.

Exam guide, verbatim — what is measured

Knowledge of

  • Few-shot examples as the most effective technique for achieving consistently formatted, actionable output when detailed instructions alone produce inconsistent results
  • The role of few-shot examples in demonstrating ambiguous-case handling (e.g., tool selection for ambiguous requests, branch-level test coverage gaps)
  • How few-shot examples enable the model to generalize judgment to novel patterns rather than matching only pre-specified cases
  • The effectiveness of few-shot examples for reducing hallucination in extraction tasks (e.g., handling informal measurements, varied document structures)

Skills in

  • Creating 2-4 targeted few-shot examples for ambiguous scenarios that show reasoning for why one action was chosen over plausible alternatives
  • Including few-shot examples that demonstrate specific desired output format (location, issue, severity, suggested fix) to achieve consistency
  • Providing few-shot examples distinguishing acceptable code patterns from genuine issues to reduce false positives while enabling generalization
  • Using few-shot examples to demonstrate correct handling of varied document structures (inline citations vs bibliographies, methodology sections vs embedded details)
  • Adding few-shot examples showing correct extraction from documents with varied formats to address empty/null extraction of required fields
4.3

Enforce structured output using tool use and JSON schemas

What you need to know

  • A tool whose input_schema is your output schema — not a prompt asking for JSON — is what makes output schema-compliant.
  • "auto" can return text instead of calling anything; never use it when structured output is mandatory.
  • strict mode eliminates syntax errors only. Shape is not correctness — semantic validation is 4.4’s job.
  • A required field the source lacks forces a fabricated value; optional or nullable turns a hallucination into an honest null.
  • Closed enums break on real data: add "unclear" and "other" plus a detail string.
tool_choice modes by guarantee, schema selection and fitGive the three tool_choice modes as rows against the three columns the exam tests — what the call guarantees, who selects the schema, and which stem it fits — so a stem maps to a mode in one lookup.guaranteeschema selectionfittool_choice autotool_choice autotool_choice anytool_choice anytool_choice forced tooltool_choice forced toolPlain text answer allowedPlain text answer allowedModel may skip every schemaModel may skip everyschemaUnsafe for mandatory structureUnsafe for mandatorystructureSome tool call guaranteedSome tool call guaranteedModel picks the schemaModel picks the schemaUnknown document typeUnknown document typeNamed tool call guaranteedNamed tool call guaranteedYou pick the schemaYou pick the schemaOne extraction must run firstOne extraction must run first
tool_choice modes by guarantee, schema selection and fit

Give the three tool_choice modes as rows against the three columns the exam tests — what the call guarantees, who selects the schema, and which stem it fits — so a stem maps to a mode in one lookup.

Extraction via tool use, and where each error class is caughtShow the concrete call path from document to validated record, and make clear that the tool schema removes syntax errors while only the semantic validator can catch wrong values.Extraction serviceExtractionserviceMessages APIMessagesAPISemantic validatorSemanticvalidatorDownstream systemDownstreamsystemdocument + extraction toolschema + tool_choicetool_use block,schema-compliant inputparsed record, nosyntax errors possiblesemantic errors(sums, wrong field)validatedrecord
Extraction via tool use, and where each error class is caught

Show the concrete call path from document to validated record, and make clear that the tool schema removes syntax errors while only the semantic validator can catch wrong values.

Worked examples

An extraction tool schema designed against fabrication
Scenario 6 · Structured Data Extraction

Note what is not required: any field the document may legitimately omit is nullable, so "absent" has a legal representation. The currency enum ships with other plus a detail string and an unclear value, so an unanticipated or genuinely ambiguous currency does not get forced into USD.

json Tool definition used purely to constrain output shape

{
  "name": "extract_invoice",
  "description": "Record the fields present in this invoice. Omit or null any field the document does not contain; never infer a value.",
  "input_schema": {
    "type": "object",
    "properties": {
      "invoice_number": { "type": "string" },
      "issue_date": {
        "type": ["string", "null"],
        "description": "YYYY-MM-DD, or null if the document has no issue date"
      },
      "vendor_tax_id": { "type": ["string", "null"] },
      "currency": {
        "type": "string",
        "enum": ["USD", "EUR", "GBP", "other", "unclear"]
      },
      "currency_detail": {
        "type": ["string", "null"],
        "description": "Required when currency is 'other'; the code as printed"
      },
      "line_items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "description": { "type": "string" },
            "amount": { "type": "number" }
          },
          "required": ["description", "amount"]
        }
      }
    },
    "required": ["invoice_number", "currency", "line_items"]
  }
}
Choosing tool_choice: unknown document type vs mandatory step
Scenario 6 · Structured Data Extraction

Two different problems, two different modes. When the incoming document could be an invoice, a purchase order or a receipt, "any" guarantees structured output and tells you which schema matched via the returned tool name. When a downstream enrichment step requires metadata to already exist, force that specific tool by name. Leaving either case on "auto" risks a plain-text answer that breaks the pipeline.

typescript tool_choice selected per situation

// Document type unknown: the model must call one of the schemas,
// and its choice of tool name is the classification.
const res = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 4096,
  tools: [extractInvoice, extractPurchaseOrder, extractReceipt],
  tool_choice: { type: 'any' },
  messages: [{ role: 'user', content: [documentBlock] }],
});

const call = res.content.find((b) => b.type === 'tool_use');
// call.name  -> which document type matched
// call.input -> the structured extraction, already schema-compliant

// Metadata must exist before the enrichment pass runs: force that one tool.
const meta = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 2048,
  tools: [extractMetadata, extractInvoice],
  tool_choice: { type: 'tool', name: 'extract_metadata' },
  messages: [{ role: 'user', content: [documentBlock] }],
});
Normalization rules in the prompt beside the strict schema
Scenario 6 · Structured Data Extraction

The schema says issue_date is a string and amount is a number. It cannot say that "11/02/26" is ambiguous, that "1.240,00" is European decimal notation, or that "about a tablespoon" is not a number. Those rules belong in the prompt, and they are what stops the model from silently guessing a convention.

text Prompt fragment paired with the extraction tool

Normalization rules:
- Dates: emit YYYY-MM-DD. If the source order is ambiguous (11/02/26) and the
  document gives no locale signal, set the date to null and set
  date_ambiguous to true. Do not pick an interpretation.
- Amounts: emit a decimal number with no currency symbol and no thousands
  separator. Treat "1.240,00" as 1240.00 only when the document uses comma
  decimals elsewhere; otherwise set the amount to null.
- Informal quantities ("about a tablespoon", "roughly room temperature"):
  leave the numeric field null and copy the phrase verbatim into the
  matching *_note field.
- Never convert, round, or infer a unit that the document does not state.

Anti-patterns

  • Prose JSON requests: instructing the model to "respond with only valid JSON matching this shape" instead of defining a tool with a JSON schema because prose leaves syntax errors, markdown fences and preamble on the table while tool use eliminates them.
  • Leaving tool_choice on "auto" when structured output is mandatory instead of using "any" or a forced named tool because with "auto" the model may legitimately answer in plain text and the pipeline breaks.
  • All-required schemas: marking every field required so downstream code never sees a null, instead of making genuinely optional fields nullable because the model then fabricates a value to satisfy the schema.
  • Treating schema compliance as correctness: skipping semantic checks because the tool-use output validated, instead of verifying sums and field placement because a strict schema eliminates syntax errors and nothing else.

How it is examined

  • Map the stem to the tool_choice mode: "document type is unknown, several extraction schemas exist""any"; "this extraction must run before the enrichment step" → forced {"type":"tool","name":...}; "structured output is required" → anything but "auto".
  • Distractors here are prompt-side or post-processing fixes: "add 'return only JSON' to the prompt", "add a JSON repair/retry parser", "lower output length". The credited answer is tool use with a JSON schema.
  • If the stem describes valid JSON whose values are wrong (line items that do not sum, a value in the wrong field), the fault is semantic and the answer lives in 4.4 — not in a stricter schema or more required fields.
Why this is true

Tool use is the schema enforcement mechanism

The reliable way to get structured output is not to ask for JSON in the prompt. Instead you define a tool whose input_schema is your output schema, let the model call it, and read the structured data out of the tool_use block. Because the model emits tool input rather than free text, you get schema-compliant output and JSON syntax errors are eliminated: no markdown fences, no preamble, no trailing comma to repair. The extraction "tool" usually executes nothing on your side; its schema exists purely to constrain the shape of the answer.

This is the API-layer mechanism. When the surface is Claude Code in CI rather than a direct API call, the equivalent controls are --output-format json plus --json-schema (3.6). Same goal of a schema-enforced machine-readable result, expressed as CLI flags instead of a tool definition.

The three tool_choice modes

The distinction the exam tests:

Mode Guarantee Who picks the tool Use it when
"auto" May call a tool, may return plain text Model Never, when structured output is mandatory
"any" Must call a tool Model Several schemas, document type unknown — the tool name doubles as the classification
{"type": "tool", "name": "..."} Must call that tool You One particular extraction has to run, e.g. metadata before enrichment consumes it

Strict schemas stop syntax errors, not semantic ones

This is the highest-value nuance in the statement. JSON Schema's strict mode is the feature name to know here: it makes the provider enforce the declared shape rather than treat it as a hint. The guide lists it under JSON Schema as strict mode for syntax error elimination — syntax errors, and nothing more. A strict schema guarantees the shape: required keys present, values of the right type, enum values legal. It guarantees nothing about meaning. Line items can still fail to sum to the stated total; a vendor tax ID can land in the invoice-number field; a date can be wrong in perfect ISO 8601. Schema compliance is not correctness, and the remedy is semantic validation (4.4), not a stricter schema.

Schema design that does not induce fabrication

Two design decisions carry most of the weight:

  • Required vs optional. If a source document may legitimately not contain a field, model it as optional or nullable. A field marked required forces the model to produce something, and what it produces is a fabricated value. Making absence representable is what converts a hallucination into an honest null.
  • Extensible enums. Closed enums break on real-world data. Add an "unclear" value for genuinely ambiguous cases, and an "other" value paired with a detail string so an unanticipated category can be captured rather than forced into the nearest wrong bucket.

Finally, a schema constrains structure but says nothing about formatting. Source documents write dates, currencies and units inconsistently. Put format normalization rules in the prompt alongside the strict schema: "dates as YYYY-MM-DD; amounts as decimals with no currency symbol; for a range, record the lower bound and note it".

Exam guide, verbatim — what is measured

Knowledge of

  • Tool use (tool_use) with JSON schemas as the most reliable approach for guaranteed schema- compliant structured output, eliminating JSON syntax errors
  • The distinction between tool_choice: "auto" (model may return text instead of calling a tool), "any" (model must call a tool but can choose which), and forced tool selection (model must call a specific named tool)
  • That strict JSON schemas via tool use eliminate syntax errors but do not prevent semantic errors (e.g., line items that don't sum to total, values in wrong fields)
  • Schema design considerations: required vs optional fields, enum fields with "other" + detail string patterns for extensible categories

Skills in

  • Defining extraction tools with JSON schemas as input parameters and extracting structured data from the tool_use response
  • Setting tool_choice: "any" to guarantee structured output when multiple extraction schemas exist and the document type is unknown
  • Forcing a specific tool with tool_choice: {"type": "tool", "name": "extract_metadata"} to ensure a particular extraction runs before enrichment steps
  • Designing schema fields as optional (nullable) when source documents may not contain the information, preventing the model from fabricating values to satisfy required fields
  • Adding enum values like "unclear" for ambiguous cases and "other" + detail fields for extensible categorization
  • Including format normalization rules in prompts alongside strict output schemas to handle inconsistent source formatting
4.4

Implement validation, retry, and feedback loops for extraction quality

What you need to know

  • A retry without the specific validation error in the prompt is just a re-roll; append the errors so the model can self-correct.
  • The follow-up request carries the original document, the failed extraction, and the specific validation errors.
  • Retries fix format and structural errors; they cannot supply information that is absent from the source document — escalate instead.
  • Tool use removes syntax errors, so your validator exists specifically for semantic errors: values that do not sum, values in the wrong field.
  • Pydantic is the guide-named library for this loop: it handles schema validation while your own checks raise semantic errors — schema-valid is not the same as semantically valid, and either failure feeds the same validation-retry loop.
  • Have the extraction emit calculated_total next to stated_total, and a conflict_detected boolean, so inconsistency is a comparison rather than a guess.
  • Tag findings with detected_pattern so dismissals can be analyzed by the code construct that triggered them.
Validate, retry with error feedback, escalateShow that the retry edge carries the document, the failed extraction and the specific errors, that the loop is bounded, and that an absent-information failure exits to escalation rather than looping.Extract via tool useExtract viatool useValidate semanticsValidatesemanticsRetry with error feedbackRetry witherrorfeedbackAcceptedAcceptedEscalated to humanEscalated tohumanQuarantinedQuarantinedschema-compliantrecordno semanticerrorsformat orstructural errorsinformation absentfrom sourcedocument + failedextraction + specific errorsretry budgetexhausted
Validate, retry with error feedback, escalate

Show that the retry edge carries the document, the failed extraction and the specific errors, that the loop is bounded, and that an absent-information failure exits to escalation rather than looping.

one transition at a time

Click a transition to highlight its label — the exit condition it tests, and for the retry edge the document, failed extraction and specific errors it carries.

Worked examples

A retry loop that feeds the validation errors back
Scenario 6 · Structured Data Extraction

The loop is bounded, the errors are part of the next prompt, and the terminal branch distinguishes "the model got the shape wrong" from "the data is not in this document". Only the first is worth retrying; the second is escalated with the errors attached so a human sees why.

typescript Validate → retry-with-error-feedback → escalate

let extraction = await extract(document);            // tool_use, schema-compliant
let errors = validateSemantics(extraction);          // sums, field placement, conflicts

for (let attempt = 1; attempt <= 2 && errors.length > 0; attempt++) {
  // The follow-up request includes the document, the failed extraction,
  // and the specific errors -- this is what makes it a correction, not a re-roll.
  extraction = await extract(document, {
    failedExtraction: extraction,
    validationErrors: errors,
    // e.g. "line_items sum to 1240.00 but stated_total is 1420.00"
    //      "vendor_tax_id looks like an invoice number, not a tax id"
  });
  errors = validateSemantics(extraction);
}

if (errors.length > 0) {
  // Retrying cannot invent data the document never contained.
  return errors.every(isInformationAbsentFromSource)
    ? escalateToHuman(document, extraction, errors)
    : quarantine(document, extraction, errors);
}
return extraction;
Self-correction fields in the extraction schema
Scenario 6 · Structured Data Extraction

Rather than recomputing totals outside the model and hoping the field mapping was right, the schema asks for both numbers plus an explicit conflict flag. The validator's job collapses to comparing two fields and reading one boolean, and a genuinely inconsistent source document is reported as such instead of being quietly resolved.

json Schema fragment enabling a one-comparison semantic check

{
  "stated_total": {
    "type": ["number", "null"],
    "description": "The total as printed on the document, verbatim"
  },
  "calculated_total": {
    "type": "number",
    "description": "The sum of line_items[].amount that you extracted"
  },
  "conflict_detected": {
    "type": "boolean",
    "description": "true when the source document itself is internally inconsistent"
  },
  "conflict_note": {
    "type": ["string", "null"],
    "description": "Required when conflict_detected is true: which values disagree"
  }
}
detected_pattern turns dismissals into a feedback loop
Scenario 5 · Claude Code for Continuous Integration

Every finding records the construct that triggered it. After a week of PRs the dismissal data can be grouped by detected_pattern, which is how you discover that one construct accounts for most of the noise — the input to rewriting that category's criteria or adding a negative few-shot example.

json A structured finding carrying its trigger

{
  "location": "src/billing/invoice.ts:88",
  "category": "correctness",
  "severity": "HIGH",
  "issue": "total computed before discount is applied",
  "suggested_fix": "compute subtotal, apply discount, then sum tax",
  "detected_pattern": "reduce_over_items_without_discount_call",
  "dismissed": true,
  "dismissal_reason": "discount applied by caller in applyPromotion()"
}

Anti-patterns

  • Blind retry: re-sending the identical prompt after a validation failure instead of appending the specific validation errors because nothing in the second request tells the model what to correct.
  • Retrying for absent data: burning retries when the required value exists only in an external document you never supplied, instead of escalating or fetching the source because retry cannot create information the model never saw — and pressure to answer invites fabrication.
  • Treating tool-use schema compliance as validation: skipping semantic checks because the output parsed, instead of verifying sums and field placement because tool use eliminates syntax errors only.
  • Free-text findings with no detected_pattern field, instead of tagging each finding with the construct that triggered it because you cannot analyze dismissal patterns you never recorded.

How it is examined

  • When a stem says an extraction failed the same way after three retries and the missing figure lives in a document that was not provided, the credited answer is that retries are ineffective here — escalate or supply the source. "Increase the retry count" and "add few-shot examples" are the distractors.
  • When the stem says the JSON was valid but the line items do not sum to the total, look for semantic validation plus retry-with-error-feedback. "Make the schema stricter" or "mark more fields required" are wrong — the schema already did its job.
  • Options that merely log validation failures for later analysis are distractors against the option that includes the errors in the follow-up request. Logging is not feedback.
Why this is true

Retry only works if the model learns what failed

A retry that re-sends the original prompt unchanged is a re-roll: same inputs, same distribution, no reason to expect a different outcome. Retry-with-error-feedback means the follow-up request carries three things:

  1. the original document,
  2. the failed extraction exactly as the model produced it, and
  3. the specific validation errors — not "invalid output" but "line_items sum to 1240.00 but stated_total is 1420.00" and "vendor_tax_id contains what appears to be an invoice number".

With those three, the second attempt is a self-correction task on a concrete defect rather than a fresh guess.

Know which failures a retry can fix

Classifying the failure comes before choosing the response, and the guide treats that classification as its own skill.

Failure kind What it looks like Retry with error feedback? What to do
Syntax a markdown fence, a trailing comma, an unquoted key not applicable already eliminated upstream by tool use with a JSON schema (4.3)
Format or structural a date in the wrong notation, a nested object flattened, a normalization rule ignored yes — this is the class retry exists for resend the three items above, on a bounded loop
Semantic line items that do not sum to the stated total, a vendor tax ID sitting in the invoice-number field yes same loop, driven by your validator's errors
Information absent from the source a figure that lives only in a referenced external appendix you never supplied no escalate, or fetch the missing source and re-extract

No amount of re-asking creates data the model never saw. Retrying an absent-information failure just multiplies cost and, worse, pressures the model toward fabrication.

Semantic vs syntax errors

The semantic row is the one your validator exists for: values that do not reconcile, values in the wrong field, internally contradictory records. Because the schema cannot catch these, your validator is the only thing standing between a plausible-looking record and a corrupted downstream system.

Pydantic is the guide's named example of the validator in this loop. You declare the record as a Pydantic model and it does the schema validation, while your own field and cross-field checks raise the semantic validation errors. The guide's phrasing is "when Pydantic or JSON schema validation fails, send a follow-up request". The same validate → error → retry loop is what you build regardless of which of the two raised the failure. Keep the two levels distinct: schema-valid means the shape parses and the types match; semantically valid means the values are true of the source document.

Design the schema to help you validate

The strongest pattern is to make the model surface the evidence for its own consistency check:

  • Extract calculated_total (sum the line items yourself, in the extraction) alongside stated_total (what the document prints). A mismatch is then a single comparison rather than a re-derivation.
  • Add a conflict_detected boolean (with a note) so the model can report that the source is internally inconsistent, instead of silently picking one of two contradictory values.

Feedback loops beyond a single record

The same principle applies to review findings. Adding a detected_pattern field — the code construct that triggered the finding — turns individual dismissals into analyzable data. When developers dismiss findings, you can group by detected_pattern and see that, say, 90% of dismissals come from one construct, which points straight at the criteria to rewrite (4.1) or the negative few-shot example to add (4.2). Without that field you have a dismissal rate and no idea what to fix.

Exam guide, verbatim — what is measured

Knowledge of

  • Retry-with-error-feedback: appending specific validation errors to the prompt on retry to guide the model toward correction
  • The limits of retry: retries are ineffective when the required information is simply absent from the source document (vs format or structural errors)
  • Feedback loop design: tracking which code constructs trigger findings (detected_pattern field) to enable systematic analysis of dismissal patterns
  • The difference between semantic validation errors (values don't sum, wrong field placement) and schema syntax errors (eliminated by tool use)

Skills in

  • Implementing follow-up requests that include the original document, the failed extraction, and specific validation errors for model self-correction
  • Identifying when retries will be ineffective (e.g., information exists only in an external document not provided) versus when they will succeed (format mismatches, structural output errors)
  • Adding detected_pattern fields to structured findings to enable analysis of false positive patterns when developers dismiss findings
  • Designing self-correction validation flows: extracting "calculated_total" alongside "stated_total" to flag discrepancies, adding "conflict_detected" booleans for inconsistent source data
4.5

Design efficient batch processing strategies

What you need to know

  • Batch is a cost-for-latency trade: 50% cheaper, up to 24 hours, no latency guarantee.
  • Batch fits non-blocking latency-tolerant work (overnight reports, weekly audits, nightly test generation); blocking pre-merge checks stay on the synchronous API.
  • The batch API cannot do multi-turn tool calling inside one request — it cannot execute a tool mid-request and return the result.
  • custom_id is the correlation key for request/response pairs and the handle you use to resubmit only what failed.
  • Results are retrieved by polling for completion — there is no push callback, and polling does not shorten the 24-hour worst case, so it never makes batch safe for a blocking gate.
  • Submission cadence = promised SLA minus the 24-hour worst case; a 30-hour SLA implies submitting at least every 4 hours.
  • Refine the prompt on a sample set before batching a large corpus, so first-pass success is high and resubmission costs stay low.
Synchronous API vs Message Batches APIGive the learner a two-row table of the two APIs against the four columns the exam tests — cost, latency guarantee, multi-turn tool calling and workload fit — so a stem can be mapped to an API in one lookup.costlatencycapabilityfitSynchronous Messages APISynchronous MessagesAPIMessage Batches APIMessage Batches APIStandard pricingStandard pricingImmediate responseImmediate responseMulti-turn tools within one requestMulti-turn tools within onerequestBlocking pre-merge checksBlocking pre-mergechecks50% cost savings50% cost savings24-hour window, no SLA24-hour window, no SLAOne turn, no mid-request toolsOne turn, no mid-requesttoolsOvernight reports and weekly auditsOvernight reports andweekly audits
Synchronous API vs Message Batches API

Give the learner a two-row table of the two APIs against the four columns the exam tests — cost, latency guarantee, multi-turn tool calling and workload fit — so a stem can be mapped to an API in one lookup.

Fitting a 4-hour submission cadence inside a 30-hour SLAMake the SLA arithmetic visual: the queue wait plus the 24-hour worst-case processing window must fit inside the promised SLA, which is what fixes the submission interval.Hour 0 document arrivesdocument arrivesHour 4 batch submittedbatch submittedHour 28 results availableresults availableHour 30 SLA deadlineSLA deadlineQueued up to 4 hourswaits for next submission window ·submission intervalProcessing up to 24 hoursdocumented worst case · worst-casecompletiontwo hours of margin0h4h28h30h
Fitting a 4-hour submission cadence inside a 30-hour SLA

Make the SLA arithmetic visual: the queue wait plus the 24-hour worst-case processing window must fit inside the promised SLA, which is what fixes the submission interval.

Worked examples

Splitting a CI workload between the two APIs
Scenario 5 · Claude Code for Continuous Integration

The instinct to move everything to batches for the 50% discount is the trap. The pre-merge review blocks a human and must be synchronous; the nightly test generation and the weekly audit block nobody and are exactly what batches are for. The answer is a split, not a single API.

text Workload-to-API mapping

Pre-merge PR review        blocking, developer waiting   -> synchronous API
Post-merge deep analysis   non-blocking, results by 9am  -> Message Batches API
Nightly test generation    non-blocking, overnight       -> Message Batches API
Weekly compliance audit    non-blocking, 7-day cadence   -> Message Batches API
Interactive /explain cmd   blocking, human in the loop   -> synchronous API

Rule of thumb: if a person or a pipeline gate is waiting on the answer,
there is no latency SLA to rely on and batch is the wrong choice --
regardless of the 50% saving.
Selective resubmission keyed on custom_id
Scenario 6 · Structured Data Extraction

Results are consumed by custom_id, never by position. Failures are collected and resubmitted individually with a cause-appropriate modification — here, documents that exceeded the context limit are chunked before the retry, while other failures are resubmitted unchanged. Nothing that already succeeded is paid for twice.

typescript Collect failures, then resubmit only those

const failed: string[] = [];

for await (const r of client.messages.batches.results(batchId)) {
  if (r.result.type === 'succeeded') {
    save(r.custom_id, r.result.message);   // key by custom_id, not order
  } else {
    failed.push(r.custom_id);              // errored | expired | canceled
  }
}

// Resubmit only the failures, with a modification matched to the cause.
const retryRequests = failed.flatMap((id) =>
  exceededContextLimit(id)
    ? chunk(documents[id]).map((part, i) => buildRequest(id + '-part' + i, part))
    : [buildRequest(id, documents[id])]
);

if (retryRequests.length > 0) {
  await client.messages.batches.create({ requests: retryRequests });
}
Deriving submission frequency from a promised SLA
Scenario 6 · Structured Data Extraction

The arithmetic the exam expects. Only the 24-hour worst case is contractual, so the entire remaining budget is queue time, and the submission interval must fit inside it.

text SLA arithmetic

Promised end-to-end SLA .................... 30 h
Batch processing worst case ................ 24 h
Remaining budget for queue wait ............  6 h

Submission interval chosen ..................  4 h
Worst case for any document ..... 4 h wait + 24 h processing = 28 h  <= 30 h  OK

Submitting once daily (24 h interval) gives 24 + 24 = 48 h -- breaks the SLA
even though most batches would finish in under an hour. Plan against the
documented window, not against observed latency.

Anti-patterns

  • Batching a blocking gate: routing pre-merge PR review through the Message Batches API to capture the 50% discount because there is no latency SLA and the merge could wait up to 24 hours.
  • Full-corpus resubmission: resubmitting the entire batch after partial failures instead of only the failed custom_ids with a cause-appropriate fix because you pay a second time for work that already succeeded.
  • Batching an agentic loop: putting a multi-turn tool-calling workflow into a single batch request because the batch API cannot execute tools mid-request and return their results.
  • Batching before refining: submitting a large corpus before validating the prompt on a sample set because every first-pass failure becomes an iterative resubmission cost that dwarfs the sample run.

How it is examined

  • Stems give you a latency requirement and a cost pressure at the same time. The credited answer usually splits the workload — synchronous for the blocking path, batch for the overnight or weekly path — rather than choosing one API for everything.
  • Expect SLA arithmetic: subtract the 24-hour worst-case processing window from the promised SLA and the remainder is your maximum submission interval. Answers computed from typical observed latency are wrong.
  • An option that describes a batch job needing tool results mid-flight is wrong on the multi-turn constraint alone, even if its latency profile looks acceptable.
Why this is true

What the Message Batches API buys and what it costs

The trade is explicit, and it runs on more axes than cost.

Synchronous Messages API Message Batches API
Cost standard pricing 50% cost savings
Latency immediate response up to 24 hours, no guaranteed SLA
Tool calling multi-turn tool calling within one request does not support multi-turn tool calling within a single request
Retrieval the response is the return value poll for completion; no push callback
Workload it fits blocking work: pre-merge gates, interactive commands non-blocking, latency-tolerant work: overnight quality reports, weekly compliance audits, nightly test generation, corpus backfills

Most batches finish far sooner than the window, but you cannot build a promise on that. The only number you may plan against is 24 hours, which is why a pre-merge check that gates a developer's merge cannot sit in that queue.

The capability limit deserves reading precisely, because the exam tests its exact scope. The guide excludes the multi-turn case, so an agentic loop that needs to call a tool, read the result and continue belongs on the synchronous API. The wording implies that a one-shot request returning a tool_use block — the structured-extraction pattern from 4.3 — is not what the restriction is about, since no tool is executed mid-request. But the guide does not state that capability, so treat it as a reading of "multi-turn" rather than a fact to key an answer on. What is testable is the exclusion itself.

custom_id is the join key, and results are polled

Each request carries a custom_id and every result comes back with it. Results are not guaranteed to arrive in submission order, so custom_id — not array position — is how you correlate response to request, and how you identify precisely which documents failed.

Retrieval is a pull, not a push: you check the batch's status until it is done, then read the results. Nothing wakes your pipeline up when the work finishes, which is a second reason batch cannot sit under a blocking gate. Adding status polling does not rescue such a workflow either, because progress reporting shortens nothing.

Sizing submission cadence against an SLA

The guide's own worked example: you promise a 30-hour SLA and batch processing may take up to 24 hours. That leaves 6 hours of slack, which is the maximum time a document may wait in your queue before submission. Submitting every 4 hours therefore keeps the worst case at 4 + 24 = 28 hours, inside the promise with margin. The general recipe: maximum queue window = promised SLA − 24 hours, then pick a submission interval at or below that.

Handling failures and de-risking the first pass

When a batch ends with partial failures, resubmit only the failed custom_ids, with a modification appropriate to the cause — chunk the documents that exceeded context limits, fix the ones that hit a validation problem. Resubmitting the whole batch pays a second time for work that already succeeded.

Because a wrong prompt is amplified by volume, refine the prompt on a small sample set before batch-processing large volumes. Every first-pass failure becomes an iterative resubmission cost, so a sample run that lifts the first-pass success rate is usually the cheapest step in the whole pipeline.

Exam guide, verbatim — what is measured

Knowledge of

  • The Message Batches API: 50% cost savings, up to 24-hour processing window, no guaranteed latency SLA
  • Batch processing is appropriate for non-blocking, latency-tolerant workloads (overnight reports, weekly audits, nightly test generation) and inappropriate for blocking workflows (pre-merge checks)
  • The batch API does not support multi-turn tool calling within a single request (cannot execute tools mid-request and return results)
  • custom_id fields for correlating batch request/response pairs

Skills in

  • Matching API approach to workflow latency requirements: synchronous API for blocking pre-merge checks, batch API for overnight/weekly analysis
  • Calculating batch submission frequency based on SLA constraints (e.g., 4-hour windows to guarantee 30-hour SLA with 24-hour batch processing)
  • Handling batch failures: resubmitting only failed documents (identified by custom_id) with appropriate modifications (e.g., chunking documents that exceeded context limits)
  • Using prompt refinement on a sample set before batch-processing large volumes to maximize first- pass success rates and reduce iterative resubmission costs
4.6

Design multi-instance and multi-pass review architectures

What you need to know

  • A model that just generated code retains the reasoning behind it, so it is unlikely to question its own decisions in the same session.
  • An independent instance with no prior reasoning context catches subtle issues better than a self-review instruction or extended thinking.
  • Split large multi-file reviews into per-file passes for local issues plus separate integration passes for cross-file data flow.
  • Merge the two finding sets rather than concatenating them: dedupe on (location, detected_pattern), integration passes win on contract questions and local passes win on line-level ones. Why a single giant pass degrades is covered in 1.6.
  • A verification pass where the model self-reports confidence per finding enables calibrated routing (auto-comment vs human triage).
  • Confidence is routing metadata attached to findings, not the criterion that decides what counts as a finding (see 4.1).
  • Routing thresholds must be calibrated against a labeled validation set (5.5), and confidence is never an escalation trigger in a live conversation (5.2) — the axis is three-way: banned upstream (4.1), credited as calibrated downstream routing (4.6), rejected in-flight (5.2).
Pass taxonomy, merge rule and confidence routingShow the two pass types with the question each asks and the question each structurally cannot answer, then how their findings are merged by authority and routed by calibrated self-reported confidence.Large multi-file change setLargemulti-filechange setPer-file local passPer-filelocal passCross-file integration passCross-fileintegrationpassBlind spot, caller preconditionsBlind spot,callerpreconditionsBlind spot, line-level detailBlind spot,line-leveldetailMerged findingsMergedfindingsVerification pass with confidenceVerificationpass withconfidenceAuto-posted PR commentsAuto-postedPRcommentsHuman triage queueHumantriage queueasks logic errorsand uncheckedinputsasks contract anddata-flowmismatchesstructurally cannotanswerstructurally cannotanswerlocal findings winon line-levelquestionsintegration findingswin on contractquestionsdedupe on locationand detectedpatternhigh confidence,calibrated thresholdlower confidence,calibrated threshold
Pass taxonomy, merge rule and confidence routing

Show the two pass types with the question each asks and the question each structurally cannot answer, then how their findings are merged by authority and routed by calibrated self-reported confidence.

one transition at a time

Hover a pass to see which question it asks and which it structurally cannot answer.

Worked examples

A second independent instance reviews the generated code
Scenario 5 · Claude Code for Continuous Integration

The key detail is the message history: the reviewer call starts from an empty conversation and receives the requirements plus the diff, never the generator's transcript or thinking. It must reconstruct intent from the artifact, which is what makes it able to disagree with the generator's assumptions.

typescript Generator and reviewer do not share context

// Pass 1 -- generation. This conversation holds all the design reasoning.
const generation = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 8192,
  messages: generatorHistory,     // requirements, exploration, revisions
});
const code = extractCode(generation);

// Pass 2 -- review by an INDEPENDENT instance. Fresh message list:
// the generator's reasoning is deliberately NOT carried over.
const review = await client.messages.create({
  model: 'claude-opus-5',
  max_tokens: 8192,
  system: reviewerCriteria,       // explicit report/skip criteria (4.1)
  tools: [reportFindings],        // schema-enforced findings (4.3)
  tool_choice: { type: 'tool', name: 'report_findings' },
  messages: [{ role: 'user', content: renderReviewRequest(requirements, code) }],
});

// Anti-pattern for contrast:
//   generatorHistory.push({ role: 'user', content: 'Now review your own code.' })
// -- same session, same reasoning context, confirms rather than challenges.
Per-file passes plus a cross-file integration pass
Scenario 5 · Claude Code for Continuous Integration

A 40-file PR is decomposed into the two pass types, each defined by the question it can answer and the question it structurally cannot, and the two finding sets are then reconciled by an explicit merge rule. Note that the merge is not a concatenation: findings are deduped and every collision is resolved in favor of whichever pass had the visibility to answer it.

text Pass decomposition for a large PR

LOCAL PASSES  (one per changed file, N = 40)
  Input:  the single file's diff + surrounding context for that file
  Asks:   logic errors, unchecked inputs, error handling, resource leaks
  Cannot: judge whether callers satisfy this file's new preconditions

INTEGRATION PASSES  (grouped by data flow, N = 4)
  Input:  the signatures/contracts that changed + every call site touching them
  Asks:   type and contract mismatches across module boundaries,
          state mutated in one module and read in another,
          a precondition assumed by the callee and not met by the caller
  Cannot: see line-level detail inside unrelated files

MERGE  (a reconciliation, not a concatenation)
  1. Dedupe on (location, detected_pattern).
  2. On a collision, the pass that could see the answer wins:
       contract / data-flow question  -> integration finding
       line-level question            -> local finding
  3. Findings only one pass type could produce pass through unchanged.

  Result: one finding set in which no two entries disagree about the same
  construct, because each question type has exactly one authoritative pass.

TAIL
  A verification pass then attaches self-reported confidence to each merged
  finding, and the pipeline routes on it -- thresholds calibrated against a
  labeled validation set (5.5), never used to escalate a live conversation.
Confidence-annotated findings for calibrated routing
Scenario 5 · Claude Code for Continuous Integration

A verification pass re-examines the merged findings and attaches a self-reported confidence to each. The pipeline routes on that value: high confidence posts automatically, medium goes to a human triage queue, low is recorded for pattern analysis but not shown. Confidence is used to route — not to decide what the reviewer should have looked for.

json Verification-pass output and its routing rule

[
  {
    "location": "src/billing/invoice.ts:88",
    "issue": "total computed before discount is applied",
    "severity": "HIGH",
    "detected_pattern": "reduce_over_items_without_discount_call",
    "confidence": "high",
    "route": "post_as_pr_comment"
  },
  {
    "location": "src/sync/worker.ts:142",
    "issue": "possible race on the cursor when two workers start together",
    "severity": "MEDIUM",
    "detected_pattern": "shared_cursor_without_lock",
    "confidence": "medium",
    "route": "human_triage_queue"
  }
]

Anti-patterns

  • In-session self-review: appending "now critically review your own code" to the generating conversation instead of spawning an independent instance because the model still holds the reasoning that produced the code and confirms rather than challenges it.
  • Extended thinking as a substitute for independent review because more reasoning inside the same context does not remove the context bias that self-review suffers from.
  • One-shot mega review: sending every changed file in a single review prompt instead of per-file passes plus separate integration passes because one pass collapses the two question types into one context and the local and cross-file findings compete there instead of being merged under an explicit rule (the degradation mechanism itself is covered in 1.6).
  • Dropping confidence from findings and treating every finding as equal instead of having the verification pass self-report confidence because you lose the signal that would let you route findings to auto-comment or human triage.

How it is examined

  • When a stem says an agent generates code, reviews it, and still misses subtle bugs, the credited answer is a second independent instance without the generator’s reasoning context. "Add a self-review instruction", "enable extended thinking", and "raise the reasoning effort" are the standard distractors.
  • When a stem asks what happens after decomposition — how two sets of findings get reconciled, or which pass type owns a given kind of issue — the answer is the two-type taxonomy plus an explicit merge step (dedupe, integration wins on contracts, local wins on line detail). The prior question of why one giant pass degrades at all is 1.6 territory.
  • Confidence is a three-way axis, so read the exact role the stem assigns it: (a) an upstream filter deciding what counts as a finding is a distractor (4.1); (b) downstream routing metadata on an already-generated finding, with thresholds calibrated on a labeled validation set, is credited (4.6 with 5.5); (c) a threshold that escalates a live conversation to a human is wrong (5.2, official Sample Question 3 — the agent is already incorrectly confident on the hard cases). Between two routing options, the one that mentions calibration is the stronger.
Why this is true

Why a model is a poor reviewer of its own work

When a model generates code and is then asked, in the same session, to review it, it still holds the reasoning that produced that code. Every design decision already looks justified, because the justification is right there in context. The result is that self-review is less likely to question its own decisions — it tends to confirm rather than challenge. This is a context property, not a capability shortfall. That is why the guide is explicit that independent review instances, running without the generator's prior reasoning context, catch subtle issues more effectively than either a self-review instruction ("now critically review your work") or extended thinking. Adding more reasoning inside the same context does not remove the bias built into that context.

Practically: for generated code, spin up a second Claude instance that receives the code and the requirements but not the generation transcript. It has to reconstruct intent from the artifact, which is exactly what a human reviewer does and exactly what surfaces the assumption the generator never questioned.

Multi-pass review: the pass taxonomy and the merge rule

Why a single giant pass degrades is covered in full in 1.6 — attention dilution, contradictory findings inside one PR, and why a larger context window is not the remedy. Here the subject is the shape of the decomposition and what becomes of the findings afterwards.

The taxonomy has exactly two pass types, each defined by the question it can answer.

Pass type The question it answers What it structurally cannot answer Wins the merge on
Per-file local pass — one per changed file, sees that file's diff logic errors, error handling, unchecked inputs whether callers satisfy this file's new preconditions line-level questions — the only pass that saw the file in detail
Separate integration pass — one per data flow, sees the changed contracts and every call site touching them cross-file data flow: does the emitted type match what the consumer expects, does the caller apply the discount the callee assumes, does a contract change break a distant call site line-level detail inside unrelated files contract and data-flow questions — the only pass that saw both sides

Because the two types answer different questions they get different prompts, and they produce two finding sets that must be merged rather than concatenated. The merge rule falls straight out of the taxonomy:

  1. Dedupe on (location, detected_pattern).
  2. Resolve each collision in favor of the pass that could see the answer, as the last column above records.
  3. Pass through untouched any finding that only one pass type could have produced.

Verification passes with self-reported confidence

The third technique is a verification pass in which the model self-reports confidence alongside each finding, so findings can be routed by calibration. High-confidence findings post automatically as PR comments, low-confidence findings go to a human triage queue, and the rest are dropped or batched. Note the careful distinction against 4.1 — confidence is used downstream, as routing metadata on an already-generated finding, not upstream as the criterion that decides whether the model should report at all. Asking the model to filter by confidence during finding does not improve precision; asking it to label findings with confidence so your pipeline can route them does add value.

Two preconditions bound that value, and both live in Domain 5:

  • The thresholds must be calibrated. A self-reported "high" is not a probability until you have measured what it means. The procedure is 5.5's: have the model emit field-level or finding-level confidence, then set the routing thresholds against a labeled validation set, so "auto-post above this level" reflects a measured accuracy rather than an assumption. An uncalibrated threshold is 4.1's anti-pattern wearing a number.
  • Routing is not escalation. Confidence is never the trigger for handing a live conversation to a human (5.2). Official Sample Question 3 rejects precisely that option: the agent is already incorrectly confident on the hard cases, so a confidence threshold escalates the easy ones and keeps the hard ones. Explicit escalation criteria with few-shot examples are the credited fix there. Routing works here because a finding already exists and is being sorted offline against calibrated data; escalation fails because the decision must be made in-flight by the same miscalibrated model.
Exam guide, verbatim — what is measured

Knowledge of

  • Self-review limitations: a model retains reasoning context from generation, making it less likely to question its own decisions in the same session
  • Independent review instances (without prior reasoning context) are more effective at catching subtle issues than self-review instructions or extended thinking
  • Multi-pass review: splitting large reviews into per-file local analysis passes plus cross-file integration passes to avoid attention dilution and contradictory findings

Skills in

  • Using a second independent Claude instance to review generated code without the generator's reasoning context
  • Splitting large multi-file reviews into focused per-file passes for local issues plus separate integration passes for cross-file data flow analysis
  • Running verification passes where the model self-reports confidence alongside each finding to enable calibrated review routing