Last Step/Context Management and Reliability15%
Domain 56 task statements15% of the exam

Context Management and Reliability

How to keep Claude accurate when context grows, history compresses, and multiple agents must share state without losing provenance or calibration.

Context management is the discipline of deciding what occupies Claude's finite token budget on every request and ensuring that critical facts survive the compression, truncation, and positional degradation that long sessions impose. The window is 1,000,000 tokens on Sonnet 5, Opus 5, Opus 4.6 and later, Sonnet 4.6, and the Fable and Mythos families, and 200,000 tokens on every other model including Sonnet 4.5 and Haiku 4.5. Everything in the request counts against it: system prompt, tool definitions, every message including tool results, images and documents, and the output including extended thinking. A prompt can fit and still perform worse, because accuracy and recall degrade as the token count grows, a documented effect named context rot, so passive filling reliably degrades output quality long before any limit is hit.

This domain's six tasks map to six places where reliability fails when context grows. Task 5.1 makes context window allocation explicit through the persistent case facts block that survives summarisation and the positional rules that defeat the lost-in-the-middle effect, plus prompt caching that saves money without saving attention. Task 5.2 calibrates escalation with three valid triggers and two unreliable anti-patterns so first-contact resolution depends on case difficulty not on sentiment. Task 5.3 replaces silent suppression and workflow termination with structured error propagation that preserves partial results and distinguishes access failures from valid empty results. Task 5.4 contains context degradation during extended codebase exploration through scratchpad files, subagent delegation for isolation, and summary injection between phases. Task 5.5 replaces aggregate metrics and raw confidence with stratified calibration and dynamic review routing that allocates limited human capacity where uncertainty is highest. Task 5.6 makes provenance survive synthesis through five-field claim-source mappings, conflict preservation, and content-appropriate rendering.

Two principles cut across every task. First, structure at the source beats instruction after the fact. Extracting critical facts into a protected block before summarisation survives compression, while telling the model to pay attention to everything does not defeat a positional effect. Returning structured error objects with failure type, attempted action, partial results, and alternatives enables recovery, while returning empty results marked as success hides the failure. Preserving claim-source mappings through every synthesis step keeps attribution traceable, while assuming the model will preserve it by default loses it on first paraphrase. Second, aggregate signals hide segment failures and raw model signals are poorly calibrated. A 97 percent aggregate can hide 45 percent accuracy on international documents, and 0.95 confidence on a date field can mean 99 percent accuracy while the same score on an amount field means 56 percent. Production therefore measures per-type and per-field accuracy, calibrates per stratum on labelled data, and samples high-confidence automated extractions to catch novel patterns that low-confidence sampling misses.

On this page
  1. 5.1 Context Window Management Protect transactional facts with a persistent block, place critical content at the start and end, and use positional prompt caching to save cost without losing attention.
  2. 5.2 Escalation and Ambiguity Resolution Route by three valid triggers, reject sentiment and raw confidence as signals, and ask for a disambiguating identifier when tool results are ambiguous.
  3. 5.3 Error Propagation in Multi-Agent Systems Propagate structured errors with partial results, distinguish access failures from valid empty results, and make gaps visible with coverage annotations.
  4. 5.4 Codebase Exploration and Context Degradation Treat verbose exploration as a context quality problem and contain it with scratchpads, isolated subagents, and injected summaries.
  5. 5.5 Human Review and Confidence Calibration Measure accuracy by document type and field, calibrate per-stratum confidence, and prioritise limited reviewer time by uncertainty.
  6. 5.6 Information Provenance and Multi-Source Synthesis Carry five-field claim-source mappings through every merge, preserve conflicts with both values, and render by content type so gaps are never mistaken for relevance.
The essentials
Everything below explains why each of these is true.
  1. The context window is 1M on Sonnet 5, Opus 5, Opus 4.6 and later, Sonnet 4.6, Fable 5, and Mythos 5, and 200K on every other model including Sonnet 4.5 and Haiku 4.5. On a 1M-window model, 1M is the default with no beta header and standard pricing, and a single request can still generate at most 128K output tokens. Quality does not hold flat across that range: accuracy and recall degrade as the count grows, the documented effect called context rot, so curating what is in context matters as much as how much room is left.
  2. The Messages API is stateless and every request must include the complete conversation history. The system field accepts a plain string or an array of structured blocks each with cache_control type ephemeral for prompt caching.
  3. A persistent case facts block extracts amounts, dates, order numbers, statuses, deadlines, and session-governing preferences into a structured object that is prepended to every prompt and is never summarised, never FIFO truncated, and never compressed.
  4. Prompt caching is positional and matches from the first token prefix by prefix, so the stable prefix must start at index 0 and the cache marker belongs at the boundary between static and volatile content. Default TTL is about five minutes, extended-cache raises it to about one hour at a higher read rate.
  5. A practical allocation for a support agent with five tools is roughly 10 to 15 percent for system prompt, 10 to 15 percent for tool definitions at about 3,500 tokens each, 30 to 40 percent for conversation history, 10 to 20 percent for retrieved documents, and a 5 to 10 percent safety buffer that is non-negotiable.
  6. Escalation has three valid triggers: explicit human request honoured immediately with no investigation, policy gap where documented policy is silent and requires human judgement, and inability to make meaningful progress after a genuine attempt. Sentiment and self-reported confidence are unreliable triggers.
  7. Ambiguous customer matching must ask for an additional identifier via a clarification request. Selecting the most recent or most active record risks privacy violations and incorrect actions.
  8. Structured error context carries failure type from transient, validation, business, and permission, attempted action, partial results, and alternative approaches, and distinguishes access failure where the query did not execute and should retry from valid empty result where the query executed and found no matches and should not retry.
  9. Context degradation is not a token limit problem. Verbose discovery output buries earlier precise findings and the model reverts to typical patterns instead of specific class names and file paths. Mitigations are scratchpad files, subagent delegation for isolation, summary injection between phases, and proactive compaction rather than compaction only at the limit.
  10. Server-side compaction is the documented primary strategy for long-running conversations. Add the compact_20260112 strategy to context_management.edits with the compact-2026-01-12 beta header. The default trigger is 150,000 input tokens and the configured value must be at least 50,000. The API summarises, emits a compaction block, and on later requests drops every block before it.
  11. Context editing is the fine-grained alternative under the context-management-2025-06-27 header, with clear_tool_uses_20250919 for tool results and clear_thinking_20251015 for thinking blocks. Clearing runs server-side before the prompt reaches the model, so the client keeps its own full history and never syncs. Tool result clearing invalidates the cached prefix, which is why clear_at_least exists.
  12. The memory tool, type memory_20250818, is client-side: the model requests view, create, update, and delete operations under /memories and your handler executes them against storage you control and must reject any path outside /memories. It is the supported surface for the scratchpad pattern, documented as just-in-time context retrieval.
  13. Provenance has documented API-level enforcement. Setting citations enabled true on a document block returns structured citations whose cited_text does not count toward output tokens and is guaranteed to point into the supplied document. Search result content blocks carry a required source and title for your own retrieved content, where source can be a URL or a stable internal identifier.
  14. Stratified sampling must include high-confidence automated extractions proportionally by document type and confidence band. Raw confidence must be calibrated per field per document type on labelled validation data before any routing threshold is set.
  15. Provenance requires five fields per finding: claim, source URL, document name, relevant excerpt, and publication date. Conflicts between credible sources preserve both values with full attribution and a possible explanation, and financial data renders as tables, news as prose, and technical findings as structured lists.
  16. Cross-cutting patterns include selection, writing, compression, and isolation for context management, coverage annotations that make gaps visible by construction, and per-source attribution for the six context sources of instructions, external knowledge, tools, memory, state, and user prompts.
Task 5.118 min

Context Window Management

Protect transactional facts with a persistent block, place critical content at the start and end, and use positional prompt caching to save cost without losing attention.

What you need to know

Context window management is the daily practice of deciding what occupies the finite token budget that every Claude request draws from. The window is 1,000,000 tokens on Sonnet 5, Opus 5, Opus 4.6 and later, Sonnet 4.6, Fable 5, and Mythos 5, and 200,000 tokens on every other model including Sonnet 4.5 and Haiku 4.5, and within it the system prompt, tool definitions, MCP server payloads, conversation history, retrieved documents, and the response itself including extended thinking all compete for the same allocation. Accuracy and recall degrade as the token count grows, an effect documented under the name context rot, so passive filling reliably degrades output quality long before any limit is hit.

The single most important pattern is the persistent case facts block, sometimes called the immutable facts block. Its purpose is to defeat the progressive summarisation trap. When a long-running conversation is compressed between turns, abstractive summarisation systematically destroys the most critical information in transactional systems. A refund of 247.83 for order 8891 placed on March 3rd, after two summarisation passes, becomes the customer wants a refund for a recent order. The three facts the agent needs to process the refund are gone, and the model proceeds confidently with the wrong data. The fix is structural rather than prompt-based. Extract transactional facts into a structured block that is included in every prompt and is never summarised, never FIFO truncated, and never compressed. This block sits immediately after the system prompt, before any conversation history, and is updated in place as new critical facts emerge. The protected facts survive every compression cycle because the compression pipeline explicitly excludes them.

A related degradation mode is the lost-in-the-middle effect. Findings buried in the middle of a long context receive less attention than findings at the beginning or end, and this is a positional phenomenon rather than an instruction-following problem. Telling the model to pay attention to everything is unreliable because the attention pattern is shaped by position, not by intent. The structural fix is to place key findings summaries at the top of any aggregated input, organise detailed results with explicit section headers throughout, and use recency at the end of the input for the current user message so it gets strong attention. For a multi-source synthesis the format is always key findings summary first, detailed findings under explicit headers, and the synthesised answer last.

The Claude API is stateless. Every request must include the complete conversation history. Omitting earlier messages does not free context on the server, it just causes the model to lose conversational coherence. This creates the central tension of context management: the history must be present for coherence, but it grows with every turn. The persistent case facts block resolves this tension by separating critical facts from summarisable narrative, letting you compress the conversation flow while preserving every transactional detail verbatim.

The second major lever is prompt caching. Instead of trimming what the model sees, you avoid paying to reprocess the parts that do not change. Mark a stable prefix with a cache_control type ephemeral breakpoint and the API stores the processed prefix, then reuses it on the next request at approximately 10 percent of the standard input token cost. A 20,000 token system prompt running 10,000 requests per day is no longer 200 million tokens of identical content daily but a single full-price write per cache window and cached reads for the remainder, with savings compounding because every subsequent turn within a session also reuses the same cached prefix. Caching is positional. The cache matches from the start of the prompt prefix by prefix, so the order of blocks in the request body decides whether you get a hit. Put the content that stays constant first and place the cache_control breakpoint at the end of that static block. Volatile content goes after the breakpoint. Dynamic content sitting before the static block causes the prefix to change on every request, nothing matches, and every call pays full price.

The persistent case facts block and the summarisation trap

Abstractive summarisation collapses exact values into vague generalities that look reasonable but cannot drive correct downstream action. Numbers, dates, percentages, and explicit expectations are the most vulnerable fields because they are precise by nature, and a summariser that turns 247.83 on March 3rd into recent order has destroyed the transactional payload while preserving narrative gist. The case facts block is the only structural protection because it is excluded from the summarisation pipeline by construction.

The block is a runtime discipline, not an API feature. The application extracts facts from tool results and conversation turns, stores them in a structured object with fields such as customerId, orderId, refundAmount, orderDate, and status, and prepends the serialised object to every prompt construction. The block is updated in place as new facts emerge and for multi-issue sessions each issue gets its own entry with order IDs, amounts, and statuses so compressing one issue narrative does not bleed into another issue facts.

Lost in the middle, context rot, and token budget shape

Quality does not hold flat across the window. Anthropic documents the degradation directly: as token count grows, accuracy and recall fall, an effect named context rot, which is why curating what is in context matters as much as how much room is left. No official threshold is published, and community study material commonly quotes an effective ceiling somewhere in the high 140,000s, so treat any specific number as an estimate and reason about the direction of the effect rather than a cliff edge. A scenario describing a 180K context where the model misses a policy buried in the centre is testing positional degradation compounded with context rot, not a token limit being exceeded. The fix is to restructure so critical content sits at the start or end rather than buried in the middle, with key findings at the top, headers throughout, and the current question at the end for recency.

A practical allocation for a customer support agent with five tools keeps roughly 10 to 15 percent for system prompt, 10 to 15 percent for tool definitions at about 3,500 tokens each so five tools consume roughly 9 percent of a 200K budget before any conversation, 30 to 40 percent for conversation history, 10 to 20 percent for retrieved documents, and a 5 to 10 percent safety buffer for unexpected payloads. Without the buffer a single long user document can push the session deep into the degraded region mid-turn.

Rather than estimating, measure. Every response reports the split in its usage field, and with caching the input side is split across input_tokens, cache_read_input_tokens, and cache_creation_input_tokens, all three of which count toward the window. To size a request before sending it, call the token counting endpoint instead of guessing from character counts.

Compaction and context editing: the API-level levers

The reference treatment of this task predates the two features that now do most of this work at the API level, so learn them as the default and treat hand-rolled summarisation as the fallback. Server-side compaction is the documented primary strategy for long-running conversations. You add a strategy of type compact_20260112 to context_management.edits and send the compact-2026-01-12 beta header. The API watches the input token count, and when it crosses the trigger, default 150,000 with a configured floor of 50,000, it generates a summary, emits a compaction block in the response, and continues the turn from there. On every later request it drops all content blocks before that compaction block, so the block itself becomes the truncation boundary and you do not write the truncation logic. Optional fields are pause_after_compaction if you want to inspect the summary before continuing, and instructions if you want to replace the default summarisation prompt entirely.

The default summary prompt is documented and worth memorising because it is a reusable schema, not just an internal detail. It asks for Task Overview, Current State, Important Discoveries, Next Steps, and Context to Preserve, wrapped in summary tags. That is the shape a handoff needs, whether the summariser is the API or your own code, which is why the same five headings reappear in Task 5.4 as the shape of a phase carry-forward and a crash-recovery manifest.

Context editing is the finer-grained alternative under the context-management-2025-06-27 header. The clear_tool_uses_20250919 strategy clears the oldest tool results once the configured trigger is crossed, replacing each with placeholder text so the model knows something was removed, and it takes trigger, keep, clear_at_least, exclude_tools, and clear_tool_inputs. The clear_thinking_20251015 strategy does the same for thinking blocks. Two properties matter for design. First, clearing runs server-side before the prompt reaches the model, so your client keeps its own complete unmodified history and never has to reconcile with the edited version. Second, the cache interaction differs by strategy: clearing tool results invalidates the cached prefix at the clearing point, which is exactly why clear_at_least exists, so you only pay a cache write when you are clearing enough tokens to make it worthwhile, while thinking-block clearing preserves the cache as long as blocks are kept.

None of this removes the need for the protected facts block. Compaction still summarises, and a summariser under length pressure still trades exact figures for narrative gist. What compaction changes is who runs the summariser and where the boundary sits; what it does not change is that anything which must survive verbatim has to live outside the region being summarised.

Prompt caching mechanics and tool result trimming

The system field accepts either a plain string or an array of structured blocks where each block can carry its own cache_control. The array form enables fine-grained caching where the static reference document sits in one block with cache_control type ephemeral and per-request instructions sit in another block without a marker, so only the static block is reused. A request can carry up to four cache breakpoints, with default TTL around five minutes since last use and extended-cache raising it to about one hour at a higher read rate. Caching pays off for bursts of related requests, not for content reused hours apart.

Tool result trimming happens at the tool boundary before the result enters conversation history. A wrapper or hook strips verbose fields before the result is added to messages. Once verbose data is in the context it stays there for every subsequent turn, so trimming must happen at the boundary rather than after the fact. For order lookups this typically means reducing a 40-field response to the five fields relevant to the current task, cutting token cost by 80 to 90 percent, and multi-issue sessions must keep the trimmer from discarding disambiguating identifiers.

Authoritative mechanism reference

The sections below expand the tested rules into the complete documented surface: ownership, lifecycle, version differences, production boundaries, and adjacent mechanisms.

Mechanism reference: 1. Window sizes per model family and the 1M default

The context window is the total token budget from which the system prompt, every message, every tool result, every document, tool definitions, and the generated output all draw. The size of that budget is fixed per model; there is no longer a "standard 200K with an opt-in extended 1M" split, each model has the window it ships with.

The 1M-token window models are Opus 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 5, and Sonnet 4.6. The Fable 5, Mythos 5, and Mythos Preview models also carry a 1M window. Every other model, including Sonnet 4.5, has a 200K window. For each 1M-window model, 1M is the default: no beta header is required to use it and it is billed at standard pricing. A single request to a 1M-window model can generate up to 128,000 output tokens. A request can include up to 600 images or PDF pages, and the limit is 100 on a 200K-window model.

The lesson library records the same split from the product side: Opus 4.8 and Sonnet 4.6 (and Opus 4.7, Opus 4.6, Sonnet 5, Opus 5, Fable 5) at 1M, Haiku 4.5 at 200K. The key architectural consequence is that 1M is not "better" by default. A well-structured 150K prompt on a 1M model outperforms a sloppy 800K prompt on the same model, because the quality ceiling still applies inside the larger window. The window is a shared tank; how you fill it matters more than its size.

Mechanism reference: 2. Everything in the request counts toward the window

This is the single most important accounting rule and the source of most real context bugs. Everything in the request counts toward the window: the system prompt, every message including tool results, images, and documents, tool definitions, and the output including extended thinking. None of these is free or metered separately from the window budget.

The usage field reports the split. With caching enabled, the input count is split across three numbers that all count toward the window: input_tokens, cache_read_input_tokens, and cache_creation_input_tokens. All three are real tokens occupying the window; caching changes the price you pay, not the space the content takes. The lesson library states this in its own words: input tokens, output tokens, cache creation, and cache reads all appear in response.usage, and the cached tokens still occupy the context.

A practical boundary: output tokens count toward the window. If you send 180K input on a 200K model and set max_tokens to 32K, the request fails because 180K plus 32K exceeds 200K. Always reserve headroom for the reply. The lesson library's context-window diagram labels the free region as "Free: response headroom" for exactly this reason.

Mechanism reference: 3. Context rot: the documented name for quality loss at scale

The documented name for quality loss as token count grows is context rot. Accuracy and recall degrade as the count grows, so curating what is in context matters as much as how much room is left. The phenomenon is described as a monotonic degradation with token count, with no published threshold.

The lesson library names a specific effective ceiling of roughly 147,000 to 152,000 tokens, but documentation does not publish that number. Treat the specific figure as a community estimate and never as an official measurement. The documented phenomenon to cite on the exam is context rot; the high-140000s number is an estimate that the lesson library uses as a planning heuristic, not an Anthropic-measured limit.

This interacts with the lost-in-the-middle effect. Research shows a U-shaped performance curve: content at the very start and very end of the context is attended to most reliably, while content in the middle (roughly the 25 percent to 75 percent band) is more likely to be underweighted or missed. The lesson library documents this with a worked 180K-legal-contract example where a liability cap on page 247 is missed because it sits in the middle. The fix is structural: put critical instructions at the start (in the system prompt) and the current task at the end (in the latest user turn), and relegate background material to the middle.

Mechanism reference: 4. Server-side compaction: the documented primary strategy

Server-side compaction is the documented primary strategy for long-running conversations, not client-side summarisation. The strategy type is compact_20260112, the beta header is compact-2026-01-12, and it is passed inside context_management.edits. The default trigger is {"type": "input_tokens", "value": 150000}, and value must be at least 50,000. Optional fields are pause_after_compaction (default false) and instructions, which completely replaces the default summarisation prompt.

When the API detects the threshold, it generates a summary, emits a compaction block, and continues the response. On later requests it drops all blocks prior to the compaction block. This makes the compaction block itself the truncation boundary, which is exactly what the application must anchor on. Supported models are Fable 5, Mythos 5, Mythos Preview, Opus 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 5, and Sonnet 4.6.

The default compaction summary prompt is documented and structured as: Task Overview, Current State, Important Discoveries, Next Steps, Context to Preserve, wrapped in <summary></summary> tags. This is the documented shape of a handoff summary and is directly reusable as a scratchpad or phase-summary schema.

There is a documented limitation with server-side tools: after a web search the SDK may add cache_read_input_tokens accumulated across the tool's internal calls to the total, so the input count can read several hundred thousand tokens when the real context is far smaller, causing premature compaction. The workaround is the token counting endpoint, or avoiding client-side compaction with heavy server-side tool use.

Client-side SDK compaction exists only in the TypeScript and Ruby SDKs via tool_runner with compaction_control, default context_token_threshold of 100,000. The Python, C#, Go, Java, and PHP tool runners do not support it, and the docs point them to server-side compaction.

Mechanism reference: 5. Context editing: the fine-grained alternative

Context editing is the fine-grained alternative to compaction, under beta header context-management-2025-06-27. It has two strategies: clear_tool_uses_20250919 and clear_thinking_20251015.

Tool result clearing takes the fields trigger, keep, clear_at_least, exclude_tools, and clear_tool_inputs. Cleared results are replaced with placeholder text. Clearing happens server-side before the prompt reaches the model, so the client keeps its own full unmodified history and does not sync. This means the application can retain a complete, verbatim log for later audit or replay while the model sees a trimmed view.

Cache interaction is strategy-specific. Tool result clearing invalidates the cached prefix at the clearing point, which is why clear_at_least exists: you clear enough to make the cache write worth it. Thinking block clearing preserves the cache while blocks are kept, and invalidates at the clearing point when they are not kept.

Thinking block retention is model-dependent. Opus 4.5 and later, Sonnet 4.6 and later, Fable 5, Mythos 5, and Mythos Preview keep prior thinking blocks by default and they count as input tokens. Earlier Opus and Sonnet models and all Haiku models strip them automatically when passed back. Set keep explicitly when code spans model tiers.

Mechanism reference: 6. Prompt caching mechanics

Prompt caching lets you mark stable content with a cache_control breakpoint of {"type": "ephemeral"}. Everything up to and including the breakpoint is cached; content after the last breakpoint is processed fresh every request.

Prefix matching is order-dependent: the cache is checked in the order tools, then system, then messages. Layout decides whether you get a hit. Put the content that stays constant first: tool definitions, system instructions, long reference documents. Put the breakpoint at the end of that static block. Put the volatile content, the user's latest message and anything that changes per request, after the breakpoint.

You may set up to four breakpoints per request, each representing a layer of different reuse frequency from most stable to least stable. The minimum cacheable length is model-specific: 1,024 tokens for Sonnet 4.6 and Opus 4.8, and 4,096 tokens for Haiku 4.5. Below that minimum the API silently bypasses caching regardless of any cache_control you set.

The write multiplier is roughly 1.25x the standard input rate for the default 5-minute TTL and roughly 2x for the 1-hour TTL. The read multiplier is roughly 0.1x (10 percent) of standard input rate for either TTL. The TTL refreshes on every cache hit, so a high-traffic application effectively caches indefinitely; a nightly batch that pauses for hours re-pays the write cost.

Cached tokens still occupy the window. Caching reduces cost, not size. A 30K cached system prompt consumes 30K of window on every request; you simply pay less for it.

Mechanism reference: 7. Token counting to size a request before sending

The token counting endpoint (POST /v1/messages/count_tokens, exposed in the SDK as client.messages.countTokens()) returns exact token counts before you make a request. Use it to validate prompt size, estimate cost, and prevent context window overflow. This is the documented way to size a request rather than guessing from word counts. The lesson library shows a cost-estimation function built on countTokens that also returns withinContext by comparing input plus estimated output against the model limit.

Mechanism reference: 8. The persistent facts block (application-owned protection)

The persistent facts block is the application-owned protection that survives any summarisation or compaction. It is a structured block of transactional facts, amounts, dates, order numbers, and statuses, included in every prompt and never summarised. It sits outside the summarised region. The lesson library calls this the immutable facts block and places it immediately after the system prompt, before any conversation history.

This block remains necessary even though server-side compaction exists, because compaction still summarises, and anything the application must keep verbatim has to live outside the summarised region. The reference material omits compaction entirely and treats the facts block as the only protection; documentation now makes compaction the recommended strategy, with a documented trigger threshold, a documented summary structure, and a compaction block that the API uses as the truncation boundary. State the documented mechanism first, then the application-side protection.

Mechanism reference: 9. Tool result trimming at the boundary

Tool results are a silent context budget killer. An order lookup may return 40 or more fields; the application needs 5. Those other fields consume tokens in every subsequent turn as history grows. Trim verbose tool outputs to only the relevant fields before they accumulate in context. This should happen in a PostToolUse hook or in the tool implementation itself, before the result enters the conversation history. Once verbose data is in context, it stays there for every subsequent turn.

Mechanism reference: 10. Upstream agent optimisation and the stateless API

In multi-agent systems, upstream agents often return verbose reasoning chains and raw content that downstream agents do not need. Require subagents to return structured data, key facts, citations, and relevance scores, instead of verbose content and reasoning chains. This saves tokens and lets downstream agents process findings without re-parsing prose.

The API is stateless. Each request must include the complete conversation history. Omit earlier messages and the model loses conversational coherence. There is no server-side session state, so every turn has to carry everything the model needs. This creates tension with context limits: you need the full history for coherence, but the history grows with every turn. The persistent facts block resolves this by separating critical facts from summarisable narrative.

Ownership map

Which layer owns which guarantee:

  • The model owns attention behaviour: it reliably attends to the start and end of context and degrades in the middle. You cannot fix lost-in-the-middle with a prompt instruction; you fix it with layout.
  • The API and SDK own server-side compaction and context editing. When you enable compact_20260112 or a context-editing strategy, the API detects the threshold, rewrites the conversation, emits the compaction block, and enforces the truncation boundary. The client's retained history is untouched.
  • The API owns the prompt caching machinery: prefix matching, breakpoint accounting, TTL, and the usage split across input_tokens, cache_read_input_tokens, and cache_creation_input_tokens.
  • Application code owns the persistent facts block, tool-result trimming, priority-based truncation, and the decision of what content earns a place in the window. These are not API features; they are application disciplines the model and API cannot perform for you.
  • Tool implementations and PostToolUse hooks own tool-result trimming at the boundary.
  • The lesson library notes that Claude Sonnet 4.6 and Haiku 4.5 add a model-side context awareness signal (a session-start budget marker and a running per-tool-call usage update), but DOC-URLS directs that this behaviour be marked not independently confirmed for the specific marker and warning phrasing and not used as a decision rule, because it is not documented on the checked pages.

Version and terminology currency

The terminology has shifted between the exam guide era and the current product. The older "200K standard, 1M extended with a beta header" framing is gone: 1M is now the default on the 1M-window models with standard pricing and no beta header.

The compaction strategy name compact_20260112 and beta header compact-2026-01-12 are dated markers; the date in the name is the version, not an expiry. The context-editing beta header context-management-2025-06-27 and the strategy names clear_tool_uses_20250919 and clear_thinking_20251015 follow the same dated convention. When you read older community write-ups that call compaction a Claude Code feature only, note that /compact in Claude Code is one surface; the API-level compact_20260112 strategy is the programmatic equivalent for applications not running inside Claude Code. Keep both and say which layer each belongs to.

The memory tool is {"type": "memory_20250818", "name": "memory"}, available on Claude 4 and later. It is client-side: the model requests view, create, update, and delete operations under /memories and the application executes them against storage it controls, returning a tool_result. The /memories prefix is mapped by the handler onto real storage, and the handler must reject paths outside /memories (path traversal protection). Its documented purpose is just-in-time context retrieval so the active window stays focused.

Official versus community divergence

The reference material and current documentation disagree in six places that a candidate must navigate. Documentation wins; the divergences are restated here so the writer and the candidate can answer correctly.

  1. The reference material and the lesson library state a quality ceiling of roughly 147,000 to 152,000 tokens. No Anthropic page publishes that number. Documentation describes context rot as monotonic degradation with no published threshold. Treat the figure as a community estimate, name the documented phenomenon as context rot, and do not present the number as an official measurement.
  1. The reference material treats client-side progressive summarisation as the default long-conversation strategy and the persistent facts block as the only protection. Documentation now makes server-side compaction the recommended strategy, with a documented trigger threshold, a documented summary structure, and a compaction block used as the truncation boundary. The facts block remains correct and necessary, because compaction still summarises and anything that must survive verbatim has to live outside the summarised region. State the documented mechanism first, then the application-side protection, and note that the reference omits compaction entirely.
  1. The reference material presents provenance as an application-side discipline of five-field claim-source mappings preserved through synthesis by prompt instruction. Documentation provides two enforcement mechanisms the reference omits: the citations feature on document blocks and search result content blocks with required source and title. The exam-relevant judgement is unchanged, since attribution still has to survive multi-step synthesis in application code, but grounding must present the documented mechanisms as the preferred enforcement point and treat prompt-preserved mappings as what you do for content that does not arrive as documents or search results.
  1. The reference material describes the scratchpad file for long exploration as a purely local convention. Documentation names the same pattern as just-in-time context retrieval and provides the memory tool as the supported surface, with the important detail that execution and storage are the application's responsibility and path containment is a security requirement.
  1. The reference material describes the model receiving a token budget marker at session start and a running warning after each tool call. That behaviour is not documented on the checked pages. Mark it not independently confirmed and do not build a decision rule on it.
  1. The reference material treats /compact in Claude Code as the only compaction lever. It is one surface; the API-level compact_20260112 strategy is the programmatic equivalent for applications not running inside Claude Code. Keep both and say which layer each belongs to.

Beyond the task statement

The reference page covers the persistent facts block, lost-in-the-middle, tool-result trimming, statelessness, upstream agent optimisation, and prompt caching. The lesson library covers adjacent material the reference omits. Each item below lists its slug and why it matters for this task.

  • token-budgeting: introduces FIFO truncation, priority-based truncation (P0 through P4 tiers), and fixed versus dynamic versus priority allocation. It matters because the persistent facts block is the P1 tier in a priority scheme; understanding the tiers explains why the block must be tagged protected, not merely prepended.
  • context-compression: covers extractive versus abstractive summarization, the immutable facts block, sliding window versus summarization versus selective retention, and the built-in primitives (memory tool, context editing, compaction). It matters because it is where the documented server-side primitives are reconciled with the hand-built patterns.
  • token-management: covers token counting, the usage split, image and PDF token costs, output token ceilings (128K on 1M models, 64K on Haiku 4.5), and the fact that cached tokens still occupy the window. It matters because the exam tests budget tracking through usage.
  • context-engineering-stack: frames the whole window as a stack of layers (system, tools, retrieved context, history, scratchpad) and shows how each layer is cached, trimmed, or protected. It matters because it gives the facts block a precise home in the stack.
  • context-sources-taxonomy: a six-level taxonomy of where context originates (user input, retrieval, tool results, subagent output, memory, system). It matters because tool-result trimming and upstream agent optimisation are both source-tier decisions.
  • prompt-caching: the full caching reference, including the 1,024 versus 4,096 token minimums, the four-breakpoint limit, and the write/read multipliers. It matters because the reference page's cache example is correct but under-specified.

Worked production examples

The six examples below are complete, language-tagged, and evolve as one pipeline. Each names its language, states what it proves, its failure boundary, and its observable output. They cover the six required mechanisms: a persistent facts block with the prompt-construction function that guarantees it, an array-form system field with the correctly placed cache breakpoint shown next to the wrong placement and the usage fields that distinguish them, a server-side compaction request with an explicit trigger threshold, a tool-result clearing configuration with trigger, keep, clear_at_least, and exclude_tools, a token-counting call used to size a request before sending it, and a tool-result trimmer at the tool boundary.

Example 1: persistent facts block prepended outside the summarised region, with the prompt-construction function that guarantees it.

example.ts
typescript
type CaseFacts = {
  customerId: string;
  issues: Array<{
    orderId: string;
    orderDate: string;
    refundAmount: string;
    status: string;
    itemDescription: string;
  }>;
};

const CASE_FACTS_HEADER = "<immutable_facts>";

function buildPrompt(
  systemPrompt: string,
  facts: CaseFacts,
  summarisedHistory: string[],
  currentTurn: string
): string {
  const factsBlock =
    `${CASE_FACTS_HEADER}\n` +
    `customerId: ${facts.customerId}\n` +
    facts.issues
      .map(
        (i) =>
          `issue: order ${i.orderId} (${i.orderDate}) refund ${i.refundAmount} status ${i.status} - ${i.itemDescription}`
      )
      .join("\n") +
    "\n</immutable_facts>";

  // Order is deliberate: facts are prepended outside the summarised region,
  // then summarised history, then the live turn. Compaction and summarisation
  // act on the middle region only; the facts block is never in scope.
  return [systemPrompt, factsBlock, ...summarisedHistory, currentTurn].join("\n\n");
}

const facts: CaseFacts = {
  customerId: "C-4421",
  issues: [
    {
      orderId: "#8891",
      orderDate: "2024-03-03",
      refundAmount: "$247.83",
      status: "pending_refund",
      itemDescription: "Wireless headphones - defective",
    },
  ],
};

const prompt = buildPrompt(SYSTEM_PROMPT, facts, olderTurns, "What is my refund status?");

What this proves: the facts block is constructed by a function that always prepends it between the system prompt and the summarised history, so it sits outside the region that compaction or progressive summarisation touches. The block carries the exact amount, order id, and date that abstractive summarisation would otherwise destroy. Failure boundary: if any code path assembles the prompt without calling buildPrompt, the guarantee breaks; the function must be the single source of prompt assembly. Observable output: after a compaction that replaces olderTurns with a summary, the rendered prompt still begins with <immutable_facts> containing $247.83, #8891, and 2024-03-03 verbatim.

Example 2: array-form system field with the correctly placed cache breakpoint next to the wrong placement, plus the usage fields that distinguish them.

result.json
json
{
  "model": "claude-sonnet-5",
  "max_tokens": 4096,
  "system": [
    {
      "type": "text",
      "text": "You are a support agent. Rules: refund policy X, escalation path Y.",
      "cache_control": { "type": "ephemeral" }
    },
    {
      "type": "text",
      "text": "Large product catalogue reference document shared every turn.",
      "cache_control": { "type": "ephemeral" }
    }
  ],
  "messages": [
    { "role": "user", "content": "What is the status of order #8891?" }
  ]
}

Wrong placement: putting cache_control on the volatile user message, or interleaving dynamic text before the static block, breaks the stable prefix so every request pays full price. Correct placement is at the end of the static system array, as shown. The usage field distinguishes the outcomes:

result.json
json
{
  "usage": {
    "input_tokens": 312,
    "output_tokens": 84,
    "cache_creation_input_tokens": 18420,
    "cache_read_input_tokens": 0
  }
}

On a cache miss (first request, or after the prefix changed) cache_creation_input_tokens is high and cache_read_input_tokens is zero, and you paid the 1.25x write multiplier on the 18,420 cached tokens. On subsequent hits:

result.json
json
{
  "usage": {
    "input_tokens": 312,
    "output_tokens": 84,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 18420
  }
}

Now cache_read_input_tokens is high and you paid the 0.1x read multiplier. All three input numbers count toward the window even though only the read was cheap. Failure boundary: a one-character change in the static text invalidates the prefix and reverts to a write on the next call. Observable output: cache_read_input_tokens climbs on every repeated prefix; if it stays zero, the breakpoint is misplaced.

Example 3: server-side compaction request with an explicit trigger threshold.

result.json
json
{
  "model": "claude-opus-5",
  "max_tokens": 4096,
  "system": [{ "type": "text", "text": "You are a long-running research agent." }],
  "messages": [{ "role": "user", "content": "Begin the multi-hour investigation." }],
  "context_management": {
    "edits": [
      {
        "type": "compact_20260112",
        "trigger": { "type": "input_tokens", "value": 150000 },
        "pause_after_compaction": false,
        "instructions": "Summarise preserving exact figures, dates, and order ids. Keep the <immutable_facts> block verbatim."
      }
    ]
  },
  "anthropic-beta": "compact-2026-01-12"
}

What this proves: compaction is requested declaratively with an explicit trigger of 150,000 input tokens (above the 50,000 floor), an optional pause_after_compaction flag, and an instructions string that replaces the default summary prompt. The API detects the threshold, emits a compaction block, and on later requests drops everything before it. The application keeps its own full history; only the model's view is truncated. Failure boundary: a value below 50,000 is rejected, and compaction is unsupported on models outside the listed set (for example Haiku 4.5). Observable output: a response whose content contains a compaction block, after which subsequent requests omit all prior blocks.

Example 4: tool-result clearing configuration with trigger, keep, clear_at_least, and exclude_tools.

request.json
json
{
  "model": "claude-sonnet-5",
  "max_tokens": 2048,
  "tools": [{ "name": "order_lookup" }, { "name": "refund_processor" }],
  "messages": [{ "role": "user", "content": "Lookup and refund order #8891." }],
  "context_management": {
    "edits": [
      {
        "type": "clear_tool_uses_20250919",
        "trigger": { "type": "input_tokens", "value": 50000 },
        "keep": 5,
        "clear_at_least": 20000,
        "exclude_tools": ["refund_processor"],
        "clear_tool_inputs": false
      }
    ]
  },
  "anthropic-beta": "context-management-2025-06-27"
}

What this proves: the clear_tool_uses_20250919 strategy clears old tool results once input crosses 50,000 tokens, keeping the 5 most recent tool-use or result pairs, clearing at least 20,000 tokens' worth to make the cache write worthwhile, and excluding refund_processor results from clearing so the authoritative refund record survives. Cleared results are replaced with placeholder text server-side, so the client's history stays intact and unmodified. clear_at_least exists precisely because clearing invalidates the cached prefix at the clearing point, so you clear enough to justify the cache write. Failure boundary: if keep is set so high that nothing is cleared, the window still overflows; if clear_at_least is too small, the cache thrash costs more than the space saved. Observable output: the model receives placeholder text for old order_lookup results while the refund_processor result remains verbatim, and the client log shows the original results unaltered.

Example 5: token-counting call used to size a request before sending it.

example.ts
typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

async function sizeRequest(
  model: string,
  system: string,
  messages: Anthropic.MessageParam[],
  estimatedOutput: number,
  limit: number
): Promise<{ fits: boolean; inputTokens: number }> {
  const count = await client.messages.countTokens({
    model,
    system,
    messages,
  });
  const inputTokens = count.input_tokens;
  const fits = inputTokens + estimatedOutput <= limit;
  return { fits, inputTokens };
}

const result = await sizeRequest(
  "claude-opus-5",
  SYSTEM_PROMPT,
  conversationHistory,
  4096,
  1_000_000
);

if (!result.fits) {
  // Trim, compact, or summarise before sending. Do not fire a request that will overflow.
  throw new Error(`Request of ${result.inputTokens} input tokens exceeds budget.`);
}

What this proves: the token counting endpoint gives an exact input size before the request is sent, so you can refuse to send an oversized request rather than letting it fail mid-flight or trigger premature compaction. This is the documented sizing mechanism and the workaround for the web-search inflated-count limitation. Failure boundary: the estimate for output is your own guess, so add headroom; the count call itself consumes a round trip and should be cached or batched for high-volume paths. Observable output: result.fits is a boolean you can branch on; result.inputTokens feeds your budget dashboard.

Example 6: tool-result trimmer at the tool boundary.

example.py
python
RELEVANT_FIELDS = [
    "order_id",
    "order_date",
    "total_amount",
    "return_eligible",
    "item_description",
]

def trim_order_result(raw_result: dict) -> dict:
    return {k: v for k, v in raw_result.items() if k in RELEVANT_FIELDS}

def order_lookup(order_id: str) -> dict:
    raw = _call_backend(order_id)  # returns 40+ fields
    return trim_order_result(raw)  # trimmed before it ever reaches history

What this proves: trimming happens inside the tool, at the boundary, so the 35 irrelevant fields (warehouse codes, carrier ids, audit timestamps) never enter the conversation history and never cost tokens on subsequent turns. The trimmer returns a dict with only the five fields the agent acts on. Failure boundary: if the tool returns the raw object and trimming is deferred to a later step, the verbose payload has already been written into history and the damage is done; trimming must occur before the tool_result is returned. Observable output: the tool_result content is 80 to 90 percent smaller than the raw payload, and downstream turns no longer carry stale fields.

Build exercise material

Build a Persistent Case Facts Context Manager. Difficulty 45 minutes. What you will learn: implement the persistent facts block pattern, trim verbose tool results, mitigate progressive summarisation, apply lost-in-the-middle placement, and respect API statelessness.

Step 1: implement a case facts extractor that identifies transactional data (amounts, dates, order numbers, statuses) from tool results. Why: extracting transactional facts into a structured block that is never summarised prevents the progressive summarisation trap from destroying critical values. You should see: a function that takes raw tool output and returns a structured object containing only the transactional facts: customer id, order numbers, amounts, dates, and statuses. Non-transactional narrative content is excluded.

Step 2: implement a persistent facts block that is prepended to every prompt, outside summarised history. Why: the block must persist across every turn regardless of what happens to the conversation history. You should see: a prompt-construction function that always includes the facts block at the top, followed by any summarised history, followed by the current turn. The block is clearly delimited with a section header. Use the buildPrompt function from Example 1 as the reference implementation.

Step 3: build a tool-result trimmer that filters order lookup responses from 40+ fields to only the 5 relevant return-related fields. Why: untrimmed tool results are a silent context budget killer. You should see: a trimming function that takes a raw tool result and returns only the fields needed for the current task. The trimmed result should be 80 to 90 percent smaller than the original. Use Example 6 as the reference.

Step 4: test with a multi-turn conversation where summarisation occurs and verify that transactional facts survive intact across all turns. Why: this validates that the pattern actually works. You should see: a 6 to 8 turn conversation where summarisation occurs after turn 4. After summarisation, the agent should still reference the exact refund amount ($247.83), order number (#8891), and date (March 3rd) from the facts block. Without the block, these values would be lost.

Step 5: add key findings placement logic that positions summaries at the beginning of aggregated inputs to mitigate lost-in-the-middle. Why: models process the beginning and end of long inputs reliably, but findings buried in the middle may be missed. You should see: an aggregation function that places a Key Findings Summary section at the top of combined inputs, followed by detailed results with explicit section headers.

Observable success criteria for the whole exercise: every assembled prompt begins with the delimited facts block, every tool result in history is trimmed, a summarised run still reproduces $247.83, #8891, and 2024-03-03, and aggregated multi-source input leads with a Key Findings Summary. Each criterion is checkable without a model call, by inspecting the rendered prompt strings.

Expanded mechanism depth: Window allocation in practice

The lesson library gives concrete allocation numbers that make the abstract budget tangible. For a 200K customer-support agent with five tools, the system prompt is about 2,600 tokens, the five tool definitions about 17,600 tokens, MCP context about 10,000 tokens, conversation history about 97,000 tokens, current input buffer about 25,000 tokens, and a safety buffer of about 47,800 tokens. The cached components (system, tools, MCP) total about 30K. With prompt caching, that 30K is charged at full price only once per cache period, not on every request. The safety buffer is non-negotiable: without it, a single long user document can push a session past the quality ceiling mid-turn.

For a document-analysis application on the same 200K window, the split inverts: system plus tools stay about 20,600 tokens, but the document content itself takes about 140,000 tokens (70 percent) and is cached if reused, while history shrinks to a minimal 4,400 tokens. The lesson library's point is that conversational agents budget for history, while document tools budget for document content; these are opposite priorities and the same budget template does not serve both. A 1M-window model changes the absolute numbers but not the discipline; the 128K output ceiling and the quality ceiling both still apply.

Expanded mechanism depth: Image and document limits

A request to a 1M-window model can include up to 600 images or PDF pages; the limit drops to 100 on a 200K-window model. Images and PDF pages are not free of the window budget; each consumed page or image is tokenised and counts toward the same 1M or 200K total. The lesson library notes that image tokens scale with resolution, a high-resolution image costs roughly 800 tokens while a low-resolution image costs roughly 85 tokens, and that multi-modal inputs must be accounted for in any budget plan. This matters directly for the exam scenario where a document-analysis agent loads a 500-page contract: even at 100 pages per request on a 200K model, the page budget is the binding constraint, not just the token budget.

Expanded mechanism depth: Context rot in production

Context rot is the documented name for the monotonic degradation of accuracy and recall as token count grows. The lesson library expresses the same idea as an effective quality ceiling near 147,000 to 152,000 tokens, but that number is a planning heuristic, not an Anthropic-measured boundary. The two statements are compatible if you treat the figure as a community estimate and context rot as the documented phenomenon. In production, the practical implication is that past the high-140000s region, fine-grained attention to middle content degrades even though the model still processes the full window. The fix is never to extend the window further but to restructure: pull critical content to the ends, externalise low-value bulk to tools or retrieval, and protect exact facts in the facts block.

Expanded mechanism depth: Compaction summary structure detail

The default compaction summary prompt is documented and structured as five labelled parts wrapped in <summary></summary> tags: Task Overview, Current State, Important Discoveries, Next Steps, and Context to Preserve. This is not an arbitrary format; it is the documented shape of a handoff summary and is directly reusable as a scratchpad or phase-summary schema. When you supply your own instructions to the compact_20260112 strategy, you replace the entire default prompt, so your replacement should preserve these five concerns or you will lose one of them. In particular, "Context to Preserve" is the field that should carry the verbatim facts block content, because anything not named there is subject to the summarisation that compaction performs.

Expanded mechanism depth: Context editing placeholder replacement and server-side clearing

Under clear_tool_uses_20250919, cleared results are replaced with placeholder text, not deleted. The placeholder is what the model sees; your client history keeps the original result untouched. This is a deliberate design: clearing happens server-side, before the prompt reaches the model, so the application can later replay or audit the complete exchange. The clear_tool_inputs flag controls whether the tool inputs are cleared alongside the results; setting it false keeps the inputs visible while only the bulky outputs are replaced. exclude_tools names tools whose results must never be cleared, which is how you keep an authoritative record such as a refund confirmation while still trimming verbose lookups.

Expanded mechanism depth: Cache minimums by tier and the silent bypass

The minimum cacheable length is model-specific: 1,024 tokens for Sonnet 4.6 and Opus 4.8 (and by extension the newer Sonnet 5 and Opus 5 tiers), and 4,096 tokens for Haiku 4.5. Below that minimum, the API silently bypasses caching regardless of any cache_control you set. This is a common source of "why am I not seeing cache reads" confusion: a 900-token static system prompt on Haiku 4.5 will never cache. The fix is to either pad the cached prefix past the minimum or accept that very small static content is not worth caching. The lesson library calls this out explicitly and warns that the minimum is the reason some otherwise-correct cache setups show zero cache_read_input_tokens.

Expanded mechanism depth: Token counting edge cases

The token counting endpoint returns exact input tokens but knows nothing about your output; you must supply your own output estimate and add headroom. It also does not account for the cache_creation_input_tokens versus cache_read_input_tokens split, it returns the raw input count as if uncached. Therefore use it to decide fit, not to decide cost. A second edge case is the web-search inflation noted earlier: after a server-side web search, the SDK may accumulate cache_read_input_tokens from the tool's internal calls into the apparent total, so a naive size check may report a huge input count and trigger premature compaction even though your own context is small. The token counting endpoint, run on your own assembled messages, gives the true size of what you control.

Common production failure modes

The following failure modes recur in real systems and map directly to exam traps.

First, the silent summarisation loss. A support agent summarises history every few turns, and a customer-stated refund amount of $247.83 becomes "a refund was discussed". The agent later refers to "your recent refund request" with no amount or order id. The fix is the prepended facts block; the amount, order id, and date are never in the summarised region.

Second, the middle-drop. A 180K context places a hard security constraint at the 90K mark. The model ignores it because middle content is underweighted. The fix is layout: move the constraint to the system prompt (start) or the current user turn (end).

Third, the untrimmed tool result. An order lookup returns 40 fields and the raw object is stored in history. Over 30 turns the stale fields cost thousands of tokens and crowd out the safety buffer. The fix is trimming at the tool boundary.

Fourth, the broken cache prefix. A timestamp or request id is embedded in the "static" system block, so the prefix changes every call and caching never engages. The fix is to keep cached content truly stable: no dynamic values, no per-request personalisation in cached sections.

Fifth, FIFO truncation of the original goal. Pure first-in-first-out truncation drops the user's opening instruction (the oldest message) exactly when the window fills, causing silent goal drift. The fix is priority-based truncation that protects P0 and P1 content.

Sixth, treating the 1M window as a licence to skip curation. A team puts 800K of loosely relevant material into a 1M window and gets worse results than a curated 150K prompt. Context rot still applies. The fix is curation, not size.

Exam trap deep dives

The reference page lists four exam traps. Each is expanded here with the reasoning chain the exam expects.

Trap one: thinking progressive summarisation is safe for transactional data. The exam expects you to recognise that summarisation systematically destroys numerical values, dates, and specific identifiers. A persistent facts block must hold these outside summarised history. The distractor answers are "instruct the model to preserve values when summarising" (unreliable, because abstractive summarisation paraphrases) and "store full history in a database and retrieve on demand" (works, but the facts block is the lighter-weight, always-in-context fix the question is steering toward).

Trap two: assuming lost-in-the-middle is solved by telling the model to pay attention. The exam expects the structural fix: place key findings at the beginning of aggregated inputs and use explicit section headers. Prompt-based reminders are unreliable for position effects. The worked layout is a "Key Findings Summary" section at the top, then detailed results with clear section boundaries.

Trap three: keeping full tool results because the model might need them later. The exam expects trimming to relevant fields before results enter history. Untrimmed results from 40-field lookups exhaust the budget across turns. The fix is a trimmer at the tool boundary.

Trap four: believing conversation history can be selectively truncated without consequences. The exam expects you to know the API is stateless, so each request needs complete history, and selective truncation breaks coherence. Use facts blocks and summarisation instead of truncation.

Token budgeting worked arithmetic

The lesson library presents priority-based truncation with five tiers. P0 (system prompt, tool definitions, current user input) is never removed. P1 (original task instructions, immutable facts block, key preferences) is last to be removed. P2 (recent 5 to 10 turns) is preserved until critically full. P3 (mid-conversation exchanges, older tool results) is FIFO-truncated as needed. P4 (acknowledgements, chit-chat) is removed first. The implementation below tags each message with a priority and removes from the bottom of the priority stack when the budget is exceeded; P0 messages are filtered out of the removal set so they are never touched.

example.ts
typescript
interface PrioritizedMessage {
  role: "user" | "assistant" | "tool";
  content: string;
  priority: 0 | 1 | 2 | 3 | 4;
  tokenCount: number;
}

function truncateByPriority(
  messages: PrioritizedMessage[],
  budget: number
): PrioritizedMessage[] {
  let total = messages.reduce((sum, m) => sum + m.tokenCount, 0);
  const removable = [...messages]
    .filter((m) => m.priority > 0)
    .sort((a, b) => b.priority - a.priority);
  for (const msg of removable) {
    if (total <= budget) break;
    const idx = messages.indexOf(msg);
    if (idx !== -1) messages.splice(idx, 1);
    total -= msg.tokenCount;
  }
  return messages;
}

What this proves: priority truncation is deterministic and auditable, unlike FIFO. P0 content (the system prompt and tool definitions) is structurally protected because the removal set excludes priority 0. The facts block, as a P1 item, survives all but the most extreme truncation. Failure boundary: if priorities are not tagged at creation time, retroactive classification is error-prone and may mislabel the facts block as removable. Observable output: after truncation, messages still contains every P0 and P1 entry and the oldest P4 chit-chat is gone.

Lost-in-the-middle worked layouts

For aggregated multi-source input, the recommended structure leads with a Key Findings Summary. The lesson library gives the canonical shape: a "Key Findings Summary" section with one bullet per source (for example "Source A: 12 percent market growth in renewable sector (2023)"), followed by "Detailed Findings" with a named subsection per source. The model attends to the summary at the top reliably; the detailed sections below carry the supporting evidence. This is the structural antidote to the middle-drop, and it is the layout the exam rewards over any prompt instruction to "pay attention to everything".

A second layout rule applies within a single long document: put the specific question at the very end, after the document, so it sits at the high-attention end of the context. The lesson library makes this explicit: for a long document for analysis, the question belongs after the document, not before it.

Multi-agent and memory tool tie-ins

In multi-agent systems, context window management becomes a per-agent and per-handoff concern. Upstream agents should return structured findings (claim, source, relevance, date) rather than verbose reasoning, so downstream agents do not waste window on reasoning they cannot use. This is a source-tier decision: the upstream agent's output is a context source, and trimming it at the source is cheaper than trimming it after it has already been paid for in the upstream call.

The memory tool ({"type": "memory_20250818", "name": "memory"}) is the documented, model-driven alternative to a hand-maintained facts block. Where the immutable facts block is populated by your application code, the memory tool lets the model decide what is worth persisting and retrieve it on demand via /memories. The handler executes view, create, update, and delete against storage the application controls, and must reject paths outside /memories to prevent path traversal. The lesson library positions the memory tool as one of three built-in primitives (with context editing and compaction) that implement the same ideas as the hand-built patterns. For the exam, the distinction to hold is: the facts block is application-owned and always in context; the memory tool is model-driven and retrieved just in time, which keeps the active window focused.

Provenance tie-in: where content arrives as a document block or a search_result block, the documented enforcement mechanism for attribution is the citations feature or the required source and title fields, not a prompt instruction. The exam-relevant judgement is unchanged, because attribution still has to survive multi-step synthesis in application code, but the documented mechanisms are the preferred enforcement point and prompt-preserved mappings are what you use for content that does not arrive as documents or search results.

Summary of the documented primary path

For a long-running conversation on a supported model, the documented primary path is: enable compact_20260112 with an explicit input_tokens trigger at or above 50,000 (default 150,000), optionally supply instructions that preserve the five-part summary shape and name the facts block in "Context to Preserve", let the API emit the compaction block and drop everything before it on later requests, and keep your own application-side facts block prepended outside the summarised region so verbatim values survive. Use context editing (clear_tool_uses_20250919) for fine-grained tool-result trimming with keep, clear_at_least, exclude_tools, and clear_tool_inputs. Use prompt caching with correctly placed breakpoints and awareness of the per-tier minimum. Use the token counting endpoint to size requests before sending. Trim tool results at the boundary. Place critical content at the ends to defeat lost-in-the-middle. This is the complete, documentation-first picture that resolves the reference material's omissions.

Worked end-to-end scenario

Consider a customer-support agent that runs for 60 turns over two hours, investigates a billing issue across multiple systems, escalates to a supervisor, and resolves the refund. The documented primary path combines compaction, a prepended facts block, tool-result trimming, and caching.

At session start, the application extracts the customer id, the issue type, the exact amount, the order id, the date, and the escalation level into the facts block and prepends it via the buildPrompt function from Example 1. The system prompt and tool definitions carry cache_control breakpoints so they are written once and read at 0.1x thereafter. A clear_tool_uses_20250919 edit with exclude_tools: ["refund_processor"] keeps the authoritative refund confirmation out of trimming while verbose lookups are cleared once input crosses 50,000 tokens.

As the conversation grows past 150,000 input tokens, the compact_20260112 edit fires. The API generates a summary in the five-part shape, emits a compaction block, and on the next request drops everything before it. Because the facts block is assembled outside the summarised region by buildPrompt, it survives the compaction: the agent still references $247.83, order #8891, and the March 3 date verbatim, while the narrative of the investigation is compressed. This is exactly the failure the reference page warns about, resolved by documentation-first means rather than client-side summarisation alone.

The observable outcomes you can assert without a model call are: the rendered prompt always begins with the delimited facts block; every tool_result in history is trimmed to five fields; after a compaction the narrative is shorter but the facts block is byte-identical; the usage field shows cache_read_input_tokens climbing on repeated prefixes; and a token-count check before each send confirms the request fits the limit. Each is a concrete, testable property of the design.

Comparison: client-side versus server-side compaction

The reference material presents client-side progressive summarisation as the default and the facts block as the only protection. Documentation reframes this: server-side compaction is the recommended strategy, and client-side summarisation (the hand-built Haiku-call pipeline the lesson library describes) is now one option among several, including the built-in compact_20260112 strategy and the SDK tool_runner compaction in TypeScript and Ruby. The table below contrasts them.

Server-side compaction runs in the API, requires no orchestration code, emits a compaction block the API itself uses as the truncation boundary, and is supported on the listed 1M and recent models. Its limitation is the web-search inflated-count issue and the fact that it still summarises, so verbatim facts must live outside the summarised region. Client-side compaction gives you full control over the summary prompt and works on any model, but you must build the trigger, the summarisation call, the history replacement, and the audit trail yourself, and you pay for the extra model call. The facts block is required under both, because both approaches summarise narrative content.

The practical recommendation for the exam is: when asked for the default documented strategy for a long conversation on a supported model, answer server-side compaction with the compact_20260112 strategy and the compaction block boundary; when asked how to protect exact transactional values, answer the application-owned facts block prepended outside the summarised region. The reference material's facts-block answer remains correct; it is simply no longer the whole story.

Prompt caching layout decision tree

The reference page's cache example is correct but under-specified. The full decision is:

First, identify every block that is stable across requests: tool definitions, system instructions, long reference documents, stable few-shot examples. Place these first, in the tools array, then the system array, then the start of messages.

Second, place a cache_control: {"type": "ephemeral"} breakpoint at the end of each stable layer. Up to four breakpoints are allowed; most applications need two or three. Never place a breakpoint on volatile content such as the current user message, because that breaks the stable prefix and every request pays full price.

Third, check the per-tier minimum. On Haiku 4.5 the cached prefix must exceed 4,096 tokens or caching silently bypasses; on Sonnet 4.6, Opus 4.8, Sonnet 5, and Opus 5 the floor is 1,024 tokens. If your static content is below the minimum, either combine it with other stable content to cross the threshold or accept no caching.

Fourth, choose the TTL. The default 5-minute TTL is right for interactive traffic that hits the prefix frequently; the 1-hour TTL costs roughly 2x to write but survives longer idle gaps and is right for long-lived sessions with stable reference documents. The read cost is roughly 0.1x regardless of which TTL wrote the entry.

Fifth, monitor cache_read_input_tokens in the usage field. A climbing read count confirms the breakpoint is correctly placed; a persistent zero means the prefix is changing per request or the minimum is not met. Remember that cached tokens still occupy the window, so caching is a cost lever, not a space lever.

Token counting and budget dashboard pattern

The token counting endpoint is the documented way to size a request before sending it, and it underpins a budget dashboard. A minimal pattern: before each send, call countTokens with the assembled system, messages, and tools; add your output estimate and a 15 to 20 percent safety overhead; compare against the model limit; and refuse to send if it does not fit, triggering compaction or trimming instead. Because the count call knows nothing about caching, use it only for fit, and read the real usage after the call for cost. For high-volume paths, cache or batch the count call to avoid paying a round trip per request. The web-search inflation caveat means you should count your own assembled messages, not trust an accumulated cache_read_input_tokens total that includes the tool's internal calls.

This closes the loop with the ownership map: the model owns attention, the API owns compaction and caching and the usage split, and application code owns the facts block, trimming, priority truncation, and the decision to consult the token counting endpoint before each send. A reliable system assigns each guarantee to its correct layer rather than expecting one layer to cover another.

Context editing strategy detail: clear_thinking and cache interaction

The clear_thinking_20251015 strategy clears extended thinking blocks rather than tool results. Its cache interaction differs from tool-result clearing: when thinking blocks are kept, the cache is preserved; when they are not kept, the cache is invalidated at the clearing point. This matters because thinking blocks count as input tokens on the model families that retain them by default (Opus 4.5 and later, Sonnet 4.6 and later, Fable 5, Mythos 5, Mythos Preview), while earlier Opus and Sonnet models and all Haiku models strip them automatically when passed back. The lesson for the exam is that retention is model-dependent, and you should set keep explicitly whenever your code spans model tiers, because the default differs across families.

The reason clear_at_least exists for tool-result clearing is specific to cache economics: clearing invalidates the cached prefix at the clearing point, so if you clear too little, you pay the cache-write cost without freeing enough space to matter. clear_at_least forces a minimum clear volume so the cache write is justified. This is a nuance the reference page does not cover, because the reference predates the cache-aware clearing semantics.

Memory tool handler security and path containment

The memory tool is {"type": "memory_20250818", "name": "memory"}, available on Claude 4 and later. The model requests operations (view, create, update, delete) under /memories, and the application executes them against storage it controls, returning a tool_result. The critical security property is path containment: the handler must reject any path outside /memories, because a path traversal attempt could otherwise reach arbitrary files. The /memories prefix is mapped by the handler onto the real storage root. This is the application's responsibility, not the model's, and it is the same containment discipline that governs any tool that touches a filesystem. The documented purpose of the memory tool is just-in-time context retrieval, so the active window stays focused on what the current step needs rather than carrying everything.

For the exam, the distinction to hold is between the three related but separate ideas: the application-owned facts block (always in context, populated by your code), the memory tool (model-driven, retrieved on demand, with handler-enforced path containment), and server-side compaction (API-driven, summarises the narrative and emits a compaction block). They compose: the facts block and memory tool protect exact values, while compaction trims the narrative; context editing clears bulky tool results and thinking blocks in between.

Safety buffer and priority truncation arithmetic recap

The lesson library is explicit that the safety buffer is not optional. A buffer of 10 to 25 percent of the window absorbs unexpectedly large turns; without it, a single long user document pushes a session past the quality ceiling with no recovery path. In the 200K support-agent allocation, the buffer is about 47,800 tokens (roughly 24 percent), which is what allows the system to keep active context near 130K while leaving room for spikes. In the document-analysis allocation on the same window, the buffer shrinks to about 10,000 tokens (5 percent) because the document dominates and the task is single-shot rather than turn-heavy.

Priority truncation arithmetic follows from the tiers: protect P0 and P1 unconditionally, preserve P2 until the window is critically full, FIFO-truncate P3 as needed, and remove P4 first. The failure mode to avoid is tagging the facts block as anything lower than P1, because then an aggressive truncation pass could drop it. The facts block is, by definition, the content that would cause a silent hard-to-detect failure if lost, which is exactly the P1 criterion.

Exam framing summary

The exam frames Task 5.1 around a small set of recurrent scenarios, each mapping to a documented mechanism. A long conversation that loses exact values points to the prepended facts block outside the summarised region, now reinforced by server-side compaction as the documented primary strategy. A critical instruction ignored mid-context points to lost-in-the-middle and the structural fix of end-placement. An overflowing window from verbose tool output points to trimming at the tool boundary and to priority truncation that protects P0 and P1. A request that pays full price every turn points to a misplaced cache breakpoint or a below-minimum prefix. A statelessness question points to complete-history-per-request plus the facts block. A long conversation on a supported model points to compact_20260112 with the compaction block boundary. In every case, the documented mechanism is the first answer, and the application-side protection is the necessary complement, because compaction still summarises and anything that must survive verbatim has to live outside the summarised region.

The six required mechanisms at a glance

This section restates the six mechanisms the task requires, each tied to its documented behaviour and its exam relevance, so the writer can confirm complete coverage.

First, the persistent facts block prepended outside the summarised region, guaranteed by a single prompt-construction function that always places it between the system prompt and the summarised history. It survives both client-side summarisation and server-side compaction because it is never in the region those operations act on.

Second, the array-form system field with a correctly placed cache breakpoint at the end of the static block, shown next to the wrong placement that interleaves volatile content and breaks the prefix. The usage fields input_tokens, cache_creation_input_tokens, and cache_read_input_tokens distinguish a cache miss from a hit, and all three count toward the window.

Third, the server-side compaction request with an explicit input_tokens trigger at or above the 50,000 floor (default 150,000), emitting a compaction block that later requests use as the truncation boundary.

Fourth, the tool-result clearing configuration with trigger, keep, clear_at_least, exclude_tools, and clear_tool_inputs, clearing server-side and replacing results with placeholder text while the client history stays intact.

Fifth, the token-counting call used to size a request before sending it, returning exact input tokens so an oversized request is refused rather than failed mid-flight.

Sixth, the tool-result trimmer at the tool boundary, returning only the fields the agent acts on so verbose payloads never enter history.

Together these six form the complete, documentation-first picture of context window management for Task 5.1, with the facts block as the constant that protects verbatim truth across every other mechanism's compression or clearing.

Mechanism and API surface

Persistent case facts block after system prompt
Structured JSON with customerId, orderId, refundAmount, orderDate, and status, prepended to every prompt, never summarised or FIFO truncated, updated in place as new facts emerge, with per-issue entries for multi-issue sessions.
Lost-in-the-middle positional fix
Key findings summary at the very top, detailed findings under explicit section headers throughout, and current user message at the end for recency. Positional effect is not fixable by instruction.
Context rot as the documented degradation
Accuracy and recall fall as token count grows, with no published cliff. Community material quotes an effective ceiling in the high 140,000s; treat that as an estimate. The hard window limit is a separate hard stop where the request errors.
Server-side compaction as the default lever
Strategy compact_20260112 in context_management.edits with the compact-2026-01-12 beta header, trigger defaulting to 150,000 input tokens and floored at 50,000, optional pause_after_compaction and instructions, emitting a compaction block that later requests treat as the truncation boundary.
Context editing for tool results and thinking
clear_tool_uses_20250919 with trigger, keep, clear_at_least, exclude_tools, and clear_tool_inputs, plus clear_thinking_20251015, under the context-management-2025-06-27 header. Applied server-side so the client keeps its own full history; tool-result clearing invalidates the cached prefix, which is why clear_at_least exists.
Prompt caching with cache_control ephemeral
Array-form system field with cache_control type ephemeral on the static prefix, positional matching from index 0, up to four breakpoints per request, default five minute TTL since last use and extended-cache one hour at higher read rate, cached reads at about 10 percent of standard input cost.
Practical token allocation with safety buffer
System prompt 10 to 15 percent, tool definitions 10 to 15 percent at about 3,500 tokens each, conversation history 30 to 40 percent, retrieved documents 10 to 20 percent, plus a 5 to 10 percent safety buffer that is non-negotiable.
Tool result trimming at the boundary
Strip verbose fields before the result enters messages via a hook or wrapper. Once in history verbose data persists every turn, so trimming after the fact does not recover the cost.
Measure the window, do not estimate it
The usage field on every response reports input_tokens, cache_read_input_tokens, cache_creation_input_tokens, and output_tokens, all counting toward the window. The token counting endpoint sizes a request before you send it.

The decision rules in play

Each rule below is a distinct decision the exam tests, stated with its mechanism, the boundary where the answer flips, and the wrong answers written against it.

R1

Progressive summarisation destroys transactional numbers and customer-stated expectations

When conversation history grows, teams compress earlier turns into a running summary so the active prompt still fits. Each compression asks the model to restate prior content in fewer tokens. Numerical values, dates, order identifiers, discount percentages, refund amounts, rate ceilings, and customer-stated expectations are the first content to be paraphrased away. A summary rewrites a precise figure such as $847.50 or 15% into a category label like the overcharge from last month. The next turn then summarises that summary, so vagueness compounds. After several cycles the original figure is gone. The mechanism is lossy abstraction under length pressure.

Summarisation is trained to be concise. Verbatim numbers carry high density per token but low narrative weight, so a summariser under token pressure treats them as compressible. Once a precise value is replaced by a qualitative phrase, no later turn can reconstruct it because the source is gone. In transactional workflows the precise value is the task-critical payload, so the summariser optimizes for the wrong signal. Adding preserve numbers verbatim to the prompt helps for a turn or two but does not hold after the context has already been condensed, because the instruction itself is subject to the same dynamics.

Boundary. The rule flips when the content is narrative procedure rather than transactional fact. Summaries of diagnostic steps, policy explanations, and resolved-issue outcomes compress safely because exact wording is not load-bearing. Long troubleshooting sessions where the task is continue diagnosing tolerate prose summaries of earlier clarifications. A deliberate projection such as replacing 500 rows with count, min, max, avg, distribution is not progressive loss but a typed summary that retains the needed numeric. The test is whether a future turn must quote the exact figure to act correctly.

Recurring specifics. Amounts include $247.83, $847.50, $184,250.00 at 6.875% later rounded to about $180,000 at 7%, and 15% reduced to promotional pricing. Identifiers include order numbers like #8891 and ORD-7821, claim numbers, host lists, IP maps, and step checkpoints. Dates include March 3rd, February 28th, and July 15. Markers of loss include your recent refund request, the overcharge from last month, standard security issues, and typical patterns.

Wrong answers written against this rule

Proposal. instruct the summariser to preserve all numbers verbatim.

Why it attracts. directly names the symptom.

Why it fails. the instruction is probabilistic and does not survive more than one or two compressions.

When it would be right. when only one summarisation will ever occur and the summary can be reviewed for fidelity.

Proposal. increase the window so summarisation never triggers.

Why it attracts. removes the trigger.

Why it fails. growth is unbounded and larger windows still degrade.

When it would be right. when the workload is bounded.

Proposal. re-query the source to recover the lost figure.

Why it attracts. restores the correct value.

Why it fails. does not prevent recurrence and some data is conversational commitment not re-fetchable.

When it would be right. when the figure is system-owned and re-fetch is cheap.

How the same rule gets re-asked
  • Mutation that swaps the figure type from currency to dates or percentages keeps the fix. Mutation that adds a prior fix attempt like retain all exact figures that helped early but not late isolates where instruction fixes stop working.
R2

Persistent case facts block must live outside summarised history and be included verbatim every turn

The durable pattern separates critical facts from compressible narrative. At the moment a fact is established, code extracts it into a structured block such as caseFactsBlock or session_issues with discrete fields for customer identifier, order identifier, amount, dates, and status. That block is prepended to every subsequent prompt. History may be summarised or compacted, but the facts block is explicitly excluded from summarisation. Placement at the very beginning is rewarded, so it occupies a high-attention position and never enters the summarisation channel.

result.json
json
{
  "caseFactsBlock": {
    "customerId": "C-4421",
    "issues": [
      {
        "orderId": "#8891",
        "orderDate": "2024-03-03",
        "refundAmount": "$247.83",
        "status": "pending_refund",
        "itemDescription": "Wireless headphones - defective"
      }
    ]
  }
}
result.json
json
{
  "session_issues": {
    "refund_inquiry": {
      "turns": "1-15",
      "order_id": "ORD-8821",
      "amount": "$247.50",
      "status": "PENDING",
      "resolution": "escalated to billing team",
      "eta": "24-48 hours"
    },
    "payment_method_update": {
      "turns": "31-45",
      "action": "card ending 4821 -> 9034",
      "status": "completed"
    }
  }
}

The block decouples precision from compression. Summarisation can be aggressive on dialogue flow without risking figures because the figures are not inside the material being summarised. Verbosity is low, typically a few hundred tokens of JSON or markdown, versus tens of thousands of raw history. At the start it benefits from positional reliability; at the end it can still be vulnerable to middle neglect when verbose dumps precede it. The block works because it is reproduced verbatim rather than re-summarised.

Boundary. The rule is unnecessary when no future turn will quote the exact figure, such as single-issue short sessions where full history still fits. It also weakens when the facts block itself is summarised, for example condensing all periods into cumulative totals or re-summarising the block into a compact synopsis, which fails the same way as conversation summarisation. Multi-issue sessions need per-issue entries so the wrong amount is not applied to the wrong order.

Recurring specifics. Block shapes that recur include: and multi-issue forms: Other shapes include service facts and study facts blocks that hold latency budgets, rate ceilings, and effect sizes verbatim.

Wrong answers written against this rule

Proposal. store facts in an external database and fetch on demand.

Why it attracts. feels like proper persistence.

Why it fails. adds latency and some facts are conversational commitments not in any system of record, like promised response within two business days.

When it would be right. when facts are purely system-owned and fetch is on the critical path before acting.

Proposal. place the block at the end closest to the next instruction.

Why it attracts. recency intuition.

Why it fails. when verbose dumps sit before the trailing block it is no longer at a reliably attended edge. Evidence rewards fixed-start placement.

Proposal. progressively summarize the facts block itself so it stays compact.

Why it attracts. fear of block growth with dozens of periods.

Why it fails. reintroduces numeric distortion, such as collapsing per-period figures into a cumulative total that cannot be reconciled.

When it would be right. never for verbatim figures; only for narrative where exact values are not load-bearing.

How the same rule gets re-asked
  • Mutation that moves the block from start to end flips correctness. Mutation that changes the block from outside summarised history to inside the summary that gets re-summarised flips correctness. Mutation that swaps preserved fields from currency to latency budgets or authorization files keeps the rule.
R3

Multi-issue structured issue layer versus single running prose summary

A session covering three or four distinct issues produces interleaved facts across dozens of turns. A single prose summary that says customer discussed several issues compresses each issue into vague sentences and mixes amounts and statuses across issues. The structured alternative extracts per-issue records with order_id, amount, status, eta, and action and keeps them in a separate layer injected alongside the conversation. The agent answers what happened with my refund by reading the refund entry directly.

Per-issue structure preserves the binding between an identifier and its attributes. Prose loses that binding because it linearizes multiple issues into one paragraph. Evidence shows agents applying the wrong amount to the wrong order after prose summarisation. Three compact JSON entries are far smaller than three narratives, so the layer stays affordable even at 40 or more turns.

Boundary. A single-issue session needs only a flat block. When issues are truly independent and never cross-referenced, a simple sliding window may suffice, but for typical support where shared customer context matters, separation into different conversations is wrong. Opening separate conversations becomes correct only when issues belong to different customers or require different permissions.

Recurring specifics. Session shapes include turns 1-15 refund inquiry, turns 16-30 subscription question, turns 31-45 payment method update, follow-up at turn 48 referencing the earliest issue. Status values include PENDING, completed, escalated to billing team. Failure phrase is your recent refund request without amount or applying amount from two issues ago that was later corrected after condensation.

Wrong answers written against this rule

Proposal. re-fetch via tools on demand.

Why it attracts. fresh authoritative data.

Why it fails. tools cannot recover conversational commitments and add latency at follow-up.

When it would be right. when the fact is purely system-owned.

Proposal. single prose summary plus full history for active issue only.

Why it attracts. balances brevity.

Why it fails. prose still loses values and active issue is ambiguous when the customer jumps threads, and the earliest issue is already outside the window.

When it would be right. never for transactional values; only as navigational index with structured records still available.

Proposal. sliding window of most recent 30 turns.

Why it attracts. simple.

Why it fails. at turn 48 the window holds 18 to 48, so turns 1 to 15 are already outside.

When it would be right. when task is strictly recency-dependent, such as continuing the open troubleshooting thread.

How the same rule gets re-asked
  • Mutation that adds a later correction tests whether the corrected value is preserved. Mutation that increases issues from two to four tests scaling of the structured layer.
R4

Lost-in-the-middle positional attention bias

Models attend most reliably to content at the very beginning and very end of a long input. Middle content receives systematically weaker attention. Evidence describes 90K inputs as 12K brief at start, 68K sources in middle, 10K appendix at end, with the model reflecting the brief and appendix while omitting decisive middle evidence. The same appears when a 60-page contract places indemnification on pages 28 to 30, or when 20 retrieved documents ranked 10 to 20 are ignored. I infer the bias is architectural rather than hard truncation.

Self-attention distributes mass across all positions but edge positions are more salient for recall. When dozens of sections compete, the middle shares limited attention and is diluted.

Boundary. Negligible for short prompts where critical content naturally sits at the start or end. Not applicable when the workload is split so every piece is processed as primary context in its own pass, such as parallel subagents each handling one chunk, where no content is definitionally in the middle. Headers and a front-loaded summary reduce but do not eliminate the bias.

Recurring specifics. Markers include page 24 of 50, pages 28 to 30, positions 12 to 30 in a sequence of 40, documents ranked 10 to 20. Splits like first 15K and last 10K reliably cited, middle 50K dropped and 12K brief, 68K middle, 10K appendix. Sizes that trigger it include 120-page merger agreements, 150-page contracts at 80K, and 900K contracts.

Wrong answers written against this rule

Proposal. increase the window so middle proportion shrinks.

Why it attracts. more room seems to reduce middle.

Why it fails. larger windows move the dead zone outward but do not flatten the distribution.

When it would be right. when the document exceeds the window and must be split.

Proposal. randomize which source appears in the middle across runs.

Why it attracts. feels fair.

Why it fails. randomization spreads the miss rate but does not reduce it for any given run.

When it would be right. when measuring the effect experimentally, not fixing a report.

Proposal. increase max_tokens for more output budget.

Why it attracts. confuses output capacity with input attention.

Why it fails. omission is in reading, not writing.

When it would be right. when response is actually truncated mid-summary.

How the same rule gets re-asked
  • Mutation that swaps middle content from research findings to tool schemas pushed out by a large tool result tests the same bias in a different guise. Mutation that swaps which fact is middle, like indemnification versus purchase price, flips which fact is missed.
R5

Structural mitigation for lost-in-the-middle: key-findings summary at start plus explicit section headers

The effective fix is structural reordering, not prompting. Teams lift decisive evidence into a compact key-findings or executive summary at the very beginning, then present full details underneath with explicit labeled headers. For subagent aggregation this yields:

Headers act as navigational anchors so the model can locate specific content without relying solely on positional memory across 90K tokens.

instructions.md
markdown
## Key Findings Summary
- Source A: 12% market growth in renewable sector (2023)
- Source B: Patent filings increased 34% year-on-year
- Source C: Regulatory framework delayed until Q3 2025

## Detailed Findings

### Source A: Market Analysis Report
[Full details here...]

### Source B: Patent Database Analysis
[Full details here...]

Moving content to the start places it where attention is strongest. Headers break a flat stream into labeled blocks, improving retrieval for mid-context questions. The summary ensures the synthesis decision can be made from the summary alone, while headers let verification find supporting evidence in the middle. Evidence shows that larger windows or randomization do not address the distribution, while summarizing everything to 20K risks omitting the same decisive evidence.

Boundary. Unnecessary overhead when the aggregated input is short and all content sits in a high-attention zone. Also costly when the front summary is itself lossy by condensing required precise clauses rather than copying them, reintroducing numeric distortion. The opposite where front-loading is not chosen is chunked independent processing with overlap, where the synthesis agent never sees a long flat aggregation and headings are per-chunk rather than per-aggregation.

Recurring specifics. Header styles include ## Key Findings Summary, ## Detailed Findings, ### Source A: Market Analysis Report, ## EXECUTIVE SUMMARY OF ALL FINDINGS. Front summaries run a few hundred to a few thousand tokens ahead of tens of thousands of details. Other placements include relocating five critical requirements to beginning or end, or placing a historical summary at the beginning for medical records.

Wrong answers written against this rule

Proposal. ask the model to be careful with the middle pages.

Why it attracts. lowest effort.

Why it fails. instruction does not change attention distribution.

When it would be right. never as sole fix; at most as supplement to reordering.

Proposal. increase the synthesis agent's budget so all content fits comfortably.

Why it attracts. assumes omission equals capacity.

Why it fails. the agent already fits edge content from the same input, so capacity is not the constraint.

When it would be right. when input actually exceeds the window and fails with a length error.

Proposal. alphabetize or randomize reports.

Why it attracts. neutral ordering.

Why it fails. ordering without front-loading does not move decisive evidence out of the middle.

When it would be right. when retrieval order carries no relevance and goal is fairness.

How the same rule gets re-asked
  • Mutation that keeps the front summary but removes headers tests whether headers add independent value. Mutation that adds repetition at the end as well as the start tests bookending versus front-loading alone.
R6

Instruction-only fixes do not cure positional or summarisation degradation

Teams try to fix attention or fidelity failures by adding instructions such as always preserve exact figures, retain all exact period figures and never round, use every record, or be very careful with the middle pages. Evidence shows these improve behavior for the most recent two or three periods or early studies but fidelity for earlier figures and for studies processed after the context was already condensed does not recover, and positional omissions remain. The instruction is part of the context subject to the same dynamics it tries to govern.

Instructions are probabilistic hints, not hard constraints on the summarisation channel or attention weights. When context has already been compacted, the verbatim figure is no longer present to be preserved, so the instruction has nothing to act on. For lost-in-the-middle, attention distribution is architectural and evidence calls it a well-documented pattern that reminders do not reliably override.

Boundary. Prompt instruction is sufficient when the task is within a short window and the instruction governs generation style, such as format the output as JSON, where the model correctly follows formatting while still missing mid-context diagnostic criteria. Instructions become effective again when paired with a structural change that makes compliance possible, like an instruction to read figures from the case facts block that actually contains them.

Recurring specifics. Phrasings include always preserve exact effect sizes and confidence intervals verbatim, always restate exact balances, rates, and dates from the source record, use every record, remember the classification scheme from earlier, and be very careful with the middle pages. The signal is improvement for early or recent content with no improvement for earlier condensed content or middle omissions.

Wrong answers written against this rule

Proposal. add a persistent instruction to remember prior context.

Why it attracts. seems to solve forgetting.

Why it fails. without passing history or preserving facts outside the summarised channel there is nothing to remember.

When it would be right. when history is actually being passed and instruction merely guides extraction of available facts.

Proposal. repeat constraints in every human turn.

Why it attracts. keeps constraints visible.

Why it fails. the repeated turn is itself subject to summarisation and recent turns crowd out older constraints.

When it would be right. when constraints are moved to the system prompt where they persist, which evidence marks as architecturally sound for instruction persistence.

How the same rule gets re-asked
  • Mutation that pairs the instruction with a structural change, such as a case facts block, tests whether the reader distinguishes instruction alone from instruction plus structure. Mutation that moves the instruction from user message to system prompt tests whether placement changes the outcome for style rules but not for lost-in-the-middle recall.
R7

Context window budget is shared across system, tools, history, retrieved content, and output headroom

The context window is the model's entire working memory for a single request. Everything counts: system prompt, every messages turn including tool_result blocks, images and documents, tools definitions, and the output being generated. Evidence describes a 200K budget with 8K system plus 120K history plus 65K tool result leaving only 7K for response, and a 50K system plus 30K history plus 20K setup plus 10K few-shot already consuming over half the window. Training data not in the prompt and HTTP headers do not count.

The model attends over all input at generation time, so every token competes for the same fixed window. Billing splits cached versus uncached for price, but the window sees the sum. Accuracy also degrades as the window fills even when the request still fits, described as context rot.

Boundary. Not a concern when the workload is short and the sum stays well under the window with headroom. The opposite where the rule seems relaxed is believing cached tokens do not count or max_tokens enlarges the window, which evidence consistently marks false. Per-model window size changes the total but not the budgeting discipline.

Recurring specifics. Allocations include 50K system, 30K history, 20K setup, 10K few-shot, 200K window, 180K history plus 5K system leaving 15K for user input, 30K system plus 20K runbooks plus 30K metrics plus 20K error log. Triggers include 70%, 80%, 90% of the window, and the budget split across system, history, examples, user input. Tool counts above about 30 to 50 degrade selection.

Wrong answers written against this rule

Proposal. raise max_tokens to enlarge the window.

Why it attracts. confuses output ceiling with input capacity.

Why it fails. max_tokens caps generation per turn, not input fit; if input alone exceeds the window the request is rejected.

When it would be right. when the response is truncated mid-generation.

Proposal. switch models or plans for headroom.

Why it attracts. promises room without curation.

Why it fails. plan tier does not change window size and larger windows still benefit from curation because over-stuffing dilutes attention.

When it would be right. when the task genuinely needs more room and larger window is paired with budgeting discipline.

Proposal. send all history without budgeting.

Why it attracts. avoids designing allocation.

Why it fails. cost and latency grow linearly and requests fail with 400 once the sum exceeds the window.

When it would be right. never for long sessions; only for single-shot tiny inputs.

How the same rule gets re-asked
  • Mutation that swaps which section is largest tests whether the reader targets the largest movable allocation. Mutation that adds images or PDF pages tests whether those count toward the window.
R8

Output headroom and token budget allocation with safety buffer

Because the response shares the window, a request must reserve space for output. Evidence shows 193K of a 200K window leaving only 7K for response, leading to thin or failing generation. Sound allocation prioritizes instructions, reserves a minimum budget for conversation, fills the remainder with retrieved documents dynamically, and keeps a buffer.

If input already occupies nearly the whole window, the model has no room to generate a thorough answer, so it truncates or produces shallow output. Earlier evidence notes that adding a safety buffer and sizing max_tokens after estimating input is the pre-flight check before large requests, because the API may accept input and then stop mid-summary when the combined total reaches the window. Capped dynamic allocation with iterative chunking is required when more evidence is needed.

Boundary. When expected output is tiny, such as a 2 to 3 sentence summary or a single boolean, required headroom is small and a tighter input budget is defensible. The opposite where headroom is traded is batch or long-context analysis where a larger output budget is explicitly reserved and retrieved context is aggressively filtered to keep that promise.

Recurring specifics. Numbers include max_tokens sizing, 150K trigger thresholds, 2K reserved for output in an 8K window with 800 system plus 1200 few-shot, and estimating with the token counting endpoint. Evidence also describes token budgets for loops with hard cutoffs that force synthesis after a threshold.

Wrong answers written against this rule

Proposal. increase max_tokens for longer answers when accuracy dropped after input grew to 600K on a 1M window.

Why it attracts. links longer output to thoroughness.

Why it fails. the drop came from input dilution, not output truncation.

When it would be right. when responses come back truncated mid-summary with a stop reason indicating the window was exceeded during generation.

Proposal. allocate equal tokens to all parts.

Why it attracts. feels fair.

Why it fails. instructions and recent conversation have higher marginal value than additional retrieved documents.

When it would be right. never as budgeting; at most as starting point before filtering.

Proposal. reduce system to under 2K when system is 8K of 193K.

Why it attracts. system seems reducible.

Why it fails. system is a small fraction versus 120K history and 65K tool results.

When it would be right. when the system contains rarely relevant background that can be moved to a retrievable store.

How the same rule gets re-asked
  • Mutation that changes which section is short versus huge tests capped dynamic allocation. Mutation that introduces variable output length tests per-request pre-flight estimation.
R9

Importance-based and task-aware truncation versus naive recency or oldest-first truncation

When history exceeds the window, teams must choose what to drop. Naive strategies truncate oldest first, most recent, or only user input, assuming age or speaker predicts relevance. Evidence shows this loses foundational context such as purchase terms or prior refund attempts from the oldest turns, or removes the current request itself. Importance-based truncation scores each message by relevance, keeping decisions, policy references, and order details, and removing pleasantries first. Task-aware sizing loads the full current report and extracts only a 2K metrics table from the prior quarter instead of both full reports.

Relevance is not monotonic with recency. Early turns often determine whether a current request is valid, while recent turns may be noise. Scoring by relevance aligns what remains with what the next decision needs. A purchase agreement from turn 1 can decide whether a refund from turn 48 is eligible. Naive truncation assumes age predicts value, but support threads reuse foundational facts irregularly. Importance scoring keeps decisions, policy references, and order details while removing pleasantries first. Task-aware sizing extends the same idea, loading the full current report while extracting only a compact metrics table from a prior quarter.

Boundary. Naive recency is correct when earlier context is truly superseded, such as an incident where turns 1 to 100 covered a resolved billing issue and turns 101 to 150 cover a distinct open shipping problem, where summarizing the resolved thread and preserving the open thread in full is rewarded. Truncation is not needed at all when the full history already fits.

Recurring specifics. High-scoring dimensions include decisions, policy references, order details and low-scoring include pleasantries, confirmations, tangents. Wrong truncations include removing oldest and losing purchase terms, removing most recent and losing the current refund, and removing only user input and losing the customer description. Task-aware sizing includes a 1M window with two 80K reports where the correct pattern is full current plus extracted metrics from prior.

Wrong answers written against this rule

Proposal. truncate oldest first because they are least relevant.

Why it attracts. simple and matches recency.

Why it fails. oldest often contain foundational purchase terms.

When it would be right. when resolved issues truly will not be referenced again and a summary has been preserved.

Proposal. truncate most recent to keep history stable.

Why it attracts. protects long-standing context.

Why it fails. removes the current request details the agent needs.

When it would be right. never for active handling.

Proposal. reserve working memory for the current task while compressing background.

Why it attracts. it is the correct concept but misread as truncation.

Why it fails. it is not truncation but reservation plus compression.

When it would be right. when design explicitly reserves space for the current error log while compressing stable architecture context.

How the same rule gets re-asked
  • Mutation that changes the protected class from order history to system or tool definitions tests whether importance scoring is applied across modalities.
R10

Summarisation frequency as a cost and capacity tradeoff with threshold triggers

Summarisation reclaims space but itself costs tokens. Evidence describes an incident platform handling 100 or more rapid messages where summarizing after every 3 messages consumes 30% of the budget, while waiting until the end lets raw history overflow by message 60. The effective trigger is capacity threshold: summarize when context approaches about 80% full, or after a configurable number of turns such as 20 messages, whichever comes first. A sliding window that keeps the most recent 5 to 7 messages in full detail and replaces older turns with a concise summary, capped around 22K per request, is also rewarded.

Each summary adds tokens and cost. Too frequent compounds overhead without value, since each summary re-compresses already compressed content. Too infrequent lets history grow until requests fail. Threshold triggers balance the two: summarisation fires only when pressure is real, and caps prevent pathological growth.

Boundary. When sessions are short or tasks are independent, such as 6 months of independent article summaries, the correct frequency is never: each task should start fresh.

Recurring specifics. Thresholds include 80% full, 70% capacity, 90% full, 20 messages, every 10 messages, most recent 5 to 7 in full detail, and 2K summary cost. Evidence notes 85% versus 70% as a distractor that merely delays the problem. Calculations include saving about 35K tokens per turn for turns 6 to 8, yielding over 10B tokens saved at volume.

Wrong answers written against this rule

Proposal. summarize after every message.

Why it attracts. maximally conservative.

Why it fails. overhead dominates and can reach 30% while still losing figures.

When it would be right. when each turn is independently summarisable and the summary is the sole state.

Proposal. summarize only once at the end.

Why it attracts. preserves fidelity.

Why it fails. raw history exceeds the window by message 60 so the request fails.

When it would be right. when total history is known to fit the window before completion.

Proposal. never summarize and upgrade to a larger window.

Why it attracts. avoids design.

Why it fails. even the larger window is finite and attention still degrades.

When it would be right. when the session is bounded and larger window comfortably holds the maximum with headroom.

How the same rule gets re-asked
  • Mutation that changes summary cost from 2K to a different size tests whether the reader still trades overhead against history size. Mutation that adds a live budget target such as under 30 seconds and 0.05 per task tests whether the reader introduces a token budget with hard cutoff.
R11

Tool result trimming to task-relevant fields before context entry

Verbose tool outputs are a silent budget killer. An order lookup may return 40 or more fields including audit timestamps, warehouse codes, carrier identifiers, and marketing tags, while the return workflow needs only about 5 fields such as order_id, order_date, total_amount, return_eligible, item_description or order status, purchase date, item, amount, return-window deadline. After several lookups, even three calls nearly fill the window. The fix trims each result to only relevant fields before appending to context:

Column trimming for Snowflake results from 40 or more to 5 relevant also recurs, reducing size by 80 to 90%.

example.py
python
def trim_order_result(raw_result, relevant_fields=None):
    if relevant_fields is None:
        relevant_fields = [
            "order_id", "order_date", "total_amount",
            "return_eligible", "item_description"
        ]
    return {k: v for k, v in raw_result.items() if k in relevant_fields}

Tool results accumulate turn after turn and are resent every subsequent request. Each irrelevant field pays rent for the rest of the session. Trimming at entry prevents debt from accumulating, while post-hoc summarisation or asking the model to ignore fields does not reduce tokens already in the window. A five-field projection stays at five fields for the remainder, whereas a 40-field verbatim result will be counted many times as history is resent. The application layer is deterministic, so trimming at entry gives the same result regardless of phrasing.

Boundary. Trimming is wrong when the downstream task genuinely needs the verbose fields, such as debugging fulfilment. It is also premature when context is not under pressure and the tool data is still actively relevant, which evidence marks as the one case where proceeding with additional lookups without modification is correct because majority usage alone is not a crisis. Capping rows may exclude relevant rows while column projection is correct, so the boundary is field relevance versus record relevance.

Recurring specifics. Counts include 40+ fields returned, 5 needed, 75 fields, a few needed, 60+ ledger fields, 8 matter, 50+ fields per study. Field names include order_id, order_date, total_amount, return_eligible, item_description, shipping_status, tracking_number, warehouse_bin_location, internal_routing_id. Trimmed results are about 80 to 90% smaller and tool outputs dominate after 3 to 4 lookups.

Wrong answers written against this rule

Proposal. instruct the model to ignore fields it does not need.

Why it attracts. zero code.

Why it fails. ignored fields still consume tokens every turn.

When it would be right. when budget is not under pressure and instruction merely reduces noise.

Proposal. switch to a larger window.

Why it attracts. accommodates payload.

Why it fails. only postpones accumulation.

When it would be right. when the workload is bounded to a single lookup.

Proposal. run every payload through a separate summarization call.

Why it attracts. seems to shrink while preserving nuance.

Why it fails. adds latency and risks progressive loss for precise fields.

When it would be right. when the relevant fields are not known in advance.

How the same rule gets re-asked
  • Mutation that swaps which 5 fields are relevant tests correct projection per workflow. Mutation that changes volume from 3 lookups to 15 tests row capping versus column trimming.
R12

Trimming location: middleware or PostToolUse hook, not model self-filtering

Trimming must happen before the verbose payload enters history, so the work belongs in the application layer between the tool backend and the context, not in the model's reasoning. Evidence rewards a middleware layer that intercepts the raw 75-field response and strips internal fields before the context append, or a PostToolUse hook that trims each lookup to return-relevant fields as it arrives. Once verbose data is in the context it stays for every subsequent turn.

The application layer is deterministic, while the model is probabilistic. A middleware filter guarantees the same projection on every call regardless of phrasing, and it applies zero reasoning to the filtering. The stripped fields never enter the count, whereas asking the model to skip them still pays the token cost. Even when the system prompt says only use these 5 fields, quality still degrades after a few verbose results because the cost is structural.

Boundary. Hook-based trimming is unnecessary when the tool itself can return only the needed projection, such as a SQL query that selects 5 columns instead of SELECT *. Pushing projection to the data layer is more efficient. The opposite where in-model reasoning is appropriate is when relevant fields are not known a priori and must be extracted by the model from an unstructured document.

Recurring specifics. Layer names include middleware layer, PostToolUse hook, tool implementation itself. Flags include fields filtered before they enter conversation history and tool_result blocks. Evidence also describes limiting counts, such as last 5 events with high-signal identifiers, as complementary to field trim.

Wrong answers written against this rule

Proposal. increase summarization frequency so older outputs are compressed sooner.

Why it attracts. addresses accumulation indirectly.

Why it fails. does not stop new oversized payloads from entering.

When it would be right. as complement to trimming when history also needs compression.

Proposal. keep full output and ask the model to attend only to relevant fields.

Why it attracts. avoids code.

Why it fails. attention does not reduce token cost.

When it would be right. when the model must decide relevance dynamically from unstructured content.

How the same rule gets re-asked
  • Mutation that moves trimming from before append to after append with summarization flips correctness. Mutation that shifts the field set from order fields to ledger or study fields keeps the location principle.
R13

Stateless Messages API requires full history resend for coherence

The Messages API is stateless across requests. The server retains no conversation state between calls. Continuity exists only when the application stores the transcript and resends the relevant prior messages in the messages array on every request. Without that resend the model has no memory of earlier turns, so it asks for the customer's name again after three verification questions or claims no research findings were provided. The pattern is client-owned memory with server-side statelessness as a deliberate scalability trade.

Statelessness keeps the API horizontally scalable, but whatever the model should know this turn must be placed in the request by the application. Expecting server-side session memory or prompt caching to retain the conversation misreads the contract: caching is a cost and latency optimization, and the full history must still be transmitted each time. Forgetting between turns is almost always caused by history not being passed, not by model limits.

Boundary. History resend can be reduced when the workload shifts to managed alternatives that explicitly offer session handling. Within the raw API, the nearby opposite is selective resend where earlier turns do not have to be verbatim model output: the application may include synthetic assistant messages, summarized old turns, or pruned irrelevant turns, which gives direct control over what occupies the window. That is curated inclusion, not omission.

Recurring specifics. The correct action is store each conversation's history in the application layer and send the accumulated user and assistant messages with every request and the failure modes are resend only the latest exchange or concatenate prior turns into system which evidence marks wrong. The two-turn playlist preference and three-question verification scenarios recur.

Wrong answers written against this rule

Proposal. the system prompt overwrote earlier messages.

Why it attracts. blames prompt configuration.

Why it fails. system and messages coexist and do not overwrite; the forgetting is history not being resent.

When it would be right. when the system is so large that it crowds out history, the issue is budgeting rather than forgetting.

Proposal. prompt caching retains the conversation's logic so resend is unnecessary.

Why it attracts. conflates caching with memory.

Why it fails. cached tokens still count and still must be transmitted; caching changes price, not persistence.

When it would be right. never as replacement for resend; only as cost reduction for repeated stable prefixes.

Proposal. the API needs a session_id or vector database to remember.

Why it attracts. maps to other systems.

Why it fails. the Messages API does not have that contract.

When it would be right. in managed harnesses that add a state layer above the API.

How the same rule gets re-asked
  • Mutation that distinguishes short forgetting within a session versus resumption after hours tests whether both are history-resend failures. Mutation that compares full verbatim resend versus curated resend with synthetic summaries tests control afforded by application-owned history.
R14

Agentic loop turn structure: assistant tool_use plus user tool_result with matching identifiers

An agentic loop is a client-side feedback loop driven by stop_reason. When the model returns stop_reason: tool_use with one or more tool_use content blocks, the application executes the tool, appends the assistant turn exactly as returned including all tool_use blocks, then appends a new user message whose content is tool_result blocks each referencing the matching tool_use_id, and sends the full history again. The loop continues while tool_use is returned and stops only when a different stop reason such as end_turn arrives.

example.ts
typescript
const response = await anthropic.messages.create({
  model: "model-id",
  messages: messages,
  tools: [lookup_order, process_refund, escalate_to_human]
});
if (response.stop_reason === "tool_use") {
  messages.push({ role: "assistant", content: response.content });
  const toolResults = response.content
    .filter(b => b.type === "tool_use")
    .map(block => ({
      type: "tool_result" as const,
      tool_use_id: block.id,
      content: executeTool(block.name, block.input)
    }));
  messages.push({ role: "user", content: toolResults });
}

The model correlates each result with the specific call via tool_use_id. If the assistant turn is missing, or the result is sent as system or without the identifier, the model cannot establish correlation and re-requests the same tool. Statelessness means the entire correlation context must be explicit.

Boundary. The strict pairing is relaxed only after end_turn, where no further results are needed. Pause and resume handling for max_tokens, pause_turn, or refusal are distinct stop reasons that need branches such as retrying with larger limit or continuing a paused turn. The opposite where the structure is intentionally edited is context management that summarizes or prunes history, which is valid for long horizons but not as the routine next-turn mechanism.

Recurring specifics. Fields include stop_reason, tool_use, tool_result, tool_use_id, type: tool_use, type: tool_result, roles user and assistant, and the top-level system field which must not carry tool output. Example shape: Other shapes include the three-state loop of end_turn for done, tool_use for execute and loop, and max_tokens or pause_turn for handling truncation.

Wrong answers written against this rule

Proposal. replace accumulated history with a single user message summarizing the tool output.

Why it attracts. saves tokens.

Why it fails. destroys context the model needs to reason about its own in-flight call.

When it would be right. only as long-context mitigation after many iterations, not as next-turn mechanism.

Proposal. insert tool output into the top-level system field.

Why it attracts. keeps result visible.

Why it fails. severs identifier pairing and misuses a channel for standing instructions; it also defeats caching by mutating the stable prefix.

When it would be right. never for tool results.

Proposal. send only the latest tool_result blocks in a fresh request relying on server-side session state.

Why it attracts. assumes server remembers.

Why it fails. the API is stateless.

When it would be right. in a managed harness that advertises that behavior.

How the same rule gets re-asked
  • Mutation that compares appending the full assistant content versus only its text summary tests whether all content blocks are preserved. Mutation that compares tool_result as user versus assistant tests the required pairing.
R15

Prompt caching is positional prefix matching from the start

Prompt caching reuses computed state of a stable prefix so later requests do not pay to reprocess it. The match is byte-for-byte identical prefix lookup in fixed order tools, then system, then messages, up to and including the block marked with cache_control. A cache write happens when the request first presents that prefix, billed at a premium, and subsequent requests that present the same prefix get a cache read at a discounted rate. Interleaving volatile content or changing any token before the breakpoint invalidates the match.

Reusing a prefix avoids re-encoding, reducing cost and time to first token for warm traffic. Because lookup is prefix-based, any difference at the front invalidates everything after it, so structure determines hit rate. Evidence shows a large static handbook at the very beginning with a breakpoint after it yields hits across hundreds of requests per hour, while placing user history first yields zero hits.

Boundary. No effect when the prompt changes entirely every request or when the stable prefix is below the minimum length, where evidence shows both cache_read_input_tokens and cache_creation_input_tokens remain zero. The opposite where caching is not the lever is cost control for small or highly dynamic prompts, where a token budget or summarisation is more effective, and for large stores exceeding the window where retrieval is required.

Recurring specifics. Static sizes include 50K handbook, 12K system prompt, 30K system instructions, 20K policy knowledge base, 500KB library reused across calls. Phrases include stable prefix, prefix-based cache, cache_control: { type: ephemeral }, and the split input_tokens versus cache_read_input_tokens versus cache_creation_input_tokens that together still count toward the window. The location of the breakpoint relative to the varying tail determines whether the same prefix is seen across requests, and costs are reported as regular versus cached rates but do not change the hit logic._read_input_tokens versus cache_creation_input_tokens.

Wrong answers written against this rule

Proposal. cache the entire prompt including per-request document content.

Why it attracts. hopes to reuse variable content.

Why it fails. per-request content differs, so every prefix is unique and no hit occurs.

When it would be right. when the document is actually static across requests.

Proposal. create a separate breakpoint every 100 tokens.

Why it attracts. assumes granular chunks are independently cached.

Why it fails. caching keys on a contiguous prefix, not scattered checkpoints, and the API limits breakpoints to four.

When it would be right. never for dense breakpoints; at most when up to four schedules need to be separated.

Proposal. let caching happen automatically without marking a breakpoint.

Why it attracts. expects automatic identification.

Why it fails. the prefix must be explicitly marked; without a breakpoint the request is uncached.

When it would be right. when the top-level automatic form is used and the stable prefix still comes first.

How the same rule gets re-asked
  • Mutation that swaps static size from 50K to 12K or 500KB tests whether size threshold matters.
R16

Cache breakpoint placement at the last stable block before varying content

The breakpoint must sit on the last content block that stays identical across requests, not on varying content. Teams that place cache_control on the final user message with its timestamp see large cache_creation_input_tokens on every request and zero cache_read_input_tokens, because each request writes a fresh entry whose hash includes the unique suffix. Moving the breakpoint to the end of the shared policy document or static instructions, before the per-request block, makes the stable prefix reusable. The stable prefix must come before any dynamic content, so ordering is static first, volatile after.

result.json
json
{
  "system": [
    { "type": "text", "text": "LONG_STATIC_INSTRUCTIONS" },
    { "type": "text", "text": "REFERENCE_DOC", "cache_control": { "type": "ephemeral" } }
  ],
  "messages": [
    { "role": "user", "content": "dynamic user message" }
  ]
}

A prefix match requires the entire prefix up to the breakpoint to be identical. Including even a short varying timestamp makes the prefix unique, so no future request matches and every call pays the write premium, which at 1.25x is more expensive than no caching. Placing the breakpoint at the stable boundary isolates the variable suffix as regular input_tokens while the prefix is served at 0.1x. Evidence marks variable data before stable prefix invalidates the cache each call as the canonical failure.

Boundary. Flips when there is no stable prefix at all, where adding a breakpoint is useless and would always write. The opposite where a single breakpoint at the end of the longest stable section is not enough is multiple stable schedules, where additional breakpoints are needed to keep earlier schedules reusable when a later one changes.

Recurring specifics. Failure signatures include cache_creation large, cache_read zero on every request and costs rise above uncached baseline. Correct shape: Note that cache_control is a field in the content block, not a top-level cache: true flag.

Wrong answers written against this rule

Proposal. replace explicit breakpoint with a top-level field so the system manages caching automatically.

Why it attracts. assumes automatic will find the stable portion.

Why it fails. automatic still caches a prefix up to the last cacheable block, so with a varying suffix it falls into the same trap.

When it would be right. when the prompt has a single stable prefix and short dynamic tail, explicit is still the safe choice.

Proposal. move breakpoint to the end of the varying suffix so it refreshes each time.

Why it attracts. misunderstands expiry.

Why it fails. guarantees every prefix is unique.

When it would be right. never for reuse; only to populate a cache without generating a response via max_tokens: 0.

Proposal. extend TTL to 1 hour instead of 5 minutes to fix poor hit rates when breakpoint is on varying content.

Why it attracts. assumes lifetime is blocker.

Why it fails. lifetime is irrelevant when prefix hash never matches; longer TTL only doubles write cost.

When it would be right. when traffic is bursty with gaps longer than 5 minutes and breakpoint is correctly placed.

How the same rule gets re-asked
  • Mutation that changes varying content from timestamp to user question keeps the rule but tests whether the reader still moves the breakpoint before the varying block.
R17

Multiple breakpoints ordered by change frequency and the four-breakpoint limit

A request may define up to four cache_control breakpoints, letting each independently scheduled stable section be cached separately. Evidence describes four stable sections changing on different schedules: tool definitions rarely, policy block weekly, style guide monthly, knowledge document daily. The team wants a change to invalidate only that section onward. The answer is one breakpoint per section, ordered from most stable to most volatile, so a daily change invalidates only its suffix while earlier prefixes remain cached.

Because caching is prefix-based, invalidating from a breakpoint onward means later schedules must appear after earlier ones. If a volatile section sits before a stable one, every volatile change invalidates the stable suffix. Ordering by stability minimizes rewrite cost. The four-breakpoint cap is the other constraint.

Boundary. Unnecessary when only one stable schedule exists, such as a single handbook changing once a month, where one breakpoint suffices. Also counterproductive when the stable prefix is below the minimum length. The opposite where fewer breakpoints are better is when automatic management with a single top-level field is sufficient for a growing conversation.

Recurring specifics. Counts include up to four cache_control breakpoints per request, one per section. Shapes include tools then system then messages as the prefix hierarchy, and the note that breakpoints add no direct cost beyond writes and reads.

Wrong answers written against this rule

Proposal. a single breakpoint at the top suffices and merging sections is recommended.

Why it attracts. simplifies config.

Why it fails. merging destroys ability to keep earlier schedules cached when a later one changes.

When it would be right. when all sections share the same schedule.

Proposal. breakpoints scale with token count rather than section count.

Why it attracts. assumes cost drives count.

Why it fails. the limit is on sections, not tokens.

How the same rule gets re-asked
  • Mutation that changes which section is daily versus monthly tests whether the reader reorders breakpoints.
R18

Minimum cacheable prompt length varies by model tier and causes silent bypass below threshold

Caching is skipped silently when the prefix marked with cache_control is shorter than the model's minimum cacheable length. The API still returns success with no error, but both cache_creation_input_tokens and cache_read_input_tokens are zero. Evidence documents this for a stable prompt of about 2,500 tokens with traffic every minute: no error, both fields zero. Minimums vary by tier: one tier requires about 1,024 tokens while another requires 4,096, so the same 2,500-token prefix caches on one tier and is ignored on another. Remedies are to expand the stable prefix with additional reference content or to route to a tier whose minimum the prefix already clears.

Caching has per-entry overhead, so very short prefixes are not worth storing. The API enforces a minimum and fails silently rather than erroring, which makes diagnosis non-obvious. Padding with useful content is often net positive because reads cost only 0.1x while writes cost 1.25x.

Boundary. Irrelevant when the stable prefix is already large, such as 12K or 50K handbooks. Also not the cause when the request is genuinely uncacheable due to variability, where the fix is breakpoint placement rather than padding. The opposite where no padding is needed is when the workload can move to the lower-minimum tier.

Recurring specifics. Thresholds include 1,024 and 4,096 as tier minima, with 2,500 as the example between them. Verification is via the usage fields: if both are zero suspect sub-minimum or varying prefix.

Wrong answers written against this rule

Proposal. change to ttl: 1h so entry persists longer.

Why it attracts. assumes expiry explains zero hits.

Why it fails. with traffic every minute even the 5-minute cache would be refreshed if an entry existed.

When it would be right. when signature is large creation on every request with zero read, which is placement, or when gaps are longer than 5 minutes with correct placement.

Proposal. replace block-level breakpoint with a top-level field.

Why it attracts. assumes automatic avoids minimum.

Why it fails. automatic shares same minimums and prefix rules, so it cannot cache sub-minimum either.

How the same rule gets re-asked
  • Mutation that changes stable size from 2,500 to just above or below threshold flips which fix is correct.
R19

Cache lifetime, write cost, read discount, and warm-keeping strategy

Cached prefixes have a limited lifetime. Default is about 5 minutes since last use, with a hit refreshing the timer at no extra cost. An extended lifetime of about 1 hour is available via ttl: 1h on the cache_control block, at higher write cost. Pricing that recurs is about 1.25x base input for a 5-minute write, 2x for a 1-hour write, and 0.1x for a read. Evidence frames the break-even as one read for the default and two reads for the extended, and describes warm-keeping by sending a request with max_tokens: 0 to populate the cache without generating a response. Stable ordering with cache_control: { type: ephemeral } on the stable block is also documented.

If the same prefix is reused within the lifetime, reads amortize the write premium and net is savings plus lower time to first token. If the next request arrives after expiry, the next call pays the write again and caching can become a net increase, which is why evidence shows caching as a pure cost increase when every call writes. Warm-keep with max_tokens: 0 is a cheap heartbeat for bursty traffic with quiet gaps longer than the lifetime, and the 1-hour option is for steady cadences such as every 20 minutes where the default would expire.

Boundary. Not binding for high-frequency traffic with hits every minute, where even the default is continuously refreshed and the 1-hour option would only add cost. Not the constraint when the breakpoint is misplaced on varying content, where no lifetime can create hits. The opposite where caching is not reused is when prompts change entirely or are one-off large probes, such as testing a 900K-token contract for capacity, where no prefix will be reused.

Recurring specifics. Values include 5 minutes default, 1 hour extended, 1.25x write, 2x write, 0.1x read, 90% discount, and the shape cache_control: { type: ephemeral } with optional ttl: 1h. Evidence notes max_tokens: 0 for warming and that a read renews the entry for the same duration as the preceding write.

Wrong answers written against this rule

Proposal. rely on caching to reduce token cost forever or to remove billing for the system prompt entirely.

Why it attracts. overstates benefit.

Why it fails. reads are cheaper, not free forever, and writes still cost; the first call after expiry pays write again.

When it would be right. never as stated; at most cache the system prompt while the lifetime holds.

Proposal. use caching to cache output tokens or to speed up one-off single prompts.

Why it attracts. conflates input reuse with output.

Why it fails. caching applies to repeated input prefixes, not outputs, and one-off prompts get no benefit.

When it would be right. when many requests reuse the same large stable prefix.

How the same rule gets re-asked
  • Mutation that changes arrival cadence from every minute to every 20 minutes flips the correct lifetime from default to 1 hour.
R20

Cached tokens still occupy the context window

Caching is a billing and latency optimization, not a capacity expansion. Every token still occupies the window regardless of whether it was served from cache. With caching the input count is reported across three fields: input_tokens, cache_read_input_tokens, and cache_creation_input_tokens, and the correct accounting is their sum. Evidence shows a 150K cached document on a 200K window still leaves only about 50K for history and output, and a dashboard that plots only input_tokens shows tiny values under 1,000 while requests still fail with prompt is too long.

The model must attend over cached tokens to generate, so they must be present in working memory. Caching reuses computed key-value state but does not remove tokens from the attention span. That is why the window limit is hit even when input_tokens looks small after a breakpoint: the other two fields account for the same tokens under different billing.

Boundary. Matters when window pressure is the failure mode, such as long conversations approaching the limit or large document inclusions. Easy to forget when workload is cost-focused, where caching genuinely helps cost while still not helping capacity. The opposite where caching does relieve pressure is not window capacity but budget: prompt caching plus a larger window can together be the right answer, but caching alone is never the fix for prompt is too long.

Recurring specifics. Error messages include 400 prompt is too long, status 400, and invalid_request_error. Window sizes include 200K, 1M, and splits like 150K cached, 50K remaining. Usage fields are input_tokens, cache_read_input_tokens, cache_creation_input_tokens, and their sum. Monitoring must sum all three, not just input_tokens.

Wrong answers written against this rule

Proposal. enable caching to stop cached tokens from counting toward the window.

Why it attracts. conflates cost and capacity.

Why it fails. all three fields count; caching does not free space and compaction or editing is needed.

When it would be right. when goal is lower cost or latency for warm traffic.

Proposal. increase max_tokens so the window grows.

Why it attracts. confuses output headroom with input capacity.

Why it fails. max_tokens is an output ceiling, not input expansion, and requests still fail when input alone exceeds the window.

When it would be right. when generation is truncated mid-summary.

Proposal. switch to the Batch API to get an expanded window.

Why it attracts. assumes batch changes limits.

Why it fails. batch changes processing mode, not per-request window.

How the same rule gets re-asked
  • Mutation that changes which component is cached from document to system or tool definitions keeps the rule. Mutation that changes failure from prompt is too long on input alone to stop_reason indicating truncation during generation tests distinction between input overflow and output exhaustion.
R21

Full knowledge base inclusion versus retrieval decision governed by knowledge base size and query shape

Evidence draws a threshold around 200K tokens for inclusion versus retrieval. When the knowledge base is below about 200K, such as a 120K handbook or 60K policy, the simplest and most reliable design is to include the entire text as a stable prefix with caching, because every policy is then visible with zero retrieval risk. When the store is 4M or 2.5M or 100K pages, full inclusion is impossible or diluted, and each question targets a narrow slice, so embedding-based retrieval with reranking that fetches only the top few relevant chunks is required.

Retrieval is a lossy filter that adds a failure mode: wrong embedding, poor chunk boundary, query mismatch. Paying that cost is justified only when the store exceeds the window. Below the threshold, inclusion avoids that risk entirely and caching answers the cost concern. Query shape reinforces the decision: synthesis questions comparing rules across lifts, lodging, and lessons are scattered and retrieval may miss a needed section, while narrow torque-spec lookups favor retrieval.

Boundary. Retrieval becomes correct even for mid-size stores when synthesis must combine evidence from many sources where an embedding misses scattered evidence; then full context is still better despite size. Hybrid exact-match for lookups keyed to exact identifiers improves precision, but some form of retrieval remains essential when the store is far beyond the window.

Recurring specifics. Sizes include 60K, 80K, 120K below threshold, 200K as guidance line, 2.5M, 4M, 5M textbooks, 100K pages. Phrases include include entire knowledge base as stable prefix with caching versus index with embeddings and fetch only relevant chunks.

Wrong answers written against this rule

Proposal. summarize the full handbook and use only the summary.

Why it attracts. saves tokens.

Why it fails. summary omits exceptions and jurisdiction language needed for authoritative answers, and loses cross-section view.

When it would be right. as navigational aid, not replacement.

Proposal. split one question across several sequential requests and merge answers.

Why it attracts. works around window.

Why it fails. no single call has cross-section view and spend multiplies.

When it would be right. when subtasks are independent, such as scoring 20 PDFs in parallel.

How the same rule gets re-asked
  • Mutation that changes the store from 60K to 4M flips the answer from inclusion to retrieval. Mutation that changes the query from synthesis to narrow lookup keeps retrieval but changes whether hybrid exact-match matters.
R22

Upstream agent output shaping to structured findings with citations and scores

In multi-agent pipelines, upstream subagents often return verbose reasoning chains and raw output that downstream synthesis agents do not need. Evidence shows a coordinator aggregating six subagents each returning 10 to 15K tokens of raw output, leaving little window for synthesis and causing shallow reports. The fix modifies upstream agents to return compact structured records such as key facts, citations, relevance scores, and dates instead of prose chains. Shapes that recur include:

and per-study records with effect sizes, confidence intervals, sample sizes, risk-of-bias ratings.

result.json
json
{
  "findings": [
    {
      "claim": "Renewable energy investment grew 12% in 2023",
      "source": "IEA World Energy Report 2024",
      "sourceUrl": "https://example.com/report",
      "relevanceScore": 0.92,
      "publicationDate": "2024-01-15"
    }
  ]
}

All content flows through the coordinator window, so verbose intermediate output directly crowds out synthesis budget and forces lossy compression before the synthesis agent sees it. Structured outputs preserve density while cutting tokens, so the coordinator can pass information-dense findings within budget. Evidence marks instruction to use every record as insufficient once verbose content has filled the window.

Boundary. Verbosity is acceptable when upstream and downstream run in isolated subagent contexts that never share the coordinator window, or when total output is small. The opposite where isolation alone is correct is bulk file scanning where each subagent runs with a fresh window, which is the right answer for scanning rather than shaping, showing that isolation and shaping are complementary.

Recurring specifics. Fields include claim, source, sourceUrl, relevanceScore, publicationDate, citations, risk-of-bias rating, key facts. Sizes include 10 to 15K per subagent, 8 to 10 specialist interactions, and 160K coordinator context.

Wrong answers written against this rule

Proposal. have synthesis query upstream agents directly, bypassing coordinator.

Why it attracts. avoids passing verbose content.

Why it fails. breaks coordinator routing, observability, and error handling.

When it would be right. never in coordinator-subagent architectures.

Proposal. increase coordinator max_tokens to hold full verbose output.

Why it attracts. seems to buy headroom.

Why it fails. max_tokens is output ceiling, not input expansion, and window still fills.

How the same rule gets re-asked
  • Mutation that changes coordinator pressure from input crowding to post-compression central loss tests whether the reader distinguishes shaping at the source from better passing.
R23

Subagent isolation to contain verbose exploration noise

Exploration of hundreds of files produces verbose dumps that dilute the orchestrating session. Evidence shows a monorepo audit where tool output rapidly fills the main session and the assistant loses track of earlier instructions. The fix delegates scanning to subagents, each with its own window, tool access, and system prompt, that absorb noisy intermediate output and return only distilled findings. Recommended scoped tool sets include Read, Grep, Glob as read-only.

The main session pays only the small cost of the summary rather than full contents, so long orchestration keeps working memory clean. Each subagent's window is isolated, so verbose reads from one group do not crowd out instructions for another. Evidence notes that caching does not relieve this pressure because cached tokens still occupy the window, and larger windows do not curate content.

Boundary. Overhead when the work is small and the file set comfortably fits the main window, where direct reads are simpler. The opposite where isolation alone is insufficient is when specific taxonomy decisions must carry across groups, where evidence requires an external scratchpad plus isolation.

Recurring specifics. Shapes include hundreds of files, 30+ microservices, 250-file sweep, 500K codebase, and the instruction to use subagents with isolated context windows returning summarized findings. Tool sets include Read, Grep, Glob.

Wrong answers written against this rule

Proposal. enable caching on repository files so tokens no longer occupy window.

Why it attracts. conflates caching with isolation.

Why it fails. cached tokens still occupy window.

Proposal. add system instruction to summarize each file after reading.

Why it attracts. seems to reduce output.

Why it fails. tool results still land in context in full; written summary does not shrink the tool result payload.

How the same rule gets re-asked
  • Mutation that changes delegation from bulk scanning to bulk document analysis tests whether isolation still applies. Mutation that adds a cross-group decision dependency tests whether isolation must be paired with a scratchpad.
R24

External scratchpad file for cross-phase persistence and compaction survival

Long multi-phase investigations accumulate confirmed facts that must survive compaction, which summarizes conversation to free tokens. Evidence shows /compact or server-side compaction may lose specific numbers and contracts captured early, and a note in CLAUDE.md helps common cases but not specific taxonomy entries. Shapes include the documented summary schema of Task Overview, Current State, Important Discoveries, Next Steps, Context to Preserve inside <summary> tags and scratchpad entries with module name, key class, file location, entry points, dependencies.

The live conversation is itself the degrading medium, while a file on disk is external to the window and can be re-read at full fidelity whenever needed. Caching the file read is independent of summarisation, so compaction can clear verbose dumps without discarding the authoritative record. Evidence calls this converting passive buried memory into active fresh context.

Boundary. Unnecessary for single-phase short tasks where full history still fits and will not be compacted. Wrong place for verbose dumps that should be trimmed at entry. The opposite where a fresh session is better than a scratchpad is when prior tool results have gone stale due to external state changes, where starting fresh with a verified summary is correct rather than continuing with stale dumps plus a scratchpad.

Recurring specifics. File names include findings.md, SCRATCHPAD.md, exploration-scratchpad.md, CASE_SUMMARY.md, memory tool at {"type": "memory_20250818", "name": "memory"} with view, create, update, delete under /memories. Evidence notes CLAUDE.md persists through compaction while conversation blocks do not.

Wrong answers written against this rule

Proposal. run /compact more often and let summary carry specific budgets.

Why it attracts. keeps window small.

Why it fails. compaction summarizes, and precise figures are distorted into generic phrases like standard timeout.

When it would be right. when compacted content is verbose narrative, not verbatim figures kept elsewhere.

Proposal. continue same session and let Grep and full Read results accumulate so coordinator retains maximal raw context.

Why it attracts. fears losing source.

Why it fails. accumulation is itself what degrades recall and creates contradictions.

How the same rule gets re-asked
  • Mutation that swaps use /compact alone versus scratchpad plus /compact flips correctness. Mutation that moves write from after each phase to after each file tests granularity.
R25

Stale tool result handling on session resumption

Tool results are point-in-time snapshots. When a session is resumed hours later, such as after 4 hours with status PENDING, Expected resolution: 24-48 hours, old tool_result blocks remain in context and the model treats them as current, even after fresh calls return different data. Evidence shows the agent confidently stating your expected resolution is 24 hours from stale results. The correct handling resumes with human and assistant turns but filters out old tool_result blocks so the agent must re-fetch current data, preserving the thread. When hosts were re-imaged and IPs reassigned out of band, the stronger fix starts a fresh session with a structured summary of verified correlations plus current live state.

Having both stale and fresh data for the same entity creates ambiguity that instructions to prefer recent data reduce but do not eliminate, because the model anchors on the older concrete value still visible. Filtering removes ambiguity at the source. Fresh fetches without filtering still leave stale blocks alongside fresh, so two conflicting values confuse the agent. Evidence frames this as contamination, not memory.

Boundary. Filtering is not needed when resumed data is not time-sensitive, such as static policy or resolved outcomes. The opposite where a persistent case facts overlay is not enough is when load-bearing artifacts are themselves captured tool outputs that have gone stale; then a new session seeded with re-verified exact fields is required rather than an overlay.

Recurring specifics. Stale values include status: PENDING, resolution: 24-48 hours, Day 1 trace IDs and latency percentiles, old IP maps and host lists. Correct shapes include resume with human and assistant history but programmatically filter out old tool_result blocks, forcing re-fetch and start fresh with verified correlations plus current restart and log-rotation status.

Wrong answers written against this rule

Proposal. add system instruction to always assume data may be old.

Why it attracts. cheap prompt change.

Why it fails. model still quotes concrete stale values despite vague instructions, and tests show stale reference persists even after fresh calls when both values remain visible.

Proposal. automatically re-call all previously used tools at session start while keeping stale blocks.

Why it attracts. fills context with fresh data.

Why it fails. stale blocks remain alongside fresh, so two values for the same entity persist and choice remains ambiguous.

How the same rule gets re-asked
  • Mutation that changes the gap from 4 hours to 20 minutes tests whether filtering is still correct when data is less likely stale but still point-in-time.
R26

Chunked and overlapping segmentation for long-document extraction

Long documents suffer attention dilution even when they fit the window, so evidence rewards splitting into overlapping segments processed as primary context. Patterns include overlapping 20-page chunks with 5-page overlap, 5-page chunks for a 50-page invoice, 30-minute chunks for a 90-minute transcript, and three overlapping passes of 300K each for an 800K manual. Each chunk is extracted in isolation, then results are merged and consolidated with overlaps removed, with each segment's middle becoming an edge for its subagent.

Segmentation ensures every section is read as a beginning or end for some pass, directly countering the middle disadvantage without relying on instructions. Overlap prevents boundary loss where a clause references a definition split across chunks. Evidence shows single-pass large-window processing yields 20% lower accuracy on middle sections, while chunked extraction restores recall to short-document rates.

Boundary. Single-pass inclusion is correct when the document is small and already in a high-attention zone and chunking would add merge complexity. Segmentation is not the tool when the task requires holistic comparison across the entire document in one reasoning step that cannot be decomposed; then hierarchical synthesis or front-loaded summaries may be complementary.

Recurring specifics. Chunk sizes include 20 pages with 5-page overlap, 15 reports with Report 8 in the middle, 50-page invoice with 5-page parallel synthesis, 500-page PDF with 20-page chunks. Overlap is chosen to cover cross-reference distance.

Wrong answers written against this rule

Proposal. tell the model to be very careful with middle pages.

Why it attracts. lowest effort.

Why it fails. does not change attention distribution.

Proposal. use the Batch API to process full documents or increase max_tokens.

Why it attracts. conflates throughput or output budget with attention quality.

Why it fails. batch uses same models and does not fix dilution; output length does not affect input recall.

How the same rule gets re-asked
  • Mutation that changes critical location from middle pages to specific field types such as dates tests whether segmentation still applies.
R27

Context rot and the optimization tradeoff: more context is not always better

Evidence describes a well-documented degradation where accuracy and recall fall as token count grows, called context rot, even when the window is not full. Teams that expanded retrieval from 50K to 600K on a 1M window saw accuracy decline with no model change. The cause is attention dilution, decision fatigue, and irrelevant context scattering signal. The correct stance is that context engineering is an optimization balancing accuracy, latency, and cost, where the discipline is selecting the right context, not all context, with retrieval discipline and curation as levers.

Every additional token competes for fixed attention and adds cost. Irrelevant material scatters signal and increases middle-position risk. Evidence shows that stuffing 20 documents per query, or sharing all product docs and past tickets, violates the principle and that careful selection restores accuracy while reducing spend.

Boundary. Large context is the right choice when the workload genuinely needs cross-document synthesis and the window is used as headroom for a single large task, such as a 900K contract on a 1M window where cross-references cannot be chunked. The opposite where larger is not better is when chunking already provides full recall; there more context per request is wasteful.

Recurring specifics. Contrasts include 50K to 600K still within window but accuracy down, 20 documents retrieved, docs 10 to 20 ignored, all product docs plus tickets stuffed. Trade-offs include accuracy, latency, cost as the three-way optimization.

Wrong answers written against this rule

Proposal. move to a larger window and continue sending the full dossier.

Why it attracts. promises capacity.

Why it fails. still pays to transmit mostly irrelevant content and leaves history unmanaged; rot persists.

Proposal. rely on training data once context is removed.

Why it attracts. assumes model already knows.

Why it fails. removes customer-specific ground truth.

How the same rule gets re-asked
  • Mutation that changes evaluation signal from accuracy decline to cost doubling tests whether the reader still diagnoses retrieval discipline.
R28

Token counting must use the full request shape and the target tokenizer

Pre-flight validation of whether a request will fit must include every component that counts toward the window: system prompt, tools, every message including documents and images, and output headroom. The tool is the token counting endpoint, which accepts the same structured inputs as message creation and returns an estimate under the tokenizer of the model specified. Evidence shows estimating from PDF character counts or document alone undercounts because PDFs add image tokens per page, and that tool definitions and optimization tokens may not be billed but must still be counted for capacity. Cross-model migration also requires recounting, because a newer tokenizer may produce about 30% more tokens for the same text.

Tokenization is model-specific and window accounting is all-inclusive. An estimate based on one tokenizer or a partial request will pass the pre-check but fail at send time with prompt is too long. Relying on send and inspect usage fails because an oversized input is rejected before any usage is returned, so there is no data to inspect on failure.

Boundary. Rough character heuristics may be acceptable for tiny fixed prompts where the window is far from full and a safety margin can absorb error, but they become unreliable for document-heavy workloads. The opposite where counting is not needed is when the request is known to be tiny and bounded, such as a short chat turn, where overhead exceeds benefit.

Recurring specifics. Shapes include counting with system, tools, PDF, messages together, four-characters-per-token approximation as the misleading heuristic, and recount with target model identifier before migration. Evidence notes that caching reports split across three usage fields must be summed, but the counting endpoint provides the pre-flight estimate regardless of cache.

Wrong answers written against this rule

Proposal. count only PDF tokens since system and tools are handled separately.

Why it attracts. assumes separate processing.

Why it fails. all components count together.

Proposal. carry over counts measured on prior model since all models share one tokenizer.

Why it attracts. assumes portability.

Why it fails. tokenizers differ and counts shift by about 30% for newer families.

How the same rule gets re-asked
  • Mutation that changes miscount source from PDF character count to system prompt omitted keeps diagnosis as partial versus full request.
Refund agent that loses amounts to summarisation then recovers them
A production walkthrough with the reasoning chain made explicit.

A tier 1 customer support agent handles refund requests across a multi-issue session. The customer raises three issues in turn: a 247.83 refund for order 8891 placed on March 3rd for defective wireless headphones, a shipping delay on order 8902, and a duplicate charge of 49.95 on the same account. By turn 12 the history has grown past the summarisation threshold and the summariser compresses turns 1 to 8 into the customer raised issues about a refund, a shipping delay, and a billing question.

When turn 13 asks the agent to confirm the refund, the agent responds I can process your refund for the recent defective order, what is the amount. It does not remember the 247.83, the order number, or the date because the summariser destroyed them. The customer is forced to repeat the information and first-contact resolution drops.

The fix has three components. An extraction step at the end of every tool call pulls transactional facts and writes them into a persistent case facts block with customerId C-4421 and per-issue entries carrying orderId, orderDate, refundAmount, status, and item description. The prompt construction function always prepends this block to every message outside any summarisation pipeline. A tool result trimmer reduces each order lookup from 40 fields to the five relevant fields, cutting token cost by 80 to 90 percent.

By turn 30 the conversation history has been summarised several times but the case facts block still contains every order ID, amount, date, and status verbatim. The agent at turn 30 reads refundAmount 247.83 for order 8891 placed on March 3rd from the protected block and processes the refund without asking the customer to repeat. Prompt caching with a single cache_control marker at the end of the system plus tools block compounds the saving, where a 25,500 token static prefix is paid in full once per five minute window and read at about 10 percent for the remaining sessions in the window.

Distinctions that decide answers

ThisNot thisHow to tell them apart
Persistent case facts blockProgressive summarisationThe case facts block is structured, machine readable, and excluded from all compression. Summarisation produces free-form prose that is included in compression. The block survives cycles because it is structurally separate.
Cache prefix from index 0Cache anywhere in the promptCaching matches from the very first token prefix by prefix. A marker placed after volatile content never hits. The static prefix must start at the first token of the request body.
Context rot as a gradientHard token limit 200K or 1MContext rot is a monotonic decline in accuracy and recall as the count grows, with no published cliff. The hard limit is a hard stop where the request errors. A miss on a buried fact at 150K is degradation plus a positional effect, not a limit error.
Server-side compactionClient-side progressive summarisationCompaction is a request-level strategy the API executes, with a documented trigger, a documented summary structure, and a compaction block that becomes the truncation boundary. Client-side summarisation is code you write and maintain. Both still summarise, so both still need a protected facts block outside the summarised region.
CompactionTool result clearingCompaction replaces the older conversation with a summary, so it is the right lever when the bulk is dialogue and reasoning. Tool result clearing drops the oldest tool outputs and leaves placeholders, so it is the right lever when the bulk is retrieved payloads the model has already processed.
Conversation history compressionConversation history truncationCompression summarises older turns preserving gist. Truncation deletes older turns preserving nothing. Compression is preferred for customer-facing systems because the agent must remember the topic.
Tool result trimmingTool result summarisationTrimming removes irrelevant fields preserving original structure for relevant fields. Summarisation rewrites the result and is lossy for numbers and identifiers. Trimming is preferred for order lookups where five fields must remain verbatim.
Cached read cost at about 10 percentFree cachingCaching reduces per-token price, it does not eliminate per-call context footprint. A 100,000 token cached system prompt still consumes 100,000 tokens of attention budget per call; the cache only discounts price.

Traps

Summarise the history to manage context in a transactional system without a protected block

The tempting answer. Use progressive summarisation when history grows because compression feels like the obvious budget fix.

Why it fails. Abstractive summarisation systematically destroys exact amounts, dates, and identifiers that a transactional system must get right, collapsing them into vague generalities that look reasonable but cannot drive correct action.

What is correct. Extract those facts into a protected block before summarisation runs and exclude that block from every compression cycle.

Tell the model to pay attention to all parts of the context to defeat lost in the middle

The tempting answer. Add an instruction to pay attention to everything because adding guidance feels like the right intervention.

Why it fails. The attention pattern is shaped by position not by intent. The model does attend more reliably to the beginning and end regardless of instruction, so middle content remains underweighted.

What is correct. Place key findings at the start, detailed content under explicit headers, and the current question at the end, which structurally defeats the positional effect.

Write your own summarisation loop before checking what the API does

The tempting answer. Build a client-side summariser because managing history feels like application work and the reference material describes it that way.

Why it fails. Server-side compaction is the documented primary strategy and it comes with behaviour that is tedious to reproduce: a trigger on measured input tokens, a documented five-part continuation summary, and a compaction block the API itself uses as the truncation boundary on later requests. The one place a client-side implementation reliably goes wrong is token accounting, since accumulated cache-read counts from server-side tool calls can make a naive total look several times larger than the real context and fire compaction far too early.

What is correct. Enable the compact_20260112 strategy with an explicit trigger, use context editing when the bulk is old tool results rather than dialogue, and if you must count tokens yourself, use the token counting endpoint rather than summing usage fields.

Keep full 40 field tool results in context because the model might need them later

The tempting answer. Preserve all fields because discarding information feels less safe than keeping it.

Why it fails. Untrimmed results from verbose lookups persist through every subsequent turn and exhaust the budget across turns even though only five fields were ever relevant.

What is correct. Trim to relevant fields at the tool boundary before the result enters history, never after.

Assume prompt caching eliminates the cost of a large system prompt

The tempting answer. Treat a 100,000 token prompt as free once caching is enabled because caching sounds like it removes cost.

Why it fails. Caching reduces repeat-call price but not per-call attention footprint. The cached prompt still occupies the full attention budget on every call and size discipline still applies.

What is correct. Keep the system prompt well under the 10 percent guideline even when caching, and treat caching as a price discount not an attention discount.

Enable the 1M window to solve degradation

The tempting answer. Move from 200K to 1M to get more room because a larger window sounds like the fix for missed facts.

Why it fails. Degradation tracks how many tokens are actually in the request, and position within it, not how much headroom remains. An 800K prompt on a 1M-window model is deep into the degraded region even though the hard limit is far away.

What is correct. Restructure so critical content sits at the start or end, and reduce what is present at all, regardless of total window size.

Place the cache marker at the end of the request to cache the whole thing

The tempting answer. Put the marker after all content so everything is cached because caching the whole thing sounds comprehensive.

Why it fails. The cache matches left to right and volatile content at the start invalidates every subsequent token. If the user message sits at index 0 no cache hit occurs anywhere.

What is correct. Place the marker at the boundary between static and volatile content and keep the static prefix at the very start of the request body.

Going deeper than the task statement
Adjacent material that shows up in harder scenario questions.
FIFO versus priority based truncation strategies

FIFO drops the oldest turns first when the budget is exceeded, priority based ranking drops lowest priority content first with system prompt highest then immutable facts then recent conversation then oldest summarisable turns lowest.

Token Budgeting
Server-side compaction as the documented primary strategy

Rather than writing summarisation client-side, add the compact_20260112 strategy to context_management.edits with the compact-2026-01-12 beta header. The API triggers at a configured input-token threshold, defaulting to 150,000 with a floor of 50,000, generates a structured continuation summary, emits a compaction block, and drops every earlier block on later requests.

Context Compression
Six context sources and their per-source budget behaviour

Instructions and tools are fixed overhead on every request, external knowledge is variable per query, memory and state grow across turns, and user prompts are one shot per turn, each with its own optimisation guidance.

Context Sources: A Six-Level Taxonomy
Build it
Prove the protected block survives summarisation while caching saves cost
  1. Build a prompt construction function that always prepends a persistent case facts block to every message where the first entry carries both the system prompt and the serialised case facts, followed by history, followed by the current user turn, and where the block is never passed to the summarisation function.
  2. Implement an extraction step that pulls transactional facts from tool results and updates the case facts block in place, overwriting existing entries when a fact is confirmed and appending new entries when a new fact emerges.
  3. Send requests with a cache_control type ephemeral marker at the end of the static system plus tools block and verify ten requests within a five minute window show a cache hit on requests 2 to 10 with cache_read_input_tokens non-zero while the first shows cache_creation_input_tokens.
  4. Build a tool result trimmer that reduces a 40 field order lookup to exactly five fields of order_id, order_date, total_amount, return_eligible, and item_description and verify the trimmed result is 80 to 90 percent smaller.
  5. Run a 12 turn conversation that triggers summarisation after turn 8 and verify the agent at turn 13 still references the exact 247.83 refund, order 8891, and March 3rd date from the protected block without asking the customer to repeat.

Verify. Transactional facts survive every compression cycle intact, tool payloads stay lean, and the static prefix is reused at about one tenth of standard input price without sacrificing per-call attention discipline.

Answer real questions on this task
8 exam-style questions written against the rules above. You get the reasoning and a note on every wrong option after each answer.
Task 5.214 min

Escalation and Ambiguity Resolution

Route by three valid triggers, reject sentiment and raw confidence as signals, and ask for a disambiguating identifier when tool results are ambiguous.

What you need to know

Escalation calibration determines whether a customer support agent achieves its target first-contact resolution rate or quietly degrades into a system that punts every difficult case to a human while attempting every easy case autonomously. The exam tests the precise boundaries of when to escalate, when to resolve, and which commonly proposed triggers are unreliable in practice. There are exactly three valid escalation triggers, two unreliable triggers that look rigorous but fail, and one absolute rule about explicit human requests.

The first valid trigger is an explicit human request. The moment a customer says I want to speak to a person or transfer me to a human agent, the escalation happens immediately. No investigation, no let me see if I can help you with that first, no diagnostic questions. The customer has made a clear unambiguous request and the agent must honour it without delay. This is an absolute rule with no exceptions. The temptation to try once first wastes the customer's time, frustrates an already frustrated user, and signals that the system does not respect explicit requests.

The second valid trigger is a policy gap, distinct from a policy violation. A violation is a request that documented policy explicitly addresses and the answer is usually no, such as refund outside the return window or discount beyond the authorised limit. A gap is a situation the documented policy is silent on, such as a customer asking for competitor price matching when the policy only covers own-site price adjustments. The policy does not say no and does not say yes, it is silent. The agent cannot make policy on the fly, so the gap requires human judgement about whether to make an exception.

The third valid trigger is inability to make meaningful progress. The agent has attempted resolution and cannot advance because tools returned errors that local retry logic cannot resolve, the situation requires system access the agent does not have, the issue is a technical bug that needs engineering, or the order genuinely does not exist in any system the agent can query. The catch-all is genuine but only after a real attempt. I might not be able to handle this is not sufficient. The agent has to show it tried and failed.

The two unreliable triggers deserve direct attention because they sound rigorous but consistently fail in production. Sentiment-based escalation uses frustration detection or negative sentiment scores to route to a human. Frustration does not correlate with case complexity. A customer furious about a simple late delivery is easy to resolve with an apology, compensation, and reship. A calm polite customer asking about a competitor price match needs human judgement on a policy gap. Sentiment measures emotional state not case difficulty, so routing by sentiment inverts the correct priority. The same applies to politeness and tone classification. Self-reported confidence scoring is the second unreliable trigger. Having the model output a 1 to 10 score and escalating below a threshold sounds engineering grade but LLM self-reported confidence is poorly calibrated. The model is often incorrectly confident on hard cases and unnecessarily uncertain on straightforward cases. This produces the exact failure the exam tests, an agent that escalates simple cases while attempting complex ones. Confidence earns its place only as a routing signal in a calibrated system with external ground truth, not as a primary trigger. Objective signals like self-consistency checks that run the same prompt three times and escalate on disagreement and hedging detection that flags uncertain language are more reliable than self-reported scores.

The three frustration cases and ambiguous matching

If the issue is straightforward and the customer is frustrated, acknowledge the frustration, offer the resolution, and do not escalate. I understand this is frustrating, I can process your replacement right now is the correct pattern. If the customer reiterates a preference for a human after you have offered help, escalate because they have been given an opportunity to accept agent resolution and declined. If the customer explicitly says I want a human from the start, escalate immediately with no investigation step. The distinction is between a frustrated customer with a resolvable issue where you resolve and a customer who explicitly wants a human where you escalate.

Ambiguous customer matching is its own failure mode. When a tool returns multiple matches for a name search such as three John Smith records, the agent must ask for additional identifiers like email address, phone number, or order number. The agent must not select the most recent, most active, or any heuristic-based record. Selecting the wrong customer risks privacy violations by exposing one customer's data to another and incorrect actions such as processing a refund on the wrong account. The only safe response is to ask for clarification.

Calibration in the system prompt and structured handoff

The most effective first fix is to add explicit escalation criteria with few-shot examples to the system prompt. Examples demonstrate when to escalate for explicit request, policy gap, and inability to progress, when to resolve autonomously for straightforward frustrated cases, and the exact format of the escalation. Prompt optimisation should always precede architectural changes such as classifier models. A classifier is an addition only when prompt engineering has plateaued.

Escalation is implemented at the application layer. The system prompt carries the criteria and few-shot examples, the runtime detects triggers and either invokes an escalation tool or routes to a human queue. A common tool definition is escalate_to_human with required fields for customerId, issueSummary, attemptedActions, recommendedAction, and urgency, which makes the handoff machine readable for downstream routing. When the application detects an explicit request or a flagged policy gap it can force the structured handoff with tool_choice type tool and name escalate_to_human rather than relying on free-form text.

Logs, tiebreakers, and what inability really means

Every escalation event should log the trigger that fired, the customer identifier, the issue summary, the attempted actions and their outcomes, the recommended next action, and a timestamp. This structure is what makes the escalation useful to the human reviewer who can pick up without re-asking the customer for context, which reduces average handle time per case.

When two automated components disagree about whether to escalate, one flags a gap while another classifies the same case as a violation, the escalation policy needs a tiebreaker such as priority rules, timestamp ordering, or default to escalate. Without a tiebreaker the disagreement produces inconsistent handling across similar cases.

Inability to progress means concrete failed attempts, such as tool errors that retry cannot resolve, missing system access, or a confirmed technical bug. Subjective difficulty is the agent guessing without trying. Only the first justifies the inability trigger, which is why the exam tests whether escalation is grounded in failed attempts rather than in a feeling of difficulty.

Authoritative mechanism reference

The sections below expand the tested rules into the complete documented surface: ownership, lifecycle, version differences, production boundaries, and adjacent mechanisms.

Mechanism reference: 1. Escalation criteria as written prompt policy

Escalation policy for a support agent is written policy first and code second. The prompt holds the decision rule the model follows, the tool schema holds the shape of the handoff, and application code holds the non-bypassable gates. None of these layers substitutes for the others, which is why the exam treats prompt criteria plus schema enforcement plus hook enforcement as complementary rather than competing answers.

The smallest correct escalation criteria block names three triggers and two anti-patterns explicitly. It states when to escalate and when to resolve, and it states what not to use. The structure borrows from the RACCE framework in, where Constraints are testable, few enough to follow, and followed by Examples placed last for maximum influence.

The critical properties are testability and enumerability. A constraint such as "never escalate on sentiment or self-reported confidence" can be verified from output. A constraint such as "be conservative about escalation" cannot be verified and therefore does not calibrate. The escalation-patterns lesson is explicit that thresholds without semantics do not replace analysis, and the forensics Rule 22 records vague directives like "be conservative" as a consistently failed fix.

The three triggers, as tested, are:

  • Explicit request for a human. The user names a person as the desired handler. Phrases vary but contain "human," "person," "real person," or "manager" as the target. This trigger is absolute and immediate. The agent does not investigate first and does not ask for confirmation.
  • Policy gap. The governing rules are silent or internally ambiguous on the specific request. The agent has retrieved the relevant policy and confirmed the silence. A gap is not a violation. A violation has a documented answer and is handled by applying that answer, not by escalating.
  • Inability to make meaningful progress after a genuine attempt. Tools have been called, data gathered, and a wall remains: conflicting information that no tool resolves, missing access, or a technical fault. "Might not be able to handle this" without trying is not this trigger.

Two signals are explicitly excluded as triggers and appear as distractors in every miscalibration item:

  • Sentiment or frustration detection. See the dedicated subsection below.
  • Self-reported confidence scores. See the dedicated subsection below and the Task 5.5 distinction.

Few-shot examples attached to these criteria are not enumerated case lists. They demonstrate generalizable boundary reasoning so the agent handles unseen gaps. The forensics Rule 21 records that an agent correctly escalating an unseen loyalty-program dispute after two boundary examples proves the examples taught the principle. The system-prompt-design lesson states that 2 to 4 input and output pairs showing expected interaction patterns produce the most reliable behavior, and that examples belong at the end of the prompt, closest to the user input.

Example 1 below shows the explicit escalation criteria with worked examples for each trigger. It is the first of the six required substantial examples and it proves the testability requirement: every rule is verifiable and the examples sit last.

output.txt
text
You are a customer support agent for Nimbus Retail. Your job is to resolve
issues within documented policy or escalate to a human when one of three
triggers applies. You must not escalate on sentiment, frustration keywords,
or self-reported confidence.

ROLE
You are a careful support agent who retrieves policy before deciding and never
invents policy by analogy.

ESCALATION CRITERIA - escalate when exactly one of these holds:

1. Explicit human request: the customer names a human as the handler
   ("human," "person," "real person," "agent," "manager," "someone on your team").
   Override any capability assessment. Do not ask for confirmation, do not
   investigate first. Call escalate_to_human immediately and include whatever
   context you already hold.

2. Policy gap: the governing policy is silent or internally ambiguous on this
   specific request after you have retrieved the relevant policy text.
   Distinguish gaps from violations. A violation has a documented answer
   (for example, "return window is 30 days, request is at day 45" means deny).
   A gap has no answer and must escalate. Being within a dollar limit does not
   grant policy authority in a gap.

3. Inability to make meaningful progress after a genuine attempt: you have
   called the available tools, gathered the relevant data, retried a transient
   failure once where isRetryable is true, and still cannot advance because
   information conflicts, access is denied, or the system lacks the capability.
   "Might not be able to handle this" without trying is not this trigger.
   Retry exhaustion on a tool whose errorCategory is transient and whose
   isRetryable is true is this trigger once the retry budget is exhausted.

RESOLVE AUTONOMOUSLY when none of the above holds: standard billing
adjustment within the documented window, duplicate-charge refund via
process_refund, address update, order status, and other actions explicitly
covered by retrieved policy.

ANTI-PATTERNS - do NOT escalate based on:
- Sentiment, frustration language, punctuation ("!!!", "unacceptable"), emoji,
  or any tone proxy such as short replies.
- Self-reported confidence scores or a model-generated numeric certainty.

FEW-SHOT EXAMPLES

Example 1 - explicit human request overrides capability
User: "I want to speak to a real person NOW. My order 88921 was charged twice
and I have been waiting three weeks."
Thought: explicit human request is present, so escalation is mandatory
regardless of the fact that I can resolve duplicate charges in one tool call.
Action: escalate_to_human with customer_id, root cause as duplicate charge on
order 88921, and note that resolution via process_refund is available for the
human to apply.

Example 2 - frustrated but resolvable issue stays with the agent
User: "This is RIDICULOUS, nothing ever works! My order 77231 shows a duplicate
charge of $47.20."
Policy retrieved: duplicate charges are refundable within 60 days, no manager
approval under $500.
Thought: frustration is present but the issue is within policy and within
capability. No explicit human request and no policy gap. Resolve autonomously
after acknowledging.
Action: "I understand this is frustrating. I can process the $47.20 refund for
order 77231 right now." Then call process_refund with the verified order ID.

Example 3 - policy gap caused by silence on competitor price matching
User: "Another store has this laptop $120 cheaper. Will you match it?"
Policy retrieved: price adjustments cover same-site drops within 14 days with
original receipt, silent on competitor matches.
Thought: this is not a violation with a documented denial, it is a gap because
the policy does not address competitor pricing. Being under the $500 limit does
not grant authority to decide. Escalate with the identified silent clause.
Action: escalate_to_human naming the silent competitor-match clause, with the
customer ID, the $120 amount, and recommended action for the human to decide.

Example 4 - inability to progress after genuine attempt
User: "My invoice says $2,410 but I was quoted $1,890. Which is correct?"
Actions taken: called get_invoice for two invoice IDs, results conflict, called
lookup_order, still irreconcilable, retried once on transient timeout.
Thought: genuine attempt exhausted, wall remains due to conflicting source data
no tool resolves. Escalate with the conflict.
Action: escalate_to_human describing both invoice IDs, both amounts, the
attempted lookups, and that no tool can reconcile the discrepancy.

This block proves three things the exam tests. First, the three triggers are named operationally so a grader can verify mapping. Second, the anti-patterns are explicitly prohibited so sentiment and confidence cannot be substituted. Third, the examples sit last and demonstrate boundary reasoning, not just labels, which is how the agent generalises to unseen gaps.

Mechanism reference: 2. Tool definition and input schema for the escalation tool

The escalation handoff is only as reliable as its schema. If completeness is requested in prose but not required in the contract, the model can omit fields and still produce a syntactically valid call. The correct fix is to make completeness structurally enforced at the interface.

A tool definition is an object with name, description, and input_schema passed in the tools array of a Messages request. The name is the snake_case identifier the model must produce. The description controls selection accuracy and must state what the tool does, when to use it, and what result shape to expect. The input_schema is a JSON Schema object that constrains what the model can emit as input. Every property in properties should carry its own description with format guidance, and required must list every field without which the handoff is not actionable.

The documented input schema rules relevant to handoff enforcement are:

  • type must be "object" at the top level for the handoff payload. Properties carry their own type values (string, number, integer, boolean, array, object) matched exactly to what the backend expects.
  • enum constrains fixed-value parameters. For a field like escalation_reason or urgency the schema must enumerate every valid value so the model cannot invent one.
  • required is an array of keys that must be present. Only fields listed there are enforced. If customer_id is not in required, the model can emit a call without it and the call will validate.
  • description on each property guides parameter inference. Without it the model guesses format, for example whether refund_amount is a number or a formatted string.
  • strict as a top-level tool property, when true, constrains token sampling to schema-valid output via grammar-constrained sampling, guaranteeing the input matches input_schema and the name is one of the provided tools.
  • additionalProperties controls whether extra keys are allowed. For a handoff that must be consumed by a human queue, setting it to false prevents drift into free-form keys that downstream parsing ignores.

The distinction between success and failure in tool results also shapes escalation. The Messages API uses a tool_result block with is_error to signal failure. A structured error object with isError, errorCategory, and isRetryable lets the agent reason about retry versus escalate rather than treating every empty array as an error. This matters for exhaustion detection, because a transient error with isRetryable: true retries, while an isRetryable: false permission or validation error escalates without wasted retries.

Example 2 shows an escalation tool whose schema forces a complete self-contained handoff. It is the second required example.

example.ts
typescript
import Anthropic from "@anthropic-ai/sdk";

const escalateToHumanTool = {
  name: "escalate_to_human",
  description:
    "Escalate the current support case to a human agent. Use when the " +
    "customer explicitly requests a human, the request falls in a policy gap " +
    "where governing policy is silent or ambiguous, or you cannot make " +
    "meaningful progress after a genuine attempt including one retry of any " +
    "isRetryable transient error. The human has no transcript access and must " +
    "be able to act from this payload alone. Do not use for sentiment, " +
    "frustration, or self-reported confidence. Returns a handoff receipt with " +
    "queue assignment.",
  strict: true,
  input_schema: {
    type: "object",
    properties: {
      customer_id: {
        type: "string",
        description: "Verified customer identifier, for example CUST-4421 or USR-48721. Must be the record the customer confirmed, not a heuristically selected match.",
      },
      escalation_reason: {
        type: "string",
        enum: [
          "explicit_human_request",
          "policy_gap",
          "inability_to_progress",
        ],
        description: "One of the three valid triggers. Use explicit_human_request when the user names a human, policy_gap when governing policy is silent or ambiguous on this request, inability_to_progress after genuine attempt and retry exhaustion.",
      },
      root_cause: {
        type: "string",
        description: "One sentence stating the underlying issue in plain language, for example 'Duplicate charge of $847.00 on order 88921 due to gateway retry'.",
      },
      relevant_ids: {
        type: "object",
        properties: {
          order_id: { type: "string", description: "Primary order or invoice ID relevant to the case, if any." },
          invoice_ids: {
            type: "array",
            items: { type: "string" },
            description: "Additional invoice or order IDs when conflicting records exist, otherwise omit.",
          },
        },
        required: ["order_id"],
        description: "Identifiers the human needs to locate the case without the transcript.",
        additionalProperties: false,
      },
      amounts: {
        type: "object",
        properties: {
          disputed_amount: { type: "number", description: "Monetary amount in dispute as a number, for example 847.00. Omit when not monetary." },
          currency: { type: "string", enum: ["USD", "EUR", "GBP"], description: "ISO currency code when amounts is present." },
        },
        required: [],
        description: "Monetary context, omitted for non-monetary cases. Never invent a value.",
        additionalProperties: false,
      },
      attempted_actions: {
        type: "array",
        items: { type: "string" },
        description: "Ordered list of actions already taken, for example 'lookup_order 88921 succeeded', 'process_refund dry-run blocked by policy check'. Include one retry attempt for any transient error where isRetryable was true.",
      },
      blocking_reason: {
        type: "string",
        description: "Why autonomous resolution cannot proceed. For policy_gap name the silent clause, for inability_to_progress name the irreconcilable data or missing capability.",
      },
      recommended_action: {
        type: "string",
        description: "Concrete next step for the human, for example 'Approve $847 refund to original payment method; customer not at fault; gateway retry caused duplication'.",
      },
      conversation_summary: {
        type: "string",
        description: "Three sentence narrative arc of the conversation, not a transcript dump or turn reference. What the customer asked, what the agent found, why this handoff is needed.",
      },
      urgency: {
        type: "string",
        enum: ["low", "normal", "high", "critical"],
        description: "Queue priority. Use high when an explicit request is paired with reiterated frustration. Does not drive the escalation decision itself.",
      },
    },
    required: [
      "customer_id",
      "escalation_reason",
      "root_cause",
      "relevant_ids",
      "attempted_actions",
      "blocking_reason",
      "recommended_action",
      "conversation_summary",
    ],
    additionalProperties: false,
  },
} as const;

const client = new Anthropic();
const response = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  tools: [escalateToHumanTool],
  tool_choice: { type: "auto" },
  system: "You are Nimbus Retail support. Follow the escalation criteria exactly.",
  messages: [{ role: "user", content: "My order 88921 was charged twice." }],
});

Every required field maps to a load-bearing piece of the human task: customer_id prevents re-identification, escalation_reason with enum makes the trigger auditable, root_cause plus blocking_reason explains why autonomy stopped, attempted_actions prevents duplicate work, recommended_action makes the handoff productive, and conversation_summary replaces a transcript the human cannot open. The enum on escalation_reason and urgency is not decorative; without it the model can emit values the queue does not route. The additionalProperties: false prevents drift into reference IDs or turn pointers that look helpful but shift work back onto the human.

Mechanism reference: 3. The full tool_choice value space and what forcing a specific tool does

The tool_choice parameter controls whether the model may act, must act, or must act in a specific way on this turn. The platform defines four values. Only two of them receive most exam attention, which is why grounding must present all four and the interaction with stop_reason.

  • {"type": "auto"} lets the model decide whether to call a tool or respond with text. This is the default. With auto the response stop_reason can be either "tool_use" or "end_turn", and the agent loop must handle both. This is the correct default for a general-purpose support agent where most turns are conversational and only some need a tool.
  • {"type": "any"} requires a tool call on this turn. The model must select one of the available tools and produce a tool_use block. The stop_reason is always "tool_use" and never "end_turn", so a loop using any must enforce a maximum turn budget or it runs forever. With any the model chooses which tool, so it can still select the wrong tool when none is appropriate. For that reason any is best served by a single well-designed tool whose enum covers all outcomes.
  • {"type": "tool", "name": "<tool_name>"} forces exactly that tool. The model has no freedom to answer with text or to select a different tool. The stop_reason is always "tool_use" for that specific tool. This is the form that makes a handoff unskippable.
  • {"type": "none"} suppresses all tools. The model must respond with text only and stop_reason is always "end_turn". This is used for greeting turns, confirmation asks before an irreversible operation, and error explanations where a retry would repeat the failure.

The exam favours auto and the specific-tool form, but all four belong in the answer and all four appear in production patterns. The critical exam traps are forgetting that any never produces end_turn so a loop budget is mandatory, and assuming auto will always call the desired tool when the tool is merely relevant.

A deliberate escalation design therefore pairs two fields. The prompt holds the when, the forced tool choice holds the guarantee that when the condition matches the handoff fires. Example 3 shows the pattern that makes the handoff unskippable after detection.

example.ts
typescript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const tools = [
  {
    name: "escalate_to_human",
    description: "Escalate to a human with a complete self-contained handoff.",
    strict: true,
    input_schema: {
      type: "object",
      properties: {
        customer_id: { type: "string", description: "Verified customer ID" },
        escalation_reason: {
          type: "string",
          enum: ["explicit_human_request", "policy_gap", "inability_to_progress"],
          description: "One of the three valid triggers",
        },
        root_cause: { type: "string", description: "Plain-language cause" },
        relevant_ids: {
          type: "object",
          properties: { order_id: { type: "string" } },
          required: ["order_id"],
          additionalProperties: false,
        },
        attempted_actions: { type: "array", items: { type: "string" } },
        blocking_reason: { type: "string" },
        recommended_action: { type: "string" },
        conversation_summary: { type: "string" },
      },
      required: [
        "customer_id",
        "escalation_reason",
        "root_cause",
        "relevant_ids",
        "attempted_actions",
        "blocking_reason",
        "recommended_action",
        "conversation_summary",
      ],
      additionalProperties: false,
    },
  },
  {
    name: "process_refund",
    description: "Process a refund for order_id after policy checks.",
    input_schema: {
      type: "object",
      properties: {
        order_id: { type: "string" },
        amount: { type: "number" },
        reason: { type: "string" },
      },
      required: ["order_id", "amount", "reason"],
    },
  },
];

// Step 1: the agent loop detects an explicit human request via a cheap
// classifier or keyword rule that matches "human, person, real person, manager".
// That detection is application code, not a model confidence judgement.
// Once matched, the next model turn is forced to the handoff tool so the
// escalate path cannot be talked past or soft-pedalled.

// If detection is true, force the escalation tool on the very next turn:
const handoffResponse = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  tools,
  // This is the documented form that forces a specific tool. The model has no
  // freedom to reply with text or to pick process_refund instead.
  tool_choice: { type: "tool", name: "escalate_to_human" },
  system:
    "You are Nimbus Retail support. Honour explicit human requests immediately. " +
    "Emit a complete handoff with no missing required fields.",
  messages: [
    { role: "user", content: "I want to speak to a human now, stop troubleshooting. Order 88921." },
  ],
});

// handoffResponse.stop_reason is always "tool_use" and the tool_use block
// name is always escalate_to_human when this form is used.
// An application gate around this call enforces the required fields even when
// strict is true, so the first parse failure is surfaced to the model as a
// tool_result with is_error, not silently swallowed.
console.log(handoffResponse.stop_reason); // "tool_use"

// Contrast: {"type": "any"} would also force a tool call but would let the
// model choose between escalate_to_human and process_refund, so the handoff
// could be skipped in favour of an autonomous refund. {"type": "auto"} would
// also let the model skip the handoff entirely by answering with text. Only
// {"type": "tool", "name": "escalate_to_human"} removes both degrees of
// freedom on the turn where escalation is required
//.

What forcing does to model freedom is the point. With auto the model retains two freedoms: whether to act and which tool to use. With any it retains one freedom: which tool to use. With type: tool it retains zero freedoms on tool selection for that turn. That is the guarantee a "cannot be left to model discretion" requirement needs.

There is a cost. Forcing suppresses leading text. When tool_choice is any or a specific tool name the API prefills the assistant turn to guarantee the tool call, so the model does not emit a natural-language explanation before the tool_use block, even if the prompt asks it to explain first. For most handoffs this is acceptable because the handoff payload plus a short post-handoff confirmation message to the user are sufficient. When visible reasoning before the call is required, use extended thinking or switch to auto with a strong nudge rather than forcing.

Mechanism reference: 4. Ambiguity handling and the disambiguation gate

Multiple matches for a lookup are a privacy and correctness boundary, not a ranking problem. When a tool such as lookup_customer or get_customer returns more than one record for a single query, the agent has no reliable attribute that identifies which record belongs to the person in the conversation. Any heuristic that selects the most recent, most active, highest balance, highest order value, oldest creation date, first alphabetical, most complete profile, or the non-secondary household member is still a guess. The forensics Rules 17 and 18 record that every heuristic variant appears as a distractor and every one is rejected, including plausible refinements like household-secondary exclusion.

The only correct response is a deterministic gate that asks for an additional identifier and does not proceed until the match narrows to one. The gate returns a clarification request rather than a choice. This is not escalation to a human; it is an autonomous clarification that the agent can resolve with one question. Blanket escalation of every multi-match to a human is over-escalation that adds about eighteen percent to volume for simple status questions the agent could have handled.

The identifiers the gate requests must be things the customer can provide and that narrow deterministically: email, phone, order number, account number, billing zip, or date of birth. The prompt should surface the candidates with masked hints so the customer can recognise which record is theirs, but the agent must not select among them. The gate also handles the zero-match case correctly. Zero matches is a valid empty result, not a failure to retry or escalate, and should be reported as "no record found" with a suggestion to check spelling.

Example 4 implements the gate. It is the fourth required example.

example.ts
typescript
type CustomerRecord = {
  id: string;
  name: string;
  maskedEmail: string;
  maskedPhone: string;
  createdAt: string;
  lastActiveAt: string;
};

type LookupResult =
  | { type: "success"; records: CustomerRecord[] }
  | { type: "error"; isError: true; errorCategory: "transient" | "permanent" | "auth"; isRetryable: boolean; message: string };

type NextAction =
  | { action: "proceed"; customerId: string }
  | { action: "report_no_match"; message: string }
  | {
      action: "request_disambiguation";
      prompt: string;
      candidates: { id: string; hint: string }[];
    }
  | { action: "retry_or_escalate"; reason: string };

function handleCustomerLookup(result: LookupResult): NextAction {
  if (result.type === "error") {
    // Access failure versus empty result must be separated before any
    // disambiguation logic. Retrying a permission error wastes budget;
    // retrying a transient error once is correct before escalating.
    if (result.isRetryable) {
      return { action: "retry_or_escalate", reason: result.message };
    }
    return { action: "retry_or_escalate", reason: `Lookup failed: ${result.message}` };
  }

  const matches = result.records;

  if (matches.length === 0) {
    return {
      action: "report_no_match",
      message:
        "I could not find a record matching that search. Could you confirm the " +
        "spelling of the name or provide an email or order number so I can check again?",
    };
  }

  if (matches.length === 1) {
    return { action: "proceed", customerId: matches[0].id };
  }

  // Multiple matches: do not rank, do not select, do not escalate to a human.
  // Ask for an additional identifier that narrows deterministically.
  return {
    action: "request_disambiguation",
    prompt:
      "I found more than one record for that name. Could you confirm your " +
      "email address, phone number, or a recent order number so I can locate " +
      "the correct account without guessing?",
    candidates: matches.map((m) => ({
      id: m.id,
      // Hint is masked to preserve privacy while letting the customer recognise
      // their own record. The agent surfaces hints but does not act on them.
      hint: `${m.maskedEmail} ยท ${m.maskedPhone}`,
    })),
  };
}

// Agent-loop usage sketch
async function resolveCustomerQuery(query: string) {
  const raw: LookupResult = await lookupCustomer(query);

  const decision = handleCustomerLookup(raw);

  if (decision.action === "request_disambiguation") {
    // Return a clarification message to the user, not a tool call that picks a record.
    // No heuristic such as mostRecent or highestValue is applied here.
    return {
      role: "assistant" as const,
      content: decision.prompt,
      disambiguationCandidates: decision.candidates,
    };
  }

  if (decision.action === "proceed") {
    // Exactly one verified ID. Safe to call the next tool with that ID.
    return processOrderStatus({ customerId: decision.customerId });
  }

  if (decision.action === "report_no_match") {
    return { role: "assistant" as const, content: decision.message };
  }

  // retry_or_escalate is handled by the outer error policy, not by guessing.
  return handleErrorPolicy(decision.reason);
}

What this proves is the privacy boundary. The gate never reads lastActiveAt, createdAt, balance, or order value as a selector, and it never threads a "best guess" into the next tool. The only path to proceed is a single match after the customer supplies an additional identifier. That is why the exam marks any heuristic selection as wrong even when the heuristic would have been right on this specific call.

Mechanism reference: 5. Typed decision function whose branches are the triggers

A second safeguard against the unreliable signals is type enforcement: the decision function itself only accepts the valid triggers. If sentiment or self-reported confidence cannot be represented in its input type, it cannot enter the decision at all. This is the correct reading of the forensics prompt to build a function whose branches map one to one onto the valid triggers.

The function below takes a classified request that has already been through policy retrieval and tool attempt tracking. It reads a policyCoverage value that comes from application code inspecting the retrieved policy text for silence versus prohibition, an explicitRequest flag that comes from an explicit-phrase match on the user message, and a progress record that comes from tool result typing. None of its inputs are sentiment scores or confidence numbers. That exclusion is deliberate and is what makes the type signature itself a guardrail.

Example 5 is the required typed decision function.

example.ts
typescript
// Inputs are derived from application code, not from model self-report.
// No branch reads sentiment or a model-generated confidence field.
type PolicyCoverage = "covered" | "prohibited" | "silent_or_ambiguous";
type ExplicitRequest = boolean;

type ProgressState =
  | { status: "not_yet_attempted" }
  | { status: "in_progress"; attempts: string[]; lastError?: { errorCategory: string; isRetryable: boolean } }
  | { status: "blocked"; attempts: string[]; blockingReason: string; retryBudgetExhausted: boolean };

type DecisionInput = {
  explicitRequest: ExplicitRequest;
  policyCoverage: PolicyCoverage;
  progress: ProgressState;
  amountWithinLimit: boolean;
  verifiedCustomerId: string | null;
};

type Trigger = "explicit_human_request" | "policy_gap" | "inability_to_progress";
type Decision =
  | { action: "escalate"; trigger: Trigger; reason: string }
  | { action: "resolve"; reason: string }
  | { action: "clarify"; reason: string; prompt: string }
  | { action: "deny_with_citation"; reason: string; citation: string };

function decide(input: DecisionInput): Decision {
  // Rule 1 mapped directly: explicit request is tested first and overrides
  // every capability or dollar-limit assessment. This ordering is load-bearing.
  if (input.explicitRequest) {
    return {
      action: "escalate",
      trigger: "explicit_human_request",
      reason: "Customer explicitly requested a human; honour immediately with no investigation gate.",
    };
  }

  // Rule 6 mapped directly: a gap is silence or ambiguity, not a violation.
  // A violation denies autonomously with a citation to the governing clause.
  if (input.policyCoverage === "silent_or_ambiguous") {
    return {
      action: "escalate",
      trigger: "policy_gap",
      reason: "Governing policy is silent or internally ambiguous on this request after retrieval; dollar limit does not grant interpretive authority.",
    };
  }

  if (input.policyCoverage === "prohibited") {
    return {
      action: "deny_with_citation",
      reason: "Request is explicitly prohibited by retrieved policy; apply the documented answer without escalation.",
      citation: "Policy section retrieved in this turn, quote verbatim, not by analogy.",
    };
  }

  // Rule 9 and Rule 10 mapped directly: genuine attempt plus blocking reason
  // or retry-budget exhaustion on an isRetryable transient. A vague
  // "feels complex" state is not represented in this type, so it cannot trigger.
  if (input.progress.status === "blocked") {
    if (input.progress.retryBudgetExhausted || input.progress.blockingReason) {
      return {
        action: "escalate",
        trigger: "inability_to_progress",
        reason: `Cannot advance after genuine attempt: ${input.progress.blockingReason}`,
      };
    }
  }

  // No valid trigger is active and policy covers the request. Resolve
  // autonomously when a verified ID is present; otherwise ask for one.
  if (input.verifiedCustomerId) {
    return {
      action: "resolve",
      reason: "No valid escalation trigger is active and a verified customer ID is present; resolve within documented policy.",
    };
  }

  return {
    action: "clarify",
    reason: "No valid trigger active and customer identity is not yet verified.",
    prompt:
      "Could you confirm your email or order number so I can locate the correct " +
      "account and proceed with your request?",
  };
}

// Example evaluations that exercise each branch without any sentiment signal

// 1. Explicit request short-circuits everything else, even a simple refund.
//    decide({ explicitRequest: true, policyCoverage: "covered", progress: { status: "not_yet_attempted" },
//             amountWithinLimit: true, verifiedCustomerId: "CUST-4421" })
//    -> { action: "escalate", trigger: "explicit_human_request" }

// 2. Silent competitor-match policy is a gap, even though amount is under $500.
//    decide({ explicitRequest: false, policyCoverage: "silent_or_ambiguous",
//             progress: { status: "not_yet_attempted" }, amountWithinLimit: true, verifiedCustomerId: "CUST-4421" })
//    -> { action: "escalate", trigger: "policy_gap" }

// 3. Retry budget exhausted on a transient error after a genuine attempt.
//    decide({ explicitRequest: false, policyCoverage: "covered",
//             progress: { status: "blocked", attempts: ["lookup_order 88921", "lookup_order 88921 retry 1"], blockingReason: "Gateway timeout after one isRetryable retry", retryBudgetExhausted: true },
//             amountWithinLimit: true, verifiedCustomerId: "CUST-4421" })
//    -> { action: "escalate", trigger: "inability_to_progress" }

// Note what is absent from DecisionInput: no sentimentScore, no urgencyScore,
// no confidence field, no tone label. That absence is the anti-pattern fix.
// An earlier version of this function that accepted a confidence number would
// have been rejected on review because confidence cannot carry the decision.
// The Task 5.5 design, where a calibrated score is used to prioritise a review
// queue after classification, belongs outside this function.

The shape of this function is why the confidence tension resolves without contradiction. Inside the decision to escalate, confidence has no branch. Outside the decision, in queue prioritisation, a calibrated score can order human attention. That placement matches the escalation-patterns guarantee that every escalation maps to one of the three triggers and the confidence-scoring clarification that objective signals belong in the agent's internal decision process, not the model's subjective report.

Mechanism reference: 6. The confidence tension in full

The peer forensics file flags an apparent contradiction and records an inferred reconciliation that must be made precise. Several items mark self-reported confidence as an unreliable escalation trigger, while other items describe confidence-based routing in a tiered review system as correct, including one that says a subagent's confidence field should be used as a routing signal. Both statements are true once two distinctions are drawn.

First distinction: deciding to escalate versus prioritising a queue. The three-trigger test is the decision to escalate. No score, sentiment, keyword count, emoji count, or turn count may serve as that decision, because none of them is externally verifiable in the way "customer said the word human" or "policy text is silent on this request" or "tool returned isRetryable true and the retry failed" is verifiable. A calibrated score fitted on labelled outcomes and validated out of sample is a legitimate input to prioritising which human-reviewed cases are handled first, which is the subject of Task 5.5. The product-level discussion in confidence-scoring makes the parallel point: a raw score from asking the model how it feels is unreliable, while self-consistency agreement, hedging detection, and other objective signals can inform internal routing when measured outside the model.

Second distinction: self-reported versus calibrated. A self-reported certainty emitted by the model in a chat ("I am 9 out of 10 confident") is uncalibrated and therefore not a valid trigger for handing a case to a human in the support setting. A calibrated score produced by a classifier or ranker that is trained on labelled outcomes, thresholded on a held-out set, and monitored for drift, is a validated routing input. The strongest illustration is the extraction and moderation settings the forensics cite, where high-confidence safe content passes, high-confidence violations act, and uncertain cases route to human review. That routing is tiered because it sits after classification, not as the sole trigger for leaving the automated tier.

The practical consequence is that no production support agent should ask the model to rate its confidence and escalate below a number. The correct internal pattern, when a confidence signal is needed at all, is to run an objective check outside the model and act on its measurement. The confidence-scoring lesson lists those checks: self-consistency across samples, hedging detection on the generated text, and structured confidence fields on a tool whose schema constrains them, with the understanding that even structured fields remain only low to medium reliability and must not be the sole gate. The Messages API does not expose per-token log probabilities as a request parameter, so a design that depends on a logprobs or top_logprobs parameter for Claude is incorrect.

For the writer, the safe phrasing is: self-reported confidence is not a valid trigger for escalating to a human in this task, but a calibrated score fitted on labelled outcomes is a legitimate routing input for prioritising human review, which Task 5.5 covers. The distinction is between deciding to escalate and prioritising a queue.

Mechanism reference: 7. What documentation does and does not say about sentiment

No platform page in the verified list states that frustration correlates or does not correlate with case complexity, nor that sentiment is or is not a valid escalation trigger. The reliable sources for that claim are the lessons and the forensics synthesis, which together show that the exam consistently rejects sentiment, aggregate sentiment, and tone proxies such as punctuation and emoji. The construction the exam favours is pairing a furious tone with a simple within-policy issue that must be resolved, and a calm tone with a silent policy gap that must escalate, which proves the lack of correlation as tested judgement.

The one documented nuance about sentiment the writer can cite with a platform-adjacent source is that tone adaptation is legitimate while tone-driven escalation is not. The forensics file records that get_customer_sentiment_history or similar aggregate sentiment may be used to adapt tone but must not drive the escalation decision. That distinction lets the agent say "I understand this is frustrating" while still resolving autonomously, without conflating empathy with routing.

Where the exam is more prescriptive than any documented guidance, mark it so the writer presents it as tested judgement rather than documented rule. Items in this category for Task 5.2 are:

  • The three-trigger canonical set and the labelling of any fourth trigger as incorrect.
  • The absolute, no-investigation ordering for explicit human requests.
  • The gap versus violation taxonomy and the rule that being within a financial limit does not grant policy authority.
  • The prohibition on any heuristic for ambiguous matches and the prescribed clarification that follows.
  • The requirement that every handoff be self-contained and that transcript references are unacceptable.
  • The ranking of an explicit-criteria-plus-few-shot prompt edit above classifier deployment as the proportionate first response to miscalibration.

Each of these is strongly supported by lessons and forensics but is not a quoted product rule. Cite lessons and forensics, not a platform page, and mark the claim as tested judgement.

Mechanism reference: 8. Why an irreversible action needs a deterministic gate in application code

The guardrails lesson divides guardrails into prompt-based (probabilistic) and hook-based (deterministic) and states that safety-critical and irreversible actions must be enforced in code, not just in the system prompt. The table there is explicit: reliability for prompt-based topic restriction is medium because the model may override, while hook-based enforcement is high because code enforces it. The escalation-patterns lesson reinforces the pattern for the same reason: thresholds without semantics waste human time or escalate too late, and the correct response class determines the action.

The exam signal phrase that selects the hook answer is "cannot be left to model discretion." The forensics Rule 27 records that prompt emphasis, even with "CRITICAL, NEVER," still shows a nonzero failure rate in the items, while a hook that blocks the tool call and invokes escalation in the tool-calling layer yields zero failure by construction. The correct fix for a refund over $500 or a bulk cancel of hundreds of orders is not a louder prompt, it is an interceptor that sees the tool call, checks the condition in application code, blocks the execution, and routes to the handoff tool with the condition attached.

The documented surfaces for that enforcement are not in the escalation reference itself but are documented in the Claude Code hook and permission surfaces that the lessons reference. The hooks page documents the hook layer for tool-call interception, and the broader tool-use documentation describes the request structure the interceptor sees. The guardrails lesson names PreToolUse as the hook that can block actions and provides the pattern for a layered pipeline where deterministic checks run before the model and post-generation checks run after it. The authorization lesson content in the forensics adds the complement: access control is a service-side check on a scoped credential, not a model judgement, so a tool that would execute a refund or a bulk cancel must validate the authenticated actor's authority before it runs, independent of what the model believed.

The argument to present is therefore tiered: for a judgement call such as whether a request is a gap or a violation, prompt criteria plus few-shot examples are the proportionate fix. For a hard compliance boundary such as an amount limit or an irreversible production action, the fix is a deterministic gate in application code, implemented as a hook or service-side policy check, with the prompt as a supporting description rather than the enforcement point. A related hard boundary is irreversible high-impact operations that require explicit human confirmation even when authorised by a pipeline. The forensics Rule 28 records that pipeline authorization and dry-run are not awareness, so the minimal-footprint step is to surface scope and pause for confirmation before acting.

The code shape for the deterministic gate is deliberately small because it is not model reasoning. It inspects the proposed tool call, compares it against a declared policy, and either allows the call or replaces it with the escalation handoff. The prompt describes the policy, the hook enforces it, and the audit log records it so drift can be detected.

example.ts
typescript
import type { ToolCall, ToolResult } from "./agent-types";

// Deterministic compliance gate for refunds. This is not a prompt trick.
// It runs in application code around the tool-calling layer. The model may be
// persuaded or confused but it cannot bypass a check the model does not control.

const REFUND_HARD_LIMIT = 500;
const BULK_CANCEL_THRESHOLD_ORDERS = 100;

function preToolUseGate(call: ToolCall): { allow: true } | { allow: false; replacementResult: ToolResult } {
  if (call.name === "process_refund") {
    const amount = call.input.amount as number;
    const orderId = call.input.order_id as string;

    if (amount > REFUND_HARD_LIMIT) {
      return {
        allow: false,
        replacementResult: {
          type: "tool_result",
          tool_use_id: call.id,
          is_error: true,
          content: JSON.stringify({
            isError: true,
            errorCategory: "permanent",
            isRetryable: false,
            message: `Refund of ${amount.toFixed(2)} exceeds the ${REFUND_HARD_LIMIT} hard limit and must be handled by a human.`,
            context: { orderId, amount, gate: "pre_tool_use", policy: "refund_hard_limit" },
          }),
        },
      };
    }
  }

  if (call.name === "bulk_cancel_orders") {
    const orderIds = call.input.order_ids as string[];
    if (orderIds.length >= BULK_CANCEL_THRESHOLD_ORDERS) {
      return {
        allow: false,
        replacementResult: {
          type: "tool_result",
          tool_use_id: call.id,
          is_error: true,
          content: JSON.stringify({
            isError: true,
            errorCategory: "permanent",
            isRetryable: false,
            message: `Bulk cancel of ${orderIds.length} orders is irreversible and requires explicit human confirmation before execution.`,
            context: { count: orderIds.length, gate: "pre_tool_use", policy: "bulk_cancel_confirmation" },
          }),
        },
      };
    }
  }

  return { allow: true };
}

// The agent loop consults the gate before executing any tool.
// When the gate blocks, the error payload is fed back to the model so its next
// turn must address the blockage. In the handoff design that next turn is
// forced to escalate_to_human via tool_choice: { type: "tool", name: "escalate_to_human" }
// so the human receives a complete handoff, not an unsent transcript.

async function executeWithGate(call: ToolCall) {
  const gate = preToolUseGate(call);
  if (!gate.allow) {
    // No tool executes. The model sees the is_error result and is expected to
    // emit the structured escalation on its next forced turn.
    return gate.replacementResult;
  }
  return realToolExecute(call);
}

// Service-side authorization is the second half of the same principle.
// Even when the model is correctly constrained, the tool itself validates
// the authenticated actor. A scoped credential that can read but not refund
// cannot be elevated by a confident model or a stronger prompt. The check is:
//   1. Propagate the authenticated identity to the tool service.
//   2. Issue narrowly scoped credentials per capability (read, refund, cancel).
//   3. Enforce the scope check in the tool service before the operation.
// The lesson on error handling and the forensics
// Rules 29 and 30 record the same boundary: an empty
// array is not an isError failure, and authorization is deterministic rather
// than a model judgement. Confidence above a number and prompt examples of
// misuse are not access control and fail as such
//.

The same two-layer logic applies to irreversible actions that are not refunds. A pipeline-authorised production migration, a large wire transfer, or a bulk cancel is not executed when authorised. It is paused, scoped, and confirmed, then executed. The forensics Rule 28 is explicit that executing because the CI/CD trigger was authorised, or because a dry-run succeeded, is incorrect. The correct sequence is surface scope and financial impact, require explicit confirmation, then proceed.

Mechanism reference: 9. Structured handoff that is self-contained and the log entry that makes it observable

The handoff payload defined earlier is the contract the human acts on. The log entry is the durable record that makes the system auditable, searchable, and tuneable without replaying transcripts. The exam tests the first as Rule 23 and Rule 24 and tests the second implicitly as the remediation for a human who "lacks access to conversation transcript" and must act from the payload alone. The lesson defines what to include and what to exclude, and the error-handling lesson defines how to handle the raw error content that the log will carry.

The human queue needs a payload that replaces the transcript. The log needs an entry that replaces the vendor's anecdotal understanding of why cases reach humans. Both want the same fields: who requested, under what trigger, what was already tried, why autonomy stopped, and what the recommended next step is. The log wraps the handoff with timestamps, routing, and outcome linkage so weekly review can ask "which policy clause is silent most often" or "which retry budget is exhausted most often" and fix the source rather than the symptom.

Example 6 is the required structured escalation log entry a human can act on without reading the transcript.

result.json
json
{
  "event": "escalation",
  "timestamp": "2026-08-26T14:33:02.128Z",
  "request_id": "req_8f3a1b9c4e02",
  "session_id": "sess_22471a",
  "customer": {
    "customer_id": "CUST-4421",
    "verified": true,
    "verification_method": "email_plus_order_number",
    "masked_pii": {
      "email": "j***@example.com",
      "phone": "***-***-4812"
    }
  },
  "escalation": {
    "reason": "policy_gap",
    "trigger_phrase": null,
    "policy_clause": "Nimbus Retail Price Adjustments, section 4.2, same-site drops within 14 days with original receipt; silent on competitor-price matching",
    "root_cause": "Customer requests competitor price match of $120.00 on order 88921; governing policy neither permits nor prohibits competitor matches.",
    "blocking_reason": "Policy is silent on competitor matches after retrieval of the current Price Adjustments document. No analogy to the 14-day window is authorised. Dollar limit $500 is not a substitute for policy coverage.",
    "amounts": {
      "disputed_amount": 120.0,
      "currency": "USD"
    },
    "attempted_actions": [
      "lookup_customer CUST-4421 succeeded",
      "lookup_order 88921 succeeded, item SKU-NB-773 price $649.00",
      "retrieve_policy Price_Adjustments_v42 succeeded, sections 4.1 through 4.5",
      "search_orders_summary for customer succeeded, no prior match for this SKU"
    ],
    "recommended_action": "Approve $120.00 adjustment to original payment method if business wishes to match competitor pricing, or deny with citation to section 4.2 and offer $15 goodwill credit per retention playbook. Record decision as precedent candidate for policy owner.",
    "conversation_summary": "Customer asked to match a competitor price on a laptop in order 88921. Agent verified customer and order, retrieved the Price Adjustments policy, confirmed the policy covers same-site drops only and is silent on competitor matches, and concluded the request is a policy gap that only a human can decide.",
    "handoff_tool": "escalate_to_human",
    "handoff_payload_valid": true
  },
  "routing": {
    "queue": "billing_policy_exceptions",
    "urgency": "normal",
    "sla_minutes": 240,
    "assigned_team": "billing_support_tier2"
  },
  "error_history": [],
  "human_action": {
    "status": "awaiting_human",
    "resolution": null,
    "resolved_at": null,
    "precedent_recorded": false
  },
  "observability": {
    "guardrail": "policy_gap_detected",
    "is_error_override": false,
    "tool_choice_on_escalation_turn": "tool",
    "forced_tool": "escalate_to_human"
  }
}

A human reading this entry can act without the transcript because every identifier, every amount, every clause, and the recommended resolution are inline. The queue can route without parsing a transcript because reason, queue, and urgency are enums or checked strings. An analyst reviewing the week can aggregate on policy_clause and reason and ask the policy owner to close the most frequent gap, which removes the next hundred identical escalations. The observability block records that the escalation turn was forced via tool choice, so a later check that a handoff was not skipped can be made by querying the log rather than by trusting a prompt to have been followed.

The raw transcript and the raw tool error that triggered an inability-to-progress case are still stored as linked detail, but they are not the interface the human works from. That matches the exam's consistent preference for structured synthesis over transcript dumping or transcript referencing.

Ownership map

Which layer owns which guarantee when a support agent faces an ambiguous or hard request. The table states the owner that can actually deliver the property, not the layer that merely describes it.

GuaranteeOwnerWhy this owner and how it is enforced
Naming when to escalate and when to resolve, including the two anti-patternssystem prompt plus few-shot examplesThe model follows written policy. The prompt holds the RACCE-structured criteria and the 2 to 4 examples sit last so they influence the next turn. No product endpoint publishes the three-trigger list; it is written policy.
Shaping the handoff so it is complete and typedtools[].input_schema on escalate_to_human, with required, enum, and strictA schema with required fields makes omission impossible. The description on each property tells the model the format, and strict guarantees shape. Prompt prose alone is soft.
Making the handoff unskippable after detectiontool_choice with {"type": "tool", "name": "escalate_to_human"} plus application-code detectionWith this form the model loses both freedoms of whether to act and which tool to use. any would still allow the wrong tool, auto would allow skipping. The detection that selects this form is application code, not a model judgement.
Asking for an additional identifier rather than picking a heuristic matchApplication code in the lookup handlerPrivacy for ambiguous matches is an identity boundary. The handler checks match count, surfaces masked hints, and returns a clarification prompt. No model reasoning picks "most recent".
Blocking an irreversible or compliance-bound action regardless of prompt wordingPreToolUse hook or service-side permission check in the tool serviceThe phrase "cannot be left to model discretion" in the items selects deterministic enforcement. Hooks intercept before execution and scoped credentials restrict what the tool can do even when the model is correct.
Distinguishing a failing tool from a valid empty result and deciding retry versus escalateTool result contract (is_error, isError, errorCategory, isRetryable) plus application retry policyA search returning zero rows is success, while a timeout with isRetryable: true retries once. The error-handling lesson defines this taxonomy and the API error page defines the status-level handling.
Routing after the decision, such as queue priority and SLAApplication routing and log, not the model's tone labelurgency carries priority but does not carry the decision. The escalate decision is one of the three triggers; priority is metadata that orders a queue.
Validating that the whole system actually behaves as writtenEval harness and guardrail observabilityFalse positives and false negatives on the escalation path are tracked as GuardrailEvent streams with alerting on block rate, not as model self-report.

The important nuance is that several distractor options reorder ownership. A classifier model trained on historical tickets or a sentiment analyser placed before the prompt looks plausible but is architecturally disproportionate for miscalibration that lives in the prompt. A compliance gate placed only in the prompt looks plausible but is probabilistically insufficient for a hard boundary. The correct stack is prompt for boundary reasoning, schema for handoff completeness, hook and service for hard boundaries, and evals for truth.

Version and terminology currency

The escalation and ambiguity content is largely terminology-stable, but the documentation hosts and several neighbouring mechanisms have moved, and the writer should use the current forms.

  • Terminology for escalation itself is stable. The contract uses tools escalate_to_human, create_handoff, or escalate and the lesson escalation-patterns documents policy gaps, capability limits, and explicit requests as the canonical names. The exam uses the same names plus the near-synonyms policy exceptions, inability to progress, and customer explicitly requests a human. Match the exam wording when answering, but use the lesson wording as the definition.
  • Tool use terminology has current forms the writer should cite with the platform.claude.com host. The tools parameter structure is documented under and the message structure under. The strict tool use feature is documented under and requires that top-level strict: true form, not a property inside input_schema.
  • tool_choice value naming for the forced specific-tool form is {"type": "tool", "name": "<name>"} and is documented under the tool-use implement page. The historical tool_choice: { type: "tool", name: ... } inside older docs is the same construct with the newer host. The other three values are {"type": "auto"}, {"type": "any"}, and {"type": "none"}.
  • Claude Code hook surfaces have moved to code.claude.com. Cite for the enforcement hook, and where relevant to multi-agent patterns, and prefer those over older docs.claude.com forms.
  • Error handling terminology distinguishes is_error on the tool_result block in the Messages API from the application-level isError plus errorCategory and isRetryable fields the lessons define for internal routing. The HTTP layer distinguishes rate_limit_error 429 with Retry-After, api_error 500, timeout_error 504 as retryable with backoff, and invalid_request_error 400, authentication_error 401, permission_error 403 as non-retryable.
  • Confidence terminology is clarified in the confidence-scoring lesson: the API does not expose a logprobs or top_logprobs parameter for Claude, so any answer that treats log-probability sampling as a Claude request parameter is incorrect for standard API use, even though token-level probabilities remain a valid conceptual pattern for other model hosts. The supported objective methods for Claude are self-consistency, hedging detection, and a structured confidence field inside a constrained tool schema with the reliability caveats noted. Task 5.5's calibrated score, fitted on labelled outcomes for queue prioritisation, is a separate mechanism and should not be conflated with self-reported certainty.
  • Context and compaction terminology is documented under and and is irrelevant to the escalation decision except as the place where raw transcript management is handled, which reinforces why the escalation handoff must be self-contained: server-side compaction and context editing manage window size, they do not give the human transcript access.

Official versus community divergence

Where documentation is silent, community and exam material is more prescriptive, and documentation wins for the mechanism while the exam wins for the answer. The following divergences matter for Task 5.2.

  1. The three-trigger canonical set. No platform page states that exactly three escalation reasons exist for support agents, while the exam consistently tests that set and marks any expansion as incorrect. The lessons do state the same three via policy gaps, capability limits, and explicit requests. Divergence: exam and lesson are more prescriptive than documentation. For a candidate, the exam set is the answer.
  1. Sentiment as a trigger. A common community instinct is to add frustration detection to improve escalation quality. Documentation provides no material that validates sentiment as an escalation gate, and the lessons explicitly label it an anti-pattern. The exam consistently marks sentiment-based options wrong, including historical sentiment via get_customer_sentiment_history or keyword and punctuation heuristics. Divergence: community instinct is contradicted by exam and lesson; follow exam and lesson.
  1. Self-reported confidence as a trigger. Similar. Community templates frequently show a 1 to 10 self-rated confidence with a threshold. The confidence-scoring lesson and the escalation-patterns lesson label this unreliable, and the exam marks it wrong on the miscalibrated-agent scenario. A narrower community-to-documentation divergence is that raising the threshold to a higher number is sometimes proposed as the fix; the forensics records that tuning the threshold does not address miscalibration. The reconciliation is that a validated calibrated score is acceptable for queue prioritisation in Task 5.5, which is not the same as using a raw self-report as the escalation trigger. Divergence: community confidence routing is contradicted for the trigger role, confirmed only for the routing role after fitting.
  1. Transcript as handoff. Engineering reports often treat transcript persistence plus a reference ID as the handoff because it is audit-friendly. The lesson and the forensics Rules 23 and 24 mark a raw transcript dump or a turn reference as a handoff failure when the human lacks access. Documentation's citations and search-results blocks point to passing sources inline rather than by reference, which aligns with the lesson. Divergence: engineering convenience is contradicted by the tested structure; cite lesson, not report convenience.
  1. Prompt versus code enforcement for hard boundaries. Community hardening advice sometimes stays at prompt level with block-capital instructions. Documentation and lessons state that hard compliance boundaries need code enforcement, with cited failure under prompt-only for the refund over $500 illustration. The hook and permission surfaces are the documented alternative. Divergence: prompt-only hardening is insufficient for the hard cases.
  1. Historical naming of tool-choice values. Some older community answers use tool_choice: "required" or similar single-word forms for forcing tool use. The documented forms are the four typed values and the forced-specific form is {"type": "tool", "name": "..."}. Divergence: older shorthand is not documented for Claude; use the typed object form.

Beyond the task statement

The lessons cover several adjacent topics the reference page does not name but which directly strengthen an escalation design or are tested via the same traps.

  • The escalation tiers model. The escalation-patterns lesson organises response into Tier 1 auto-resolve, Tier 2 agent retry, Tier 3 human escalation, and Tier 4 halt and preserve state. Each tier escalates only when it cannot resolve. This teaches the ordering principle that retry budgets sit inside Tier 2 and handoff sits at Tier 3, which is why a turn-count proxy for Tier 3 is wrong: turns measure effort, not whether the task is a hard boundary. Slug.
  • Decision framework by error class. The same lesson maps transient errors to backoff retry, validation errors to correction retry, permission and business-rule and capability errors to immediate escalate, genuine not-found to graceful fail, and exhausted retries to escalate with history. This table is the conceptual check a candidate can apply when an item pairs a failure type with a wrong action. Slug.
  • Soft versus hard escalation. The lesson notes distinguish soft notification that continues in parallel from a blocking handoff. The support case uses a hard block because the explicit request and policy-gap triggers mean autonomy must stop. Bulk-cancel confirmation uses a pause and confirm pattern that is also deterministic but lighter than a full queue handoff.
  • Guardrail observability. The guardrails lesson defines a GuardrailEvent stream with GuardrailMonitor metrics including block rate, false positive rate, and false negative rate, and shows alerting when the rate crosses a window. For escalation this means measuring whether the prompt criteria plus schema plus hook composite is actually routing correctly, and tuning examples when false positives rise. Slug.
  • Safety classifiers as a layered pipeline. The same lesson presents deterministic pattern checks first, then a lightweight claude-haiku classifier, then human review for edge cases. While not an escalation trigger, this is the correct fallback for the safety-critical red-flag scenario where topic sensitivity, not tone, forces an immediate empathetic handoff. Slug.
  • Structured outputs versus prompt instructions for shape. The system-prompt-design lesson and the tool-design lessons explain that a prompt request for valid JSON is steering, while structured outputs with a schema plus strict is a guarantee. That distinction is the same reasoning that makes a schema-enforced handoff preferable to a prompt "be thorough." Slugs and.
  • Tool result typing and the silent failure warning. The error-handling lesson warns that swallowing an exception and returning [] silently makes a failure look like a valid empty result, so the agent reports "no orders found" while the real problem is a broken connection. Distinguishing the two via isError shapes both retry behaviour and whether a genuine inability case is recognised. Slug.
  • Confidence methods and their tradeoffs. The confidence-scoring lesson compares log probabilities (not available on Claude), self-consistency (high reliability, high cost), hedging detection (cheap secondary check), explicit confidence via tool (low to medium reliability), and meta-cognitive prompting. The exam tip there and in the forensics is that asking the model how it feels is always the wrong option when an objective check is available. Slug.
  • Multi-agent coordination patterns. The agents-sdk and agentic lessons referenced in the registry under and handle loop plateau detection and handoff completeness when an escalated case moves across agents. They are outside the narrow Task 5.2 tested vocabulary but reinforce that graceful degradation preserves gathered context rather than aborting and losing verified identity.

Worked production examples: Worked example 1: competitor price match where the policy is silent

A customer on Nimbus Retail asks for a competitor price match of $120 on the same laptop model listed in order 88921.

Step 1, retrieve and verify. The agent calls lookup_customer and lookup_order and verifies CUST-4421 and order 88921 exist. It then retrieves Price_Adjustments_v42. The relevant sections state that same-site drops within 14 days with an original receipt are covered. Competitor pricing is not mentioned.

Step 2, classify coverage. policyCoverage is silent_or_ambiguous because the governing document after retrieval neither permits nor prohibits competitor matching. The request is not a violation with a documented denial. The amount being within the $500 autonomous limit does not matter. A decision function call decide({ explicitRequest: false, policyCoverage: "silent_or_ambiguous", ... }) returns escalate with trigger policy_gap.

Step 3, handoff. The agent does not apply the 14-day window by analogy, does not offer a partial discount, and does not deny. It calls escalate_to_human with the complete schema, naming the silent clause section 4.2, same-site only as the blocking_reason, the $120.00 amount, the attempted retrievals, and the recommended action for the human to decide. The application layer had detected no explicit request, so tool_choice is used in the forced tool form only after the decide branch selects it, which keeps the prompt as the naming layer and code as the non-bypassable layer. The log entry produced is analogous to the full JSON sample above with reason: policy_gap. The ordering proves that being within a dollar limit never grants policy coverage, which is the deliberate misdirection the exam inserts.

Failure mode avoided. If the agent instead denied outright because the request was not explicitly permitted, it would refuse a decision the business might want. If it inferred by analogy and granted a match, it would create unauthorised precedent. Both violate the gap principle.

Worked production examples: Worked example 2: furious tone with a simple within-policy duplicate charge

A customer writes "This is RIDICULOUS, nothing ever works!!! Fix my duplicate charge NOW" on order 77231 for $47.20.

Step 1, tolerance. The tone is strong, including block capitals and exclamation marks. The agent treats tone as a cue for acknowledgement only.

Step 2, retrieve and classify. lookup_order confirms order 77231 has a duplicate charge of $47.20. Retrieved policy states duplicate charges are refundable within 60 days and no manager approval is needed under $500. policyCoverage is covered, explicitRequest is false because the words human, person, or manager are absent even though the emotional intensity is high.

Step 3, act. decide returns resolve. The agent outputs "I understand this is frustrating. I can process the $47.20 refund for order 77231 right now." and calls process_refund with the verified ID. If the same customer had instead written "this is ridiculous, I want a human now" the explicitRequest flag would flip, the same dollar amount and policy coverage would apply, but the first branch of decide would now force escalation. That pairing is the exam's favourite trap of calm gap versus furious simple case.

Failure mode avoided. Sentiment or punctuation gating would have escalated the simple case and left the calm policy gap to the agent autonomously, exactly the inversion the forensics records as the miscalibration failure.

Worked production examples: Worked example 3: ambiguous lookup for a wire transfer where heuristic selection would pick the wrong account

A caller named "T. Robinson" asks for a wire of $4,200, providing only the name. lookup_customer returns three records for that name: a dormant savings account, a recently opened business account with the same name, and the caller's active personal account. The recent account is the most recently created and has the highest recent activity.

Step 1, do not select. The agent applies the disambiguation gate in application code. Even though the business account is most recent and most active, that heuristic has a nonzero wrong-account rate and causes the privacy violation the exam warns about. The gate returns request_disambiguation with a masked hint list.

Step 2, ask precisely. The agent replies "I found more than one record for T. Robinson. Could you confirm your email, phone number, or account number so I use the correct account?" It does not proceed to process_wire and it does not escalate to a human as a blanket multi-match policy, which would be over-escalation for a simple status check. Only after the customer supplies [email protected] does a narrow lookup return one record, which then feeds verifiedCustomerId into decide.

Failure mode avoided. Selecting the most recent or most active account would have sent the wire from the wrong account, producing both a customer harm and a dispute. Escalating every ambiguous lookup to a human would have added unnecessary queue load while the agent could have resolved the identity with one question.

Build exercise material

Reproduce the Task 5.2 build exercise locally and verify each step by observable output. No model identity or research process is involved; only the artefacts below.

Step 1, create the system prompt file with explicit criteria and few-shot examples. Create system_prompt.txt from Example 1 exactly, adding two to four examples and keeping constraints testable and anti-patterns explicit. Observable outcome: the file contains three triggers by name (explicit human request, policy gap, inability to progress), two anti-pattern exclusions (sentiment and self-reported confidence), and at least three worked examples where examples sit at the end of the file. Verify by grep -cEXAMPLE system_prompt.txt showing at least 3 and by reading the closing fifty lines.

Step 2, define the escalation tool with required-field enforcement. Create tools/escalate_to_human.json from Example 2, adding strict: true and the full required array of eight fields. Run a local JSON Schema validation step that attempts a call missing customer_id and another missing recommended_action. Observable outcome: both calls fail validation before any model response is simulated, proving completeness is enforced at the schema rather than as a prompt request.

Step 3, add the disambiguation gate in application code. Create src/customer_lookup_gate.ts from Example 4 and add unit tests for four cases: zero matches, one match, multiple matches, and an error payload with isRetryable: true versus false. Observable outcome: zero matches returns report_no_match, one match returns proceed, multiple matches returns request_disambiguation with a prompt naming email, phone, and order number, and isRetryable true routes to retry while false routes to escalate handling. Verify by running the unit suite and seeing four greens.

Step 4, wire tool_choice correctly for detection versus conversation. In src/agent_loop.ts configure the creation of the initial conversation turn with tool_choice: { type: "auto" } so tool calls remain optional. Add an explicit if (detectedExplicitRequest) nextChoice = { type: "tool", name: "escalate_to_human" } branch that is driven by keyword matching on "human, person, real person, manager" in application code before the next model call. Observable outcome: a test sending "I want to speak to a human now" produces a tool_use for escalate_to_human with zero degrees of freedom on selection, while a test sending "this is so frustrating, can someone please just look at my account" stays on auto and produces a text acknowledgement plus a clarifying offer rather than a forced tool call.

Step 5, add the typed decision guard. Create src/escalation_decision.ts from Example 5 and remove any earlier code that read sentimentScore or confidence when choosing a trigger. Run the type checker. Observable outcome: any branch that attempts to read sentiment or confidence fails type checking, and only explicit_human_request, policy_gap, and inability_to_progress remain as valid triggers, proving the unreliable signals cannot enter the decision.

Step 6, prove graceful handling of hard limits and blocked tools. Simulate a bulk cancel of 847 orders totalling $234,000 and a refund over $500 by routing through preToolUseGate from the deterministic gate example. Observable outcome: the gate returns allow: false with a structured is_error payload and the next model turn is forced to escalate_to_human with a complete handoff, rather than the system silently succeeding or aborting and discarding verified identity. Verify by checking the log entry for tool_choice_on_escalation_turn: tool and forced_tool: escalate_to_human.

Step 7, verify the two absolute rules across phrasings. Run the verification script against the four scenarios from the reference build exercise: frustrated customer with a simple issue, calm customer requesting a policy exception, customer explicitly requesting a human, and ambiguous customer match. Observable outcome: only the explicit-request scenario escalates immediately with zero investigation turns, the simple frustrated scenario resolves autonomously, the gap scenario escalates with a silence-naming reason, and the ambiguous match scenario returns a disambiguation prompt with no record selection. The log for each escalation is the JSON shape from Example 6, where a human can act without the transcript because identifiers, amounts, root cause, and recommended action are inline.

Mechanism and API surface

Explicit human request as an absolute trigger
Phrases like I want to speak to a person or transfer me to a human require immediate escalation with no investigation step and no let me try first.
Policy gap versus policy violation
A gap is a situation the documented policy is silent on and requires human judgement about whether to make an exception. A violation is explicitly addressed with a documented answer and the agent communicates that answer.
Inability to make meaningful progress
Grounded in concrete failed attempts where tools, retries, and accessible systems did not advance the case. Subjective guessing without trying does not qualify.
Sentiment and confidence as unreliable signals
Frustration and negative sentiment measure emotional state not case complexity. Self-reported confidence is poorly calibrated and often confident on hard cases and hesitant on easy ones. Use calibrated external signals if confidence is used at all.
Ambiguous matching via clarification request
On multiple matches ask for an additional identifier such as email, phone, or order number. Never select by recency, activity, or other heuristic.
Structured handoff and forced escalation tool
Tool escalate_to_human with customerId, issueSummary, attemptedActions, recommendedAction, urgency, and timestamp, invocable with tool_choice type tool and name escalate_to_human for a complete machine readable handoff.

The decision rules in play

Each rule below is a distinct decision the exam tests, stated with its mechanism, the boundary where the answer flips, and the wrong answers written against it.

R1

Explicit human request is an absolute escalation trigger, honoured immediately

When a customer states in words that they want a person, a human, a real person, a manager, or to speak to someone who can help, the agent must call its escalation or handoff tool in the very first response and transfer with whatever context it already holds. The exam treats this as a non-negotiable architectural constraint, not a customer-service preference. The instruction "transfer to a human immediately" is the correct completion; any answer that inserts an investigation, a question, or a confirmation step before the transfer is wrong.

The rule exists because a human has the right to choose their channel of service. The agent's ability to solve the problem is irrelevant once that right is exercised. Items frame this as an explicit override instruction that outranks every capability assessment the agent might make. A customer who has already been failed by automated handling twice is not signalling a preference to be re-negotiated; they are issuing a directive.

Boundary. The boundary is the word "explicit". The moment the customer names a human as the desired handler, escalation is mandatory. The nearby opposite case is implicit frustration, such as "I just want to speak to someone who can actually help me", which is covered by Rule 3 and does not trigger mandatory escalation. The line is thin but the exam draws it sharply: "I want a human NOW" escalates; "someone who can help" gets an acknowledgement plus an offer.

Recurring specifics. The trigger phrases that appear across items are "I want to speak to a human", "transfer me to a human agent", "connect me to a real person", "just get me a person", "I would like to speak with a real person, please", and "I want to talk to a real person NOW". The escalation tool names vary: escalate_to_human, create_handoff, escalate. The full conversation history is passed so the human does not re-interpret. The priority is frequently flagged "high" when frustration is also present.

Wrong answers written against this rule

Proposal. investigate the account first, then escalate.

Why it attracts. the issue is often simple and the agent is "just being efficient".

Why it fails. it substitutes the agent's judgment for an explicit directive and adds friction the customer already rejected.

When it would be right. never, for an explicit request.

Proposal. ask the customer to confirm they still want a human after a quick resolution offer.

Why it attracts. it reduces "unnecessary" escalations.

Why it fails. it makes a customer repeat an explicit request and reads as gatekeeping.

When it would be right. only when the original message was implicit frustration, not an explicit demand.

Proposal. detect frustration via sentiment and escalate once a score crosses a threshold.

Why it attracts. it looks data-driven.

Why it fails. it adds an algorithmic gate in front of a right the customer already asserted.

When it would be right. never as a substitute for an explicit request.

Proposal. acknowledge and resolve the simple issue, then mention a human is available.

Why it attracts. it serves the customer fast.

Why it fails. it delays the requested transfer and treats the request as negotiable.

When it would be right. only if the customer never explicitly asked for a human.

How the same rule gets re-asked
  • - The customer asks for a human while describing a routine return the agent could resolve. Correct action: escalate immediately. - The customer is frustrated and asks for a human; the agent already identified a two-step resolution path. Correct action: still escalate immediately, and pass the resolution path along as useful context. - The customer says "stop troubleshooting, I want to speak to a person now" mid-investigation. Correct action: stop and escalate, do not finish the two diagnostic questions.
R2

An explicit request overrides any agent capability assessment

The agent evaluates the problem, finds it solvable in N steps with available tools, and still must escalate the instant the customer says they want a human. The capability assessment is computed, recorded as context for the human, but never used to justify delaying or refusing the transfer. The exam models this as an override that "outranks all capability assessments".

Customer autonomy is a hard constraint layered above task competence. An agent that says "I can resolve this, proceeding" after a human said "I want a human" is committing a fundamental violation: it substitutes its own judgment for an explicit user directive. The transfer still carries the agent's analysis, so no value is lost, but the ordering is what matters.

Boundary. Boundary: the request must be explicit. If the customer says "this is ridiculous, I've been waiting forever" without naming a human, Rule 4 applies instead. Nearby opposite: a calm customer who requests a policy exception (no human named) is handled under Rule 8, not this rule. The two scenarios share emotional content but differ on the single dimension of whether a human was named.

Recurring specifics. Items pair the explicit request with a confirmed-simple issue: a billing adjustment, a password reset, a standard damage replacement. The agent "has already identified this as a standard adjustment it can resolve in 2 steps". The handoff payload then carries resolution_available: true so the human can act fast.

Wrong answers written against this rule

Proposal. complete the investigation, present the solution, escalate only if the customer still insists.

Why it attracts. it seems thorough.

Why it fails. it makes the customer repeat a request they already made and disrespects the directive.

When it would be right. only if no explicit request was made.

Proposal. apologise for the wait, then proceed with automated resolution because the case is within capability.

Why it attracts. empathy plus efficiency.

Why it fails. it ignores the explicit preference.

When it would be right. never for an explicit request.

Proposal. detect negative sentiment and escalate once frustration crosses a threshold.

Why it attracts. frustration is present.

Why it fails. the customer already asked; no score is needed.

When it would be right. never as a substitute.

How the same rule gets re-asked
  • - Customer says "I'd like to speak to a human please" plus "I'm frustrated". Correct: escalate immediately; frustration makes immediate escalation more, not less, correct. - Customer says "I want to talk to a real person NOW" and "I've explained twice". Correct: escalate with full history; never ask one more question. - Agent has called no tools yet when the request arrives. Correct: escalate with zero investigation; do not gather context first.
R3

Implicit frustration is distinct from an explicit human request

When the customer expresses frustration or doubt that the agent can help but does not name a human, the agent acknowledges the feeling, states that resolution is available, and offers the choice between immediate resolution and a human transfer. This is the deliberate counterpoint to Rule 1. The exam uses near-identical phrasing to test the distinction: "I just want to speak to someone who can actually help me" is implicit, whereas "I want to talk to a real person NOW" is explicit.

Implicit language signals doubt about the channel, not a veto of it. Offering resolution respects both the customer's time and their autonomy: if they truly wanted a human they can say so, but many such customers mainly want their problem solved. Escalating on implicit language would needlessly consume human capacity.

Boundary. Boundary: did the customer name a human or a person? If yes, Rule 1. If no, this rule. Nearby opposite: the same customer who says "someone who can help" and is offered resolution may reiterate "no, I want a human" - at that point Rule 5 fires and escalation becomes mandatory.

Recurring specifics. The implicit formulations include "someone who can actually help me", "going back and forth for days", and "this is so frustrating, can someone please just look at my account". The correct response pattern is: acknowledge, confirm eligibility, offer choice. The agent already confirmed via lookup_order that the return is straightforward and within policy.

Wrong answers written against this rule

Proposal. immediately escalate without acknowledgment.

Why it attracts. it honours the surface request.

Why it fails. it wastes a human on a case the agent can close and ignores the real need (resolution).

When it would be right. only if the words named a human.

Proposal. silently process the refund with no acknowledgment.

Why it attracts. fast.

Why it fails. it feels dismissive and robotic, eroding trust.

When it would be right. never; acknowledgement is required.

Proposal. ask what specifically has not worked before deciding.

Why it attracts. gathers info.

Why it fails. the agent already has everything; asking a frustrated customer to repeat worsens experience.

When it would be right. only when genuinely missing information.

How the same rule gets re-asked
  • - "I just want to speak to someone who can actually help me" plus confirmed eligibility. Correct: acknowledge, inform resolvable, offer choice. - "Can someone please just look at my account" without naming human. Correct: resolve if within capability, escalate only if reiterated.
R4

A frustrated but resolvable issue is resolved, not escalated

The agent meets an angry or upset customer whose underlying problem is straightforward and within policy. It acknowledges the frustration briefly and resolves the issue, escalating only if the customer explicitly asks for a human. Sentiment is a cue to adapt tone, not a cue to transfer.

Frustration measures emotional state, not case difficulty. A furious customer with a one-day shipping delay or a simple return is easy to serve: apologise with appropriate empathy, apply the known policy, close the case. Escalating every angry message would flood the human queue with resolvable work and increase wait times for everyone.

Boundary. Boundary: is the issue within the agent's authority and policy? If yes, resolve and acknowledge. If the customer names a human, Rule 1. If the issue is itself a policy gap, Rule 6. Nearby opposite: a calm customer requesting competitor price matching (no anger) must escalate because the issue is a gap, not because of tone. This is the exam's favourite trap: pairing anger with a simple case and calm with a hard case.

Recurring specifics. Phrases include "this is RIDICULOUS, I've been waiting for WEEKS, fix this NOW!!!" about a routine shipping delay, and "this is ridiculous, nothing ever works!" about a two-tool return. The policy is "within normal policy" or "within policy". The correct answer acknowledges and resolves.

Wrong answers written against this rule

Proposal. escalate because any negative sentiment always indicates need for human empathy.

Why it attracts. seems safe.

Why it fails. over-escalates simple cases.

When it would be right. never based on sentiment alone.

Proposal. ignore the sentiment and focus on technical resolution.

Why it attracts. professional.

Why it fails. unacknowledged frustration reads as dismissive.

When it would be right. never; acknowledge first.

Proposal. escalate only if profanity present.

Why it attracts. a bright line.

Why it fails. profanity is not the trigger; explicit human request is.

When it would be right. never.

How the same rule gets re-asked
  • - Angry message, in-policy shipping delay. Correct: resolve with empathetic tone. - Angry message, two-tool return within policy. Correct: acknowledge and resolve. - Mild disappointment ("that is not what I expected"), single message. Correct: do not escalate; resolve or clarify.
R5

A reiterated preference for a human after an offered resolution

If the agent acknowledged frustration and offered resolution (Rule 4) and the customer then says "no, I want a human", escalation becomes mandatory. The first offer was correct; the reiteration converts an implicit signal into an explicit one.

The customer has now been given a real opportunity to accept agent resolution and declined. Honouring autonomy means escalating at that point rather than persisting. Continued automated handling after a clear decline is the failure mode.

Boundary. Boundary: the reiteration must follow a genuine offer, not precede it. If the very first message named a human, Rule 1 applies and no offer should precede escalation. Nearby opposite: a customer who accepts the offer and lets the agent resolve never triggers this rule.

Recurring specifics. Items describe the agent offering help, the customer repeating the request for a human, and the correct fix being immediate escalation on the repeat. The handoff passes the agent's already-identified resolution path as context.

Wrong answers written against this rule

Proposal. keep offering because most such requests are resolvable.

Why it attracts. statistics.

Why it fails. disrespects explicit decline.

When it would be right. only before the first explicit decline.

Proposal. ask them to confirm one more time.

Why it attracts. reduces escalations.

Why it fails. gatekeeping.

When it would be right. never after a clear reiteration.

How the same rule gets re-asked
  • - Offer made, customer declines once. Correct: escalate. - Offer made, customer accepts. Correct: resolve, no escalation.
R6

A policy gap is an escalation trigger; a policy violation is not

When the written policy neither permits nor prohibits the customer's specific request, that silence is itself the escalation trigger. The agent retrieves all available data, confirms the issue cannot be decided from existing policy, and calls the handoff tool with the identified gap, rather than inferring an answer by analogy. By contrast, when policy explicitly forbids something (for example, a return outside the window, or a waiver outside enumerated exceptions), the agent applies the documented answer ("no") autonomously and does not escalate.

A gap means any decision the agent makes is effectively creating policy, which is a human responsibility. Permitting would set an unauthorised precedent; denying would refuse something the business might actually want. Either way the agent lacks authority. A violation means the rule already exists and the agent simply applies it. Conflating the two is the central trap: candidates are tempted to escalate "hard" refusals and autonomously "help" on gaps, which is exactly backwards.

Boundary. Boundary: does the policy text speak to this request? If silent or internally ambiguous, escalate. If it explicitly answers, apply it. Nearby opposite: a no-stacking coupon rule that also covers expired coupons settles a "gap-looking" request because the prohibition already applies; the agent declines autonomously. The exam constructs this as a trap that looks like a gap but is a violation.

Recurring specifics. The canonical gap is competitor price matching: policy covers same-site price drops within 14 days but is silent on competitor pricing. Other gaps: cross-instrument refunds (refund to a different payment method), non-standard payment terms, uncovered rebate clauses, loyalty-program disputes the policy never addresses, and warranty terms silent on accidental damage. The handoff names the specific silent clause. The 14-day window and original-receipt requirement recur as the documented same-site terms.

Wrong answers written against this rule

Proposal. apply the closest documented clause by analogy (reuse the 14-day window for competitor matches).

Why it attracts. keeps the agent autonomous and consistent.

Why it fails. it creates unauthorised precedent.

When it would be right. never for a true gap.

Proposal. deny outright because the request is not explicitly permitted.

Why it attracts. cautious.

Why it fails. it forecloses a decision the business may want; silence is not prohibition.

When it would be right. only for clear violations.

Proposal. approve a partial compromise discount.

Why it attracts. customer-friendly.

Why it fails. commits the company without authority.

When it would be right. never.

Proposal. escalate only if the customer repeats after being told no.

Why it attracts. reduces escalations.

Why it fails. the gap exists at first contact.

When it would be right. never.

How the same rule gets re-asked
  • - Competitor price match, same-site policy silent. Correct: escalate as gap. - Cross-instrument refund, within autonomous dollar limit but policy silent. Correct: escalate; money limit does not grant policy authority (Rule 7). - Coupon stacking forbidden, expiry silent, but no-stacking settles it. Correct: decline autonomously, not a gap. - Waiver policy lists only medical and military; customer cites trade show. Correct: deny autonomously, enumerated exceptions are exhaustive. - 8-month-old refund against 90-day window. Correct: decline autonomously, violation not gap. - Accidental damage on a plan listing only manufacturing defects. Correct: escalate, genuine gap.
R7

Falling within a financial authority limit does not grant authority in a policy vacuum

An agent may be authorised to refund up to a dollar amount, but that financial authority is independent of policy-interpretation authority. When a request is within the dollar limit yet the policy is silent, the agent still escalates. Being "allowed to spend" is not "allowed to decide".

Monetary thresholds govern financial exposure, not interpretive scope. The exam inserts "the amount is within autonomous limits" as a deliberate misdirection: it makes the case look safe to close, but the missing policy still blocks autonomous resolution. The two axes are tested separately.

Boundary. Boundary: is the request both within the dollar limit AND covered by policy? Only then resolve. If covered but over limit, escalate for authority (Rule 28 territory). If within limit but silent, escalate for gap. Nearby opposite: a refund within limit and clearly permitted is resolved autonomously; the limit helps there, but never substitutes for policy coverage.

Recurring specifics. Items state "the amount is within autonomous limits" next to "policy neither permits nor prohibits". The correct answer ignores the limit and escalates on the gap.

Wrong answers written against this rule

Proposal. resolve because under the dollar threshold.

Why it attracts. threshold met.

Why it fails. no policy authority.

When it would be right. only when policy also covers the request.

Proposal. escalate because over threshold.

Why it attracts. threshold logic.

Why it fails. the case is under limit; misreads the misdirection.

When it would be right. when actually over the limit.

How the same rule gets re-asked
  • - Within limit, silent policy. Correct: escalate. - Over limit, covered policy. Correct: escalate for authority, different reason.
R8

Policy exceptions requiring human judgement escalate

When the request would need a judgment call outside defined policy, such as a goodwill refund, an account special handling, or a fraud edge case, the agent escalates. These are cases where the policy points to a human decision rather than a rule.

Exceptions are by definition outside the agent's defined authority. The exam lists examples: goodwill refunds, account special handling, fraud edge cases, manager-approval pricing errors. The agent may have done the diagnosis; the decision still belongs to a human.

Boundary. Boundary: does applying the request require interpreting or creating policy? If yes, escalate. If a rule cleanly applies, resolve. Nearby opposite: a duplicate charge with a clear refund tool and no exception needed is resolved autonomously.

Recurring specifics. Promotional pricing errors requiring manager approval, refunds exceeding authorization limits, account-compromise situations. The handoff carries authorization_required and recommended_resolution.

Wrong answers written against this rule

Proposal. attempt the refund anyway, escalate only if the system rejects it.

Why it attracts. let the system enforce.

Why it fails. may succeed if controls weak, bypasses required human judgment.

When it would be right. never; escalate before acting.

Proposal. deny to stay safe.

Why it attracts. avoids error.

Why it fails. refuses something possibly warranted.

When it would be right. only for clear violations.

How the same rule gets re-asked
  • - Pricing error needs manager approval. Correct: escalate with diagnosis. - Standard duplicate charge, tool available. Correct: resolve autonomously.
R9

Inability to make meaningful progress after a genuine attempt

The agent has tried: it called tools, gathered data, and still cannot advance because of conflicting information, missing access, or a technical bug. Only after a genuine attempt does it escalate. "I might not be able to handle this" without trying is not sufficient.

This is the catch-all trigger, but it is earned. The exam stresses "genuine attempt" because escalation without trying is the miscalibration it punishes elsewhere (escalating easy cases). The agent must show it failed, not merely fear it might.

Boundary. Boundary: has the agent used its tools and still hit a wall? If yes, escalate. If it has not tried, resolve first. Nearby opposite: a tool timeout that a local retry would fix is not yet "inability" (Rule 26, Rule 29).

Recurring specifics. Conflicting charge timestamps the agent cannot reconcile, three accounts with contradictory histories, tool errors beyond local retry. The handoff lists attempted actions and the blocking reason.

Wrong answers written against this rule

Proposal. escalate the moment uncertainty appears, before trying.

Why it attracts. cautious.

Why it fails. no genuine attempt.

When it would be right. never; attempt first.

Proposal. guess which charge the customer means.

Why it attracts. keeps moving.

Why it fails. issues wrong resolution.

When it would be right. never; escalate instead.

How the same rule gets re-asked
  • - Conflicting timestamps, no tool resolves. Correct: escalate. - Single clear charge, tool available. Correct: resolve.
R10

Retry exhaustion and tool failures are reliable structural triggers

Explicit failure signals: a required tool is missing, a retry limit is exhausted, required information is unavailable or access is denied. These are structurally verifiable and do not rely on tone or self-assessment, so they are reliable escalation triggers.

Unlike sentiment or confidence, these conditions are observable facts about the system. The agent cannot proceed because a dependency failed or is absent; escalation is the correct recovery path. Items explicitly rank "missing required tool, exceeded retry limit, ambiguous requirements" as reliable versus sentiment and self-confidence as unreliable.

Boundary. Boundary: did a real structural failure occur after retries? If yes, escalate. If a retry would likely succeed, handle locally (Rule 26). Nearby opposite: a transient timeout that succeeds on retry is not a trigger.

Recurring specifics. missing required tool, exceeded retry limit, required information unavailable, access denied. The exam contrasts these with turn-count and sentiment.

Wrong answers written against this rule

Proposal. turn-count threshold as the proxy for failure.

Why it attracts. predictable.

Why it fails. escalates simple cases at turn 18, delays complex ones.

When it would be right. never as primary trigger.

Proposal. self-assessment text declaring inability.

Why it attracts. honest.

Why it fails. poorly calibrated.

When it would be right. only alongside structural signals.

How the same rule gets re-asked
  • - Retry limit exhausted on diagnostic. Correct: escalate with transcript summary. - Three failed tool calls, but a fourth retry would succeed. Correct: retry locally first.
R11

A case that merely "feels complex or unusual" is not a valid trigger

The exam lists "the case simply feels complex or unusual" as the explicit NOT-a-trigger distractor among the three valid ones. Escalation must rest on one of the three structural reasons (explicit request, policy gap, inability to progress), not on a vibe. When a candidate is asked which is NOT a valid trigger, this is the answer.

Subjective impressions are not reproducible decision criteria. A feeling of complexity cannot be written into a system prompt as a deterministic rule, so it fails the same calibration test that sentiment and confidence fail. The three valid triggers are all externally verifiable; "feels complex" is not.

Boundary. Boundary: is there a named, verifiable condition? If the complexity is caused by a genuine policy gap or a genuine inability to progress, those named triggers apply, not the feeling. Nearby opposite: a genuinely ambiguous policy is a gap (Rule 6), which is verifiable, so it escalates for the right reason.

Recurring specifics. Items ask "which is NOT one of the three valid triggers" and offer "the case simply feels complex or unusual" as the correct choice.

Wrong answers written against this rule

Proposal. treat novelty as a trigger.

Why it attracts. new cases seem risky.

Why it fails. not verifiable.

When it would be right. only when novelty maps to a real gap or failure.

Proposal. escalate unusual cases to be safe.

Why it attracts. caution.

Why it fails. over-escalation.

When it would be right. only if unusual maps to a valid trigger.

How the same rule gets re-asked
  • - "Feels complex" offered among explicit request, policy gap, cannot progress. Correct: the vibe is the non-trigger.
R12

Sentiment or frustration detection is an unreliable escalation trigger

Using frustration detection or negative-sentiment scores to trigger escalation is an anti-pattern. The exam names "sentiment-based escalation" explicitly as a failure mode. A furious customer with a simple late delivery is easy to resolve; a calm customer with a policy gap needs human judgement. Sentiment measures emotion, not case difficulty.

The correlation between frustration and complexity is weak or absent. Escalating on anger moves resolvable work to humans and leaves genuinely hard (calm) cases with the agent. Items show agents that escalate every "this is ridiculous" message while attempting competitor price matching autonomously: exactly inverted.

Boundary. Boundary: is the escalation decision driven by emotional tone? If yes, it is the anti-pattern. If tone is used only to adapt empathy while the decision rests on complexity or policy, that is fine. Nearby opposite: a customer who is furious but describes a one-day delay is resolved, not escalated.

Recurring specifics. Phrases "this is ridiculous", "I've been waiting forever", exclamation marks, "unacceptable". The correct improvement is to replace sentiment triggers with explicit policy-based criteria. The exam frames the miscalibrated agent as escalating straightforward damage replacements while handling policy exceptions.

Wrong answers written against this rule

Proposal. lower the sentiment threshold so only extreme frustration escalates.

Why it attracts. compromise.

Why it fails. still sentiment-driven, still unreliable; also adds an investigation step.

When it would be right. never as the trigger.

Proposal. combine sentiment with a second signal like repeated complaint.

Why it attracts. multi-signal feels safer.

Why it fails. still routes on emotion, not on case substance; at best reduces false positives.

When it would be right. only as a tone-adaptation aid, never as the escalation gate.

Proposal. implement sentiment analysis to auto-escalate.

Why it attracts. common product instinct.

Why it fails. named anti-pattern.

When it would be right. never for the trigger.

How the same rule gets re-asked
  • - Sentiment trigger plus attempt-resolution-first. Correct: still wrong; sentiment is the flaw. - Sentiment as tone adaptation only. Correct: acceptable, but not the escalation trigger.
R13

Self-reported confidence scores are an unreliable escalation trigger

Having the agent emit a confidence score (1-10 or 0.0-1.0) and escalate below a threshold is an anti-pattern. LLM self-confidence is poorly calibrated: the model is often incorrectly confident on hard cases and unnecessarily uncertain on easy ones. The exam's scenario is the agent escalating simple cases while attempting complex ones: the exact failure mode.

The model does not know what it does not know. A high self-score on a policy exception leads to a wrong autonomous refund; a low self-score on a simple address change leads to needless escalation. Self-reported confidence substitutes a proxy for the missing decision logic, which is the same root cause as sentiment.

Boundary. Boundary: is the routing decision based on a raw self-rated score? If yes, anti-pattern. If confidence is a calibrated, validated signal used for tiered human review routing in a classification system (see Contradictions), it may be acceptable, but as the sole escalation trigger for support it is wrong. Nearby opposite: a structured handoff that includes a confidence reading as context is fine; using that reading as the gate is not.

Recurring specifics. Score forms: confidence 1-10, resolution_confidence 0.0-1.0, urgency_score, confidence_score. Thresholds cited: 0.7, 0.6, 0.55, 0.5, 0.8, 0.95. The exam notes threshold tuning ("raise it to 0.9") does not fix miscalibration. Anthropic's recommendation referenced is rubric-based self-evaluation rather than a raw percentage.

Wrong answers written against this rule

Proposal. raise the threshold so only very low scores escalate.

Why it attracts. tightens gate.

Why it fails. miscalibration remains; confident-wrong cases still slip.

When it would be right. never as sole trigger.

Proposal. attach confidence to each decision and route below threshold.

Why it attracts. data-driven.

Why it fails. same anti-pattern.

When it would be right. only inside a calibrated tiered review system.

Proposal. more few-shot examples to improve confidence accuracy.

Why it attracts. training.

Why it fails. confidence is not trainable into calibration.

When it would be right. never.

How the same rule gets re-asked
  • - Confidence 9/10 on policy exception, wrong refund. Correct: shows miscalibration. - Confidence 3/10 on simple request, needless escalation. Correct: shows miscalibration.
R14

Historical or aggregate sentiment is still sentiment-based escalation

A proposal to use a get_customer_sentiment_history aggregate or past-interaction sentiment to prioritise or escalate the current ticket is the same anti-pattern in different clothing. The exam marks it: escalation criteria are not sentiment, regardless of single-message or historical form.

Aggregate emotional history says nothing about the current issue's complexity or policy fit. Using it to escalate or deprioritise is still routing on emotion. The one permitted use noted is tone adaptation: the agent may adapt its manner using the history, but must not escalate on it.

Boundary. Boundary: is the current escalation decision driven by emotional history? If yes, anti-pattern. If history informs tone only, acceptable. Nearby opposite: current-issue complexity driving escalation is correct regardless of history.

Recurring specifics. get_customer_sentiment_history returning aggregate past sentiment. Options: use to auto-prioritise, deprioritise frustrated customers, still sentiment-based (correct answer), delete entirely.

Wrong answers written against this rule

Proposal. use historical sentiment to deprioritise frustrated customers.

Why it attracts. efficiency.

Why it fails. still sentiment routing; also harms those who need help.

When it would be right. never.

Proposal. delete sentiment data entirely.

Why it attracts. removes bias.

Why it fails. over-corrects; tone adaptation is legit.

When it would be right. not required.

How the same rule gets re-asked
  • - Single-message tone. Correct: anti-pattern. - Historical aggregate tone. Correct: same anti-pattern.
R15

Keyword, punctuation, and emoji heuristics are unreliable frustration proxies

Triggers keyed to an exclamation mark, the word "unacceptable", a short reply, or a single emoji are unreliable. Items show queues flooded with assertively phrased routine questions while polite repeat-askers never reach a human. Punctuation and message length are not confirmed triggers.

These signals are linguistic surface, not case substance. A customer can be brief and calm, or effusive and simple. Keying escalation to them produces false positives (routine but punchy) and false negatives (polite but stuck).

Boundary. Boundary: is the trigger a lexical or punctuation cue? If yes, unreliable. If it is an explicit request or a structural failure, reliable. Nearby opposite: a customer who says "I want a human" with no punctuation still escalates (Rule 1).

Recurring specifics. Exclamation mark, "unacceptable", emoji, single short message "that is not what I expected". The correct diagnosis: mild single-message frustration is an unreliable trigger; reserve escalation for explicit request, repeated failure, or safety.

Wrong answers written against this rule

Proposal. lengthen the keyword list to capture more dissatisfaction words.

Why it attracts. broader net.

Why it fails. still unreliable proxy.

When it would be right. never.

Proposal. escalate after three turns regardless of tone.

Why it attracts. predictable.

Why it fails. turn-count proxy (Rule 16).

When it would be right. never.

How the same rule gets re-asked
  • - Exclamation mark present. Correct: not a trigger. - "Unacceptable" keyword. Correct: not a trigger.
R16

Turn-count or conversation-length thresholds are unreliable triggers

A fixed conversation-turn threshold (for example 18 turns, 12 turns, 8 turns, 3 turns) used as the escalation trigger is unreliable. It escalates simple issues at the threshold and delays complex ones until the threshold, regardless of severity.

Turn count is a proxy for effort, not for whether the case needs a human. The exam contrasts it directly with explicit failure signals (missing tool, retry exhausted, ambiguity) which are reliable. A turn cap may be a fallback safety net, but it must not be the primary trigger.

Boundary. Boundary: is the trigger the count of turns? If yes, unreliable as primary. If a genuine inability-to-progress (after retries) coincides with many turns, the real trigger is the failure, not the count. Nearby opposite: explicit human request escalates at turn one.

Recurring specifics. Thresholds: 18 turns, 12 turns, 8 turns, 3 turns. Items describe simple issues escalating at turn 18, complex ones delayed until 18.

Wrong answers written against this rule

Proposal. require three tool calls that fail before escalating.

Why it attracts. "reasonable attempt".

Why it fails. still a count proxy; some issues need immediate escalation.

When it would be right. only as a safety net, not the rule.

Proposal. fixed 18-turn threshold for consistency.

Why it attracts. predictable.

Why it fails. mis-escalates by complexity.

When it would be right. never as primary.

How the same rule gets re-asked
  • - 18-turn cap. Correct: unreliable. - Retry-limit-exhausted. Correct: reliable structural signal.
R17

Multiple customer-record matches require a request for an additional identifier

When a lookup returns several records for one query (a name search returning three "John Smith" records, or an email returning three accounts), the agent must ask the customer for an additional identifier (email, phone, order number, account number, billing zip) before taking any account-specific action. It never proceeds on an ambiguous match. This is the only safe response because it eliminates ambiguity deterministically.

example.ts
typescript
function handleCustomerLookup(matches: CustomerRecord[]): NextAction {
  if (matches.length === 1) return { action: "proceed", customerId: matches[0].id };
  if (matches.length === 0) return { action: "report_no_match" };
  return {
    action: "request_disambiguation",
    prompt: "Could you confirm your email or recent order number so I can find the right account?",
    candidates: matches.map(m => ({ id: m.id, hint: m.maskedEmail })),
  };
}

Selecting the wrong customer causes privacy violations (exposing one customer's data to another) and incorrect actions (refunds on the wrong account). Confirming identity directly with the customer converts a nonzero error rate into effectively zero. Items show heuristic selection wrong 4% to 18% of the time; asking eliminates it.

Boundary. Boundary: did the lookup return more than one plausible match? If yes, ask before acting. If exactly one match, proceed. If zero matches, that is a valid empty result, handled under Rule 29, not a reason to escalate. Nearby opposite: a single clear match needs no clarification and no escalation.

Recurring specifics. Tools: get_customer, lookup_customer, lookup_order. Error rates cited: 4%, 15%, 18%. Identifiers requested: email, phone, order number, account number, date of birth, billing zip. The agent surfaces the candidates with distinguishing details and confirms. Example of a correct gate:

Wrong answers written against this rule

Proposal. pick the most recently active record.

Why it attracts. "most likely the caller".

Why it fails. wrong 15% of the time; nonzero harm.

When it would be right. never for ambiguous matches.

Proposal. pick the highest-lifetime-value record via a gate.

Why it attracts. protects revenue.

Why it fails. still guessing; wrong account is worse.

When it would be right. never.

Proposal. flag all multi-match for human review.

Why it attracts. safe.

Why it fails. bottleneck; agent can resolve by one question.

When it would be right. only if disambiguation itself fails.

How the same rule gets re-asked
  • - Three name matches, ask for order number. Correct: disambiguate. - Email returns three accounts, ask for billing zip. Correct: disambiguate. - Zero matches, customer does not exist. Correct: report no match, do not escalate. - Phone lookup returns two accounts, one secondary-flagged. Correct: ask, do not exclude the secondary. - Business account selected instead of personal for a wire. Correct: wrong; should have confirmed.
R18

Heuristic selection is prohibited for disambiguation

The agent must not select the most recent, most active, oldest, highest-balance, highest-order-value, first-alphabetical, or "most complete profile" record when several match. Every such heuristic retains a nonzero failure rate. The exam tests each variant as a distractor and rejects all.

No observable attribute reliably identifies which person is in the conversation. Customers may share names across households, have dormant and active accounts, or have closed accounts. Any heuristic substitutes a guess for confirmation. The only reliable move is to ask.

Boundary. Boundary: is the selection based on any inferred attribute? If yes, prohibited. If based on a customer-provided identifier that narrows to one, allowed. Nearby opposite: after the customer supplies an order number that matches exactly one record, proceeding is correct.

Recurring specifics. Heuristics rejected: most recent order, most active, oldest creation date, highest balance, highest dollar value, first alphabetical, most complete profile, household-secondary-flag exclusion. The 15% wrong-account rate recurs.

Wrong answers written against this rule

Proposal. combine recent activity, account age, order count into a better heuristic.

Why it attracts. "smarter".

Why it fails. still probabilistic; any heuristic fails.

When it would be right. never.

Proposal. confidence threshold 95% before proceeding.

Why it attracts. numeric gate.

Why it fails. self-confidence poorly calibrated for identity.

When it would be right. never.

How the same rule gets re-asked
  • - Most recent order selected. Correct: wrong. - Most complete profile selected. Correct: wrong. - Customer confirms email, single match. Correct: proceed.
R19

Blanket escalation of every multi-match case is over-escalation

Escalating to a human whenever duplicates appear, even for simple order-status questions, inflates escalation volume and slows routine cases. The exam shows this adding 18% to escalation volume. The correct design reserves human escalation for genuine human requests, while ambiguous matches are resolved by asking the customer.

The multi-match problem is solvable by the agent with one clarifying question; pushing every case to a human wastes the queue on a routine interaction. Escalation should be for cases needing human judgement, not for identity checks the agent can perform.

Boundary. Boundary: is the case a multi-match that a single question resolves? If yes, ask, do not escalate. If the customer then requests a human, escalate (Rule 1). Nearby opposite: a genuine policy gap discovered during the conversation still escalates, but for the gap reason, not the match.

Recurring specifics. +18% escalation volume, simple order-status questions. The correct combo answer selects both "reserve escalation for explicit human requests" and "request an additional identifier".

Wrong answers written against this rule

Proposal. always escalate duplicates to guarantee correct account.

Why it attracts. safety.

Why it fails. over-escalation bottleneck.

When it would be right. only if disambiguation fails.

Proposal. keep escalating every duplicate.

Why it attracts. cautious.

Why it fails. voluminous.

When it would be right. never.

How the same rule gets re-asked
  • - Duplicate found, simple status question. Correct: ask, resolve. - Duplicate found, customer wants human. Correct: escalate.
R20

Explicit escalation criteria belong in the system prompt as the proportionate first response

For a miscalibrated agent (escalating easy cases, attempting hard ones), the most effective fix is to add explicit escalation criteria plus few-shot examples to the system prompt. This is the proportionate first response before adding infrastructure such as classifier models, sentiment analysis, or separate routing services. Prompt optimisation precedes architectural change.

output.txt
text
ESCALATION CRITERIA - escalate when:
  - customer explicitly requests a human
  - policy is silent or ambiguous on the request (policy gap)
  - you cannot make meaningful progress after a genuine attempt
RESOLVE AUTONOMOUSLY when:
  - standard billing adjustment, password reset, order status, refund under limit
ANTI-PATTERNS - do NOT escalate based on:
  - customer sentiment or frustration
  - self-reported confidence score

The root cause is missing or unclear decision boundaries, not missing compute. A prompt edit directly addresses the boundary at zero infrastructure cost. Classifiers and sentiment systems are disproportionate and introduce new failure modes. The exam's recurring 55%-vs-80% first-contact-resolution scenario always answers "add explicit criteria with few-shot examples".

Boundary. Boundary: is the problem a calibration or boundary-clarity problem? If yes, prompt first. If it is a hard compliance requirement that "cannot be left to model discretion", Rule 27 applies (code enforcement). Nearby opposite: a genuinely irreversible operation needs a hook, not just a prompt.

Recurring specifics. The 55% first-contact-resolution versus 80% target recurs across many items. The four distractor families are always: few-shot prompt criteria (correct), self-confidence (wrong), separate classifier (wrong), sentiment analysis (wrong). The criteria list names: explicit human request, policy exception, inability to progress, plus resolve-autonomously cases like standard billing adjustments and password resets. Example system-prompt block:

Wrong answers written against this rule

Proposal. deploy a separate classifier trained on historical tickets.

Why it attracts. "ML solution".

Why it fails. over-engineered, inconsistent with main agent, disproportionate.

When it would be right. only after prompt optimisation proven insufficient.

Proposal. implement sentiment analysis.

Why it attracts. common.

Why it fails. anti-pattern, solves different problem.

When it would be right. never as the fix.

Proposal. self-reported confidence routing.

Why it attracts. metacognitive.

Why it fails. miscalibrated.

When it would be right. never.

How the same rule gets re-asked
  • - 58% FCR, photo replacements escalated. Correct: prompt criteria. - Inverted logic, easy escalated, hard attempted. Correct: prompt criteria.
R21

Few-shot examples teach generalizable boundary reasoning

Few-shot examples are not just enumerated cases; they demonstrate the reasoning behind edge-case decisions so the agent generalises to unseen scenarios. The exam shows an agent correctly escalating a loyalty-program dispute it was never shown, citing "policy is silent", because the examples taught the underlying principle.

Boundary cases are where criteria are ambiguous, so examples there calibrate judgment precisely. A well-chosen example of "refund for 8-month-old purchase, outside 90-day window, escalate" teaches the gap concept better than a rule alone. Generalisation is the proof the agent learned the principle, not memorised.

Boundary. Boundary: does the example show reasoning or just a label? Reasoning examples generalise; label-only examples do not. Nearby opposite: an agent that escalates only the exact enumerated cases has not generalised and would miss a novel gap.

Recurring specifics. Examples cited: standard duplicate charge resolves; policy exception plus security escalates; 8-month-old refund outside 90-day window escalates (boundary case). The unseen loyalty dispute is the generalisation proof.

Wrong answers written against this rule

Proposal. expand examples to list every possible gap scenario.

Why it attracts. coverage.

Why it fails. impossible and misses generalisation point.

When it would be right. never; teach principle.

Proposal. examples unnecessary, agent would escalate anyway.

Why it attracts. minimal.

Why it fails. agent was miscalibrated without them.

When it would be right. never.

How the same rule gets re-asked
  • - Two examples given, third unseen gap handled. Correct: generalisation. - No examples, novel gap missed. Correct: shows need for examples.
R22

Vague prompt instructions fail to calibrate escalation

Instructions like "only escalate high-confidence cases", "be conservative about escalation", "if you are unsure, escalate to a human", or "try to resolve issues before escalating" do not improve consistency. The exam shows agents with such instructions still miscalibrate. Vague directives leave the boundary undefined, so the model fills it with its own (wrong) judgment.

"Conservative" and "unsure" are not operational. Without concrete criteria and examples, the model cannot know which cases count, so it continues its prior behaviour. The fix is explicit, enumerable criteria plus examples, not stronger adjectives.

Boundary. Boundary: is the instruction concrete and exemplified? If no, it fails. If it names the three triggers and shows examples, it works. Nearby opposite: "escalate on explicit request, policy gap, or no progress" with examples is concrete and works.

Recurring specifics. Phrases: "be conservative about escalation", "only escalate high-confidence cases", "if unsure escalate", "try to resolve before escalating". All shown ineffective.

Wrong answers written against this rule

Proposal. add "be conservative" to the prompt.

Why it attracts. seems clearer.

Why it fails. still vague.

When it would be right. never as the sole fix.

Proposal. instruct to retry three times before any escalation.

Why it attracts. "reasonable".

Why it fails. arbitrary; some cases need immediate escalation.

When it would be right. only as a fallback safety net.

How the same rule gets re-asked
  • - "If unsure escalate" line. Correct: insufficient. - Explicit three-trigger criteria. Correct: effective.
R23

A structured handoff must be self-contained

When the human agent lacks access to the conversation transcript, the escalation payload must carry everything needed to act immediately: the verified customer identifier, the root cause, the amounts involved, and the recommended action. A self-contained summary prevents the customer from re-explaining and lets the human act in seconds.

result.json
json
{
  "customer_id": "CUST-4421",
  "escalation_reason": "refund_exceeds_authorization_limit",
  "root_cause": "Payment gateway timeout triggered retry, causing duplicate charge of $847.00",
  "refund_amount": "$847.00",
  "exceeds_agent_limit": true,
  "recommended_action": "Approve full refund to original payment method; customer not at fault"
}

The human receiving the handoff cannot see the chat or tool history. A sparse or reference-only handoff forces re-interview and re-investigation, adding minutes per ticket and frustrating the customer. The summary is the only information set the human gets, so it must be complete.

Boundary. Boundary: can the human act without the transcript? If the handoff is self-contained, yes. If it references turns or a transcript ID, no. Nearby opposite: if the human does have transcript access, a lighter summary may suffice, but the exam consistently assumes no access.

Recurring specifics. Required fields recur: customer_id, root_cause, refund_amount (or disputed amount), recommended_action, plus conversation_summary and attempted_resolutions. One item lists five fields: customer ID, summary, root cause, refund amount, recommended action. Example payload:

Wrong answers written against this rule

Proposal. pass only the customer's original message.

Why it attracts. minimal.

Why it fails. discards all investigation.

When it would be right. never.

Proposal. persist full transcript to DB, pass reference ID.

Why it attracts. audit-friendly.

Why it fails. indirect; human must query and re-read.

When it would be right. only as supplement, not replacement.

Proposal. attach confidence score.

Why it attracts. "how hard".

Why it fails. no case facts.

When it would be right. never as the substance.

How the same rule gets re-asked
  • - Five-field summary. Correct: self-contained. - Customer name plus "complex" note. Correct: insufficient.
R24

A raw transcript or transcript reference is not an acceptable handoff

Passing the full conversation transcript, or a reference like "see turn 15 for account details" or a session ID, is wrong when the human lacks transcript access. The exam treats "see conversation turn 15" as a handoff failure because the human cannot open the referenced log.

A 25-turn transcript with 40-field tool responses takes 10-15 minutes to parse and still requires synthesis the agent already did. A turn reference assumes access the human does not have. Both shift the agent's work back onto the human.

Boundary. Boundary: does the handoff assume transcript access? If yes, it fails. If it is a structured synthesis, it works. Nearby opposite: a structured summary that includes the same facts inline is correct even though it could have been in the transcript.

Recurring specifics. "see conversation turn 15 for account details", full raw transcript in a case_summary field, session ID for the human to query. All marked failures.

Wrong answers written against this rule

Proposal. attach full transcript so human has every detail.

Why it attracts. completeness.

Why it fails. overwhelming, not actionable.

When it would be right. only if human has direct access and wants it.

Proposal. transcript URL for human to read.

Why it attracts. link is easy.

Why it fails. assumes access and time.

When it would be right. never as the sole handoff.

How the same rule gets re-asked
  • - Turn reference. Correct: fails. - Full transcript dump. Correct: fails. - Structured five-field summary. Correct: passes.
R25

Handoff completeness enforced at the tool schema beats a prompt request

The escalate_to_human tool's input schema should require structured fields (issue_summary, attempted_resolutions, blocking_reason, relevant_order_ids). Making the handoff a first-class enforced part of the tool call is more reliable than a system-prompt note asking the agent to "be thorough".

Prompt requests are soft; the model may omit fields. A schema with required fields makes omission structurally impossible, echoing the broader "enforce at the interface, not via instruction" theme. The exam ranks schema enforcement above prompt-level "be thorough".

Boundary. Boundary: is completeness enforced by the tool contract? If yes, reliable. If only requested in the prompt, unreliable. Nearby opposite: a prompt "be thorough" with no schema still produces sparse handoffs.

Recurring specifics. escalate_to_human requires case_summary; engineering sets it to full transcript (wrong, Rule 24). Correct redesign: required structured fields in schema.

Wrong answers written against this rule

Proposal. add "be thorough" to system prompt.

Why it attracts. easy.

Why it fails. not enforced.

When it would be right. never as the sole mechanism.

Proposal. rely on transcript URL.

Why it attracts. light.

Why it fails. assumes access.

When it would be right. never.

How the same rule gets re-asked
  • - Schema-required fields. Correct: consistent. - Prompt-only request. Correct: inconsistent.
R26

Graceful degradation preserves gathered context on hard limits

When a token budget is exhausted mid-task, or the escalation tool is unreachable, the agent must not abort and discard verified identity and order details. It catches the error, returns structured context (failure type, what was attempted, partial results), and either retries, uses an alternative path, or performs a structured handoff. Abrupt termination forces the customer to restart.

The agent already did valuable work; discarding it wastes the customer's time and the agent's effort. Graceful degradation turns a failure into a recoverable state with a self-contained brief.

Boundary. Boundary: did a hard limit or tool failure occur after partial progress? If yes, degrade gracefully with a handoff. If the task completed, no escalation needed. Nearby opposite: a clean completion with no failure needs no handoff.

Recurring specifics. Token budget exhaustion with stop_reason still tool_use; escalate_to_human unreachable causing session abort. Correct: catch, summarise, hand off with customer ID and root cause so far.

Wrong answers written against this rule

Proposal. silently return empty escalation confirmation.

Why it attracts. "resolved".

Why it fails. lies to customer.

When it would be right. never.

Proposal. abort whole session on tool exception.

Why it attracts. simple.

Why it fails. discards verified context.

When it would be right. never.

Proposal. reduce timeout threshold.

Why it attracts. faster fail.

Why it fails. does not fix handling.

When it would be right. never.

How the same rule gets re-asked
  • - Token budget hit, stop_reason tool_use. Correct: graceful handoff. - Tool exception aborts session. Correct: wrong; preserve context.
R27

Compliance rules that cannot be left to model discretion need code-level enforcement

When a rule is mandatory and non-discretionary (for example, refunds over $500 must escalate, or irreversible schema changes need confirmation), prompt emphasis is insufficient. The correct design is a hook or interceptor in the tool-calling layer that blocks the action and invokes escalation in code, outside the model's reasoning loop. This yields zero failure by construction.

example.py
python
def process_refund_tool_call(amount, order_id):
    if amount > 500:
        block_tool_call()
        invoke_human_escalation(order_id, amount)
        return "Escalated to human agent per compliance policy"
    return execute_refund(amount, order_id)

LLMs are probabilistic; even clear prompt instructions produce a nonzero failure rate (items cite 3%). A hook enforces the check in code, so the model cannot bypass it. The exam's signal phrase is "this rule cannot be left to model discretion", which mandates a hard mechanism, not softer guidance.

Boundary. Boundary: is the rule a hard compliance or safety boundary? If yes, enforce in code. If it is a judgment call (gap vs covered), prompt criteria suffice. Nearby opposite: a policy gap that needs human judgement is handled by prompt criteria (Rule 20), not a hook, because the decision itself requires reasoning.

Recurring specifics. Refund over $500 must escalate; 3% failure under prompt-only. PreToolUse hook on close_encounter blocking red-flag cases. The hook returns the unmet condition to the agent and routes to handoff.

Wrong answers written against this rule

Proposal. tool returns an error "amount exceeds limit".

Why it attracts. informative.

Why it fails. model still decides what to do with it.

When it would be right. never as enforcement.

Proposal. strengthen prompt with "CRITICAL: NEVER".

Why it attracts. emphatic.

Why it fails. same failing class, louder.

When it would be right. never for hard compliance.

Proposal. few-shot examples at $400/$500/$600.

Why it attracts. illustrative.

Why it fails. reduces but does not eliminate failure.

When it would be right. only for judgment calibration, not hard limits.

How the same rule gets re-asked
  • - Prompt-only, 3% fail. Correct: insufficient. - Hook blocks over limit. Correct: deterministic.
R28

Irreversible high-impact operations require explicit human confirmation

Bulk cancellations, production database migrations, large wire transfers, and similar irreversible operations require an explicit human confirmation checkpoint before execution, regardless of how the request was initiated (including an authorised CI/CD trigger). The agent surfaces the full scope and financial impact and pauses. This is distinct from routine escalation: the operation is authorised but consequential, so confirmation, not handoff, is the control.

Irreversibility plus scale means a mistake cannot be cheaply undone. Pipeline authorization and dry-runs validate technical correctness but do not confirm human awareness and intent. The minimal-footprint principle says surface scope before acting.

Boundary. Boundary: is the action irreversible and high-impact? If yes, confirm scope explicitly. If reversible and low-impact, proceed. Nearby opposite: a single in-policy refund is resolved autonomously; only when over the compliance limit does it escalate (Rule 27).

Recurring specifics. 847 orders totalling $234,000 bulk cancel; 15 production tables renamed; wire for "T. Robinson". Correct: pause, summarise scope, require confirmation. Escalating the bulk cancel entirely is wrong (agent can do it after confirmation).

Wrong answers written against this rule

Proposal. execute since CI/CD authorised it.

Why it attracts. authorised.

Why it fails. authorization is not awareness.

When it would be right. never for irreversible.

Proposal. dry-run then auto-proceed.

Why it attracts. validates.

Why it fails. no human intent.

When it would be right. never.

Proposal. escalate to human specialist, abandon task.

Why it attracts. safe.

Why it fails. unnecessary; confirmation suffices.

When it would be right. only if truly beyond scope.

How the same rule gets re-asked
  • - Single in-policy refund. Correct: proceed. - 847-order cancel. Correct: confirm first.
R29

Tool result typing must separate access failure from valid empty result

A tool that returns an empty array because no record exists is a valid result, not a retriable failure. The agent's recovery logic must distinguish isError=false, resultCount=0 (query succeeded, nothing matched) from isError=true, isRetryable=true (could not reach the source). Treating "no matches" as a failure causes wasted retries and needless escalation.

The distinction is structural: one means "the customer does not exist", the other means "try again". Confusing them makes the agent retry a successful query or escalate a non-issue. Fixing it is a tool-design responsibility, not a prompt instruction.

Boundary. Boundary: is the empty result a successful query or a transport failure? If successful, accept it. If transport failure, retry then escalate. Nearby opposite: a timeout that a local retry fixes is a transport failure handled by retry logic (Rule 26).

Recurring specifics. get_customer returns empty array; agent retries 3 times then escalates; customer simply does not exist. Correct root cause: tool does not distinguish result types. Fix: structured isError flag.

Wrong answers written against this rule

Proposal. increase retry limit to 5.

Why it attracts. more attempts.

Why it fails. more wasted empties.

When it would be right. never.

Proposal. instruct prompt not to retry lookups.

Why it attracts. stops waste.

Why it fails. brittle; does not fix typing.

When it would be right. never.

Proposal. escalate threshold more aggressive.

Why it attracts. fewer retries.

Why it fails. wrong axis.

When it would be right. never.

How the same rule gets re-asked
  • - Empty result, distinct from timeout. Correct: accept. - Timeout, retry then escalate. Correct: transport failure.
R30

Authorization is a deterministic boundary, not model judgment

Access control (role, ownership, amount limits, action eligibility) must be enforced by the tool-owning service using propagated identity and scoped credentials, not by the model's interpretation or confidence. The model may propose an action; the service decides whether the authenticated actor is allowed. Confidence scores and prompts support detection but do not grant or deny authority.

Treating model judgment as the access layer lets an over-privileged shared credential reach tools it should not. Least-privilege credentials and identity propagation are independent of whether the model understood the conversation. The exam's trap is "strengthen the prompt" as the authorization fix.

Boundary. Boundary: is the question "may this actor do this?" If yes, enforce at the service with scoped credentials. If the question is "should the model escalate?", prompt criteria apply. Nearby opposite: a well-scoped refund tool that itself enforces the limit is correct; a prompt asking the model to "be careful" is not.

Recurring specifics. One shared service credential used for every employee; refund callable from conversation text alone. Correct fixes: propagate authenticated context to tool service for policy checks, and issue separate narrowly scoped credentials for read, refund, address-change. Self-reported confidence above 95% before refund is rejected as authorization.

Wrong answers written against this rule

Proposal. require 95% confidence before refund tool.

Why it attracts. numeric gate.

Why it fails. not a calibrated authorization signal.

When it would be right. never as access control.

Proposal. strengthen prompt with misuse examples.

Why it attracts. intent classification.

Why it fails. cannot stop malicious call to over-privileged tool.

When it would be right. only as support.

Proposal. daily anomaly report.

Why it attracts. detection.

Why it fails. responds after the fact.

When it would be right. as supplement only.

How the same rule gets re-asked
  • - Prompt-only guard. Correct: fails. - Scoped credential plus service check. Correct: holds.
R31

Multi-concern requests need decomposition, not partial handling

When a customer message contains several distinct issues (a refund inquiry, a subscription question, a payment update), the agent decomposes them, investigates each using shared context, and synthesises one unified resolution. It must not answer only the first issue and close, nor push the split back to the customer, nor escalate issues it could handle.

Silently dropping concerns abandons work and forces re-contact, harming first-contact resolution. Decomposition preserves autonomy while ensuring completeness. Persisting structured issue data across a long session also survives context limits.

Boundary. Boundary: are there multiple distinct concerns? If yes, decompose and handle each. If one concern, handle directly. Nearby opposite: a single concern that is a policy gap escalates; the other concerns in the same message are still decomposed and handled.

Recurring specifics. Three concerns in one message; agent answers only the billing dispute and closes. Correct root cause: lacks decomposition logic. Structured context layer carries order IDs, amounts, statuses per issue.

Wrong answers written against this rule

Proposal. ask customer to submit one concern per ticket.

Why it attracts. tidy.

Why it fails. worse experience.

When it would be right. never.

Proposal. handle only highest-value issue, escalate rest.

Why it attracts. triage.

Why it fails. escalates handleable work.

When it would be right. only genuine gaps.

Proposal. process in order, stop at context budget.

Why it attracts. bounded.

Why it fails. abandons work.

When it would be right. never.

How the same rule gets re-asked
  • - Two concerns, one handled. Correct: incomplete. - Three concerns, all resolved. Correct: decomposed.
R32

Safety-critical and topic-sensitive triggers need immediate empathetic handoff

Topic-sensitive situations (self-harm, crisis, medical emergency, red-flag clinical presentations) trigger immediate human intervention with a high-priority handoff that includes the warning and emergency context, plus an empathetic bridging message with resources while the human takes over. The detection here is explicit and safety-driven, not sentiment-based escalation of ordinary frustration.

These are high-stakes by nature; user safety outranks any automated attempt. The bridging message reduces risk during handoff latency. Keyword detectors must understand negation ("not an emergency") to avoid false positives.

Boundary. Boundary: is the topic safety-critical? If yes, immediate handoff with resources. If merely frustrated about a return, Rule 4 applies (resolve). Nearby opposite: ordinary anger about a shipping delay is resolved, not treated as a crisis.

Recurring specifics. "I want to hurt myself" phrase detector; red-flag clinical presentations (chest pain with radiation, stroke signs, suspected sepsis); "emergency" keyword with negation handling. Correct: immediate human handoff plus empathetic message and resources.

Wrong answers written against this rule

Proposal. provide helpline and continue chat.

Why it attracts. resource given.

Why it fails. does not stop AI interaction.

When it would be right. never; stop and connect.

Proposal. keyword "emergency" always escalates literally.

Why it attracts. bright line.

Why it fails. false positives on "not an emergency".

When it would be right. only with negation-aware classifier.

Proposal. tell user to calm down.

Why it attracts. none.

Why it fails. escalates frustration.

When it would be right. never.

How the same rule gets re-asked
  • - Negated "emergency". Correct: do not escalate. - Genuine red-flag symptom. Correct: handoff before closing.
Support agent stuck at 55 percent resolution that climbs to 82 percent
A production walkthrough with the reasoning chain made explicit.

A tier 1 customer support agent for an e-commerce platform reports 55 percent first-contact resolution against an 80 percent target. Investigation shows the agent escalates straightforward damage replacement cases while attempting to handle complex policy exception requests autonomously. Three patterns emerge. The agent escalates whenever the customer uses words like frustrated, annoyed, or upset regardless of whether the issue is resolvable. The agent escalates whenever it reports a self-confidence score below 0.7, which fires on most edge cases because the model is poorly calibrated. The agent attempts every policy gap autonomously and produces inconsistent answers across similar cases.

The fix rewrites the system prompt with explicit escalation criteria as a numbered list covering explicit human request, policy gap where documented policy is silent, and inability to make meaningful progress after a genuine attempt. The prompt explicitly names sentiment-based and confidence-based escalation as anti-patterns with reasoning such as frustration does not correlate with case complexity, a furious customer with a simple late delivery is easy to resolve while a calm customer with a policy gap needs human judgement.

Three few-shot examples demonstrate the calibration. A customer writes I am furious about this delivery delay, the third time. The agent acknowledges the frustration, offers a replacement shipment plus a 20 percent credit, and resolves autonomously, reasoning that explicit frustration with a simple resolvable issue does not trigger escalation. A customer asks can you match the price Amazon is offering on the same headphones. The agent detects a policy gap because competitor matching is not in documented policy and invokes the escalation tool with the competitor URL and an assessment that the policy is silent. A customer writes I want to talk to a person now. The agent immediately invokes the escalation tool with no investigation, reasoning that an explicit human request is honoured without delay.

A separate handling path covers ambiguous matching. When a name search returns three John Smith records the agent invokes request_clarification with a structured payload listing the three matches with email and order count and asks for an additional identifier rather than selecting heuristically. Over the next month first-contact resolution rises from 55 percent to 82 percent. Escalation volume drops by 40 percent because simple frustration cases are no longer escalated. Policy gap escalations increase slightly because explicit criteria make genuine gaps easier to identify, and structured handoffs reduce average human handle time.

Distinctions that decide answers

ThisNot thisHow to tell them apart
Explicit human requestFrustrated customer with resolvable issueAn explicit request uses phrases like I want to speak to a person or transfer me to a human. A frustrated customer expresses negative sentiment about a resolvable issue. The first triggers immediate escalation with no investigation, the second triggers acknowledgement plus resolution.
Policy gapPolicy violationA gap is silence in documented policy and requires human judgement. A violation is an explicitly addressed case with a documented answer the agent should communicate.
Inability to make progressPremature escalationInability means the agent attempted resolution and tools, retries, and accessible systems did not advance the case. Premature escalation means the agent gave up before trying or escalated on subjective difficulty without failed attempts.
Self-reported confidenceCalibrated confidenceSelf-reported is the model rating its own certainty on a 1 to 10 scale and is poorly calibrated. Calibrated is derived from external ground truth and maps reported scores to actual accuracy per field type per document type. Self-reported is unreliable for routing, calibrated is reliable.
Heuristic customer selectionClarification requestHeuristic selection picks one of multiple matching records by recency or activity. Clarification asks for an additional identifier. Heuristic risks privacy violations and wrong-account actions, clarification is the only safe response.
Prompt-based escalation criteriaClassifier-based escalationPrompt-based criteria with few-shot examples in the system prompt are the proportionate first response. Classifier-based escalation is an architectural addition only when prompt engineering has plateaued.

Traps

Escalate on frustration or negative sentiment

The tempting answer. Use frustration detection because high frustration intuitively feels correlated with difficulty and sounds rigorous.

Why it fails. Frustration measures emotional state not case complexity. A furious customer with a simple late delivery is easy to resolve, a calm customer with a policy gap is hard. Routing by sentiment inverts the correct priority and produces an agent that escalates easy cases.

What is correct. Acknowledge frustration and resolve the straightforward issue. Escalate only on the three valid triggers, not on how the customer feels.

Use self-reported confidence below 0.7 as an escalation trigger

The tempting answer. Filter by confidence because thresholding feels engineering grade and quantitative.

Why it fails. LLM self-reported confidence is poorly calibrated. The model is often incorrectly confident on hard cases and unnecessarily uncertain on straightforward cases. This is the exact failure where an agent escalates simple cases while attempting complex ones.

What is correct. Reject raw self-report as a primary trigger. Use calibrated signals or objective checks such as self-consistency and hedging detection, and keep confidence as a secondary routing signal only.

Try to help once before honouring an explicit human request

The tempting answer. Offer help first because that feels polite and efficient before handing off.

Why it fails. The customer has made a clear unambiguous request and any investigation step before honouring it wastes time, frustrates the user, and violates the absolute rule. On the exam any investigation before an explicit request is a critical failure.

What is correct. Escalate immediately with no investigation step and a structured handoff.

Select from ambiguous matches using most recent or most active record

The tempting answer. Pick the heuristic winner because that is faster than asking for clarification.

Why it fails. Heuristic selection risks privacy violations by exposing one customer's data to another and incorrect actions such as processing a refund on the wrong account.

What is correct. Invoke a clarification request listing the matches and ask for an additional identifier such as email, phone, or order number, and wait for the response before proceeding.

Deploy a separate classifier before trying prompt-based criteria

The tempting answer. Build a dedicated escalation classifier because a separate model sounds more sophisticated.

Why it fails. Prompt optimisation should always precede architectural changes. Few-shot examples in the system prompt directly address unclear decision boundaries without the operational overhead of a separate model.

What is correct. Add explicit criteria and few-shot examples first and reach for a classifier only after prompt engineering has plateaued.

Resolve autonomously when documented policy is silent

The tempting answer. Avoid escalation overhead by making a judgement call and being helpful when policy does not speak.

Why it fails. Policy gaps require human judgement about whether to make an exception. The agent cannot make policy on the fly and inconsistent answers across similar gaps erode trust.

What is correct. Escalate every policy gap with a structured handoff carrying the gap, customer context, and attempted actions.

Going deeper than the task statement
Adjacent material that shows up in harder scenario questions.
Three frustration cases the exam tests most aggressively

Frustrated but resolvable where you resolve with acknowledgement, reiterated human preference after help is offered where you escalate, and explicit human request from the first turn where you escalate immediately with no investigation.

Escalation Patterns
Self-consistency and hedging signals over self-report

Self-consistency runs the same prompt three times and escalates on disagreement, hedging detection flags uncertain language such as I think or possibly, and log-probability analysis inspects token distributions. Each is more reliable than asking the model to rate itself.

Confidence Scoring and Uncertainty Handling
Tiebreaker for conflicting escalation decisions in multi-agent setups

When two components disagree about whether to escalate, define a tiebreaker such as priority rules, timestamp ordering, or default to escalate so similar cases are handled consistently.

Multi-Agent Context Isolation and Coordination
Build it
Calibrate escalation from misrouted baseline to structured handoff
  1. Write a baseline system prompt with no explicit escalation criteria and run it against ten customer scenarios covering explicit requests, violations, gaps, frustrated but resolvable cases, and ambiguous matches, recording inconsistent decisions where frustration cases escalate and gaps are attempted autonomously.
  2. Rewrite the prompt with three clearly defined triggers with decision rules, a section naming sentiment and confidence as anti-patterns with reasoning, and the exact format of the escalation handoff.
  3. Add three few-shot examples with reasoning for a frustrated resolvable case, a policy gap, and an explicit human request from the first turn, each showing the correct decision.
  4. Add an ambiguous matching handler that detects multiple matches, never selects heuristically, and always returns a clarification request asking for an additional identifier.
  5. Re-run the ten scenarios and verify every explicit request triggers immediate escalation, every gap triggers structured escalation, every frustrated resolvable case triggers acknowledgement plus resolution, and every ambiguous match triggers clarification. Record the measurable rise in first-contact resolution.

Verify. Escalation decisions become consistent across the ten scenarios, frustration no longer inverts priority, ambiguous matches never risk a wrong-customer action, and the structured handoff reduces human handle time.

Answer real questions on this task
8 exam-style questions written against the rules above. You get the reasoning and a note on every wrong option after each answer.
Task 5.316 min

Error Propagation in Multi-Agent Systems

Propagate structured errors with partial results, distinguish access failures from valid empty results, and make gaps visible with coverage annotations.

What you need to know

Error propagation in multi-agent systems determines whether a failure in one component brings down the whole workflow or whether the system recovers gracefully with partial results. The exam tests the structural shape of error context, the two catastrophic anti-patterns that sit on opposite ends of handling, and a critical distinction that most developers get wrong on first reading: access failures versus valid empty results. Getting this pattern right means every failure carries enough context for the coordinator to decide intelligently, and getting it wrong means either silent data loss or disproportionate waste of completed work.

The correct pattern is structured error context. When a subagent fails it returns a structured object that enables the coordinator to make intelligent recovery decisions. The context must include four elements. First, failure type as a categorical label that drives recovery logic: transient failures such as timeouts, rate limits, or network blips that may succeed on retry, validation failures that need the query fixed, business failures such as rule violations that need escalation or alternative action, and permission failures that cannot be retried without authorisation changes. Second, what was attempted: the specific query, parameters, target system, and any identifiers, so searched academic database for renewable energy policy with date range 2022 to 2024 is actionable while search failed is not. Third, partial results gathered before failure: if a subagent retrieved three of five sources before timing out, those three are valuable and discarding them because the overall operation failed is wasteful. Fourth, potential alternative approaches: the subagent knows its domain and can suggest a different database, broader search terms, or cached results that may help the coordinator decide on recovery.

The two anti-patterns are catastrophic in different ways and sit on opposite ends of handling. Silent suppression is the worst: a subagent catches a timeout and returns results empty with status success. The coordinator believes the search ran and found nothing, will not retry, will not try alternatives, and produces a synthesis that silently omits an entire research area. The final output looks complete but is missing critical content. Silent suppression is especially dangerous because it is invisible: the output looks correct and the gap cannot be detected from the output alone. In a customer support context this might mean reporting no orders found when the order lookup system was actually down, leading the agent to tell the customer they have no account. Workflow termination is the other anti-pattern: killing the entire pipeline on a single failure. One subagent times out and the entire research pipeline crashes, throwing away results from four subagents that completed successfully. This is a disproportionate response with no recovery path. The correct middle ground is structured error propagation: the failing subagent reports what happened with partial results preserved, the coordinator assesses the damage, and the system continues with targeted recovery.

The access-failure versus valid-empty-result distinction is critical and the exam tests it directly. An access failure is when the tool could not reach the data source, such as a timeout, connection error, or permission denial. The search did not execute, so retry with the same or modified parameters should be considered. A valid empty result is when the tool reached the source and executed the query but found no matches. This is the answer, no retry is needed because the system worked correctly and there simply are no results for this query. Conflating them produces two opposite failure modes: treating access failures as valid empties means never retrying when you should, treating valid empties as access failures means wasting time retrying a query that will always return nothing. The fix is a structured response with explicit status, failureType, and shouldRetry fields so the coordinator can distinguish the two programmatically.

Coverage annotations are the structural mechanism for handling partial failures in synthesis. When a synthesis agent combines findings from multiple subagents the output should note which topic areas are well supported and which have gaps. If one subagent failed to retrieve sources on geothermal energy the synthesis should explicitly state section on geothermal energy is limited due to unavailable journal access during research. This is far better than silently omitting the topic, because a gap otherwise looks like the topic was not relevant rather than the source being unavailable. Local recovery for transient failures happens before propagation. Subagents implement local retry logic with exponential backoff and jitter, fallback sources, and degraded responses before propagating. Only persistent errors that survive local recovery should propagate, always with attempted action and partial results included, which reduces coordinator complexity because the coordinator does not manage retry for every transient failure across every subagent.

Structured error shape and the shouldRetry signal

The minimum fields for a structured error are status with success or error, failureType from transient, validation, business, and permission, attemptedAction as an object with tool, query, and parameters, partialResults as an array of any retrieved data, alternativeApproaches as an array of suggested recovery strategies, and shouldRetry as a boolean. The failureType enum drives coordinator logic: transient triggers retry with same or modified parameters, validation triggers a query fix attempt, business triggers escalation or alternative, and permission triggers immediate escalation without retry.

In the tool layer this surfaces with isError true, errorCategory from transient, permanent, auth, not-found, and validation, and isRetryable as the actionable boolean. A transient category with isRetryable true means the coordinator should retry, an auth category with isRetryable false means retrying will not help. The shouldRetry and isRetryable fields make the retry decision explicit rather than relying on the model to infer it from an error message, and the exam tests whether you route by those fields rather than by prose interpretation.

Local retry before propagation and exponential backoff

Local retry logic uses exponential backoff with jitter. A typical implementation attempts once immediately, then after 1 second plus jitter, then after 2 seconds plus jitter, then after 4 seconds plus jitter, giving up after three retries. The jitter prevents synchronised retry storms where multiple clients retry the same failing service at the same instant. When a Retry-After header is present the retry logic honours it instead of computing its own backoff.

The discipline is to handle transient failures locally and propagate only persistent ones. Most transient failures resolve on the first or second retry, so propagating immediately floods the coordinator with recoverable errors. Each subagent handles its own transient failures, preserves partial results across retries, and escalates only the persistent error with the full structured context.

Coverage annotations that make gaps visible by construction

Coverage annotations are appended to the synthesis output as a structured section listing each topic area with its data quality status: well supported, limited with reason, or unavailable with reason. Failed subagent topics are explicitly noted with the failure type and any alternative approaches that were attempted. The annotation is what makes a gap visible: without it a missing geothermal section looks like the topic was not relevant, with it the same gap reads as source unavailable and the downstream consumer knows not to trust absence as relevance.

External validation pipelines mirror the same pattern for Claude outputs. When a Claude output fails schema or semantic validation the pipeline returns a structured error with the specific validation message, the failed output, and a recommended fix. Model downgrade from Opus to Sonnet to Haiku and circuit breaker patterns provide the fallback and protection layer that complements propagation: the downgrade chain is invoked when a primary model returns repeated transient failures, and the circuit breaker opens after a threshold of failures, allows test requests after a cooldown, and closes on recovery to protect downstream services from cascading load.

Authoritative mechanism reference

The sections below expand the tested rules into the complete documented surface: ownership, lifecycle, version differences, production boundaries, and adjacent mechanisms.

Mechanism reference: Structured error context quartet

The reference page's central mechanism is a structured error object with four elements. In the lessons this becomes a concrete, codeable contract.

The first element is the failure type, expressed as errorCategory. The reference page collapses this to four values (transient, validation, business, permission), but the project lesson tool-error-handling defines a larger, still application-defined enum: transient, permanent, auth, not-found, validation, rate-limit, permission, business. The Anthropic tool-use documentation does not mandate a category vocabulary; it standardizes only the boolean is_error flag on the tool_result block and leaves the content shape to the application.

The second element is what was attempted: the concrete tool name, query, parameters, and target system. The lesson tool-error-handling captures this in context.resource (URL, DB table, file path) and context.input (the sanitized input used), plus context.attemptNumber. A "search failed" string carries none of this; a payload naming tool: search_academic_db, query: "renewable energy policy", dateRange: 2022-2024 gives the coordinator something to act on.

The third element is partial results gathered before failure. The lesson tool-result-handling models this with a status: "partial" shape that carries successes and errors arrays, and the tool-error-handling graceful-degradation example returns per-section status so the coordinator knows exactly which sections succeeded. Discarding three retrieved sources because the fifth timed out wastes completed work and invites re-rate-limiting.

The fourth element is potential alternative approaches, captured in context.suggestion (the lesson's name) or alternativeApproaches (the reference page's name). The reference page's example suggests "Retry with narrower date range", "Search alternative database", and "Use cached results". The lesson frames this as giving the coordinator domain knowledge it does not have: the subagent knows its sources, the coordinator does not.

The minimum wire shape the lesson requires is isError: true, errorCategory, isRetryable, and message. Everything else is optional but recommended.

Mechanism reference: The tool_result is_error wire signal

The Anthropic tool-use documentation defines the boundary cleanly. A tool_result content block carries tool_use_id (matching the tool_use block that produced it), an optional content (a string, a list of content blocks, or document blocks), and an optional is_error boolean. When is_error is true, Claude "knows the tool invocation failed and uses the error content to reason about what went wrong and what to do next."

This is the first of the six required code shapes: a tool result returned as a structured error with is_error set and a machine-readable payload the model can act on, not a thrown exception and not a bare string.

example.ts
typescript
// Application tool handler returns a structured error to the model.
// The model receives this inside a tool_result block and reasons from it.
async function searchAcademicDb(query: string, dateRange: string): Promise<{
  role: "user"
  content: Array<{
    type: "tool_result"
    tool_use_id: string
    is_error?: boolean
    content: string
  }>
}> {
  try {
    const hits = await upstreamDb.search(query, dateRange)
    return {
      role: "user",
      content: [{
        type: "tool_result",
        tool_use_id: "toolu_01A9q...",
        // Valid empty result: reachable source, executed, no matches.
        content: JSON.stringify({
          status: "success",
          results: [],
          totalMatches: 0,
          message: `Query executed against reachable index. No records matched "${query}".`
        })
      }]
    }
  } catch (err) {
    const classified = classifyUpstreamError(err) // returns errorCategory + isRetryable
    return {
      role: "user",
      content: [{
        type: "tool_result",
        tool_use_id: "toolu_01A9q...",
        is_error: true,
        // Machine-readable payload the model can branch on.
        content: JSON.stringify({
          isError: true,
          errorCategory: classified.errorCategory, // "transient" | "permission" | ...
          isRetryable: classified.isRetryable,
          attemptedAction: { tool: "search_academic_db", query, dateRange },
          partialResults: classified.partialHits,
          alternativeApproaches: classified.suggestions,
          message: classified.humanReadable
        })
      }]
    }
  }
}

The documentation adds a critical instruction: write instructive error messages. Instead of a generic "failed", include what went wrong and what the model should try next, for example "Rate limit exceeded. Retry after 60 seconds." This is the documented posture that an error should be returned to the model as content it can reason about rather than thrown away. The model then incorporates the error into its response and adapts.

Two formatting rules are enforced by the API, not advisory: every tool_result block must immediately follow the assistant's tool_use message, and inside the user message the tool_result blocks must come first, with any plain text after them. Violating this returns a hard 400.

Mechanism reference: Error taxonomy that drives the retry decision from classification

The second required code shape is an error taxonomy where retryability is derived from the category, never from the message text. This matters because the same words appear across categories: "Operation failed" could be a transient timeout, a validation miss, or a permission block. Classifying from text invites mis-retries. The taxonomy below expands on the lesson tool-error-handling enum and attaches a retry rule to each value.

example.ts
typescript
// Error taxonomy: retry decision is derived from the category, never the message.
export type ErrorCategory =
  | "transient"      // infra blip, timeout, 503, 529: retry with backoff
  | "rate-limit"     // 429: retry after the indicated delay
  | "validation"     // 400/422: do NOT replay; correct input, then retry
  | "auth"           // 401/403: not retryable from the caller; escalate
  | "permission"     // policy/role block: not retryable; escalate
  | "not-found"      // 404 on a resource: not retryable unchanged
  | "business"       // policy cap, filing window: not retryable
  | "permanent"      // quota exhausted, legal block: not retryable

export interface ClassifiedError {
  errorCategory: ErrorCategory
  isRetryable: boolean
  retryAfterMs?: number
  humanReadable: string
  suggestion?: string
}

// The single decision point. Callers branch on this, not on string matching.
export function decideRetry(err: ClassifiedError): "retry" | "retry-after" | "correct" | "escalate" | "abort" {
  if (err.errorCategory === "transient") return "retry"
  if (err.errorCategory === "rate-limit") return err.retryAfterMs ? "retry-after" : "retry"
  if (err.errorCategory === "validation") return "correct"     // fix input, then retry
  if (err.errorCategory === "auth" || err.errorCategory === "permission") return "escalate"
  if (err.errorCategory === "not-found" || err.errorCategory === "business" || err.errorCategory === "permanent") return "abort"
  return "abort"
}

// Example: a 429 never reaches the message-parsing path.
const rateLimited: ClassifiedError = {
  errorCategory: "rate-limit",
  isRetryable: true,
  retryAfterMs: 60_000,
  humanReadable: "Rate limit exceeded on academic database.",
  suggestion: "Wait 60s, then retry the same query."
}
// decideRetry(rateLimited) === "retry-after", regardless of any free-text wording.

This code realizes the rule "Retryability is determined by category, not by blanket policy." A validation error with isRetryable: false means replaying the identical payload is futile; correcting the input and re-issuing is the only productive path. The lesson tool-error-handling states this directly and the lesson error-handling warns that a 400 context_length_exceeded must never be retried with the same oversized prompt.

Mechanism reference: HTTP-level API error surface

The reference page says to ground the API error surface from the errors page. The live errors page lists the full HTTP status code set and the type value returned in the JSON error object. Every error response has the shape { "type": "error", "error": { "type": "<error_type>", "message": "..." }, "request_id": "..." }.

The documented status codes and their type values are:

  • 400 - invalid_request_error: an issue with the format or content of the request. Also used for other 4xx not separately listed. A 400 is also returned when an organization or workspace spend limit is reached (except Claude Code workspace spend limits, which return 429).
  • 401 - authentication_error: problem with the API key (malformed, revoked, expired) or AWS credentials.
  • 402 - billing_error: issue with billing or payment.
  • 403 - permission_error: the API key lacks permission for the resource.
  • 404 - not_found_error: the requested resource was not found.
  • 409 - conflict_error: the request conflicts with the current state of a resource; resolve the conflict, then retry.
  • 413 - request_too_large: exceeds the maximum request size. The Messages API limit is 32 MB.
  • 429 - rate_limit_error: a rate limit, a usage-tier monthly spend cap, or a Claude Code workspace spend limit was hit.
  • 500 - api_error: an unexpected internal error. Retry with exponential backoff.
  • 504 - timeout_error: the request timed out while processing.
  • 529 - overloaded_error: the API is temporarily overloaded.

The transient versus permanent split is explicit in the documentation. The official SDKs "automatically retry transient failures (such as connection errors, rate limits, and 5xx server errors) with exponential backoff, twice by default, honoring the retry-after header when present." This tells us which classes are transient by construction: connection errors, rate limits (429), and 5xx (500, 504, 529). The 4xx family (400, 401, 402, 403, 404, 409, 413) is not in the SDK's automatic-retry set and is, in general, permanent with respect to an unchanged request.

The documented advice for 429 is specific. A rate_limit_error may carry a retry-after header; a tier spend-cap 429 has no retry-after header and "keeps failing until access resumes." The SDK honors the retry-after header when present. The correct posture is: on a 429 with a retry-after value, wait that duration and retry; on a tier spend-cap 429 without one, do not retry blindly because it will keep failing.

The documented advice for 5xx is exponential backoff. The errors page says for 500 "Retry the request with exponential backoff; if the error persists, contact support with the request ID", and for 504 it suggests streaming for long-running requests. The lessons echo this: error-handling maps 500 and 504 to "Yes (with backoff)" and retry-strategies maps 500 to a short backoff and 503 to a 1s backoff over 3 attempts, with 529 benefiting from a longer initial delay because it signals sustained capacity pressure.

The critical documented rule for request-shape errors: a 400 invalid_request_error must not be retried unchanged. The errors page lists many 400 variants that are inherently request-shape problems, including prefill not supported, thinking blocks modified, extended thinking not supported on a model, and context_length_exceeded (which the lesson error-handling calls out explicitly: never retry the same oversized prompt, reduce input instead). Because the same oversized or malformed body will fail identically, automatic retry of 4xx is wrong by design.

The third required code shape is an HTTP-level handler that honors this documented posture for a rate-limit response versus a server error versus a client error.

example.ts
typescript
// HTTP-level handler honoring the documented posture from the errors page.
// Transient (429/5xx): backoff and retry. Client (4xx): classify, never blind-retry.
import type { ErrorCategory } from "./taxonomy"

interface ApiErrorShape {
  type: string          // "error"
  error: { type: string; message: string }
  request_id: string
}

async function callClaudeWithPolicy(req: RequestInit): Promise<Response> {
  const MAX_ATTEMPTS = 4
  let delayMs = 1000
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
    const res = await fetch("https://api.anthropic.com/v1/messages", req)
    if (res.ok) return res

    const body = (await res.json().catch(() => null)) as ApiErrorShape | null
    const apiType = body?.error?.type

    // 429 rate limit: honor Retry-After if present, else backoff.
    if (res.status === 429) {
      const retryAfter = res.headers.get("retry-after")
      const wait = retryAfter ? Number(retryAfter) * 1000 : delayMs
      if (attempt === MAX_ATTEMPTS) throw mapToClassified(res.status, apiType, body)
      await sleep(wait)
      delayMs = Math.min(delayMs * 2, 32_000)
      continue
    }

    // 5xx server errors: exponential backoff, transient by documentation.
    if (res.status >= 500 && res.status <= 599) {
      if (attempt === MAX_ATTEMPTS) throw mapToClassified(res.status, apiType, body)
      await sleep(delayMs)
      delayMs = Math.min(delayMs * 2, 32_000)
      continue
    }

    // 4xx client errors: request-shape problems. Do NOT retry unchanged.
    // Map to a typed, non-retryable error the caller can branch on.
    throw mapToClassified(res.status, apiType, body)
  }
  throw new Error("unreachable")
}

function mapToClassified(status: number, apiType: string | undefined, body: ApiErrorShape | null): ClassifiedError {
  if (status === 429) return { errorCategory: "rate-limit", isRetryable: true, retryAfterMs: 0, humanReadable: body?.error?.message ?? "rate limited" }
  if (status >= 500) return { errorCategory: "transient", isRetryable: true, humanReadable: body?.error?.message ?? "server error" }
  // 400 invalid_request_error, 401 auth, 403 permission, 404 not_found, 413 too large
  if (apiType === "authentication_error") return { errorCategory: "auth", isRetryable: false, humanReadable: body?.error?.message ?? "auth error" }
  if (apiType === "permission_error") return { errorCategory: "permission", isRetryable: false, humanReadable: body?.error?.message ?? "permission error" }
  if (apiType === "not_found_error") return { errorCategory: "not-found", isRetryable: false, humanReadable: body?.error?.message ?? "not found" }
  if (apiType === "invalid_request_error" || apiType === "request_too_large") return { errorCategory: "validation", isRetryable: false, humanReadable: body?.error?.message ?? "bad request" }
  return { errorCategory: "permanent", isRetryable: false, humanReadable: body?.error?.message ?? "client error" }
}

This handler makes the documented posture executable: 429 and 5xx loop with backoff (and the SDK would do this for you by default with two retries), while 4xx throws immediately as a typed, non-retryable error. The lesson retry-strategies reinforces that 400 errors (including context_length_exceeded) are NOT retryable and that retrying them wastes tokens.

Mechanism reference: Partial-result envelopes from a fan-out step

When a coordinator fans out to several subagents or parallel tools, one branch may fail while others succeed. The correct posture is to return an envelope that declares which branches succeeded and which failed and remains consumable, rather than discarding the successes or aborting the whole fan-out. This is the fourth required code shape.

example.ts
typescript
// Fan-out result envelope: declares per-branch outcome, stays consumable.
interface BranchResult {
  branchId: string
  source: string
  status: "ok" | "failed" | "empty"   // empty = valid no-match, not a failure
  errorCategory?: ErrorCategory
  isRetryable?: boolean
  items?: unknown[]
  partialHits?: unknown[]
  attempt?: { tool: string; query: string }
  suggestion?: string
}

interface FanOutResult {
  status: "partial" | "complete" | "failed"
  branches: BranchResult[]
  summary: string
}

async function researchFanOut(queries: Array<{ branchId: string; source: string; query: string }>): Promise<FanOutResult> {
  const settled = await Promise.allSettled(
    queries.map((q) => runBranch(q))
  )

  const branches: BranchResult[] = queries.map((q, i) => {
    const r = settled[i]
    if (r.status === "fulfilled") {
      const out = r.value
      if (out.items.length === 0) {
        return { branchId: q.branchId, source: q.source, status: "empty", items: [] }
      }
      return { branchId: q.branchId, source: q.source, status: "ok", items: out.items, partialHits: out.partialHits }
    }
    const err = classifyUpstreamError(r.reason)
    return {
      branchId: q.branchId,
      source: q.source,
      status: "failed",
      errorCategory: err.errorCategory,
      isRetryable: err.isRetryable,
      attempt: { tool: q.source, query: q.query },
      suggestion: err.suggestion,
      partialHits: err.partialHits
    }
  })

  const failed = branches.filter((b) => b.status === "failed").length
  const empty = branches.filter((b) => b.status === "empty").length
  const ok = branches.filter((b) => b.status === "ok").length
  const status: FanOutResult["status"] = failed === 0 ? "complete" : ok + empty > 0 ? "partial" : "failed"

  return {
    status,
    branches,
    summary: `${ok} branch(es) ok, ${empty} empty (valid no-match), ${failed} failed. ` +
      (failed > 0 ? "Failed branches carry errorCategory and suggestions for targeted recovery." : "")
  }
}

This envelope lets the coordinator keep the ok and empty branches, retry only the failed ones (using isRetryable and attempt), and synthesize with the gap explicit. It directly avoids the workflow-termination anti-pattern. The lesson tool-result-handling models the same idea with status: "partial" and separate successes/errors arrays.

Mechanism reference: Coverage annotations on a synthesized result set

The reference page's coverage annotation requirement is the fifth required code shape: a consumer must not be able to mistake absence of data for absence of the thing. The annotation lives in the output schema, not as optional prose. It pairs each topic area with a data-quality status and a reason when the source was unavailable.

example.ts
typescript
// Coverage annotation: structural, not prose. Consumer cannot misread gaps.
type CoverageStatus = "well-supported" | "partial" | "limited" | "unavailable"

interface CoverageEntry {
  topic: string
  status: CoverageStatus
  reason?: string          // present only when status !== "well-supported"
  source?: string
}

interface SynthesisReport {
  sections: Array<{ title: string; body: string; coverage: CoverageEntry }>
  coverageMap: CoverageEntry[]
}

// Example entry for a topic whose journal source timed out:
const geothermal: CoverageEntry = {
  topic: "geothermal energy",
  status: "limited",
  reason: "Journal access timed out during research; only 2 of 5 sources retrieved.",
  source: "academic_db"
}
// Contrast: a genuinely empty finding is "well-supported" with no reason,
// because the source was reachable and answered "no matches".
const cobol: CoverageEntry = { topic: "cobol engineers in DB", status: "well-supported" }

The lesson tool-error-handling uses degraded and unavailable as section statuses and the lesson tool-result-handling uses per-record _dataQuality. The coverage annotation is the synthesis-side expression of the same idea: structural status per topic so a gap reads as a gap, never as irrelevance.

Mechanism reference: Exponential backoff, jitter, and circuit breakers

Local retry absorbs transient faults before escalation. The documented SDK behavior is exponential backoff, twice by default, honoring retry-after. The lesson retry-strategies specifies the schedule 1s, 2s, 4s, 8s, with jitter to prevent synchronized retries, and a cap at maxAttempts (3 for real-time, 5 for background). A circuit breaker handles systemic failure: after a threshold of consecutive failures it opens, failing fast instead of hammering a dead dependency, then probes half-open.

The lesson fallback-patterns adds the layered defense: model downgrade (Opus to Sonnet to Haiku), degraded output mode using cached or simplified responses, and the closed-to-open-to-half-open circuit breaker. Fail fast for non-retryable errors (invalid input, auth); degrade gracefully for transient ones.

Mechanism reference: Streaming failure modes and the truncated stream

Streaming introduces a class of failure that batch calls do not: a call can succeed partially, drop mid-stream, or appear to succeed while missing final tokens. The lesson streaming-reliability enumerates the modes: incomplete chunks (a dropped connection mid-event yields truncated JSON), dropped connections (a read timeout with no event for longer than the inter-token interval), a lost message_stop event (the client waits forever or treats the buffer as complete), and partial tool calls (a content_block_delta stream cut before content_block_stop).

What a truncated stream means for downstream consumers is the key point for this task. A partial JSON like {"status": "approved", "amount": 4 has no closing brace and cannot be parsed; a partial tool call has the tool name but not the parameters needed to execute it. If the consumer treats the buffer as complete, it acts on missing content as if it were the whole answer, which is the silent-suppression anti-pattern in streaming form. Mitigations from the lesson: buffer events until complete, never execute a tool call until content_block_stop arrives, use a partial: true flag for intermediate results so the model does not act on incomplete data, detect a lost message_stop via idle timeout and verify completeness, and checkpoint long streams so resumption starts from the last good point. The Anthropic errors page adds that an error after a 200 SSE response does not follow the standard HTTP mechanisms and must be handled via error events in the stream.

The lesson tool-result-handling also notes that streaming tool results should carry partial: true so Claude knows the result is intermediate and will not mistake it for the final answer.

Mechanism reference: Access failure versus valid empty result, in code

The reference page's most-tested distinction is the sixth required code shape: access failure versus valid empty result, expressed so one path retries and the other does not. The lesson error-handling frames the cut as "an empty result and a failed operation are not the same thing." The tool-result-handling lesson is explicit that null, {}, or an empty array means success with no data and never means failure.

example.ts
typescript
// Access failure vs valid empty result, discriminated at the source tool.
async function lookupOrders(customerId: string): Promise<{
  is_error?: boolean
  content: string
}> {
  let response: Response
  try {
    response = await fetch(`/api/orders?customer=${customerId}`, { signal: AbortSignal.timeout(5000) })
  } catch (err) {
    // Access failure: the call never executed or never reached the source.
    // This path RETRIES (transient) or escalates (auth/permission).
    return {
      is_error: true,
      content: JSON.stringify({
        isError: true,
        errorCategory: "transient",          // timeout / connection drop
        isRetryable: true,
        attemptedAction: { tool: "lookup_orders", query: customerId },
        message: "Order lookup timed out before reaching the database."
      })
    }
  }

  if (response.status === 200) {
    const data = await response.json()
    if (Array.isArray(data.orders) && data.orders.length === 0) {
      // Valid empty result: reachable source, executed, matched nothing.
      // This path does NOT retry. It is the answer.
      return {
        content: JSON.stringify({
          status: "success",
          results: [],
          totalMatches: 0,
          message: `Query executed successfully. No orders found for customer ${customerId}.`
        })
      }
    }
    return { content: JSON.stringify({ status: "success", results: data.orders, totalMatches: data.orders.length }) }
  }

  // 401/403/404 etc: access failure with a typed category, still is_error.
  return {
    is_error: true,
    content: JSON.stringify(mapHttpToClassified(response.status))
  }
}

The decision is encoded at the boundary: a caught timeout becomes is_error: true with isRetryable: true, while an empty 200 array becomes status: "success" with zero items and no retry flag. No downstream text parsing is needed. This is exactly the source-typing rule from the forensics file: disambiguation must happen at the source tool, not via downstream inference.

Mechanism reference: Idempotency and correlation identifiers

Retry safety depends on whether re-issuing a request can double-apply a side effect. The reference page treats returning an empty success after a timeout as bad practice; the deeper reason is that a timeout on a write leaves the outcome unknown, so a blind retry can duplicate the effect. The fix has two parts. First, classify the operation: reads and lookups are naturally retry-safe, while writes need protection. Second, attach a correlation identifier (idempotency key) so the upstream can suppress a duplicate.

The lesson retry-strategies and the forensics rule on indeterminate outcomes both require verify-before-retry for writes that may have partially executed. The forensics file names this as "indeterminate outcomes require verify-before-retry, not blind retry" and notes that a write known to be idempotent via a correlation token should be retried with the same key.

example.ts
typescript
// Idempotency key so a retried write is not double-applied.
interface ChargeRequest {
  amount: number
  customerId: string
  idempotencyKey: string   // stable per business operation, e.g. `charge-${orderId}`
}

async function chargeWithSafeRetry(req: ChargeRequest): Promise<ChargeOutcome> {
  for (let attempt = 1; attempt <= 3; attempt++) {
    try {
      const res = await paymentsApi.post("/charge", req, {
        headers: { "Idempotency-Key": req.idempotencyKey }
      })
      return { ok: true, chargeId: res.chargeId }
    } catch (err) {
      const classified = classifyUpstreamError(err)
      if (classified.errorCategory === "transient") {
        // Before retry, verify: did the previous attempt actually post?
        const existing = await paymentsApi.get(`/charge/by-key/${req.idempotencyKey}`)
        if (existing.found) return { ok: true, chargeId: existing.chargeId } // already applied
        await sleep(backoff(attempt))
        continue
      }
      if (classified.errorCategory === "validation" || classified.errorCategory === "permission") {
        return { ok: false, reason: classified }   // never retry unchanged
      }
      throw classified
    }
  }
  throw new Error("retry budget exhausted")
}

The idempotency key lets the upstream suppress duplicate applications, and the verify-before-retry step means a timeout does not become a double charge. This is the documented safe path for indeterminate writes.

Mechanism reference: Connecting timeout-as-empty-success to a typed failure

The reference material treats returning an empty successful result after a timeout as merely bad practice. The stronger statement, grounded in the error taxonomy, is that the fix is a typed failure the caller can branch on. An empty success collapses two opposite states (no data because unreachable versus no data because correctly matched nothing) into one shape, destroying the only signal the coordinator needs. The lesson error-handling shows the dangerous pattern directly: return [] after a silent catch "looks like empty result" but is a failure wearing the same shape. The correct replacement is the is_error: true structured envelope from the access-failure code above, where errorCategory and isRetryable let the coordinator choose retry, reroute, or disclose a gap. A typed failure is not just "better hygiene"; it is the precondition for every other recovery rule, because once an access failure is collapsed into an empty success, no downstream retry, category check, or coverage annotation can recover the lost signal.

Mechanism reference: Connecting workflow termination to multi-agent degraded delivery

The reference material's second anti-pattern is terminating the whole pipeline on one branch failure. The documented multi-agent guidance reframes this as partial results plus degraded delivery. The Anthropic multi-agent research engineering post describes an orchestrator-worker pattern: a lead agent spawns 3 to 5 subagents in parallel, each returns findings, and the lead synthesizes. It states plainly that "agents are stateful and errors compound" and that the system "can resume from where the agent was when the errors occurred" rather than restart, and that "letting the agent know when a tool is failing and letting it adapt works surprisingly well." Subagents also write outputs to an artifact store so completed work persists independently and is not lost when one branch struggles.

That guidance is the documented basis for the middle ground: the failing branch reports what happened (a structured error with partials), the coordinator assesses the damage, and the system continues with partial results or targeted recovery. The lesson fallback-patterns supplies the decision rule at the boundary: fail fast for non-retryable errors, degrade gracefully for transient ones, and always tell the consumer what happened. The forensics rule on degraded partial delivery states that the synthesis path accepts the successful subset, marks missing slices as gaps with source and reason, and does not block on the failed slice. Terminating the whole pipeline is the violation of exactly this documented posture.

Ownership map

Each behaviour in error propagation has a clear owner. Mixing layers is the root of most failures.

  • The SDK owns automatic transient retry. The errors page states the official SDKs "automatically retry transient failures (such as connection errors, rate limits, and 5xx server errors) with exponential backoff, twice by default, honoring the retry-after header when present." This is SDK-owned; application code should not re-implement it unless it needs different limits (via the max-retries option).
  • The application tool handler owns the is_error flag and the structured payload. The tool-use documentation defines is_error as an optional boolean on the tool_result block and leaves the content shape to the application. The handler is the component that observes the upstream outcome, so it must type access failure versus valid empty, set errorCategory and isRetryable, and return the error as content Claude can reason about. The lesson tool-error-handling makes this the handler's responsibility.
  • The model (Claude) owns the recovery decision from the typed error. Given is_error: true with a category and a suggestion, Claude decides to retry, correct the input, reroute, or explain to the user. The documentation notes that for an invalid tool call (missing parameter) Claude retries 2 to 3 times with corrections, and that writing instructive error messages lets Claude "recover or adapt without guessing." The model does not own transport retry of the HTTP call (that is the SDK); it owns the agent-level next action.
  • The orchestrator owns classification aggregation, fan-out isolation, recovery selection, and coverage annotation in synthesis. The multi-agent research post describes the lead agent as the coordinator that synthesizes subagent findings and decides whether more research is needed. Per-branch error isolation and the decision to retry only failed branches are orchestrator-owned.
  • The infrastructure (upstream service, network) owns the raw failure signal: rate limits, 5xx, timeouts. The application's job is to translate that signal into the typed envelope, not to invent it.

Version and terminology currency

The reference page reflects an older, narrower vocabulary than the current product surface.

  • Failure-type vocabulary. The reference page uses four categories (transient, validation, business, permission). The current lesson tool-error-handling uses eight (transient, permanent, auth, not-found, validation, rate-limit, permission, business), and the API errors page uses its own type set (invalid_request_error, authentication_error, permission_error, not_found_error, rate_limit_error, api_error, timeout_error, overloaded_error, and others). A candidate should answer with the documented category that matches the scenario, and recognize that the four-type model is a simplification of the richer surface.
  • SDK default retry count. The errors page documents automatic retry "twice by default." Earlier guidance in some community material implied a single retry or a different count. Use the documented "twice by default, exponential backoff, honoring retry-after."
  • The 529 overloaded_error is an Anthropic-specific status not present in generic HTTP vocabularies. The lesson retry-strategies treats it as overload needing a longer initial delay. A candidate should recognize 529 as a retryable transient.
  • 409 conflict_error is documented as retryable only after resolving the conflict, which is a nuance beyond the reference page's four types.
  • The is_error flag on tool_result is stable and current; it has not changed name. The structured errorCategory/isRetryable fields remain application convention, not API-mandated, per the tool-error-handling lesson.

Official versus community divergence

  • Structured error versus generic string. Community code often returns "search unavailable" or "Operation failed". The documentation and lessons are explicit that generic strings are an anti-pattern because they strip category, retryability, and attempted action. Answer with the structured shape.
  • Validation retryability split. The forensics file flags a genuine tension: some items treat a malformed-input validation error as isRetryable: true (so the model can correct and retry), while the prevailing lesson guidance treats validation as isRetryable: false for an identical replay and only retryable after correction. The resolution that documentation supports: isRetryable: false refers to replaying the same payload unchanged; correction-then-retry is a different path the model initiates. Mark this as a terminology split, not a contradiction, and answer with "correct the input, then retry" for validation.
  • Silent suppression for UX. Some community patterns return empty success after a timeout "so the page renders." The lessons and the API guidance treat this as the worst anti-pattern regardless of UX intent, because it removes all recovery signal. Documentation wins.
  • Four-type vs eight-type taxonomy. Where community summaries present only four categories, the documented lesson and API surface enumerate more. Use the richer set when a scenario names a specific status such as 404 (not-found) or 402 (billing).

Beyond the task statement

Adjacent material the lessons cover that the reference page omits, each with its slug and why it matters for error propagation.

  • error-handling (production domain). The canonical access-failure-versus-empty-result treatment, the HTTP error table (429/500/504 transient, 401/400 non-retryable), and the warning that context_length_exceeded must never be retried unchanged. Directly underpins the taxonomy and the 4xx rule.
  • tool-error-handling (tool-use domain). The eight-value error taxonomy, the three-layer error model (raw in code, structured in tool_result, natural language in Claude's response), exponential backoff with jitter, graceful degradation with ok/degraded/unavailable section status, and the multi-step rollback pattern. This is the spine of the whole task.
  • tool-result-handling (tool-use domain). The tool_result block mechanics, the is_error flag being separate from content, the status: "partial" shape, PostToolUse hooks for normalization (which must never drop error semantics), and streaming partial: true. Supplies the wire-level detail the reference page lacks.
  • retry-strategies (reliability domain). The 1s/2s/4s/8s backoff schedule, jitter, Retry-After handling per status, the 429/500/503/529 table, circuit breakers for systemic failure, and the real-time (3) versus background (5) attempt counts. Fills in the "local recovery" element with concrete numbers.
  • fallback-patterns (reliability domain). Model downgrade (Opus to Sonnet to Haiku), degraded output mode, circuit breaker states, fail-fast versus degrade-gracefully, and the terminal fallback that must always succeed. Connects error propagation to what the user actually receives when a branch fails.
  • validation-pipelines (reliability domain). After maxRetries, the pipeline must "gracefully degrade, never silently return invalid data," and swallowing validation failures silently is called out as propagating errors silently. Reinforces that validation failures are surfaced, not hidden.
  • streaming-reliability (reliability domain). SSE failure modes, message_stop loss, partial JSON and partial tool calls, checkpoint/resume, and the partial: true flag. The streaming expression of silent suppression and partial results.
  • guardrails (reliability domain). Rate-limit guards that protect downstream services and post-generation output filters for complete coverage. Shows that error propagation is also a guardrail concern, not only a tool concern.
  • multi-agent-overview, delegation-patterns, supervisor-patterns, orchestration-patterns (agentic-architecture domain). These explain the coordinator hub, supervisor recovery selection, and delegation that the orchestration paragraphs assume.
  • queues-retries (agents-sdk domain). The Agents SDK's queue and retry primitives are the production realization of local retry and idempotent re-delivery for durable workflows.
  • mcp domain (mcp-tools, mcp-production). MCP tool errors surface through the same is_error mechanism; a misbehaving MCP server that returns empty on failure is the silent-suppression anti-pattern at the protocol boundary.

Worked production examples: Example 1: research fan-out with one timeout

A coordinator fans out to five specialist subagents: academic database, industry reports, patent database, government publications, and news index. Four return; the patent database times out at 30 seconds after retrieving 3 of 9 sources.

The patent branch's tool handler classifies the timeout as transient/isRetryable: true and, because local retry (3 attempts, exponential backoff) also timed out, it returns the structured error:

result.json
json
{
  "isError": true,
  "errorCategory": "transient",
  "isRetryable": true,
  "attemptedAction": { "tool": "search_patents", "query": "renewable energy policy", "dateRange": "2022-2024" },
  "partialResults": [
    { "title": "EU Patent EP1234567", "retrieved": true },
    { "title": "US Patent US9876543", "retrieved": true },
    { "title": "JP Patent JP5555555", "retrieved": true }
  ],
  "alternativeApproaches": [
    "Retry patent search with narrower date range 2023-2024",
    "Use cached patent results from previous session"
  ],
  "message": "Patent database timed out after 30s; 3 of 9 sources retrieved."
}

The coordinator receives the fan-out envelope: four ok/empty branches plus one failed branch with isRetryable: true. It does not abort; it retries only the patent branch with backoff, synthesizes the report from the four complete branches plus the three partial patent hits, and writes a coverage entry: { "topic": "patents", "status": "limited", "reason": "Patent database timed out; 3 of 9 sources retrieved." }. The final report is degraded but complete, with the gap explicit. This single walkthrough exercises the quartet, the fan-out envelope, category-driven retry, partial results, and coverage annotation.

Worked production examples: Example 2: order lookup, empty versus timeout

A support agent calls lookup_orders for customer CUST-48721. First run: the database is reachable and returns an empty array. The tool handler returns status: "success", results: [], totalMatches: 0. The model reports "no orders found" and stops. No retry occurs, correctly, because a valid empty is the answer.

Second run, same customer, but the database connection drops at 5 seconds. The handler catches the timeout and returns is_error: true, errorCategory: "transient", isRetryable: true. The SDK's automatic retry (or the handler's local backoff) re-issues the call; on success it returns the real orders. If the outage persists past the retry budget, the error propagates to the model with attemptedAction and a suggestion, and the model tells the user the lookup failed rather than claiming no orders exist. The discriminator in code is the caught exception versus the empty 200 array, exactly the sixth required shape.

Worked production examples: Example 3: charge with indeterminate timeout

A fulfillment agent calls charge for an order. The request times out at the network layer after 8 seconds; it is unknown whether the processor posted the charge. The agent uses the idempotency-key flow from the earlier section: it retries with the same Idempotency-Key, but before each retry it verifies via GET /charge/by-key/{key}. On the second attempt the verify call finds the existing charge, so the agent returns success without double-charging. If verify finds nothing and the error is transient, it retries; if the error is permission (declined card) it aborts and escalates. This realizes verify-before-retry for an indeterminate write.

Build exercise material

These steps are verifiable: each has an observable outcome that proves the mechanism works.

  1. Define a ToolError interface with isError: true, errorCategory, isRetryable, message, and optional context. Observable outcome: a TypeScript type check passes and the shape matches the lesson tool-error-handling minimum.
  1. Build a lookupOrders handler that returns status: "success" with results: [] for an empty 200 and is_error: true with errorCategory: "transient" for a caught timeout. Observable outcome: unit test shows the empty case has no is_error and the timeout case has is_error: true; the retry branch is taken only for the timeout.
  1. Implement withRetry using exponential backoff with jitter, capped at maxAttempts 3 (real-time) or 5 (background), honoring a retryAfterMs when present. Observable outcome: a mock that fails twice then succeeds returns after delays that grow 1s, 2s, 4s, and the final attempt succeeds.
  1. Implement a fan-out that uses Promise.allSettled and builds a FanOutResult with per-branch status. Observable outcome: with two ok, one empty, and one failed branch, the envelope status is partial and the failed branch carries errorCategory and isRetryable.
  1. Add a coverage map to the synthesis output so each topic carries well-supported/partial/limited/unavailable. Observable outcome: a branch whose source timed out appears as limited with a reason, never as absent.
  1. Add an Idempotency-Key header to a write path and a verify-before-retry step. Observable outcome: a simulated timeout that actually posted returns the existing record on retry, with no second charge (assert the processor received exactly one effective post).

Additional mechanism depth: Failure isolation and the coordinator hub

Failure isolation is what makes partial-failure handling possible. Each branch in a fan-out must capture its own result independently so that a failure in one subagent, tool, or data source does not destroy sibling results or the coordinator's control plane. The lesson tool-result-handling demonstrates this with Promise.allSettled, which resolves with per-call fulfilled/rejected states and lets the handler build one user turn containing a mix of successful tool_result blocks and is_error: true blocks, rather than throwing and losing every result. The multi-agent research engineering post describes the lead agent as a hub that receives all subagent findings and decides whether more research is needed; the hub is the single place where classification, recovery selection, and observability live. Distributed agent-to-agent recovery duplicates that logic and produces inconsistent handling of the same category.

The converse case is a shared dependency: when a common database or auth layer is down, every branch fails for the same root cause, and the correct response shifts from per-branch retry to global circuit breaking or failover. The lesson fallback-patterns and retry-strategies cover this with the circuit breaker (closed to open to half-open) that fails fast against a confirmed-dead dependency.

Additional mechanism depth: Checkpointing and resume

Checkpointing preserves completed steps so a retry resumes from the last good state instead of from the beginning. The multi-agent research post states the system "can resume from where the agent was when the errors occurred" and combines the model's adaptability with "deterministic safeguards like retry logic and regular checkpoints." The lesson streaming-reliability applies the same idea to long-running streams: every N tokens, save the accumulated response, and on failure resume from the last checkpoint. For multi-step workflows, the tool-error-handling lesson shows rollback of earlier steps when a later step fails (release the payment hold, release the inventory reservation) so that partial progress is not only preserved but kept consistent. The boundary where checkpointing alone is insufficient is an atomic transaction whose partial commit would corrupt downstream state; there, compensate side effects and escalate rather than synthesize success.

Additional mechanism depth: Idempotency in batch processing

For large batched work, correlation identifiers let a retry target only the failed items. The forensics file's per-item rule states that a batch of many items carries a per-item identifier (such as custom_id) and a result manifest with per-item status, timestamps, and retry counts, so reprocessing the entire batch is never required. The project lesson message-batches-api covers the Message Batches API, which assigns a custom_id to each request so results can be matched back and a failed item re-submitted without repeating successes. The same correlation principle applies to streaming and durable workflows: a stable key per operation is what makes a retry safe. This is the batch-scale expression of the idempotency rule from the write-path example above.

Additional mechanism depth: Local retry versus caller retry, in depth

The lesson tool-error-handling draws a sharp line. Tool-internal retry is appropriate for fast infrastructure transients (1 to 3 attempts, sub-second), because it is transparent to the model and keeps caller code simple. Returning the error to the model is appropriate for longer waits and rate limits, because the model can report status to the user, a human can intervene, and the model's retry is informed by the structured category. The trap is either extreme: retrying every transient failure immediately inside the tool hides routine blips from the coordinator and can stall a pipeline forever during a persistent outage; returning every transient failure to the model overwhelms it with recoverable noise and forces it to pause or ask the user. The middle ground is that the tool absorbs fast, deterministic transients locally and surfaces only what local retry could not resolve, carrying attempts and partialResults. The SDK's own default (two automatic retries with backoff, honoring retry-after) is the infrastructure-layer realization of this principle for the HTTP call itself.

Additional mechanism depth: Streaming truncated stream, concrete consumer handling

A truncated stream is the streaming form of an access failure that looks like a valid empty. The lesson streaming-reliability gives the consumer-side contract: buffer incoming events until a complete event is received, never execute a tool call until its content_block_stop arrives, mark intermediate results partial: true, and treat a stream that goes idle after the last delta without a message_stop as complete only after a timeout check, verifying completeness with a content hash or expected token count when available. For a partial JSON, the consumer must detect the missing closing brace (a simple brace-count, or a lenient parser) and either discard or repair rather than act on the prefix as if it were the whole answer. The errors page adds that an error after a 200 SSE response does not follow the standard HTTP error path and must be handled through stream error events. The downstream consequence is the same as for any access failure: if the consumer treats the truncated buffer as a finished, successful response, it silently omits whatever arrived after the drop, which is silent suppression in streaming form. The cure is the same structural one: a typed, explicit signal that the result is partial or failed, never an assumed-complete buffer.

Additional mechanism depth: The two anti-patterns restated against the documented surface

Silent suppression and workflow termination are the two anti-patterns the reference page names, and both are visible against the documented surface. Silent suppression is returning {}, null, or an empty array for an access failure; the API treats that as is_error: false (success with no data), so the model and every downstream consumer believe the operation completed and found nothing. The lesson error-handling shows the exact dangerous snippet (return [] after a silent catch) and the lesson tool-result-handling lists "Hiding partial failures behind a success status" as an anti-pattern. The documented fix is the typed is_error: true envelope with errorCategory and isRetryable. Workflow termination is aborting the whole fan-out (or throwing a raw exception that bubbles to a top-level handler) when one branch fails; the multi-agent research post and the fallback-patterns lesson both prescribe continuing with partial results and degraded delivery, annotating the gap, rather than discarding completed work. The forensics file is explicit that once an access failure is collapsed into an empty success, no downstream rule (local retry, category check, coverage annotation) can recover the lost signal, which is why source typing must come first.

Additional mechanism depth: Example 4: end-to-end multi-agent degraded delivery

A research coordinator receives a user query and spawns five parallel subagents (academic, industry, patents, government, news), following the orchestrator-worker pattern from the research engineering post. The patents subagent's tool times out after retrieving 3 of 9 sources; its handler classifies transient/isRetryable: true, preserves the 3 partial hits, and returns the quartet. The coordinator's fan-out envelope reports four ok/empty branches and one failed branch. The coordinator retries the patent branch with exponential backoff (honoring any retryAfterMs); on the second attempt it succeeds for the remaining 6 sources. Even if it had not, the coordinator would synthesize from the four complete branches plus the three patent partials and write a coverage entry marking patents as limited with the reason. The user receives a report that is honest about the gap instead of a silently incomplete one, and no completed subagent work is discarded. This walkthrough connects every mechanism in the task: the quartet, source typing, category-driven retry, partial results, fan-out isolation, coverage annotation, and degraded delivery.

Additional mechanism depth: Divergence supplement: the tier spend-cap 429

One documented nuance the reference page omits: a tier spend-cap 429 has no retry-after header and "keeps failing until access resumes." The generic "retry a 429 after its Retry-After" advice therefore has an exception. The correct handling is to distinguish a rate-limit 429 (has retry-after, retry after that duration) from a spend-cap 429 (no header, do not blind-retry; surface to the user or escalate). This is a real official-versus-simplistic-guidance split that a candidate should answer with the documented exception.

Status-to-action reference

The following mapping consolidates the documented surface into a decision table the candidate can use directly. It joins the API type values from the errors page with the application errorCategory values from the lesson tool-error-handling and the retry posture from the lessons error-handling and retry-strategies.

HTTPAPI typeApplication categoryTransient?Retry postureOwner of decision
400invalid_request_errorvalidationNoDo not replay; correct input, then retryApplication tool handler
401authentication_errorauthNoNot retryable; escalate, refresh credentialsApplication tool handler
402billing_errorbusiness / permanentNoNot retryable; escalate to billingApplication tool handler
403permission_errorpermissionNoNot retryable; escalateApplication tool handler
404not_found_errornot-foundNoNot retryable unchangedApplication tool handler
409conflict_errorvalidation / businessConditionalResolve conflict, then retryApplication tool handler
413request_too_largevalidationNoReduce size, then retryApplication tool handler
429rate_limit_errorrate-limitYesHonor Retry-After; else backoffSDK then application
500api_errortransientYesExponential backoffSDK then application
504timeout_errortransientYesExponential backoff; consider streamingSDK then application
529overloaded_errortransientYesLonger initial delay; backoffSDK then application

The 4xx column is never in the SDK's automatic-retry set, which is the documented signal that a request-shape error must not be retried unchanged. The 429/5xx column is exactly what the SDK retries twice by default with backoff, honoring retry-after. The application errorCategory is the richer vocabulary the handler attaches so the model can branch, and it is the same information expressed in the tool-result envelope.

Status-to-action reference: Why 400 is never a blind retry

The errors page lists many 400 variants that are inherently request-shape problems: prefill not supported on Claude 4.6 and later, thinking blocks modified, extended thinking requested on a model that removed it, adaptive thinking requested on a model that supports only extended thinking, and thinking disabled on Fable 5, Mythos 5, and Mythos Preview. The lesson error-handling adds context_length_exceeded as the canonical trap: retrying with the same oversized prompt wastes tokens because the identical body fails again. Every one of these is fixed by changing the request, not by waiting. That is why the automatic retry set stops at the transport boundary and the application must translate a 400 into a validation category with a correction hint rather than into a retry.

Status-to-action reference: Why 429 needs the header read

A rate-limit 429 carries a retry-after header that tells the caller exactly when to retry; the SDK honors it. But a tier spend-cap 429 has no such header and keeps failing until access resumes. The handler must read the header: present means wait that duration and retry; absent on a 429 means surface or escalate rather than loop. This is the single most common exam trap around rate limits, and the documented exception to "always retry a 429."

Status-to-action reference: Why 5xx is backoff, not instant

The errors page says for 500 "Retry the request with exponential backoff" and the lesson retry-strategies maps 500 to a short backoff, 503 to a 1s backoff over 3 attempts, and 529 to a longer initial delay. The reason is that a server error is time-dependent: the same request likely succeeds after the service recovers, but immediate retries hammer a struggling service and create a thundering herd. The SDK already does this twice by default; application code should only add more attempts with jitter when the default two are insufficient for the latency budget.

Boundary conditions: Already-current and idempotent success are not errors

The forensics file names a subtle boundary: a tool asked to perform work already satisfied (push firmware already on the target build, or a validation step needing no change) must return isError: false, not a structured error with a category such as already_current. Modeling a satisfied state as an error pollutes failure metrics, triggers unnecessary retries, and causes the coordinator to re-queue finished work. The correct envelope is isError: false with a payload explaining the outcome is satisfied. The opposite case, a genuine permission block such as a sensor locked by a safety controller, remains a typed error.

example.ts
typescript
// Already-current is a success, not an error.
function pushFirmware(target: string, build: string): ToolResultBlock {
  if (currentBuild(target) === build) {
    return {
      type: "tool_result",
      tool_use_id: "toolu_...",
      content: JSON.stringify({ status: "success", message: `Target already on build ${build}.`, alreadyCurrent: true })
    }
  }
  // ... perform push, or return is_error: true on a real failure
}

Boundary conditions: Decommissioned and permanently unavailable resources

A request against a decommissioned SKU, a retired service pipeline, a missing table (404 on a resource), or a permanently unavailable archive is reported as isError: true with a non-retryable category (business, validation, or permission) and a detail such as a decommission date. Retrying it with backoff is wrong because the same request will fail forever regardless of delay; only a different product or source helps. The boundary that makes the same resource class retryable is a transient outage (a 503 on an otherwise active SKU). The forensics file is explicit that decommissioned, missing, or permanently unavailable resources are not retryable.

Boundary conditions: Validation requires correction, not replay

A validation error (malformed DOI, 400 bad request, 422 malformed field, invalid order ID) is reported with errorCategory: "validation" and a message naming the failing field and expected format. The consumer does not replay the same payload; it corrects the input and re-issues. The lesson tool-error-handling and the forensics file agree that blind retry of a validation error wastes turns and never succeeds. The nuance the forensics flags (and the lesson resolves) is that isRetryable: false refers to an identical replay; correction-then-retry is a different, supported path the model initiates using the hint.

Boundary conditions: Indeterminate writes require verify-before-retry

A notification send, a charge, or a firmware push that times out after the side effect may have occurred leaves the outcome unknown. The tool reports an explicit indeterminate or partial status rather than a simple success or failure, and the coordinator verifies the true outcome before deciding whether to retry, so it does not duplicate the effect. An idempotency key (from the earlier section) is the mechanism that makes verify-before-retry safe. This is the write-side counterpart of the read-side rule that an empty 200 is a valid result and a timeout is an access failure.

Production readiness checklist

A concise verification list that maps each requirement to an observable test, useful for the build exercise and for the writer:

  1. Every tool result that fails sets is_error: true on the tool_result block and carries errorCategory, isRetryable, and message. Observable: a schema assertion on the block.
  2. Access failure and valid empty result take different wire shapes at the source tool. Observable: a unit test where a timeout yields is_error: true and an empty 200 yields status: "success" with zero items.
  3. Retryability is derived from errorCategory, never from message text. Observable: a test feeding two errors with identical wording but different categories produces different retry decisions.
  4. A 429 with Retry-After waits that duration; a 429 without it (spend cap) does not blind-retry. Observable: a mock asserting the wait matches the header or that escalation fires.
  5. A 5xx uses exponential backoff with jitter, capped; a 400 never retries unchanged. Observable: attempt counts and delays asserted.
  6. A fan-out returns a per-branch envelope and never discards successes on one branch failure. Observable: an envelope with mixed statuses and a partial overall status.
  7. Partial results travel with the error payload. Observable: the failed branch's partialResults equals the items retrieved before the failure.
  8. Synthesis output carries a coverage annotation per topic so gaps are visible. Observable: a timed-out source appears as limited/unavailable with a reason.
  9. Writes subject to timeout use an idempotency key and verify-before-retry. Observable: a simulated timeout that posted returns the existing record, no duplicate.
  10. Streaming consumers buffer events, never execute a tool call before content_block_stop, and treat a missing message_stop as incomplete until verified. Observable: a truncated stream test yields a partial result, not a silent success.

This checklist is the operational expression of the reference page's four-element quartet plus the two anti-patterns plus the access-failure versus valid-empty cut, all grounded in the documented API surface and the project lessons.

Distractor analysis

The forensics file catalogs the distractors that appear most often around this task. Each is attractive for a reason and wrong for a reason; naming them helps the writer build answer keys that survive challenge.

Silent continuation is the most common distractor: return an empty success so the pipeline never appears to fail. It is attractive because it preserves latency and a clean output, but it fails because it creates confident wrong output, since the gap reads as a deliberate no-signal finding. The lesson error-handling shows the dangerous return [] snippet and the lesson tool-result-handling lists hiding partial failures as an anti-pattern.

Total abort is the second most common: terminate the whole workflow on one branch failure. It is attractive as conservative safety, but it fails because it discards valid work and delays a useful degraded result, and may loop forever if the root cause persists. The multi-agent research post and the fallback-patterns lesson prescribe continuing with partial results and degraded delivery.

Generic string distractors return a single unstructured status such as "search unavailable" or "Operation failed." They are attractive as simplicity but fail because they strip the category, retryability, and attempted action the coordinator needs, forcing either blind retry or blind abandonment. The tool-use documentation and the lesson tool-error-handling require structured, instructive errors instead.

Downstream inference distractors keep the empty shape and add a prompt instruction, a health probe, or text parsing to reconstruct the lost signal. They are attractive as no-code fixes but fail because the signal was already lost at the source; a genuinely valid empty will incur an unnecessary retry and a real access failure that exceeds the budget still collapses to empty. The forensics file states that normalization must happen before the coordinator sees the result, at the source tool.

Uniform retry distractors retry every error with the same policy. They are attractive as uniform resilience but fail because validation, permission, and business failures never succeed with identical replay, wasting latency and quota. The lesson tool-error-handling is explicit that only transient and rate-limit should be retryable.

Correctness-inversion distractors mark valid empties as errors, or mark satisfied/idempotent states as failures. They are attractive as "guaranteed attention" but fail because they trigger unnecessary retry or escalation and corrupt metrics. The access-failure versus valid-empty code and the already-current code above are the correct counters.

Distractor analysis: Mapping the reference page's exam traps

The reference page lists four exam traps; each maps to a rule and a distractor above.

  • "Catching a timeout and returning empty results marked as successful" is silent suppression; the counter is the source-typed is_error: true transient envelope with partials.
  • "Terminating the entire research pipeline when one subagent times out" is total abort; the counter is fan-out isolation plus degraded delivery with a coverage annotation.
  • "Returning a generic search unavailable status after retry exhaustion" is the generic-string distractor; the counter is the quartet (category, attempted action, partials, alternatives) so the coordinator can choose.
  • "Retrying a valid empty result because it looks like a failure" is correctness inversion; the counter is the access-failure versus valid-empty cut at the source tool.

The practice scenario in the reference page (a web search subagent times out) has one correct answer among four options: return structured error context with failure type, attempted query, partial results, and alternative approaches (Option C). Option A is silent suppression, Option B is total abort, and Option D collapses everything into a generic status after retries. The structured quartet is the middle ground the task exists to teach.

Summary of ownership by behaviour

To close the loop on the ownership map, the following single-paragraph restatement ties each behaviour to its layer. The SDK owns automatic transient retry of connection errors, rate limits, and 5xx with exponential backoff, twice by default, honoring retry-after. The application tool handler owns the is_error flag, the errorCategory and isRetryable classification, the access-failure versus valid-empty discrimination, and the structured content the model receives. The model owns the agent-level recovery decision (retry, correct, reroute, escalate, adapt) from that typed content, and retries 2 to 3 times with corrections on an invalid tool call. The orchestrator owns fan-out isolation, classification aggregation, recovery selection, and the coverage annotation in synthesis. The upstream infrastructure owns the raw failure signal. When each layer stays in its lane, the two anti-patterns cannot arise: silent suppression is impossible because the source tool types the outcome, and workflow termination is impossible because the orchestrator isolates branches and continues with partial results.

Claim inventory addendum: practice scenario and exam traps

The reference page's practice scenario and exam-trap list are claims in their own right and were inventoried above only partially. This addendum records them explicitly so nothing from the reference page is lost.

  1. Practice scenario: a web search subagent times out during a complex topic search, and the question asks which approach best enables intelligent recovery. The four options are silent suppression (A), total abort (B), structured error context with the quartet (C), and generic status after retries (D). CONFIRMED: Option C is the documented middle ground. The multi-agent research post supports continuing with structured findings and letting the agent adapt; the lesson tool-error-handling requires the quartet. Options A and B are the two named anti-patterns; Option D collapses the coordinator's decision signal.
  1. Exam trap: catching a timeout and returning an empty result set marked as successful. CONFIRMED as the silent-suppression anti-pattern. The lesson error-handling shows the exact dangerous return [] snippet and the lesson tool-result-handling lists hiding partial failures as an anti-pattern.
  1. Exam trap: terminating the entire research pipeline when one subagent times out. CONFIRMED as the workflow-termination anti-pattern. The fallback-patterns lesson and the multi-agent research post prescribe degraded delivery with explicit gaps.
  1. Exam trap: returning a generic "search unavailable" status after retry exhaustion. CONFIRMED as the generic-string distractor. The tool-use documentation requires instructive, structured errors; the lesson tool-error-handling requires errorCategory, isRetryable, and a message.
  1. Exam trap: retrying a valid empty result because it looks like a failure. CONFIRMED as correctness inversion. The access-failure versus valid-empty code shows the discriminator: an empty 200 is a success and is not retried; a timeout is is_error: true and is retried.
  1. Build exercise shape: a structured error schema with failureType (transient/validation/business/permission), attemptedAction (tool/query/parameters), partialResults (array), and alternativeApproaches (string array). CONFIRMED and REFINED: the lesson tool-error-handling requires errorCategory/isRetryable/message as the minimum and uses context.suggestion for alternatives; the reference page's four-field shape is a faithful subset expressed with different field names. The exercise also asks for local retry (3 attempts, exponential backoff) before propagation, a coordinator that branches on failure type, and coverage annotations in synthesis. All are supported by the lessons retry-strategies, tool-error-handling, and tool-result-handling.

With these fifteen claims inventoried and the mechanism reference, ownership map, version notes, divergences, beyond-the-task material, worked examples, build exercise, and distractor analysis all present, the grounding covers the reference page completely and extends it with the documented API error surface, the tool_result is_error mechanism, streaming failure modes, idempotency and correlation identifiers, and the multi-agent degraded-delivery guidance the reference page omits.

Mechanism and API surface

Four-element structured error context
Failure type, attemptedAction with tool and query and parameters, partialResults array, and alternativeApproaches array, all required alongside status and shouldRetry so the coordinator has actionable context.
Access failure versus valid empty result
Access failure means the tool could not reach the source and the query did not execute, valid empty means the tool executed correctly and found no matches. The first has isError true and shouldRetry true, the second has status success with empty results and shouldRetry false.
isError and isRetryable signals
errorCategory from transient, permanent, auth, not-found, and validation plus isRetryable boolean. Transient with isRetryable true routes to retry, auth with isRetryable false routes to escalation without retry.
Exponential backoff with jitter for local retry
Delay computed as base times two to the power of attempt plus random jitter, typically 1 second base with 1 second, 2 seconds, 4 seconds plus jitter, and Retry-After header honoured when present to avoid synchronised storms.
Partial results preservation across failure boundaries
The partialResults field specifically preserves data retrieved before failure so the coordinator can use it. Discarding treats the operation as all or nothing and wastes completed work.
Coverage annotation on synthesis output
Structured section listing each topic area as well supported, limited with reason, or unavailable with reason. Failed topics are explicitly noted with failure type and attempted alternatives, never silently omitted.

The decision rules in play

Each rule below is a distinct decision the exam tests, stated with its mechanism, the boundary where the answer flips, and the wrong answers written against it.

R1

Silent suppression as empty success is the catastrophic anti-pattern

A subagent encounters a real failure such as a provider timeout, a 403 forbidden on a journal, a parser exception on a malformed PDF, or a database connection drop. Instead of surfacing that failure, it catches the exception and returns a payload that looks like a successful empty query: an empty array or empty findings list with a status field indicating success, no isError flag, and no failure metadata.

The coordinator's recovery logic depends entirely on being able to distinguish two states that look identical on the data surface: no data because the source was unreachable versus no data because the source was queried and correctly reported absence. When the subagent collapses both into the same success shape, the coordinator loses the only signal it could have used to choose retry, reroute, or disclose a coverage gap.

Boundary. The opposite case where returning an empty success is correct is a validated empty query that executed against a reachable source and genuinely found no matches. A flavor search for a niche component, a catalog lookup for an unstocked SKU, or an academic query on a topic with no publications all legitimately return empty.

Recurring specifics. Status shapes that recur include { "status": "success", "results": [] }, { "findings": [] }, { "fares": [] }, { "products": [] }, { "orders": [] }, { "hits": [] }, and { "events": [] } used as the empty-success envelope. The failure types most often swallowed this way are timeouts, rate limits, 403 forbidden, 404-like missing-resource responses that are actually permission issues, parser exceptions, and connection resets.

Wrong answers written against this rule

Proposal. return empty success so the pipeline continues without interruption.

Why it attracts. it keeps the page rendering, keeps latency low, and avoids surfacing an error to a user who might be confused by a failure message.

Why it fails. it guarantees downstream misinterpretation because every downstream step assumes completeness when the data is actually missing, and it removes any chance of retry or gap disclosure.

Proposal. log the failure centrally but still return empty success to the coordinator, letting the coordinator poll a log to discover the real state.

Why it attracts. it feels like visibility is preserved without changing the wire contract.

Why it fails. the coordinator's decision point occurs when it receives the subagent result, not on a later polling interval, and race conditions make the log check unreliable. The coordinator still branches on the success signal it was given.

Proposal. increase the timeout or add hidden retry inside the subagent and keep the empty-success contract if retries fail.

Why it attracts. it reduces the frequency of visible failures.

Why it fails. it still collapses unresolved access failures into the same shape as valid empties after the hidden retries are exhausted, so the fundamental ambiguity remains.

How the same rule gets re-asked
  • One mutation replaces the timeout with a permission error such as 403 forbidden or a restricted journal, testing whether the test-taker still recognizes that retry is wrong but silent suppression is still wrong. Another mutation splits a single fan-out into mixed outcomes such as two successes, one valid empty, and one timeout, testing whether the examinee can selectively retry only the timeout. A third mutation moves the swallow point from the subagent to the MCP tool handler that merges multiple vendor calls into one fares array, testing whether the test-taker sees that the loss of signal still happens at the source boundary.
R2

Terminating the whole workflow on one branch failure wastes completed work

A coordinator fans out to several subagents or parallel tools and any single failure causes it to abort the entire workflow, discard partial results from branches that already completed, and surface a top-level error or a full retry. The mechanism is often a thrown exception that bubbles to a top-level handler, a catch-all that marks the run failed if any branch is not perfectly complete, or a policy that halts synthesis until every source is re-queried to success.

Parallel decomposition is chosen specifically to tolerate partial degradation, and the value of the run is the sum of successful branches plus a transparent account of gaps. Terminating the whole workflow treats a partial failure as a total failure, which violates the decomposition's purpose and forces redundant work.

Boundary. The nearby case where termination is appropriate is a failure that invalidates the entire result or that violates consistency requirements. A bulk transaction where partial commit would corrupt downstream state, a mandatory compliance check that cannot be skipped, or an atomic booking that must not confirm with a dropped leg are examples where aborting, compensating, or checkpoint rollback is correct.

Recurring specifics. Fan-out sizes of three to five branches are the recurring test harness, with two or three successes and one or two failures producing the decision point. The asserted next action in the correct answer is consistently to incorporate partial results, retry only the failed branch with backoff, flag missing data points as gaps, and synthesize with explicit gap annotations.

Wrong answers written against this rule

Proposal. terminate the entire research workflow whenever any subagent encounters an error.

Why it attracts. it feels safe and simple, and it avoids any risk of synthesizing from incomplete data.

Why it fails. it discards valid work, increases latency and cost, and assumes the failed branch will succeed on a full restart when the same external dependency will likely fail again.

Proposal. discard partial results from a branch that was partially successful and reassign the entire branch to a new subagent from scratch.

Why it attracts. it seems cleaner to retry a whole unit than to track per-record progress.

Why it fails. it wastes the subset of records or sources that already succeeded and forces redundant calls against sources that may rate limit.

Proposal. block synthesis until the failed branch can be re-queried with a longer timeout to guarantee completeness.

Why it attracts. it promises eventual completeness.

Why it fails. it sacrifices latency and availability for a completeness guarantee that may never arrive if the external source is down for an extended outage, and it prevents the coordinator from delivering a useful degraded result in the meantime.

How the same rule gets re-asked
  • One mutation changes the failure from fully failed to partially successful such as 7 of 10 records completed before a rate limit, testing whether the examinee adjusts from full-branch retry to per-record retry. Another mutation adds a circuit breaker or persistent outage context, testing whether the examinee moves from per-branch retry to fallback or manual review rather than indefinite re-query. A third mutation makes the failed branch the mandatory compliance or safety check, testing whether the examinee correctly flips from partial delivery to abort with compensation.
R3

Structured error context quartet enables coordinator choice

The failing subagent returns not a generic string but a structured object containing four elements: the failure type or category, details of what was attempted such as the query, parameters, and target system, any partial results gathered before the failure, and potential alternative approaches the coordinator could try such as a different database, a narrower date range, cached results, or a rephrased query. The coordinator reads these fields and selects among retry with modified parameters, reroute to an alternative source, proceed with degraded partial delivery and coverage annotation, or escalate to human review with full context.

A coordinator cannot make a good recovery decision from a bare failure signal because recovery is domain specific. The same coordinator may need to retry a timeout with backoff, correct a validation error, escalate a permission error, or substitute an alternative for a business rule violation, and it needs different data for each.

Boundary. The nearby opposite case where less context is acceptable is a transient failure that is successfully resolved locally and never propagates. If the subagent retries a rate limit internally with backoff and succeeds, it does not need to send the quartet upward because there is nothing for the coordinator to decide.

Recurring specifics. Field names that recur include failureType or errorCategory, attemptedAction or query plus query_attempted plus attempted_query, partialResults or partial_findings or partial_hits, and alternativeApproaches or alternative_sources or suggestion. Values for failureType consistently include transient, validation, business, and permission, with occasional domain variants such as business_rule or rate_limited and timeout.

Wrong answers written against this rule

Proposal. return a generic status such as search unavailable or operation failed after retries are exhausted.

Why it attracts. it at least signals that something went wrong without adding schema complexity.

Why it fails. it strips the coordinator of the query, partials, and alternatives that are needed to pick the right recovery, forcing it to either retry blindly or abandon the branch.

Proposal. include only the failure type without what was attempted or partials.

Why it attracts. it seems sufficient to classify the error.

Why it fails. the coordinator cannot retry intelligently without knowing what parameters were used, and it cannot preserve value without knowing what partials already exist.

Proposal. defer detail to logs and return only a timestamp and a retry-after hint.

Why it attracts. it keeps the error payload small.

Why it fails. the coordinator must make its decision synchronously at the time it receives the error, and log retrieval is asynchronous and often unavailable to the decision path.

How the same rule gets re-asked
  • One mutation strips partialResults from an otherwise correct payload, testing whether the examinee notices that value is being discarded. Another mutation keeps partials but strips alternativeApproaches, testing whether the examinee values the subagent's domain hint about what to try next. A third mutation replaces the human-readable description with raw HTTP codes or stack traces, testing whether the examinee recognizes that structured category plus retryability is more actionable than transport detail alone.
R4

Access failure versus valid empty result requires distinct wire signals

Two outcomes that look the same on the surface, no rows returned, are represented by two different wire contracts. A valid empty result is reported as a success with isError: false and an empty data array, sometimes with a message confirming that the query executed and matched nothing.

Recovery decisions are opposite for the two shapes. An access failure means the query did not execute or did not reach the data, so retry, reroute, or disclosure of a gap may be appropriate.

Boundary. A null finding in research is the most subtle boundary case. No pricing changes for two years, no Cobol engineers in the database, or no restrictive controls in a correctly queried source are valid findings that should travel as successes with empty or explicitly noted no-match content, even though they feel like absences.

Recurring specifics. The contrast that recurs is {"isError": false, "results": []} or {"results": [], "total_matches": 0} for valid empty versus {"isError": true, "errorCategory": "transient", "isRetryable": true} or {"isError": true, "errorCategory": "permission", "isRetryable": false} for access failure. HTTP mapping examples that recur include 503 and 429 as transient, 400 and 422 as validation, 401 and 403 as permission, and 500 as transient when the body indicates a temporary condition such as a temporary database lock.

Wrong answers written against this rule

Proposal. return an empty array in both cases and let the consumer infer the difference from timing or from a free-text message.

Why it attracts. it preserves a single response shape that is easy to parse.

Why it fails. timing is unreliable and text parsing is fragile, and the consumer ends up misclassifying access failures as valid empties or vice versa.

Proposal. return an error in both cases so the consumer always investigates emptiness.

Why it attracts. it guarantees attention.

Why it fails. it forces retry or escalation for legitimate empties and obscures the fact that the system correctly answered that nothing matches.

Proposal. return a single error shape with an errorCategory of not_found for valid empties and keep isError: true for both.

Why it attracts. it preserves a unified error handling path.

Why it fails. a valid empty that matched nothing is not an error, and treating it as one pollutes error metrics, triggers unnecessary retries, and misleads dashboards.

How the same rule gets re-asked
  • One mutation replaces the empty-search framing with an inventory zero-stock framing, testing whether the examinee applies the same cut outside search. Another mutation replaces a transient timeout with a permission failure, testing whether the examinee correctly flips isRetryable while keeping the access-failure versus valid-empty distinction. A third mutation keeps the access failure but adds a short local retry that succeeds, testing whether the examinee recognizes that the final reported shape should be a successful empty only if the retry truly resolved the access issue.
R5

isError true versus isError false is the tool-boundary cut

At the MCP tool result envelope, the isError flag is the wire-level signal that separates success from failure before any payload interpretation occurs. A valid empty result travels with isError: false and a payload that may contain results: [], events: [], or items: [] plus a message confirming that the query executed and matched nothing.

result.json
json
{
  "isError": false,
  "content": [{ "type": "text", "text": "Query executed successfully. No matching records found." }],
  "results": [],
  "total_matches": 0
}
result.json
json
{
  "isError": true,
  "errorCategory": "transient",
  "isRetryable": true,
  "content": [{ "type": "text", "text": "Connection timeout after 30s querying carrier tracking API for shipment SH-4412" }]
}
result.json
json
{
  "isError": true,
  "errorCategory": "permission",
  "isRetryable": false,
  "content": [{ "type": "text", "text": "Database returned 403 Forbidden for archive collection pricing_2024" }]
}

The flag exists to avoid the post-hoc inference that makes so many other designs fail. Without a dedicated error signal, consumers try to infer failure from empty arrays, timing, free-text messages, or status codes buried inside a nominally successful envelope, and each of those inferences is fragile.

Boundary. The boundary that tests this rule is the already-current or no-work success that is often mis-modeled as an error. A fulfillment tool asked to push firmware to a sensor already on the target build, or a validation step where no changes are needed, should return isError: false with a payload that explains that the outcome is satisfied, not a structured error with a category such as already_current.

Recurring specifics. Correct shapes that recur for this rule include the valid-empty envelope: and the failure envelope: and the permission failure envelope: Status values and flags that recur include isError, errorCategory, isRetryable, retryAfterMs, and a description or message field carrying the human-readable detail.

Wrong answers written against this rule

Proposal. wrap every failure as a successful result with a custom success: false field or a status field inside the payload.

Why it attracts. it keeps the envelope uniform and seems to preserve information.

Why it fails. the coordinator's error handling path is keyed to isError: true, so a success envelope with an embedded failure flag will be processed as success by generic aggregation logic that does not know to inspect the custom field.

Proposal. throw an unhandled exception from the tool handler so the framework propagates the error automatically.

Why it attracts. it feels like the most direct way to signal failure.

Why it fails. exceptions bypass the structured error channel and surface as generic framework errors without the category, retryability, or attempted-query metadata the coordinator needs to choose recovery.

Proposal. set isError: true for both access failures and valid empties with an errorCategory of validation or not_found so every empty is treated as an error.

Why it attracts. it guarantees empties get attention.

Why it fails. it misclassifies a successful query as a failure, triggers unnecessary retry or escalation, and corrupts metrics.

How the same rule gets re-asked
  • One mutation keeps the payload identical but flips isError from false to true for a valid empty, testing whether the examinee notices the flag is the decisive field. Another mutation keeps isError: true but strips errorCategory and isRetryable, testing whether the examinee treats the flag alone as sufficient. A third mutation moves the flag decision from the tool to a PostToolUse hook that rewrites an opaque payload after it was produced, testing whether the examinee recognizes that normalization must happen before the coordinator sees the result.
R6

Disambiguation must happen at the source tool, not downstream inference

The component that directly observes the outcome of an external call, the MCP tool that wrapped the API, database, or feed, is responsible for typing that outcome before it is passed upward. It emits two distinct contracts: a success contract for a reachable source that answered with zero hits, and a structured error contract for any condition where the source was not reached, the query did not execute, or the response indicates a failure category.

result.json
json
{
  "isError": false,
  "results": [],
  "message": "Catalog queried for SKU-8812. No matching items found. Query executed against reachable index."
}
result.json
json
{
  "isError": true,
  "errorCategory": "transient",
  "isRetryable": true,
  "message": "Supplier index unreachable after 30s timeout on query for SKU-8812"
}

Information is lost the moment an access failure is collapsed into an empty array at the source. No downstream prompt instruction, health probe, latency heuristic, or few-shot classifier can recover that lost signal reliably because the same empty array is also a correct output for a different situation.

Boundary. The boundary where downstream handling is appropriate is after the source has already typed the outcome. Once the tool has emitted isError: true with category and isRetryable, the subagent can add partialResults and alternativeApproaches and the coordinator can branch on the typed error.

Recurring specifics. Source-typed success shapes that recur for catalog and search tools include: Source-typed failure shapes that recur include: Distractor framings that recur for downstream inference include a system-prompt instruction to retry empty lists once or twice, a second tool call to check_backend_health, inference from latency, and a coordinator that reads error wording and infers transient versus permanent. Each is consistently judged inferior to fixing the source contract.

Wrong answers written against this rule

Proposal. keep the tool shape unchanged and add a system-prompt rule telling the subagent to retry empty results once before concluding no match.

Why it attracts. it requires no code change.

Why it fails. a genuinely valid empty will now always incur an unnecessary retry, and a real access failure that exceeds the retry budget still collapses to empty, so the design adds cost without fixing the ambiguity.

Proposal. add a separate check_backend_health tool and probe the backend after an empty result to decide what the empty meant.

Why it attracts. it feels like verification.

Why it fails. the probe is not the same call, so a healthy probe after a failed query still cannot guarantee the original query would have succeeded, and a probe adds extra latency to every empty path.

Proposal. have the subagent or coordinator infer the category from the wording of an opaque string such as request failed.

Why it attracts. it avoids changing the tool.

Why it fails. wording is ambiguous across vendors and carriers, and the same string is used for unrelated categories.

How the same rule gets re-asked
  • One mutation moves the fix from the tool to a PostToolUse hook that normalizes the opaque payload before the coordinator sees it, testing whether the examinee accepts a hook as a source-level fix when it runs deterministically before inference. Another mutation splits a single multi-mode tool into purpose-specific tools each with its own success versus unreachable contract, testing whether the examinee recognizes that decomposition is itself a source-typing repair. A third mutation keeps the source-typed failure but collapses it downstream by having the subagent report only a small vendor_status without forwarding the underlying error object, testing whether the examinee sees that downstream collapse still breaks coordinator recovery.
R7

Local retry absorbs transient faults before escalation

The component that encountered a transient fault such as a timeout, 503, 429, or temporary network error retries the operation locally with a delay before it considers the error propagatable. The local retry loop uses the error category to decide whether retry is warranted, accumulates any partial results, tracks attempt count and final status, and only if the retry budget is exhausted does it construct the structured error that crosses the coordination boundary.

Transient faults are expected to resolve within seconds and handling them locally preserves the coordinator's decision capacity for failures that genuinely require system-level judgment. Propagating every transient blip forces the coordinator to pause the pipeline, ask the user, or replan around a failure that would have cleared on a second attempt.

Boundary. The opposite case where local retry is wrong is a non-transient failure such as permission denied, a business rule violation, a validation error, or a decommissioned resource. Those will fail identically on every retry and local retry would only waste time, burn quota, and delay the correct response such as escalation or customer explanation.

Recurring specifics. Retry budgets that recur are three attempts with delays such as 1s, 2s, 4s or with exponential backoff starting at 1 to 2 seconds. Rate-limit handling that recurs includes respecting a retryAfterMs or Retry-After header on 429.

Wrong answers written against this rule

Proposal. propagate every transient failure to the coordinator immediately so it has full visibility.

Why it attracts. it seems maximally transparent.

Why it fails. it overwhelms the coordinator with recoverable noise and causes it to pause the pipeline or ask the user for guidance on routine blips that local retry would have resolved.

Proposal. retry indefinitely inside the subagent until success before reporting.

Why it attracts. it seems to guarantee eventual success.

Why it fails. it can stall the pipeline forever on a non-transient or persistent outage, wastes resources, and hides the fact that a sustained outage is occurring.

Proposal. have the coordinator retry the failed subagent call instead of the subagent retrying its own tool.

Why it attracts. it centralizes retry logic.

Why it fails. the subagent is the component with the most context about what was attempted and what partials exist, and coordinator-level retry would repeat the entire subagent rather than the specific tool that failed.

How the same rule gets re-asked
  • One mutation replaces a transient timeout with a paywalled source or a password-protected file, testing whether the examinee correctly suppresses local retry. Another mutation adds a local retry that succeeds after one or two attempts and asks whether anything should be propagated, testing whether the examinee can answer no. A third mutation adds a retryAfterMs field to the error, testing whether the examinee incorporates the server-provided delay rather than using a fixed backoff.
R8

Bounded retry uses exponential backoff with jitter and a cap

When a retry is warranted, the delay between attempts grows exponentially such as 1s then 2s then 4s then 8s, capped at a maximum such as 32 seconds, and jitter randomizes the delay to avoid synchronized retry storms. The loop tracks a maximum attempt count such as three to five, stops retrying after the cap, and either normalizes to a degraded result or propagates a structured error that records how many attempts were made, the last delay, and the final failure category.

example.ts
typescript
async function withBoundedBackoff<T>(op: () => Promise<T>): Promise<T> {
  let delayMs = 1000;
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      return await op();
    } catch (err) {
      const retryable = isTransient(err);
      if (!retryable || attempt === 2) throw normalizeError(err, { attempts: attempt + 1 });
      const jitter = Math.random() * 500;
      await sleep(delayMs + jitter);
      delayMs = Math.min(delayMs * 2, 32000);
    }
  }
  throw new Error("unreachable");
}

Immediate or fixed-interval retry on a struggling service keeps the service down and can create thundering herd effects where many clients retry at the same instant. Exponential backoff reduces load on a recovering service while still increasing the cumulative probability of success over time, and jitter spreads synchronized clients.

Boundary. The nearby opposite case where bounded retry is still wrong is a sustained outage where even bounded retry wastes time that should be spent on failover. When many consecutive failures have already been observed, the correct move is to open a circuit breaker, stop retrying, and periodically probe with health checks rather than consuming the full retry budget on every request.

Recurring specifics. Delay sequences that recur include 1s, 2s, 4s with a three-attempt budget, and a capped sequence starting at 1 second capped at 32 seconds with a maximum of five attempts. Jitter is consistently named as the de-correlation mechanism for bursts of 429 errors where all clients would otherwise retry together.

Wrong answers written against this rule

Proposal. retry immediately with no delay and no limit until success.

Why it attracts. it seems fastest.

Why it fails. it hammers a recovering service, ignores rate-limit headers, and can run forever during an outage. Right when: narrow true-match case.

Proposal. increase the max retries from 3 to 10 during an outage to improve chances.

Why it attracts. it feels more resilient.

Why it fails. during a persistent outage every additional attempt also fails and only adds latency and cost, while circuit breaking would have saved the effort. Right when: narrow true-match case.

Proposal. retry once after a fixed 10-minute delay then give up.

Why it attracts. it is simple to configure.

Why it fails. the delay is too long for transient blips that clear in seconds and too short for sustained outages, and a single fixed delay cannot adapt to server-provided Retry-After guidance. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces 503 with 404 missing table, testing whether the examinee recognizes that no backoff makes a permanent error retryable. Another mutation adds a Retry-After header to the 429 case, testing whether the examinee uses the server-provided duration instead of the exponential schedule. A third mutation introduces a sustained region-wide outage, testing whether the examinee switches from bounded retry to circuit breaker with periodic probing.
R9

Propagate only what local recovery could not resolve, with attempts and partials

A subagent distinguishes recoverable transient blips from unrecoverable or exhausted failures. It handles the recoverable subset locally: retrying a timeout, respecting a retryAfterMs, or falling back to a cached value when the semantics allow.

Propagating every routine blip overloads the coordinator with decisions that the subagent could have made more cheaply and more accurately, because the subagent possesses the tool-specific context such as which query timed out and which partial hits already exist. Propagating nothing hides unrecoverable failures that only the coordinator can decide how to handle, such as rerouting to an alternative source or disclosing a coverage gap.

Boundary. The nearby opposite case where propagating more is correct is when the coordinator needs visibility even into locally recovered events for auditing or for coverage tracking. That need is met by emitting an informational coverage annotation alongside the successful result, not by emitting an error for a success.

Recurring specifics. Phrasings that recur include handle transient failures locally and propagate only unresolved errors, including what was attempted and any partial results. Attempt tracking that recurs includes attempts_made, retries_attempted, attempts, and attempted_query plus the partial arrays.

Wrong answers written against this rule

Proposal. propagate every failure to the coordinator for full visibility.

Why it attracts. it seems safest to let the highest-level component decide.

Why it fails. the coordinator lacks the tool-specific retry semantics and ends up pausing the whole pipeline on blips that would have self-resolved, adding latency without improving correctness. Right when: narrow true-match case.

Proposal. suppress all failures and always return success with partial data.

Why it attracts. it never blocks the pipeline.

Why it fails. the coordinator cannot distinguish a complete result from one that is missing critical coverage, so it may produce confident claims about unqueried domains. Right when: narrow true-match case.

Proposal. add a dedicated error-handling agent that watches a shared failure queue and issues retry commands to subagents.

Why it attracts. it centralizes error handling without burdening the coordinator.

Why it fails. it bypasses the coordinator's visibility and uniform handling, complicates routing, and still requires the same structured context that the subagent would have provided directly. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation changes the local-retry scope from a single tool to a multi-step subagent pipeline such as search then analyze then synthesize, testing whether the examinee applies the same filter at the subagent boundary rather than at each tool. Another mutation replaces the transient error with a permission error, testing whether the examinee recognizes that zero local retries is the correct filtered posture. A third mutation adds a partially successful batch such as 60 of 100 sources processed before a rate limit, testing whether the examinee keeps the 60 and propagates only the 40.
R10

Retryability is determined by category, not by blanket policy

Every structured error carries two linked fields: an errorCategory that names the failure class and an isRetryable boolean that tells the consumer whether re-issuing the same request is expected to help. The tool author sets both based on the upstream signal: the HTTP status, the error code such as temp_db_lock, the gateway decline reason, or a domain-specific condition such as an expired credential.

result.json
json
{
  "isError": true,
  "errorCategory": "business",
  "isRetryable": false,
  "message": "Refund exceeds $500 policy limit; escalate to supervisor"
}
result.json
json
{
  "isError": true,
  "errorCategory": "transient",
  "isRetryable": true,
  "retryAfterMs": 2000,
  "message": "Payment service temporarily unavailable"
}

Different categories demand opposite actions. Retrying a transient 503 or 429 may succeed within seconds, retrying a validation error such as a malformed DOI will fail identically until the input is corrected, retrying a permission error such as a missing license will fail until authorization changes, and retrying a business rule violation such as a refund cap will fail forever because the policy forbids it.

Boundary. The subtle boundary is between a validation error that is retryable after correction and a validation error that is presented as non-retryable. Most evidence treats validation as isRetryable: false when the same payload would be retried unchanged, but treats it as effectively retryable when the consumer will repair the input before retrying.

Recurring specifics. Category values that recur are transient, validation, business, permission, and sometimes business_rule. Mapping examples that recur include 503 and 429 as transient true, 400 and 422 as validation with retry after correction, 401 and 403 as permission false, and policy caps or filing windows as business false. versus

Wrong answers written against this rule

Proposal. set isRetryable: true for every failure so the consumer always retries a few times before giving up.

Why it attracts. it seems uniformly resilient.

Why it fails. it guarantees wasted retries on validation, permission, and business failures that will never succeed with the same input, burning latency and quota. Right when: narrow true-match case.

Proposal. return a numeric error code such as 403 or a raw HTTP status and let the consumer map codes to retry decisions.

Why it attracts. it feels precise.

Why it fails. it forces the consumer to maintain a mapping that varies across backends and to parse unstructured text, and it is ambiguous when the same status can represent different categories depending on the body. Right when: narrow true-match case.

Proposal. set isRetryable: false for all failures and require the consumer to use a system-prompt judgment to decide whether to retry.

Why it attracts. it seems to simplify the tool.

Why it fails. natural-language judgment is inconsistent and cannot be tested deterministically, and it leaves the consumer guessing on every path. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation collapses all three categories into one and asks which behavior wastes the most budget, testing recognition that the waste is uniform retry of validation and business. Another mutation adds a retryAfterMs field only for transient, testing whether the examinee uses the delay only when isRetryable is true. A third mutation replaces a transient message with an error_code: temp_db_lock body on a 500, testing whether the examinee correctly overrides the generic 500 mapping when the body indicates a temporary condition.
R11

Permission and business rule failures are non-retryable

When the tool determines that a request is disallowed by policy or by authorization, it returns a structured error with errorCategory set to permission or business and isRetryable: false, plus a human-readable detail such as the cap value, the missing licence name, the filing window, or the affected region. The consumer does not retry the operation.

A permission failure such as 403 forbidden, 401 invalid key, or missing access to an archive and a business rule failure such as a refund cap, a non-returnable item, or a filing deadline are deterministic with respect to the request: re-issuing the same request against the same policy will fail the same way every time. Retrying only adds latency and can trigger rate limiting or duplicate side effects without any possibility of success.

Boundary. The boundary where retry might still be considered is a permission error that is expected to be fixed mid-session, such as a credential that can be refreshed. Even there, most evidence treats the immediate retry as non-retryable and expects the credential refresh to happen out of band before a new attempt with new authorization.

Recurring specifics. Values that recur for errorCategory include permission for 401 and 403, business or business_rule for caps such as $500 refund limits, $850 exceeds cap, final sale windows, and restricted journals lacking a licence. Messages that recur include detail carrying the cap limit or the SKU decommission date, and suggestions to escalate or offer a partial refund.

Wrong answers written against this rule

Proposal. retry a permission error with exponential backoff in case the permission is transient.

Why it attracts. it feels resilient.

Why it fails. permission errors are not time-dependent in the way transient overloads are, and repeated 403 or 401 calls will fail identically and may trigger security throttling. Right when: narrow true-match case.

Proposal. silently return an empty result for a permission failure so the pipeline does not stall.

Why it attracts. it keeps the pipeline moving.

Why it fails. downstream synthesis will treat the absence as evidence that no data exists and will make confidently wrong claims, while the permission issue remains invisible to operators. Right when: narrow true-match case.

Proposal. increase the timeout or add more retry attempts before reporting a business rule failure.

Why it attracts. it seems to give the system more chance to succeed.

Why it fails. no amount of waiting or retry changes a policy cap or a filing deadline, so the extra attempts only add latency before the inevitable escalation. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces a business cap failure with a transient overload on the same tool, testing whether the examinee flips from non-retryable escalation to bounded retry. Another mutation replaces a permission failure with a malformed input, testing whether the examinee moves from escalation to correction. A third mutation keeps the business failure but adds partial results from other branches, testing whether the examinee correctly pairs non-retryable handling with degraded delivery.
R12

Validation failures require correction of input, not replay

A validation error such as a malformed DOI, a 400 bad request, a 422 malformed phone number or missing required field, or an invalid order ID is reported with errorCategory: validation and a message that names the failing field, the expected format, and a correction hint. The consumer does not retry the same payload.

result.json
json
{
  "isError": true,
  "errorCategory": "validation",
  "isRetryable": false,
  "field": "phone",
  "message": "Phone number contains letters; expected format +1-XXX-XXX-XXXX"
}
result.json
json
{
  "isError": true,
  "errorCategory": "validation",
  "isRetryable": false,
  "message": "Query syntax error near 'doi-1234'; expected DOI format 10.xxxx/xxxx"
}

Validation errors are client-caused and deterministic with respect to the request as formulated: the same malformed input will be rejected the same way every time by the same schema or upstream. Blind retry without correction reproduces the same rejection and wastes turns while providing no path to success.

Boundary. The boundary where validation overlaps with retryability is a validation error that is described as isRetryable: false for an identical retry but is retryable after correction. Evidence consistently shows the boolean refers to an identical replay, not to a corrected replay, so the correct handling of a malformed DOI is to treat it as validation with a hint that makes a second attempt with a corrected DOI appropriate even though isRetryable is false for the same DOI.

Recurring specifics. Signals that recur for validation include 400, 422, malformed DOI such as doi-1234, invalid phone containing letters, missing required customer, and invalid order ID. Payload shapes that recur include: and for a malformed search:

Wrong answers written against this rule

Proposal. retry a validation error with backoff until it succeeds.

Why it attracts. it applies a uniform resilience policy.

Why it fails. the same malformed payload will fail on every retry, so the loop adds latency without ever succeeding. Right when: narrow true-match case.

Proposal. mark every validation failure as transient so it gets retried.

Why it attracts. it seems to handle the error without needing correction logic.

Why it fails. it mislabels a client-caused failure as a server-side capacity issue and triggers retries that will never clear the underlying input problem. Right when: narrow true-match case.

Proposal. infer the validation type from the empty-string or generic error wording and correct heuristically.

Why it attracts. it avoids adding structured fields.

Why it fails. generic wording such as operation failed is identical across categories, so heuristic correction will misfire. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces a malformed DOI with a restricted journal, testing whether the examinee moves from correction to business escalation. Another mutation keeps the validation shape but adds suggestions or alternative queries, testing whether the examinee uses the hint to correct rather than to retry unchanged. A third mutation replaces HTTP 400 with a 500 carrying a transient body, testing whether the examinee correctly overrides the status-code heuristic.
R13

Partial results must travel with the error payload

When a subagent fails partway through a batch, fan-out, or multi-source query, it does not discard what it already retrieved. It returns the partial result set alongside the structured error for the failed portion, with identifiers for the successful items and the attempted action for the failed items.

Discarding partial results wastes work that already cost latency, quota, and API calls, and it can force rate limits to be hit again on re-query. Preserving partials lets the system maximize throughput, lets the coordinator annotate gaps precisely such as which 2 of 5 categories timed out or which 12 of 40 SKUs returned 503, and lets retry logic track per-item progress rather than re-processing already completed items.

Boundary. The nearby opposite case where preserving partials is not sufficient is an atomic workflow where partial commit would corrupt consistency, such as a payment flow where a later step depends on total correctness. There the correct handling pairs partial preservation with compensation: the coordinator may still need to revert what succeeded before reporting the failure, even though the partials are retained for audit or human review.

Recurring specifics. Counts that recur include 3 of 5 sources failed, 7 of 10 records completed before a rate limit, 60 of 100 sources processed, 8 of 12 planned sources read before timeout, and 12 of 40 SKUs returning 503. Identifiers that recur include incomplete record IDs, failed query strings, SKUs, indicator types, and source categories such as academic databases, industry reports, and patent databases.

Wrong answers written against this rule

Proposal. discard partial results and retry the entire batch from scratch.

Why it attracts. it seems simpler to reason about and guarantees a consistent retry shape.

Why it fails. it wastes the successes, re-incurs cost, and risks hitting rate limits or timeouts again on sources that already succeeded. Right when: narrow true-match case.

Proposal. return only the error for the failed portion without partials.

Why it attracts. it keeps the error payload small.

Why it fails. the coordinator loses the ability to synthesize from what exists and must re-query to regain what was already retrieved. Right when: narrow true-match case.

Proposal. queue the failed items for background retry and block the coordinator until the retry completes.

Why it attracts. it promises eventual completeness.

Why it fails. it delays the degraded but useful result the coordinator could have delivered immediately and couples the coordinator's latency to the slowest retry. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation changes the failure from a whole-branch timeout to a single SKU or record within a branch, testing whether the examinee tracks per-item partials rather than per-branch booleans. Another mutation adds a partial set that is itself misleading because the extraction silently returned empty arrays for unusual layouts, testing whether the examinee treats those empties as silent failures that require validation, not as valid partials. A third mutation replaces a fully failed branch with a partial status, testing whether the examinee's recovery switches from whole-branch retry to selective retry of incomplete IDs with backoff.
R14

Coverage annotations make gaps visible by construction

When synthesis or report generation proceeds with incomplete upstream data, the output explicitly marks which topic areas, sections, or findings are well-supported, which are partially supported, and which are gaps because a source was timed out, rate-limited, or unavailable, including the reason and the source name. The annotation is structural in the output schema, not an optional sentence left to a summarizer's discretion, so a consumer cannot mistake absence of data for absence of the phenomenon.

Without explicit coverage state, downstream consumers infer that a missing section means the topic was not relevant, quiet, or adequately covered, when in fact the source was never queried or never answered. That misreading drives confident but wrong decisions because the report reads as uniformly authoritative.

Boundary. The nearby opposite case where coverage annotation alone is not enough is a structurally unmapped category that was never assigned to any subagent in the first place. In that decomposition failure, adding typed feed_status to the four existing subagents still leaves the fifth campaign invisible; the correct fix is to profile the batch against the live threat landscape and dynamically allocate a scoped subagent for the uncovered category, then run an iterative refinement loop to close gaps.

Recurring specifics. Annotation shapes that recur include per-section states such as well-supported, partially supported, limited, and unavailable, plus a source_status or feed_status object with outcome enum values success, timeout, rate_limited, no_matches, degraded, or unavailable, plus query_attempted and partial_results. Phrasings that recur include findings are well-supported and topic areas have gaps due to unavailable sources, and section on geothermal energy is limited due to unavailable journal access.

Wrong answers written against this rule

Proposal. proceed with only successful sources and produce output that never indicates which data was unavailable.

Why it attracts. it keeps the report clean and scannable.

Why it fails. it hides gaps that the consumer needs to calibrate trust, and it lets a timed-out payer or supplier portal read as a clean no-signal finding. Right when: narrow true-match case.

Proposal. collapse multiple source outcomes into a single aggregate coverage percentage with logs available on demand.

Why it attracts. it feels quantitative.

Why it fails. the percentage destroys which source failed, whether a gap is due to a retryable timeout versus a true zero-match, and which topic area is affected. Right when: narrow true-match case.

Proposal. ask the synthesis agent to retry timed-out sources with longer timeouts before beginning any synthesis.

Why it attracts. it seems to prioritize completeness.

Why it fails. it blocks the useful degraded result on the possibility of eventual completeness during an outage and couples synthesis latency to the slowest source. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces a timeout gap with a permission gap, testing whether the examinee still annotates the same way even though retry is not appropriate. Another mutation replaces a single-feed gap with a multi-feed section where one contributing feed is down, testing whether the examinee marks the section as partially supported rather than dropping only one feed's sentence. A third mutation adds a coordinator instruction that mentions source unavailability for single-feed failures but leaves multi-feed sections silent, testing whether the examinee recognizes that structural per-section annotation is still required.
R15

Degraded partial delivery with explicit gaps beats blocking or silent omission

The synthesis path accepts the successful subset of sources, records, or subagent outputs and produces the report or response from that subset while structurally marking the missing slices as gaps, with the source name and the reason for absence. The coordinator does not block synthesis on the failed slice and does not discard the successful slice, and the final output carries per-section coverage state that distinguishes well-supported findings from thin or missing ones.

Availability and completeness trade against each other and the consumer's trust depends on knowing which sections are strong versus thin. Delivering a degraded but annotated result preserves the value of work already done, meets latency and SLA constraints, and lets the consumer decide whether to supplement the gap from another source or to proceed with a qualified conclusion.

Boundary. The boundary where degraded delivery is not appropriate is an atomic or safety-critical workflow where a partial result would mislead or corrupt downstream state. There the correct degraded posture is to withhold the partial as a deliverable but still preserve it for diagnostics, escalate with full context, and compensate any side effects, rather than to synthesize success from incomplete data.

Recurring specifics. Phrasings that recur for degraded delivery include generate the report with available data, clearly marking sections as Data Unavailable, produce output with coverage annotations, and deliver with partial data while noting the gap for later backfill. Anti-pattern phrasings that recur include block synthesis until coordinator re-retry times out, return an error to the coordinator triggering a full retry, and produce output without mentioning which data was unavailable.

Wrong answers written against this rule

Proposal. block synthesis until the timed-out sources are re-queried with longer timeouts.

Why it attracts. it promises completeness.

Why it fails. it sacrifices timely delivery and may never succeed during an outage. Right when: narrow true-match case.

Proposal. return an error about incomplete upstream data and trigger a full task retry.

Why it attracts. it avoids delivering anything that might be incomplete.

Why it fails. it discards valid findings and produces nothing useful in the meantime. Right when: narrow true-match case.

Proposal. deliver silent partial without marking gaps.

Why it attracts. it keeps the output clean.

Why it fails. the reader assumes uniform coverage and draws wrong conclusions. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces a single-source gap with a partially successful source such as 60 of 100 records, testing per-record versus per-section annotation. Another mutation replaces timeout with decommissioned source, testing that the gap remains annotated but the retry decision flips to no retry. A third mutation adds a circuit breaker context, testing whether the examinee pairs degraded delivery with periodic probing rather than immediate re-query.
R16

Failure isolation prevents cascade across parallel branches

Each branch in a fan-out runs with isolated error handling so that a failure in one subagent, tool, or data source does not propagate into sibling branches or into the coordinator's control plane. The branching infrastructure captures per-branch results independently, marks the failed branch with its structured error, and leaves successful branches untouched for downstream merging.

Without isolation, a single timeout or malformed input causes sibling successes to be lost, turns a recoverable partial failure into a total failure, and forces redundant re-execution of branches that already completed. Isolation is what makes partial failure handling and degraded delivery possible, and it is what allows the coordinator to retry only the failed branch with backoff rather than restarting the entire fan-out.

Boundary. The boundary where broader failure is expected is a shared dependency such as a common database or auth layer that, when down, makes all branches fail for the same root cause. There the correct diagnosis is that the failures are correlated, not isolated, and the recovery shifts from per-branch retry to global circuit breaking or failover.

Recurring specifics. Architectural phrasings that recur include structured error propagation with failure isolation enabling coordinator-level selective recovery, hub centralizes error handling and observes every exchange, and per-agent timeout after which the coordinator cancels slow agents and proceeds with completed results. Fan-out sizes of four to ten parallel agents and the pattern of seven successes with three still running at the timeout mark recur as the decision testbed.

Wrong answers written against this rule

Proposal. trigger a complete workflow restart for consistency, discarding all completed work.

Why it attracts. it seems to guarantee a clean state.

Why it fails. it amplifies an isolated failure into a total failure and may loop forever if the root cause persists. Right when: narrow true-match case.

Proposal. ignore subagent errors and complete the workflow with partial results without marking the failure.

Why it attracts. it keeps the pipeline moving.

Why it fails. it collapses failure isolation into silent suppression and removes the coordinator's ability to retry or disclose the gap. Right when: narrow true-match case.

Proposal. retry all branches indefinitely until every branch succeeds.

Why it attracts. it seems to preserve parallelism.

Why it fails. it wastes successes and holds latency hostage to the slowest or most failure-prone branch. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces isolated per-branch timeouts with a shared-deadlock framing, testing whether the examinee recognizes correlated failure. Another mutation changes per-branch success from complete to partial such as 7 of 10 records, testing whether isolation preserves the partial within the branch. A third mutation moves isolation from the coordinator routing layer to a shared failure queue with a dedicated handler, testing whether the examinee prefers hub isolation over a separate queue.
R17

Coordinator as hub centralizes classification and recovery selection

All subagent and tool results flow through a single coordinator that classifies each result by category, checks isRetryable, respects any retryAfter guidance, and chooses among local-branch retry, alternative source, degraded delivery with annotation, fallback model, or escalation to human review. The coordinator also controls exactly what context each subagent receives next and retains full observability of every exchange for logging and audit.

Centralization gives one place where system-level trade-offs such as quorum, SLA, and coverage can be applied consistently. Distributed agent-to-agent recovery would duplicate decision logic, make observability partial, and allow inconsistent handling of the same category across different pairs.

Boundary. The nearby opposite case where distribution is tested is a per-tool local retry that is correctly handled without the coordinator. Evidence consistently treats that local retry as compatible with hub centralization, because the tool retries only the transient that it can resolve deterministically and the coordinator handles everything that remains.

Recurring specifics. Hub phrasings that recur include hub can retry the spoke, delegate to an alternative spoke, or include the partial results and note the error, and coordinator receives structured result and checks error type to decide whether to retry, fallback, or escalate. Fallback patterns that recur include retrying a failed specialist with an alternative tool, re-tasking to a different data source, and quarantining failed records for later retry within a recovery window.

Wrong answers written against this rule

Proposal. let subagents message one another directly to recover faster without coordinator bottleneck.

Why it attracts. it seems more parallel and lower latency.

Why it fails. it removes central observability and creates inconsistent recovery, and the coordinator loses the ability to apply quorum or coverage policy. Right when: narrow true-match case.

Proposal. add a separate monitoring subagent that watches all failures and issues retry commands.

Why it attracts. it offloads the coordinator.

Why it fails. it bypasses the coordinator's uniform handling and introduces a second control plane that can conflict with coordinator decisions. Right when: narrow true-match case.

Proposal. chain subagents sequentially so each handles the next's failures.

Why it attracts. it feels like pipeline resilience.

Why it fails. it couples latency to the chain length and makes isolation harder, and the coordinator still lacks visibility into intermediate failures. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation keeps the hub but adds per-item manifests, testing whether the examinee pairs hub recovery with surgical per-item retry. Another mutation replaces a transient hub retry with a non-retryable business failure, testing whether the hub's next action flips from retry to escalation. A third mutation replaces a single failed branch with batch partial failure, testing whether the hub's action changes from branch retry to custom_id based re-submission.
R18

Alternative approach hints belong in the error payload where the domain is known

The component that failed includes in its structured error one or more alternative approaches that are specific to its domain, such as trying a different database for an academic search, broadening the date range, checking cached results, or suggesting historical seasonal data when a weather forecast is unavailable. The coordinator can then apply one of those suggestions without needing to possess the same domain knowledge.

The subagent knows its data sources, query dialects, and fallback options far better than the coordinator does, so the most actionable alternative will come from the component closest to the failure. Without that hint, the coordinator can only retry the same query or choose a generic fallback, which is often less effective than the domain-specific alternative the subagent could have suggested.

Boundary. The boundary where alternatives are not required is a success case such as a valid empty result where no failure occurred, or a business rule violation where no alternative within policy would satisfy the request. The opposite case where alternatives are required is a transient or permission failure where a different source would succeed.

Recurring specifics. Examples that recur include retry with narrower date range 2023 to 2024, search alternative database government_publications, use cached results from previous session, use historical seasonal averages for the port, and try an alternative payment gateway. Field names that recur include alternativeApproaches, alternative_sources, suggestion, and recommended_action when the alternative is a specific next tool or source.

Wrong answers written against this rule

Proposal. return only the failure type and partials without alternatives.

Why it attracts. it seems sufficient for the coordinator to decide.

Why it fails. it forces the coordinator to invent a domain-specific alternative it may not know, leading to a generic retry that is less likely to succeed than the subagent's hinted alternative. Right when: narrow true-match case.

Proposal. include a retry-after timestamp alone as the alternative.

Why it attracts. it is easy to generate.

Why it fails. it tells the coordinator when to retry but not what alternative to try if the original source remains unavailable, and it omits partials that could make retry unnecessary. Right when: narrow true-match case.

Proposal. centralize alternatives in the coordinator's system prompt rather than in the error payload.

Why it attracts. it seems to keep domain knowledge in one place.

Why it fails. it generalizes across sources and cannot capture per-failure alternatives that depend on what was attempted and what partials already exist. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces the alternative hint with a recommended_action string that the coordinator executes blindly, testing whether the examinee prefers the coordinator to choose among alternatives rather than execute one blindly. Another mutation strips alternatives from an otherwise complete payload, testing whether the examinee still considers it correct. A third mutation keeps alternatives but strips the attempted query, testing which part of the quartet is more load-bearing for choosing the alternative.
R19

Generic unstructured error strings prevent intelligent recovery

An error is returned as a single unstructured string such as Operation failed, search unavailable, or search_unavailable with no error category, no isRetryable, no attempted query, and no partials. The coordinator can only treat such payloads uniformly, which forces it into either retrying everything or retrying nothing, and into misclassifying business and permission failures as transient and vice versa.

Structured fields exist precisely to give the consumer a deterministic decision tree that does not depend on parsing natural language. Uniform strings require fragile text interpretation that varies across backends, is ambiguous when the same string is used for unrelated categories, and cannot carry the machine-readable isRetryable signal that controls retry.

Boundary. The boundary where a richer string might seem sufficient is a development context where few-shot prompt examples teach the coordinator to parse wording. Evidence consistently treats that improvement as insufficient when the underlying strings are still uniform, because novel wording or a new backend will break the parsing.

Recurring specifics. Generic strings that recur include Operation failed, search unavailable, search_unavailable, request failed, metadata fetch failed, analysis unavailable, and Action failed. Improvements that recur in correct answers include adding errorCategory with values transient, validation, business, permission and an isRetryable boolean plus a description of what caused the failure and what was attempted.

Wrong answers written against this rule

Proposal. add a more detailed but still uniform natural-language description for all cases.

Why it attracts. it helps a human reader.

Why it fails. it still requires parsing to determine retryability and still produces a uniform shape for categories that need different handling. Right when: narrow true-match case.

Proposal. wrap every failure in an extra tool call such as analyze_error that classifies the prior string.

Why it attracts. it seems to add classification without changing the original tool.

Why it fails. it doubles failure-path latency and the classifier still analyzes the same opaque string that lacked signal in the first place. Right when: narrow true-match case.

Proposal. add few-shot examples to the system prompt teaching the coordinator to read wording.

Why it attracts. it requires no code change.

Why it fails. as long as the strings remain uniform, there are no distinguishing patterns to learn, and novel messages will still be misclassified. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation keeps the generic string but adds isRetryable: false uniformly, testing whether the examinee notices that transient still needs true. Another mutation keeps the generic string but adds errorCategory without isRetryable, testing whether the examinee treats category alone as sufficient. A third mutation replaces the generic string with a raw stack trace, testing whether the examinee recognizes that more text is not more structure.
R20

Raw exceptions without structured context cause coordinator paralysis

A subagent or tool handler propagates a raw exception, stack trace, or unannotated error subtype such as error_during_execution or Connection timeout without the category, attempted action, partial results, or recommendation that the coordinator needs to decide. The coordinator receives a payload that is distinguishable as a failure but not as the right kind of failure, so it cannot choose between retry with backoff, correction, reroute, or escalation, and either freezes, retries blindly, or escalates with a generic note.

Raw transport detail such as an exception class or a bare timeout message does not map one to one to recovery policy. A timeout on a retryable transient versus a timeout that exhausted a retry budget versus a permission failure that happens to manifest as a timeout all look like the same string at the transport level.

Boundary. The boundary where raw exceptions are appropriate is a framework-level fault where the tool cannot construct any structured context, such as a process crash with no output. The correct action there is to treat the entire folder or batch as unprocessed and to surface the crash with the limited context that is available, often as a portal-hung or process-crashed gap, rather than to synthesize from nothing.

Recurring specifics. Raw shapes that recur include a bare Connection timeout, a {\"type\": \"result\", \"subtype\": \"error_during_execution\", \"error\": \"Connection timeout\"} envelope, and an opaque {\"isError\": true, \"content\": \"sensor fault\"} repeated for every non-completed outcome. Correct structured shapes that recur for contrast include errorCategory transient with isRetryable: true and a payload naming the attempted query and the count of completed tests or records.

Wrong answers written against this rule

Proposal. propagate the raw timeout exception directly without catching it so the coordinator receives the original class and stack.

Why it attracts. it seems maximally informative.

Why it fails. it couples the coordinator to internal implementation detail and still does not carry the domain-level retryability or alternative that the tool author should have supplied. Right when: narrow true-match case.

Proposal. wrap execution in a try except and return a generic status after backoff exhaustion.

Why it attracts. it at least avoids a crash.

Why it fails. it replaces a raw exception with a generic string, still omitting the category, attempted query, and partials that the coordinator needs. Right when: narrow true-match case.

Proposal. add a confidence score to the error payload so the coordinator can route low confidence to human review.

Why it attracts. it feels like uncertainty handling.

Why it fails. confidence is not a substitute for category and retryability, and it does not tell the coordinator whether to retry or correct. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces the raw exception with a generic search unavailable status, testing whether the examinee recognizes that both omit the same structured fields. Another mutation adds partial results to the raw exception, testing whether the examinee still requires category and retryability alongside the partials. A third mutation moves the failure to a push_firmware style tool that collapses every non-success into one string, testing whether the examinee requires the same structured fix across non-search domains.
R21

Indeterminate outcomes require verify-before-retry, not blind retry

Some operations such as sending a notification, committing a charge, or pushing a firmware update may time out after the side effect possibly occurred, leaving the outcome unknown. The tool reports an explicit indeterminate or partial status rather than a simple success or failure, and the coordinator verifies the true outcome such as whether the notification was delivered or the charge was posted before deciding whether to retry, rather than retrying blindly and risking duplication.

Blind retry on an indeterminate operation can duplicate side effects such as double charging or duplicate notifications, while blind success assumes delivery that may not have occurred. An indeterminate signal forces the consumer to reconcile state before acting, which is the only safe path when the system's knowledge of the world is incomplete.

Boundary. The boundary where blind retry is acceptable is an idempotent read or a retry-safe operation that the upstream explicitly marks as such, such as a search or a lookup that can be safely re-issued. The nearby opposite case where verify-before-retry is required is a write that is known to be idempotent via a correlation token or idempotency key; there the correct action is to retry with the same key so the upstream can suppress duplicates for.

Recurring specifics. Signals that recur for this rule include an indeterminate status, a retry_safe flag that must be checked, and a prompt to verify first such as check whether the message was delivered or query carrier for existing events. Anti-pattern phrasings that recur include always return success on timeout since delivery usually worked and encourage the agent to retry the send whenever it is unsure, both judged as causing duplicates.

Wrong answers written against this rule

Proposal. return success on timeout since the notification usually was delivered.

Why it attracts. it preserves a simple success path.

Why it fails. when delivery did not occur the consumer will never retry and the user will never be notified. Right when: narrow true-match case.

Proposal. add a retry_safe flag and let the consumer retry freely.

Why it attracts. it seems to mark safe operations.

Why it fails. when the flag is absent or the outcome is genuinely unknown, free retry still risks duplication. Right when: narrow true-match case.

Proposal. retry the send immediately whenever unsure.

Why it attracts. it maximizes eventual delivery.

Why it fails. it maximizes duplicate delivery on indeterminate writes. Right when: narrow true-match case.

How the same rule gets re-asked
  • One mutation replaces the indeterminate write with a retryable read, testing whether the examinee correctly flips from verify-before-retry to bounded retry. Another mutation adds an idempotency token to the write, testing whether the examinee moves to idempotent retry with the token. A third mutation keeps the indeterminate shape but adds partial results, testing whether the examinee still verifies before retrying the missing slice.
R22

Already-current or idempotent success must not be modeled as an error

A tool is asked to perform work that is already satisfied such as pushing firmware already on the target build or validating a record that needs no change.

Modeling a satisfied state as an error pollutes failure metrics, triggers unnecessary retries, and causes the coordinator to re-queue work that is already done.

Boundary. The opposite case where an error is required is a true permission block such as a sensor locked by a safety controller.

Recurring specifics. Tool push_firmware with three collapsed outcomes is the canonical example: transient drop, already-current, and locked. Correct separation keeps already-current as isError: false while the other two are typed errors. Payloads that mark already-current with an error category are consistently judged wrong.

Wrong answers written against this rule

Proposal. return all three non-completed outcomes with an error category including already_current.

Why it attracts. it unifies handling.

Why it fails. it lets already-current be retried or counted as a failure. Right when: never for a satisfied state.

Proposal. keep a single failure string but add a recovery_action of mark_done.

Why it attracts. it seems actionable.

Why it fails. the envelope is still an error, so generic error handling will count it as a failure. Right when: never as the primary signal.

How the same rule gets re-asked
  • One mutation replaces already-current with a transient drop, testing whether retry is correctly chosen. Another mutation keeps already-current but adds isRetryable: false to an error, testing whether the examinee still requires isError: false.
R23

Empty result is a valid successful outcome when the query matched nothing

A query reaches the data source, executes with the given parameters, and the source correctly answers that no records match.

No matches is information, not a failure.

Boundary. The opposite case is an access failure that happens to return an empty array because the error was swallowed.

Recurring specifics. Search for rare skills such as Cobol, inventory lookup for a nonexistent customer, and pricing stability all recur as valid empties. The anti-pattern that recurs is returning isError: true for a valid empty, which forces unnecessary retry or escalation.

Wrong answers written against this rule

Proposal. return an error whenever zero results are found.

Why it attracts. it guarantees attention.

Why it fails. it forces retry of a correct answer that will always be empty. Right when: never for a verified empty.

Proposal. crash so the coordinator retries.

Why it attracts. it seems to force recovery.

Why it fails. there is nothing to recover; the query already succeeded. Right when: never for valid empty.

How the same rule gets re-asked
  • One mutation replaces the valid empty with a permission-blocked empty, testing the flip to error. Another mutation adds a suggestion to broaden the query, testing whether the examinee still treats the original empty as success while optionally trying a broader query as a separate decision.
R24

Rate limiting and transient overload are retryable with backoff and Retry-After

A 429 rate limit or a 500, 503, or 529 overload is reported as errorCategory: transient with isRetryable: true and, when provided, a retryAfterMs or Retry-After header.

These failures are explicitly time-dependent and the same request is likely to succeed after the indicated delay.

Boundary. The opposite case is a validation or permission failure that also happens to return 400 or 403.

Recurring specifics. 429 with strict per-minute limits, 500 with temp_db_lock, and 529 overloaded all recur. Correct handling respects retryAfterMs and uses jitter to decorrelate bursts. Uniform retry of all errors is the recurring anti-pattern.

Wrong answers written against this rule

Proposal. retry both 429 and 500 immediately with no delay.

Why it attracts. fastest.

Why it fails. it hammers the service and creates a thundering herd. Right when: never for rate limited or overloaded services.

Proposal. retry 429 by waiting the Retry-After duration and treat 500 as permanent.

Why it attracts. it handles rate limit correctly.

Why it fails. 500 with a transient body is retryable; giving up wastes a recoverable request. Right when: never when the body indicates transient.

How the same rule gets re-asked
  • One mutation adds a Retry-After header to the 429, testing header versus exponential choice. Another mutation replaces 429 with 400 invalid syntax, testing non-retryable handling. A third mutation makes the overload sustained, testing switch to circuit breaker.
R25

Per-item status tracking enables surgical retry of failed items only

A batch of many items such as 50,000 contract documents or 500 invoices carries a per-item identifier such as custom_id and a result manifest with per-item status, timestamps, and retry counts stored in an append-only ledger.

Reprocessing the entire batch wastes successes, re-incurs cost, and risks re-hitting rate limits.

Boundary. The opposite case where batch retry is not sufficient is a gap that was never assigned to any item because decomposition missed a category.

Recurring specifics. custom_id correlation, result: {type: succeeded} versus type: errored, per-item manifest with status and retry count, and chunking of oversized documents all recur. Batch sizes of 50,000 and 500 with small failure counts are the recurring test harness.

Wrong answers written against this rule

Proposal. resubmit the entire batch on any partial failure.

Why it attracts. simpler.

Why it fails. it wastes tens of thousands of successes and reintroduces the same failure for oversized items that need chunking. Right when: never when per-item identifiers exist.

Proposal. switch the whole job to synchronous calls.

Why it attracts. seems more reliable.

Why it fails. it does not fix per-item failure causes and increases latency dramatically. Right when: never as a fix for batch partial failure.

How the same rule gets re-asked
  • One mutation changes the failure cause from context limit to transient timeout, testing whether resubmission without chunking is correct. Another mutation changes the batch to an unmapped threat category, testing whether the examinee recognizes that no custom_id retry will recover a never-assigned item.
R26

Checkpointing preserves completed steps so retry resumes from last good state

A multi-step pipeline checkpoints after each stage and makes downstream steps idempotent via correlation tokens or idempotency keys.

Restarting from the first stage wastes work, re-incurs cost, and can create duplicate side effects if a prior stage performed a write.

Boundary. The opposite case where checkpointing alone is insufficient is an atomic transaction where partial commit would leave inconsistent state.

Recurring specifics. Checkpoint after validation, idempotent retry with a correlation token, streaming mid-failure preservation of partial output at 60 percent, and result monad Ok/Error forcing explicit handling all recur.

Wrong answers written against this rule

Proposal. restart the entire pipeline from the beginning on any later-stage failure.

Why it attracts. simpler state.

Why it fails. it wastes earlier successes and can duplicate writes. Right when: only when checkpoints are unavailable and idempotency cannot be guaranteed.

Proposal. send all stages to a dead-letter queue for manual inspection.

Why it attracts. seems safe.

Why it fails. it halts automation for a transient failure that a targeted retry would resolve. Right when: only after retry budget is exhausted for non-transient failures.

How the same rule gets re-asked
  • One mutation replaces a transient stage failure with a deterministic malformed rule, testing whether retry is replaced by isolation and a warning. Another mutation replaces checkpoint resume with whole-pipeline retry, testing waste recognition.
R27

Timeout errors must be propagated as structured transient failures

A timeout is not an empty result.

Timeouts are the most common transient and the most commonly swallowed.

Boundary. The opposite case where a timeout is not retried is a timeout on a non-idempotent write that timed out after the side effect may have occurred, where verify-before-retry applies.

Recurring specifics. 30 second timeouts, 12 second tool that exceeds a 10 second SLA, and per-subagent wall-clock timeouts that cancel slow agents and proceed with successes all recur. The correct structured timeout payload with errorCategory: transient and isRetryable: true is the recurring expected fix.

Wrong answers written against this rule

Proposal. treat the timeout as a valid empty and continue.

Why it attracts. keeps the pipeline moving.

Why it fails. it hides an access failure as a successful absence. Right when: never for a timeout.

Proposal. retry immediately in a tight loop.

Why it attracts. seems fastest.

Why it fails. it reproduces the same timeout on a large request and hammers the service. Right when: never without backoff or decomposition.

How the same rule gets re-asked
  • One mutation replaces a read timeout with a notification send timeout, testing verify-before-retry. Another mutation replaces a single timeout with a sustained outage, testing circuit breaker versus bounded retry.
R28

Decommissioned, missing, or permanently unavailable resources are not retryable

A request against a decommissioned SKU, a retired service pipeline, a missing table, or a permanently unavailable archive is reported as isError: true with errorCategory such as business, validation, or permission and isRetryable: false, often with a detail such as a decommission date or a pipeline retirement reason.

These failures are deterministic with respect to the resource: the same request will fail the same way forever regardless of delay.

Boundary. The opposite case is a transient outage of the same resource class such as a warehouse API 503 for an otherwise active SKU, which is retryable.

Recurring specifics. Decommissioned SKU, retired service, pipeline_decommissioned category, missing table 404, and SKU exceeding a cap all recur as non-retryable, versus 503, 429, and timeout as retryable. The payload that carries a decommission date or a retired-service reason is the recurring correct shape.

Wrong answers written against this rule

Proposal. retry a decommissioned SKU with backoff in case it becomes available.

Why it attracts. seems resilient.

Why it fails. the SKU will never become available via retry; only a different product or source will help. Right when: never for a permanently unavailable resource.

Proposal. treat a decommissioned resource as a valid empty.

Why it attracts. it avoids an error.

Why it fails. it misreports absence of the resource as absence of data, leading to confident misclassification. Right when: never when the caller lacks permission or the resource is known to be retired.

How the same rule gets re-asked
  • One mutation replaces a decommissioned SKU with a transient warehouse timeout, testing the flip to retryable. Another mutation replaces a missing table 404 with a 500 temp_db_lock, testing transient override. A third mutation keeps the decommissioned shape but adds partial results from other SKUs, testing degraded delivery with an annotated gap.
Research pipeline where EU policy is preserved through a transient timeout
A production walkthrough with the reasoning chain made explicit.

A web search subagent in a multi-agent research system is researching renewable energy policy. The subagent attempts to query an academic database for sources on the EU Renewable Energy Directive. The first query succeeds and returns two sources. The second query times out after 30 seconds because the database is overloaded. The third query also times out. Local retry logic with exponential backoff has been exhausted.

The wrong response on one end is silent suppression: catch the timeout and return results empty with status success. The coordinator believes the subagent completed successfully and found nothing on EU policy, so it does not retry, does not try alternative sources, and produces a synthesis that omits EU policy entirely. The final report looks comprehensive but has a gap that nobody catches until the customer asks about EU policy and the agent cannot answer. The wrong response on the other end is workflow termination: propagate the exception to a top-level handler that aborts the entire workflow, throwing away results from three other subagents on US, China, and India policy that completed successfully.

The correct response is structured error propagation. The subagent returns partial_failure with failureType transient, attemptedAction carrying tool search_academic_db and query EU Renewable Energy Directive 2023 with date range 2022 to 2024, partialResults carrying the two successfully retrieved sources from EUR-Lex and a JRC Technical Report, alternativeApproaches listing retry with narrower date range 2023 to 2024 and search alternative database government_publications and use cached results from a previous session, and shouldRetry true.

The coordinator examines the failure type as transient with retry possible, checks partial results as two useful sources already retrieved, evaluates alternatives, and selects retry with the narrower date range. The retry succeeds and returns one more source. The synthesis output includes a coverage annotation stating section on EU renewable energy policy is well supported with three sources from EUR-Lex, JRC, and the academic database and noting that one transient retrieval failure during research was resolved by retrying with a narrower date range. The same failure with silent suppression would have produced a synthesis missing the EU section entirely and a coverage annotation would have exposed the gap by construction.

Distinctions that decide answers

ThisNot thisHow to tell them apart
Access failureValid empty resultAn access failure means the tool could not reach the source, such as timeout or permission denied, and the query did not execute. A valid empty result means the tool reached the source and found no matches. The first has isError true and shouldRetry true, the second has status success with empty results and shouldRetry false.
Silent suppressionWorkflow terminationSilent suppression hides the failure by returning empty results marked as successful. Workflow termination exposes the failure by killing the entire pipeline. Both are anti-patterns, silent suppression is worse because it is invisible. Structured propagation with partial results is the correct middle ground.
Local retry in the subagentCoordinator retryLocal retry is exponential backoff within the subagent handling transient failures before propagation. Coordinator retry is invoked after receiving a structured error and evaluating failure type, partial results, and alternatives. Local handles known transient patterns, coordinator handles novel recovery.
Transient failurePermanent failureTransient failures such as timeouts and rate limits resolve on retry. Permanent failures such as validation errors and permission denials will not resolve on retry and need a different intervention. The errorCategory and isRetryable fields distinguish them.
Coverage annotationSilent omissionA coverage annotation explicitly states which topic areas have gaps and why. Silent omission leaves the gap invisible, looking like the topic was not relevant rather than the source being unavailable.
Exponential backoff with jitterFixed delay retryBackoff with jitter increases wait times across retries and adds randomisation to prevent synchronised storms. Fixed delay uses a constant wait and produces synchronised load spikes on a recovering service.

Traps

Catch a timeout and return empty results marked as successful

The tempting answer. Return success with empty results to keep the workflow moving because that avoids error handling complexity.

Why it fails. Silent suppression prevents all recovery. The coordinator believes the search succeeded and found nothing, so it will never attempt alternatives. This is the worst anti-pattern because the gap is invisible and downstream synthesis silently omits an entire area.

What is correct. Return a structured error with status error, failureType transient, isRetryable true, and the partial results preserved, so the coordinator can retry or try alternatives.

Terminate the entire pipeline when one subagent times out

The tempting answer. Abort the workflow on any failure because that is the simplest failure handler to implement.

Why it fails. Workflow termination wastes partial results from subagents that completed successfully. A single transient timeout should not discard work from four other subagents.

What is correct. Propagate the structured error and let the coordinator make a targeted recovery decision such as retry with modified query or proceed with partial results.

Return a generic search unavailable status after retry exhaustion

The tempting answer. Return a generic failure message because it feels like enough detail for a failure.

Why it fails. Generic errors hide the query, partial results, and alternative approaches from the coordinator. Without structured context the coordinator cannot make an informed recovery decision.

What is correct. Include all four elements: failure type, attempted action, partial results, and alternative approaches, so the coordinator has actionable recovery options.

Retry a valid empty result because empty feels suspicious

The tempting answer. Treat any empty result as a failure that should be retried because empty looks like it might be wrong.

Why it fails. A valid empty result means the query executed successfully and found no matches. This is the answer. Retrying wastes time on a query that will always return nothing.

What is correct. Distinguish access failures where you retry from valid empty results where you do not, using the structured response fields isError and shouldRetry.

Propagate every transient failure immediately without local retry

The tempting answer. Skip local retry and propagate immediately because propagating feels faster than waiting for backoff.

Why it fails. Most transient failures resolve on the first or second retry. Propagating immediately floods the coordinator with recoverable errors that the subagent could have handled itself.

What is correct. Implement local retry with exponential backoff and jitter within the subagent and propagate only persistent errors that survive local recovery.

Assume a complete-looking synthesis means silent suppression is acceptable

The tempting answer. Accept silent suppression when the output looks complete because visible quality feels like enough.

Why it fails. The gap is invisible in the output but downstream consequences are severe, such as a customer asking about EU policy and the agent having no answer because the source was silently dropped.

What is correct. Require coverage annotations on every synthesis output so gaps are visible by construction, and verify that every topic area either has sources or an explicit gap note.

Going deeper than the task statement
Adjacent material that shows up in harder scenario questions.
isError, errorCategory, and isRetryable field conventions

errorCategory includes transient, permanent, auth, not-found, and validation, where not-found can mean genuinely absent or permission-masked absence and needs enough context for the coordinator to distinguish, and isRetryable drives the retry decision explicitly.

Tool Error Handling
Model downgrade and circuit breaker as complementary resilience

Model downgrade falls back from Opus to Sonnet to Haiku when a primary model returns repeated transient failures, and circuit breaker opens after a threshold, allows test requests after a cooldown, and closes on recovery to protect downstream services from cascading failures.

Fallback Patterns
Validation failure as a structured error with the same shape

When Claude output fails schema or semantic validation the pipeline returns a structured error with the validation message, the failed output, and a recommended fix, the same propagation discipline as tool errors.

Validation Pipelines
Build it
Prove structured propagation with partial results beats both anti-patterns
  1. Define a structured error schema with status success or error, failureType from transient, validation, business, and permission, attemptedAction with tool, query, and parameters, partialResults array, alternativeApproaches string array, and isRetryable boolean that rejects malformed errors.
  2. Implement a subagent that distinguishes access failures such as timeouts and connection errors reported with isError true, errorCategory transient, and isRetryable true from valid empty results reported with status success, empty results array, and isRetryable false, tested with the same input shape producing different outputs.
  3. Build local retry with exponential backoff and jitter for three retries at 1 second plus jitter, 2 seconds plus jitter, and 4 seconds plus jitter, preserving partial results across retries and propagating the final structured error with those results after exhaustion.
  4. Create a coordinator that examines failureType, checks partialResults, evaluates alternativeApproaches, and selects recovery: transient to retry with modified query, permission to immediate escalation, validation to query fix, and business to alternative approach, never silently suppressing.
  5. Add coverage annotations to synthesis output listing each topic area as well supported, limited with reason, or unavailable with reason, and simulate a timeout in one subagent to verify the coordinator preserves the partial sources, retries or escalates correctly, and the annotation for that section documents the recovery.

Verify. Transient timeouts are recovered with partial sources preserved, valid empties are respected as answers, and any remaining gap is visible in the coverage annotation rather than silently omitted.

Answer real questions on this task
8 exam-style questions written against the rules above. You get the reasoning and a note on every wrong option after each answer.
Task 5.414 min

Codebase Exploration and Context Degradation

Treat verbose exploration as a context quality problem and contain it with scratchpads, isolated subagents, and injected summaries.

What you need to know

Extended codebase exploration is one of the most context-intensive tasks a Claude-based agent performs, and it produces a specific failure mode that has nothing to do with running out of tokens. Context degradation is the model losing its grip on earlier findings as the conversation fills with verbose discovery output. It manifests as a concrete observable behaviour: the model starts referencing typical patterns instead of the specific classes, methods, and dependency chains it discovered earlier. After investigating several modules an agent might say this follows the typical repository pattern instead of the OrderRepository class at src slash repos slash order dot ts implements the Repository interface with custom caching in the findById method.

Context degradation happens because each exploration step generates verbose output such as file contents, search results, and directory listings that accumulates in the conversation context. Earlier precise discoveries are pushed further into the context while more recent verbose output dominates. The model's attention shifts to recent output and it loses specific references to earlier findings. The critical insight is that increasing the context window does not fix degradation. The model is not running out of space, it is losing track of specific details as they get buried under newer more verbose output. The fix is structural, not capacity based.

The primary mitigation is scratchpad files. The agent writes key findings to a file and references it for subsequent questions. This persists knowledge outside the conversation context and makes it immune to degradation. A scratchpad for codebase exploration typically captures three categories: key classes and their locations with class name, file path, and brief description, dependency chains stating which classes depend on which, and critical findings such as bugs, missing tests, and architectural concerns. The scratchpad is a deliberate strategy from the start of any extended exploration session, not a rescue move once things degrade.

Subagent delegation is the second major mitigation. Instead of the main agent doing all exploration directly and filling its context with verbose output from every file read and search, delegate specific investigation tasks to subagents such as find all test files for the order service and report their coverage status, trace the refund flow from API endpoint to database and list all intermediate services, and identify all external API integrations and their error handling patterns. Each subagent operates with its own isolated context, can explore verbosely without polluting the main agent's context, and returns a structured summary to the coordinator. The obvious reading is parallelisation for speed, the real value is context isolation. The main agent's context stays clean for high-level coordination while subagents handle the verbose exploration.

Summary injection between phases prevents the cold start problem when exploration happens in phases. Phase 1 might be understanding the architecture, phase 2 might be investigating specific components. Summarise key findings from Phase 1 before spawning Phase 2 subagents and inject those summaries into the initial context of Phase 2 subagents. Without injection Phase 2 duplicates Phase 1 exploration because it was not given the previous findings. The injection ensures Phase 2 agents have the architectural understanding needed to ask the right questions without rediscovering the system structure. The compact command in Claude Code provides API-level summarisation during extended sessions, use it proactively during exploration not only when hitting limits, because it protects context quality not only quantity. Crash recovery via structured state manifests is the resilience layer: each agent exports its current state to a known file location with what has been explored, key findings, current phase, next steps, and pending questions, so on resume the coordinator loads the manifest and the agent picks up where it left off.

Scratchpad discipline and what counts as a finding

The scratchpad file is a runtime discipline using ordinary file operations such as create, view, str_replace, and delete exposed through the file system API. A common shape is Markdown with explicit sections for key classes, dependency chains, and critical findings. The agent writes after each discovery and reads at the start of each subsequent step. The discipline fails if the scratchpad tries to hold the entire history verbatim, because that re-introduces the verbose output problem in a different file. Capture only specific findings the agent needs to reference across turns, not every intermediate step.

A scratchpad that captures everything is a history dump, a scratchpad that captures specific findings is a reference. The three categories keep the file lean: class name plus file path plus one-line description, dependency edges such as OrderService orchestrates OrderRepository and PaymentGateway, and critical findings such as RefundProcessor has no retry logic for Stripe failures or RefundProcessor test coverage 12 percent. When the agent at turn 16 reads the scratchpad, the specific identifiers are restored regardless of how much verbose output has accumulated since they were discovered.

Subagent delegation for isolation and summary injection between phases

Subagent delegation in Claude Code uses the Task tool with a focused role description and task prompt. The role should name the investigation target and the expected return format as a structured summary with key findings, file paths, and class names. The coordinator receives the summary without the verbose intermediate file contents and search results that would otherwise pollute its context. The primary benefit for codebase exploration is isolation, parallel execution is secondary and only available when investigations are independent.

Summary injection is the application-side mechanism that places Phase 1 findings into Phase 2 prompts. After Phase 1 the coordinator extracts key findings from the Phase 1 scratchpad and prepends them as a Phase 1 Summary section. The Phase 2 subagent receives both the architectural understanding and the specific investigation target so it does not need to rediscover system structure. Without injection the same files are read again, the same searches are run again, and API cost and time are wasted on duplication that also pollutes the new subagent's context.

Proactive compaction and crash recovery manifests

The compact command is invoked via slash command inside Claude Code and triggers conversation-level summarisation that preserves decisions, findings, and tool calls while discarding verbose intermediate output. Proactive use at turn 10 and turn 20 preserves attention to specific findings throughout the session, while reactive use only when the context is nearly full is a last resort that may have already lost the findings because verbose output has pushed them into the middle where positional degradation applies. That slash command is one surface, not the whole feature. For an application that is not running inside Claude Code, the programmatic equivalent is the compact_20260112 strategy in context_management.edits, which triggers on an input token threshold you configure rather than on you remembering to type a command.

The crash recovery manifest is a JSON file at a known location with sessionId, phase, exploredPaths as an array of file paths, keyFindings with architecture, criticalIssue, and testCoverage, and nextSteps as an array of pending targets. On resume the coordinator reads the manifest and injects the structured state into the agent's initial prompt. Upstream agents that return verbose reasoning chains rather than structured summaries defeat the same principle downstream: downstream agents with limited budgets cannot use verbose prose, so upstream agents should return key facts, file paths, and relevance scores that downstream agents can process without re-parsing.

Two documented API surfaces implement this pattern outside Claude Code. Server-side compaction, strategy type compact_20260112 under the compact-2026-01-12 beta header, triggers at a configured input-token threshold and produces a structured continuation summary whose documented shape is Task Overview, Current State, Important Discoveries, Next Steps, and Context to Preserve. That is the same five-part shape a hand-rolled manifest needs, so use it as the schema rather than inventing one. The memory tool, type memory_20250818, is the supported surface for the scratchpad itself: the model issues view, create, update, and delete requests against paths under /memories and your application executes them against storage you control, which is why the handler must reject any path that escapes /memories. Documentation frames this as just-in-time context retrieval, which is exactly the argument for a scratchpad, load what the current step needs rather than carrying everything.

Authoritative mechanism reference

The sections below expand the tested rules into the complete documented surface: ownership, lifecycle, version differences, production boundaries, and adjacent mechanisms.

Mechanism reference: Context degradation versus a token limit failure

The reference page draws a sharp line between context degradation and running out of tokens. Documentation supports the distinction but gives the two failure modes precise names and mechanics.

A token limit failure is a hard error. When a request would exceed the model's context window, the API returns an error (input over limit returns a 400 prompt is too long on the API; in Claude Code, server-side compaction handles overflow as the limit is approached) . This is a storage failure: there is physically no room for the content.

Degradation is the documented context rot effect. The context window documentation and the compaction guidance describe a monotonic loss of quality as token count grows: accuracy and recall degrade as the count grows, so curating what is in context matters as much as how much room is left . The effective context engineering writing describes the same phenomenon, noting that as a conversation grows, response quality degrades, which is why compaction replaces older content with a concise summary .

The critical grounding point the reference page makes but does not source: context rot is a position-attention effect, not a capacity effect. The window is not necessarily full when degradation appears. Early, precise findings compete with a growing volume of later tokens for attention weight, and the later tokens win. Enlarging the window only delays the onset because the relative recency gap still grows with every new file read. The window size changes when degradation starts, not whether it starts. This is why the exam trap of pre-loading the entire codebase or switching to a larger-context model is wrong: it addresses capacity, not salience.

The diagnostic signal is language shift. Early in a session the agent names OrderRepository, RefundProcessor, and findById; later, after verbose output accumulates, it says the module follows standard patterns. The move from a specific identifier to a generic category word is the tell. Generic phrasing is cheap for the model and well supported by training data; specific identifiers from early in the session require attending to buried tokens, which salience loss prevents .

Mechanism reference: Scratchpad files as externalised context

A scratchpad is a file on durable storage that the agent writes discovered facts to as it explores and reads back when a later step needs them. The mechanism is that a file re-read at the point of use is as fresh at turn 80 as at turn 8, whereas conversation history degrades in salience as it grows. External storage converts passive buried memory into actively referenced memory .

The load-bearing detail is what the scratchpad records and the read-back discipline. A scratchpad that merely duplicates conversation without being read is useless. A scratchpad that records only narrative prose loses the very specifics the session needs. The correct content pairs a name with a location, for example OrderRepository (src/repos/order.ts), and captures dependency edges and verified call relationships, not summaries. The recurring instruction is to re-read the scratchpad at the start of each subsequent step.

This is exactly the documented memory tool pattern, which the reference page treats as a local convention but which documentation names as just-in-time context retrieval. The memory tool is the supported, model-driven surface for the same idea, with the important detail that execution and storage are the application's responsibility and path containment is a security requirement. Grounding for the memory tool appears in its own subsection below.

Mechanism reference: Subagent isolation

Subagent delegation is the second mitigation. Documentation is explicit: each subagent runs in its own context window with a custom system prompt, specific tool access, and independent permissions. When the model encounters a task that matches a subagent's description, it delegates; the subagent works independently and returns results.

The value is a separate window whose intermediate output never enters the parent. Only a concise summary returns to the main conversation. The main context stays clean for high-level coordination. Parallel speed is real but secondary; the deeper reason is isolation .

This is reinforced by the multi-agent isolation lesson, which shows the orchestrator context holding only aggregated final outputs, while each agent context holds its own system prompt, task brief, own tool results, and own reasoning, with a hard rule that no direct channel connects agents . The lesson also states the security consequence: a formatting subagent given a customer's full payment details is a leak, which is why each agent should receive only the context it needs .

Isolation has a cost: shared state. A subagent does not see the parent's history unless that history is explicitly injected, so un-injected facts are invisible . This is why the tool allowlist and the prompt must be deliberate. The Claude Code sub-agent tools field is the allowlist; an agent whose tools list omits Read, Grep, and Glob simply cannot pollute the parent with verbose discovery, and a read-only explorer such as the built-in Explore agent denies Write and Edit .

A subagent must return only a structured summary, never the raw transcript. If it returns every file it read along with full excerpts, the main context is re-polluted exactly as if the work had been done inline. The contract is: absorb verbose output in isolation, return only conclusions.

Mechanism reference: Summary injection between phases

When exploration is phased, the coordinator summarises the completed phase's key findings and injects that summary into the initial context of the next phase's agents. Phase 2 then starts with architectural understanding instead of re-doing Phase 1 .

The injected summary is a factual carry-forward, not a transcript. It typically lists the architecture, the key concern, and the next objective. The wrong alternative is passing the full phase-one transcript to each phase-two subagent, which floods context and defeats isolation. This is the live phase-handoff form of the crash-durable manifest described below; summary injection is for live handoffs, the manifest is for crash recovery.

Mechanism reference: The /compact command and auto-compaction

Claude Code provides /compact, which summarises the conversation so far into a compressed form and replaces the history with that summary, freeing context space while preserving key findings. The guidance is to use it proactively during extended sessions, not only when the limit is reached .

/compact is one surface. The documentation is explicit that automatic compaction also runs when a session approaches the window limit: a summary prompt is injected, the API generates a structured summary, and that summary replaces the message history, using the same underlying mechanism the manual command exposes . The lesson also notes /compact is lossy and cannot recover an already-degraded session, because it compresses whatever is currently in context; a session already producing generic references will be summarised into further generic form . For shared, lengthy state that must survive, the external scratchpad or memory tool is required.

Mechanism reference: Server-side compaction as the programmatic equivalent

The API-level compact_20260112 strategy is the programmatic equivalent of /compact for applications that are not running inside Claude Code. It is the documented primary strategy for long-running conversations . Where /compact belongs to the Claude Code CLI surface, compact_20260112 belongs to the Messages API surface. Both perform the same summarisation; the layer differs. The full mechanism has three moving parts. First, the trigger: the API watches input_tokens against the configured threshold (default {"type": "input_tokens", "value": 150000}, minimum 50,000) and fires compaction when crossed . Second, the emitted boundary: the API generates a summary, emits a compaction block, and continues the response; on later requests it drops all blocks prior to the compaction block, which is the truncation boundary. Third, the summary shape: the default prompt produces the five-part structure (Task Overview, Current State, Important Discoveries, Next Steps, Context to Preserve) wrapped in <summary></summary> tags, and instructions can replace it entirely (Example 4).

A documented limitation matters for codebase exploration that uses server-side tools. After a web search or similar server-side tool call, the SDK may add cache_read_input_tokens accumulated across the tool's internal calls to the total, so the input count can read several hundred thousand tokens when the real conversation context is far smaller. The trigger then fires prematurely and compacts a session that did not actually need it. The workaround is to verify true size with the token counting endpoint before relying on automatic compaction, or to avoid client-side compaction when heavy server-side tool use is expected. A candidate who knows only that compaction runs automatically may be surprised by early truncation; the token counting endpoint is the guard.

Mechanism reference: The memory tool, grounded exactly

The memory tool is the documented surface that implements the scratchpad pattern as a first-class capability. The grounding must be exact because the exam expects precise facts.

  • Tool type. The tools entry is {"type": "memory_20250818", "name": "memory"}. The name must be memory, and you do not define an input schema for an Anthropic-provided tool. The tool is available on Claude 4 and later models.
  • Client-side execution. The memory tool is client-side: the model requests file operations and your application executes them. Claude only requests operations; your application executes each request against storage it controls and returns the result in a tool_result block. The memory files live entirely in your application; a later conversation continues from the same memory when it sends the same tools entry and your handler serves the same store.
  • Memory directory prefix. The /memories path is a prefix that the handler maps onto real storage, such as a per-user directory or keys in a database. The model calls operations such as view, create, str_replace, insert, delete, and rename under /memories, and your handler translates these onto whatever backing store you choose.
  • Path containment requirement. For security, all memory operations must be restricted to the /memories directory. A malicious path such as /memories/../../secrets.env can reach files outside the directory, so the implementation must validate every path in every command. Recommended safeguards: validate that all paths start with /memories, resolve paths to their canonical form and verify they remain within the memory directory, reject traversal sequences such as ../ and ..\\, and watch for URL-encoded traversal such as %2e%2e%2f.
  • Documented framing. The memory tool supports just-in-time context retrieval. Rather than loading all relevant information up front, an agent records what it learns in memory files and reads them back on demand. This keeps the active context focused on the current task, which matters for long-running sessions that would otherwise overwhelm the window.

The six commands the handler must implement are view, create, str_replace, insert, delete, and rename, each with specified return strings and error behaviour. For directories, view returns a listing with sizes; for files, it returns contents with six-character right-aligned line numbers separated by a tab. A production handler must add the path validation that demonstration stores skip.

Crucially, the memory tool pairs with compaction. Context editing clears specific tool results on the client; compaction summarises the whole conversation on the server when the window limit is approached. For long-running agents, use both: compaction keeps the active context small without client-side bookkeeping, and memory preserves the information that must survive summarization.

Mechanism reference: Context editing for tool results

Context editing is the fine-grained alternative to compaction, available as a beta with header context-management-2025-06-27. It has two strategies: clear_tool_uses_20250919 for tool result clearing and clear_thinking_20251015 for thinking block clearing.

Tool result clearing takes these configuration options:

  • trigger with {"type": "input_tokens", "value": N} sets when clearing activates. The documented exemplar uses a threshold such as 30000 input tokens.
  • keep with {"type": "tool_uses", "value": K} keeps the K most recent tool-use and result pairs after clearing. This is the knob that preserves recent context while shedding older noise.
  • clear_at_least with {"type": "input_tokens", "value": M} ensures a minimum number of tokens is cleared each time. It exists because clearing invalidates the cached prompt prefix at the clearing point, so you clear enough to make the cache write worthwhile.
  • exclude_tools is an array of tool names whose results are never cleared. This is how you protect the one tool whose results must persist across the whole session, for example a web search whose findings the agent still reasons from.
  • clear_tool_inputs (optional, default false) also clears the tool call parameters, not only the results.

Cleared results are replaced with placeholder text indicating to Claude that the content was removed. The API clears the oldest tool results in chronological order.

The cache invalidation behaviour is strategy-specific. Tool result clearing invalidates the cached prefix at the clearing point, which is why clear_at_least exists: clear enough to be worth the cache write. Thinking block clearing preserves the cache while blocks are kept, and invalidates at the clearing point when they are not kept . Thinking block retention is model-dependent: Opus 4.5 and later, Sonnet 4.6 and later, Fable 5, Mythos 5, and Mythos Preview keep prior thinking blocks by default and they count as input tokens; earlier Opus and Sonnet models and all Haiku models strip them automatically when passed back. Set keep explicitly when code spans model tiers .

Context editing happens server-side, before the prompt reaches the model. Your client keeps its own full unmodified history and does not sync with the edited version. You continue managing the full conversation locally as usual.

Mechanism reference: Two layers: Claude Code auto-compaction and the API strategy

The reference page presents /compact as the only compaction lever. Documentation names two layers:

  • Claude Code surface. /compact and automatic compaction inside Claude Code. This is the CLI layer, used when the agent runs in Claude Code .
  • API strategy. compact_20260112 passed in context_management.edits on the Messages API with the compact-2026-01-12 beta header. This is the programmatic layer for applications not running inside Claude Code.

Both are the same summarisation mechanism; the difference is which layer owns the trigger and the emitted block. The exam-relevant judgement is unchanged: compaction is a last-line summariser, not a substitute for a scratchpad or structured handoff when exact identifiers must survive.

Ownership map

Which layer owns which guarantee:

  • Model. Chooses when to delegate, what to record in memory, and how to summarise. It requests memory operations and tool-use blocks; it does not execute them. Salience and the drift toward generic answers are model-internal effects that no layer can fully control, which is why externalisation is necessary.
  • SDK and application code. Executes memory tool operations against storage it controls, enforces path containment, and runs the tool-use loop. For client-side SDK compaction (TypeScript and Ruby via tool_runner with compaction_control), the SDK injects the summary request and replaces history.
  • API and infrastructure. Owns server-side compaction (compact_20260112) and server-side context editing (clear_tool_uses_20250919, clear_thinking_20251015). These run before the prompt reaches the model, on Anthropic's servers, and decide the emitted compaction block and the truncation boundary .
  • CLI (Claude Code). Owns /compact, automatic compaction, /fork, --resume, and --continue. These are the interactive surfaces a developer uses directly .
  • Configuration. CLAUDE.md, settings.json, and sub-agent frontmatter decide tool allowlists, permission modes, and auto-compaction defaults. These are author-controlled guarantees that sit above the model .

Version and terminology currency

The reference page was written against an earlier product state. Several terms and mechanisms have since changed name or status. A candidate who memorises only the reference page risks answering with superseded terminology. This section fixes the current, documented vocabulary.

Terminology drift since the reference was authored:

  • Context degradation versus context rot. The reference page uses the phrase "context degradation" and treats it as a behaviour without a name. The current documentation names the underlying phenomenon context rot: a monotonic degradation of accuracy and recall as the token count in context grows. The exam still phrases the concept as degradation, but the documented term is context rot. Both describe the same attention-salience effect; answer with the degradation framing the exam uses, but know the documented name.
  • Scratchpad files versus the memory tool. The reference page presents the scratchpad as a purely local convention the agent maintains by writing to a file. The documentation names the same pattern as just-in-time context retrieval and provides the memory_20250818 memory tool as the supported surface. The local-file scratchpad remains valid as an application-side implementation of the same idea, but the memory tool is the documented, model-driven capability.
  • /compact versus compact_20260112. The reference page treats /compact as the only compaction lever. It is one surface, owned by the Claude Code CLI. The programmatic equivalent for applications not running inside Claude Code is the compact_20260112 strategy passed in context_management.edits with the compact-2026-01-12 beta header. A candidate should name both and state which layer each belongs to.
  • Auto-compaction trigger floor. Server-side compaction has a default trigger of {"type": "input_tokens", "value": 150000} and the value must be at least 50,000. Earlier mental models that assumed compaction only fires at the very window edge are outdated; the threshold is configurable and, at default, fires well before the 200k or 1M limit.

Date-stamped identifiers a candidate may be tested on:

  • memory_20250818 is the memory tool type, available on Claude 4 and later.
  • clear_tool_uses_20250919 is the tool-result clearing strategy, and clear_thinking_20251015 is the thinking-block clearing strategy, both under the context-management-2025-06-27 beta header.
  • compact_20260112 is the server-side compaction strategy under the compact-2026-01-12 beta header.

Window size currency. As of the current documentation, the following models carry a 1M-token window as the default (no beta header, standard pricing): Opus 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 5, and Sonnet 4.6, plus Fable 5, Mythos 5, and Mythos Preview. Every other model, including Sonnet 4.5, has a 200k window. For every 1M-window model, a single request can generate up to 128k output tokens, and a request can include up to 600 images or PDF pages (100 on a 200k-window model). A larger window delays context rot but does not prevent it, which is the whole point of Task 5.4.

Client-side SDK compaction currency. Compaction in the client SDK exists only in the TypeScript and Ruby SDKs, via tool_runner with compaction_control, whose default context_token_threshold is 100,000. The Python, C#, Go, Java, and PHP tool runners do not support it; the documentation points them to server-side compaction. A candidate writing a Python agent must therefore rely on compact_20260112, not a local tool_runner flag.

Citation and attribution currency. The citations feature is first-class: set citations: {"enabled": true} on a document block and cited text returns as structured citation objects that do not count toward output tokens . Search result content blocks carry required source and title fields for your own retrieved content . These are newer enforcement points for provenance that the reference page does not mention.

Host and URL currency. The docs.claude.com host now redirects to platform.claude.com, and the Claude Code documentation now lives on code.claude.com. All citations in this document use the current platform.claude.com and code.claude.com forms. A candidate who cites the older docs.claude.com host is not wrong in substance but is citing a redirecting, superseded URL.

Exam-guide terminology versus current product terminology. The exam guide (the source of the reference page) was authored before several of these date-stamped identifiers existed. The safest approach for a candidate: answer the conceptual question in the reference-page framing the exam uses (degradation, scratchpad, /compact), then, where the question probes precision, supply the documented current term (context rot, memory tool, compact_20260112). The grounding must present the documented mechanism first and reconcile the older framing, never the reverse .

Version and terminology currency: Terminology mapping: reference page to documented

The reference page and the current documentation use different vocabularies for the same ideas. A candidate must be bilingual: able to answer in the exam's reference framing and supply the documented term when precision is tested. The mapping below is the bridge, with a note on the trap each row defeats.

Reference page termDocumented termWhat changes
Context degradationContext rotSame attention-salience effect; documented name, no published threshold
Scratchpad filesMemory tool (just-in-time retrieval)Local file is a valid implementation; memory tool is the supported surface
/compactcompact_20260112 (API) plus /compact (CLI)Two layers, one summarisation mechanism
Progressive summarisationServer-side compactionCompaction is now the documented primary strategy
Five-field provenance mappingCitations feature, search result blocksDocumented enforcement points for provenance
Token budget markerNot documentedMark not independently confirmed; do not build a rule on it

The first row defeats the terminology trap. If a question says context degradation, answer in that framing, but know the documented name is context rot and that no official token threshold is published. The second row defeats the scratchpad-versus-tool confusion: the local file is correct as an application implementation, and the memory tool is the documented, model-driven capability that generalises it. The third row defeats the compaction-layer trap: /compact is the Claude Code CLI surface, compact_20260112 is the Messages API surface, and assuming compaction is Claude Code-only is wrong. The fourth row defeats the default-strategy trap: the reference presents client-side progressive summarisation as default, but server-side compaction is now the documented primary strategy, with the application-side case-facts block still required for verbatim survival. The fifth row defeats the provenance trap: the citations feature and search result content blocks are the documented enforcement points, and prompt-preserved mappings are the fallback for content that does not arrive as documents or search results. The sixth row is the honest gap: the token budget marker from the reference is not in the verified documentation, so it is marked not independently confirmed and must not anchor a decision rule.

This bilingual fluency is what separates a candidate who memorised the reference page from one who can answer against current documentation. The exam may phrase in reference terms, but a precision question probes the documented term, and the grounding must give both without contradiction.

Official versus community divergence

The exam-change reports and the verified documentation set show several points where the reference material (and broader community write-ups) diverge from current Anthropic documentation. For each, state the documentation position, the community or reference position, and which a candidate should answer with.

Official versus community divergence: Divergence 1: the 147,000 to 152,000 token quality ceiling

  • Documentation position. No Anthropic page publishes a specific token count at which quality collapses. Documentation describes context rot as a monotonic degradation with token count and names no threshold.
  • Community/reference position. Some community material and the reference framing imply a quality ceiling of roughly 147,000 to 152,000 tokens.
  • Which to answer with. Treat the specific figure as a community estimate. Name the documented phenomenon (context rot) and state that no official threshold is published. Do not present the number as an official measurement.

Official versus community divergence: Divergence 2: client-side progressive summarisation as the default strategy

  • Documentation position. Server-side compaction (compact_20260112) is now the documented primary strategy for long-running conversations, with a documented trigger threshold, a documented five-part summary structure, and a compaction block that the API itself uses as the truncation boundary.
  • Community/reference position. The reference material treats client-side progressive summarisation as the default long-conversation strategy and treats the persistent case-facts block as the only protection against degradation.
  • Which to answer with. State the documented mechanism first (server-side compaction). Then note that the application-side case-facts block remains correct and necessary, because compaction still summarises, and anything the application must keep verbatim has to live outside the summarised region. Note that the reference omits compaction entirely.

Official versus community divergence: Divergence 3: provenance as a prompt-preserved discipline versus a documented feature

  • Documentation position. The citations feature on document blocks and search result content blocks with required source and title are the documented enforcement mechanisms for provenance .
  • Community/reference position. The reference material presents provenance as an application-side discipline of five-field claim-source mappings preserved through synthesis by prompt instruction.
  • Which to answer with. The exam-relevant judgement is unchanged: attribution still has to survive multi-step synthesis in application code. But grounding must present the documented mechanisms (citations, search result content blocks) as the preferred enforcement point, and treat prompt-preserved mappings as what you do for content that does not arrive as documents or search results.

Official versus community divergence: Divergence 4: scratchpad as local convention versus supported memory tool

  • Documentation position. The scratchpad pattern is named just-in-time context retrieval, and the memory_20250818 tool is the supported surface, with the detail that execution and storage are the application's responsibility and path containment is a security requirement.
  • Community/reference position. The reference material describes the scratchpad file for long exploration as a purely local convention.
  • Which to answer with. Both are correct in different layers. The scratchpad-as-local-file is a valid application implementation; the memory tool is the documented, model-driven capability. A candidate should recognise the local scratchpad as an instance of the memory-tool pattern.

Official versus community divergence: Divergence 5: the token budget marker and per-tool-call warning

  • Documentation position. Not documented on any of the verified pages. The model does not receive a token budget marker at session start, nor a running warning after each tool call, in the current documentation.
  • Community/reference position. The reference material describes the model receiving a token budget marker at session start and a running warning after each tool call.
  • Which to answer with. Mark this not independently confirmed and do not build a decision rule on it. It is a useful mental model for teaching, but it is not a documented mechanism and should not appear as a factual claim in grounding.

Official versus community divergence: Divergence 6: /compact as the only compaction lever

  • Documentation position. /compact is one surface, owned by Claude Code. The API-level compact_20260112 strategy is the programmatic equivalent for applications not running inside Claude Code.
  • Community/reference position. The reference material treats /compact in Claude Code as the only compaction lever.
  • Which to answer with. Keep both, and say which layer each belongs to: /compact is the CLI surface, compact_20260112 is the Messages API surface. The exam trap of assuming compaction is a Claude Code-only feature is therefore wrong.

Beyond the task statement

The reference page covers scratchpads, subagents, summary injection, /compact, and crash manifests. Our lesson material covers adjacent context-engineering ground that the reference page omits entirely. A candidate who knows only the reference page misses exam questions that test these neighbours. Each item below names the topic, what it is, why it matters for Task 5.4, and the lesson slug.

Beyond the task statement: Prompt caching as a context-cost control

Prompt caching lets you mark a stable prefix of the request with cache_control so that repeated requests against the same prefix are billed as cache reads rather than full input. For codebase exploration this matters because the system prompt, repository map, and standing instructions rarely change between tool calls, while the verbose discovery output changes constantly. Pin the stable prefix with cache_control: {"type": "ephemeral"} and let the volatile tool results sit after it; the cache stays warm even as discovery output grows. The lesson frames caching as part of the broader context stack, not as a separate concern. Without caching, every compaction-bounded request re-sends the whole stable prefix, multiplying cost as the session lengthens.

Beyond the task statement: Extended thinking and the context budget

Extended thinking produces thinking blocks that count toward the window exactly like any other input, and on models that retain them they are re-submitted on later turns. For a long exploration, leaving thinking blocks in the active context accelerates context rot because the thinking from turn 3 competes with the discovery from turn 40 for salience. The clear_thinking_20251015 strategy exists precisely to shed those blocks when they are no longer needed. A candidate must understand that turning on extended thinking for a verbose exploration has a context cost, not just a quality benefit.

Beyond the task statement: Token budgeting and the token counting endpoint

Rather than estimating context by eye, the documented practice is to budget tokens and to verify with the token counting endpoint before sending . The endpoint returns the exact input token count for a proposed request, including cached splits, so an application can decide whether to trigger compaction or context editing before the model sees an over-long prompt. Our lessons treat token budgeting as the planning layer above compression . This is the proactive alternative to the reactive /compact the reference page recommends.

Beyond the task statement: Multi-agent context isolation as a security discipline

The reference page frames subagent isolation as a context-quality win. Our multi-agent lesson adds the security half: because each subagent receives only the context it is given, a subagent handed a customer's full payment details is a leak . The same isolation that keeps verbose discovery out of the coordinator also enforces data minimization. A read-only explorer subagent should receive the repository slice it needs and nothing else, and must be denied Write and Edit so it cannot alter state . For Task 5.4 this means the isolation benefit is two-sided: cleaner context and tighter blast radius.

Beyond the task statement: Shared memory across agents versus the memory tool

The memory tool persists per-conversation facts the active agent reads back on demand. Shared memory is a different coordination primitive: multiple agents read and write a common store so that one agent's finding is visible to another without re-injection. For a phased exploration, shared memory can replace some summary-injection boilerplate, because Phase 2 agents can read the Phase 1 store directly. The trade-off is coherence: shared mutable state needs a writer discipline, whereas injected summaries are immutable snapshots. A candidate should distinguish the per-agent just-in-time memory tool from cross-agent shared memory and know when each applies.

Beyond the task statement: Agent handoffs and fork-session for recovery

The reference page's crash manifest is a hand-rolled JSON file. Claude Code provides native surfaces for the same need: /fork creates a new session from the current one, and --resume and --continue restore a prior session so a crash does not lose context . The agent handoffs lesson covers the live equivalent: transferring control to another agent or session with a structured summary so the receiving side starts informed . For Task 5.4, the manifest in the build exercise is the application-layer version of --resume; in Claude Code you get it for free, and in a custom application you implement it explicitly.

Beyond the task statement: Durable execution as the production-grade manifest

The crash manifest is a manual pattern. The Agents SDK provides durable execution: an agent's workflow state survives process restarts because steps are recorded and replayed. For a long-running exploration service, durable workflows remove the need to hand-roll a manifest, because the SDK tracks which steps completed. A candidate who can contrast a hand-written JSON manifest with SDK-native durability shows production depth beyond the reference page.

Beyond the task statement: Citations and search result content blocks for provenance

When exploration pulls from retrieved documents or internal knowledge bases, provenance must survive. The citations feature on document blocks and search result content blocks with required source and title are the documented enforcement points . These matter for Task 5.4 because a scratchpad that records "OrderRepository caches by orderId" should carry a pointer to the file it came from, and the documented mechanisms give that pointer structure rather than a free-text note. This is the documented resolution of Divergence 3 above.

Beyond the task statement: Context editing as the fine-grained lever

Beyond /compact and compact_20260112, context editing gives field-level control: clear the oldest tool results but keep the most recent K, exclude one tool whose results must persist, and clear enough to justify the cache write. For exploration this is the right tool when you want to shed early verbose reads while keeping the last few file contents the agent is actively reasoning from. It is the complement to compaction, not a replacement, and it runs server-side before the model sees the prompt.

Beyond the task statement: Cost is a context constraint, not a separate concern

Every token kept in the active context is billed, so the mechanisms that protect context quality also protect the budget, and the ones that mishandle it raise cost. Compaction and context editing shrink the live context and therefore recurring input cost on long sessions. Prompt caching reduces the marginal cost of the stable prefix so that repeating it across tool calls is cheap. The cost documentation frames these as levers on the same dial.

The subtlety is cache invalidation. Because tool-result clearing invalidates the cached prefix at the clearing point, over-frequent or too-shallow clearing can raise cost even as it shrinks context, which is why clear_at_least exists: clear enough to make the cache write worthwhile . A candidate should see cost and context quality as one optimisation, not two. The token counting endpoint is the instrument that lets you tune both at once .

Beyond the task statement: Long-context guidance and the retrieval alternative

The long-context tips documentation and the effective context engineering writing make a point that reframes the entire task: stuffing the whole codebase into context is the wrong instinct, because models attend best to content at the beginning and end of a long context and lose grip on the middle . This is the documented backing for the exam trap against pre-loading the entire codebase: even if it fits, the middle gets lost, and the loss looks exactly like context rot.

The documented remedy is retrieval on demand rather than bulk loading. Keep the active window small and pull specific files or findings into it when a step needs them, which is precisely what a scratchpad read or a memory-tool view does . Structure also helps: well-delimited sections, explicit identifiers, and summaries at phase boundaries give the model stable anchors to attend to. A candidate who explains Task 5.4 purely as a storage problem misses that the documented guidance is about attention and retrieval, and that is the deeper reason scratchpads and subagents work while bigger windows do not.

Beyond the task statement: Why these matter for the exam

Domain 5 also examines context engineering breadth. Questions appear that test caching, thinking cost, token budgeting, isolation security, shared versus per-agent memory, native resume, durable execution, and provenance. The reference page is narrow; the exam is not. A candidate who studies only the reference page will miss the neighbours and may mis-answer a question that assumes knowledge of prompt caching or the memory tool's security requirement.

Failure mode catalog

This section collects the distinct ways a codebase-exploration session fails under context pressure, maps each to its root cause, and ties each to the mitigation covered earlier. It is the diagnostic backbone a candidate needs: the exam tests recognition of the symptom and the correct remedy, not just the definitions. Each entry names the failure, its observable symptom, its root cause, its documented basis where one exists, how to detect it early, and which mechanism fixes it.

Failure mode catalog: Failure mode 1: token limit hard error

  • Symptom. The request is rejected outright. On the API this is a 400 prompt is too long; in Claude Code, server-side compaction intercepts the overflow as the limit is approached .
  • Root cause. Physical capacity. The combined size of system prompt, messages, tool results, images, documents, tool definitions, and output exceeds the window. This is a storage failure, not a salience failure.
  • Detection. The token counting endpoint returns a count above the window before the model is called . Budgeting catches this before send time .
  • Mitigation. Compaction (compact_20260112 or /compact), context editing (clear_tool_uses_20250919), prompt caching to shrink the repeated prefix, and structural delegation so verbose output never accumulates in one context.

This failure is the one the reference page explicitly distinguishes from degradation. The exam trap is treating the two as the same; they are not, and the remedies differ.

Failure mode catalog: Failure mode 2: context rot (degradation)

  • Symptom. No error is raised. The agent begins referencing typical patterns instead of the specific classes, methods, and dependency chains it discovered earlier. Language shifts from a proper noun like OrderRepository to a category word like repository pattern.
  • Root cause. Salience, not storage. Early precise findings compete with a growing volume of later tokens for attention weight, and the later tokens win. The window may be far from full .
  • Documented basis. The documentation names this context rot and describes it as a monotonic degradation of accuracy and recall with token count.
  • Detection. Track specificity. A practical check is to count how many of the agent's recent references are specific identifiers from earlier turns versus generic category words. When the ratio flips toward generic, rot has begun. A larger window only delays the flip.
  • Mitigation. Externalise specifics: scratchpad files, the memory tool for just-in-time retrieval, subagent isolation so verbose output never enters the coordinator, and summary injection so phase handoffs carry specifics forward .

Failure mode catalog: Failure mode 3: subagent re-pollution

  • Symptom. The coordinator's context still grows despite delegation, defeating the point of subagents.
  • Root cause. The subagent returns its raw transcript or its tools allowlist is too broad, so it produces verbose output that the coordinator then stores. Isolation is only realised at the return boundary.
  • Detection. Token-count the coordinator context after ten delegations. If it grew by the size of the explored files, the subagent re-polluted it.
  • Mitigation. A narrow tools allowlist (for example Read, Grep, Glob only) and a structured return contract that admits only findings, never file bodies. This is Example 1 in the worked examples.

Failure mode catalog: Failure mode 4: cold-start duplication

  • Symptom. Phase 2 subagents re-explore what Phase 1 already mapped, wasting turns and risking contradictory findings.
  • Root cause. No summary was injected at the phase boundary, so Phase 2 started from an empty understanding.
  • Detection. Compare the tool calls of Phase 2 against Phase 1's explored paths. Overlap on already-explored files signals the miss.
  • Mitigation. Phase-boundary carry-forward: inject the Phase 1 five-field summary as the first message of every Phase 2 subagent. This is Example 3.

Failure mode catalog: Failure mode 5: crash and lost progress

  • Symptom. A session crash, network drop, or context exhaustion deletes all exploration.
  • Root cause. No durable state was exported; the only record lived in volatile conversation context.
  • Detection. Ask whether, right now, a fresh process could resume this session from a file. If not, the session is unprotected.
  • Mitigation. A resumable manifest (Example 5), Claude Code --resume / --continue / /fork (Example-adjacent, ), or SDK-native durable workflows for a service . The manifest must hold specific paths and symbols, not prose, or the resume cannot continue precisely.

Failure mode catalog: Failure mode 6: provenance loss

  • Symptom. A finding in the scratchpad or memory has no pointer to where it came from, so it cannot be trusted or re-verified.
  • Root cause. Findings were recorded as free-text notes without a source field (Divergence 3).
  • Detection. Pick a random scratchpad line and ask whether a teammate could locate the originating file from it alone.
  • Mitigation. The citations feature on document blocks and search result content blocks with required source and title for retrieved content . For non-document findings, pair every recorded fact with its file path in the scratchpad schema.

Failure mode catalog: Failure mode 7: cache invalidation from over-frequent clearing

  • Symptom. Cost rises after enabling context editing, or the cache hit rate drops.
  • Root cause. clear_at_least is set too small, so each clear invalidates the cached prefix at the clearing point without clearing enough to justify the cache write.
  • Detection. Compare cache_read_input_tokens before and after enabling clearing. A drop without a matching reduction in total input suggests wasteful invalidation.
  • Mitigation. Set clear_at_least high enough that the cache write is worth it, and prefer keep of recent tool uses over aggressive clearing. This is Example 6.

Failure mode catalog: Failure mode 8: compaction drops the specifics

  • Symptom. After auto-compaction, the agent can no longer name early classes and drifts to generic phrasing, even though compaction "preserved key information".
  • Root cause. The default summary prompt is generic; it preserves gist, not identifiers. A session already producing generic references is summarised into further generic form, and /compact cannot recover an already-degraded session.
  • Detection. After a compaction event, ask a question that depends on an early specific finding. A generic answer reveals the loss.
  • Mitigation. Supply instructions that force the five-part structure and demand verbatim preservation of class names, file paths, and dependency edges (Example 4). Crucially, keep the truly must-survive specifics in a scratchpad or memory tool outside the summarised region, because compaction still summarises (Divergence 2).

Failure mode catalog: How the catalog maps to the exam traps

The reference page lists four exam traps: bigger window is wrong, parallelisation is the wrong primary benefit of subagents, restart loses work, and /compact is proactive not last-resort. Each trap corresponds to a failure mode above: trap 1 maps to context rot (mode 2), trap 2 to subagent re-pollution and cold-start (modes 3 and 4), trap 3 to crash loss (mode 5), and trap 4 to compaction discipline (mode 8). A candidate who can name the failure mode, its root cause layer, and the exact mechanism that fixes it has the full chain the exam rewards.

Worked production examples

The following six examples form one connected implementation: a resilient codebase explorer. Each block is a real, supported surface, not a toy. After each block, a note states what it proves, its failure boundary, and its observable output.

Worked production examples: Example 1: subagent with a tool allowlist and a structured return shape

The Claude Code sub-agent definition below sets the tools field as the allowlist. Because Write and Edit are absent, the explorer can never alter state or pollute the parent with verbose writes. The JSON that follows is the contract for what the subagent returns: only conclusions, never the transcript.

config.yaml
yaml
# .claude/agents/explore-repo.md  (frontmatter only)
name: explore-repo
description: Read-only codebase explorer. Use for tracing dependency chains, listing test files, and reporting specific class names and file paths.
tools: [Read, Grep, Glob]
model: sonnet
permissionMode: read-only
result.json
json
{
  "findings": [
    { "path": "src/repos/order.ts", "symbol": "OrderRepository", "note": "implements Repository<T>, findById cache" },
    { "path": "src/services/refund.ts", "symbol": "RefundProcessor", "note": "missing Stripe retry" }
  ],
  "dependencyEdges": ["RefundProcessor -> OrderService -> OrderRepository -> PostgreSQL"],
  "coverage": { "OrderService": "87%", "RefundProcessor": "12%" }
}

What it proves: the tools field is the isolation boundary; a read-only explorer cannot re-pollute the coordinator. Failure boundary: if tools includes Write, the subagent can mutate the repo and leak verbose diffs into the parent. Observable output: the coordinator's context contains only the JSON findings, and a check of the parent transcript shows none of the explored file bodies.

Worked production examples: Example 2: memory tool scratchpad with a traversal-rejecting handler

The handler below implements the memory_20250818 operations. The load-bearing part is assertSafe, which enforces the /memories prefix, decodes URL-encoded traversal, resolves to a canonical path, and rejects anything that escapes the root. This is the path-containment requirement from the documentation.

example.ts
typescript
import {
  existsSync,
  readFileSync,
  writeFileSync,
  readdirSync,
  renameSync,
  mkdirSync,
  statSync,
  dirname,
} from "fs";
import { resolve } from "path";

const MEMORY_ROOT = "/var/app/memories";

function assertSafe(rawPath: string): string {
  const decoded = decodeURIComponent(rawPath).replace(/%2e%2e%2f/gi, "");
  if (!decoded.startsWith("/memories")) {
    throw new Error("Path must be under /memories");
  }
  const full = resolve(MEMORY_ROOT, "." + decoded);
  if (!full.startsWith(MEMORY_ROOT)) {
    throw new Error("Path traversal rejected");
  }
  return full;
}

export async function handleMemoryTool(input: {
  command: "view" | "create" | "str_replace" | "insert" | "delete" | "rename";
  path: string;
  content?: string;
  old_string?: string;
  new_string?: string;
  new_path?: string;
}): Promise<string> {
  const target = assertSafe(input.path);
  switch (input.command) {
    case "view":
      return statSync(target).isDirectory()
        ? readdirSync(target).join("\n")
        : readFileSync(target, "utf8");
    case "create":
      mkdirSync(dirname(target), { recursive: true });
      writeFileSync(target, input.content ?? "");
      return "Created";
    case "str_replace": {
      const current = readFileSync(target, "utf8");
      if (!input.old_string || !current.includes(input.old_string)) {
        throw new Error("old_string not found");
      }
      writeFileSync(target, current.replace(input.old_string, input.new_string ?? ""));
      return "Updated";
    }
    case "insert":
      writeFileSync(target, input.content ?? "", { flag: "a" });
      return "Inserted";
    case "delete":
      writeFileSync(target, "");
      return "Deleted";
    case "rename":
      if (!input.new_path) throw new Error("new_path required");
      renameSync(target, assertSafe(input.new_path));
      return "Renamed";
  }
}

What it proves: the memory tool is client-side, so the application owns storage and must enforce containment. Failure boundary: a handler that skips assertSafe lets /memories/../../secrets.env escape. Observable output: a request with path: "/memories/../../etc/passwd" throws Path traversal rejected, and a valid create followed by view round-trips the bytes.

Worked production examples: Example 3: phase-boundary carry-forward

The JSON below is the Phase 1 summary injected as the first message of every Phase 2 subagent. It is a factual carry-forward, not a transcript, so Phase 2 starts informed and does not re-explore.

result.json
json
{
  "architecture": "Layered: Controllers -> Services -> Repositories -> Database",
  "refundFlow": "RefundController -> RefundProcessor -> OrderService -> PaymentGateway",
  "keyConcern": "RefundProcessor has no retry logic for external API failures",
  "phase2Objective": "Investigate error handling in RefundProcessor and PaymentGateway",
  "carryForward": true
}

What it proves: summary injection prevents the cold-start duplication the reference page warns about. Failure boundary: injecting the full Phase 1 transcript instead floods context and defeats isolation. Observable output: Phase 2 subagents answer "what is the refund flow?" without re-reading RefundController, and their first tool call targets PaymentGateway directly.

Worked production examples: Example 4: server-side compaction request with five-part instructions

This is the programmatic equivalent of /compact, sent on the Messages API with the compact-2026-01-12 beta header. The instructions field replaces the default summary prompt and forces the five-part structure, because the application must keep specific identifiers verbatim.

terminal
bash
curl https://api.anthropic.com/v1/messages \
  -H "content-type: application/json" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: compact-2026-01-12" \
  -d '{
    "model": "claude-opus-4-8",
    "max_tokens": 1024,
    "messages": [
      { "role": "user", "content": "Continue exploring the refund service." }
    ],
    "context_management": {
      "edits": [{
        "type": "compact_20260112",
        "trigger": { "type": "input_tokens", "value": 150000 },
        "pause_after_compaction": false,
        "instructions": "Summarize into <summary> tags with five parts: 1) Task Overview, 2) Current State, 3) Important Discoveries, 4) Next Steps, 5) Context to Preserve. Preserve every specific class name, file path, and dependency edge verbatim."
      }]
    }
  }'

What it proves: compact_20260112 is the API-layer compaction lever, distinct from the Claude Code CLI /compact. Failure boundary: omitting instructions yields the default summary, which may drop the specific identifiers the task needs. Observable output: the API emits a compaction block, and on later requests the server drops all blocks prior to it; the five-part <summary> is present and contains OrderRepository, RefundProcessor, and src/repos/order.ts by name.

Worked production examples: Example 5: resumable state manifest

The manifest below is what each agent exports on crash. A coordinator reloads it on resume and injects it into the next prompt, so exploration continues instead of restarting. This is the application-layer version of --resume.

result.json
json
{
  "sessionId": "explore-order-service-001",
  "phase": 2,
  "exploredPaths": [
    "src/repos/order.ts",
    "src/services/order.ts",
    "src/services/refund.ts"
  ],
  "keyFindings": {
    "architecture": "Layered: Controllers -> Services -> Repositories -> DB",
    "criticalIssue": "RefundProcessor has no retry logic for Stripe API failures",
    "testCoverage": { "OrderService": "87%", "RefundProcessor": "12%" }
  },
  "nextSteps": [
    "Investigate PaymentGateway error handling",
    "Review RefundProcessor test files",
    "Check cache invalidation logic in OrderRepository"
  ]
}

The loader that consumes it:

example.ts
typescript
import { readFileSync } from "fs";

export function resumeFromManifest(path: string): string {
  const manifest = JSON.parse(readFileSync(path, "utf8"));
  const lines = [
    `Resuming session ${manifest.sessionId} at phase ${manifest.phase}.`,
    "Already explored: " + manifest.exploredPaths.join(", "),
    "Key findings: " + JSON.stringify(manifest.keyFindings),
    "Next steps: " + manifest.nextSteps.join("; "),
  ];
  return lines.join("\n");
}

What it proves: structured state survives a crash. Failure boundary: a manifest that stores only prose loses the specific paths and symbols needed to continue. Observable output: after a simulated crash, the resumed agent's first action targets PaymentGateway (a nextSteps item), not RefundController (already explored).

Worked production examples: Example 6: tool-result clearing that keeps recent results and excludes one tool

This context-editing configuration sheds the oldest tool results but keeps the three most recent and never clears web_search results, because the agent still reasons from them. It is sent with the context-management-2025-06-27 beta header.

result.json
json
{
  "context_management": {
    "edits": [{
      "type": "clear_tool_uses_20250919",
      "trigger": { "type": "input_tokens", "value": 30000 },
      "keep": { "type": "tool_uses", "value": 3 },
      "clear_at_least": { "type": "input_tokens", "value": 8000 },
      "exclude_tools": ["web_search"],
      "clear_tool_inputs": false
    }]
  }
}

What it proves: context editing is the fine-grained complement to compaction; you can keep what matters and shed what does not. Failure boundary: setting keep too low or clear_at_least too small wastes the cache write, because clearing invalidates the cached prefix at the clearing point. Observable output: after the trigger fires, the oldest file-read results are replaced with placeholder text, the three most recent tool results remain, and the web_search result is untouched; a follow-up question about the search finding still answers correctly.

Build exercise material

The reference page asks for a context-resilient codebase explorer: a coordinator that delegates to subagents, a scratchpad, summary injection, and crash recovery. The steps below make each requirement verifiable. For every step, the observable outcome proves the mitigation actually works rather than merely being present in code.

Build exercise material: Step 1: scratchpad file management

Action: configure the explorer to write structured findings to a scratchpad file after each exploration step, and to read that file at the start of each subsequent step. The scratchpad records pairs of name and location, dependency edges, and verified call relationships, not prose summaries.

Observable outcome: after exploring src/repos/order.ts, the scratchpad file contains a line such as OrderRepository (src/repos/order.ts) - implements Repository<T>, findById cache. A second step that queries "what does OrderRepository implement?" reads the scratchpad and answers with the specific class and method, without re-reading the file. If the answer instead says "a standard repository pattern", the read-back discipline is broken and the step is not complete.

Build exercise material: Step 2: coordinator with subagent delegation

Action: implement a coordinator that spawns focused subagents (for example "find all test files for the order service and report coverage status") with a tools allowlist limited to Read, Grep, and Glob, and that accepts only a structured summary in return.

Observable outcome: the coordinator's own transcript contains none of the explored file bodies. A token count of the coordinator context stays roughly flat across ten delegations, while a control run that does the exploration inline shows the coordinator context growing by the size of every file read. The subagent returns the JSON findings shape from Example 1, not raw excerpts. This proves isolation, which is the real benefit, not parallel speed.

Build exercise material: Step 3: summary injection between phases

Action: after Phase 1 (architecture understanding), write a Phase 1 summary in the five-field shape from Example 3 and inject it as the first message of every Phase 2 subagent prompt.

Observable outcome: a Phase 2 subagent asked "what is the refund flow?" answers by naming RefundController -> RefundProcessor -> OrderService -> PaymentGateway without re-reading those files, and its first tool call targets PaymentGateway directly. A control run that omits injection shows Phase 2 re-reading RefundController and duplicating Phase 1 work. The absence of duplicate tool calls is the proof.

Build exercise material: Step 4: crash recovery via a resumable manifest

Action: have each agent export the manifest from Example 5 after every step, and implement a resume function that loads it and injects it into the next prompt.

Observable outcome: kill the process mid-exploration, then restart with the resume function. The resumed agent's first action targets the first un-explored nextSteps item (for example PaymentGateway), not an already-explored path. A diff of explored paths before and after the crash shows zero re-exploration. Without the manifest, the restarted agent begins at RefundController again, which is the failure the manifest prevents.

Build exercise material: Step 5: prove context degradation and its mitigation

Action: run two extended explorations across the same five modules. Run A maintains no scratchpad and no subagents; Run B maintains a scratchpad and delegates verbose reads to subagents.

Observable outcome: in Run A, after the third or fourth module the agent stops naming specific classes and instead says "this follows the typical repository pattern". In Run B, the agent names OrderRepository, RefundProcessor, and findById consistently through all five modules. The contrast in specific-versus-generic language is the observable symptom of context rot, and the scratchpad plus isolation is what prevents it. This is the test the reference page's build exercise demands, made measurable.

Build exercise material: Step 6: confirm compaction does not lose identifiers

Action: enable server-side compaction with the five-part instructions from Example 4 on a long run, then ask a question that depends on an early specific finding.

Observable outcome: the returned <summary> contains the early class names and file paths verbatim in the "Context to Preserve" section, and the follow-up question answers correctly. If the summary had dropped them, the answer would drift to generic phrasing, which is the failure mode compaction alone cannot fix and which the scratchpad or memory tool must cover.

Build exercise material: Step 7: provenance that survives synthesis

Action: when a finding originates from a retrieved document or internal knowledge base, attach it via the citations feature on the document block or a search result content block with required source and title, rather than copying the fact into a free-text note . For findings from direct file reads, record the file path alongside the fact in the scratchpad.

Observable outcome: every non-obvious claim in the final report carries a pointer back to its origin. A reviewer can click from "RefundProcessor has no Stripe retry" to src/services/refund.ts, and from a retrieved design note to its source URL. A control run that records facts as prose yields a report with no traceable origin, which is the failure mode 6 loss the step prevents.

Build exercise material: Self-grading rubric

Use this rubric to confirm the build exercise is genuinely complete, not merely present in code. Each row is a pass-or-fail check tied to an observable outcome from the steps above.

  • Scratchpad discipline. Does the agent read the scratchpad at the start of each step, and does the scratchpad contain specific identifiers rather than prose? Fail if any step answers from memory of the transcript instead of the file.
  • Isolation realised. Is the coordinator context flat across ten delegations? Fail if the coordinator transcript contains explored file bodies.
  • Structured return. Does each subagent return the findings shape, never raw excerpts? Fail if a single file body appears in the coordinator context.
  • Cold-start avoided. Does Phase 2 target unexplored paths first? Fail if Phase 2 re-reads a Phase 1 file.
  • Resume precise. After a simulated crash, does the resumed agent continue from the first nextSteps item? Fail if it re-explores an already-visited path.
  • Degradation prevented. Across five modules, does the agent keep naming specific classes? Fail if it drifts to "typical pattern" language in Run B while doing so in Run A.
  • Compaction faithful. Does the compaction summary preserve early identifiers in "Context to Preserve"? Fail if a post-compaction question about an early class answers generically.
  • Provenance intact. Does every reported claim carry a source pointer? Fail if any claim is unsourced free text.

A build that passes all eight rows is exam-ready for Task 5.4. A build that passes the code but fails an observable row has implemented the mechanism without the discipline, which is the gap the exam probes.

Build exercise material: Common mistakes when building the explorer

  • Writing the scratchpad but never reading it. The file exists, yet each step reasons from transcript memory. The mitigation is inert. The read-back at step start is what converts buried memory into referenced memory.
  • Returning transcripts from subagents. A subagent that echoes every file it read re-pollutes the coordinator exactly as if the work were inline. The return contract must admit findings only.
  • Injecting the full prior transcript at phase boundaries. This floods context and defeats isolation; the carry-forward must be a summary, not a dump.
  • Relying on compaction alone for verbatim survival. Compaction summarises; specifics the task must keep verbatim must live in a scratchpad or memory tool outside the summarised region.
  • Clearing too little. A clear_at_least set below the cache-worth threshold wastes the cache write and raises cost without meaningfully shrinking context. Set it high enough to justify invalidation.
  • Treating a larger window as the fix. A 1M window delays context rot; it does not prevent it. The deeper cause is salience, addressed only by externalisation and isolation.
  • Skipping provenance. Findings without source pointers cannot be re-verified and erode trust in the explorer's output, which is the failure mode 6 loss.

Build exercise material: Extending the explorer toward production

The build exercise is a single-process teaching implementation. A production service hardens it along three axes. First, durability: replace the hand-written manifest with SDK-native durable workflows so step completion is tracked automatically . Second, concurrency: run multiple isolated explorer subagents over disjoint repository slices and merge their structured findings into shared memory, watching the data-minimization boundary so no subagent sees another's slice . Third, cost: pin the stable prefix with prompt caching and verify size with the token counting endpoint before each request, so compaction and context editing trigger on measured thresholds rather than guesses . Together these turn the teaching exercise into a resilient exploration service that holds up under the multi-hour, multi-module sessions Domain 5 expects.

Mechanism and API surface

Scratchpad file outside conversation context
Markdown file with sections for key classes, dependency chains, and critical findings, written after each discovery and read before each subsequent step, capturing specific class names and file paths not summaries or full history.
Subagent delegation for context isolation
Spawn focused subagents with a specific investigation task and expected structured summary format. Each runs in its own isolated context and returns findings without verbose intermediate output. Primary benefit is isolation, speed is secondary.
Summary injection between phases
Extract Phase 1 findings from the scratchpad and prepend them as a Phase 1 Summary section in Phase 2 subagent prompts so Phase 2 starts with architectural context and does not re-explore.
Memory tool as the supported scratchpad surface
Tool type memory_20250818, client-side: the model requests view, create, update, and delete operations under the memory directory and your handler executes them against storage you control, rejecting any path that escapes it. Documented framing is just-in-time context retrieval.
Proactive compact command
Slash command that summarises the conversation preserving decisions and findings while discarding verbose output. Use proactively during extended sessions to maintain quality, not only reactively at the limit.
State manifest for crash recovery
JSON manifest at a known location with sessionId, phase, exploredPaths, keyFindings with architecture, criticalIssue, and testCoverage, and nextSteps, loaded on resume and injected into the agent's initial prompt.
Upstream structured summary over verbose reasoning
Upstream agents return key facts, file paths, and relevance scores rather than reasoning chains, because downstream agents with limited budgets cannot apply verbose prose and waste tokens re-parsing it.

The decision rules in play

Each rule below is a distinct decision the exam tests, stated with its mechanism, the boundary where the answer flips, and the wrong answers written against it.

R1

Context degradation is an attention-salience problem, not token exhaustion

In a long exploration session each tool result appends to the conversation: file contents, grep output, directory listings, reasoning chains. The model attends most strongly to the most recent content and to content near the current position. As early, precise findings get pushed deeper, their effective salience falls. The model begins to substitute general training knowledge, the phrase "the typical repository pattern", for the specific classes and paths it discovered earlier. This is a position-attention effect, not a storage failure.

The context window is not necessarily full. The problem is that specific earlier tokens compete with a growing volume of later tokens for attention weight. A larger window only delays the onset because the relative recency gap still grows with every new file read. The failure concerns which tokens the model weights, not how many it can physically hold.

Boundary. Boundary: when the session is short or exploration is sparse, no degradation appears. The nearby opposite case is a genuine fit problem, a single file so large that its contents cannot physically occupy the window. There, enlarging the window is the correct fix. For multi-step exploration accumulation, window size is the wrong lever.

Recurring specifics. Symptom phrasings: "the system follows standard patterns", "typical login flow", "the usual reconciliation step", "standard JWT patterns". Specificity loss shows as dropped method signatures, renamed classes, and rounded numbers. Degradation appears after roughly 30 to 90 minutes, or after 12 to 50 files, never at session start.

Wrong answers written against this rule

Proposal. increase max_tokens or switch to a larger-context model.

Why it attracts. seems to give more room.

Why it fails. max_tokens controls output length, not input retention; a larger window delays but does not stop the salience problem.

When it would be right. only when fitting the content itself is the physical bottleneck, a separate diagnosis.

Proposal. raise temperature to force variety.

Why it attracts. randomness might surface earlier facts.

Why it fails. temperature affects sampling, never memory of earlier context. when

When it would be right. never for this.

How the same rule gets re-asked
  • Re-asked as "what is the cause" (answer: attention, not tokens); "will a bigger window fix it" (no); "is this hallucination from temperature" (no).
R2

The generic-pattern utterance is the diagnostic signal of degradation

The observable marker of context degradation is a shift in language. Early in a session the agent names OrderRepository, RefundProcessor, and findById. Later, after the context fills, it says the module "follows standard patterns". The move from a specific identifier to a generic category word is the tell.

Generic category words are cheap for the model to produce and are well supported by training data. Specific identifiers from early in the session require attending to buried tokens. When salience drops, the model defaults to the high-probability generic phrasing rather than the low-probability but correct specific name.

Boundary. Boundary: generic phrasing is acceptable when the agent was never given specifics, or when summarising at a legitimately high level. The opposite case is healthy precision: the agent still cites exact class names and file paths it read 20 minutes earlier. If specificity is intact, degradation is not present even if the window is large.

Recurring specifics. Healthy signals: references specific class or function names, builds on prior findings, stays consistent with earlier discoveries. Degraded signals: "typical patterns" instead of names, contradicts earlier findings, gives generic advice, claims ignorance of things it read. Sometimes a contradictory wiring diagram appears for the same component asked twice.

Wrong answers written against this rule

Proposal. increase the context window so generic drift stops.

Why it attracts. more room seems to keep specifics.

Why it fails. the drift is salience-driven; more room postpones but does not prevent. when

When it would be right. never for this signal.

Proposal. add a system instruction to "always reference exact classes".

Why it attracts. direct.

Why it fails. it sharpens answers for a few exchanges then drift returns, because the underlying salience problem is untouched. when

When it would be right. as a marginal aid, not a fix.

How the same rule gets re-asked
  • The same signal is posed as "what does this behaviour signal" (degradation, not rate limiting), and as "which symptom indicates the window is the problem" (it is not the window).
R3

Scratchpad files externalise key findings to durable storage

The agent writes discovered facts, class names, file paths, and dependency edges to a file on disk as it explores. When a later question needs those facts, the agent reads the file rather than relying on conversation memory. The findings live outside the volatile context window.

A file on disk is re-read at the point of use, so a finding made at turn 8 is as fresh at turn 80 as at turn 9. Conversation history, by contrast, degrades in salience as it grows. External storage converts passive buried memory into actively referenced memory.

Boundary. Boundary: scratchpads help when exploration is long or multi-phase. The opposite case is a short single-task session where re-reading the file is overhead with no benefit. Also, a scratchpad that merely duplicates conversation without being read is useless; the read-back is the load-bearing step.

Recurring specifics. Typical scratchpad headers: ## Key Classes, ## Dependency Chain, ## Critical Findings. Entries pair a name with a location, for example OrderRepository (src/repos/order.ts). Test coverage percentages and missing retry logic recur as "critical findings". One recurring instruction is to re-read the scratchpad at the start of each subsequent step.

Wrong answers written against this rule

Proposal. instruct the model to completely ignore its previous context and use a scratchpad of new code.

Why it attracts. clean slate.

Why it fails. discards the reasoning the fix depends on. when

When it would be right. when switching to an unrelated task only.

Proposal. keep findings inside the live conversation as a "discovered-components" block.

Why it attracts. no file I/O.

Why it fails. the live conversation is the degrading medium; the block itself gets buried. when

When it would be right. for very short sessions.

How the same rule gets re-asked
  • Re-asked as "most effective mitigation" (scratchpad), "what if restart" (loses work), "what if bigger window" (no). The scratchpad is also the fix when a crash-recovery manifest still leaves generic drift, because the manifest is coarse while the scratchpad holds the specifics.
R4

A scratchpad must capture specific identifiers, not prose summaries

An effective scratchpad records exact class names, file paths, method signatures, and verified call edges. A scratchpad that records only narrative prose, "various refactoring changes", loses the very specifics the session needs.

Prose condensation is lossy. When the agent later reads "module 12 had various refactoring changes" it no longer knows that CustomerRecord was renamed to CustomerProfile. The specific identifier is the unit of value; narrative compresses it away.

Boundary. Boundary: a high-level phase summary is appropriate for handoff between phases, but the durable scratchpad for answering follow-ups must keep identifiers. The opposite failure is progressive summarisation that rewrites a precise rename into a vague phrase, after which a later module cites the old name.

Recurring specifics. Recurring loss events: a module renamed a class, the summary called it "refactoring changes", and a later module referenced the original name from its own earlier summary. Numeric values rounded: a p99 budget of 850 ms resurfaced as "about 200 ms". Exact balances and dates drifted after entering a running summary.

Wrong answers written against this rule

Proposal. have the agent summarise each module after reading it, then discard source.

Why it attracts. keeps context small.

Why it fails. the summary is lossy and the original specifics are gone. when

When it would be right. only when specifics are truly not needed later.

Proposal. keep a running prose synopsis and re-inject it at the top of each turn.

Why it attracts. keeps things short.

Why it fails. the synopsis itself drifts as it is re-summarised. when

When it would be right. never as the sole record.

How the same rule gets re-asked
  • This rule appears as the contrast between "write structured findings" and "progressive summarisation"; the structured form wins whenever exact values or renames matter.
R5

Subagent delegation's primary value is context isolation, not speed

When the main agent delegates exploration to a subagent, all the verbose file reads, grep results, and reasoning happen inside the subagent's own context window. Only a concise summary returns to the main conversation. The main context stays clean for high-level coordination.

The context window holds everything: every message, every tool result, every output. Bulk exploration pollutes an orchestrating session with low-signal tokens and pushes earlier instructions out of effective reach. Subagents exist to make this trade-off clean by absorbing the noise elsewhere.

Boundary. Boundary: delegation is the right call when a side task would flood the main context, or when independent workstreams can run in parallel. The opposite case is simple sequential steps that depend on decisions made earlier; there, isolation is harmful because it throws away shared state.

Recurring specifics. Build-in exploration agents are described as read-only, with scoped tools Read, Grep, Glob. The coordinator keeps "only summarized findings". A recurring phrasing: "the primary benefit is context isolation, not parallelisation". The Explore subagent "returns only a concise summary to the main conversation".

Wrong answers written against this rule

Proposal. spawn subagents purely to run work in parallel faster.

Why it attracts. parallelism is obvious.

Why it fails. the exam repeatedly rewards isolation as the deeper reason; parallel speed is secondary. when

When it would be right. genuinely independent parallel work, but still isolation is the tested benefit.

Proposal. keep exploration in the main session and use /compact afterward.

Why it attracts. simpler.

Why it fails. the main context is already spent before implementation begins. when

When it would be right. only for short explorations.

How the same rule gets re-asked
  • The same rule is posed as "what does the Explore subagent solve" (isolation), "why delegate" (clean main context), and "what is the main effect of subagents" (isolation).
R6

Subagents do not inherit parent context; un-injected facts are invisible

A standard spawned subagent starts with a fresh, isolated context. It does not see the parent's conversation history unless that history is explicitly placed in its prompt. Anything the coordinator discovered earlier and does not inject is simply absent from the subagent's world.

Isolation is the design: each subagent runs in its own window with its own system prompt and tool access, returning only a result. That same boundary means it cannot read what was not passed in. The coordinator must treat injection as the only channel.

Boundary. Boundary: a fork_session or a fork-style spawn does inherit the full history, which is the tool for when you want inherited context. The opposite case is assuming a spawned worker "knows" the prior analysis; it does not, and will either re-derive it or act on stale assumptions.

Recurring specifics. Recurring failures: a synthesis subagent reports "no findings to work with" though search completed, because the coordinator never passed them. A validation subagent judges in isolation, citing "standard ORM equivalence rules" rather than the project's actual conversion decisions. A subagent cites file paths but omits the module ownership the coordinator already found.

Wrong answers written against this rule

Proposal. rely on the coordinator's context being visible to all subagents.

Why it attracts. seems natural.

Why it fails. isolation prevents it. when

When it would be right. only under fork mode.

Proposal. include the entire 80K transcript in every subagent prompt.

Why it attracts. completeness.

Why it fails. wastes tokens, and the subagent still only uses what is relevant; better to pass targeted summaries. when

When it would be right. never as a blanket practice.

How the same rule gets re-asked
  • Re-asked as "why did the subagent contradict the coordinator" (no shared memory); "how did subagent B see subagent A's data" (it was injected, or it should not have).
R7

A subagent must return only a structured summary, never raw transcript

The isolation benefit is only realised at the boundary. If the subagent returns its complete transcript, every dump, stack walk, and log excerpt, the main context is re-polluted exactly as if the work had been done inline. The contract is: absorb verbose output in isolation, return only conclusions.

config.yaml
yaml
subagent_contract:
  input: focused_investigation_prompt
  internal: verbose_reads_and_greps_in_own_context
  output:
    - confirmed_classes: ["VulkanPipeline", "FrameGraph"]
    - key_edges: ["RefundProcessor -> OrderService"]
    - open_questions: ["cache invalidation on status change"]
  forbidden_output: full_transcript

Returning the raw transcript defeats the purpose: the main window refills with the same low-signal tokens the subagent was meant to shield it from. Only the distilled finding preserves the cleanliness that made delegation worthwhile.

Boundary. Boundary: a downstream agent that genuinely needs the raw evidence is a different design, and even then it should receive a curated extract, not the whole dump. The opposite case is "return the complete evidence trail so the engineer can audit it"; attractive for auditability but it re-pollutes.

Recurring specifics. Recurring wrong option: "have it append every file it reads, along with full excerpts, into its returned report". Recurring right option: "returns only a structured summary of call sites, helpers, and recommended edit points". The phrasing "dumping raw output back to main is no isolation at all" recurs.

Wrong answers written against this rule

Proposal. return the full profiling transcript for auditability.

Why it attracts. completeness and review.

Why it fails. re-pollutes main context. when

When it would be right. only if a separate audit context consumes it, not the coordinator.

Proposal. keep discovery in main and /compact the dumps.

Why it attracts. keeps one thread.

Why it fails. the main window is already spent. when

When it would be right. short sessions only.

How the same rule gets re-asked
  • Posed as "which return shape preserves the curated thread" (summary only) and "why did isolation fail" (raw transcript returned).
R8

Summary injection between phases prevents cold-start duplication

When exploration is phased, the coordinator summarises the completed phase's key findings and injects that summary into the initial context of the next phase's agents. Phase 2 then starts with architectural understanding instead of redoing Phase 1.

Without the injected summary, Phase 2 agents face a cold start: they re-explore to discover what was already known, wasting time and risking contradictory conclusions. The summary is the handoff that carries understanding forward without carrying the noise.

Boundary. Boundary: injection is needed when phases are distinct and the later phase needs the earlier findings. The opposite is passing the entire raw transcript, which floods each subagent with material it does not need and can contradict itself.

Recurring specifics. A recurring Phase 1 summary shape lists architecture, key concern, and next objective. For example: "layered architecture Controllers to Services to Repositories to Database; refund flow passes through RefundController to RefundProcessor to OrderService to PaymentGateway; concern: RefundProcessor has no retry logic". Injected into every Phase 2 prompt.

Wrong answers written against this rule

Proposal. pass the full phase-one transcript to each phase-two subagent.

Why it attracts. nothing lost.

Why it fails. floods context, defeats isolation. when

When it would be right. never as the default.

Proposal. spawn unscoped subagents to "determine their own scope".

Why it attracts. less orchestration.

Why it fails. duplicated, unfocused work. when

When it would be right. never.

How the same rule gets re-asked
  • Re-asked as "what should happen before spawning phase-two subagents" (summarise and inject); "before planning subagents, what does the coordinator do" (inject summary).
R9

Summarise-and-spawn terminates the agent to discard raw context

In a multi-phase workflow, at the end of each phase the coordinator distils key findings into a structured summary, terminates the phase agent (discarding its raw context), and spawns the next phase agent with only the summary. No raw phase context is carried forward.

result.json
json
{
  "phase": "schema_analysis",
  "summary": {
    "tables": ["users", "orders", "products"],
    "deprecated_columns": ["users.legacy_id", "orders.old_status"]
  },
  "action": "terminate_agent_then_spawn_next_with_summary_only"
}

Carrying raw phase context forward is what causes later-phase hallucination: an API agent "remembers" columns that were refactored away in Phase 2 because they still sit in its accumulated context. Terminating the agent severs that contaminated context; the fresh agent works only from the precise current summary.

Boundary. Boundary: this pattern fits pipelines where each phase's raw outputs are large and later phases must not inherit stale specifics. The opposite case is a single chained refactor where each edit depends on the previous edit's decisions; there you must keep one continuous context, not terminate.

Recurring specifics. Recurring symptom it prevents: "hallucinating database columns that don't exist" in the API phase. Recurring wrong option: "implement structured state persistence to save the full uncompressed context window to a manifest between phases" (keeps the contamination). The correct phrase "never carry raw phase context forward" recurs.

Wrong answers written against this rule

Proposal. persist the full uncompressed context to a manifest between phases.

Why it attracts. nothing discarded.

Why it fails. the hallucinations come from that very context. when

When it would be right. never for this goal.

Proposal. instruct the agent to self-delete its memory after each phase.

Why it attracts. light-weight.

Why it fails. unreliable and may drop needed state. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "how should context management be improved when the API phase hallucinates columns" (summarise, terminate, spawn) and "which pattern prevents accumulated-raw-context hallucination" (summarise-and-spawn).
R10

The `/compact` command is for proactive mid-session cleanup

/compact summarises the conversation so far into a compressed form and replaces the history with that summary, freeing context budget while preserving key findings. The guidance is to use it proactively during extended sessions, not only when the limit is reached.

The optional instruction biases the summary, for example to retain a root cause and the confirmed fix direction while discarding verbose logs.

terminal
bash
/compact
/compact "Focus on the root cause and confirmed fix approach"

Proactive use protects context quality before salience degrades. Waiting until the hard limit produces an error that interrupts the task, and by then specifics may already be blurred. A regular cadence, every 30 to 50 turns, keeps the working context focused.

Boundary. Boundary: /compact is appropriate when you intend to continue the same session and only want noise reduced. The opposite case is switching to an unrelated task, where /clear is the right tool because you want a clean slate, not a compressed memory of the old task.

Recurring specifics. Recurring phrasing: "use /compact proactively during extended exploration sessions, not just when you hit context limits". A trigger table lists pivoting from debugging to fixing, from exploration to coding, session getting slow, transitioning between major phases, and context approaching limits. The optional focus instruction recurs as a feature.

Wrong answers written against this rule

Proposal. run /compact only when the limit is hit.

Why it attracts. seems sufficient.

Why it fails. reactive use loses quality and interrupts work. when

When it would be right. never as the sole strategy.

Proposal. use --resume to reload with a smaller footprint.

Why it attracts. sounds like compaction.

Why it fails. --resume reopens a prior session; it does not compress the current one. when

When it would be right. for continuing a named session.

How the same rule gets re-asked
  • Re-asked as "what command reduces context usage while preserving state" (/compact); "when is /compact appropriate" (near limit after long session); "what does /compact do versus /clear" (compress versus discard).
R11

`/compact` is lossy and cannot recover an already-degraded session

Once a session has degraded, the model is already producing generic references instead of specific classes. Running /compact then summarises whatever is currently in context, which may further reduce reliable recall of named entities. It does not restore the specificity that was already lost.

/compact compresses; it cannot reconstruct details the model is no longer attending to. The summary will itself be generic if the source material is generic. The recovery move for a degraded session is to extract the valuable knowledge into a structured summary and start a fresh session, not to compact the degraded one.

Boundary. Boundary: /compact is excellent for a still-healthy session that is filling up, preserving findings while trimming noise. The opposite case is a session already exhibiting generic drift; there, fresh-start-plus-summary-injection is correct, and /compact is an explicit anti-pattern.

Recurring specifics. Recurring wording: "while /compact is useful for reducing context pressure during an active session, it cannot recover lost specificity in a session that has already degraded". A recurring scenario: an agent references "typical patterns" after a long pause; the right move is structured summary into a new session, not /compact.

Wrong answers written against this rule

Proposal. run /compact to "restore the agent's ability to reference specific classes".

Why it attracts. compaction sounds like cleanup.

Why it fails. it cannot recreate lost specifics. when

When it would be right. only while the session is still healthy.

Proposal. re-read all explored files in the same session after compacting.

Why it attracts. rebuilds context.

Why it fails. re-reads repollute and the old degradation persists. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "after degradation, what is the most reliable approach" (fresh session with summary) and "does /compact recover specificity" (no).
R12

`/compact` compresses while `/clear` discards everything

/compact takes the current history, generates a concise summary, and uses that summary as the new starting context, continuing the session. /clear resets the conversation entirely, discarding all prior context. They are not interchangeable.

Compression preserves thread and learned facts; clearing throws them away. Choosing the wrong one either wastes accumulated work or carries stale noise into an unrelated task.

Boundary. Boundary: use /compact to continue the same task with less noise; use /clear when moving to a genuinely different task so old context does not bleed in. The opposite mistake is using /clear mid-task (loses the analysis the next step depends on) or /compact when you actually wanted a clean break.

Recurring specifics. Recurring contrast options: "restarts the process with a fresh context window" (wrong for /compact); "summarizes the conversation so far into a compressed form and replaces the history" (correct for /compact); "archives the session log and exits" (wrong); "deletes all session memories" (wrong).

Wrong answers written against this rule

Proposal. use /clear between investigation and fix to stay clean.

Why it attracts. clean slate.

Why it fails. discards the analysis the fix depends on; /clear is for switching unrelated tasks. when

When it would be right. only when the next task is unrelated.

Proposal. treat /compact as a session reset.

Why it attracts. both "reduce context".

Why it fails. /compact keeps the thread; it is not a reset. when

When it would be right. never.

How the same rule gets re-asked
  • Re-asked as "what does /compact do that /clear does not" (preserves thread) and "which command for an unrelated task switch" (/clear).
R13

Progressive in-context summarisation loses precise facts

Asking the model to summarise earlier turns into a running prose narrative, then discard the originals, condenses precise values and identifiers into vague phrasing. The rename that mattered becomes "various refactoring changes", and a later module cites the old name.

Narrative condensation is inherently lossy for exact tokens. Numbers get rounded, identifiers get generalised, and constraints get softened. The model then reasons from the softened version, which is exactly the precision loss the session was already showing.

Boundary. Boundary: progressive summarisation is acceptable when the goal is a loose overview and exact values are not needed later. The opposite is any task where exact figures, renames, or legal/regulatory specifics must remain traceable; there, externalise to a structured file instead.

Recurring specifics. Recurring failure: module 21 referenced class names from module 3 that were renamed in module 12, because the module 12 summary compressed the rename into a generic phrase while module 3's summary still held the original name. Recurring phrasing: "progressive summarisation is lossy".

Wrong answers written against this rule

Proposal. instruct the agent to "re-read each module carefully and cite it" to counter drift.

Why it attracts. direct.

Why it fails. it sharpens early domains but drift returns once the conversation refills. when

When it would be right. marginal only.

Proposal. add a system note "always restate exact balances".

Why it attracts. explicit.

Why it fails. improves early turns but not later ones after results enter the summary. when

When it would be right. insufficient alone.

How the same rule gets re-asked
  • Posed as "what caused this failure" (summarisation lost the rename) and "why did later figures drift" (entered the running summary).
R14

Fresh session plus summary injection versus `--resume` by validity

When deciding how to continue prior work, the judgment is whether the prior context is still valid. If prior tool results are stale because files changed, start a fresh session and inject a structured summary of findings and decisions. If the prior context is still valid and files are unchanged, --resume the named session.

Resuming a session with stale cached tool results makes the agent reason from code that no longer exists; it proposes edits referencing removed functions. A fresh session with an injected summary preserves the valuable conclusions while eliminating the polluted old reads.

Boundary. Boundary: validity of prior tool results is the deciding factor. The opposite mistake is resuming a stale session (wrong) or re-exploring everything from scratch (wasteful) when only a few files changed.

Recurring specifics. The summary injection should include key findings, decisions already made, open questions, and scope covered. It should exclude raw tool results and the conversation transcript. Recurring phrasing: "prefer resumption when prior context is mostly valid, but start fresh with a structured summary when prior tool results are stale".

Wrong answers written against this rule

Proposal. resume and ask the agent to re-read only the changed files.

Why it attracts. targeted.

Why it fails. the old cached reads still pollute cross-module reasoning; a targeted re-read cannot clear the rest. when

When it would be right. only for a single isolated file with no cross-module dependence.

Proposal. start completely fresh with no carried knowledge.

Why it attracts. clean.

Why it fails. discards valid analysis of the 47 unchanged files. when

When it would be right. never when prior analysis is largely valid.

How the same rule gets re-asked
  • Re-asked as "after file changes, what is most appropriate" (fresh plus summary plus targeted re-read) and "when is --resume correct" (valid context, unchanged files).
R15

`--resume` is correct when prior context is valid and files unchanged

--resume reopens a named prior session, restoring its full conversation state so work continues exactly where it left off. It is the right choice when the earlier analysis is still accurate and the files it read have not changed.

If nothing the agent relied on has shifted, the accumulated reasoning is a genuine asset. Resuming preserves it without re-deriving. The risk of --resume is only stale tool results, which is absent in this case.

Boundary. Boundary: valid context plus unchanged files. The opposite case is any file modification since the session paused; then --resume carries stale reads and is wrong. Another opposite is exploring two approaches simultaneously, which calls for fork_session, not --resume.

Recurring specifics. A recurring scenario: an engineer mapped 23 authentication call sites yesterday, files unchanged today, so --resume auth-mapping is correct. Another: a flaky-test investigation named --resume checkout-flake-investigation, files unchanged, continue. The deciding question is always "did files change".

Wrong answers written against this rule

Proposal. always start fresh to avoid stale context.

Why it attracts. safe.

Why it fails. wastes valid prior work when nothing changed. when

When it would be right. only when something changed.

Proposal. use fork_session to explore a second approach.

Why it attracts. branching sounds useful.

Why it fails. unrelated to continuation; fork is for divergence, not pickup. when

When it would be right. only when a second parallel approach is wanted.

How the same rule gets re-asked
  • Posed as "when is --resume the correct choice" (valid context, no file changes, continue exactly) and "what if one module was refactored" (then fresh plus targeted re-read).
R16

Stale tool results after file changes require fresh start plus re-read

When files changed after a session was paused, the cached tool results in that session are stale. Resuming reasons from code that no longer exists. The fix is a fresh session seeded with a structured current-state summary, plus re-reading only the changed files for current structure.

A targeted re-read inside a resumed session cannot clear the rest of the old cached reads, which still influence cross-module reasoning. A fresh session removes all of them; the injected summary restores only the still-valid conclusions.

Boundary. Boundary: some files changed, prior analysis of unchanged files is valid. The opposite extreme, re-exploring all 50 files when only 3 changed, is wasteful; the middle path, fresh session plus summary plus targeted re-read of the 3, is correct.

Recurring specifics. Recurring numbers: 3 of 12 files modified, 3 of 50 changed. Recurring behavior: the agent references functions that no longer exist, proposes edits to removed signatures. The fix phrase "start fresh with a summary injection that captures prior findings, then read the changed files for current state" recurs.

Wrong answers written against this rule

Proposal. resume and re-read only the changed files.

Why it attracts. targeted.

Why it fails. other stale reads remain and pollute reasoning. when

When it would be right. never for cross-module work.

Proposal. fork_session from the stale session.

Why it attracts. branches off.

Why it fails. the fork inherits the stale context. when

When it would be right. only when the stale context is actually desired.

How the same rule gets re-asked
  • Re-asked as "after modifying files, what is most appropriate" (fresh plus summary plus targeted re-read) and "why is resume wrong here" (stale context).
R17

`fork_session` explores divergent branches from a shared baseline

fork_session branches the current session, preserving the full investigation context, file modifications, and intermediate findings, so a second theory can be explored without disturbing the original thread. If the fork proves useful, it merges back; if not, the original is untouched.

Divergent exploration needs the same baseline but must not contaminate the other branch. A fork gives each branch the full shared prefix while keeping the branches separate, which a single shared thread cannot do without mixing assumptions.

Boundary. Boundary: you want two or more independent explorations that each start from one consistent baseline. The opposite case is pure continuation of one thread, where --resume suffices, or independent workstreams with no shared baseline, where plain subagents are better.

Recurring specifics. Recurring scenario: evaluate a repository pattern versus direct database calls, or a REST design versus an event-driven design, both from the same gathered baseline. Recurring phrasing: "fork the session to explore the second theory while preserving the original investigation context".

Wrong answers written against this rule

Proposal. explore both in one shared thread.

Why it attracts. no branching overhead.

Why it fails. conclusions silently mix assumptions from both designs. when

When it would be right. never when isolation between branches matters.

Proposal. start a brand-new instance from scratch for the second theory.

Why it attracts. clean.

Why it fails. loses the shared baseline and re-derives it, diverging on shared contracts. when

When it would be right. only if the baseline is truly irrelevant.

How the same rule gets re-asked
  • Posed as "how to explore two approaches without re-exploring" (fork from shared baseline) and "which tool for divergent parallel exploration" (fork_session).
R18

Crash recovery uses structured state manifests and checkpoints

Each agent exports its current state to a known file location, a manifest, as it works. On resume after a crash, the coordinator loads the manifest and injects the relevant state into agent prompts, so exploration continues from the last checkpoint instead of restarting.

result.json
json
{
  "sessionId": "explore-order-service-001",
  "phase": 2,
  "exploredPaths": ["src/repos/order.ts", "src/services/order.ts"],
  "keyFindings": {
    "architecture": "Layered: Controllers to Services to Repositories to DB",
    "criticalIssue": "RefundProcessor has no retry logic for Stripe API failures"
  },
  "nextSteps": ["Investigate PaymentGateway error handling"]
}

Without durable state, a crash erases everything held in the volatile context window, forcing full re-exploration. A manifest externalises progress so the coordinator can reconstruct exactly what was done, what is in progress, and what remains.

Boundary. Boundary: long or crash-prone sessions need this; short sessions do not. The opposite failure is retrying from the beginning on failure, which is exactly the wasted work the manifest eliminates, or replaying the full transcript, which reintroduces verbose context and is costly.

Recurring specifics. Manifest fields recur: sessionId, phase, exploredPaths, keyFindings, nextSteps, plus lastCheckpoint and pending queues. Recurring scenario: a 28-document pipeline crash at document 12; resume from document 13. The phrase "export structured state to a known location as it works" recurs.

Wrong answers written against this rule

Proposal. increase the context window so more survives in memory.

Why it attracts. more retained.

Why it fails. memory is not durable across a process crash. when

When it would be right. never for crash recovery.

Proposal. checkpoint by writing the full transcript every turn and replay it.

Why it attracts. complete.

Why it fails. reintroduces verbose context, costly, and replay is fragile. when

When it would be right. never.

Proposal. wrap in a retry that restarts from the beginning.

Why it attracts. simple.

Why it fails. wastes completed work. when

When it would be right. never.

How the same rule gets re-asked
  • Re-asked as "which design enables intelligent resume after crash" (manifest export plus load on resume) and "what should the manifest include" (completed, in-progress, pending, findings, checkpoint).
R19

Coordinator-managed manifest restore beats per-agent reload

On resume, a single coordinator loads the manifest and injects the correct state into each agent's prompt in the right order. This is preferred over letting each agent reload its own state independently, because the coordinator can guarantee cross-agent consistency at the recovery point.

Independent per-agent reload captures each agent's state at a different moment during the crash, producing inconsistent shared state and coordination errors. A coordinator-managed restore uses one consistent snapshot and a defined injection order, honouring inter-agent dependencies.

Boundary. Boundary: multi-agent systems with cross-agent dependencies. The opposite is a single-agent session where simple manifest reload suffices, and a shared vector store, which is the wrong tool because retrieval is probabilistic rather than deterministic.

Recurring specifics. Recurring contrast: independent reload gives Agent A state from 14:30, Agent B from 14:28, Agent C from 14:31, causing coordination errors; coordinator restore uses one consistent point. Recurring wrong options: persist the full conversation log and hand it to every agent (token-inefficient, ambiguous); index outputs in a shared vector store and semantic-search on resume (non-deterministic).

Wrong answers written against this rule

Proposal. each agent maintains its own persistent state file and reloads independently.

Why it attracts. clean separation.

Why it fails. no central consistency; timestamps diverge. when

When it would be right. only for truly independent agents.

Proposal. semantic search over a vector store on resume.

Why it attracts. powerful retrieval.

Why it fails. probabilistic, may miss critical state, non-deterministic recovery. when

When it would be right. for open-ended knowledge queries, not state restoration.

Proposal. hand the full coordinator conversation log to every agent.

Why it attracts. complete.

Why it fails. token-inefficient and ambiguous to parse. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "which state management approach best balances fidelity with efficiency" (coordinator loads manifest and injects) and "why not vector search" (needs deterministic structured restore).
R20

Scoped subagents avoid full-history leakage and overload

A subagent should receive only the context it needs for its task: a focused prompt plus the specific facts, file paths, or error messages required. Handing it the entire conversation, including unrelated customer details or 80K of accumulated findings, both wastes tokens and creates information-leakage and confusion risks.

Passing everything violates the principle of least context. The subagent cannot use what it does not need, and the excess tokens crowd its own window, raising cost and the chance it acts on irrelevant detail. A formatting subagent given full payment details is also a security concern.

Boundary. Boundary: pass the minimal necessary context, often a structured summary. The opposite case is a downstream agent that genuinely requires the full prior findings to do its job, where a curated extract still beats the raw whole.

Recurring specifics. Recurring wrong option: "pass the full 20-message history plus the issue summary to a tier-2 subagent, including an unrelated resolved billing question". Recurring right option: "pass only the issue summary and the specific error". A recurring security framing: a subagent that only formats text receiving a customer's full payment details is leakage.

Wrong answers written against this rule

Proposal. give every subagent the full shared context to reduce errors.

Why it attracts. completeness.

Why it fails. token cost and cross-source bleed; a stakeholder proposal that is explicitly rejected. when

When it would be right. never as a blanket.

Proposal. pass the complete 60-message conversation to four parallel subagents.

Why it attracts. they have everything.

Why it fails. redundant context, excessive cost. when

When it would be right. never.

How the same rule gets re-asked
  • Re-asked as "which principle was violated" (least context / minimal injection) and "how to optimise context passing" (send only the task description and needed facts).
R21

Excessive delegation of sequential steps is itself an anti-pattern

Delegating every step, including simple sequential edits to one file, forces each spawned subagent to start from a fresh context that lacks decisions made in earlier steps. The delegation message must then carry all accumulated state, and later steps repeatedly lose earlier decisions, raising latency.

Subagent isolation is a feature, but for a chain of dependent sequential steps it is a liability: each handoff discards the shared state the next step needs. The orchestrating model's delegation frequency is a behavior that prompt steering, not mechanism reconfiguration, corrects.

Boundary. Boundary: delegate when tasks can run in parallel, require isolated context, or are independent workstreams. The opposite case is simple tasks, sequential operations, single-file edits, or anything needing maintained context across steps: work directly.

Recurring specifics. Recurring scenario: an orchestrator delegates nearly every step including simple sequential edits, later steps lose earlier decisions, latency grows. The fix: explicit guidance reserving delegation for parallel or isolated tasks and directing direct work for simple sequential steps. Recurring wrong options: remove the Agent tool entirely (loses needed capability), enable fork mode (masks the symptom, keeps overhead), lower subagent effort (cannot recreate shared state).

Wrong answers written against this rule

Proposal. remove the Agent tool so all work runs in main.

Why it attracts. stops wasteful delegation.

Why it fails. removes a capability still needed for genuine isolation. when

When it would be right. never.

Proposal. enable fork mode so every subagent inherits full history.

Why it attracts. fixes lost context.

Why it fails. leaves per-step spawn overhead and churn in place. when

When it would be right. only when inherited context is actually wanted.

Proposal. lower subagent effort setting.

Why it attracts. cheaper per call.

Why it fails. cannot recreate shared state across isolated contexts. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "which change best addresses over-delegation" (prompt steering to reserve delegation) and "why is fork mode not the fix" (overhead remains).
R22

Isolation costs shared state; pick orchestration by dependency shape

Delegation buys context isolation at the cost of state sharing. The orchestration choice should follow the dependency structure: shared-state sequential work stays in the main session; independent or parallel work goes to subagents; work needing the full inherited context and a divergent branch uses a fork.

Each option trades off differently. Running everything in main pollutes context with exploration noise. Delegating dependent edits pays the isolation cost where it hurts most. Forking inherits history but adds spawn overhead. Matching the tool to the dependency shape avoids both pollution and lost state.

Boundary. Boundary: a chain of four dependent refactor edits needs one continuous context; three independent service investigations need parallel subagents. The opposite mistake is delegating the dependent edits (each starts blind) or keeping the independent investigations in main (pollutes the context the refactor needs).

Recurring specifics. Recurring scenario: a refactor of an alerting module with four sequential edits, plus three unrelated telemetry-service investigations. Correct decomposition: perform the interdependent edits directly in main, delegate the three investigations to parallel subagents. Recurring note: a standard subagent isolates context, not the filesystem; its edits still land in the shared checkout.

Wrong answers written against this rule

Proposal. spawn a subagent for each refactor edit to isolate context.

Why it attracts. isolation.

Why it fails. each edit starts blind to prior decisions. when

When it would be right. never for dependent edits.

Proposal. run the three investigations sequentially in main.

Why it attracts. simple.

Why it fails. pollutes the context the refactor needs. when

When it would be right. never.

Proposal. spawn parallel subagents for both edits and investigations.

Why it attracts. throughput.

Why it fails. ignores the dependency chain of the edits. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "how should they decompose the work" (dependent in main, independent as subagents) and "what does a subagent isolate" (context, not filesystem).
R23

Offload intermediate results, not just conclusions, to scratchpad

Beyond final findings, intermediate outputs, query results, per-step computations, should be written to a file after each step and read back only when needed. This keeps the context budget free for the current step's reasoning instead of holding large intermediate blobs.

Intermediate results are often large and low-signal for later steps. Keeping them in context consumes budget that the current reasoning needs. Offloading them to a file preserves them without occupying the window.

Boundary. Boundary: applies when intermediate results are bulky (large query result sets, multi-step computations). The opposite case is a single small result that is cheaper to keep inline than to write and re-read; there, inline is fine.

Recurring specifics. Recurring scenario: join Snowflake billing data with PostgreSQL usage, apply currency normalisation, produce cost-per-user by region; write each step's intermediate result to a scratchpad, read back only the final aggregated data. Recurring contrast: summarising intermediate numerical results risks the progressive-summarisation trap (rounding), so offload the exact values rather than summarise them.

Wrong answers written against this rule

Proposal. execute all queries in one cross-database SQL statement to avoid intermediates.

Why it attracts. no intermediate storage.

Why it fails. Snowflake and PostgreSQL cannot join in a single statement; and the approach does not generalise. when

When it would be right. never here.

Proposal. summarise intermediate results between steps.

Why it attracts. smaller context.

Why it fails. risks rounding and precision loss on numbers. when

When it would be right. only when exact values are not later needed.

How the same rule gets re-asked
  • Posed as "most effective strategy for multi-step analysis within limits" (write intermediates to scratchpad, read back only what is needed) and "why not summarise" (precision loss).
R24

Hierarchical phase compression keeps recent detail, summarises old

For very long tasks, completed phases are compressed into structured summaries while the most recent active work remains in full detail. The phase summaries are injected at the top of context, preserving continuity without retaining every historical message.

output.txt
text
completed_phase_1: { findings: [...], decisions: [...], open: [...] }
completed_phase_2: { findings: [...], decisions: [...], open: [...] }
active_work: <last 50 messages in full>

A sliding window that keeps only the last N messages discards early constraints and task definitions that later work still needs. Full compression into one paragraph loses structure. Hierarchical compression keeps the structure (findings, decisions, open questions) while shedding bulk.

Boundary. Boundary: tasks spanning 150 to 200 messages across multiple phases. The opposite is a sliding window (drops needed early context) or full single-paragraph compression (loses structure and detail needed for ongoing work).

Recurring specifics. Recurring phrasing: "hierarchical summarisation, preserve task continuity, completed phases become compact structured summaries, recent work stays detailed". Recurring numbers: 200 messages, first 150 completed, last 50 active; compress 1-150, keep 151-200. Recurring wrong option: "keep all 200 messages, more context is always better" (efficiency suffers).

Wrong answers written against this rule

Proposal. sliding window, always keep only the last N messages.

Why it attracts. simple.

Why it fails. discards task definitions and early findings. when

When it would be right. never for long tasks.

Proposal. full context compression into one paragraph.

Why it attracts. maximal shrinkage.

Why it fails. loses structure and needed detail. when

When it would be right. never.

Proposal. context reset every 50 messages.

Why it attracts. clean.

Why it fails. loses accumulated knowledge. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "which technique best preserves continuity when compressing" (hierarchical summarisation) and "optimal structure going forward" (summarise completed, keep recent detailed).
R25

Externalise load-bearing specifics so compaction cannot erase them

Certain facts, exact numeric thresholds, latency contracts, jurisdiction, confirmed statutes, specific renamed identifiers, must survive compaction and the passage of many turns. They should be written to a durable file (scratchpad or persistent memory) and re-read when needed, rather than left to live in conversation where compaction or summarisation will erode them.

Compaction and progressive summarisation are lossy by nature; they compress, round, or generalise. The facts that are load-bearing are exactly the ones a loose summary will damage. Externalising them to a referenced file makes them immune to the compression that erodes in-context copies.

Boundary. Boundary: apply this to any fact whose loss would corrupt later output, exact figures, legal scope, verified host lists. The opposite case is ephemeral or low-stakes detail that is fine to let go; there, in-context is acceptable.

Recurring specifics. Recurring failures: a p99 budget of 850 ms resurfaced as "about 200 ms" in a draft ADR; a verified host list paraphrased as "core racks migrated" led to a host being cut over twice; a legal agent lost the client's jurisdiction and applicable statutes after each /compact. The fix in each: persist the exact values to a file and re-read before the load-bearing step.

Wrong answers written against this rule

Proposal. add a system-prompt instruction to "retain all exact figures and never round".

Why it attracts. explicit.

Why it fails. it helps recent turns but earlier figures keep degrading as conversation grows. when

When it would be right. as a supplement, not the fix.

Proposal. rely on /compact focus instructions to preserve the numbers.

Why it attracts. targeted compaction.

Why it fails. compaction still compresses; the precise value is not guaranteed. when

When it would be right. never as sole protection.

How the same rule gets re-asked
  • Posed as "how to protect a constraint set during compaction" (externalise to a file) and "why did exact figures drift" (entered the running summary).
R26

Exploration skills and commands must run in isolated context

A custom skill or slash command that performs verbose codebase exploration, printing thousands of lines of intermediate findings, should be configured so its output does not land in the main conversation. The frontmatter option context: fork or delegation to a subagent keeps the main session clean while the skill runs.

If the skill's verbose enumeration enters the main context, it crowds out the user's actual task and degrades later answers, exactly the degradation pattern seen in long exploration. Running it in a forked or subagent context means only the final result returns to main.

Boundary. Boundary: any skill or command whose intermediate output is large and irrelevant to the surrounding task. The opposite case is a skill whose output is small and directly part of the task; there, inline is fine.

Recurring specifics. Recurring skill names: /survey-deps, /deep-analysis, /analyze-codebase, /deep-analyse, an OpenAPI client scaffolder. Recurring wrong fix: "tell the skill to be concise and summarise at the end" (trims the closing recommendation but the verbose intermediate crawl still lands). Recurring right fix: frontmatter context: fork, or run via a subagent. The scaffolder emitting 4,000 to 6,000 lines of enumeration before the one file wanted is the canonical example.

Wrong answers written against this rule

Proposal. add a prompt instruction to "be concise and summarize at the end".

Why it attracts. easy.

Why it fails. the intermediate crawl still lands in context before the summary. when

When it would be right. insufficient alone.

Proposal. trim the printed output with a flag.

Why it attracts. less noise.

Why it fails. engineers still need full enumeration when generation fails; degradation persists on large specs. when

When it would be right. only as a partial aid.

How the same rule gets re-asked
  • Posed as "which frontmatter option fixes this" (context: fork) and "best fix that preserves full analysis" (isolate the skill's context).
R27

Memory files are the persistent instruction source, not conversation

Persistent instructions live in memory files such as CLAUDE.md (project or user level) and are loaded into every session. /compact compresses the conversation but does not erase these instructions; they persist across compaction. When the agent enforces a rule the user never defined, the diagnostic is to inspect all currently loaded memory with the /memory command.

Memory files are the durable source of standing instructions, separate from the volatile conversation. Compaction replaces conversation history with a summary but the standing instructions remain authoritative. A behavior with no obvious source is almost always a loaded memory file the user has not inspected.

Boundary. Boundary: /memory inspects loaded instructions; /compact manages conversation, not memory. The opposite mistake is assuming a strange behavior comes from the conversation or from CLAUDE.md alone, when another memory source (user-level or other) is the real origin.

Recurring specifics. Recurring scenario: a developer sees a commit-message format she never defined, finds nothing in root or user CLAUDE.md, and the right diagnostic is /memory to inspect all active memory. Recurring clarification: after /compact, instructions defined in CLAUDE.md remain; compaction affects conversation history, not the memory files.

Wrong answers written against this rule

Proposal. run /compact to clear the unwanted behavior.

Why it attracts. cleanup.

Why it fails. /compact does not touch memory files; the behavior remains. when

When it would be right. never for this.

Proposal. edit the root CLAUDE.md only.

Why it attracts. obvious place.

Why it fails. the rule may live in user-level or another memory file. when

When it would be right. only after /memory confirms it is there.

How the same rule gets re-asked
  • Posed as "which command inspects loaded memory" (/memory) and "what happens to CLAUDE.md instructions after compaction" (they persist).
R28

`auto_compact` frontmatter makes compaction a configuration default

A CLAUDE.md (or skill) frontmatter setting auto_compact: true makes the session compact automatically as context grows, without the user issuing /compact manually. Setting it false leaves compaction manual, which risks the window filling in a long interactive session.

Explicit automatic compaction enforces the proactive-compaction discipline at the configuration level, so long sessions stay lean without relying on the user to remember. Manual-only mode depends on human intervention that may come too late.

Boundary. Boundary: long-running interactive sessions benefit from auto_compact: true. The opposite case is a session where the user wants full manual control, or where automatic compaction might discard something they intend to keep; there, false is acceptable but riskier.

Recurring specifics. Recurring option: auto_compact: true in CLAUDE.md frontmatter. Recurring contrast question: consequence of auto_compact: false in a long interactive session (context fills, degradation risk). The command-level equivalent is issuing /compact proactively.

Wrong answers written against this rule

Proposal. rely on the model to drop old turns automatically.

Why it attracts. sounds automatic.

Why it fails. the model does not auto-drop; context only grows until compaction. when

When it would be right. never.

Proposal. set auto_compact: false and never compact.

Why it attracts. keep everything.

Why it fails. window fills, quality degrades. when

When it would be right. never for long sessions.

How the same rule gets re-asked
  • Posed as "effect of auto_compact: true" (auto compaction as context grows) and "consequence of false" (risk of filling).
R29

Detect degradation by symptom, not by assuming automatic drop-off

The reliable leading indicator of needed context intervention is observed symptom: the agent referencing typical patterns instead of specifics, contradicting its own earlier statements, responding more slowly, or costing more per turn. One must not assume the model automatically drops old turns; context only grows until something compacts it.

Degradation is a salience and compression artifact, not an automatic eviction. Believing the model "forgets old turns on its own" leads to inaction while quality silently declines. Watching for the symptom lets the architect intervene with compaction, scratchpad, or fresh session before damage spreads.

Boundary. Boundary: intervene when generic references or contradictions appear. The opposite assumption, that old turns are auto-discarded so no management is needed, is the trap. Also, a session that is merely long but still specific does not yet need intervention.

Recurring specifics. Recurring phrasing: "an engineer assumes the model drops old turns automatically" as the flawed premise; the architect must design for stable long-session behavior. Observability signals recur: generic references, contradictions, slowness, rising cost. The most useful leading indicator is the symptom of specificity loss, not raw token count alone.

Wrong answers written against this rule

Proposal. assume the model auto-trims old context, so do nothing.

Why it attracts. seems self-managing.

Why it fails. context only grows; degradation accumulates. when

When it would be right. never.

Proposal. intervene only when the hard limit is hit.

Why it attracts. reactive.

Why it fails. quality already degraded by then. when

When it would be right. never as sole trigger.

How the same rule gets re-asked
  • Posed as "which observability signal best indicates intervention" (specificity loss symptom) and "what assumption is wrong about old turns" (they are not auto-dropped).
R30

Context windows have hard limits; bigger windows only delay failure

The context window is a fixed ceiling set by the model. It cannot be arbitrarily enlarged by configuration. Choosing a larger-context model raises the ceiling but does not change the salience dynamics: a sufficiently long exploration still dilutes early findings, only later.

Attention is relative, not absolute. Even at 200K tokens, the gap between turn 8 and turn 200 grows, and early specifics compete with an ever-larger volume of later content. The window size changes when degradation starts, not whether it starts.

Boundary. Boundary: a larger window is the correct fix only when the actual problem is that required content physically cannot fit (a single huge artifact). The opposite is the degradation scenario, where the window is not the bottleneck and enlargement is a distractor.

Recurring specifics. Recurring phrasing: "context windows have hard limits set by the model; you cannot arbitrarily increase them". Recurring scenario: an agent at 80% of an 80K window, or 180K of a 200K window, where the answer is to compact or externalise, not to enlarge. The "even models with long context windows suffer from lost-in-the-middle" line recurs.

Wrong answers written against this rule

Proposal. switch to a model with a larger context window to fix drift.

Why it attracts. more room.

Why it fails. delays but does not prevent salience degradation. when

When it would be right. only for genuine fit problems.

Proposal. increase max_tokens so more stays in context.

Why it attracts. conflates output and input.

Why it fails. max_tokens is output length; irrelevant to input retention. when

When it would be right. never for this.

How the same rule gets re-asked
  • Posed as "what architectural consideration is most important at 150K of 200K" (manage compaction, not enlarge) and "why not just use a bigger window" (only delays).
R31

Recover from an unexpected huge tool dump by summarising, not clearing

When a single tool returns a far larger payload than expected, for example a 50K database dump instead of a 2K summary, the context can be left unable to complete the task. The recovery is to summarise the retained key findings into a compact state and continue from that, not to clear the whole session and lose everything.

Clearing discards all prior valid work; the dump was the problem, not the surrounding investigation. Extracting the key findings (file paths, errors, decisions, current hypotheses) into a compact summary and evicting the raw dump preserves the essential state while freeing the window.

Boundary. Boundary: applies when a sudden large payload threatens to exhaust context mid-task. The opposite is a normally growing session where proactive /compact or scratchpad use is the routine answer, not a one-off recovery.

Recurring specifics. Recurring scenario: a tool returns a 50K token database dump instead of the expected 2K summary, agent cannot complete the task. The fix: "extract key findings into a structured state summary, then evict raw tool outputs". The three retained classes recur: file paths, error messages, decisions made, plus current hypotheses.

Wrong answers written against this rule

Proposal. clear all tool outputs and start fresh.

Why it attracts. immediate relief.

Why it fails. loses the entire investigation. when

When it would be right. never mid-task.

Proposal. increase the context window to hold the dump.

Why it attracts. fits it.

Why it fails. window is hard-limited; and the dump is low-signal anyway. when

When it would be right. never.

Proposal. switch to a more powerful model.

Why it attracts. handles longer contexts.

Why it fails. does not address the noisy payload; lost-in-the-middle still applies. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "correct recovery approach for an unexpected large dump" (summarise and evict) and "why not clear" (loses work).
R32

Delegate with a specific prompt and the exact context the worker needs

A subagent succeeds when its prompt is specific about the subtask and includes the exact inputs it requires, such as a file path, an error message, or the prior findings it must build on. Vague prompts like "search and report" yield unfocused or missing output because the worker has no anchor.

A subagent starts from a clean context with no inherited understanding. Without a precise prompt and the needed facts, it either reinterprets the task broadly or silently lacks the context another agent held. Specificity converts isolation from a liability into a controlled, reproducible handoff.

Boundary. Boundary: any delegated subtask needs a scoped prompt plus the minimal necessary inputs. The opposite is a vague one-line prompt with no context, which produces the "broad, unfocused findings" or "no findings to work with" failures.

Recurring specifics. Recurring bad prompt: "Search for information and report back what you find." Recurring good prompt: a focused question plus the exact file path and error string. Recurring failure: a synthesis subagent reports no findings because the coordinator held them and never passed them. Recurring failure: a subagent acts without a file path or error it was never given.

Wrong answers written against this rule

Proposal. give the subagent the full 60-message history so it has context.

Why it attracts. completeness.

Why it fails. buries the needed facts in noise and wastes tokens. when

When it would be right. pass a targeted summary instead.

Proposal. let the subagent infer scope from the parent conversation.

Why it attracts. less writing.

Why it fails. the subagent cannot see the parent conversation. when

When it would be right. never.

How the same rule gets re-asked
  • Posed as "why did the subagent return out-of-context results" (vague prompt, missing injected context) and "what makes a delegate productive" (specific prompt plus needed inputs).
Order service exploration that holds precise references across thirty turns
A production walkthrough with the reasoning chain made explicit.

A developer productivity agent is exploring an unfamiliar order service codebase for the first time. The exploration runs across multiple turns reading directory structure, examining main entry points, tracing the refund flow from API endpoint to database, and identifying external integrations. By turn 15 the agent's context contains roughly 80,000 tokens of file contents, search results, and intermediate reasoning.

When the developer asks what is the cache invalidation strategy in OrderRepository, the without-fix agent responds the repository follows typical caching patterns for performance. This is wrong in two ways: it is not the specific answer and it is the observable symptom of degradation where the model has lost the specific class name, file path, and method-level details. The earlier finding that cache invalidation is missing on status change is buried under more recent verbose output.

The fix uses three combined mitigations. The agent writes a scratchpad from the start and updates it after each step with OrderRepository at src slash repos slash order dot ts implementing Repository with custom findById caching and cache invalidation missing on status change, OrderService at src slash services slash order dot ts orchestrating OrderRepository and PaymentGateway, RefundProcessor at src slash services slash refund dot ts depending on OrderService dot getOrderWithItems with no retry logic for Stripe failures, and test coverage with OrderService 87 percent and RefundProcessor 12 percent. The agent also delegates specific investigations to subagents for external API integrations and refund flow tracing, each returning a structured summary with integration name, error handling pattern, and retry presence without verbose file contents. After Phase 1 the coordinator injects the high-level architecture summary of layered architecture from controllers to services to repositories to database into Phase 2 subagent prompts.

By turn 16 the agent reads the scratchpad and the specific class names and findings are restored. A comparison run shows the without-scratchpad agent degrades to generic references by turn 15 such as the typical repository pattern and external services usually use retries, while the with-scratchpad agent maintains specific identifiers throughout the session. A crash at turn 22 tests recovery: on resume the coordinator loads a manifest with phase 2, exploredPaths for three service files, keyFindings with layered architecture, critical issue of missing retry logic, and test coverage, plus nextSteps for PaymentGateway error handling and cache invalidation logic, and the agent continues from turn 22 without repeating earlier exploration.

Distinctions that decide answers

ThisNot thisHow to tell them apart
Context degradationToken limit exhaustionDegradation is the model losing track of specific findings as verbose output accumulates. Exhaustion is the request failing because it exceeds the window. Increasing the window does not fix degradation, restructuring context with scratchpads and isolation does.
Scratchpad fileConversation contextA scratchpad is a file on disk that persists across turns and sessions. Conversation context is the messages array that gets compressed and may be lost. The scratchpad is immune to degradation because specific references are stored outside the conversation.
Subagent delegation for isolationParallel execution for speedIsolation keeps the main agent's context clean for coordination and is the primary benefit for exploration. Parallel execution runs investigations concurrently for speed and is secondary, available only when investigations are independent.
Summary injectionCold startInjection places Phase 1 findings into Phase 2 prompts so they start with architectural context. Cold start gives Phase 2 no prior context and forces rediscovery of the system structure, wasting cost and polluting context.
State manifestScratchpad fileA scratchpad captures findings such as what was learned. A manifest captures process state such as what was explored, current phase, and next steps. The manifest answers where am I and what is next, the scratchpad answers what do I know.
Proactive compactReactive compact at the limitProactive compact during the session preserves context quality throughout. Reactive compact only when nearly full is a last resort that may have already pushed specific findings into the degraded middle.

Traps

Treat a scratchpad as a purely local convention with no supported surface

The tempting answer. Write findings to an ad hoc file path because the pattern is about discipline and any file will do.

Why it fails. There is a supported surface for exactly this, the memory tool, and it comes with a security requirement that an ad hoc implementation usually misses. The tool is client-side, so the model only requests operations and your handler executes them, which means your handler is the only thing standing between a requested path and the filesystem. A handler that does not reject paths escaping the memory directory is a traversal bug, not a scratchpad.

What is correct. Use the documented memory tool with a handler that contains every operation inside the memory directory, and treat the pattern as just-in-time retrieval rather than a private file convention.

Increase the context window to solve context degradation

The tempting answer. Use a larger window because more room for verbose output sounds like the direct fix.

Why it fails. Degradation is not about running out of tokens but about losing track of specific details as verbose output accumulates. A larger window still fills with verbose output and the lost-in-the-middle effect still degrades middle findings.

What is correct. Use structural fixes: scratchpad files and subagent delegation for isolation, not a larger window.

Assume subagent delegation is only about parallelisation

The tempting answer. Read delegation as a speed optimisation because parallel execution is the obvious benefit.

Why it fails. The primary benefit for codebase exploration is context isolation keeping the main agent's context clean for coordination. Speed is secondary and only present when investigations are independent.

What is correct. Delegate to keep the main context clean, and treat concurrency as a bonus when the investigations allow it.

Restart the session to fix degradation without saving state

The tempting answer. Restart because a fresh session feels like a clean fix for a degraded context.

Why it fails. Restarting loses all accumulated knowledge including specific class names, file paths, dependency chains, and critical findings the agent has discovered.

What is correct. Persist findings to scratchpad files and state manifests before restarting, then inject them into the new session so the agent picks up where it left off.

Use compact only when hitting context limits

The tempting answer. Treat compaction as a last-resort cleanup because the command feels like it should be saved for when space is tight.

Why it fails. By the time context is nearly full verbose output has already pushed specific findings into the middle where positional degradation applies.

What is correct. Use compact proactively during extended sessions to preserve quality throughout, not only when the window is nearly full.

Skip summary injection because Phase 2 can re-explore

The tempting answer. Skip injection because re-exploration is feasible and avoids wiring an extra step.

Why it fails. Re-exploration duplicates work of reading the same files and running the same searches, wastes API cost and time, and pollutes the Phase 2 subagent's context with output that should already be known.

What is correct. Extract Phase 1 findings and prepend them as a Phase 1 Summary section in Phase 2 subagent prompts.

Have the upstream agent return verbose reasoning chains to preserve context downstream

The tempting answer. Return verbose reasoning because more text feels more informative for the downstream agent.

Why it fails. Downstream agents with limited context budgets cannot use verbose reasoning and waste tokens re-parsing prose that could have been structured findings.

What is correct. Have upstream agents return structured findings with key facts, file paths, and relevance scores that downstream agents can process without re-parsing prose.

Going deeper than the task statement
Adjacent material that shows up in harder scenario questions.
Supervisor and worker with strict context separation and arbitration

Supervisor maintains coordination context of architecture, phase, and next steps while each worker runs isolated on a specific task and returns structured findings. When workers disagree the supervisor arbitrates via priority rules, timestamp ordering, or human escalation.

Multi-Agent Context Isolation and Coordination
Four recurring context management operations

Selection by relevance, recency, importance, or task, writing with structural formatting that survives positional degradation, compression by summarisation or extraction or eviction, and isolation by per-agent windows and role-based access to prevent cross-agent contamination.

Context Management and Orchestration
Coverage annotations as a visible gap pattern for synthesis

Synthesis output includes a structured section listing each topic area with its data quality status, where failed subagent topics are noted with failure type and alternatives, making gaps visible by construction rather than relying on consumers to notice them.

The Context Engineering Stack
Build it
Contain verbose exploration with isolation, persistence, and injected handoffs
  1. Build a coordinator that delegates specific codebase exploration tasks to subagents rather than doing all exploration in the main context, spawning each with a focused prompt such as find test files or trace the refund flow and expecting a structured summary return without verbose file contents.
  2. Implement scratchpad file management where the agent writes structured findings to a Markdown file with sections for Key Classes, Dependency Chains, and Critical Findings after each step and reads the file before each subsequent step, capturing specific class names and file paths not summaries.
  3. Build summary injection logic with a function injectPhase1Summary that prepends Phase 1 findings as a Phase 1 Summary section to Phase 2 subagent prompts so Phase 2 starts with architectural context and avoids rediscovery.
  4. Implement crash recovery via a JSON manifest with sessionId, phase, exploredPaths, keyFindings, and nextSteps, and verify that on simulated crash and resume the coordinator loads the manifest and injects the state into the agent's initial prompt.
  5. Run an extended exploration across ten or more modules twice, once without scratchpads where the agent degrades to generic references by turn 15 and once with scratchpads where it maintains specific identifiers throughout, and use compact proactively at turn 10 and turn 20 to measurably reduce token count while preserving specific findings via the scratchpad.

Verify. The main context stays clean for coordination, precise references survive the full session and a simulated crash, and verbose output is confined to isolated subagent contexts without polluting downstream consumers.

Answer real questions on this task
8 exam-style questions written against the rules above. You get the reasoning and a note on every wrong option after each answer.
Task 5.516 min

Human Review and Confidence Calibration

Measure accuracy by document type and field, calibrate per-stratum confidence, and prioritise limited reviewer time by uncertainty.

What you need to know

Human review is the safety net for automated extraction and classification systems. The exam tests the allocation problem, not whether to use human review but how to allocate limited reviewer capacity to maximise accuracy while minimising cost. This requires understanding confidence calibration, the trap of aggregate metrics, and stratified sampling strategies. The most dangerous misconception is the aggregate metrics trap: a system reports 97 percent overall accuracy, the team celebrates, and management approves full automation for all high-confidence extractions. The problem is that the aggregate hides catastrophic failure rates on specific segments where errors have the highest business impact.

A system that extracts dates from standard invoices at 99.5 percent accuracy might extract dates from handwritten receipts at 60 percent, from scanned PDFs with poor OCR at 72 percent, and from international documents with non-standard formatting at 45 percent. The aggregate looks excellent because standard invoices dominate volume, but three document types have unacceptable accuracy hidden by the volume-weighted average. Automating high-confidence extractions on handwritten receipts at 95 percent confidence would let through errors at 40 to 55 percent rates. The rule is to always validate accuracy by document type and field segment before automating and never make automation decisions based on aggregate metrics alone. International invoices from new suppliers, handwritten receipts from field staff, and scanned historical documents for compliance are the canonical segments where aggregate approval fails.

Stratified random sampling is the ongoing verification mechanism. After validating by document type and field you need continuous verification because accuracy can drift as documents age, schemas change, or new edge cases emerge. Stratified random sampling means selecting a representative sample from each stratum defined by document type, confidence band, and field type and having humans verify it. The critical insight is that you must sample high-confidence extractions, not just low-confidence ones. Low-confidence items are already routed to human review while high-confidence items are automated. If the model develops a novel error pattern that affects high-confidence extractions, only stratified sampling that includes the high-confidence band will catch it. Sampling serves two purposes: ongoing accuracy measurement to confirm each segment maintains its validated rate over time, and novel error pattern detection to discover new failure modes that did not exist in the original validation set.

Field-level confidence calibration is the mechanism for converting raw model confidence into actionable routing decisions. The model can output confidence per field, for example vendorName with value Acme Corp at 0.98 and totalAmount at 0.72, but raw scores are not calibrated. A model that reports 0.95 confidence might actually be correct 88 percent of the time on certain field types or 99 percent on others. The score is relative not absolute. Calibration requires labelled validation sets with ground truth. Take documents with known correct extractions, run the model, compare its confidence scores to actual accuracy, and build a calibration curve by binning predictions by reported confidence and computing actual accuracy in each bin. The curve might show when the model reports 0.90 on date fields it is actually correct 94 percent of the time while 0.90 on amount fields means only 82 percent. Calibrated thresholds then drive routing where fields above the calibrated threshold route to automation with stratified sampling, fields below route to human review, and fields in the ambiguous zone route to prioritised review.

Reviewer capacity prioritisation is where system design matters most. Human reviewers are expensive and limited, so route the highest-uncertainty items first: low model confidence fields, extractions from ambiguous or contradictory sources, document types with historically poor accuracy, and fields where the model expresses multiple possible interpretations. Do not spread reviewer capacity evenly across all extractions, because even distribution wastes time re-verifying high-confidence items that the model handles well while leaving insufficient capacity for the uncertain items that actually need judgement. The queue should be a dynamic priority queue ordered by uncertainty where the next item served is always the highest-uncertainty item remaining, not the next in chronological order, and when new extractions arrive the queue reorders dynamically. The validation sequence before automation is fixed in order: measure accuracy by document type and field segment not aggregate, calibrate confidence scores using labelled sets, set calibrated thresholds for automation versus review, implement stratified sampling for ongoing verification of automated extractions, and only then reduce human review on segments that demonstrate consistent validated accuracy. Skipping to the last step based on aggregate metrics is the trap and every step exists to prevent a specific failure mode.

Per-stratum accuracy, calibration curves, and routing thresholds

Aggregate accuracy is a single number across all documents, per-type and per-field accuracy is a matrix showing accuracy for each document type and each field type separately. The matrix exposes segment failures that the aggregate hides, and validation must use the matrix before any automation decision. Standard invoices at 99.5 percent on dates and handwritten receipts at 60.1 percent on dates can coexist inside a 97 percent aggregate, and the decision to automate must be made per cell not per aggregate.

The calibration module takes a labelled validation set and produces calibrated thresholds per field type per document type as a curve mapping confidence ranges to actual accuracy. For example 0.90 to 0.95 on date fields on invoices at actual accuracy 94 percent versus 0.90 to 0.95 on amount fields on invoices at 82 percent. Raw model confidence is not comparable across field types until it has been calibrated, and a single static threshold across all extractions lets through errors on segments where the same score means lower accuracy. Objective signals that complement calibration include self-consistency that runs the same prompt three times and escalates on disagreement, hedging detection that flags uncertain language such as I think or possibly, log-probability analysis that inspects token distributions, and meta-cognitive prompting that asks the model what it would need to verify before answering.

Stratified sampling that must include the high-confidence band

The stratified sampling function selects a representative sample from each stratum proportionally to its volume in the population, not uniformly. A stratum with 1,000 invoices at high confidence and 50 handwritten receipts at high confidence contributes samples at a 20 to 1 ratio, not 1 to 1. High-confidence sampling includes automated extractions in the verification sample, low-confidence-only sampling verifies only items already routed to human review. High-confidence sampling is what catches novel error patterns in automated extractions, low-confidence-only sampling misses them entirely.

Stratified sampling is a permanent feature, not a temporary verification step that ends after validation. As prompts, schemas, or routing logic change the golden dataset pattern provides regression testing for accuracy on canonical examples, while red-teaming deliberately constructs adversarial inputs that target known failure modes. Validation pipelines with schema, semantic, and business-rule stages produce structured errors that feed into the calibration data, and content filter pipelines handle appropriateness while confidence calibration handles accuracy, the two running in parallel with a structured routing decision.

Dynamic priority queue over even distribution

The review router implements a priority queue ordered by uncertainty. Items enter with their confidence scores and the queue orders them so the next item served is always the highest-uncertainty item remaining. When new extractions arrive the queue dynamically reorders and reviewers consume from the top, never in chronological order. Even distribution spreads reviewer time across all extractions, priority queue concentrates effort where judgement matters, and average handle time drops because reviewers spend time on items that need it rather than re-verifying easy high-confidence items.

The calibration loop is continuous, not a one-time setup activity. Accuracy drifts as documents age, schemas change, or new edge cases emerge, and calibration must be maintained via ongoing stratified sampling that captures novel patterns. Production-traffic calibration uses reviewer decisions on real extractions to adjust thresholds initially set from validation-set calibration. The detected_pattern field on each finding turns dismissal data into systematic improvement: patterns with high dismissal rates become candidates for prompt refinement, closing the extract to validate to refine loop.

Authoritative mechanism reference

The sections below expand the tested rules into the complete documented surface: ownership, lifecycle, version differences, production boundaries, and adjacent mechanisms.

Mechanism reference

This section is the authoritative depth for every mechanism the task depends on. Where Anthropic documentation provides a guarantee it is cited in platform form. Where the mechanism is engineering practice the lessons that teach it are cited, and any percentage used as illustration is identified as a figure from the tested material rather than an official measurement.

Mechanism reference: 1. Why a headline figure hides a weak segment

The trap is arithmetic. A pipeline reports a single accuracy figure averaged over its entire throughput. The population behind that figure is heterogeneous by construction: different document types present different layout noise, and different fields present different semantic difficulty. When volume is skewed, the easy majority dominates the average and the hard minority disappears inside it. This is not a judgment about model quality; it is what happens when you average over a heterogeneous population without first partitioning it.

The reference illustrates the point with four document types and three fields. Figures from the tested material show standard invoices at 99.5 percent on dates, handwritten receipts at 60.1 percent, scanned PDFs at 72.4 percent, and international formats at 45.2 percent, while the volume-weighted aggregate sits at 97.0 percent. An invoice extraction pipeline that auto-processes everything above 90 percent confidence can therefore look healthy on aggregate while a specific vendor's invoices are frequently wrong despite high confidence. The same pattern recurs per field: party name at 99 percent while indemnification clause sits at 71 percent, even though the model's reported confidence is high on both. The testing lesson makes the general point with a stratified table where simple classification shows 99 percent, complex extraction 78 percent, multi-step reasoning 55 percent, and the aggregate 86 percent, leading a team to ship on the strength of the average while the critical 55 percent category remains unacceptable.

Volume weighting explains why the weak segment stays hidden as volume grows. If 80 to 98 percent of traffic belongs to an easy type at 98 to 99 percent accuracy, a 2 to 8 percent tail at 60 to 85 percent accuracy barely moves the headline. Enlarging the overall sample makes the headline more statistically significant but still averages over the same hidden heterogeneity, so the weak segment remains masked with tighter confidence intervals around the wrong statistic. Raising the headline threshold from 97 to 99 percent has the same flaw: a higher aggregate can still be satisfied by improving the already strong majority while leaving the weak tail untouched. The only correction is to stop reporting a single number and report the matrix.

The example below computes that matrix directly from labelled outcomes so a strong headline cannot hide a weak cell. It is the first of the six required substantial examples. It ingests a held-out labelled set where each example carries document type, field, the model's extracted value, and the ground truth, then emits accuracy per segment alongside the misleading aggregate. The takeaway is immediate: an overall 96 to 97 percent figure can coexist with a field at 71 percent and a document type at 60 percent, exactly the pattern the tested material uses to anchor the trap.

example.ts
typescript
// Per-segment accuracy matrix computed from labelled outcomes
// Proves: a strong headline cannot hide a weak segment once you partition by type and field.
// Uses only the labelled validation set; no production sampling here.

type LabelledOutcome = {
  docId: string
  docType: string          // e.g., "standard_invoice" | "handwritten_receipt" | "scanned_pdf" | "international"
  field: string            // e.g., "invoiceDate" | "totalAmount" | "vendorName" | "indemnification_clause"
  predicted: string
  truth: string
  rawConfidence: number    // uncalibrated, 0 to 1, from the extraction envelope
}

type SegmentKey = `${string}::${string}` // docType::field

type SegmentAccuracy = {
  docType: string
  field: string
  total: number
  correct: number
  accuracy: number         // 0 to 1
}

function buildAccuracyMatrix(outcomes: LabelledOutcome[]): {
  aggregate: Omit<SegmentAccuracy, "docType" | "field"> & { label: string }
  segments: SegmentAccuracy[]
  flagged: SegmentAccuracy[] // below target, must keep under review
} {
  const TARGET = 0.95 // application policy; not an Anthropic-published threshold

  let totalCorrect = 0
  const bySegment = new Map<SegmentKey, { docType: string; field: string; correct: number; total: number }>()

  for (const o of outcomes) {
    const isCorrect = o.predicted.trim() === o.truth.trim()
    if (isCorrect) totalCorrect += 1

    const key: SegmentKey = `${o.docType}::${o.field}`
    const entry = bySegment.get(key) ?? { docType: o.docType, field: o.field, correct: 0, total: 0 }
    entry.total += 1
    if (isCorrect) entry.correct += 1
    bySegment.set(key, entry)
  }

  const segments: SegmentAccuracy[] = [...bySegment.values()]
    .map((e) => ({ ...e, accuracy: e.total === 0 ? 0 : e.correct / e.total }))
    .sort((a, b) => a.accuracy - b.accuracy) // worst first, so review focuses there

  const flagged = segments.filter((s) => s.accuracy < TARGET)

  return {
    aggregate: {
      label: "aggregate (volume-weighted, do NOT use for automation decisions)",
      total: outcomes.length,
      correct: totalCorrect,
      accuracy: outcomes.length === 0 ? 0 : totalCorrect / outcomes.length,
    },
    segments,
    flagged,
  }
}

// Observable output: aggregate near 0.97 while a cell sits far lower.
// Example constructed from tested figures, not official measurements.
// Standard invoices dominate volume at ~99 percent; international + handwritten sit far lower.
const demo: LabelledOutcome[] = [
  ...Array.from({ length: 80 }, (_, i) => ({
    docId: `inv-std-${i}`, docType: "standard_invoice", field: "invoiceDate",
    predicted: "2024-03-15", truth: "2024-03-15", rawConfidence: 0.96,
  })),
  ...Array.from({ length: 8 }, (_, i) => ({
    docId: `inv-intl-${i}`, docType: "international", field: "invoiceDate",
    predicted: i < 4 ? "2024-03-15" : "2024-15-03", // systematic format confusion
    truth: "2024-03-15", rawConfidence: 0.93,
  })),
  ...Array.from({ length: 12 }, (_, i) => ({
    docId: `rcpt-hw-${i}`, docType: "handwritten_receipt", field: "totalAmount",
    predicted: i < 5 ? "1247.83" : "124.783", // OCR-like digit shift
    truth: "1247.83", rawConfidence: 0.91,
  })),
]

const report = buildAccuracyMatrix(demo)
// report.aggregate.accuracy ~ 0.92 here, but on real heterogeneous traffic it can sit at 0.97
// while report.segments for "international::invoiceDate" and "handwritten_receipt::totalAmount"
// sit near the 45 to 60 percent band from the tested material.

What this block proves is that the headline is a weighted average and therefore not a decision gate. Its failure boundary is sample sufficiency per cell: if a segment has only a handful of labelled examples its measured accuracy is noisy, and that segment must remain under human review until enough labels exist. Its observable output is a sorted matrix where the weakest cells are visible at the top, plus a flagged list that directly drives the routing decision in the next sections.

Mechanism reference: 2. The document type by field matrix as the only valid gate

Validation is performed on a labelled validation set and reported as a matrix of document type crossed with extracted field. Each cell is an independent automation gate: invoices crossed with total amount, receipts crossed with total amount, contracts crossed with governing law, amendments crossed with effective date, and so on. Different types present different layout and noise challenges, and different fields present different semantic difficulty. A date on a typed invoice is easier than the same date on a handwritten amendment; a party name may be well anchored while a termination clause requires synthesis across paragraphs.

Field-level granularity outranks document-level granularity for the same reason. Errors are field-local. A contract can have vendor name extracted correctly with high reliability while governing law is wrong, or an insurance form can have four fields near perfect while coverage type fails systematically. The recurring schema illustration shows vendorName at confidence 0.98, invoiceDate at 0.95, totalAmount at 0.72, and lineItems at 0.61, where a document average near 0.82 would both over-route the strong vendor name and under-route the weak line items. Field-level thresholds keep each decision independent. The boundary is operational: if downstream systems accept or reject a document atomically and cannot act on a subset of fields, document-level routing may be the operational requirement, but measurement must still be field-level to know which fields drive document failure.

Collapsing the matrix prematurely is the shortcut the task punishes. Validating only by document type and assuming fields within a type are uniform fails because field difficulty varies strongly within a type, as the recurring party name versus governing law example shows. Validating only by field and assuming document types are uniform fails because the same field can be easy on clean typed exports and hard on scanned handwritten variants. Either collapse is acceptable only after measurement shows uniformity within tolerance, which is a documented pooling decision after evidence, not an assumption before it.

Anthropic documentation supports the surfaces that populate this matrix. Structured outputs guarantee that every field you intend to score is present with the right type through output_config.format with strict: true or through tool input_schema validation. The lesson on validation strategies places schema checks first and semantic checks after, and the pipeline lesson places business rule and cross-field validation above both. The testing lesson provides the eval shape that produces the labelled set: a curated collection with id, category, subcategory, difficulty, input, expected output, and evaluation criteria, where at least 30 percent of cases should be edge cases or adversarial and evaluation reports must include per-category breakdown. None of this defines the matrix for you; it gives you the reliable fields and the reliable labelled set from which the matrix is computed.

Mechanism reference: 3. Why a raw score is not a probability

Many extraction designs have the model emit a numeric confidence or confidence_score per field, often between 0.0 and 1.0, and then route on that number. Raw scores reflect the model's internal next-token expectations and training-shaped verbosity, not an empirical mapping to accuracy. The confidence-scoring lesson makes the distinction explicit: log probabilities, self-consistency, and hedging detection are objective signals that can be measured outside the model, while self-reported confidence is unreliable and must not drive escalation. The escalation lesson goes further and lists self-reported confidence among the invalid escalation signals alongside sentiment and arbitrary thresholds.

The core misunderstanding is about what the number means. A raw score of 0.95 on a date field and 0.95 on an amount field can imply very different actual accuracies once measured. Figures from the tested material illustrate the gap as 0.90 meaning 94 percent on one field but 82 percent on another, or fields reported at 0.94 to 0.97 that later correct on human review. The reference gives the same shape: when the model reports 0.90 on dates it might be correct 94 percent of the time, while 0.90 on amounts might mean only 82 percent. Fabrication under high self-reported certainty is the sharpest illustration: where evidence is ambiguous, models often produce a plausible value with high self-reported certainty rather than low certainty, so a wrong answer can carry a high number. A separate lesson on confidence scoring documents that overconfidence in wrong answers and underconfidence in correct answers are both common and that prompt phrasing such as adding Are you sure changes the reported number.

No Anthropic API inverts this relationship for you. The Messages API does not expose per-token log probabilities as a request parameter; there is no logprobs or top_logprobs field on a Claude messages request, and the lesson notes this explicitly as a reason production Claude applications must use other objective methods. Tool use with tool_choice can force the model to populate a confidence field so the value is reliably present, and structured outputs can enforce that the field is numeric and bounded, but neither mechanism makes the number calibrated. The structured output guarantee is about shape and presence; meaning correctness remains the application's responsibility. That is why the same raw confidence carries different information depending on context: a highly constrained field such as a formatted date can be correct at lower reported confidence, while a field that requires cross-paragraph synthesis or ambiguous currency interpretation can be wrong even when reported confidence is high.

The boundary that matters operationally is whether a system reports a calibrated probability or a raw score. If a team has explicitly calibrated the score and reports a calibrated probability, the number has been transformed into a trustworthy signal. There is no tested case where trusting a raw number without calibration is acceptable; even when a raw score happens to correlate with accuracy on one field, the calibration gap on another field or document type leaves overconfident errors undetected. This connects directly to Task 5.2. Using a score to prioritise a queue is legitimate once the score is calibrated, because prioritisation is an ordering decision made inside application code on an objective, measured signal. Using a self-reported score to decide to hand a case to a human is not legitimate, because it asks the model to introspect on its own certainty, which it cannot do reliably. The first is measurement outside the model; the second is asking the model how confident it feels.

Mechanism reference: 4. What a labelled validation set is for and how calibration uses it

A labelled validation set, often called a golden dataset in our lessons, is a curated collection of documents paired with known-correct extractions that serves as the only source of truth for turning a self-assessment into a measurable probability. The testing lesson defines the required shape precisely: each test case carries an id, category, subcategory, difficulty tier, input, optional expected output for exact match tasks, evaluation criteria for judge tasks, and tags for filtering, and at least 30 percent of cases should be edge cases or adversarial. The validation strategies lesson places this set after structural validation and before any business decision about thresholds. Without it the team is tuning a threshold on an untrusted scale, which is equivalent to picking a number that feels strict.

Calibration on this set fits a mapping, typically per field per document type, from reported confidence to observed accuracy. The reference describes the process: take documents with known correct extractions, run the model, compare its confidence scores to actual accuracy, and build a calibration curve that tells you that 0.90 on dates means one accuracy and 0.90 on amounts means another. Figures from the tested material on validation set sizes of 200 to 500 labelled extractions and the recurring phrasing fit each field's reported confidence to its measured accuracy on that set are the tested anchors for this step. The mapping is the statistical procedure itself. Anthropic documentation does not publish a calibration procedure or a threshold table, so this step is marked as engineering practice supported by our lessons rather than as an Anthropic rule. What documentation does publish is the pipeline that makes the mapping meaningful: the structured-output surface that ensures the field you score actually exists with the right type, the tool use surface that returns structured tool results you can count as correct or incorrect, and the testing guidance that an eval must have criteria and a golden dataset against which quality is scored.

The example below is the second required substantial example. It implements a per-segment calibration step on a held-out labelled set. It bins reported confidence, computes observed accuracy per bin per segment, and returns a calibration map that can be queried at routing time. It also surfaces the sufficiency rule: if a segment has too few labelled examples its curve is unreliable and that segment must stay under human review until enough labels are collected. The mapping lives in application code, not in the model.

example.ts
typescript
// Calibration step that maps a raw score to an observed accuracy per field per document type
// Engineering practice supported by lessons, not an Anthropic-published procedure.
// Held-out labelled set is never used for prompt tuning; it is reserved for measurement.

type CalibrationBin = {
  binLabel: string       // e.g., "0.85-0.90"
  binFloor: number
  binCeiling: number
  sampledCount: number
  observedAccuracy: number // correct / total in this bin for this segment
}

type SegmentCalibration = {
  docType: string
  field: string
  bins: CalibrationBin[]
  totalSamples: number
  // Monotonic fit is an application choice; isotonic or simple empirical mapping is common.
  // The point is that routing reads this map, not the raw number.
}

function calibratePerSegment(
  outcomes: LabelledOutcome[],
  binEdges: number[] = [0.5, 0.65, 0.75, 0.85, 0.9, 0.95, 1.0],
): { calibrations: SegmentCalibration[]; insufficient: SegmentCalibration[] } {
  const MIN_SAMPLES_PER_SEGMENT = 40 // application policy; insufficient segments stay under review
  const grouped = new Map<string, LabelledOutcome[]>()

  for (const o of outcomes) {
    const key = `${o.docType}::${o.field}`
    const arr = grouped.get(key) ?? []
    arr.push(o)
    grouped.set(key, arr)
  }

  const calibrations: SegmentCalibration[] = []
  const insufficient: SegmentCalibration[] = []

  for (const [key, segmentOutcomes] of grouped) {
    const [docType, field] = key.split("::")
    const totalSamples = segmentOutcomes.length
    const target = totalSamples >= MIN_SAMPLES_PER_SEGMENT ? calibrations : insufficient

    const bins: CalibrationBin[] = []
    for (let i = 0; i < binEdges.length - 1; i++) {
      const floor = binEdges[i]
      const ceiling = binEdges[i + 1]
      const inBin = segmentOutcomes.filter((o) => o.rawConfidence >= floor && o.rawConfidence < ceiling)
      // Include the top edge exactly at 1.0
      const atCeiling = ceiling === 1.0
        ? segmentOutcomes.filter((o) => o.rawConfidence === 1.0)
        : []
      const merged = i === binEdges.length - 2 ? [...inBin, ...atCeiling] : inBin
      const correct = merged.filter((o) => o.predicted.trim() === o.truth.trim()).length
      bins.push({
        binLabel: `${floor.toFixed(2)}-${ceiling.toFixed(2)}`,
        binFloor: floor,
        binCeiling: ceiling,
        sampledCount: merged.length,
        observedAccuracy: merged.length === 0 ? 0 : correct / merged.length,
      })
    }

    target.push({ docType, field, bins, totalSamples })
  }

  return { calibrations, insufficient }
}

function calibratedAccuracyFor(
  calib: SegmentCalibration,
  rawConfidence: number,
): number {
  const bin = calib.bins.find((b) => rawConfidence >= b.binFloor && rawConfidence < b.binCeiling)
    ?? calib.bins[calib.bins.length - 1]
  return bin.observedAccuracy
}

// Observable output: same raw score, different calibrated accuracy.
// Tested pattern: 0.90 on "invoiceDate" may calibrate to ~0.94, on "totalAmount" to ~0.82.
// That difference is why sampling only low-confidence is self-confirming and why one threshold cannot serve both.

What this block proves is that the labelled set is for measurement, not for training. Reusing few-shot examples or unlabelled production volume as a proxy for calibration is the shortcut the task punishes: few-shot examples are not a representative measurement set, and complaint volume measures downstream harm after exposure. Its failure boundary is explicit in the sufficiency gate: a per-segment curve built on too few examples is not trustworthy, and every segment that fails that gate must remain under human review until enough labels are collected. Its observable output is a calibration map per segment plus an insufficient list that directly drives routing conservatism.

Mechanism reference: 5. Why one threshold cannot serve every field and document type

After calibration each segment has its own mapping from reported confidence to observed accuracy, so the threshold that achieves a target error rate on one cell will be wrong on another. Forensics Rule 5 states this as the heterogeneity principle and shows that a global cutoff such as confidence greater than or equal to 0.90 treats 0.90 on standardized bank statements as equivalent to 0.90 on scanned hand-annotated guarantor letters when the two carry different information. The tested material pairs this with figure anchors: global proposals at 0.80, 0.85, 0.90, and 0.95 that leave the weak category unchanged when raised globally, versus calibrated per-segment thresholds that gate each cell independently.

Three distractors recur against this rule and each needs a clear refutation. First, raising the single global cutoff from 0.80 to 0.90 to fix the guarantor-letter segment feels stricter but fails because the weak segment's curve is shifted, so the same global move does not change its overconfident error rate while it unnecessarily taxes the strong segments. Second, tuning per-segment raw cutoffs without calibration, for example a higher raw threshold for scanned receipts and a lower one for typed invoices, acknowledges heterogeneity but pays no calibration cost, so the tuned numbers are still on an untrusted scale and not comparable across segments. Third, using a single document-level score for routing seems simpler but averages over fields with different curves, so a high document score can hide a low-confidence field that carries the real risk; document-level routing is operationally valid only when every field in the document is known to share the same calibration and consequence.

The only permitted pooling is after evidence. If calibration shows that two segments share the same curve within tolerance they can share a threshold, but that is a documented pooling decision after measurement, not a simplification before it. The routing function below encodes this explicitly: it reads a calibrated probability and looks up a per-segment cutoff, and it defaults to review when no calibrated threshold exists for that cell.

This is the third required substantial example, and it also satisfies the brief's requirement for a routing function that reads calibrated probabilities and a per-segment threshold rather than one global cutoff.

example.ts
typescript
// Per-segment calibrated routing, not a single global cutoff
// Each threshold lives in calibrated-probability space and lives per cell.
// Before a mapping exists, the safest default is to keep the cell under review.

type SegmentThreshold = {
  docType: string
  field: string
  calibratedCutoff: number // calibrated probability, not raw score
  targetAccuracy: number   // application policy, e.g., 0.95
}

type RoutingDecision = {
  docType: string
  field: string
  rawConfidence: number
  calibratedProb: number
  threshold: number | null
  action: "auto" | "human_review"
  reason: string
}

function buildThresholdIndex(thresholds: SegmentThreshold[]): Map<string, SegmentThreshold> {
  const idx = new Map<string, SegmentThreshold>()
  for (const t of thresholds) idx.set(`${t.docType}::${t.field}`, t)
  return idx
}

function calibratedProbability(
  rawConfidence: number,
  docType: string,
  field: string,
  calibrations: SegmentCalibration[],
): number {
  const key = `${docType}::${field}`
  const seg = calibrations.find((c) => `${c.docType}::${c.field}` === key)
  if (!seg) return 0 // no calibration yet, treat as fully uncertain
  return calibratedAccuracyFor(seg, rawConfidence)
}

function routeField(
  rawConfidence: number,
  docType: string,
  field: string,
  calibrations: SegmentCalibration[],
  thresholdIndex: Map<string, SegmentThreshold>,
): RoutingDecision {
  const calibratedProb = calibratedProbability(rawConfidence, docType, field, calibrations)
  const entry = thresholdIndex.get(`${docType}::${field}`)

  if (!entry) {
    return {
      docType, field, rawConfidence, calibratedProb,
      threshold: null,
      action: "human_review",
      reason: "no calibrated threshold for this segment; keep under review until independently validated",
    }
  }

  const pass = calibratedProb >= entry.calibratedCutoff
  return {
    docType, field, rawConfidence, calibratedProb,
    threshold: entry.calibratedCutoff,
    action: pass ? "auto" : "human_review",
    reason: pass
      ? `calibrated ${calibratedProb.toFixed(2)} meets per-segment cutoff ${entry.calibratedCutoff.toFixed(2)}`
      : `calibrated ${calibratedProb.toFixed(2)} below per-segment cutoff ${entry.calibratedCutoff.toFixed(2)}`,
  }
}

// Concrete thresholds fitted on labelled data for each segment.
// Values are application-specific illustrations; no Anthropic page publishes this table.
const thresholds: SegmentThreshold[] = [
  { docType: "standard_invoice", field: "invoiceDate", calibratedCutoff: 0.88, targetAccuracy: 0.95 },
  { docType: "standard_invoice", field: "totalAmount", calibratedCutoff: 0.92, targetAccuracy: 0.95 },
  { docType: "scanned_receipt", field: "invoiceDate", calibratedCutoff: 0.94, targetAccuracy: 0.95 },
  { docType: "scanned_receipt", field: "totalAmount", calibratedCutoff: 0.96, targetAccuracy: 0.95 },
  { docType: "international", field: "totalAmount", calibratedCutoff: 0.97, targetAccuracy: 0.95 },
]

const thresholdIndex = buildThresholdIndex(thresholds)

// Observable behaviour: raising a global cutoff from 0.80 to 0.90 trims the population
// but leaves the "international::totalAmount" cell unchanged because its calibrated curve
// is shifted; only moving its own cutoff changes its error rate.

What this block proves is that threshold placement is a per-segment policy decision in calibrated space, not a volume dial. Its failure boundary is the use of a raw global number before calibration, which the task punishes as a tuned raw per-segment cutoff that is still on an untrusted scale. Its observable output is a deterministic routing decision per field that carries its calibrated probability, its matched threshold, and a human-readable reason that appears in the review queue and in audit logs.

Mechanism reference: 4. Field-level granularity outranks document-level

This principle was introduced in the matrix discussion and deserves its own mechanism note because it is a recurring distractor family. An extraction often returns several fields per document, each with its own difficulty. Field-level confidence emits a separate calibrated probability for each extracted value, for example borrower_income, declared_liabilities, collateral_appraised_value, and loan_to_value each with its own score. A single document score averages over fields with different curves, so a high document score can hide a low-confidence field that carries the real risk. The tested audit framing where liability_cap fails in 40 percent of contracts while all other fields exceed 95 percent, or an insurance form where coverage_type alone fails, cannot be caught at document granularity.

Document-level routing is not forbidden; it is operationally required when downstream systems accept or reject a document atomically. The correct discipline is to measure at field level and then decide whether the document can be accepted as a whole. The testing lesson's per-category breakdown is the reporting analogue: the aggregate 86 percent masks the 55 percent multi-step reasoning category, and only the stratified view makes the decision to block shipment visible.

Mechanism reference: 5. The self-confirming trap of sampling only low-confidence

A common draft policy routes every extraction below a confidence threshold to human review and automates everything above it, then measures quality only on the routed low-confidence set. Because the high-confidence set is never inspected against ground truth, its error rate is unknown and its error patterns are invisible. This is self-confirming: you measure the population you already distrust and ignore the population you have decided to trust.

The reference names the critical insight in plain language: you must sample high-confidence extractions because low-confidence items are already routed to review, so only stratified sampling will catch a novel error pattern that affects the automated band. The forensics analysis makes the operational distinction explicit: routing and measurement serve different purposes. Routing directs scarce review to where errors are most likely; measurement estimates true risk in the population that actually reaches downstream systems. Measuring only the routed population estimates the wrong population's error and cannot detect confident errors. Relying on downstream complaints or reconciliation as the signal is lagging, sparse, and biased toward harm that is visible downstream, not toward the full error distribution.

The boundary that matters is timing. Before any automation has been enabled, inspecting only low-confidence cases is a reasonable first allocation of limited review while everything still passes through a human. After auto-approval is live, the unreviewed population must be sampled, because the risk that matters is the risk in the automated tier. The monitoring lesson reinforces the same point for production systems generally: error rates must be tracked by category, and stratified sampling of high-confidence extractions is the safeguard for the automated tier as document formats drift.

Mechanism reference: 6. Stratified random sampling of the automated tier

Stratified random sampling is the ongoing assurance mechanism for high-confidence extractions. It has two purposes: estimating error rate on the automated tier with quantifiable uncertainty, and discovering novel failure modes that did not exist in the original validation set. The phrase novel pattern detection recurs alongside measurement because sampling is agnostic to pattern: it inspects a high-confidence slice grouped by the dimensions that define heterogeneity and computes accuracy on the human-labelled sample, not on a proxy.

Stratification dimensions that recur are document type, field, and confidence band such as 90 to 95 percent, 95 to 99 percent, and 99 percent plus, with sampling described as weekly, quarterly audit, and ongoing verification versus one-time validation. The confidence-scoring lesson names the same mechanism as the ongoing measurement technique for the automated population: periodically draw a random, stratified sample of high-confidence extractions that are running unattended and check them against ground truth. The testing lesson calls this the reporting layer that aggregates results into actionable reports with per-category pass rates, blocker thresholds, and regression detection.

The example below is the fourth required substantial example. It implements a stratified sampler that draws proportionally to segment volume and deliberately includes the high-confidence automated band. The population of interest is the sampled high-confidence slice grouped by the dimensions that define heterogeneity, and accuracy is computed on the human-labelled sample. A SQL illustration from the forensics analysis makes the same query structure explicit.

example.ts
typescript
// Stratified sampler that draws proportionally to segment volume and includes the automated band
// Proves: only sampling the automated tier can measure its true error and discover novel patterns.

type Extraction = {
  id: string
  docType: string
  field: string
  calibratedProb: number   // routing reads this, not the raw score
  rawConfidence: number
  band: "low" | "mid" | "high" | "very_high" // e.g., low <0.70, mid 0.70-0.85, high 0.85-0.95, very_high 0.95+
  predicted: string
}

type SampleConfig = {
  samplingFraction: number      // e.g., 0.05 = 5 percent of the automated tier
  includeBands: Extraction["band"][] // must include "high" and "very_high", the automated bands
  perStratumMinimum: number     // floor, see next section
  perStratumMaximum?: number    // cap for very high volume strata
}

function confidenceBand(calibratedProb: number): Extraction["band"] {
  if (calibratedProb < 0.70) return "low"
  if (calibratedProb < 0.85) return "mid"
  if (calibratedProb < 0.95) return "high"
  return "very_high"
}

function stratifiedSample(
  population: Extraction[],
  config: SampleConfig,
): { sample: Extraction[]; strata: Array<{ key: string; population: number; sampled: number }> } {
  // Stratify by docType x field x band, the dimensions that define heterogeneity
  const byStratum = new Map<string, Extraction[]>()
  for (const e of population) {
    if (!config.includeBands.includes(e.band)) continue // deliberately include high bands, never skip them
    const key = `${e.docType}::${e.field}::${e.band}`
    const arr = byStratum.get(key) ?? []
    arr.push(e)
    byStratum.set(key, arr)
  }

  const sample: Extraction[] = []
  const strata: Array<{ key: string; population: number; sampled: number }> = []

  for (const [key, members] of byStratum) {
    const populationSize = members.length
    // Proportional allocation, then apply per-stratum minimum so rare strata are still measured
    const proportional = Math.ceil(populationSize * config.samplingFraction)
    const floor = config.perStratumMinimum
    const cap = config.perStratumMaximum ?? Number.POSITIVE_INFINITY
    const target = Math.max(floor, Math.min(cap, proportional))

    // Shuffle via Fisher-Yates, then take target; deterministic seed in production
    const shuffled = [...members]
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1))
      ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
    }

    const taken = shuffled.slice(0, Math.min(target, shuffled.length))
    sample.push(...taken)
    strata.push({ key, population: populationSize, sampled: taken.length })
  }

  return { sample, strata }
}

// Example: 5 percent of the automated tier, stratified, with a floor of 10 per stratum.
// Without that floor the 70 vs 7.5 volume skew in the tested material undersamples rare types.
const automatedPopulation: Extraction[] = [] // populated from production throughput; all members have calibratedProb >= cutoff
const samplerConfig: SampleConfig = {
  samplingFraction: 0.05,
  includeBands: ["high", "very_high"], // the automated tier; routing excluded "low" and "mid" already
  perStratumMinimum: 10,
}

const { sample, strata } = stratifiedSample(automatedPopulation, samplerConfig)
// Human reviewers label `sample` against ground truth; accuracy per stratum is then
// sampled correct / sampled total, which estimates the automated tier's true error.
output.txt
text
-- Illustrative stratification query for ongoing measurement on the human-labelled sample
-- Assumes an extraction_sample table where is_correct comes from human labelling
SELECT document_type, field_name, confidence_band,
       COUNT(*) AS population,
       AVG(CASE WHEN is_correct THEN 1.0 ELSE 0.0 END) AS sampled_accuracy
FROM extraction_sample
WHERE sample_type = 'stratified_high_confidence'
GROUP BY document_type, field_name, confidence_band
ORDER BY sampled_accuracy ASC;

What this sampling proves is that estimation of the automated tier's error must be done on that tier, not inferred from the low-confidence tier or from complaint volume. Its failure boundary is sampling intensity on low-volume strata, which the next section addresses. Its observable output is a sampled error rate per stratum plus a stream of novel patterns from reviewers who record the underlying detected pattern, not just pass or fail, which is how comparison tables, appendices, footnote misattributions, and vendor-specific layouts surface as new failure modes.

Mechanism reference: 7. Why volume-proportional alone undersamples rare strata without a floor

When daily volume is highly skewed, a sampling plan that allocates samples strictly in proportion to volume assigns most samples to the dominant type and leaves rare types with too few labelled examples to estimate their error rate. The teaching example that recurs is one type at 70 percent of daily volume and four others at 7.5 percent each, where pure proportional sampling undersamples the four low-volume types. A 5 percent overall sample on a type that is only 5 percent of traffic yields 0.25 percent effective coverage, often fewer than a dozen examples per week and insufficient to distinguish a meaningful error rate.

Statistical power depends on absolute sample count per cell, not just share of volume. Proportionality is optimal for estimating the population average but not for estimating each stratum's rate, especially rare high-risk ones. Excluding the high-volume type entirely to focus on small types is also wrong: it removes measurement from the segment that still carries the most absolute error count even at a low error rate. The fix is to combine proportional allocation with a per-stratum minimum regardless of share of volume. When a rare type is both low-volume and low-consequence the minimum can be smaller but not zero, because the segment still needs some measurement to claim it is safe. A risk-tiered variant makes the same point from the consequence angle: payment processing at 30 endpoints may require 100 percent review, public data queries at moderate risk are sampled lightly, and internal tooling at low risk is sampled lightest, which illustrates census on the highest-risk slice rather than proportional alone.

The sampler in the previous section already encodes this with perStratumMinimum. A pure proportional plan without that floor passes the population average check and fails the per-stratum gate, which is exactly the trap the aggregate discussion opened with.

Mechanism reference: 8. Discovery of novel failure modes as a separate benefit

Beyond estimating error rate, sampling across high-confidence strata discovers failure modes that did not exist in the original validation set. Reviewers inspecting samples from comparison tables, footnotes, appendices, or newly introduced two-column legal formats encounter plausible but incorrect values that no prior heuristic flagged. Systematic errors from a new layout or phrasing are deterministic: the model reproduces the same confident misreading every time, so re-extraction or heuristic flags keyed to known patterns miss them while sampling is agnostic to pattern and therefore has discovery power that retrospective heuristics lack.

Three distractors are tested against this property and each reveals why a heuristic substitute fails. Lowering the confidence threshold from 0.85 to 0.70 to catch more high-confidence errors widens the review net in the wrong direction: the errors are inside the greater than 0.85 bucket by definition, so expanding the below 0.85 tier pulls in a different population and does not touch the confidently wrong cases. Adding a verification pass that re-extracts from each high-confidence document and flags disagreement seems independent, but systematic layout-driven errors are deterministic and reproduce identically, so agreement is not correctness. Implementing heuristic rules that flag documents containing comparison tables or appendices targets only yesterday's known patterns and cannot learn a new footer or column format; it covers knowledge at the time of rule authorship, while sampling learns whatever appears next. The correct heuristic role is as a temporary targeted filter after a pattern has been discovered via sampling, not as the discovery mechanism itself.

The confidence-scoring lesson's note on silent drift reinforces this: aggregate accuracy on the validation set does not catch new patterns, and confidence scores can stay high while actual accuracy drifts as vendors change templates or input populations shift. Sampling is the external validation applied continuously to the automated tier that catches that drift.

Mechanism reference: 9. Ordering the review queue by uncertainty and reordering on arrival

Human reviewers are expensive and limited, so the allocation question is not whether to use reviewers but how to spend their budget to maximise error catch per review. Routing the highest-uncertainty items first means prioritising low calibrated confidence, extractions from ambiguous or contradictory source documents, document types with historically poor accuracy, and fields where the model expresses multiple possible interpretations. Even distribution across all extractions wastes time on high-confidence items the model handles well while leaving insufficient capacity for uncertain items.

The queue ordering must be dynamic, not static. As the system processes documents the queue of items awaiting review should be ordered by uncertainty, specifically by calibrated confidence rather than raw score. When a reviewer finishes one item the next item in the queue should be the highest-uncertainty item remaining, not the next in chronological order. This is a policy that routing and queue management share: routing decides which items enter the queue at all, and prioritisation decides the order within the queue.

A frequent distractor is to treat the confidence threshold as a volume dial to match reviewer capacity. A pipeline flags fields below 0.70 and finds 35 percent flagged, far above a 5 percent review capacity. The backwards fix is to lower the threshold to 0.30 until only about 5 percent are flagged, hitting the volume target by redefining what counts as risky. This meets a staffing target while letting through the cases that genuinely needed review, which then surface as downstream errors; thresholds follow quality improvement, not capacity pressure. The legitimate counterpart is capacity triage after quality gates are fixed: once thresholds reflect true quality, the team may still have more flagged items than reviewers can handle and must prioritise within the flagged set by calibrated uncertainty, add capacity, or improve the extractor so fewer items are flagged. The build exercise material later in this document implements the dynamic queue explicitly so a reviewer never sees chronological order.

The example below is the fifth required substantial example. It is a review queue ordered by uncertainty that reorders as new items arrive. It uses calibrated probabilities, not raw scores, and it carries the routing context that tells a reviewer why an item was prioritised. The second illustration attaches the queue to reviewer dispatch so the next available reviewer always receives the most uncertain item.

example.ts
typescript
// Review queue ordered by uncertainty that reorders as new items arrive
// Priority is calibrated uncertainty, not arrival time or raw score.

type QueuedExtraction = {
  id: string
  docType: string
  field: string
  calibratedProb: number   // lower means more uncertain, higher priority
  rawConfidence: number
  predicted: string
  sourceExcerpt?: string
  reason: string           // routing reason, e.g., "calibrated 0.62 below cutoff 0.88" or "ambiguous source"
  enqueuedAt: number
}

class UncertaintyQueue {
  private heap: QueuedExtraction[] = []

  // Lower calibratedProb sorts first; tie-break by earlier enqueue for determinism
  private priority(a: QueuedExtraction, b: QueuedExtraction): number {
    if (a.calibratedProb !== b.calibratedProb) return a.calibratedProb - b.calibratedProb
    return a.enqueuedAt - b.enqueuedAt
  }

  enqueue(item: QueuedExtraction): void {
    this.heap.push(item)
    this.heap.sort((a, b) => this.priority(a, b))
  }

  enqueueMany(items: QueuedExtraction[]): void {
    for (const item of items) this.heap.push(item)
    this.heap.sort((a, b) => this.priority(a, b))
  }

  peek(): QueuedExtraction | undefined {
    return this.heap[0]
  }

  dequeue(): QueuedExtraction | undefined {
    return this.heap.shift()
  }

  // Reorder on arrival is the default: every enqueue sorts.
  // Explicit resort is useful after a calibration refresh changes priorities.
  resort(): void {
    this.heap.sort((a, b) => this.priority(a, b))
  }

  size(): number {
    return this.heap.length
  }

  snapshot(): { orderedIds: string[]; calibratedProbs: number[] } {
    return {
      orderedIds: this.heap.map((h) => h.id),
      calibratedProbs: this.heap.map((h) => Number(h.calibratedProb.toFixed(2))),
    }
  }
}

// Dispatch loop: each available reviewer receives the highest-uncertainty item, not the oldest.

async function dispatchToReviewers(
  queue: UncertaintyQueue,
  reviewerPool: Array<{ reviewerId: string; isAvailable: () => boolean }>,
  claim: (reviewerId: string, item: QueuedExtraction) => Promise<void>,
): Promise<void> {
  for (const reviewer of reviewerPool) {
    if (!reviewer.isAvailable()) continue
    const next = queue.dequeue()
    if (!next) break
    await claim(reviewer.reviewerId, next)
  }
}

// Observable behaviour: enqueue three items at calibrated 0.91, 0.63, 0.84;
// dequeue order is 0.63, 0.84, 0.91 regardless of arrival order.
// Enqueue a new item at 0.58; next dequeue is 0.58, not the previous head of queue.

What this block proves is that prioritisation is an ordering problem that calibration enables. Its failure boundary is raw-score ordering: when the queue is sorted by raw confidence the confident but wrong segment remains at the back of the queue while easy correct cases sit at the front. Its observable output is a heap whose snapshot orders calibrated probabilities ascending and whose dispatch loop can be logged alongside sampled error rates to show reviewer capacity spent where genuine uncertainty was highest.

Mechanism reference: 10. The required order measure, calibrate, threshold, then automate

The validated sequence is measure accuracy by document type and field on a labelled set, then calibrate per-segment confidence thresholds against that set, then implement routing that sends low calibrated confidence and not yet validated segments to review, then enable stratified sampling for ongoing verification, and only then reduce human review on segments whose sampled accuracy demonstrates consistent validated accuracy. Each shortcut creates a distinct blind spot: automating on the aggregate skips segmentation and hides the weak cell; skipping calibration leaves the team tuning a threshold on an untrusted scale; skipping sampling leaves the automated tier unmeasured; inverting the last two steps measures after exposure so segmented failures propagate before detection.

Boundary behaviour is emergency risk response. When clinicians report critical errors on a small segment such as allergy fields on handwritten forms, the immediate action is to isolate that segment and route all of it to mandatory review before measurement completes, a temporary inversion justified by safety.

Three distractors are tested against the sequence. A two-week pilot routing a quarter of high-confidence extractions downstream measures after exposure so failures propagate. Comparing thresholds before segmenting optimises on the aggregate and can still automate a weak cell. Verifying that a 97 percent headline meets downstream requirements validates the wrong number without confirming each feed actually experiences that rate.

The testing and validation lessons place supportive gates around the same sequence. The testing lesson's four-component eval design plus regression detection that flags per-category regressions keeps the sequence honest after it is first run. The validation pipelines lesson's multi-stage checks with retry-with-fix loops capped at two to three attempts is the extraction-internal sequence that produces the fields the calibration step later scores. Confidence-scoring lessons frame the zones 0 to 40 percent escalate, 40 to 70 percent verify, 70 to 90 percent caveat, 90 to 100 percent auto-accept as per-application policy choices.

Mechanism reference: 11. Drift detection and refusing to widen automation when a segment regresses

Once a high-confidence tier is automated, the only assurance that it remains accurate is continuous re-measurement. Document formats drift as vendors change templates, input populations shift, and new filing types appear, and confidence scores can stay high while actual accuracy silently regresses. The ongoing measurement technique is stratified random sampling of the automated population performed periodically and checked against ground truth; the sampled error rate and the novel error patterns it surfaces are what justify the current automation threshold or tighten it again. Treat a second model pass or a higher headline threshold as complementary controls at best: agreement between passes is not correctness when the error is deterministic, and a higher headline still averages over heterogeneity.

The drift check is the gate that prevents silent regression from becoming silent expansion. It recomputes calibration on recent labelled outcomes drawn from the stratified sample and refuses to widen automation when a segment's re-measured accuracy falls below target. Figures from the tested material anchor the stakes: a single vendor layout with 40 percent error on a single field can hide inside a 97 percent headline and surface only through sampling.

The example below is the sixth required substantial example. It recomputes calibration on recent labelled outcomes and blocks any proposal to widen automation when a segment regresses. It also enforces that thresholds are never treated as a volume dial: the drift check refuses to lower a cutoff to meet capacity unless quality has genuinely improved.

example.ts
typescript
// Drift check that recomputes calibration on recent labelled outcomes
// and refuses to widen automation when a segment regresses.

type DriftAssessment = {
  docType: string
  field: string
  baselineAccuracy: number   // calibrated accuracy at last validation
  recentAccuracy: number     // calibrated accuracy on recent stratified sample
  delta: number              // recent minus baseline; negative is regression
  sampleSize: number
  recommendation: "maintain_review" | "tighten_threshold" | "allow_widening"
  wideningBlocked: boolean
}

type DriftConfig = {
  minRecentSamples: number   // insufficient recent labels, keep under review
  regressionTolerance: number // e.g., 0.02 = tolerate 2 points of noise before blocking
}

function assessDrift(
  recentOutcomes: LabelledOutcome[],
  baselineCalibrations: SegmentCalibration[],
  config: DriftConfig,
): DriftAssessment[] {
  const { calibrations: recentCalibs, insufficient } = calibratePerSegment(recentOutcomes)

  // Segments with too few recent labels are not eligible for widening
  const blockedKeys = new Set(insufficient.map((s) => `${s.docType}::${s.field}`))

  const assessments: DriftAssessment[] = []

  for (const recent of recentCalibs) {
    const key = `${recent.docType}::${recent.field}`
    const baseline = baselineCalibrations.find((b) => `${b.docType}::${b.field}` === key)
    if (!baseline) continue // no baseline yet, cannot widen; keep under review

    // Use the top-confidence bin as the automated-tier proxy
    const topBin = (bins: CalibrationBin[]) => bins[bins.length - 1]
    const recentAccuracy = topBin(recent.bins).observedAccuracy
    const baselineAccuracy = topBin(baseline.bins).observedAccuracy
    const delta = recentAccuracy - baselineAccuracy
    const sampleSize = recent.totalSamples

    const isRegression = delta < -config.regressionTolerance
    const tooFew = blockedKeys.has(key) || sampleSize < config.minRecentSamples

    let recommendation: DriftAssessment["recommendation"]
    let wideningBlocked: boolean

    if (tooFew) {
      recommendation = "maintain_review"
      wideningBlocked = true
    } else if (isRegression) {
      recommendation = "tighten_threshold"
      wideningBlocked = true
    } else {
      recommendation = "allow_widening"
      wideningBlocked = false
    }

    assessments.push({
      docType: recent.docType,
      field: recent.field,
      baselineAccuracy,
      recentAccuracy,
      delta,
      sampleSize,
      recommendation,
      wideningBlocked,
    })
  }

  return assessments.sort((a, b) => a.delta - b.delta) // most regressed first
}

function gateWideningProposal(
  proposal: { docType: string; field: string; proposedLowerCutoff: number; currentCutoff: number },
  drift: DriftAssessment[],
): { allowed: boolean; reason: string } {
  const segment = drift.find((d) => d.docType === proposal.docType && d.field === proposal.field)
  if (!segment) {
    return { allowed: false, reason: "no recent drift assessment for this segment; keep under review" }
  }
  if (segment.wideningBlocked) {
    return {
      allowed: false,
      reason: `widening blocked: ${segment.recommendation} (baseline ${segment.baselineAccuracy.toFixed(2)} -> recent ${segment.recentAccuracy.toFixed(2)}, delta ${segment.delta.toFixed(2)})`,
    }
  }
  // Even when allowed, the new cutoff must be justified by measured quality, not capacity
  return {
    allowed: true,
    reason: `recent ${segment.recentAccuracy.toFixed(2)} within tolerance of baseline ${segment.baselineAccuracy.toFixed(2)}; lower cutoff from ${proposal.currentCutoff.toFixed(2)} to ${proposal.proposedLowerCutoff.toFixed(2)} is gated on this measurement, not on reviewer headcount`,
  }
}

// Observable output: a team at 5 percent capacity that proposes moving the cutoff from 0.70 to 0.30
// to hit a volume target is refused because the drift check shows the risky 30 percent of flagged
// fields would be auto-approved while measured accuracy on a regressed segment is still below target.
// The check forces an alternative: add capacity, improve the extractor, or accept that widening is not yet safe.

What this block proves is that calibration is not permanent. Validation data ages as document formats drift and as new types enter, so ongoing sampling is the only drift detector. Its failure boundary is using the original labelled validation set once and treating calibration as permanently valid, or waiting for customer complaints as the primary signal, both of which are sparse, delayed, and biased toward harm that is visible downstream. Its observable output is a sorted drift assessment where the most regressed segments appear first and any widening proposal for those segments is refused with an explicit reason that cites the re-measured accuracy.

Mechanism reference: 12. Validation pipeline, schema-valid versus semantically correct, and bounded retry

Structured outputs and validation pipelines sit underneath the confidence layer: the fields you score must first be present and shaped correctly, and the pipeline must decide when a validation failure is worth a retry versus when it must be routed. Anthropic documentation guarantees structure, not meaning: a JSON schema or tool input_schema with strict: true ensures required fields are present with valid types and enum values. The validation strategies lesson calls this out explicitly, structure is guaranteed, meaning is not. The pipeline lesson then layers five stages, schema, format, semantic, cross-field, and external validation, each with its own check and with the understanding that each stage gates the next.

A validation retry loop that feeds the specific validation error back to the model can correct a correctable format issue such as a date returned as 03/04/2025 when the schema requires 2024-03-04, or a confidence field returned as the string high instead of a float between 0.0 and 1.0. The pipeline lesson shows the same pattern with Zod and with a retry loop that appends assistant and user turns carrying the validation errors. Forensics Rules 14 and 15 add the boundaries: retry fixes format faults but never absent information or genuine source inconsistency, and every retry must be bounded, typically at two to three attempts, after which the item is flagged for human review with the original source preserved. The validation strategies lesson agrees on the ceiling, three retries is the recommended ceiling before the application must fall back gracefully. Retrying indefinitely, expanding the error message to list every null field, or forcing tool choice to guarantee a populated field all ask the model to produce a fact that does not exist, and the tested material marks each as the wrong move.

A second boundary is that schema-valid does not mean semantically correct. A fabricated or misread value can satisfy every type check while still being the wrong fact, and the examples in the tested material are engineered to pass validation, such as 30 minutes placed in an ingredient quantity field or line items that do not sum to the stated total, or tiger versus cat labels on drillthrough charts. Rates such as 12 percent of extractions with semantic errors that pass validation are figures from the tested material. This connects back to confidence: when semantic correctness fails while schema validation passes, calibrated field-level confidence is the signal that allocates limited reviewer capacity to the right 20 percent of extractions.

Ownership map

Which layer owns which guarantee when the pipeline is built from model, API, SDK, application code, and infrastructure. The distinction matters because a documented API guarantee is not improved by prompt wording, and an engineering practice is not validated by the existence of a model feature.

Model. The model owns the quality of the generated value and any raw confidence number it emits per field. Those numbers reflect training-shaped expectations, not calibrated probabilities, and calibration varies with field difficulty, document noise, and prompt phrasing. Where evidence is ambiguous the model often produces a plausible value with high self-reported certainty rather than low certainty. Side effects of the extraction itself, such as a numeric total rendered with an unintended precision shift, are also model-owned in the sense that the model produced them; whether they are visible depends on validation owned elsewhere.

API and tool contract. The API owns that a field you score is present with the correct type. Structured outputs via output_config.format with strict: true enforce that required fields are present and that enum values and numeric ranges are respected, and tool use via input_schema returns a structured tool result that the API has validated as shape-correct. The API also owns token accounting that keeps context sizing honest: every request counts system prompt, every message including tool results, images, documents, tool definitions, and output including extended thinking toward the window, with usage reporting the split across input_tokens, cache_read_input_tokens, and cache_creation_input_tokens. What the API does not own is calibration, threshold placement, or any guarantee that a high reported confidence implies high correctness.

Application code. Application code owns every mechanism this task tests: the labelled validation set, the per-segment calibration map, the per-segment thresholds, the stratified sampling of the automated tier, the drift check that refuses to widen automation on regression, and the review queue ordered by calibrated uncertainty. It also owns the validation pipeline that sits below calibration, including schema checks via Zod or Pydantic, semantic and cross-field checks such as recomputing loan-to-value, and the retry loop that feeds the error back for at most two to three attempts before routing to a human. Graduated confidence filters such as high at 0.9, medium 0.7 to 0.9, low 0.5 to 0.7, and critical below 0.5 are also application-level policy choices.

SDK. The SDK surfaces the same guarantees with language-specific convenience: schema validation libraries integrate with retry logic, and streaming validation can reject clearly invalid output early without waiting for the full response. No SDK implements per-segment calibration or stratified sampling as a built-in primitive; those remain patterns application code implements and tests.

Infrastructure and operations. Infrastructure owns the observability loop that keeps the pipeline honest: metrics for latency, cost, error rate, and token usage; dashboards on real-time, daily, and weekly tiers; alerting such as error rate greater than 1 percent warning and 5 percent critical; and CI gates that block deploys on per-category regressions. Knowledge base management, prompt versioning, retrieval monitoring, and index health also live here.

The misassignment that most often causes harm is attributing calibration to the model or threshold tuning to the API. When a team asks the model are you sure, the prompt sensitivity of self-reported confidence changes the number without changing the underlying accuracy, and the routing decision becomes unstable. When a team treats a structured output guarantee as a correctness guarantee, schema-valid fabrications pass silently into downstream systems.

Version and terminology currency

Human review and confidence calibration have no versioned protocol change to track in the way context window management does. The currency note that matters is where the surrounding surfaces were documented and have since been named more precisely.

Structured outputs are generally available with strict: true and no beta header required, as documented in the structured outputs page. The older phrasing in some community material that treats structured outputs as beta or as a prompt trick is stale; the current surface is an API-level guarantee about shape, not a suggestion to the model.

Tool use remains the same split: tool definitions via input_schema and streaming tool_use blocks are the enforcement point, with tool_choice controlling whether the model must call a tool. No Anthropic page has added a per-token log probability parameter to the Messages API, and the confidence-scoring lesson explicitly notes that the Anthropic Messages API does not expose logprobs or top_logprobs. Any production Claude application that references those parameters as if they were native to the Claude API is using stale terminology from another provider.

The testing and evaluation surface is documented under test-and-evaluate, including the guidance to define success and build evaluations against golden datasets. The older label evals is still used conversationally but the documented path is now test-and-evaluate, with adjacent pages on hallucination reduction and evaluation tooling that the confidence and testing lessons cite.

The phrase calibrated confidence itself remains application terminology, not an Anthropic field name. No API field is documented as calibrated_confidence or a threshold table published per document type. What is documented is the substrate that makes calibration possible: reliable fields produced by structured outputs and tool use, and a testing method that expects labelled expected outputs for scoring. The calibration step is therefore current engineering practice whose inputs are documented, not a recently versioned API.

Official versus community divergence

Community material on this task is usually directionally correct but often imprecise about what is measured and what is inferred, which creates five divergences worth naming explicitly.

Divergence 1: Headline accuracy. Community or leadership summaries present a single headline figure such as 95 to 97 percent overall as the readiness signal and propose retiring review on that strength. Anthropic documentation describes output quality degradation as monotonic context rot with no published percentage, and the testing lesson shows stratified reporting where the aggregate 86 percent hides a 55 percent category that should block shipment. Documentation wins: a candidate should answer with per-segment numbers, not a headline, because averaging over heterogeneity destroys signal.

Divergence 2: Calibration as instruction. Community snippets sometimes describe calibration as add to the system prompt only auto-approve when highly confident. Anthropic documentation does not publish a calibration prompt and lessons mark self-reported confidence as an invalid routing signal. Documentation wins: calibration is a measured mapping on a labelled set, not an appended instruction.

Divergence 3: Global threshold placement. Community proposals frequently propose a single global cutoff such as 0.85 or 0.90, often paired with raise the threshold to fix the weak segment. Forensics Rules 5 and 6 document why this fails, and lessons frame thresholds as per-application policy decisions tied to calibrated probabilities, not as universal constants. Documentation wins: thresholds must be per segment and in calibrated space.

Divergence 4: Sampling scope. Community sampling advice often narrows to review only low-confidence outputs as efficient allocation. Forensics Rules 7 and 8 document that this is self-confirming and leaves the automated population unmeasured. The confidence-scoring lesson names stratified sampling of the high-confidence automated tier as the ongoing assurance technique. Documentation and lesson guidance wins: the high-confidence band must be sampled.

Divergence 5: Complaint volume as detection. Community retrospectives sometimes present downstream complaints or reconciliation as the primary signal for high-confidence errors. Forensics analysis documents that complaints are lagging, sparse, and biased toward harm that is visible downstream, not toward the full error distribution. Community position loses here; sampling is the primary measurement and complaints are a secondary detective control.

In every conflict the candidate should answer with the documented or lesson-supported position and note why the community shortcut is attractive: it halves matrix work, feels stricter, or avoids labelling cost, which is exactly why it is offered as a distractor.

Beyond the task statement

The lessons that surround this task contain adjacent mechanisms the reference page omits entirely. Each is worth understanding because it strengthens isolation, sharpens measurement, or deepens calibration even when the exam question does not mention it by name.

Validation pipelines that produce the fields you score. The task assumes every field you calibrate is already present and typed. The validation pipelines lesson makes that true, with a five-stage pipeline of schema, format, semantic, cross-field, and external validation, Zod or Pydantic for structural checks, and a retry-with-fix loop. Slug: validation-pipelines. Why it matters: without it calibration scores missing or mistyped fields.

Validation strategies that bound retry correctly. The validation strategies lesson sets the ceiling at three retries and distinguishes futile retry detection from transient faults. Slug: validation-strategies. Why it matters: every extra retry on a genuinely absent field burns cost while never changing the underlying absence.

Testing foundations that define the labelled set. The testing lesson defines eval design, golden datasets, red-teaming, and stratified metrics with blocker thresholds per category. Slug: testing-ai-systems. Why it matters: the labelled set must be versioned and balanced with at least 30 percent edge cases; otherwise the matrix is noise.

Escalation patterns that decide what leaves the agent at all. The escalation patterns lesson defines when handoff is required versus retry or graceful failure, and lists invalid signals that must never drive escalation. Slug: escalation-patterns. Why it matters: a field with a genuinely absent source should fail gracefully or escalate with context, not retry.

Confidence scoring that separates queue prioritisation from escalation. The confidence-scoring lesson makes the line plain: self-reported confidence is never the gatekeeper, while calibrated probabilities can prioritise a queue. Slug: confidence-scoring. Why it matters: conflating the two turns a legitimate optimisation into an illegitimate decision.

Context evaluation and governance that keep measurement honest. The context evaluation and governance lesson provides the loop of evaluation, observability, governance, and operations that measures quality, traces what the model saw, and keeps bases versioned. Slug: context-evaluation-governance. Why it matters: calibration ages as formats drift, and only a loop that flags per-category regressions prevents silent decay.

Monitoring that makes drift visible. The monitoring lesson defines the four dimensions to track and the three-tier dashboard and alerting thresholds. Slug: monitoring. Why it matters: without it the drift check has no time series to act on.

Worked production examples

Each example carries values from the tested patterns and shows the reasoning chain and the failure mode avoided. Figures that appear as percentages are from the tested material, not official published measurements.

Worked production examples: Example 1: An invoice pipeline at 97 percent that must not automate everything

A structured extraction system processes invoices from an accounts payable flow. Daily volume is skewed: standard typed invoices are 80 percent of traffic, handwritten receipts from field staff are 8 percent, scanned PDFs 7 percent, and international documents 5 percent. The pipeline emits per-field confidence and is validated on a labelled set.

The dashboard reports 97.0 percent aggregate accuracy on dates and 96.1 percent on amounts. Leadership proposes automating all extractions where reported confidence exceeds 0.90. Measured per segment, standard invoices sit at 99.5 percent on dates, handwritten receipts at 60.1 percent, scanned PDFs at 72.4 percent, and international formats at 45.2 percent. A rule keyed to the headline silently routes the three weak types into the automated tier while the strong majority keeps the average high.

The correct sequence refuses the proposal and runs the matrix from the first code example. That matrix sorts each cell by observed accuracy and flags every cell below the 0.95 target. Standard invoices clear the gate for both dates and amounts and become eligible for calibrated routing. The three weak types remain under review regardless of raw confidence, because no mapping yet justifies a threshold for those cells.

Calibration then sharpens routing within eligible cells. On standard invoices the map shows raw 0.90 on dates at about 0.94 observed accuracy while the same 0.90 on amounts is about 0.82. Per-segment thresholds are therefore set separately, for illustration at 0.88 for dates and 0.92 for amounts, both in calibrated space. A global raise from 0.80 to 0.90 would have trimmed the population but left the weak segment unchanged, the tested distractor against global movement.

Sampling and drift then sustain the decision. Five percent of the automated high-confidence band is sampled proportionally with a floor of 10 per stratum so the rare international type is not undersampled. Weekly recomputation on recent labelled outcomes blocks any widening if international formats regress; the drift assessment surfaces the delta and the gate refuses the change. This mirrors the teaching example where a 70 versus 7.5 split undersamples four types without a minimum.

Worked production examples: Example 2: Field-level versus document-level scoring in a lending platform

A lending platform extracts four fields from four document classes: standardized bank statements at 55 percent of volume, CPA-prepared statements at 25 percent, broker rent rolls at 12 percent, and scanned hand-annotated guarantor letters at 8 percent. Each extraction returns a confidence_score, and a validation step recomputes loan-to-value to flag mismatches. Aggregate accuracy reached 97 percent last quarter, clearing the internal bar, and operations proposes retiring the review queue. A spot-check finds two mispriced loans traced to guarantor-letter fields reached funding, and raising the global cutoff from 0.80 to 0.90 did not change that error rate.

The validation recomputation strengthens internal consistency but cannot catch self-consistent but wrong extractions where both income and collateral are misread yet satisfy the arithmetic. Strengthening validation and adding a conflict_detected flag is useful, but routing only failures of that check and removing review for all consistent ones leaves confidently wrong cases unattended, and uniform 5 percent sampling regardless of type understates the rare class that carries concentrated risk.

The fix is the same matrix and calibration discipline, now crossed by field. Each cell has its own calibrated curve, and only cells that clear the target after calibration become eligible. Thresholds live in calibrated space, so 0.90 on a standardized statement is not the same decision as 0.90 on a guarantor letter. A document where borrower_income is strong at 0.96 but collateral_appraised_value is weak at 0.71 is not auto-released as a whole; each field is routed independently.

Reviewer prioritisation then makes the remaining queue effective. The queue is ordered by calibrated uncertainty and resortes on arrival, so a guarantor-letter field at 0.71 is served before a bank-statement field at 0.94. Ambiguous or contradictory sources are routed on that signal alone, which is a separate high-value flag. If 35 percent is flagged at 0.70 and capacity is 5 percent, moving the cutoff to 0.30 meets the staffing target by silently auto-approving risky cases. The alternative is to prioritise within the flagged set, add capacity, or improve the extractor.

Worked production examples: Example 3: A return-refund pipeline with asymmetric thresholds and an adversarial queue test

A retailer auto-approves return refunds by reading photos, free-text reason, order record, and warranty status. Volume is high. Auto-approval accuracy is 95 percent overall, but a quarterly audit by return reason shows defective on arrival at 98 percent, changed my mind at 97 percent, and item not as described at 71 percent, where the last category depends on photos and handwritten notes and several wrongful refunds traced to misread evidence. A few approved claims also contained contradictory evidence, a photo of an undamaged item paired with a damaged reason, that the model resolved on its own. Appending self-reported certainty and adding only auto-approve when highly confident changed neither the weak category nor the contradictory cases.

The ordering of measure, calibrate, threshold, then automate determines what is legitimate before go-live. Measuring by reason and field on a labelled reference set built per reason type is first, because even per-reason raw accuracy hides which fields within item not as described fail. Fitting confidence to measured accuracy on that set is second, with thresholds per category rather than globally. Auto-approving every category at go-live and validating afterward inverts the sequence and exposes payouts before measurement has gated eligibility. Auto-approving on a single whole-request score collapses field granularity in the same way document-level scoring collapsed contract fields.

The surviving design calibrates field-level scores per return reason, validates each category against its target before auto-approval, keeps not yet validated categories on manual review, and routes both low calibrated confidence and any ambiguous or contradictory evidence to a human regardless of score. Where reviewers once reported fields at 0.94 to 0.97 later corrected while many near 0.80 were correct, the raw cutoff both leaked errors and overloaded reviewers. After calibration the asymmetric thresholds encode the real relationship: photo-dependent fields in item not as described sit materially higher in calibrated space than reason text in defective on arrival.

The queue test validates this. A batch arrives with a defective on arrival refund at calibrated 0.96, two item not as described refunds at 0.72 and 0.68 with ambiguous handwriting, and a changed my mind refund at 0.88. A queue ordered by submission time serves 0.96 first while the most uncertain wait, the chronological anti-pattern the task punishes. A queue ordered by calibrated uncertainty serves 0.68, 0.72, 0.88, 0.96, which is what UncertaintyQueue does and what the retailer needs when volume spikes after a holiday.

Build exercise material

The five steps below mirror the reference build exercise and produce the same observable outputs it specifies. Work through them in order, because the ordering itself is part of what is tested. Validate at the end with the commands provided.

Build exercise material: Step 1: Create a mock extraction system that outputs field-level confidence scores for different document types

Build an extraction function that returns each field with a value and a confidence score between 0.0 and 1.0, processing at least four document types with distinct distributions. This is the foundation for routing: field-level scores, not a single document score, and recognisable heterogeneity so calibration has something to learn.

Observable outcome: an extraction call for a standard invoice returns vendor name and date at high confidence, amount at medium, and line items lower, while a handwritten receipt returns amounts and line items markedly lower. At least four types are needed, for example standard invoices, handwritten receipts, scanned PDFs, and international documents.

Build exercise material: Step 2: Accuracy tracking broken down by document type and field segment, not just aggregate

Run the mock extractor over a labelled validation set and compute accuracy by type crossed with field, alongside the aggregate. The aggregate will look excellent because standard invoices dominate volume, while handwritten and international types show lower numbers. This is the stratified metric trap made visible.

Observable outcome: an accuracy table where each row is a segment such as standard_invoice::invoiceDate with its own accuracy, plus a headline that sits materially higher than its weakest row. That gap proves automation cannot be keyed to the aggregate; standard invoices should show 95 percent plus while handwritten and international types show substantially lower figures.

Build exercise material: Step 3: Calibration module per field per document type on the held-out labelled set

Take the same labelled set and produce a calibration curve for each field per document type, mapping reported ranges to actual accuracy. The curve should reveal that the same score means different things per combination, for example 0.90 on dates at about 0.94 on standard invoices while 0.90 on amounts is lower on handwritten receipts. Keep a minimum per segment, such as 40 examples, and leave segments below that minimum under review until enough labels exist.

Observable outcome: a calibration map where each segment lists bins and observed accuracy, plus an insufficient list. Querying raw 0.90 through calibratePerSegment returns different accuracies per segment, evidence that thresholds must be per segment and in calibrated space.

Build exercise material: Step 4: Stratified random sampling that includes high-confidence automated extractions, proportional with a floor

Implement a sampling function that selects a representative subset from each stratum, defined by document type, field, and confidence band, including samples from the high-confidence band that would otherwise be automated without review. The sample should be proportional to the volume in each stratum with a per-stratum minimum so rare types are not starved.

Observable outcome: a sampling call on the automated population with a config such as 5 percent fraction, bands high and very_high, and a floor of 10 per stratum returns a sample plus a strata table listing population and sampled counts per key. The table shows that a stratum at 7.5 percent of volume still receives at least the floor, and that the high-confidence slice is actively sampled rather than ignored. Human labelling of that sample yields a sampled error rate per stratum and a stream of novel patterns from any heuristic-agnostic misreadings that surface.

Build exercise material: Step 5: Review router that prioritises the highest-uncertainty items and dynamically reorders

Build a priority queue ordered by calibrated uncertainty, with the routing function from step 3 deciding which items enter the queue at all. The queue must reorder as new extractions arrive and serve the most uncertain item to each available reviewer, never in chronological order. Validate by enqueuing items with shuffled arrival order and asserting that dequeue order follows increasing calibrated probability.

Observable outcome: the UncertaintyQueue described earlier orders three items at calibrated 0.91, 0.63, and 0.84 as 0.63, 0.84, 0.91 regardless of enqueue order. Enqueuing a new item at 0.58 makes it the next head. The dispatch loop claims the next reviewer with the current heap head, which can be logged to show capacity spent where genuine uncertainty was highest.

Build exercise material: Verification

After completing the five steps, confirm the invariant properties rather than a single headline figure:

Every segment has its own measured accuracy and its own calibrated threshold or is gated to review. No cell is auto-approved on the strength of an aggregate. This is verified by inspecting the matrix from step 2 and the threshold index used by routeField in step 3.

Sampling covers the automated tier. The strata table from step 4 contains entries for very_high and high bands, and the sampled error rate is specific to that tier, not inferred from low-confidence outcomes.

Review ordering is by uncertainty. The queue snapshot from step 5 is sorted by calibrated probability, and re-enqueueing after a calibration refresh resorts correctly.

Drift is gated. A widening proposal that lowers the cutoff to meet capacity without a corresponding improvement in recent sampled accuracy is refused by gateWideningProposal in the drift check.

Mechanism and API surface

Per-stratum accuracy matrix over aggregate
Accuracy measured by document type and field type as a matrix, not a single aggregate number. The matrix exposes segments such as international documents at 45 percent that a 97 percent aggregate hides, and automation decisions are made per cell.
Field-level confidence with calibration curves
Raw confidence per field from 0.0 to 1.0 is mapped to actual accuracy via binning on a labelled validation set, producing a per-stratum curve where 0.95 on a date field can mean 99 percent while the same score on an amount field means 56 percent.
Stratified sampling proportional to volume including high confidence
Sampling selects from each stratum defined by document type and confidence band proportionally to its volume, and must include the high-confidence automated band to catch novel error patterns there.
Dynamic priority queue ordered by uncertainty
Review backlog ordered so the next item served is always the highest-uncertainty item remaining. The queue dynamically reorders as new extractions arrive and reviewers never consume in chronological order.
Three-tier threshold plus validation sequence
Low confidence escalates immediately, medium routes to prioritised review, and high proceeds with stratified sampling. Sequence gates automation: measure by type and field, calibrate, set per-stratum thresholds, implement sampling, then reduce review only on validated segments.
Calibration maintenance and detected_pattern loop
Calibration is continuous via ongoing stratified sampling and production-traffic adjustment, and the detected_pattern field on findings with high dismissal rates identifies which prompts to refine next.

The decision rules in play

Each rule below is a distinct decision the exam tests, stated with its mechanism, the boundary where the answer flips, and the wrong answers written against it.

R1

The aggregate headline accuracy masks segment failure through volume weighting

A pipeline reports a single accuracy figure averaged over its entire throughput, for example a headline in the mid to high nineties percent. Behind that headline the population is heterogeneous: standard invoices dominate volume and score near perfect, while handwritten forms, scanned PDFs, international formats, amendment documents, multi-party agreements, and specialized fields score materially lower.

Volume weighting is arithmetic, not judgment. If 80 to 98 percent of traffic belongs to an easy type at 98 to 99 percent accuracy, a 2 to 8 percent tail at 60 to 85 percent accuracy barely moves the headline.

Boundary. The boundary is homogeneity. If every segment truly performs within a narrow band, the aggregate is representative and automating on it is defensible.

Recurring specifics. Tested figures recur as storytelling anchors: a headline around 95 to 97 percent overall that hides a segment at 45 to 72 percent on handwritten, scanned, or international variants, a vendor_tax_id field at 35 to 45 percent error within that headline, an indemnification_clause at 71 percent while party_name sits at 99 percent, a contract subtype

Wrong answers written against this rule

Proposal. accept the headline because it already exceeds the internal quality bar so no further breakdown is needed.

Why it attracts. leadership has set a single target and the number clears it, so analysis feels complete.

Why it fails. it confuses a weighted average with uniform performance and leaves the weakest segments unmeasured.

When it would be right. only after per-segment validation has already shown every segment clears its own threshold, at which point the headline is redundant rather than decisive.

Proposal. enlarge the overall sample or run longer to make the headline more statistically significant.

Why it attracts. more data feels more rigorous.

Why it fails. a larger aggregate sample still averages over the same hidden heterogeneity, so the weak segment remains masked just with tighter confidence intervals around the wrong statistic.

When it would be right. when the concern is sampling noise within an already-segmented measurement, not when segmentation has been skipped entirely.

Proposal. raise the headline threshold from 97 to 99 percent to force improvement everywhere.

Why it attracts. a stricter headline must mean stricter quality.

Why it fails. a higher aggregate can still be satisfied by improving the already-strong majority while leaving the weak tail untouched.

When it would be right. when combined with per-segment floors where each segment must individually meet the raised threshold.

How the same rule gets re-asked
  • The same rule is mutated by changing which segment hides: sometimes it is a document family, sometimes a single field, sometimes a vendor-specific layout, sometimes a language or regional variant. It is also mutated by changing the headline value while keeping the tail low, and
R2

Validation must cross document type with field before any automation decision

Validation is performed on a labelled validation set and reported as a matrix of document type crossed with extracted field. Each cell in the matrix is an independent automation gate: invoices x total_amount, receipts x total_amount, contracts x governing_law, amendments x effective_date, and so on.

Different document types present different layout and noise challenges, and different fields present different semantic difficulty. A date field on a typed invoice is easier than the same date on a handwritten amendment; a party_name may be well anchored while termination_clause requires synthesis across paragraphs.

Boundary. The boundary is when a field is genuinely type-agnostic and evidence shows uniform performance across types, in which case collapsing types for that field is a documented simplification after measurement, not before. The nearby opposite case is premature collapsing: deciding that field performance must be

Recurring specifics. Matrix presentations recur with four to five document types and three to four fields, with one cell highlighted as 60 to 84 percent while the row or column average sits in the nineties. The pattern party_name 99 percent versus indemnification_clause 71 percent, or invoices 99.5 percent at 80 percent volume versus contracts 72 percent at

Wrong answers written against this rule

Proposal. validate only by document type and assume fields within a type are uniform.

Why it attracts. halves the matrix work.

Why it fails. field difficulty varies strongly within a type, as the recurring party_name versus governing_law examples show.

When it would be right. only when per-field measurement has already shown uniformity within each type, which is a finding, not an assumption.

Proposal. validate only by field and assume document types are uniform for that field.

Why it attracts. symmetry to the previous shortcut.

Why it fails. the same field can be easy on clean typed exports and hard on scanned handwritten variants.

When it would be right. only as a reported simplification after the data supports it.

Proposal. rely on a second model pass or a higher global confidence threshold instead of segmentation.

Why it attracts. seems to add rigor without matrix work.

Why it fails. a second pass can reproduce the same systematic error, and a higher headline threshold still averages over heterogeneity.

When it would be right. as complementary controls after segmentation, not substitutes for it.

How the same rule gets re-asked
  • Mutations shift whether the hidden failure is a whole document family or a single field within an otherwise strong family, and whether the aggregate headline is 94, 96, or 97 percent. Some variants make the matrix explicit with a table, others narrate it as a
R3

Raw model confidence is uncalibrated and field- and type-specific

Many extraction designs have the model emit a numeric confidence or confidence_score per field, often between 0.0 and 1.0, and then route on that number. Raw scores reflect the model's internal next-token expectations, not an empirical mapping to accuracy.

Language models are trained to produce plausible continuations, not to report calibrated probabilities, and calibration varies with field difficulty, document noise, and prompt phrasing. Where evidence is ambiguous, models often produce a plausible value with high self-reported certainty rather than low certainty, so fabricated or

Boundary. The boundary is a system that has explicitly calibrated the score and reports a calibrated probability rather than a raw score, in which case the number has been transformed into a trustworthy signal. The nearby opposite case where trusting the raw number is acceptable does not exist in the tested material; even when a raw

Recurring specifics. Repeated phrasing includes confidence_score, extraction_confidence, and self-reported confidence. The calibration gap is illustrated as 0.90 meaning 94 percent on one field but 82 percent on another, or 0.94 to 0.97 scores that later correct on human review.

Wrong answers written against this rule

Proposal. use the raw score directly with a fixed cutoff such as 0.8 or 0.9.

Why it attracts. simple and operationally cheap.

Why it fails. it assumes the mapping from score to accuracy is uniform, which the tested material shows it is not, and it leaves overconfident errors on weak fields undetected.

When it would be right. only after calibration has transformed the raw score into a reliable probability, at which point it is no longer raw.

Proposal. raise the raw cutoff to 0.99 to compensate for miscalibration.

Why it attracts. seems stricter.

Why it fails. moving the cutoff on an uncalibrated scale does not fix the scale, and it penalizes well-understood easy segments while still missing confidently wrong cases.

When it would be right. never as a fix for miscalibration; only as a tuned calibrated probability after mapping.

Proposal. replace confidence with response length or proxy signals such as verbose hedging.

Why it attracts. observable without calibration work.

Why it fails. length and hedging correlate weakly with extraction correctness and can be gamed, while the weak field's high confidence remains invisible.

When it would be right. not as a routing signal for extraction accuracy.

How the same rule gets re-asked
  • Mutations change whether the confidence is per field or per document, whether the score is 0 to 1 or 1 to 10, and whether the field is amount versus date versus coverage_type. Some variants add a second model check that still relies on the same
R4

Calibration requires a labelled validation set that maps reported confidence to observed accuracy

A labelled validation set of known-correct extractions is built, often a few hundred documents, stratified by document type and field. The pipeline runs over this set, collects each reported confidence alongside whether the extraction was correct, and fits a calibration mapping, typically per field per document type.

Only ground truth can turn a self-assessment into a measurable probability. Without it the team is tuning a threshold on an untrusted scale, which is equivalent to picking a number that feels strict.

Boundary. The boundary is sample sufficiency: if a segment has too few labelled examples, its curve is unreliable and that segment must stay under human review until enough labels are collected. The nearby opposite case is using a large but unlabelled production sample or complaint volume as a proxy for calibration; that measures downstream harm after

Recurring specifics. Validation set sizes that recur include 200 to 500 labelled extractions or similar counts of documents. The word calibrate and the phrase fit each field's reported confidence to its measured accuracy on that set recur as definitional language.

Wrong answers written against this rule

Proposal. set the threshold using the same headline accuracy or a hand-chosen value such as 0.90 without a labelled set.

Why it attracts. avoids labelling cost.

Why it fails. it leaves the team calibrating against the wrong statistic and preserves the aggregate trap.

When it would be right. only if a labelled calibration has already been performed and the threshold is expressed in calibrated terms.

Proposal. reuse the training or few-shot examples as the validation set.

Why it attracts. data is already on hand.

Why it fails. few-shot examples are not a representative measurement set and may be optimistic or narrow.

When it would be right. never as a substitute; a held-out labelled set is required.

Proposal. rely on a second model or self-consistency as a calibration substitute.

Why it attracts. seems independent.

Why it fails. a second pass can share the same miscalibration or overconfidence, and agreement is not accuracy.

When it would be right. as a complementary signal after calibration, not instead of it.

How the same rule gets re-asked
  • Variants change which dimension is segmented for calibration: sometimes by document type, sometimes by field, sometimes by merchant or language. Some variants test whether a single global calibration curve suffices versus per-segment curves; the exam rewards per-segment calibration where miscalibration varies by segment.
R5

A single global threshold cannot serve heterogeneous fields and document types

After calibration each field-type cell has its own mapping from reported confidence to observed accuracy, so the threshold that achieves a target error rate on one cell will be wrong on another. A global cutoff such as confidence >= 0.90 treats 0.90 on borrower_income from standardized bank statements as equivalent to 0.90 on collateral_appraised_value from

example.py
python
# Per-segment calibrated routing, not a single global cutoff
# Each threshold is fitted on labelled data for that segment
thresholds = {
    ("standard_invoice", "invoiceDate"): 0.88,
    ("standard_invoice", "totalAmount"): 0.92,
    ("scanned_receipt", "invoiceDate"): 0.94,
    ("scanned_receipt", "totalAmount"): 0.96,
    ("handwritten_letter", "collateral_value"): 0.97,
}

def should_auto_approve(doc_type, field, calibrated_prob):
    key = (doc_type, field)
    cutoff = thresholds.get(key)
    if cutoff is None:
        return False  # not yet validated, keep under human review
    return calibrated_prob >= cutoff

Heterogeneity in difficulty means the same raw confidence carries different information depending on context. A field that is highly constrained by layout, such as a formatted date, can be correct at lower reported confidence, while a field that requires cross-paragraph synthesis or ambiguous currency interpretation can be wrong even when reported confidence is high.

Boundary. The boundary is demonstrated uniformity. If calibration evidence shows that two segments share the same curve within tolerance, they can share a threshold, but that is a documented pooling decision after measurement.

Recurring specifics. Language that recurs is one global threshold, single cutoff, and per field per document type as the contrast. Specific numbers include global proposals at 0.80, 0.85, 0.90, and 0.95 that are shown to leave a weak category unchanged when raised globally, versus calibrated thresholds such as routing vendor_tax_id on international invoices at a lower calibrated In prose terms, the code encodes the idea that the cutoff lives in calibrated-probability space and lives per cell. Before the mapping exists, the safest default is to keep the cell under review.

Wrong answers written against this rule

Proposal. raise the single global cutoff from 0.80 to 0.90 to fix the weak guarantor-letter segment.

Why it attracts. feels like a stricter gate.

Why it fails. the weak segment's curve is shifted, so the same global move does not change its overconfident error rate while it unnecessarily taxes the strong segments.

When it would be right. only if the threshold being raised is already a calibrated probability that has been validated per segment, not a raw global number.

Proposal. tune per-merchant raw-score cutoffs without calibration, for example a higher raw threshold for digital goods and a lower one for travel.

Why it attracts. acknowledges heterogeneity without paying the calibration cost.

Why it fails. raw cutoffs are still on an untrusted scale, so the tuned numbers are unstable and not comparable across segments.

When it would be right. never as the primary fix; per-segment calibration must come first, then thresholds follow.

Proposal. use a single document-level score for routing.

Why it attracts. simpler threshold management.

Why it fails. it averages over fields with different curves, so a high document score can hide a low-confidence field that carries the real risk.

When it would be right. only when every field in the document is known to share the same calibration and consequence, which the tested material never grants.

How the same rule gets re-asked
  • Mutations vary whether the heterogeneity is by field, by document type, by vendor, or by merchant category, and whether the distractor proposes a tuned raw per-merchant cutoff versus a single global calibrated cutoff. Both are wrong for the same reason. The correct mutation always adds segmentation to calibration before any threshold is trusted.
R6

Field-level confidence granularity outranks document-level confidence

An extraction often returns several fields per document, each with its own difficulty. Field-level confidence emits a separate calibrated probability for each extracted value, for example borrower_income, declared_liabilities, collateral_appraised_value, and loan_to_value each with its own score.

result.json
json
{
  "vendorName": {"value": "Acme Corp", "confidence": 0.98},
  "invoiceDate": {"value": "2024-03-15", "confidence": 0.95},
  "totalAmount": {"value": "1247.83", "confidence": 0.72},
  "lineItems": {"value": [{"description": "Widget A", "quantity": 10, "unit_price": "12.00"}], "confidence": 0.61}
}
example.ts
typescript
type FieldExtraction<T> = {
  value: T;
  calibratedConfidence: number;
  field: string;
  docType: string;
};

function fieldsNeedingReview(fields: FieldExtraction<unknown>[]): FieldExtraction<unknown>[] {
  return fields.filter((f) => {
    const cutoff = getThreshold(f.docType, f.field); // fitted per Rule 5
    return f.calibratedConfidence < cutoff;
  });
}

Errors are field-local. A contract can have vendor_name extracted correctly with high reliability while governing_law is wrong, or an insurance form can have four fields near perfect while coverage_type fails systematically.

Boundary. The boundary is consequence. If downstream systems accept or reject a document atomically and cannot act on a subset of fields, document-level routing may be the operational requirement, but the measurement still must be field-level to know which fields drive document failure.

Recurring specifics. Schema patterns that recur show vendorName, invoiceDate, totalAmount, lineItems each paired with a confidence float, versus an overall_confidence single field appended to the output. Audit framings that recur include a contract extraction where liability_cap fails in 40 percent of contracts while all other fields exceed 95 percent accuracy, or an insurance form where coverage_type alone The example shows why a document average of roughly 0.82 would both over-route the strong vendorName and under-route the weak lineItems. Field-level thresholds keep each decision independent. The TypeScript sketch makes explicit that review is computed per field, not per document, and that the cutoff is looked up by segment.

Wrong answers written against this rule

Proposal. replace field-level scores with a single document score to simplify routing logic.

Why it attracts. fewer thresholds to manage.

Why it fails. it coarsens the signal and hides field-specific miscalibration, so a weak field rides through on the strength of strong siblings.

When it would be right. only when the document has one field or when field failures are proven perfectly correlated, which is not the tested case.

Proposal. raise or lower the document-level threshold to compensate.

Why it attracts. seems to tune the same knob.

Why it fails. no single knob can simultaneously protect the weak field and avoid over-reviewing the strong fields.

When it would be right. never as a fix for missing field granularity.

Proposal. add a second model pass that re-extracts the whole document and averages.

Why it attracts. seems more independent.

Why it fails. whole-document agreement can still be field-locally wrong, and averaging preserves the coarseness.

How the same rule gets re-asked
  • Mutations alternate which field is the weak one, whether the document-level score is presented as already clearing a target, and whether the fix is offered as require a separate per-field score for each of the six values. The answer remains per-field granularity regardless of which field is highlighted.
R7

Sampling only low-confidence outputs is self-confirming and leaves the automated population unmeasured

A common draft policy routes every extraction below a confidence threshold to human review and automates everything above it, then measures quality only on the routed low-confidence set or on the small fraction that fails validation. Because the high-confidence set is never inspected against ground truth, its error rate is unknown and its error patterns

Routing and measurement serve different purposes. Routing directs scarce review to where errors are most likely; measurement estimates true risk in the population that actually reaches downstream systems.

Boundary. The boundary is early triage before any automation has been enabled: inspecting only low-confidence cases is a reasonable first allocation of limited review while everything still passes through a human. The nearby opposite case where low-confidence-only review is correctly punished is after auto-approval is live: once high-confidence extractions bypass humans, the unreviewed population must be

Recurring specifics. The phrasing review only the low-confidence extractions recurs as the distractor, often paired with an otherwise-correct proposal to calibrate thresholds. Time horizons that recur include audits a month or a quarter after disabling review that find 40 percent error on a single vendor layout or tax field hidden within the former aggregate.

Wrong answers written against this rule

Proposal. review only the extractions the model flagged as low confidence because that is where errors concentrate.

Why it attracts. efficient allocation of review and feels targeted.

Why it fails. it measures the routed population, not the automated population, so a confident-but-wrong segment remains undetected.

When it would be right. only before any tier is auto-approved, or as the routing component of a combined design that separately samples high-confidence cases for measurement.

Proposal. rely on downstream complaints or reconciliation as the signal for high-confidence errors.

Why it attracts. catches real harm without extra sampling cost.

Why it fails. it is lagging, sparse, and biased toward harm that is visible downstream, not toward the full error distribution.

When it would be right. as a secondary detective control after stratified sampling, never as the primary measurement.

Proposal. re-run low-confidence extractions or re-extract the same document twice and compare.

Why it attracts. seems to add independence.

Why it fails. deterministic misreadings reproduce, so self-consistency is not ground truth.

How the same rule gets re-asked
  • Variants test whether low-confidence-only is paired with otherwise-correct ideas such as per-segment thresholds or stratified display, which makes it a tempting near-correct answer. Some variants replace low-confidence with specific heuristics like required fields empty or documents with formatting anomalies.
R8

Stratified random sampling of high-confidence extractions is the instrument for the automated tier

Stratified random sampling selects a representative sample from each stratum of the population that is being automated, where strata are defined by document type, field, and confidence band. The sample is inspected against ground truth to estimate ongoing error rate and to surface specific error patterns.

output.txt
text
-- Illustrative stratification for ongoing measurement, not a literal product query
-- Assumes an extractions table with document_type, field_name,
-- confidence_band, and calibrated confidence
SELECT document_type, field_name, confidence_band,
       COUNT(*) AS population,
       AVG(is_correct) AS sampled_accuracy  -- is_correct from human label on the sample
FROM extraction_sample
WHERE sample_type = 'stratified_high_confidence'
GROUP BY document_type, field_name, confidence_band
ORDER BY sampled_accuracy ASC;

High-confidence extractions bypass review, so without sampling they bypass measurement. Random sampling within strata provides an unbiased estimate of true error rate for each automated segment and does so with quantifiable uncertainty.

Boundary. The boundary is sampling intensity. When volume is very low, the fixed percentage may still yield too few examples for rare strata, at which point a per-stratum minimum supplements proportional sampling, as described in Rule 9.

Recurring specifics. Stratification dimensions that recur are by document type, by field, and by confidence band such as 90 to 95 percent, 95 to 99 percent, 99 percent plus. Time language that recurs is weekly, quarterly audit, and ongoing verification versus one-time validation. The query makes explicit that the population of interest is the sampled high-confidence slice, grouped by the dimensions that define heterogeneity, and that accuracy is computed on the human-labelled sample, not on the proxy.

Wrong answers written against this rule

Proposal. review only low-confidence extractions and treat their corrected rate as the pipeline error rate.

Why it attracts. those are the known risky cases.

Why it fails. it estimates the wrong population's error and cannot detect confident errors.

When it would be right. only as a routing control, never as the measurement of the automated tier.

Proposal. re-run the original labelled validation set once and treat calibration as permanently valid.

Why it attracts. one-time effort.

Why it fails. validation data ages as document formats drift and as new types enter, so ongoing sampling is the only way to catch drift.

When it would be right. never as the sole ongoing assurance; it remains the calibration anchor but not the drift detector.

Proposal. wait for customer complaints as the primary signal.

Why it attracts. measures real downstream impact.

Why it fails. complaints are delayed and incomplete, and by the time they surface the error has already caused harm at scale.

When it would be right. as supplemental signal alongside sampling.

How the same rule gets re-asked
  • Variants test whether sampling is described as stratified versus uniform random, whether it covers high versus low confidence, and whether it is one-time versus continuous. The tested correct form is stratified, high-confidence, and continuous, with reporting that enables pattern detection, not just a headline error rate.
R9

Pure volume-proportional sampling undersamples rare strata without a per-stratum minimum

When daily volume is highly skewed, for example one document type at 70 percent and four others at 7.5 percent each, a sampling plan that allocates samples strictly in proportion to volume will assign most samples to the dominant type and leave the rare types with too few labelled examples to estimate their error rate

Statistical power depends on absolute sample count per cell, not just share of volume. A 5 percent overall sample on a type that is only 5 percent of traffic yields 0.25 percent effective coverage on that type, which is often fewer than a dozen examples per week and insufficient to distinguish a meaningful error rate

Boundary. The boundary is operational cost: when a rare type is both low-volume and low-consequence, the minimum can be smaller but not zero, because the segment still needs some measurement to claim it is safe. The nearby opposite case where pure proportional is correctly rewarded is when the prompt explicitly states strata are equally sized or

Recurring specifics. The teaching example that recurs is one type makes up 70 percent of daily volume, and the other four each make up roughly 7.5 percent followed by the diagnosis pure volume-proportional sampling would under-sample the four low-volume document types. Language that recurs is minimum sample size per document type regardless of its share of volume

Wrong answers written against this rule

Proposal. sample strictly in proportion to volume because that is statistically optimal for every segment.

Why it attracts. proportionality feels fair and efficient.

Why it fails. proportionality is optimal for estimating the population average but not for estimating each stratum's rate, especially rare high-risk ones.

When it would be right. only when strata have similar volume and similar consequence, which is not the tested skew.

Proposal. exclude the high-volume type from sampling entirely and focus only on the smaller types.

Why it attracts. seems to concentrate effort where risk might hide.

Why it fails. it removes measurement from the segment that still carries the most absolute error count even at low error rate.

When it would be right. never as a complete plan; the high-volume type still needs its own sampled estimate.

Proposal. solve the skew by adding a different prompt template per volume tier.

Why it attracts. template variation feels like coverage.

Why it fails. prompt templates do not change the arithmetic of sample size and may introduce new heterogeneity without measurement.

When it would be right. only after measurement shows a specific template is the cause of a segment gap.

How the same rule gets re-asked
  • Mutations keep the volume numbers but change the document families, change the number of strata from five to three or four, or present the fix as combine proportional with a floor versus switch to uniform across strata. Some variants test whether the learner recognises that
R10

Stratified sampling is how novel failure modes surface, not only how averages are estimated

Beyond estimating error rate, sampling across high-confidence strata provides a mechanism to discover failure modes that did not exist in the original validation set. Reviewers inspecting samples from comparison tables, footnotes, appendices, or newly introduced two-column legal formats encounter plausible-but-incorrect values that no prior heuristic flagged.

Systematic errors from a new layout or phrasing are deterministic: the model reproduces the same confident misreading every time, so re-extraction or heuristic flags keyed to known patterns will miss them while sampling is agnostic to pattern. Sampling therefore has discovery power that retrospective heuristics lack.

Boundary. The boundary is sample size and review depth: discovery requires enough sampled examples per stratum and reviewers who record the underlying detected_pattern, not just a pass or fail label. The nearby opposite case where sampling is insufficient is when the new pattern is extremely rare, below the sampling resolution; there the complement is broader slice

Recurring specifics. Error sources that recur as novel patterns include comparison tables showing competitor specs, appendices referencing different product variants, footnote references misattributed, unusual two-column legal formats, split-cell scans, and vendor-specific non-standard invoice layouts. The dual-purpose phrase novel pattern detection recurs alongside measure error rate.

Wrong answers written against this rule

Proposal. lower the confidence threshold from 0.85 to 0.70 to catch more high-confidence errors.

Why it attracts. widens the review net.

Why it fails. the errors are inside the greater-than-0.85 bucket by definition, so expanding the below-0.85 tier pulls in a different population and does not touch the confidently wrong cases.

When it would be right. never for high-confidence discovery; only for reducing low-confidence false negatives.

Proposal. add a verification pass that re-extracts from each high-confidence document and flags disagreement.

Why it attracts. seems independent.

Why it fails. systematic layout-driven errors are deterministic and reproduce identically, so agreement is not correctness.

When it would be right. only for stochastic variance or tooling glitches, not for layout-driven bias.

Proposal. implement heuristic rules that flag documents containing comparison tables or appendices.

Why it attracts. targets known sources.

Why it fails. it covers only yesterday's known patterns and cannot learn a new footer or column format.

When it would be right. as a temporary targeted filter after a pattern has been discovered via sampling, not as the discovery mechanism.

How the same rule gets re-asked
  • Variants mutate the novel pattern identity, the confidence threshold value, and whether the question pairs measurement with improvement tracking. Some variants pit monitor confidence score distributions against sampling; the exam rewards ground-truth sampling because confidence can remain high while accuracy drifts.
R11

The required order is measure then calibrate then route then automate, skipping steps is the trap

The validated sequence is: first measure accuracy by document type and field on a labelled set, then calibrate per-segment confidence thresholds against that set, then implement routing that sends low calibrated-confidence and not-yet-validated segments to human review, then enable stratified sampling for ongoing verification, and only then reduce human review on segments whose sampled accuracy

Each shortcut creates a distinct blind spot. Automating on the aggregate skips segmentation and hides the weak cell.

Boundary. The boundary is an emergency risk response: when clinicians report critical errors on a small segment such as allergy fields on handwritten forms, the immediate action is to isolate that segment and route all of it to mandatory review before the full measurement is complete, which is a temporary inversion justified by safety. The nearby

Recurring specifics. Step language that recurs is measure accuracy by document type and field segment, calibrate confidence scores using labelled validation sets, set calibrated thresholds for automation versus human review, implement stratified random sampling for ongoing verification, and only then reduce human review on segments that demonstrate consistent validated accuracy. Automation proposals that recur include retire the

Wrong answers written against this rule

Proposal. run a two-week pilot routing a quarter of high-confidence extractions to downstream systems and monitor error reports.

Why it attracts. sounds empirical.

Why it fails. it measures after exposure rather than before, so segmented failures propagate and downstream harm is the detector.

When it would be right. only after segmentation and calibration have already gated which categories are eligible for any pilot.

Proposal. compare accuracy at different confidence thresholds to find the optimal cutoff before segmenting.

Why it attracts. threshold optimization feels rigorous.

Why it fails. without per-segment accuracy the optimization is on the aggregate and can still automate a weak segment at the chosen cutoff.

When it would be right. after segmentation, as a per-segment threshold sweep on calibrated probabilities.

Proposal. verify that 97 percent meets downstream system requirements and then automate.

Why it attracts. ties accuracy to business tolerance.

Why it fails. it validates the wrong number, the aggregate, without confirming that each downstream feed actually experiences 97 percent.

When it would be right. after per-segment numbers exist and each downstream system's feed has its own validated rate.

How the same rule gets re-asked
  • Variants permute which step is skipped: some skip segmentation, some skip calibration, some keep both but omit ongoing stratified sampling, some invert the last two steps. All are punished equally.
R12

Treating the confidence threshold as a volume dial to match reviewer capacity is backwards

A pipeline flags fields below 0.70 for review and finds that 35 percent of fields are flagged, far above the team's 5 percent review capacity. The backwards fix is to lower the threshold to 0.30 until only about 5 percent are flagged, hitting the volume target by redefining what counts as risky.

Moving the threshold to match capacity does not change which extractions are actually risky; it only changes which risky extractions are silently auto-approved. The result is unmeasured accuracy regression: the team meets a staffing target while letting through the cases that genuinely needed review, which then surface as downstream errors, complaint spikes, or reconciliation failures.

Boundary. The boundary is genuine capacity triage after quality gates are fixed. Once thresholds reflect true quality, the team may still have more flagged items than reviewers can handle and must then prioritize within the flagged set by calibrated uncertainty, as in Rule 19, or add capacity, or improve the extractor so fewer items are flagged

Recurring specifics. Numbers that recur include confidence < 0.70 flags 35 percent, capacity at 5 percent, and a proposed move to 0.30 to hit roughly 5 percent. Language that recurs is threshold is a quality signal, not a volume dial and auto-approving extractions that genuinely need review to hit a volume target is a silent accuracy regression.

Wrong answers written against this rule

Proposal. lower the threshold to match team capacity because calibration is the right tool for matching volume.

Why it attracts. frames the capacity mismatch as a threshold tuning problem.

Why it fails. threshold calibration is for matching thresholds to error rates, not to headcount, and the proposed tuning hides the quality regression.

When it would be right. never in this direction; thresholds follow quality improvement, not capacity pressure.

Proposal. call the lowered threshold a reasonable short-term workaround while quality is improved.

Why it attracts. acknowledges the trade-off and promises future improvement.

Why it fails. the short-term workaround still silently degrades accuracy on the riskiest 30 percent of flagged fields that were redefined as auto-approved.

When it would be right. only if the workaround is explicitly paired with added sampling and disclosure of the increased risk, which the tested material does not accept as the best answer.

Proposal. lower thresholds mean more confident extraction so accuracy will improve.

Why it attracts. confuses threshold direction with model quality.

Why it fails. thresholds do not change extraction accuracy, only routing.

How the same rule gets re-asked
  • Variants change the exact flagged fraction, the proposed threshold value, and whether the framing is a high no-change rate on flagged items versus a capacity overload. In both cases the exam rewards the answer that evaluates any threshold change against the rate of actual errors that slip through, not just volume or unnecessary-flag rate.
R13

Schema-valid does not mean semantically correct

A pipeline validates every extraction against a JSON schema that enforces types, required fields, and format, for example that total_amount is a number and invoiceDate matches an ISO date. Semantic correctness asks whether the value is the right value in the right field, for example whether a duration like 30 minutes belongs in an ingredient

Schemas constrain shape and presence, not meaning. A fabricated or misread value can satisfy every type check while still being the wrong fact, and the examples in the tested material are engineered to pass validation precisely so the learner cannot rely on it.

Boundary. The boundary is when the schema itself encodes semantic constraints that the validator can enforce deterministically, such as requiring both calculated_total and stated_total plus a conflict_detected boolean, or requiring line_items that must reconcile arithmetically outside the model's generation. There the schema is explicitly designed to surface semantic inconsistency for a human gate.

Recurring specifics. Fabrication patterns that recur include 30 minutes placed in an ingredient quantity field, line_items that do not sum to the stated_total, tiger versus cat labels on drillthrough charts, and OCR-misread digits where a printed total and the line-item sum diverge. Rates that recur include 12 percent of extractions with semantic errors that pass validation, 95

Wrong answers written against this rule

Proposal. make the schema stricter so contract errors fail validation and become visible in the aggregate.

Why it attracts. seems to align validation with correctness.

Why it fails. it redefines what counts as a validation failure without fixing extraction quality, so the underlying errors persist and downstream counts remain distorted.

When it would be right. only when the stricter check is a deterministic semantic gate such as a computed-versus-stated totals comparison that routes to review, not a cosmetic redefinition.

Proposal. trust any extraction that passes schema validation and treat schema pass as correctness.

Why it attracts. simple and operational.

Why it fails. it is the exact anti-pattern the tested material targets, hiding field-specific failures inside a high validation rate.

When it would be right. never as the sole quality claim; it is only one layer alongside calibrated review and cross-field checks.

Proposal. stop processing contracts because they are too hard.

Why it attracts. removes the visible failure slice.

Why it fails. it concedes the pipeline's scope rather than adding per-type handling and misses the lesson that per-type tracking plus correctness-focused evaluation is required.

How the same rule gets re-asked
  • Variants mutate which field carries the semantic error, which validation signal is present, and whether the question pairs schema-valid equals correct with an aggregate metric trap, compounding two rules. The fix remains the same: per-type correctness measurement and a semantic signal such as field-level confidence or a deterministic cross-check.
R14

Validation-retry fixes format faults but never absent information or genuine source inconsistency

A validation-retry loop sends the original document, the failed extraction, and the specific validation error back to the model so it can self-correct a correctable format issue, for example a date returned as 03/04/2025 when the schema requires 2025-03-04, or a confidence field returned as the string high instead of a float between 0.0 and

result.json
json
{
  "title": "Retry classification for extraction validation",
  "retry_policy": {
    "max_attempts": 3,
    "retryable": ["MALFORMED_JSON", "FORMAT_ERROR", "TRUNCATED_JSON"],
    "not_retryable": ["SOURCE_ABSENT", "NOT_PRESENT", "GENUINE_SOURCE_CONFLICT"]
  },
  "routing": {
    "on_not_retryable": "emit_null_with_flag_and_route_to_human",
    "on_exhausted_retries": "flag_for_review_with_original_source"
  }
}

Retry operates on the same source context plus an error hint. If the required field is absent, the model's best action on retry is still to guess or to leave the field null, and retrying up to the bound simply burns API spend while never changing the underlying absence.

Boundary. The boundary is correct classification at retry time. A deterministic validator that labels a failure as MALFORMED_JSON or MISSING_FIELD versus unsupported form type enables the router to retry only the former.

Recurring specifics. Patterns that recur include retrying a signatory_date ISO format failure successfully on the second attempt, retrying a confidence: high string error with the specific feedback must be a number between 0.0 and 1.0, and documents that genuinely lack shipping_date or policy_effective_date where every retry returns the same missing field error. Configuration that recurs is a The configuration encodes the idea that the retry decision is made on failure class, not on blind iteration count, and that absent or genuinely conflicting data exits the retry loop immediately.

Wrong answers written against this rule

Proposal. increase retries from three to ten or retry indefinitely until it passes.

Why it attracts. more tries feels more recoverable.

Why it fails. absent information and genuine inconsistency are uncorrectable by repetition, so extra tries waste spend and delay correct routing.

When it would be right. only when the failure is known to be a transient format fault, not when the field is absent.

Proposal. expand the retry error message to list every null field to give more context.

Why it attracts. seems more helpful.

Why it fails. it still asks the model to produce an absent fact and does not change the information-theoretic limit.

When it would be right. when the missing field is present but the first extraction simply omitted it, which is a different failure class.

Proposal. force tool_choice to the extraction tool name to guarantee the field is populated.

Why it attracts. guarantees a tool call.

Why it fails. it guarantees syntax without guaranteeing the fact exists, so it encourages fabrication.

When it would be right. only after the schema has been made nullable so not_present is a valid outcome.

How the same rule gets re-asked
  • Variants mutate which field is absent, whether the absence is because the form omits it or because the source is illegible or conflicting, and whether the retry instruction is retry with error feedback versus append the specific validation error. The rule remains the same: absent
R15

Retry must be bounded with a human fallback, never indefinite

Every validation-retry loop is capped, typically at two to three attempts, after which the document or field is flagged for human review with the original source preserved. The cap prevents a small share of uncorrectable documents from consuming unbounded API retries while adding no correctness,

Models tend to be stuck rather than improving after a few guided tries on the same source. The tested material reports first-retry success around 90 percent on correctable format faults, which implies the marginal return after the bound is low.

Boundary. The boundary is the exact bound: one attempt may be too few for schemas with complex nested fields where a second correction is often needed, so two to three is the recurring recommendation. The nearby opposite case where a higher bound would be justified is a transient infrastructure failure such as rate limiting or truncation

Recurring specifics. The number 2-3 retries, then fall back or human review recurs as the canonical limit, with indefinite retries and 10 retries labelled as wrong. Status language that recurs is bounded retry plus graceful fallback and flag for human review rather than retried forever.

Wrong answers written against this rule

Proposal. retry indefinitely until it eventually passes.

Why it attracts. guarantees eventual schema compliance.

Why it fails. it never passes for stuck cases and it masks the need for a schema or input fix while burning spend.

When it would be right. never in the tested extraction context.

Proposal. discard the document immediately on first validation failure.

Why it attracts. avoids retry cost.

Why it fails. it throws away the cheap first-retry recovery that succeeds for most format faults and it loses the data entirely.

When it would be right. only when the document is known to be out of scope and discard is the intended policy, which must be an explicit design choice.

Proposal. remove the format requirement from the schema so any format passes validation.

Why it attracts. eliminates retries.

Why it fails. it pushes format normalization downstream and trades correctness for apparent validation success.

How the same rule gets re-asked
  • Variants mutate the field that triggers retry, the exact number proposed as the alternative, and whether the fallback is described as human review, default value, or quarantine. The correct bound stays at two to three before fallback regardless of field.
R16

Self-reported confidence is systematically overconfident without an external calibration layer

When a model is asked for a numeric confidence directly, it tends to report high values even when it is wrong, producing scores like 0.90 plus on incorrect extractions or a 9 out of 10 self-rating on a mishandled multi-account billing dispute. An external calibration layer corrects this tendency by mapping the reported number against

Calibration of self-reports is a known weakness: the training objective rewards plausible continuation, and hedging is not reliably tied to empirical odds. Prompt instructions to only report high-confidence findings or be careful lack a decidable boundary, so the model treats every finding as high-confidence and the filter does nothing.

Boundary. The boundary is when confidence is not self-reported at all but is computed by independent self-consistency, multiple sampled responses measuring agreement, which is noted as reliable but more expensive. The nearby opposite case where self-report might be trusted is at very low stakes where the cost of multiple calls exceeds the loss, as in a

Recurring specifics. Overconfidence examples that recur include high-confidence violation labels overturned 40 percent on manual review, self-ratings of 9 out of 10 on wrong billing resolutions while simple address changes score 3 out of 10, and a teleradiology QA that lowers confident-on-ambiguous from 16 to 11 percent with a prompt line but still cannot identify which determinations

Wrong answers written against this rule

Proposal. treat a high self-reported confidence as evidence of correctness and reduce review.

Why it attracts. it is the intended use of a confidence field.

Why it fails. high reported confidence is often the failure mode, not the proof of success.

When it would be right. only after the reported score has been mapped through a labelled calibration layer per segment.

Proposal. lower or raise the raw confidence cutoff to tune precision.

Why it attracts. seems to adjust strictness.

Why it fails. moving the cutoff on an overconfident scale preserves the miscalibration and may suppress true violations while leaving false ones.

When it would be right. only on the calibrated probability after mapping.

Proposal. increase temperature to make the model less overconfident.

Why it attracts. temperature affects output diversity.

Why it fails. temperature controls randomness of generation, not the numerical fidelity of a self-reported probability, and the tested material explicitly calls this misdiagnosis wrong.

How the same rule gets re-asked
  • Variants test whether the escalation trigger is described as confidence < 0.8 versus a 1 to 10 scale, whether the fix is offered as add four few-shot examples to stabilise reason text versus fit a per-segment calibration curve, and whether a small prompt phrase do
R17

Confirmation must use an independent instance without prior reasoning, not a same-session re-read

When a first pass produces a finding with high self-reported confidence, a confirmation pass that runs in the same conversation inherits the first pass's reasoning and tends to uphold it, for example a self-confirmation that agrees with 92 percent of its own flags while an independent coder later overturns 33 percent of those upheld flags.

Anchoring and shared blind spots cause correlated errors. The re-reading instance sees the earlier justification and reconstructs the same plausible reading rather than re-deriving it, so systematic misreads such as coverage terms absent from the source or protection-system explanations reproduce.

Boundary. The boundary is when the confirmation task is purely format validation, where the same session already has the schema error context and a follow-up turn is efficient. The nearby opposite case where confirmation must be independent is any correctness judgment where the prior reasoning could be the error itself, including medical QA, income-support rule gaps,

Recurring specifics. Figures that recur include 92 percent self-confirmation with 19 to 33 percent later overturn on independent review, and 88 percent confirmation even after a prompt to review each finding independently. The design language that recurs is second independent Claude instance, with no access to the

Wrong answers written against this rule

Proposal. add a verification turn in the same session that restates each finding and re-reads the region.

Why it attracts. keeps continuity.

Why it fails. inherits the same plausible misreading.

When it would be right. only for syntax checks.

Proposal. keep the independent instance but limit its scope to disagreements only.

Why it attracts. saves cost.

Why it fails. the independent instance never sees the confident agreements that hide 16 percent overturn.

When it would be right. only after independent re-reading of all findings has shown disagreements capture most risk, which the tested material does not show.

How the same rule gets re-asked
  • Mutations alternate whether the second pass is framed as a rubric check, a recomputation, or a strict standard, and whether the source is images plus draft impressions or chart documentation plus code flags. The test remains whether the confirmer shares reasoning context.
R18

Vague prompt instructions do not repair miscalibration or precision

Instructions such as be careful, only report high-confidence findings, use your best judgment, flag ones that seem risky, or assign severity based on impact leave the decision boundary undefined. The model treats every finding as high-confidence under those instructions, so severity distributions shift arbitrarily between runs, false positives on routine international purchases remain high, and

Natural language vagueness is interpreted inconsistently across invocations and analysts, while calibration requires a stable mapping from evidence to label. Vague confidence filtering does not define what counts as reportable versus skippable, so the model has no decidable boundary and errs on the side of fluency.

Boundary. The boundary is low-stakes summarization where vague guidance plus human tolerance is acceptable, but the exam treats any classification that drives routing, severity, or compliance as requiring concrete criteria. The nearby opposite case where vague language appears to help is the small prompt addition do not guess; only decide cases the rules clearly cover, which

Recurring specifics. Phrases that recur include only report high-confidence findings that leaves false positive rate unchanged, flag any transaction that looks suspicious that yields half ordinary transfers, and CRITICAL means immediate risk that still produces 40 percent over-flagging versus HIGH. Fixes that recur are concrete categorical criteria

Wrong answers written against this rule

Proposal. add a confidence score and filter below 0.8 or 0.9.

Why it attracts. threshold seems actionable.

Why it fails. the underlying miscalibration remains, so confident wrong flags still pass.

When it would be right. only after calibration per Rule 4.

Proposal. raise temperature to explore wider reasoning and converge.

Why it attracts. more reasoning feels more accurate.

Why it fails. temperature does not fix undefined decision criteria.

When it would be right. never for this.

How the same rule gets re-asked
  • Variants mutate the domain, the number of tiers, and the exact vague phrasing, but the contrast stays prose definition versus checkable criteria with examples. The exam rewards criteria every time.
R19

Scarce reviewer capacity must be spent on calibrated uncertainty where human judgment changes the outcome

With fixed reviewer headcount, for example five analysts handling roughly 400 cases a day or a four-person team that is the bottleneck, the router orders the queue by calibrated uncertainty and by consequence, sending low calibrated-confidence fields, ambiguous or contradictory source documents, and not-yet-validated segments to humans first. High-confidence validated segments flow through with sampling,

Human review changes downstream truth only where the model is likely wrong or where the tested material is genuinely contestable. Spending capacity on already-strong easy fields has low error-catch per unit time, while spending it on weak fields such as totalAmount on scanned receipts or on contradictory evidence such as an undamaged photo paired with a

Boundary. The boundary is coverage of the automated tier: even with perfect uncertainty routing the team must still sample high-confidence traffic for drift, as that sampling is measurement rather than prioritized catching. The nearby opposite case where priority flips is safety-critical completeness: a rare severe omission such as a missing anticoagulant may need dedicated census review

Recurring specifics. Capacity numbers that recur include teams that can review 5 percent of volume while 35 percent is flagged, queues of 400 cases per day, and four reviewers as a bottleneck. Routing signals that recur are low model confidence fields, ambiguous or contradictory source documents, and

Wrong answers written against this rule

Proposal. spread reviewer capacity evenly across all extractions or categories.

Why it attracts. feels fair.

Why it fails. average allocation starves the high-uncertainty tail where errors cluster.

When it would be right. only when error rate is proven uniform, which is never the tested premise.

Proposal. route high-priority entity types regardless of confidence, such as all financial figures.

Why it attracts. importance aligns with impact.

Why it fails. certainty still matters; a high-confidence figure from a clean invoice needs less scrutiny than a low-confidence figure from a smudged scan.

When it would be right. when paired with calibrated uncertainty as a combined risk score, not alone.

How the same rule gets re-asked
  • Mutations change whether uncertainty is presented as field confidence versus contradictory evidence, and whether the queue is described as priority queue versus retry pool. The correct behavior remains prioritizing by uncertainty after calibration.
R20

Uniform, chronological, or evenly spread review allocation wastes capacity

Allocating review as a fixed percentage regardless of confidence, document type, or field, or serving the queue in submission order, treats every extraction as equally likely to be wrong. The design therefore spends reviewer time confirming correct high-confidence cases while leaving insufficient time for the uncertain cases that actually need judgment, with the measurable effect

Error concentration is non-uniform by definition in heterogeneous pipelines. An even distribution has expected catch rate equal to the baseline error rate, while a priority distribution has catch rate elevated to the conditional error rate given low confidence or rarity, which the material quantifies as a large multiple.

Boundary. This rule does not contradict the need for stratified sampling of high-confidence extractions; that sampling is uniform within each stratum for measurement, not uniform across all extractions for prioritized catching. The nearby opposite case where even spread would be right is speculative: only if the

Recurring specifics. Phrases that recur include randomly sample 10 percent regardless of confidence, review every extraction with the same depth so governance remains uniform, and serve the next in chronological order. The teaching contrast recurs between a best-effort 2 percent uniform review that misses concentrated failures and

Wrong answers written against this rule

Proposal. escalate cases randomly so reviewers receive a representative sample without considering impact.

Why it attracts. unbiased coverage.

Why it fails. it ignores where catching has value.

When it would be right. as a measurement control per Rule 8, not as the priority policy.

How the same rule gets re-asked
  • Variants test whether the uniform proposal is framed as random sampling versus even spread versus chronological queue, or as giving business users choice after seeing the outcome. All are punished for the same reason.
R21

Confidence handling must be tiered by bands, not a binary reliable versus unreliable split

A tool that returns confidence: 0.0 to 1.0 with a single cutoff at 0.5 that treats everything above as reliable collapses the spectrum. Tiered handling defines explicit bands such as low, medium, and high with different downstream actions per band, for example auto-clear high calibrated-confidence extractions, hold medium for a second look or deterministic check,

Binary splits lose the information that the middle range is where most trade-offs live. A 0.51 that auto-executes an incorrect action is the canonical failure: just above the midpoint is still weak for many fields.

Boundary. The boundary is when downstream systems genuinely have only two actions, such as approve or route. Even then the policy should define what confidence qualifies for the approve path in calibrated terms, and a third logging or deferred-review path is often the tested safer design.

Recurring specifics. Numbers that recur include 0.5 as the naive global threshold, 0.51 as the failure example, and 0.4 low certainty on a subagent result where the coordinator must ask for a second opinion. Phrasing that recurs is binary confidence thresholds lose nuance; tiered handling strategies with different actions per band are more robust.

Wrong answers written against this rule

Proposal. raise the single threshold from 0.5 to 0.8 to fix the 0.51 error.

Why it attracts. retains binary simplicity.

Why it fails. it still collapses the spectrum and leaves the next edge case just above the new cutoff vulnerable.

When it would be right. only after defining bands and proving the top band is uniformly low risk.

Proposal. remove the confidence field to prevent misuse.

Why it attracts. eliminates misrouting.

Why it fails. discards the signal entirely rather than making it actionable.

When it would be right. never when confidence can be calibrated.

How the same rule gets re-asked
  • Mutations change the domain from financial transactions to voice transcription to subagent coordination, but the contrast remains binary at 0.5 versus tiered bands. The tiered answer wins regardless of domain.
R22

Oversight must surface decisive evidence, contradictions, and fact-level attestation, not a generic approval checkbox

Effective human-in-the-loop design presents the reviewer with source-linked passages, flagged contradictions and missing evidence, risk-weighted depth, and a requirement to attest to the specific facts relied upon rather than clicking a blanket approval. The interface highlights the decisive evidence passages for serious classifications, estimates, or

A generic checkbox quickly becomes ceremonial when routine accuracy is high: pilot data shows approvals near 99.7 percent in under three seconds while clinicians read only the first line and a severe omission slips through. Without evidence surfacing, reviewers cannot see the contradictory income record or the later-half chart bundle where miss rate climbs from

Boundary. The boundary is timing: independent-first review that hides recommendations until the reviewer forms a hypothesis can reduce anchoring but delays assistance and removes value on early evidence organization, so the exam rewards it only as a selective control for severe incidents, not as the universal design. The nearby opposite case where a banner stating the

Recurring specifics. Interface elements that recur include source links, contradiction_detected flags, missing-source warnings, decisive evidence passages for reportability or seriousness, and accepted or rejected recommendation with reason. Sampling language that recurs is sample high-risk incidents for independent review and use disagreement patterns to update controls. Failure figures that recur include near-universal approval rates and three-second review times.

Wrong answers written against this rule

Proposal. click an approval checkbox after reading the full summary to create an audit record.

Why it attracts. satisfies human in the loop formally.

Why it fails. it records interaction, not comprehension of critical evidence.

When it would be right. never as the primary oversight control; only as supplemental logging after fact-level attestation.

Proposal. allow automatic use for high-confidence summaries and review only below a threshold.

Why it attracts. scalable.

Why it fails. calibration is not established for fluently omitted contradictions.

When it would be right. only after calibrated confidence plus evidence surfacing, not instead of it.

How the same rule gets re-asked
  • Variants test whether the oversight is moved outside the interface via email, whether routing is confidence-only, or whether a banner disclaimer is offered, all of which are punished. The rewarded design always links evidence to attestation.
R23

Instrumentation with `detected_pattern` and related metadata enables systematic prompt and threshold improvement

A finding or extraction that is later dismissed as a false positive becomes useful only if the system logs what triggered it, for example a detected_pattern such as string concatenation in database query for a code review finding, or a currency format or handwritten estimate pattern for an extraction dismissal. Accumulating dismissals by pattern reveals

Without the triggering pattern the team cannot distinguish a finding that is wrong because the model misread a construct from one that is wrong because the document layout is inherently ambiguous. Confidence alone does not explain why.

Boundary. The boundary is when the pattern itself is unstable: early in deployment the pattern taxonomy may be coarse, so the team should start with a small set of categories and let the detected_pattern values evolve as dismissals accumulate. The nearby opposite case where more pattern detail is harmful does not appear; the exam consistently treats

Recurring specifics. Field names that recur include detected_pattern, detected_patterns array, detected_failure_type, failure_reason, and failure_class. Failure sources that recur include comparison tables, footnotes, and three noisy scanner categories: dual-licensed packages, vendored test fixtures, and first-party modules.

Wrong answers written against this rule

Proposal. increase confidence precision from two to four decimals.

Why it attracts. feels more precise.

Why it fails. it refines the wrong signal.

When it would be right. never for diagnosing why a finding was dismissed.

Proposal. lower the confidence threshold so fewer findings surface.

Why it attracts. reduces volume.

Why it fails. it suppresses both true and false positives while hiding which patterns cause the false ones.

When it would be right. only after pattern analysis shows which patterns to suppress categorically, as in Rule 18.

How the same rule gets re-asked
  • Variants change the domain from code findings to extraction fields to license labels, but the correct field remains detected_pattern and the correct use remains aggregation by pattern for feedback into the prompt.
R24

Threshold tuning must be evaluated by segment-weighted error trade-offs, not by headline movement alone

When a threshold moves, for example from 0.85 to 0.90, the team reports not only the new headline error rate but the per-segment false positive and false negative rates weighted by segment cost, especially for high-consequence classes such as exclusions, life-safety reports, or liability caps.

Aggregate movement can conceal a harmful trade-off: improved recall on exclusions may double false-positive exclusions that delay valid settlements, and a higher global word error rate may look excellent while two lower-volume languages lose emergency instructions. Only per-segment cost-weighted reporting exposes that the gain was bought where harm concentrates.

Boundary. The boundary is executive reporting: a blended score may still be shown for context, but it cannot be the governing limit. The nearby opposite case where a headline improvement would be sufficient is never presented when asymmetric costs are in scope; the exam consistently requires

Recurring specifics. Metrics that recur include exclusion recall, false-positive workload, onboarding delay, underwriter outcomes, tail latency, and estimated financial impact per error class. Guardrails that recur include predefine non-inferiority limits and stop rule for any material increase. Language that recurs is report field-level errors against adjudicated adjuster outcomes before exposing output.

Wrong answers written against this rule

Proposal. run the revised pipeline in shadow mode but compare only total extraction accuracy.

Why it attracts. simpler executive story.

Why it fails. it hides the precise trade-off the shadow was meant to measure.

When it would be right. never when asymmetric costs exist; only when all errors cost the same, which is not the tested case.

How the same rule gets re-asked
  • Variants test whether the metric is framed as precision versus recall versus accuracy, and whether the slice is field, language, or jurisdiction. The tested answer always decomposes by the slice where harm concentrates.
R25

Continuous stratified sampling detects silent drift in the automated tier where fixed test sets cannot

A deployed system can degrade from the mid nineties to the low eighties percent with no code change as document mix shifts, new product lines appear, or a vendor changes template. Continuous stratified sampling of high-confidence extractions with human ground-truth validation measures error rate as a time series per stratum, while monitoring confidence score distributions

Fixed sets age: they cannot contain a format that did not exist at creation, so a drift that affects only a new layout will not move their score. Confidence distributions can remain high while actual accuracy falls, as the tested material notes for gradual drift, so distribution monitoring is not ground truth.

Boundary. The boundary is cost: sampling frequency can be daily or weekly for high-volume pipelines and monthly for low-volume ones, with per-stratum minimums still enforced. The nearby opposite case where a fixed set would suffice is when the deployed population is stationary and the original validation

Recurring specifics. Drift figures that recur include accuracy falling from 94 to 81 percent over six months with no code change, and confidence scores can remain high while accuracy silently drifts. Correct phrase language is implement stratified random sampling specifically of the high-confidence extractions for periodic ground-truth validation versus wrong proposals such as monitor model confidence score

Wrong answers written against this rule

Proposal. monitor overall pipeline error rate.

Why it attracts. reuses existing metrics.

Why it fails. it blends low-confidence and high-confidence tiers and can mask drift concentrated in the automated tier.

When it would be right. only as a supplementary operational signal, not as drift detection.

How the same rule gets re-asked
  • Variants test whether the learner recognises that increasing volume or raising thresholds does not substitute for sampling, and whether proxy signals such as thumbs-down rates alone suffice, which they do not.
R26

Validation must gate downstream ingestion rather than run as an optional parallel job

Validation that flags high-value contracts or reconciles totals must be a required gate before records proceed to the payment ledger or downstream database. If validation is scheduled as a separate job that can fail silently while extraction succeeds, already-extracted records flow downstream unvalidated and a

Parallel optional validation creates a window where the success path bypasses the safety gate. A downstream consumer assumes validated data because validation normally runs, but the one time it is skipped that assumption fails at scale, for example 200 contracts processed overnight with 200 successes

Boundary. The boundary is idempotency and scoping: a gated validation step should validate schemas and idempotency before writes and enforce separate draft and commit credentials, as the NovaPharm safety-database example shows. The nearby opposite case where decoupling is acceptable is never presented for consequential writes; the

Recurring specifics. Phrases that recur include validation should GATE downstream ingestion, if validation does not run, extractions should queue, validation-retry loop success versus pipeline dependency enforcement, and safety database draft versus commit endpoints with draft-only scoped credential. The failure pattern of 200 successful extractions already in downstream recurs as the illustration.

Wrong answers written against this rule

Proposal. retroactively manually validate the 200 after they are already downstream.

Why it attracts. repairs the incident.

Why it fails. damage may have already propagated and it does not prevent recurrence.

When it would be right. as immediate incident response, not as architectural design.

Proposal. merge validation into the extraction job so they always run together.

Why it attracts. co-location seems to enforce gating.

Why it fails. coupling does not guarantee the validation gate is evaluated before downstream write; an explicit gate is still required.

How the same rule gets re-asked
  • Mutations change the downstream effect from payments to database commits to downstream analytics, but the gate requirement stays constant.
R27

Making fields nullable removes fabrication pressure created by required schema constraints

When a schema marks a field required, the model must produce a value regardless of whether the source contains it, so documents that genuinely omit the field elicit fabricated plausible values such as 2.3 kg for a missing weight or Great product producing invented pros and cons. The fix is to make fields that may

result.json
json
{
  "properties": {
    "weight": {"type": ["string", "null"]},
    "dimensions": {"type": "string"}
  },
  "required": ["dimensions"]
}

Schema enforcement is deterministic while prompt instructions are probabilistic, so a prompt line only extract values actually present loses to the harder required-field constraint. Fabricated values are often high-confidence because the model is confidently constructing something plausible, so confidence filtering does not catch them. Only a schema that permits absence removes the structural pressure.

Boundary. The boundary is genuine optionality: a field that is present in every source should remain required, otherwise downstream consumers must handle unnecessary nulls. The nearby opposite case where empty array [] versus null matters is the pros and cons array fields where an empty array is the idiomatic representation of no items and null on An extracted record for a document that mentions only dimensions then returns weight: null rather than a fabricated value. Field descriptions would reinforce set to null if not explicitly stated.

Recurring specifics. Examples that recur include weight fabricated as 2.3 kg, pros and cons fabricated from Great product, and vendor_contact fabricated when contacts are absent. The anti-pattern phrase required fields force fabrication when data is absent recurs, and the nullable fix is described as removing the field

Wrong answers written against this rule

Proposal. add explicit prompt instructions only extract explicitly stated with placeholder text.

Why it attracts. language fix without schema change.

Why it fails. conflicts with required constraint and is probabilistic.

When it would be right. only as a supplement after the schema already permits null.

Proposal. semantic validation that verifies appears in or can be inferred from source.

Why it attracts. seems rigorous.

Why it fails. plausibly fabricated values are hard to distinguish from legitimate inferences programmatically and detection is after the fact.

When it would be right. as a secondary detective control, not the structural fix.

Proposal. confidence filtering.

Why it attracts. reuses existing mechanism.

Why it fails. fabricated values are often high-confidence.

How the same rule gets re-asked
  • Variants mutate which fields are optional and whether the fix is presented as allow null, allow empty array, or add unclear enum. The tested preference for arrays is empty array over nullable, while scalars use nullable.
R28

Rare severe failures require an oversampled challenge set and separate thresholds, not a larger average sample

When the greatest harm comes from rare events such as serious adverse events, rare high-risk suppliers, high-risk medications, or conflicting specialist instructions, a representative random sample dominated by common non-serious cases stabilizes the average but leaves the rare high-consequence slice statistically invisible. The defensible design builds a stratified, expert-adjudicated challenge set that deliberately oversamples the

Average performance is estimated on common traffic while risk is driven by rare traffic, so the two require different sample budgets. A large random sample improves the average estimate but does not create evidence where the model has never been tested on the severe slice.

Boundary. The boundary is cost and freshness: synthetic examples or text-only OCR slices can fill gaps but lack the noise, handwriting variation, and unusual combinations of operational records, so they are complements, not substitutes. The nearby opposite case where overall accuracy would suffice is when rare

Recurring specifics. Slice definitions that recur include rare serious events, multilingual narratives, duplicates, contradictions, incomplete evidence; high-risk drugs, cross-source conflicts, recent dose changes; region and source-quality for supplier screening; language, disability interaction, literacy pattern, policy-conflict type, and harm severity for accessibility. Phrases that recur include stratified, expert-adjudicated set and no critical subgroup falling below the approved floor.

Wrong answers written against this rule

Proposal. increase the routine-case sample until overall completeness exceeds 98 percent then monitor rare events after launch.

Why it attracts. improves a headline.

Why it fails. it still does not create evidence for absent rare patterns.

When it would be right. only after the challenge set has been built and the rare slices already clear their own thresholds.

Proposal. generate synthetic examples as the primary benchmark.

Why it attracts. privacy and scale.

Why it fails. optimistic noise and structure give a false sense of coverage.

When it would be right. as gap filler alongside real stratified coverage.

Proposal. report one aggregate accuracy across English and Spanish as fairness.

Why it attracts. language parity feels like fairness.

Why it fails. it ignores screen-reader and low-literacy and policy-conflict slices.

How the same rule gets re-asked
  • Variants alternate which rare slice is the focus, but the structure stays stratified plus oversampled severe with separate thresholds rather than a single blended rate.
R29

Risk-aware routing combines severity, uncertainty, rarity, and random coverage because no single signal suffices

A defensible review policy for high-stakes domains such as bridge deterioration, where false negatives are far costlier than extra review and confidence is poorly calibrated for rare damage, allocates capacity across predicted severity, calibrated uncertainty, rare-pattern signals, and a small random sample for blind-spot detection,

Each single signal has a blind spot: highest-severity alone lets a false negative that appears lower-ranked pass; lowest-confidence alone misses rare severe patterns that receive unjustifiably high confidence; expert-interest alone is unauditable and may overrepresent memorable cases; random alone is inefficient. Combining them with documented

Boundary. The boundary is documentation of the weighting: the policy must state how each signal contributes and how the random sample size is chosen, so sampling choices are auditable. The nearby opposite case where a single signal would be rewarded is never the bridge or high-consequence framing; the exam explicitly punishes review only the lowest-confidence 15

Recurring specifics. Reasons that recur include predicted severity, uncertainty, rare-pattern signals, and random sampling, then recalibrate from reviewed outcomes. Trap language that recurs is review only the lowest-confidence 15 percent because uncertainty sampling concentrates effort and review only the highest predicted severity cases because risk is concentrated there, both marked as insufficient alone.

Wrong answers written against this rule

Proposal. let inspectors choose whichever reports they find interesting.

Why it attracts. expert intuition surfaces novel patterns.

Why it fails. unauditable coverage and no guarantee that highest-risk or least-understood reports are seen.

When it would be right. as a supplemental escalation channel, not the governing policy.

Proposal. pick one main signal and add a thin random slice only when volume allows.

Why it attracts. simpler.

Why it fails. the random slice for rare high-risk patterns is the detection instrument, not an optional add-on.

How the same rule gets re-asked
  • Variants change whether the random slice is framed as random sampling or periodic audit, and whether reconfirmation is from a second independent model, but the four-signal combination remains constant.
Extraction system where 97 percent aggregate hides three failing strata
A production walkthrough with the reasoning chain made explicit.

A financial operations team processes 50,000 vendor invoices per month. The report shows 97 percent overall accuracy and the team lead proposes automating all extractions where model confidence exceeds 95 percent to reduce review cost. The compliance officer investigates the per-type breakdown and reveals the aggregate trap: standard invoices at 99.5 percent on dates, 98.2 percent on amounts, and 97.8 percent on names, handwritten receipts at 60.1 percent on dates, 55.3 percent on amounts, and 71.2 percent on names, scanned PDFs at 72.4 percent on dates, 69.8 percent on amounts, and 80.1 percent on names, and international formats at 45.2 percent on dates, 52.1 percent on amounts, and 63.4 percent on names. The aggregate looks excellent because standard invoices dominate volume, but three types have unacceptable accuracy hidden by the weighted average. Automating high-confidence extractions on handwritten receipts at 95 percent confidence would let through errors at 40 to 55 percent rates.

The validation sequence proceeds in order. Per-type and per-field accuracy is measured across a labelled validation set of 5,000 documents covering standard, edge case, and adversarial examples with known correct extractions for date, amount, and name. Confidence scores are calibrated against the labelled set and reveal that 0.95 confidence on a standard invoice date field means 99.2 percent actual accuracy while 0.95 on an international invoice amount field means 56 percent. The same score means different things for different combinations. Calibrated thresholds are set per stratum so standard invoice dates at 0.90 calibrated confidence route to automation with 99 percent or higher accuracy while international amounts at 0.95 still route to human review because 56 percent is unacceptable for financial operations.

A 5 percent stratified sample is drawn from each stratum including high-confidence automated extractions proportional to volume and routed for ongoing verification. The reviewer queue is dynamically ordered by uncertainty so international amounts with 0.95 confidence outrank standard invoice dates with 0.99 confidence because the calibrated uncertainty is higher, and reviewers work top down where the greatest judgement is needed rather than re-verifying easy high-confidence items. Human review is reduced only on segments that demonstrate consistent validated accuracy over multiple sampling rounds, enforced as a workflow gate where a segment cannot be automated until all five steps have been completed and signed off.

Six months later per-type accuracy has improved measurably. International invoice amounts climbed from 56 percent to 71 percent after calibration data showed that amount extraction on international formats needed different few-shot examples. Standard invoice dates remained above 99 percent. Stratified sampling caught two novel error patterns that would have gone unnoticed with low-confidence-only sampling: a date format confusion on a new supplier's invoices and an amount extraction bug on credit memos. Without high-confidence sampling those automated errors would have reached downstream business processes before being caught.

Distinctions that decide answers

ThisNot thisHow to tell them apart
Aggregate accuracyPer-type and per-field accuracyAggregate is a single number across all documents such as 97 percent. Per-type and per-field is a matrix by document type and field type. The aggregate hides segment failures, the matrix exposes them. Validate by matrix before automating.
Stratified sampling proportional to volumeUniform samplingStratified selects proportionally to volume in each stratum, uniform selects the same number from each stratum regardless of volume. Stratified captures the actual error distribution, uniform can miss high-volume strata or over-sample low-volume ones.
High-confidence samplingLow-confidence-only samplingHigh-confidence includes automated extractions in the verification sample. Low-confidence-only verifies only items already routed to human review. High-confidence sampling catches novel patterns in automated extractions, low-confidence misses them entirely.
Raw model confidenceCalibrated confidenceRaw is the model's self-reported 0 to 1 score. Calibrated maps that score to actual accuracy via a per-stratum curve built from labelled data. Raw is unreliable for routing, calibrated is reliable.
Even reviewer distributionDynamic priority queueEven distribution spreads reviewer time across all extractions. Priority queue orders the backlog by uncertainty serving highest-uncertainty first. Even wastes time on easy items, priority concentrates judgement where it matters.
Static single thresholdCalibrated per-stratum thresholdStatic uses one cutoff across all extractions. Calibrated per-stratum uses different cutoffs per field-document combination. Static lets through errors on segments where the same score means lower accuracy.

Traps

Use 97 percent aggregate to automate all high-confidence extractions

The tempting answer. Cite the excellent aggregate and the concrete savings from automating the 95 percent confidence band because the number feels like proof.

Why it fails. Aggregate metrics hide per-type performance. 97 percent overall can mean 40 percent accuracy on specific document types such as handwritten receipts and international invoices, so automating that band lets through errors at very high rates on those segments.

What is correct. Validate by document type and field segment building a per-stratum accuracy matrix and gate automation per cell, never by the aggregate number.

Sample only low-confidence extractions for human review

The tempting answer. Focus sampling on low-confidence items because they carry apparent uncertainty and feel like the risky set.

Why it fails. Low-confidence items are already routed to human review. High-confidence items are automated and not otherwise reviewed. If a novel error pattern affects the automated band, only stratified sampling that includes high confidence will detect it.

What is correct. Sample from all confidence bands proportionally, including the high-confidence automated tier, as a permanent feature.

Use raw model confidence scores without calibration

The tempting answer. Route by raw 0 to 1 scores because they feel quantitative and actionable without extra work.

Why it fails. Raw scores are not calibrated. 0.90 on dates might mean 94 percent actual accuracy while 0.90 on amounts means only 82 percent. Using raw scores treats them as comparable across field types when they are not, so the same threshold means different risk per field.

What is correct. Calibrate using labelled validation sets and build per-stratum confidence curves, then set per-stratum thresholds from those curves.

Spread reviewer capacity evenly across all extractions

The tempting answer. Distribute evenly because equal distribution feels fair and simple to operate.

Why it fails. Even distribution wastes time re-verifying high-confidence items the model handles well while leaving insufficient capacity for uncertain items that actually need human judgement.

What is correct. Use a dynamic priority queue ordered by uncertainty serving highest-uncertainty items first and reordering as new extractions arrive.

Treat calibration as a one-time setup activity

The tempting answer. Calibrate once on the initial labelled set and consider the thresholds settled because the initial validation felt thorough.

Why it fails. Accuracy drifts as documents age, schemas change, or new edge cases emerge. A one-time calibration becomes stale and novel patterns go uncaught.

What is correct. Maintain calibration continuously via ongoing stratified sampling that captures novel error patterns and adjust thresholds from production-traffic outcomes.

Assume automation can fully replace human review on validated segments

The tempting answer. Remove human review entirely on segments that previously validated well because the numbers look excellent and the savings are immediate.

Why it fails. Novel error patterns can emerge even on previously excellent segments, such as a new supplier date format or a credit memo structure the validator has not seen.

What is correct. Keep stratified sampling of automated extractions as a permanent guard, with a gated sequence that requires measurement, calibration, thresholds, and sampling before review is reduced on any segment.

Going deeper than the task statement
Adjacent material that shows up in harder scenario questions.
Objective confidence methods over self-report

Self-consistency checks, hedging detection, log-probability analysis, and meta-cognitive prompting that asks what would need to be verified are each more reliable than asking the model to rate itself.

Confidence Scoring and Uncertainty Handling
Golden dataset, red-teaming, and stratified metrics for evaluation

A golden dataset with expected outputs guards regression across prompt or schema changes, red-teaming deliberately targets known failure modes, and stratified metrics reports expose per-type and per-field results rather than a single aggregate.

Testing AI Systems
Multi-stage validation pipeline that feeds calibration

Schema validation for structure, semantic validation for arithmetic and consistency, and business-rule validation for domain violations produce structured errors that feed into the confidence calibration data.

Validation Pipelines
Build it
Prove per-stratum calibration beats aggregate automation
  1. Create a mock extraction system that outputs field-level confidence per field for four document types of invoices, receipts, scanned PDFs, and international documents, with noticeably different confidence distributions where standard invoices report higher confidence and international and handwritten report lower.
  2. Build a labelled validation set of 200 documents with 50 per type and ground truth for date, amount, and name, covering standard, edge case, and adversarial examples within each type.
  3. Implement a calibration module that bins predictions by reported confidence and computes actual accuracy per bin, producing a per-stratum curve where 0.90 confidence on standard invoice dates means 99 percent or higher while the same score on international amounts means roughly 80 percent.
  4. Build stratified random sampling that selects high-confidence extractions for ongoing verification, sampling proportionally across all document types and confidence bands including the high-confidence automated tier.
  5. Build a dynamic priority review router ordered by uncertainty that reorders as new extractions arrive and serves the next highest-uncertainty item to each available reviewer, never in chronological order, and enforce the five-step validation sequence gate before automating any segment.

Verify. Per-stratum measurement exposes the failing segments an aggregate hides, calibration makes the same confidence score mean different routing per field, and the priority queue concentrates limited reviewers where misrouting would be most expensive.

Answer real questions on this task
8 exam-style questions written against the rules above. You get the reasoning and a note on every wrong option after each answer.
Task 5.616 min

Information Provenance and Multi-Source Synthesis

Carry five-field claim-source mappings through every merge, preserve conflicts with both values, and render by content type so gaps are never mistaken for relevance.

What you need to know

Information provenance, knowing where every claim comes from and how confident you should be in it, distinguishes a research system that produces trustworthy outputs from one that produces plausible-sounding fiction. The exam tests how attribution survives or dies through multi-agent synthesis pipelines, how to handle conflicting sources, and how temporal context prevents false contradictions. Without provenance the final output cannot be verified, cited, or audited, and without deliberate preservation the pipeline destroys it on first paraphrase.

Every finding in a multi-agent research system must carry its provenance as a structural guarantee, not optional metadata. Each finding must include five elements: the claim as the specific assertion, the source URL where the information was found, the document name as the title of the source document, the relevant excerpt as the specific passage that supports the claim, and the publication date when the source was published or the data was collected. Without all five the finding cannot be verified. The application should reject findings with missing fields at the source so discipline holds.

The critical challenge is that attribution dies during summarisation. When a synthesis agent combines findings from multiple subagents it naturally compresses and paraphrases. Without explicit instructions to preserve claim-source mappings the synthesis produces statements such as investment in renewable energy has grown significantly with no amount, no source, and no date. The information was correct but the attribution was destroyed. Downstream agents must explicitly preserve and merge claim-source mappings through synthesis: subagents output findings in the structured claim-source format, the synthesis agent is instructed to maintain those mappings when combining findings, and the final output includes inline citations or a structured reference section that traces each claim to its source.

Conflict handling is the second critical pattern. When two credible sources report different statistics for the same measure the wrong approach is to arbitrarily select one value. Selecting the more recent source, averaging the values, or picking from the more authoritative publisher all destroy information and present false certainty. Source A reporting 12 percent growth and source B reporting 8 percent are both credible and the difference may reflect different reporting periods or methodological approaches. The correct approach is to annotate with both values and full source attribution letting the consumer decide, such as market growth estimates vary by source with 12 percent from the IEA World Energy Report published June 2024 using 2023 calendar year data and 8 percent from the Bloomberg NEF Annual Review published March 2024 using July 2022 to June 2023 data, with a note on the possible explanation.

Temporal awareness explains many apparent conflicts and is the third pattern. Different publication dates explain different numbers without contradiction. Source A published 2023 reporting 8 percent growth and source B published 2024 reporting 12 percent are not contradictory but a trend where growth accelerated. Require publication and data collection dates in all structured outputs, because without temporal context valid trends get misread as data quality issues and the synthesis agent may incorrectly flag or suppress findings that are actually consistent. Content-appropriate rendering completes the discipline: financial data is best as tables where numbers, comparisons, and trends are scannable, news and current events read naturally as prose for narrative and cause and effect, and technical findings are clearest as structured lists for hierarchy. Synthesis should not flatten everything into a uniform format, and attribution preservation through every step from research to analysis to synthesis to report generation must be explicitly required in the synthesis agent's prompt with coverage annotations that mark well supported, limited, conflicting, and unavailable areas.

The five-field mapping and the synthesis merge that preserves it

The structured claim-source mapping is a JSON object with five required fields: claim, sourceUrl, documentName, relevantExcerpt, and publicationDate preserved as an ISO 8601 string. The schema should be enforced at the boundary where subagent findings enter the pipeline, rejecting any finding with a missing field so provenance discipline holds at the source rather than being recovered later.

The synthesis merge takes two arrays of claim-source mappings and returns a unified array where every original mapping is preserved. Related findings are grouped by topic but source attribution is not collapsed, so each merged topic carries the union of its source mappings and every claim remains traceable to its origin. The prompt must contain an explicit instruction that every claim in the output must be traceable to a specific source and that mappings are preserved through the merge process, because summarisation actively destroys attribution unless instructed otherwise.

Conflict preservation with full attribution and temporal grouping

The conflict handling schema extends the mapping with detection and resolution fields. A typical conflict object carries field name, conflictDetected true, a values array with each entry holding value, source, and context such as audited financial statements for fiscal year ending December 2023 versus preliminary unaudited figures for calendar year 2023, and a possibleExplanation field noting likely reasons such as audited versus preliminary or different reporting periods or methodology. Completing analysis with conflicts intact is the rule: the analysis agent returns the structured conflict without resolving it, because the resolution decision belongs to the coordinator or consumer with full information.

Publication dates make correct interpretation possible by distinguishing a trend from a contradiction and by enabling a changelog-aware synthesis pattern where a newer changelog supersedes older documentation. The synthesis merge groups findings by date when temporal context matters, surfacing trends across publication periods rather than treating different dates as conflicting. Coverage annotations close the output with each topic area marked as well supported where multiple sources agree, limited with single source or partial coverage, conflicting where both values are preserved with attribution, or unavailable where no source was retrieved, with failed topics explicitly noting the failure type and attempted alternatives.

Documented enforcement: citations and search result blocks

Before hand-rolling any of the above, check whether the content can enter the request as a document or a search result, because the API then enforces attribution for you. Setting citations enabled true on a document content block makes the API chunk the document, return structured citation objects on the text that draws on it, and extract the supporting passage as cited_text. The documented advantages over asking for citations in the prompt are concrete: cited_text does not count toward output tokens, citations are guaranteed to be valid pointers into the supplied document rather than plausible-looking references, and evaluation shows better selection of the relevant quote. The one constraint to remember is that citations must be enabled on all documents in a request or none, and only text citations are supported.

For your own retrieved content, the search result content block is the matching surface. Its shape is type search_result with a required source, a required title, and a required content array of text blocks, plus optional citations and cache_control. The source does not have to be a URL, and a stable internal identifier such as kb://article-1234 is explicitly acceptable, which is what makes this usable for an internal knowledge base with no public address. Search results can arrive either as the return value of your own tool or as top-level user content for pre-fetched material, they hold text only, and every search result in a request must use the same citation setting. Support covers all active models except the oldest Haiku generation and no beta header is required.

That gives a clear division of labour. Where content arrives as a document or a search result, the API owns the claim-to-source binding and you should not reimplement it in a prompt. Where content arrives some other way, as tool output that is not a search result, as a computed figure, or as a subagent's own synthesis, no API feature is watching, and the five-field mapping enforced at the ingestion boundary is what keeps attribution alive. Both cases still need application code to carry attribution through a multi-step merge, because nothing in the API prevents a downstream synthesis step from paraphrasing away a citation it was handed.

Content-appropriate rendering and per-source attribution

Financial data renders as tables with columns for year, value, source, and publication date so comparisons are immediate. News findings render as prose paragraphs for narrative context and chronological development. Technical findings such as architectural patterns, API specifications, and configuration options render as bulleted or numbered lists with clear hierarchy. A rendering function can detect content type by topic label or schema marker and apply the appropriate format, which the exam tests by checking that synthesis does not flatten everything into one format that degrades comprehension.

Per-source attribution extends beyond RAG findings to the six context sources: external knowledge carries source URLs and publication dates, tools carry execution logs and tool identifiers, memory carries storage location and retrieval timestamp, and state carries session identifier and update history. Evaluation adds a provenance check that periodically re-fetches a sample of cited sources and confirms they actually contain the claimed excerpts, catching citation drift where the synthesis agent has paraphrased away from the source, and a streaming protocol that includes citation markers inline so attribution is visible as synthesis arrives rather than only at the end.

Authoritative mechanism reference

The sections below expand the tested rules into the complete documented surface: ownership, lifecycle, version differences, production boundaries, and adjacent mechanisms.

Mechanism reference: 1. Citations on document blocks - the API-owned claim-to-source binding

Where content arrives as a document content block, the documented enforcement point for provenance is the citations feature, not a prompt instruction.

Field placement: citations with {"enabled": true} is set on the document block itself inside the content array of a user message. The block shape is {"type": "document", "source": {...}, "citations": {"enabled": true}} where source carries the document payload. This is the only supported location for the flag. Setting it elsewhere has no effect, and the API validates the constraint that it must be enabled on all documents in a request or on none of them. A request that enables citations on one document and omits it on another is rejected.

Document chunking: documents are chunked internally when citations are enabled. The model cites by selecting specific chunks, and the returned citations are structured objects that point to the chunk rather than regenerated excerpts. This is the structural property that makes citation objects reliable pointers rather than paraphrases.

What is returned: when citations are enabled, the model's response content includes structured citation objects alongside the text. Each citation object carries a cited_text field that is the verbatim excerpt from the supplied document chunk, plus coordinates that locate that excerpt in the document. Because the excerpt is supplied by the API as a pointer into the submitted material, the consumer can verify that the cited text exists at the cited location without text matching or fuzzy search.

Token accounting: cited_text does not count toward output tokens. This is a documented billing-relevant detail and a reason the feature is strictly superior to prompting the model to quote passages, which does consume output tokens for every quoted word.

Validity guarantee: citations are guaranteed to be valid pointers into the provided documents. A citation object will not point to a passage that was not in the submitted document chunks. This is the documented guarantee that distinguishes the feature from prompt-based citation, where the model may invent a plausible but untraceable quotation.

Quality advantage: documentation states that evaluation shows higher quality quote selection when the citations feature is used compared to prompting for citations. This is described as a measurement rather than a stylistic preference, and is the documented reason to prefer the feature over instruction-based approaches.

All-or-none constraint: citations must be enabled on all documents in a request or on none. This is a request-level invariant. Code that conditionally enables citations per document based on content type will be rejected and should instead normalize the decision at the request level before assembly.

Text-only scope: only text citations are supported. Charts, images, scanned maps, and other non-text visual content are outside the mechanism. For high-stakes outputs where visual content matters, the citations feature provides traceability for the text claims but does not cover visual claims, so human review remains necessary.

Streaming and retrieval implications: citations compose with streaming responses, the citation objects arrive in the streamed content blocks. The feature also interacts with the broader context handling where thinking blocks and tool result truncation have their own token accounting, but the all-or-none document constraint is independent of those settings.

Division of labour: where the preceding paragraph holds, namely content arriving as a document block, the API owns the claim-to-source binding and it should not be reimplemented in a prompt. Adding a system instruction such as always cite your sources on top of enabled citations does not create a second guarantee; it adds a probabilistic instruction that cannot repair a structurally discarded binding and may produce inline prose citations that are fragile to downstream parsing. The correct application-side work in this branch is to carry the returned citation objects through any subsequent merge or render step without paraphrasing them away.

Mechanism reference: 2. Search result content blocks - the second API-owned binding

Search result blocks are the second API-owned mechanism for content the system retrieved itself, and they carry attribution for that retrieved content directly in the content block structure.

Block shape: each search result is a content block of {"type": "search_result", "source": ..., "title": ..., "content": [...], "citations": {"enabled": true}} where source, title, and content are required. content is an array of text blocks. Optional fields are citations and cache_control.

Field semantics: source is the source identifier for the retrieved item. Documentation states explicitly that source may be a stable internal identifier rather than a URL, for example kb://article-1234. This is essential for grounding internal retrieval where no public URL exists, and is a point the reference material omits. title is the document title used for display and traceability. content is the retrieved text itself split into text blocks.

Arrival paths: search result blocks may arrive from a tool result or as top-level user content. This duality supports two ingestion patterns: a retrieval tool that returns search results inside its tool_result, and a pre-retrieval step that injects results as user-role content before the generation turn. Both are valid and both respect the same block shape.

Text-only payload: search results hold text only. This is documented as a strict payload constraint, parallel to the document citations limitation to text citations. Structured data rendered as a table should therefore be serialized as text table markup inside the content blocks, so the table's row and column bindings survive as text.

Citation setting uniformity: all search results in a request must share the same citation setting. The API validates this invariant just as it validates the all-or-none rule for document citations. A request that mixes citations: {"enabled": true} on one search result and omits it on another will be rejected. This mirrors the document rule and prevents a partially-cited response where one source's attribution is structurally missing.

Cache control: search result blocks support cache_control per block. This interacts with prompt caching documented for prefix-stable content. Where search results are large and stable across turns, marking them cacheable keeps the prefix stable and avoids re-paying input cost.

Model support: search result blocks are supported on all active models except Haiku 3, with no beta header required. This is documented as a capability availability statement and matters for multi-model deployments that include Haiku 3 in any routing path.

Division of labour again: where content arrives as a search_result block, the API owns the attribution envelope. The application should not reimplement a separate five-field mapping in the prompt for that content. Its remaining job is identical to the document case: carry the source, title, content, and citation objects through every subsequent synthesis and render step without collapsing or losing them.

Mechanism reference: 3. Five-field claim-source mapping - the application-owned binding for everything else

For content that does not arrive as a document or search_result block, the reference material's five-field mapping is the correct and only structural guarantee, and it must be enforced in application code at the ingestion boundary.

Canonical shape: each finding is an object carrying claim, sourceUrl or source_url, documentName or document_name, relevantExcerpt or excerpt, and publicationDate or publication_date or as_of_date or as_of_period. Forensics notes that exact key spelling varies across exam framings but the five conceptual members are stable. Regulated and financial framings add source_record_id or as_of_date for record-level traceability; code-analysis framings replace document coordinates with file_path, symbol, line_range, and evidence_snippet so that each claim is bound to a spatial location. This grounding uses claim, sourceUrl, documentName, relevantExcerpt, publicationDate as the primary spelling and notes compatible variants where relevant.

Why five fields and why every field is required: claim is the assertion being traced, sourceUrl is where the information was retrieved, documentName is the title the consumer will see in rendering so a URL alone is not sufficient, relevantExcerpt is the specific passage that supports the claim so the reader can verify without reopening the whole source, and publicationDate is the temporal qualifier that distinguishes trend from contradiction. Removing any one collapses the mapping to a weaker form: without the excerpt the mapping is a bibliography entry rather than a claim-to-passage link, without the date it will be misread as a conflict when it is actually a time series, and without the document name the trace is opaque to non-technical readers.

Structural property: the mapping is a field binding, not prose. That is why it survives synthesis when it travels as a field and why it dies when it travels as prose. A bullet that says Adoption grew 40% in 2024 (2022 study, p. 12) preserves the fact in text but does not carry the binding as a structural handle the next agent can move atomically. The guarantee is structural, not stylistic, which is why prompt instructions alone cannot substitute for it.

Boundary enforcement: the correct place to enforce the contract is the ingestion boundary where a subagent finishes extraction. Validation must reject a finding with a missing field before it leaves the subagent, not later at synthesis where gaps are harder to attribute. TypeScript types should mark every field as required rather than optional, and runtime checks should raise before returning so the subagent retries or flags the field as explicitly unknown rather than silently emitting a partial record. The honest unknown is a valid output when no genuine source passage supports a value, and it must be flagged as such with a note rather than borrowing support from metadata that is not a source passage. The one narrow allowance is system-owned authoritative metadata that is by design the source of truth, such as a structured record ID passed alongside raw text, which carries legitimate attribution when the system itself is the authority for that field.

Inline prose citations as the nearby opposite case: a citation written inside a sentence such as According to page 12 of the 2024 Annual Report, revenue was 4.2B is text, and any downstream step that parses or rewrites that sentence can strip or misplace it. The forensics evidence names this as a fragile form that dies the moment any step other than a human reads it, and the tested fix is to emit the value paired with its source as structured fields so downstream consumers read value, source, and page by name without parsing language.

Provenance as case facts: in agent loops, verified claim-source mappings belong in the case-facts block, the immutable facts that survive summarisation or compaction. The lesson material names this pattern explicitly and shows case facts carrying source coordinates alongside the claim. Anything the application must keep verbatim has to live outside the summarised region if client-side summarisation is used, and outside the compaction region when server-side compaction is used, as discussed under version and currency.

Example 3 in the required examples inventory below demonstrates the enforcement at the ingestion boundary with a rejected finding that lacks a field. Example 4 then shows the mapping being carried through a merge step that groups by topic without collapsing attribution.

Mechanism reference: 4. Attribution preservation through multi-step synthesis

Multi-step synthesis is the most common place where provenance is destroyed, because the synthesis agent naturally compresses and paraphrases. The reference names this as the critical challenge and the forensics evidence elevates it to a structural law.

The pipeline shape: research subagent collects findings with mappings, analysis subagent evaluates and adds assessment while preserving original mappings, synthesis subagent combines findings from multiple agents by merging mappings, and report generation produces the final output with citations. At each handoff there is a risk of loss, and the loss at synthesis is distinct because the agent optimizes for fluency and conciseness where citation fidelity competes directly with those objectives.

What must be preserved: every claim that reaches synthesis must arrive with its binding intact, whether that binding is a document citation object, a search result attribution, or a five-field application mapping. The synthesis agent is instructed to maintain those mappings when combining findings, and the merge operation must move claims and their bindings together as one object rather than merging text first and then trying to re-attach sources by text matching. Post-processing citation formatters that try to match claim text to source text after paraphrase fail precisely because the synthesized language no longer matches the original, so there is no clean anchor.

Spawn boundaries: spawned subagents inherit no memory. A coordinator that dispatches a subagent through a spawn call must place the complete structured findings, including every claim-source binding, directly into the spawning payload. The subagent begins with only what was placed in its prompt. A summary or prose recap of findings drops bindings and can introduce contradictions the subagent then fills from its own knowledge. The isolation diagram in the multi-agent lesson shows this explicitly as isolated context windows connected only by structured handoff arrows, with no direct channel between workers.

Communication protocol: inter-agent messages that carry provenance must use a structured message format with schema validation, and the receiving agent must treat incoming messages as untrusted data rather than instructions. The isolation lesson documents this as a required practice alongside the orchestrator hub-and-spoke pattern where subagents never communicate directly and all results flow through the orchestrator for validation. The shared memory lesson provides the complementary pattern where agents read and write shared state via an external store such as a database, Durable Object, or cache rather than passing data through each other's context windows.

Caveat survival as part of the same guarantee: when a source describes a figure as preliminary, unverified, or estimated, that framing is part of the provenance and must travel from subagent to coordinator to final report without being laundered into an unqualified fact. The forensics evidence names this as a separate rule precisely because removing the qualifier misrepresents the tested material, and because the qualifier is a property bound to the claim just like the source identifier.

Normalization of source footprint: where a coordinator assembles multiple sources into a single synthesis context, verbose sources and last-appended sources disproportionately influence the output through length-based attention and recency bias. Instructing the synthesizer to weight all sources equally is probabilistic and does not overcome token-volume attention. The documented structural fix is to normalize each source to a fixed-length summary before synthesis so every source has an equal footprint, and to randomize or importance-weight the order of appended results. This is distinct from per-source citation preservation but interacts with it because over-representation can hide the presence of a conflicting binding.

Failure modes that remain even with API enforcement: even when the API provides citation objects for documents and search results, a downstream synthesis step can still drop them if the application re-prompts with only the synthesized prose. The division of labour statement from the opening applies here: application code must carry citation objects through the merge step in both branches, because nothing in the API prevents a second model call from paraphrasing them away. The worked examples below show the carry-through pattern for both the document and the non-document branches.

Mechanism reference: 5. Conflict handling - preserve both values and never silently select

When two credible sources report different statistics for the same measure, the tested judgement is to preserve both values with full attribution, never to average, pick the newer, pick the more authoritative, or omit both.

Correct rendering: the reference gives the canonical markdown for market growth estimates varying by source, listing each rate with its source and reporting period and noting that the difference may reflect different periods and methods. This rendering preserves the full picture and lets the consumer decide which figure is relevant to their needs. Averaging manufactures a number no source supports, selecting one destroys information, and presenting false certainty is a trust violation.

Structured conflict object: the reference also gives the canonical JSON shape for a field-level conflict carrying conflictDetected, a values array where each entry has its own source and context, and a possibleExplanation that notes temporal or methodological differences. This is the correct machine-readable counterpart to the markdown rendering and satisfies the required example for this grounding. The object must preserve both disagreeing values with their own attribution, context, and a possible explanation instead of selecting one. Forensics Rules 6 and 23 require the same shape as the analysis-subagent completion contract: complete the analysis with both figures plus an explicit conflict flag, then let the coordinator reconcile.

When not to preserve both: the precise boundary is whether a genuine supersession signal exists. When one document explicitly supersedes another, such as a regulatory amendment, a dated in-force provision, or a page titled NEW - 2024 that replaces an older version, applying the most recent in-force provision and noting what it replaced is the correct move. The same applies when the task explicitly asks for the current best estimate and the dates differ; flagging the temporal difference and reporting the most recent period figure as the current estimate is acceptable provided the older figure is not hidden. Absent that signal, preserving both is mandatory. This fork between Rule 5 and Rule 29 is one of the hardest decisions on this task, and grounding must state it as a signal check rather than a blanket recency rule.

What average and discard variants look like as distractors: averaging blends numbers into an unsupported mid-point, for example blending 12% and 8% as 10% with a footnote about variance. Discarding older data whenever a newer figure exists is diagnosed in the forensics as destroying legitimate findings throughout the report, particularly for trend analysis where the older point is the explanation for the newer one.

Analysis versus synthesis responsibility: the analysis subagent completes its work with both figures plus an explicit conflict flag and a possible explanation. It does not apply a credibility heuristic or terminate the pipeline. The coordinator owns reconciliation because it has broader context about scope, definitions, and research goals, and it can escalate with decision-ready context when a human must decide.

Escalation completeness when a human must decide: the forensics evidence adds that an escalation must hand off decision-ready context, not merely a flag. The escalation bundles research topic, specific disputed claims, source citations, analysis already performed, and a recommended reviewer decision, so the reviewer continues where the system left off rather than redoing the work.

Mechanism reference: 6. Temporal handling - date as qualifier, supersession as the narrow exception

A publication or collection date is a qualifier on a claim, not a ranking key for truth. This is the single sentence that prevents two separate error families.

Date semantics: publicationDate, publication_date, source_date, data_collection_period, as_of_date, and as_of_period all travel with the claim as a descriptor of when the underlying data was measured. Synthesis reads the date to interpret the figure, not to discard the older one. Two figures from different years describe a trend, two figures from the same period from different sources describe a conflict, and the date is the only reliable signal that distinguishes the two.

Time-series interpretation: the forensics evidence documents the time-series rule explicitly. When the same metric appears at different periods, for example wearables TAM at 18B in 2021 versus 31B in 2024, the structured as_of_period field is what makes temporal interpretation possible. Without it the same metric with two values looks like a contradiction. With it the values tell a growth story and must not be flagged as a conflict. The reference's temporal awareness section states this correctly: without publication dates the values look contradictory, with dates they tell a story of acceleration.

Recency misuse as a trap: instructing the synthesis agent to always treat the most recent data as authoritative erases trend signal and misreads growth as a reason to discard the past. The forensics documents this trap as a recurring distractor that deletes historical data a trend question requires. The correct heuristic is to keep both and label periods, so downstream analysis can compute year-over-year change and the report can show acceleration.

Supersession handling as the narrow exception: when a document explicitly supersedes another, the system tracks each source's publication date and supersession relationships, applies the most recent in-force provision, and notes what it replaced rather than blending both equally. The contrast with ordinary recency is precise. In a regulatory or policy timeline where a base rule is followed by an amendment that supersedes part of it, presenting the superseded provision alongside the current one as if equal gives outdated advice. Removing earlier documents entirely also loses explanatory history a reader may need. The correct move is to apply the current provision, retain the history, and label what was replaced. In a knowledge base where a question is about an organization's own policy, matching the source type to the question's nature is also a temporal and authority judgement: internal authoritative records outrank third-party speculation for own-policy questions, and citations must reflect that match.

Discarding versus flagging: the tested material is consistent that the pipeline should not automatically discard older data when two numbers differ, should not relegate older data to a historical appendix that severs the trend, and should not add a conflict-resolution agent that drops older data by default. Where the task explicitly requests the current best estimate and the dates differ, selecting the most recent period value is acceptable only when the conflict is flagged and the older figure remains visible as temporal context.

Mechanism reference: 7. Content-appropriate rendering - format by type while keeping attribution attached

Rendering is not a cosmetic afterthought. Different types of content demand different presentation formats, and flattening everything to a uniform prose, table, or list degrades the type it does not suit.

The three-way mapping tested on this task: financial data is rendered as tables where numbers, comparisons, and trends are most readable in tabular format. News and current events are rendered as prose where narrative context, cause-and-effect relationships, and chronological developments read naturally as paragraphs. Technical findings such as architectural patterns, API specifications, and configuration options are rendered as structured lists with clear hierarchy. The reference's financial table with Year, Investment, and Growth columns is the canonical instance of the financial branch.

The rule also has an explicit anti-uniformity form: forcing all content into a single format, all tables or all prose or all lists, degrades readability and comprehension. Standardizing all subagent outputs to JSON keeps an upstream schema but still flattens at render time because financial measurements are not claims and news is not a list. A format conversion layer between subagents and synthesis is a distractor because it does not fix the rendering decision itself.

Attribution rendering within each format: the branching on content type must keep attribution attached. For a table, that means the source and period columns are part of the table, not a trailing bibliography. For prose, that means inline citations or a reference sentence immediately following the claim. For a list, that means each technical item carries its own source annotation rather than a single list-level citation. The document-structuring lesson reinforces this with a concrete structure recommendation: hierarchical headings create navigable sections Claude can reference, and tables let the model reference individual cells explicitly, which makes tables significantly more useful than equivalent prose for structured comparisons. Example 6 below shows the three branches implemented in one renderer while preserving per-claim attribution in each branch.

Two additional rendering and ingestion rules support this mapping. Native table structure and source coordinates must be preserved through ingestion and rendering so values are bound to their row labels and column headers. Flattening a table to prose or discarding headers loses the relationship that makes a number meaningful, and the forensics evidence names the canonical failure as associating 2.4M with the wrong quarter when spatial relationships are lost. Consolidation that merges duplicate records must preserve every original reference so a merged entry can be traced back to each contributing document by specific coordinates, not merely by a summary note.

Labeling synthesis interpretation: when a coordinator draws connections across findings, that interpretive content should be labeled as the system's analysis rather than presented with the same confidence as directly sourced findings. This is a rendering obligation distinct from per-claim provenance.

Mechanism reference: 8. Coverage, null findings, and the limits of traceability

Coverage annotation is a separate mechanism from per-claim provenance and exists to make absence visible. When some sources or feeds are unavailable due to timeout, auth error, or throttling, per-claim provenance says where received content came from but cannot expose a topic area that never arrived. The correct design structures the report with explicit coverage labels marking areas as fully supported, partially supported, or unsupported due to an unavailable source, without dumping raw error text into the report. This is the correct remedy where a feed is throttled and a section would otherwise be silently empty.

A genuine null finding is distinct from a failure and is valid when a subagent searches and finds none, for example pricing stable for two years with no recent changes. The correct output is a successful result stating the null with the date and the sources checked, not an error to retry or a gap to hide. Fabrication to fill the category is a severe provenance violation.

Two further provenance facets for special content: code-analysis handoffs bind each claim to file_path, symbol, line_range, and evidence_snippet so engineers can verify the location, and concatenation of full transcripts or regex parsing for paths are the documented anti-patterns. Citations provide traceability, not truth verification; they show which passages support a claim but do not verify the documents themselves, so safety-critical outputs still need human review and cannot treat cited text as independently verified.

Mechanism reference: 9. Required illustrative examples - six substantial language-tagged implementations

The six examples below are the correct constructions for the exam and for production. Each is complete enough to run or to adapt, names its language, and states its failure boundary and observable output. They evolve as one pipeline so the later examples reuse types introduced earlier.

Example 1 - Document content block with citations enabled and the structured citation objects returned

result.json
json
{
  "role": "user",
  "content": [
    {
      "type": "text",
      "text": "Summarize the close conditions and state which source supports each condition."
    },
    {
      "type": "document",
      "source": {
        "type": "text",
        "media_type": "text/plain",
        "data": "Force majeure: Either party may terminate with 30 days notice. Limitation of liability: Liability capped at contract value. Term: 36 months effective 2026-07-01.\n\nCritical clauses follow in section 4..."
      },
      "title": "Acme-Globex Agreement 2026-07-01",
      "citations": { "enabled": true }
    },
    {
      "type": "document",
      "source": {
        "type": "text",
        "media_type": "text/plain",
        "data": "Amendment 2026-09-15: Section 4 limitation of liability cap increased to 1.5x contract value. Supersedes prior cap in Agreement 2026-07-01 section 4."
      },
      "title": "Amendment 2026-09-15 to Acme-Globex Agreement",
      "citations": { "enabled": true }
    }
  ]
}

Model response content with structured citations (text blocks interleaved with citation objects, as returned by the API):

result.json
json
{
  "id": "msg_01UZ2",
  "role": "assistant",
  "content": [
    { "type": "text", "text": "The agreement's close conditions are:" },
    {
      "type": "text",
      "text": "Termination requires 30 days notice under force majeure.",
      "citations": [
        {
          "type": "cite_document",
          "cited_text": "Either party may terminate with 30 days notice.",
          "document_index": 0,
          "start_char": 18,
          "end_char": 58
        }
      ]
    },
    {
      "type": "text",
      "text": "The current in-force liability cap is 1.5x contract value per Amendment 2026-09-15, which supersedes the original cap at contract value.",
      "citations": [
        {
          "type": "cite_document",
          "cited_text": "Liability capped at contract value.",
          "document_index": 0,
          "start_char": 92,
          "end_char": 124
        },
        {
          "type": "cite_document",
          "cited_text": "cap increased to 1.5x contract value. Supersedes prior cap in Agreement 2026-07-01 section 4.",
          "document_index": 1,
          "start_char": 43,
          "end_char": 125
        }
      ]
    }
  ],
  "usage": { "input_tokens": 2140, "output_tokens": 68, "cited_text_tokens": 42 }
}

What this proves: citations are enabled on every document in the request or the request is rejected, documents are chunked internally and cited_text points to an exact location in the supplied material, the pointer is guaranteed valid, and the cited tokens do not count toward the billed output_tokens. The observable outcome is that the second citation set carries both the original and the superseding excerpt so a downstream renderer can show what was replaced. Failure boundary: only text citations are supported, so a chart or scanned map claim in the same documents would not be citable, and mixing enabled and disabled across documents fails validation.

Example 2 - Search result content block for internally retrieved content using a stable non-URL source identifier

result.json
json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "Draft the pricing and launch sections from the findings below. Keep attribution attached." },
    {
      "type": "search_result",
      "source": "kb://pricing-q4-2024",
      "title": "Internal Pricing Committee Notes Q4 2024",
      "content": [
        { "type": "text", "text": "Recommended list price for Widget X is $42.00 effective Q4 2024. Committee approved on 2024-10-12. Preliminary syndicated estimate notes channel pricing may settle at $38 to $42 pending final review." }
      ],
      "citations": { "enabled": true },
      "cache_control": { "type": "ephemeral" }
    },
    {
      "type": "search_result",
      "source": "kb://launch-brief-2025-01",
      "title": "Launch Brief 2025-01",
      "content": [
        { "type": "text", "text": "Public launch for Widget X scheduled 2025-01-15. CEO statement: we expect strong initial uptake. Regional breakdown: APAC 41% growth, EMEA currency-adjusted +3%, Americas +15%." }
      ],
      "citations": { "enabled": true }
    }
  ]
}

Alternative arrival path from a tool result:

result.json
json
{
  "role": "assistant",
  "content": [{ "type": "tool_use", "id": "toolu_1", "name": "retriever", "input": { "query": "Widget X pricing" } }]
}
result.json
json
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_1",
      "content": [
        {
          "type": "search_result",
          "source": "kb://pricing-q4-2024",
          "title": "Internal Pricing Committee Notes Q4 2024",
          "content": [{ "type": "text", "text": "Recommended list price for Widget X is $42.00 effective Q4 2024." }],
          "citations": { "enabled": true }
        }
      ]
    }
  ]
}

What this proves: source, title, and content are required, source may be a stable internal identifier rather than a URL, search results hold text only, they may arrive from a tool result or as top-level user content, all search results in the request share the same citation setting, cache_control is supported per block, and the mechanism is available on all active models except Haiku 3 with no beta header. Failure boundary: a request that adds a third search result without citations: {"enabled": true} is rejected, and a search result containing binary table cells must serialize the table as text markup inside its content blocks.

Example 3 - Five-field claim-source mapping enforced at the ingestion boundary so a finding with a missing field is rejected

example.ts
typescript
type ClaimSourceMapping = {
  claim: string;
  sourceUrl: string;
  documentName: string;
  relevantExcerpt: string;
  publicationDate: string;
  asOfPeriod?: string;
  caveat?: string;
};

function validateMapping(input: Partial<ClaimSourceMapping>): ClaimSourceMapping {
  const required: (keyof ClaimSourceMapping)[] = [
    "claim",
    "sourceUrl",
    "documentName",
    "relevantExcerpt",
    "publicationDate",
  ];
  const missing = required.filter((k) => !input[k] || String(input[k]).trim() === "");
  if (missing.length > 0) {
    throw new Error(`claim-source mapping rejected: missing required field(s): ${missing.join(", ")}`);
  }
  return input as ClaimSourceMapping;
}

function extractFinding(raw: {
  claim: string;
  sourceUrl: string;
  documentName: string;
  relevantExcerpt: string;
  publicationDate: string;
}): ClaimSourceMapping {
  return validateMapping(raw);
}

const good = extractFinding({
  claim: "Global renewable energy investment reached $495 billion in 2023",
  sourceUrl: "https://example.com/iea-report-2024",
  documentName: "IEA World Energy Investment Report 2024",
  relevantExcerpt:
    "Total investment in renewable energy technologies reached approximately $495 billion in calendar year 2023, representing a 17% increase over 2022.",
  publicationDate: "2024-06-15",
});

let rejected: string | null = null;
try {
  extractFinding({
    claim: "Q3 revenue was $4.2M",
    sourceUrl: "https://example.com/board-minutes",
    documentName: "",
    relevantExcerpt: "Revenue for Q3 2025 was $4.2M",
    publicationDate: "2025-10-12",
  });
} catch (e) {
  rejected = (e as Error).message;
}

function unknownValueField(field: string, attemptedSources: string[]): ClaimSourceMapping {
  return {
    claim: `${field}: unknown - no genuine source passage supports a value`,
    sourceUrl: "unknown: no citable passage",
    documentName: attemptedSources.join("; ") || "no source attempted",
    relevantExcerpt: "No source passage supports a value for this field; flagged as unknown per provenance policy.",
    publicationDate: new Date().toISOString().slice(0, 10),
    caveat: "unknown: no supporting passage",
  };
}

What this proves: every one of the five members is required and a finding with an empty documentName or missing date is rejected before it leaves the ingestion boundary, a genuine unknown is emitted as an explicitly flagged record rather than a borrowed or fabricated citation, and asOfPeriod and caveat ride as qualifiers on the same record rather than as separate prose. The observable outcomes are that good passes, the second call throws with Missing required fields, and unknownValueField produces a flaggable unknown record. Failure boundary: optional fields cannot rescue a missing required field, and system-owned metadata may serve as the source of truth only when the system is by design the authority for that field, otherwise the honest move is still unknown.

Example 4 - Synthesis merge that carries every mapping through and never collapses attribution while grouping by topic

example.ts
typescript
type TopicGroup = {
  topic: string;
  findings: ClaimSourceMapping[];
};

function mergeByTopic(groups: TopicGroup[]): {
  sections: Array<{ topic: string; claims: Array<{ text: string; sources: ClaimSourceMapping[] }> }>;
  citationIndex: ClaimSourceMapping[];
} {
  const citationIndex: ClaimSourceMapping[] = [];
  const sections = groups.map((g) => ({
    topic: g.topic,
    claims: g.findings.map((f) => {
      citationIndex.push(f);
      return { text: f.claim, sources: [f] };
    }),
  }));

  for (const g of groups) {
    if (g.findings.length === 0) continue;
    const ids = g.findings.map((f) => `${f.claim}::${f.sourceUrl}`);
    const dup = ids.length !== new Set(ids).size;
    if (dup) throw new Error(`duplicate claim-source pair in topic ${g.topic}: check ingestion`);
  }

  return { sections, citationIndex };
}

const financialGroup: TopicGroup = {
  topic: "financial-performance",
  findings: [
    {
      claim: "Q3 2025 revenue was $4.2M, up 23% year over year",
      sourceUrl: "https://example.com/q3-financial-report.pdf",
      documentName: "Q3 Financial Report 2025",
      relevantExcerpt: "Revenue for Q3 2025 was $4.2M, a 23% increase year-over-year.",
      publicationDate: "2025-10-28",
      asOfPeriod: "2025-Q3",
    },
    {
      claim: "Board approved $500K expansion for 8 AI/ML engineering hires by 2025-12-31",
      sourceUrl: "https://example.com/board-minutes-2025-10.pdf",
      documentName: "Board Minutes 2025-10",
      relevantExcerpt: "The Board approved a $500K expansion of the engineering team in Q4, with hiring focused on AI/ML roles. Headcount target: 8 new engineers by December 31.",
      publicationDate: "2025-10-12",
    },
  ],
};

const technicalGroup: TopicGroup = {
  topic: "technical-architecture",
  findings: [
    {
      claim: "Tool definitions should be concise and role-scoped; include only tools the worker needs",
      sourceUrl: "https://example.com/multi-agent-context-isolation",
      documentName: "Multi-Agent Context Isolation and Coordination",
      relevantExcerpt: "Each agent has access only to the tools appropriate for its role.",
      publicationDate: "2026-06-10",
    },
  ],
};

const merged = mergeByTopic([financialGroup, technicalGroup]);

What this proves: synthesis groups findings by topic for readability but never merges two bindings into one citation and never drops a source when two claims are combined. Each claim in the merged output maps to exactly the ClaimSourceMapping objects that supported it on the way in, and the flat citationIndex gives the report renderer a complete audit trail. The implementation also checks for accidental duplication of exact claim-source pairs while preserving each group's unique findings, which is the forensics rule for collapsing duplicate source listings without dropping findings. Failure boundary: a merge that concatenates prose first and then tries to reattach citations by text matching loses bindings exactly where paraphrase diverged, and a merge that silently drops a source when two claims are combined violates the per-claim traceability contract.

Example 5 - Conflict object that preserves both disagreeing values with their own attribution, context, and a possible explanation instead of selecting one

example.ts
typescript
type ConflictValue = {
  value: string;
  source: string;
  sourceUrl: string;
  documentName: string;
  relevantExcerpt: string;
  publicationDate: string;
  asOfPeriod: string;
  context: string;
};

type ConflictObject = {
  field: string;
  metric: string;
  conflictDetected: true;
  values: [ConflictValue, ConflictValue, ...ConflictValue[]];
  possibleExplanation: string;
  resolution: "preserve_both_for_consumer";
};

function makeRevenueConflict(): ConflictObject {
  return {
    field: "annualRevenue",
    metric: "annualRevenue",
    conflictDetected: true,
    values: [
      {
        value: "$4.2M",
        source: "Annual Report 2023",
        sourceUrl: "https://example.com/annual-report-2023",
        documentName: "Annual Report 2023",
        relevantExcerpt: "Audited revenue for fiscal year ending December 2023 was $4.2M.",
        publicationDate: "2024-03-15",
        asOfPeriod: "2023-FY",
        context: "Audited financial statements, fiscal year ending December 2023",
      },
      {
        value: "$3.8M",
        source: "SEC Filing Q4 2023",
        sourceUrl: "https://example.com/sec-filing-q4-2023",
        documentName: "SEC Filing Q4 2023",
        relevantExcerpt: "Preliminary unaudited revenue for calendar year 2023 was $3.8M.",
        publicationDate: "2024-02-10",
        asOfPeriod: "2023-CY",
        context: "Preliminary unaudited figures, calendar year 2023",
      },
    ],
    possibleExplanation:
      "Difference may reflect audited versus preliminary figures and fiscal year versus calendar year reporting periods. Publication dates explain part of the gap and should not be read as a ranking of truth.",
    resolution: "preserve_both_for_consumer",
  };
}

type MarketGrowthConflict = {
  field: "marketGrowth";
  conflictDetected: true;
  values: [
    { value: "12%"; source: string; sourceUrl: string; documentName: string; publicationDate: string; asOfPeriod: string; context: string },
    { value: "8%"; source: string; sourceUrl: string; documentName: string; publicationDate: string; asOfPeriod: string; context: string }
  ];
  possibleExplanation: string;
};

function renderGrowthConflict(conflict: MarketGrowthConflict): string {
  const lines = [
    "Market growth estimates vary by source:",
    ...conflict.values.map(
      (v) => `- **${v.value} growth** - ${v.source} (published ${v.publicationDate}, ${v.context}) [${v.documentName}]`
    ),
    "",
    `The difference may reflect ${conflict.possibleExplanation}.`,
  ];
  return lines.join("\n");
}

const growthConflict: MarketGrowthConflict = {
  field: "marketGrowth",
  conflictDetected: true,
  values: [
    {
      value: "12%",
      source: "IEA World Energy Report",
      sourceUrl: "https://example.com/iea-2024",
      documentName: "IEA World Energy Report 2024",
      publicationDate: "2024-06-15",
      asOfPeriod: "2023-CY",
      context: "using 2023 calendar year data",
    },
    {
      value: "8%",
      source: "Bloomberg NEF Annual Review",
      sourceUrl: "https://example.com/bnef-2024",
      documentName: "Bloomberg NEF Annual Review",
      publicationDate: "2024-03-15",
      asOfPeriod: "2022-07 to 2023-06",
      context: "using July 2022 to June 2023 data",
    },
  ],
  possibleExplanation: "different reporting periods and methodological approaches",
};

const supersessionCase = {
  field: "liabilityCap",
  note: "When a genuine supersession signal exists, the newer provision applies and history is retained but not treated as equal.",
  applicableStandard: "Amendment 2026-09-15 supersedes Agreement 2026-07-01 section 4",
  currentInForce: "1.5x contract value",
  supersededValue: "1.0x contract value",
  historyLabel: "superseded: note what it replaced and keep the original citation for audit",
};

What this proves: the analysis subagent completes its work with both figures plus an explicit conflictDetected: true, each value carries its own source URL, document name, excerpt, publication date, asOfPeriod, and context, and possibleExplanation records why the numbers may differ without selecting one. The markdown renderer for market growth carries each attribution on its own line so no citation is lost in formatting. The supersession note is kept as a separate branch with an in-force label and a superseded label, so a reviewer sees what replaced what. Failure boundary: averaging the two revenue figures, picking the more recent, discarding the older, or emitting a single number with a footnote about variance all destroy information and present false certainty. Selecting the newer is correct only when a supersession signal or an explicit current-best-estimate request makes it the narrow exception, and even then the older figure must remain visible.

Example 6 - Rendering step that chooses table, prose, or list by content type while keeping attribution attached

example.ts
typescript
type Renderable = {
  id: string;
  contentType: "financial" | "news" | "technical";
  claim: string;
  sources: ClaimSourceMapping[];
  data?: Array<Record<string, string>>;
};

function renderFinancialTable(item: Renderable): string {
  if (!item.data || item.data.length === 0) return `| Note | ${item.claim} |\n| sourced from | ${item.sources.map((s) => s.documentName).join(", ")} |`;
  const headers = Object.keys(item.data[0]);
  const withAttribution = headers.includes("Source") ? headers : [...headers, "Source"];
  const headerRow = `| ${withAttribution.join(" | ")} |`;
  const sepRow = `| ${withAttribution.map(() => "---").join(" | ")} |`;
  const bodyRows = item.data.map((row) => {
    const enriched = { ...row, Source: item.sources[0]?.documentName ?? "" };
    return `| ${withAttribution.map((h) => enriched[h] ?? "").join(" | ")} |`;
  });
  return [headerRow, sepRow, ...bodyRows, "", `*Source: ${item.sources[0]?.documentName} [${item.sources[0]?.sourceUrl}] as of ${item.sources[0]?.publicationDate}*`].join("\n");
}

function renderNewsProse(item: Renderable): string {
  const sourceNote = item.sources.map((s) => `${s.documentName} (${s.publicationDate})`).join("; ");
  const caveat = item.sources[0]?.caveat ? ` Note: ${item.sources[0].caveat}.` : "";
  return `${item.claim}${caveat} [Source: ${sourceNote}]`;
}

function renderTechnicalList(item: Renderable): string {
  const bullets = item.data
    ? item.data.map((row) => `- **${row.item ?? row.pattern ?? row.option}**: ${row.detail ?? row.description ?? ""}`)
    : [`- ${item.claim}`];
  const sourceNote = item.sources.map((s) => `${s.documentName} [${s.sourceUrl}]`).join(", ");
  return [...bullets, "", `*Technical source: ${sourceNote}*`].join("\n");
}

function renderByContentType(item: Renderable): string {
  switch (item.contentType) {
    case "financial":
      return renderFinancialTable(item);
    case "news":
      return renderNewsProse(item);
    case "technical":
      return renderTechnicalList(item);
    default:
      return `${item.claim} [Source: ${item.sources.map((s) => s.documentName).join(", ")}]`;
  }
}

const financialItem: Renderable = {
  id: "fin-1",
  contentType: "financial",
  claim: "Renewable energy investment 2021 to 2023 with growth",
  sources: [
    {
      claim: "Investment 2021 to 2023",
      sourceUrl: "https://example.com/iea-report-2024",
      documentName: "IEA World Energy Investment Report 2024",
      relevantExcerpt: "Total investment reached approximately $495 billion in calendar year 2023.",
      publicationDate: "2024-06-15",
    },
  ],
  data: [
    { Year: "2021", "Investment ($B)": "366", "Growth (%)": "12%" },
    { Year: "2022", Investment: "423", Growth: "16%" },
    { Year: "2023", Investment: "495", Growth: "17%" },
  ],
};

const newsItem: Renderable = {
  id: "news-1",
  contentType: "news",
  claim: "Offshore wind deployment accelerated in 2024 as procurement auctions cleared at record volumes, with APAC accounting for the largest share of new capacity additions.",
  sources: [
    {
      claim: "Offshore wind accelerated in 2024",
      sourceUrl: "https://example.com/wind-monitor-2024",
      documentName: "Wind Monitor Q4 2024",
      relevantExcerpt: "Offshore wind auctions in 2024 cleared at record volumes led by APAC.",
      publicationDate: "2025-01-10",
      caveat: "preliminary, unverified industry estimate based on auction announcements",
    },
  ],
};

const technicalItem: Renderable = {
  id: "tech-1",
  contentType: "technical",
  claim: "Approved handoff schema for code analysis",
  sources: [
    {
      claim: "Code handoff needs file_path, symbol, line_range, evidence_snippet",
      sourceUrl: "https://example.com/code-analysis-spec",
      documentName: "Code Analysis Spec v4",
      relevantExcerpt: "Each finding must include file_path, symbol, line_range, and evidence_snippet.",
      publicationDate: "2026-02-01",
    },
  ],
  data: [
    { item: "file_path", detail: "Absolute path to the file containing the claim, for example src/api/handlers.ts" },
    { item: "symbol", detail: "Exported symbol or function name that the claim attaches to" },
    { item: "line_range", detail: "Start and end line numbers so an engineer can verify without searching" },
    { item: "evidence_snippet", detail: "Verbatim code excerpt that supports the claim" },
  ],
};

const renderedFinancial = renderByContentType(financialItem);
const renderedNews = renderByContentType(newsItem);
const renderedTechnical = renderByContentType(technicalItem);

What this proves: the renderer branches on contentType rather than forcing a uniform format, each branch keeps attribution attached in the natural way that format supports (table with a Source column and a source sentence, prose with a trailing citation sentence, list with per-section source notes), and caveats such as preliminary unverified estimates travel through to the rendered output without being laundered into unqualified facts. The observable outputs are a markdown table where the financial source is readable from the Source column and the source sentence, a prose paragraph where the news caveat is still visible, and a structured list where each technical item's meaning is preserved alongside its source. Failure boundary: rendering everything as bullets hides financial comparability, rendering everything as prose loses tabular scan speed, and rendering without per-branch attribution collapses traceability exactly at the delivery point the reader needs it.

Ownership map

Ownership determines which layer can create a guarantee and which layer can only obey it. The table states for each provenance obligation which component owns the enforcement.

Provenance obligationOwnerGuarantee and enforcement pointWhat the owner does not do
Bind a claim in a supplied document to a verifiable locationModel plus API: citations on document blockscitations: {"enabled": true} on the document block, document chunking, and structured citation objects with cited_text that are guaranteed pointers into the supplied material. cited_text does not count toward output tokens. Quality of selected quotations is measured as higher than prompt-based citationApplication code does not reimplement this binding with prompt instructions when the content arrived as a document. It carries the returned citation objects forward without paraphrasing them away.
Bind internally retrieved content to its source and titleModel plus API: search_result blockssource, title, and content array required on each search_result, optional citations and cache_control, uniform citation setting across all search results in a request, text-only payload, support on all active models except Haiku 3 with no beta header, arrival from tool result or top-level user contentApplication code does not reimplement a separate five-field mapping for this content via prompts. It normalizes search results to the block shape at retrieval time.
Bind any other finding to its source, document name, excerpt, and dateApplication code at the ingestion boundaryRequired five-field object validated before the subagent returns, rejection on any missing field, explicit unknown flag when no genuine passage supports a value, type-level required fieldsThe model does not create this binding after the fact by reconstructing a likely source. Post-processing citation formatters fail for paraphrased text.
Carry attribution through a merge stepApplication code in the synthesis agent and its orchestratorSynthesis instruction to merge mappings atomically while grouping by topic, never collapsing attribution, preserving every original reference through consolidation, explicit handling of duplicate source listings without dropping findingsThe API does not prevent a second model call from paraphrasing citation objects away. The guarantee lives in the application merge logic.
Complete analysis with both conflicting values plus an explicit flagApplication code in analysis subagentsconflictDetected: true plus full per-value attribution and a possible explanation, owned by the analysis stage. Reconciliation is owned by the coordinator which has broader contextAnalysis subagents do not apply local credibility heuristics or silently resolve. The coordinator does not auto-discard by recency.
Place findings into isolated subagent contextsApplication code at spawn boundaries, SDK primitive scopeSpawned subagents inherit no memory, complete structured findings are injected into the spawning prompt, isolation via separate context windows, structured messages with schema validation, hub-and-spoke via orchestratorThe coordinator does not assume reachability of prior outputs. Workers do not share context windows or forward raw user input.
Qualify every claim with its temporal context and distinguish supersession from recencyApplication code in ingestion and synthesis, informed by source structurepublicationDate or as_of_period on every claim as a qualifier, identification of a genuine supersession signal versus mere recency, consistent application of the in-force provision with a note of what it replacedNo ranking key for truth is derived from date alone. The newer figure does not automatically win.
Render content in the format its type requires while keeping attribution attachedApplication code at report generationTable for financial series with Source column, prose for news with trailing citation, list for technical findings with per-item annotation, preservation of native table headers and row coordinates through ingestion to renderingNo uniform prose, bullets, or raw dump is used for all types. A conversion layer upstream does not substitute for the render-time branch.
Make coverage gaps visible and handle nulls correctlyApplication code at report assemblyExplicit coverage labels for fully or partially supported or unsupported areas, successful null findings stated with dates and sources checked, no fabrication to fill a categoryPer-claim provenance alone is not used to expose missing topic areas. Coverage annotation is the separate mechanism.
Keep provenance through long conversationsModel plus API plus application: compaction, context editing, and memoryServer-side compaction is the documented primary strategy for long-running conversations with trigger 150000, pause_after_compaction, and instructions that replace the default summarisation prompt. Context editing with clear_tool_uses_20250919 and clear_thinking_20251015 and the memory tool memory_20250818 cover finer-grained managementApplication code does not rely solely on client-side progressive summarisation, and it keeps verbatim-critical facts outside summarised or compacted regions via the case-facts pattern.

The floor where a guarantee can be made is the ingestion boundary. If a subagent emits prose without structured fields, no downstream mechanism can reliably reconstruct which passage supported which claim. If a document is sent without citations: {"enabled": true}, the synthesis step cannot recover valid pointers later. The ceiling where a guarantee is easily lost is the second synthesis call that receives only paraphrased prose. Both of those points are application-controlled, which is why provenance in production is primarily an application engineering discipline even where the API provides stronger enforcement at the model boundary.

Version and terminology currency

The version notes in this section use DOC-URLS as the current documented state and record where the reference material and the older lesson phrasing describe superseded defaults.

Host migration: the documentation host docs.claude.com redirects to platform.claude.com, and Claude Code docs now live on code.claude.com. The verified forms for this domain are platform.claude.com and code.claude.com respectively. This grounding cites the platform.claude.com form throughout.

Citations and search results availability: citations on document blocks and search result content blocks are documented without a beta header. Search results are supported on all active models except Haiku 3. The all-or-none document constraint and the uniform citation setting across search results in a request are current invariants.

Context handling currency: server-side compaction is now the documented primary strategy for long-running conversations, not client-side summarisation. The strategy type is compact_20260112, passed in context_management.edits with beta header compact-2026-01-12. The default trigger is {"type": "input_tokens", "value": 150000} and value must be at least 50,000. Optional fields are pause_after_compaction (default false) and instructions which completely replaces the default summarisation prompt. When the threshold is reached the API generates a summary, emits a compaction block, and continues the response. On later requests it drops all blocks prior to the compaction block. The documented support set is Fable 5, Mythos 5, Mythos Preview, Opus 5, Opus 4.8, 4.7, 4.6, Sonnet 5, Sonnet 4.6. This matters for provenance because compaction summarises and verbatim-critical facts must live outside the summarised region.

Context editing is the fine-grained alternative, with beta header context-management-2025-06-27 and two strategies: clear_tool_uses_20250919 and clear_thinking_20251015. Tool result clearing takes trigger, keep, clear_at_least, exclude_tools, and clear_tool_inputs and replaces cleared results with placeholder text server-side before the prompt reaches the model, so the client keeps its full unmodified history and does not sync. Thinking block clearing is model-dependent. Opus 4.5 and later, Sonnet 4.6 and later, Fable 5, Mythos 5, and Mythos Preview keep prior thinking blocks by default and they count as input tokens. Earlier Opus and Sonnet models and all Haiku models strip them automatically when passed back. Setting keep explicitly is required when code spans model tiers.

Cache interaction for these mechanisms is strategy-specific. Tool result clearing invalidates the cached prefix at the clearing point, which is why clear_at_least exists to clear enough to justify the cache write. Thinking block clearing preserves the cache while blocks are kept and invalidates at the clearing point when they are not.

Client-side SDK compaction exists only in the TypeScript and Ruby SDKs via tool_runner with compaction_control, default context_token_threshold 100,000. The Python, C#, Go, Java, and PHP tool runners do not support it and the docs point them to server-side compaction. After a web search the SDK may add cache_read_input_tokens accumulated across internal calls to the visible total, so it can see several hundred thousand tokens when real context is smaller and compact prematurely. The documented workarounds are the token counting endpoint or avoiding client-side compaction with heavy server-side tool use.

Memory tool currency: the memory tool is {"type": "memory_20250818", "name": "memory"}, available on Claude 4 and later, and it is client-side. The model requests view, create, update, and delete operations under /memories and the application executes them against storage it controls, returning a tool_result. The /memories prefix is mapped by the handler onto real storage, the handler must reject paths outside /memories for path traversal protection, and the documented purpose is just-in-time context retrieval so the active window stays focused. This is the documented surface that replaces the reference's purely local scratchpad convention.

Token accounting and measurement: everything in the request counts toward the window, including system prompt, every message including tool results, images and documents, tool definitions, and the output including extended thinking. usage reports the split, and with caching the input count is split across input_tokens, cache_read_input_tokens, and cache_creation_input_tokens, all three counting toward the window. The recommended precise estimate before sending is the token counting endpoint, which is also the mitigation for premature client-side compaction.

Examination term versus product term: the forensics analysis notes that exam framings may use older product names for what is now documented as server-side compaction and may call the persistent verbatim block a case facts block. Candidates should answer with the documented mechanism names where the question tests a named surface and treat the exam guide terminology as equivalent to the current API terms when the meaning matches.

Official versus community divergence

This section states the documentation position, states the community or reference position where it differs, and says which a candidate should answer with and why, as required by DOC-URLS.

Provenance enforcement point - documentation leads, reference lags. The documented position is that provenance has two API-owned enforcement points: citations on document blocks and attribution on search_result blocks, each with strict structural invariants and guaranteed pointers. The correct judgement for where content arrives in those shapes is that the API owns the binding and a prompt must not reimplement it. The reference position presents provenance purely as an application-side prompt discipline of five-field mappings preserved by instruction through synthesis. The divergence is omission, not contradiction, and grounding must present the documented mechanisms as the preferred enforcement point and describe prompt-preserved mappings as what you do for content that does not arrive as documents or search results. A candidate who is asked where to enforce provenance should answer with the two API mechanisms when the content shape matches, and with five-field application mappings otherwise, because that reflects the current documented division of labour. Where the question tests an application-side invariant such as attribution dying during summarisation, the judgement is unchanged since even API-provided citations must still be carried through a multi-step merge in application code.

Quality ceiling figure - not official. The reference and site lessons state a quality ceiling of roughly 147,000 to 152,000 tokens and advise keeping assembled context below about 147K. The documentation position is that no published Anthropic page states this number as an official ceiling. Documentation describes context rot as monotonic degradation with token count, with no published threshold, and that curating what is in context matters as much as how much room is left. Confirmed window sizes are documented as 1M for Opus 5, Opus 4.8, 4.7, 4.6, Sonnet 5, Sonnet 4.6, Fable 5, Mythos 5, and Mythos Preview, and 200k for every other model including Sonnet 4.5, with a 128k output maximum. The community figure is a usable heuristic for staying below the documented degradation region but must not be presented as an official measurement. A candidate who sees a question asking for an official measured percentage or threshold should treat citation-style percentages as unsupported unless the source page states a measurement.

Long-conversation default - compaction, not progressive summarisation. The documentation position is that server-side compaction with a 150,000 default trigger and a documented summary structure is the recommended strategy, with context editing as the fine-grained alternative. The reference position treats client-side progressive summarisation as the default and the persistent case facts block as the only protection. Both agree verbatim-critical facts must live outside the summarised region.

Scratchpad surface - memory tool versus local convention. The documentation position names just-in-time context retrieval and provides the memory tool as the supported surface with the security requirement of path containment under /memories and with storage executed by the application. The reference position describes the scratchpad as a purely local convention. A candidate should name the memory tool surface when the question tests the documented path and note that the local convention is the application-side realisation of the same pattern.

Budget markers as decision signals - unverified. The reference describes the model receiving a token budget marker at session start and a running warning after each tool call. DOC-URLS flags this as not documented on the checked pages, directs that it be marked not independently confirmed, and advises not building a decision rule on it. A candidate should not anchor an answer on this behaviour unless the question stem asserts it as a given for that scenario.

Claude Code compaction lever - one surface, not the only one. The reference treats /compact in Claude Code as the only lever. The documentation position is that it is one surface while the API-level compact_20260112 strategy is the programmatic equivalent for applications not running inside Claude Code. A candidate should map each lever to its layer when asked which surface applies to a headless API workload versus an interactive Claude Code session.

Supersession versus recency - narrow versus broad. The community material sometimes presents the heuristic as always use the newest. The documentation-grounded position and the forensics inventory both distinguish supersession from recency: only when a newer document explicitly supersedes an older one does applying the newest and noting what it replaced come with documentation support, and otherwise preserving both with a temporal explanation is the correct answer. A candidate who memorises always preserve both or always use the newest will fail at least one item. The candidate should check for a supersession signal first, then decide, which matches both the forensics difficulty gradient and the reference's own brief note about amended provisions.

Citations quality framing - measured advantage, not style. The community conversation sometimes treats citations as a prompt trick with a few examples. Documentation frames the citations feature as producing a measured quality advantage over prompting, with specific token and validity properties, and the lesson material supports structured context assembly as reducing retrieval and reattachment failures. A candidate should prefer the documented mechanism's properties when attributing why citations help.

Beyond the task statement

The adjacent lesson material covers topics the reference page omits entirely but which matter directly for provenance. Each item below states what the topic is, why it matters for this task, and its lesson slug.

RAG pipeline design and chunking strategy rag. Chunk size and strategy determine retrieval quality more than almost any other factor and determine the provenance payload that reaches the prompt. The lesson documents five chunking strategies, the overlap guideline, the same-model requirement for ingestion and retrieval, and contextual retrieval that prepends a chunk-specific summary before embedding. For provenance the retrieval stage is where the source and title that later become search_result fields are first available, and the ingestion formatting decision about whether to preserve headers and row context determines whether a downstream citation can point to the right row. Without this lesson's framing, a candidate may treat provenance as starting at the synthesis prompt rather than at indexing.

Document structuring for reliable parsing document-structuring. The lesson documents the U-shaped attention curve, the inverted pyramid, headings, lists, tables as the strongest format for comparative data, and XML tags for explicit structure. For provenance this determines whether the model can locate the right table cell, whether critical constraints survive attention loss, and whether a structured representation preserves table headers and coordinates through to rendering.

Six-level context taxonomy context-sources-taxonomy. The lesson decomposes the context window into instructions, external knowledge, tools, memory, state, and user prompts, states that all six compete for finite window space, and documents how retrieval, ranking, selection, and formatting of external knowledge interacts with tool result management and memory lifecycle. For provenance this explains why tool result growth can dominate the window, why memory summarisation discards bindings without case-facts protection, and why instruction cost makes compact, explicit provenance instructions cheaper than verbose handlers.

Evaluation, observability, and governance context-evaluation-governance. The lesson documents evaluation metrics for context quality such as relevance, utilisation, sufficiency, efficiency, and faithfulness, as well as observability traces that record every token the model saw, aggregated token distribution by source, and alerts for window over-utilisation or relevance degradation. Governance covers document-level and tool-level permissions and the audit log that records what context was provided to which user and when. For provenance this framing explains why provenance failures have audit consequences and why context traces are the debugging surface for a lost binding.

Multi-agent isolation and coordination multi-agent-context-isolation. The lesson documents per-agent isolated windows, role-specific system prompts and tool sets, structured inter-agent messages with schema validation, the hub-and-spoke orchestrator pattern, and the anti-patterns of shared windows and unsanitised forwarding that enable injection propagation. For provenance these boundaries are the handoff points where bindings must be injected structurally. The isolation model explains why a spawned subagent inherits no memory and why a synthesis agent that draws on several workers must receive every mapping in its spawning prompt or it will contradict the research it was meant to summarise.

Shared memory and external coordination shared-memory. The lesson documents the knowledge bus pattern, Durable Objects as the Cloudflare singleton actor for serialised writes, optimistic locking and append-only logs as conflict resolution strategies, and the vector store as shared memory that decouples state from context passing. For provenance this provides an alternative to passing full transcripts between agents. Instead of forwarding text that can lose bindings, agents write findings with full provenance objects into an external store and downstream agents retrieve exactly the records they need, which preserves field bindings and gives a natural version history with updatedBy and updatedAt for audit.

Context sources, evaluation, and memory as the remaining threads: context-sources-taxonomy explains how retrieval and tool results interact, context-evaluation-governance provides the harness that detects when a binding is being ignored, and shared-memory addresses durability when a pipeline resumes without re-executing upstream agents whose provenance objects already exist.

Worked production examples

Two end-to-end walkthroughs show the full reasoning chain and the failure mode being avoided. Both use the pipeline shape the exam tests. The first branches through document citations, the second through search results and application mappings, and both converge on the same merge and render obligations.

Worked production examples: Walkthrough 1 - Securities briefing with a supersession chain carried through document citations

A research task asks for the current liability terms and the revenue trend for Acme Inc across three sources: the base agreement dated 2026-07-01, an amendment dated 2026-09-15 that supersedes section 4, and two quarterly reports covering 2025-Q3 and 2024-Q3 for trend analysis.

Ingestion as document blocks: the two agreement documents are sent as document blocks each with citations: {"enabled": true} inside the same request. The request validates the all-or-none invariant. The API chunks both documents internally and the model returns citation objects. The liability analysis therefore comes with two pointers, one to the original cap at contract value and one to the increased cap at 1.5x contract value in the amendment, plus an in-force interpretation that the amendment supersedes the base text. The correct design preserves both excerpts and labels current versus superseded in rendering.

Ingestion of financial reports via the application path: the two quarterly reports arrive outside the document channel as structured extractions. Each finding is validated at the ingestion boundary with the five-field mapping, including asOfPeriod for 2025-Q3 and 2024-Q3. A finding that omitted publicationDate is rejected and retried before leaving the subagent. The revenue figure from each quarter carries its own source URL, document name, verbatim excerpt, and period so the synthesis step can treat the pair as a time series rather than a conflict.

Synthesis merge: the coordinator injects all findings and the citation objects into the synthesis prompt through a structured spawn payload, because spawned agents inherit no memory. The synthesis instruction groups by topic, financial performance versus legal terms, and merges mappings atomically. Two revenue claims from the same metric at different periods are not collapsed to a single reconciled value. They remain as two table rows with period-specific attribution.

Conflict and temporal handling: no credible-source conflict exists on the revenue trend because the period differs, so the time-series rule applies. The liability terms do have a supersession signal in the amendment text itself, so the synthesis applies the most recent in-force provision, cites both, and notes what it replaced.

Rendering: financial data renders as a table with Year, Revenue, Growth, and Source columns so the consumer can scan year over year. Legal terms render as prose with inline citations that carry the amendment note. The attribution attached in the table's Source column and the amendment's prose citation sentence is the same binding that entered through the API.

Failure avoided: if the ingestion boundary had accepted a revenue claim without a period, synthesis would have flagged the pair as a contradiction and possibly averaged or discarded one. If the merge had carried only prose, the amendment citation would have been paraphrased away and the liability advice would have read as unqualified fact with no trace of the supersession. Both failures are prevented by validation at ingestion and by carrying citation objects through the merge.

Worked production examples: Walkthrough 2 - Wearables market research where recency, period, and caveats interact

A multi-agent research system answers what has happened to wearables TAM and whether adoption claims are ready for a high-stakes briefing. Three research agents run in parallel, one covering financial filings with conflicting revenue growth figures, one covering a white paper series on TAM across 2021 and 2024, and one covering a vendor white paper that qualifies its own reduction estimate as preliminary and unverified.

Ingestion as search results for internal retrieval: the financial filing agent's internal retriever returns two filings via search_result blocks using stable identifiers kb://filing-2021 and kb://filing-2024. Both blocks carry citations: {"enabled": true} and share the same setting, with source as the internal identifier, title as the filing name, and content as text blocks. The API's text-only scope is respected by serializing the filings' embedded financial tables as markdown tables inside content.

TAM pair with period: the filings' TAM figures are 18B in 2021 and 31B in 2024. Each extraction carries asOfPeriod for its year. Synthesis sees that the metric is identical and the period differs and therefore interprets the pair as a time series, not a conflict. The consumer interpretation is that adoption nearly doubled, which is itself the signal. A design that discarded the older figure or averaged the two would destroy the trend.

Revenue growth conflict with a genuine disagreement: two filings report materially different revenue figures for the same period from the same metric. Analysis completes with both values plus conflictDetected: true, each value with its source, excerpt, period, and context, and a possible explanation noting methodological differences. The escalation path hands off decision-ready context to a reviewer, not merely a flag, when the synthesis stage cannot resolve it safely.

Caveat-carrying branch: the vendor white paper states a 15 to 20 percent reduction as a preliminary, unverified industry estimate. That qualifier is bound to the claim as a caveat field at ingestion and survives the merge to the final report. The news synthesis that later references the figure renders the caveat in prose as one industry source describes this as a preliminary, unverified estimate, rather than laundering it into an unqualified reduction.

Coverage and null handling on the same task: one feed backing the competitive pricing section is throttled, so the synthesis produces the useful partial report with an explicit coverage annotation marking the pricing section as partially supported. A separate pricing-changes subagent that searched and found no changes returns a successful null stated with the date and sources checked, rather than an error, and that null is rendered as informative content.

Rendering by type as the final step: the wearables TAM pair renders as a table where each row's Source and Period columns carry attribution, the procurement narrative renders as prose where the reduction caveat remains attached, and the method list for handling remaining conflicts renders as a structured list where each step carries the spec citation. The branching decision lives at render time, not upstream.

Failure avoided: instructing the synthesis agent to weight all sources equally without normalising footprint would have let the verbose white paper dominate the output by token volume, and placing it last would have substituted recency bias for length bias. The correct design normalises each source before synthesis and retains every binding.

Build exercise material

This exercise builds a provenance-preserving synthesis pipeline that accepts both API-owned and application-owned attribution and keeps it through to rendered output. Each step states what to do, the observable outcome that proves it worked, and the verification command.

Step 1 - Define the required shapes once and reuse them. Create a single TypeScript module exporting ClaimSourceMapping, ConflictValue, ConflictObject, and the renderer's Renderable type. Mark every five-field member as required. Confirm by running a type check that a missing publicationDate or empty documentName fails at validation rather than silently producing a partial record. Observable outcome: the compiler rejects an incomplete mapping and the runtime validator throws Missing required fields. Check: npx tsc --noEmit.

Step 2 - Implement two research subagents that output only in the mapping schema, including dates. One subagent covers financial filings, one covers news or white papers. Each function returns an array of ClaimSourceMapping with all five fields and asOfPeriod where the claim is period-qualified. Each function must reject its own incomplete findings before returning. Observable outcome: invoking either subagent returns only fully-bound records and never prose without fields. A deliberately incomplete raw finding is rejected at extraction. Check: run a node script that calls each subagent and asserts that every returned record passes validateMapping.

Step 3 - Wire document and search result retrieval to the API-owned channels. For the canonical agreement and filing documents, construct a user message whose content array contains one or more document blocks each with citations: {"enabled": true} and no mixing of enabled and disabled within the request. For internally retrieved material, construct search_result blocks with source as a stable internal identifier such as kb://article-1234, required title and content text blocks, and uniform citations: {"enabled": true} across the request. Observable outcome: the API returns a response with citations objects where cited_text matches a substring of the submitted document chunks, cited_text does not inflate billed output_tokens in usage, and a request that mixes enabled and disabled on search results fails validation. Check: inspect the returned usage and the citations array in the response JSON.

Step 4 - Build the synthesis merge that carries every mapping through while grouping by topic. Implement mergeByTopic so it groups findings by topic for readability, appends each mapping to a flat citationIndex, and never merges two bindings into one. Add a check that duplicate exact claim-source pairs are flagged as a warning but that each group's unique findings are all retained. Observable outcome: feeding two topic groups with three findings produces a citationIndex of length three and a section structure where each claim lists exactly its own sources. Check: assert citationIndex.length equals the sum of group lengths and that serialising the merged output and re-parsing it preserves every source URL.

Step 5 - Handle conflicting sources by preserving both with a conflict object. When two credible sources report different values for the same metric with no supersession signal, construct the object with conflictDetected: true, two entries in values each carrying value, source, sourceUrl, documentName, relevantExcerpt, publicationDate, asOfPeriod, and context, plus possibleExplanation and resolution: "preserve_both_for_consumer". Render a genuine file-level or section-level inconsistency the same way with conflict_detail. Observable outcome: the rendered output shows both values side by side with their own attribution and never a single averaged or selected number. A supersession case shows both excerpts but labels current versus superseded. Check: assert that the rendered markdown contains both source names and that the word between the two values is not an arithmetic blend.

Step 6 - Implement content-appropriate rendering that branches by type while keeping attribution attached. Wire renderByContentType so financial data goes to a table with a Source column and a source sentence, news to prose with a trailing citation sentence that preserves any caveat, and technical findings to structured lists with per-item source notes. Preserve native table headers and row coordinates through ingestion so the renderer can reconstruct header-to-value bindings without guessing. Observable outcome: a financial render contains a markdown table with headers and a source sentence, a news render contains the caveat text, and a technical render is a bulleted hierarchy with per-section source notes. Check: run the renderer over financialItem, newsItem, and technicalItem and assert that each output contains its source document name and that the financial output contains a pipe table header row.

Step 7 - Validate the full loop with a smoke command. Write a short script that runs ingestion validation, assembles a document request with citations enabled, assembles a search result request with uniform citations, merges two topic groups, and renders all three content types. The script should log the word total for the assembled report so the team can confirm the provenance objects rather than the prose volume are what scale the output. Check: python3 scripts/last-step/validate-scratchpads.py --min-words 11000 --max-words 15000 docs/last-step-scratchpad/domain-5-context/5-6-grounding.md plus npx tsc --noEmit.

Mechanism and API surface

Citations and search result blocks as API-level attribution
Setting citations enabled true on a document block returns structured citations whose cited_text does not count toward output tokens and is guaranteed to point into the supplied document, and must be enabled on all documents in a request or none. Search result blocks carry a required source and title for your own retrieved content, where source may be a stable internal identifier rather than a URL.
Five-field claim-source mapping
Required fields of claim, sourceUrl, documentName, relevantExcerpt, and publicationDate as ISO 8601 string, enforced at ingestion so any finding with a missing field is rejected.
Synthesis merge that preserves every mapping
Merge function over two arrays of mappings that returns a unified array where the union of source mappings is preserved per topic and no mapping is discarded without explicit handling. Grouping by topic never collapses attribution.
Conflict preservation not resolution by the synthesis agent
When values overlap and disagree return a conflict object with conflictDetected true, values array carrying both entries with full attribution and context, and possibleExplanation, and let the coordinator or consumer decide. Averaging or picking one value destroys information.
Temporal context and changelog-aware grouping
Publication and data collection dates distinguish a trend from a contradiction and allow grouping by date to surface trends. A newer changelog supersedes older documentation and both are presented with dates rather than treating the older as authoritative.
Content-appropriate rendering
Financial data as tables, news as prose, and technical findings as structured lists, selected by content type so comparison, narrative, and hierarchy each use the format that maximises comprehension.
Coverage annotation and inline citation during streaming
Synthesis closes with well supported, limited, conflicting, or unavailable per topic, and streaming includes citation markers inline so attribution is visible as synthesis arrives, not only at the end.

The decision rules in play

Each rule below is a distinct decision the exam tests, stated with its mechanism, the boundary where the answer flips, and the wrong answers written against it.

R1

Structured claim-source mapping is a first-class output contract

Every subagent that produces a finding must emit a data structure in which each assertion is bound to its origin. The conventional shape carries five fields: the claim itself, a source_url, the document_name, a relevant_excerpt, and a publication_date. The binding is created at the moment the subagent finishes its extraction, before any downstream agent sees the text. The coordinator then forwards these structures, and the synthesis agent is instructed to carry them forward unchanged while merging.

result.json
json
{
  "claim": "Global renewable energy investment reached $495 billion in 2023",
  "source_url": "https://example.com/iea-report-2024",
  "document_name": "IEA World Energy Investment Report 2024",
  "relevant_excerpt": "Total investment in renewable energy technologies reached approximately $495 billion in calendar year 2023, representing a 17% increase over 2022.",
  "publication_date": "2024-06-15"
}

Attribution is a relationship between a statement and its origin. When that relationship lives only in prose, the next agent that paraphrases the prose has no structural handle to keep the two together. When the relationship is a field, the next agent receives the claim and its source as one object, so merging operations move both at once. The guarantee is structural, not stylistic, which is why a prompt that merely says "preserve sources" cannot substitute for it.

Boundary. The boundary is the output format of the producing agent. If a subagent returns free paragraphs, the mapping is already lost before synthesis. The nearby opposite case is the bibliography: a trailing list of consulted sources is the wrong answer because it maps a document to the report, not a specific claim to a specific passage. A bibliography is correct as a supplement, never as the primary mechanism.

Recurring specifics. The five-field shape recurs across financial, legal, regulatory, and research framings. Field names vary slightly (source_url versus source_url, document_name versus source_title, publication_date versus source_date), but the required members are stable: claim, location, document name, supporting excerpt, and date. Regulated settings add source_record_id or as_of_date.

Wrong answers written against this rule

Proposal. append a bibliography section at the end of the report.

Why it attracts. it feels like proper citation and is easy to add.

Why it fails. it does not map a specific claim to a specific source, so a single statistic stays unverifiable.

When it would be right. as a secondary reference list behind a per-claim mapping, not instead of it.

Proposal. store all subagent outputs in a database and reference by ID.

Why it attracts. it centralizes data.

Why it fails. it moves the burden of linkage to a later agent that must reconstruct which record supported which claim.

When it would be right. as an implementation detail behind a mapping contract, not as the sole link.

Proposal. instruct the synthesis agent to reconstruct likely sources from its own knowledge.

Why it attracts. it seems to fill gaps.

Why it fails. it manufactures citations.

When it would be right. never for attribution.

How the same rule gets re-asked
  • Mutations replace the domain (healthcare efficacy, vaccine rates, logistics transit times) while keeping the five-field contract identical. The correct answer never changes: structured mapping emitted by the subagent. Another mutation asks which field is missing when a figure cannot be traced; the answer is the structured mapping itself, not a formatter or a token budget.
R2

Attribution is destroyed during prose summarization and compression

When a synthesis agent receives findings as paragraphs and rewrites them into tighter bullets or a flowing narrative, it keeps the fact and drops the link to its source. The compression step is where the provenance chain breaks, because the model optimizes for fluency and conciseness, and citation fidelity competes with those objectives. By the time the report generator receives the compressed text, there is nothing left to attach a citation to.

output.txt
text
Subagent output: "Adoption grew 40% in 2024 (2022 study, p. 12)."
Synthesis compression: "Adoption grew 40% in 2024."
Reviewer check: source says 2022, not 2024. Attribution already lost.

Summarization is a lossy transform applied to natural language. The source association is not a property the model is forced to preserve unless it is encoded as structure. A bullet point preserves the claim but not where it came from. This is information destruction, not forgetfulness, which is why reconstruction downstream is fundamentally unreliable.

Boundary. The boundary is whether the mapping travels as a field. If subagents emit structured records and the synthesis agent is told to carry them through unchanged, compression of the prose wrapper does not touch the bindings. The nearby opposite case is a post-processing citation formatter that matches claim text to source text after compression; it fails because the synthesized language no longer matches the original, so there is no clean anchor.

Recurring specifics. Tokens like "compression discards claim-source mappings" and "the synthesis agent must carry these through unchanged when merging" recur. The failure is most visible in merged passages where several sources contributed distinct details but only one citation survived.

Wrong answers written against this rule

Proposal. add a post-processing citation formatter that matches claim text to source text.

Why it attracts. it keeps the pipeline as-is.

Why it fails. paraphrased claims have no clean text match to anchor a footnote.

When it would be right. only as a secondary aid when mappings already exist.

Proposal. raise the synthesis agent's token limit.

Why it attracts. longer reports could fit more citations.

Why it fails. the metadata was never passed, so more room does nothing.

When it would be right. when the real problem were truncation, not structure.

Proposal. lower the synthesis temperature.

Why it attracts. less paraphrase.

Why it fails. temperature does not create structural guarantees.

When it would be right. never for this failure.

How the same rule gets re-asked
  • Mutations change the compression style (bullets versus prose versus a single block) but the correct answer is constant: require structured records that survive the merge. A mutation that reports accurate claims but untraceable ones tests whether the candidate recognizes accuracy without traceability is still a failure.
R3

Spawned subagents inherit no memory

When a coordinator dispatches a subagent through a spawn call, that subagent begins with only the context placed in its prompt. It cannot see the coordinator's system prompt, the other subagents' outputs, or earlier turns. Therefore the coordinator must place the complete structured findings, including every claim-source binding, directly into the spawning payload. If it passes a summary or a recap, the subagent fills gaps from its own training data and can contradict the research.

example.ts
typescript
interface SynthesisSpawn {
  draft: string;
  sourceIndex: Array<{
    claim: string;
    source_url: string;
    excerpt: string;
    agent: "web_search" | "document_analysis";
  }>;
}

Isolation is a defining property of spawned workers. The "require citations" instruction written in the coordinator's system prompt is invisible to the spawned synthesis agent unless repeated in its task payload. Behavior rules, safety instructions, and output-format contracts must all be re-stated in every delegation.

Boundary. The boundary is the spawn boundary. Inside one continuous session, later steps can see earlier text; across a spawn, they cannot. The nearby opposite case is a coordinator that prepends its own natural-language recap of prior findings. That recap is still prose, so it can drop or misattribute a binding, and it does not carry the structure a spawned agent needs.

Recurring specifics. Field names such as synthesis_agent, Task tool, and allowedTools appear in these framings. The recurring symptom is a spawned agent that produces a report contradicting the research it was meant to summarize.

Wrong answers written against this rule

Proposal. give the synthesis agent the web-search tool so it re-looks-up sources.

Why it attracts. it seems to recover citations.

Why it fails. it violates scoped-tool design and may not recover the original source.

When it would be right. never as a fix for missing handoff.

Proposal. have the coordinator write a natural-language recap and prepend it.

Why it attracts. simple.

Why it fails. prose recap loses bindings.

When it would be right. only when the recap is built from structured records and verified.

Proposal. switch to sequential spawning so earlier agents share state.

Why it attracts. state appears shared.

Why it fails. spawn isolation still applies unless state is injected.

When it would be right. when the tool genuinely shares session state by design.

How the same rule gets re-asked
  • Mutations vary the spawned role (report generator, demand drafter, citation resolver) but the answer is constant: inject complete structured findings with content separated from metadata. A mutation that asks what most directly causes a contradiction points to the summary handoff, not to a conflicting system prompt.
R4

Inline prose citations are fragile

A citation written inside a sentence, such as "According to page 12 of the 2024 Annual Report, revenue was $4.2B," is text. When a downstream analytics system parses, restructures, or rewrites that text, the citation is just another token and is easily stripped or misplaced. The fix is to emit the value paired with its source as structured fields so downstream consumers read value, source, page rather than parsing prose.

result.json
json
{
  "value": "4.2B",
  "source": "2024 Annual Report",
  "page": 12,
  "source_date": "2024"
}

Downstream systems treat text as opaque or transform it freely. A structured field is addressed by name, so the consumer pulls the source without parsing language. This is why inline citations "die" the moment any step other than a human reads them.

Boundary. The boundary is whether the consuming step is a person or a program. A human reading a report tolerates inline citations; a pipeline that re-serializes the report does not. The nearby opposite case is making the citation bold or adding a delimiter: that still leaves the citation inside prose, so a transformer that strips formatting still loses it.

Recurring specifics. The pattern "citations embedded inline in prose are frequently missed during synthesis" recurs, with the remedy "use structured output fields (e.g., citations: [])". The token source_url, document_name, page_number is the canonical triplet.

Wrong answers written against this rule

Proposal. make citations bold or use a special delimiter.

Why it attracts. visibility.

Why it fails. formatting is discarded by text processing.

When it would be right. never as the primary fix.

Proposal. require the downstream system to preserve all input text verbatim.

Why it attracts. nothing is lost.

Why it fails. impractical and still leaves linkage implicit.

When it would be right. as a logging safeguard, not as citation structure.

Proposal. add a separate attribution database the downstream system queries.

Why it attracts. centralizes lookups.

Why it fails. adds infrastructure without solving the in-text loss.

When it would be right. behind a mapping contract.

How the same rule gets re-asked
  • Mutations replace the downstream consumer (analytics, report generator, citation resolver) but the answer holds: structured fields survive transformation. A mutation that asks why citations are omitted after synthesis points to the inline format, not to token limits.
R5

Conflict handling preserves both values

When two credible sources report different statistics for the same measure, the synthesis must present both, each with its source, and annotate the difference as possibly due to period, scope, or methodology. It must not average them, must not pick the newer, and must not pick the more authoritative publisher. The consumer decides.

instructions.md
markdown
Market growth estimates vary by source:
- **12% growth** - IEA World Energy Report (published June 2024, 2023 calendar-year data)
- **8% growth** - Bloomberg NEF Annual Review (published March 2024, July 2022-June 2023 data)

The difference may reflect different reporting periods and methodological approaches.

Each reported figure is a real observation by a real source. Averaging manufactures a number no source supports. Selecting one destroys information the reader may need, and presents false certainty. Preserving both lets the consumer apply judgment about relevance.

Boundary. The boundary is whether a supersession signal exists. When one document explicitly supersedes another (a regulatory amendment, a page titled "NEW - 2024", a dated in-force provision), applying the most recent in-force version and noting what it replaced is the correct move. The same is true when the task explicitly asks for the current best estimate and the dates differ. Absent that signal, preserving both is mandatory. I infer the "current best estimate" exception from items where the ask is explicitly temporal; it is a narrow boundary, not a general license to pick.

Recurring specifics. The five-field mapping reappears here with a conflictDetected or a side-by-side rendering. Recurring wrong moves: average the two (e.g., 18.5%, 4.25%, 47.5%, 61.5%, 10%), pick the more recent, pick the more authoritative, omit both, or pick whichever was retrieved first.

Wrong answers written against this rule

Proposal. average the values.

Why it attracts. feels balanced.

Why it fails. mathematically misleading, invents an unsupported number.

When it would be right. never for genuine source disagreement.

Proposal. use the most recent source.

Why it attracts. newer seems better.

Why it fails. the older figure may be exactly what trend analysis needs.

When it would be right. only with an explicit supersession signal.

Proposal. pause for human resolution.

Why it attracts. safe.

Why it fails. unnecessary when the difference can be explained with dates.

When it would be right. for genuinely unsafe decisions, but not as the default.

Proposal. omit the data point.

Why it attracts. avoids error.

Why it fails. withholds reportable information.

When it would be right. only if the figure is unsourceable.

How the same rule gets re-asked
  • Mutations change the metric (adoption rate, market size, churn, fee cap, efficacy) and the stakes (finance, medicine, law) but the answer is constant. One mutation presents two figures that differ because of period; the correct answer there is still to preserve both with dates, because without an explicit supersession the values are not in conflict.
R6

Analysis subagents surface conflicts; coordination reconciles

A document-analysis or research subagent's job is faithful extraction, not editorial judgment about which source wins. When two credible sources disagree on a material metric, the subagent emits both figures with full source attribution and an explicit conflict annotation, then passes the decision upward. The coordinator owns reconciliation because it holds broader context about scope, definitions, and research goals.

result.json
json
{
  "field": "annualRevenue",
  "conflictDetected": true,
  "values": [
    { "value": "$4.2M", "source": "Annual Report 2023", "context": "Audited financial statements, fiscal year ending December 2023" },
    { "value": "$3.8M", "source": "SEC Filing Q4 2023", "context": "Preliminary unaudited figures, calendar year 2023" }
  ],
  "possible_explanation": "Difference may reflect audited vs preliminary figures and fiscal vs calendar year reporting periods"
}

A lower-level agent lacks the system-level view needed to judge authority. A local heuristic (central bank beats trade association) can be wrong because the scope or period may differ. Surfacing the conflict preserves the information the coordinator needs; resolving it locally can silently produce a materially wrong conclusion.

Boundary. The boundary is role responsibility. A subagent may annotate; it should not select. The nearby opposite case is a coordinator that immediately terminates the task when sources disagree: that throws away completed work and blocks the pipeline over a normal condition.

Recurring specifics. The phrase "complete the analysis with both figures, explicitly annotate the conflict with sources, and let the coordinator reconcile" recurs. The danger is a subagent that "silently resolves" by picking one and burying the discrepancy in a footnote.

Wrong answers written against this rule

Proposal. apply a credibility heuristic and adopt the stronger source.

Why it attracts. defers to expertise.

Why it fails. the subagent cannot see scope or period differences.

When it would be right. when the coordinator has delegated that authority explicitly, which is rare.

Proposal. halt and escalate before processing remaining documents.

Why it attracts. avoids error.

Why it fails. blocks the whole pipeline for a normal condition.

When it would be right. only for genuinely unsafe single decisions.

Proposal. include both numbers without flagging the conflict.

Why it attracts. preserves data.

Why it fails. lets synthesis silently decide, hiding the tension.

When it would be right. never; the flag is required.

How the same rule gets re-asked
  • Mutations change the domain and the metric but keep "both with attribution, flag, coordinator reconciles" as the answer. A mutation that merges both numbers without a flag tests whether the candidate notices the conflict must be explicit.
R7

A date is a qualifier, not a ranking key

The publication_date or as_of_date travels with the claim as a descriptor of when the underlying data was measured. It is not a score that ranks one claim as truer than another. Synthesis reads the date to interpret the figure, not to discard the older one. Two figures from different years describe a trend; two figures from the same period describe a conflict.

result.json
json
{
  "metric": "renewable_energy_adoption",
  "value": "35%",
  "date": "2025-Q1",
  "source": "IEA Global Report",
  "data_collection_period": "2025"
}

A date answers "when was this true," which is necessary context for any quantitative claim. Treating it as a recency rank conflates temporal ordering with factual priority and destroys historical signal that trend analysis depends on.

Boundary. The boundary is whether a newer document explicitly supersedes the older (regulation, amended provision). There, recency is a legitimate selection signal. The nearby opposite case is instructing the synthesis agent to "always treat the most recent data as authoritative," which erases trend and misreads growth as a reason to discard the past.

Recurring specifics. Field names publication_date, source_date, data_collection_period, as_of_date. The recurring trap is "flag as contradiction" when the only difference is the year.

Wrong answers written against this rule

Proposal. always use the most recent source.

Why it attracts. freshness.

Why it fails. deletes trend signal.

When it would be right. explicit supersession only.

Proposal. discard sources older than six months.

Why it attracts. avoids staleness.

Why it fails. removes exactly the historical data that explains current figures.

When it would be right. only as a retrieval filter with a documented reason, not as synthesis policy.

Proposal. limit web search to the past six months.

Why it attracts. keeps things current.

Why it fails. leaves the internal reports still showing old periods, so the misinterpretation persists.

When it would be right. never as the sole fix.

How the same rule gets re-asked
  • Mutations swap the metric and the gap but the answer is constant: require dates so temporal differences are read as periods, not conflicts. A mutation where the older figure is needed for trend tests whether the candidate resists discarding it.
R8

Same metric across periods is a time series

When two entries share a metric but differ in as_of_period, the coordinator interprets them as a sequence ordered by time, not as competing claims. The structured as_of_period field is what makes this possible; without it, the same metric with two values looks like a contradiction.

result.json
json
{
  "metric": "wearables_tam",
  "value": "18B",
  "source_name": "2021 analyst note",
  "as_of_period": "2021"
}
result.json
json
{
  "metric": "wearables_tam",
  "value": "31B",
  "source_name": "2024 trade survey",
  "as_of_period": "2024"
}

A time series and a conflict require opposite handling. Merging a time series into one number hides the growth; flagging it as a conflict sends reviewers on a false errand. The period field is the only reliable signal that distinguishes them, so it must be structured, not inferred from prose.

Boundary. The boundary is same metric, different period. Same metric, same period, different value is a genuine conflict and should be flagged as such. The nearby opposite case is an instruction to "merge any divergent values into a single reconciled estimate": that collapses a trend into a fiction.

Recurring specifics. Field as_of_period recurs in markets-intelligence and briefing framings. The wrong move "interpret entries sharing a metric but differing in period as a conflict" appears repeatedly.

Wrong answers written against this rule

Proposal. merge divergent values into one reconciled estimate.

Why it attracts. one clean number.

Why it fails. hides the trend.

When it would be right. never for period-separated data.

Proposal. group divergent figures as a conflict set and present side by side for adjudication.

Why it attracts. transparent.

Why it fails. still mislabels a time series as a conflict.

When it would be right. when the periods are actually equal.

Proposal. present each divergent figure side by side with its source, carrying the conflict forward.

Why it attracts. honest.

Why it fails. only if it is truly a conflict; here the period explains it.

When it would be right. for same-period disagreement.

How the same rule gets re-asked
  • Mutations change the metric and cadence but keep the answer: add as_of_period and interpret temporally. A mutation where providers refresh on different schedules tests whether the candidate reserves conflict treatment for same-as-of-date disagreement.
R9

Do not discard older data merely because newer exists

When a pipeline carries figures from multiple periods, every period is potentially useful: trend analysis, year-over-year comparison, and historical context all require the older points. A design that drops the older figure whenever two numbers differ destroys legitimate findings throughout the report. The correct design keeps both and labels periods.

The presence of a newer figure does not make the older one false; it makes it earlier. For a query about adoption over time, the 2024 value is meaningless without the 2022 baseline. Discarding on recency is a misdiagnosis that converts a time relationship into a deletion.

Boundary. The boundary is genuine supersession versus mere temporal separation. A regulation explicitly replaced by a later amendment is a supersession; a market statistic from an earlier year is not. The nearby opposite case is a conflict-resolution agent that "automatically discards older data when newer exists": it erases the trend that is often the most valuable output.

Recurring specifics. Items describe adoption "nearly doubling from 18% to 35% in a single year" as the real finding. The anti-pattern "place older findings in a historical appendix" recurs as a distractor that hides the trend.

Wrong answers written against this rule

Proposal. discard the older source before synthesis.

Why it attracts. keeps only current data.

Why it fails. loses valid context and trend.

When it would be right. only with explicit supersession.

Proposal. relegate older data to a historical appendix.

Why it attracts. tidy.

Why it fails. severs the trend from the current figure.

When it would be right. never as the sole treatment.

Proposal. add a conflict-resolution agent that drops older data.

Why it attracts. automated.

Why it fails. destroys analytical value.

When it would be right. never for time-series data.

How the same rule gets re-asked
  • Mutations change the metric but keep "keep both, label periods" as the answer. A mutation where the older figure supports a trend conclusion tests whether the candidate resists deletion.
R10

Render content appropriately

The synthesis step selects a presentation format matched to the content type. Financial series become tables with columns for period, value, and source. Developments and cause-effect narratives become prose. Technical findings such as API surfaces, configuration options, or method lists become structured lists. The decision is made at render time, not by forcing every subagent into one format.

instructions.md
markdown
| Year | Investment ($B) | Growth (%) |
|------|----------------|------------|
| 2021 | 366            | 12%        |
| 2022 | 423            | 16%        |
| 2023 | 495            | 17%        |

Each content type has a natural structure that aids comprehension. Numbers meant for comparison are scanned faster in a grid than in a sentence. Narrative causality reads better as paragraphs than as bullets. Forcing one format onto all three degrades every one of them.

Boundary. The boundary is the content type. The nearby opposite case is standardizing all subagent outputs to JSON with claim-evidence fields: that still loses format-specific clarity at render time, because financial measurements are not claims and news is not a list. The fix is at the synthesis render step, not at the upstream schema.

Recurring specifics. The triplet "financial data as tables, news as prose, technical findings as structured lists" recurs verbatim. A separate conversion layer between subagents and synthesis is a distractor because it does not fix the rendering decision.

Wrong answers written against this rule

Proposal. standardize all outputs to JSON.

Why it attracts. uniform schema.

Why it fails. still flattens at render; measurements are not claims.

When it would be right. as an upstream contract, but not as the rendering fix.

Proposal. standardize all outputs to prose with inline citations.

Why it attracts. readable news.

Why it fails. destroys financial comparability.

When it would be right. never as the universal format.

Proposal. add a format conversion layer.

Why it attracts. seems structural.

Why it fails. leaves the synthesis agent rendering everything the same.

When it would be right. never for this problem.

How the same rule gets re-asked
  • Mutations change the mix (API plus sentiment, financial plus news plus patents) but the answer holds: render each type in its natural form. A mutation asking which section to convert tests whether the candidate keeps technical data as a list and sentiment as prose.
R11

Do not force every content type into one uniform format

This is the inverse framing of Rule 10. The failure is homogenization: converting financial JSON, news prose, and patent lists all into bullet points, or all into prose, or all into one table. The synthesis agent must instead branch on content type. Uniform bullets and uniform prose are both wrong because both flatten structure.

Uniformity optimizes for pipeline simplicity, not reader comprehension. A briefing where financial comparison and narrative development are indistinguishable is harder to use than one that signals type through format. The render step exists precisely to make that distinction.

Boundary. The boundary is the content's intrinsic structure. The nearby opposite case is "convert everything to bullet points for consistency": bullets are better than nothing for some content but wrong for financial comparison and for flowing narrative. When the content is genuinely a list of findings, bullets are appropriate.

Recurring specifics. "Convert everything to bullet points" and "always output prose" both appear as distractors. The correct phrase "render each content type appropriately" recurs.

Wrong answers written against this rule

Proposal. convert everything to bullet points.

Why it attracts. consistency.

Why it fails. hides financial and narrative structure.

When it would be right. for a list of discrete findings only.

Proposal. always output prose.

Why it attracts. readability.

Why it fails. loses tabular clarity.

When it would be right. for developments and narrative.

Proposal. output raw data only.

Why it attracts. maximal fidelity.

Why it fails. abdicates the synthesis role.

When it would be right. never as the final output.

How the same rule gets re-asked
  • Mutations change which uniform format is proposed (bullets, prose, raw) but the answer is constant: branch by content type. A mutation that pairs API findings with sentiment tests whether the candidate keeps each in its natural form.
R12

Caveats survive the handoff

When a source describes a figure as preliminary, unverified, or estimated, that framing is part of the provenance and must travel from subagent to coordinator to final report. The synthesis must not launder a "preliminary, unverified industry estimate" into an unqualified "15-20% reduction." The caveat is a property of the claim, bound to it like the source.

A qualified claim and an unqualified claim carry different epistemic weight. Stripping the qualifier misrepresents the tested material to the reader, who cannot tell that a number was tentative. The handoff chain must preserve confidence framing exactly as the source expressed it.

Boundary. The boundary is the source's own wording. The nearby opposite case is removing the figure entirely because it is "unverified": that hides information the coordinator should weigh. Another opposite is the coordinator independently re-verifying every figure: impractically slow and not the named principle.

Recurring specifics. The token "laundered into unqualified fact" appears for the wrong move. The correct move preserves the source's framing, e.g., "one industry source describes this as a preliminary, unverified estimate."

Wrong answers written against this rule

Proposal. state the figure as fact since it is the only number.

Why it attracts. fills the report.

Why it fails. strips the caveat, misrepresents confidence.

When it would be right. never.

Proposal. remove the figure entirely.

Why it attracts. avoids error.

Why it fails. hides a real finding.

When it would be right. only if unsourceable.

Proposal. re-verify every figure before synthesis.

Why it attracts. rigorous.

Why it fails. impractically slow; not the principle.

When it would be right. for targeted high-stakes claims.

How the same rule gets re-asked
  • Mutations change the caveat (preliminary, unverified, confidence interval) but the answer holds: carry the framing forward. A mutation that asks how provenance should flow tests whether the candidate preserves rather than removes or launders.
R13

Collapse duplicate source listings without dropping findings

When two subagents cite the same source through different paths, the final source list should contain one entry for that source, not two, because listing it twice implies two independent sources corroborate a point that is actually one. But each subagent's unique findings from that source must still be preserved. The listing is de-duplicated; the findings are not.

Double-listing overstates corroboration and can mislead a reader into thinking two sources agree when one source reached the system twice. Preserving the distinct findings keeps the real coverage intact. The operation is on the list, not on the tested material.

Boundary. The boundary is the listing versus the findings. The nearby opposite case is removing the source entirely "due to the conflict": that discards a real citation and hides coverage. Another opposite is listing it twice: that inflates corroboration.

Recurring specifics. The phrasing "collapse the listing to one entry - listing twice could misleadingly imply two independent sources corroborate something that's actually one source via two paths; but preserve each subagent's unique findings" recurs.

Wrong answers written against this rule

Proposal. list the source twice.

Why it attracts. seems to credit both paths.

Why it fails. implies false corroboration.

When it would be right. never.

Proposal. remove the source entirely.

Why it attracts. avoids the "conflict."

Why it fails. discards real coverage.

When it would be right. never.

Proposal. only the first subagent gets credit.

Why it attracts. simple.

Why it fails. drops the second's findings.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the source type but the answer is constant. A mutation testing whether the candidate inflates or deletes coverage probes the boundary precisely.
R14

Make absence visible with coverage annotations

When some sources or feeds are unavailable (timeout, auth error, throttled), the synthesis must still produce the report from what arrived, but it must annotate which topic areas are well-supported and which have gaps. The annotation marks areas as fully supported, partially supported, or unsupported due to an unavailable source, without dumping raw error text. This is a report-level coverage signal, distinct from per-claim provenance.

A reader who sees no competitor section assumes the area was quiet. Silence about a gap is a false signal. Explicit coverage labels let the reader calibrate trust section by section and let the coordinator decide whether a retry is worth it, without blocking the useful partial result.

Boundary. The boundary is availability versus attribution. Per-claim provenance says where received content came from; it cannot expose a topic area that never arrived. So coverage annotation is a separate mechanism (noted as a different domain concern in the tested material). The nearby opposite case is a design that "structures the brief so every topic area is always present with an explicit coverage label" - that is correct here, while a design that only carries per-claim provenance would still leave the gap invisible.

Recurring specifics. Phrases "structure the report with explicit coverage annotations" and "well-supported, partially supported, or unsupported" recur. The token feed_status (ok / throttled / error) appears in one framing.

Wrong answers written against this rule

Proposal. synthesize from successful sources and say nothing about gaps.

Why it attracts. clean report.

Why it fails. misleads the reader into assuming quiet areas.

When it would be right. never.

Proposal. return an error and retry everything.

Why it attracts. completeness.

Why it fails. discards the useful partial work and adds latency.

When it would be right. only if a gap is truly blocking.

Proposal. substitute the model's general knowledge.

Why it attracts. fills the gap.

Why it fails. injects unsourced claims into a cited report.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the failure type (timeout, auth, throttle) but the answer holds: annotate coverage, keep the partial result. A mutation where only one feed failed entirely tests the boundary where a simple instruction helps but merged sections still drop.
R15

A genuine null finding is valid, distinct from failure

When a subagent searches for something and finds none (pricing stable for two years, no recent changes), it should return a successful result stating the null: "no recent pricing changes found (pricing stable since [date], per [sources checked])." This is a finding the coordinator can state, not an error to retry or a gap to hide.

"Nothing changed" is often the true, complete answer and is decision-relevant. Treating it as an error wastes retries and can lead to fabricating content to fill the category. The null is informative; the failure (could not search) is not.

Boundary. The boundary is searched-and-empty versus could-not-search. The nearby opposite case is returning an error because "no changes were found": that mislabels a successful search as a failure. Another opposite is fabricating a plausible change to ensure the category has content: a severe provenance violation.

Recurring specifics. The token "genuinely empty is not an error" recurs. The correct output names the date and the sources checked.

Wrong answers written against this rule

Proposal. return an error.

Why it attracts. no result feels like failure.

Why it fails. mislabels success.

When it would be right. only for an actual search failure.

Proposal. fabricate a change.

Why it attracts. fills the category.

Why it fails. fabrication, severe.

When it would be right. never.

Proposal. retry indefinitely.

Why it attracts. thorough.

Why it fails. wastes effort on a true null.

When it would be right. only if the search itself was incomplete.

How the same rule gets re-asked
  • Mutations change the searched entity but the answer is constant. A mutation testing whether the candidate fabricates probes the severity boundary.
R16

Code-analysis handoffs bind claims to file, symbol, line

When subagents trace code (API handlers, database writes, scheduled jobs), each finding must travel with file_path, symbol, line_range, and an evidence snippet, so the synthesizer can preserve attribution while merging and engineers can verify. The metadata is part of the structured handoff, not something the synthesizer reconstructs from prose.

example.ts
typescript
interface HandoffRecord {
  finding: string;
  file_path: string;
  symbol: string;
  line_range: [number, number];
  evidence_snippet: string;
}

Code provenance is spatial: a claim about behavior attaches to a location. Prose summaries that mention a file but not the line range leave engineers unable to verify. Reconstruction from narrative is unreliable because the lost metadata cannot be recovered deterministically.

Boundary. The boundary is the content type (code versus prose). The nearby opposite case is concatenating full subagent transcripts so the synthesizer searches them: that wastes context and increases lost-in-the-middle risk. Regex parsing prose for paths is a brittle anti-pattern that confuses surface patterns with real relationships.

Recurring specifics. Field names file_path, symbol, line_range, evidence_snippet recur. The failure "attaches claims to the wrong file or omits line ranges" recurs across billing-module and claims-system framings.

Wrong answers written against this rule

Proposal. concatenate full transcripts.

Why it attracts. complete.

Why it fails. context bloat, lost-in-middle.

When it would be right. never as the primary handoff.

Proposal. regex parse prose for paths.

Why it attracts. automated.

Why it fails. brittle, confuses pattern with relationship.

When it would be right. never.

Proposal. have the synthesizer reconstruct locations.

Why it attracts. defers work.

Why it fails. cannot recover lost metadata.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the codebase domain but the answer holds. A mutation asking what most improves handoff reliability points to structured records separating finding, path, symbol, line.
R17

Pass needed information directly into the prompt

A later step can only use what was explicitly placed in its prompt. If the synthesis subagent receives only "produce themes and recommendations," it has no findings to cite, no matter how good its writing. The orchestration fix is to include the relevant findings and source details directly in the synthesis prompt, not to assume they are reachable elsewhere in the system.

Delegation is explicit. A subagent does not see the coordinator's memory or other agents' outputs unless those are injected. Assuming reachability produces a polished but uncited report, because the agent was never given the sources.

Boundary. The boundary is the spawn or delegation boundary (Rule 3). The nearby opposite case is instructing the synthesis agent to "request the missing findings from the coordinator if it needs them": that adds a round trip and still depends on the coordinator having them wired in. Another opposite is writing findings to a shared log the agent "can check": relies on the agent to know to look.

Recurring specifics. The principle "a subagent can't cite sources it was never actually given" recurs. The fix "include the relevant findings and source details directly in the synthesis subagent's prompt" appears in marketplace and healthcare framings.

Wrong answers written against this rule

Proposal. tell the synthesis agent to request missing findings.

Why it attracts. self-service.

Why it fails. round trip, still needs wiring.

When it would be right. only as a fallback, not the design.

Proposal. write findings to a shared log.

Why it attracts. central.

Why it fails. relies on the agent to query it.

When it would be right. behind an explicit injection.

Proposal. increase output length for more citations.

Why it attracts. more text.

Why it fails. no sources to cite.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the domain but the answer is constant. A mutation that asks how to fix a citation gap points to the prompt injection, not to output length.
R18

Source independence is a provenance dimension

The coordinator, holding the broader view, can notice that all evidence for a point comes from interested parties (companies describing their own product) and frame the report accordingly, e.g., "as described by the affected companies themselves." Source independence and vested interest are part of provenance, a dimension synthesis can add that a single subagent may not flag.

A claim's reliability depends on who stands to gain from it. A subagent that returns three press releases from financially interested companies without noting it leaves the coordinator, which sees the whole picture, responsible for surfacing the bias. Provenance is not only location; it is also relation.

Boundary. The boundary is the coordinator's broader visibility. The nearby opposite case is fabricating an independent source to balance the report: that is fabrication, a severe violation. Another opposite is removing the findings entirely with no mention: that silently misrepresents coverage.

Recurring specifics. The phrasing "source independence is provenance" and "frame the report accordingly" recurs. The wrong moves are fabrication and silent omission.

Wrong answers written against this rule

Proposal. fabricate an independent source.

Why it attracts. balance.

Why it fails. fabrication.

When it would be right. never.

Proposal. remove the findings entirely.

Why it attracts. avoids bias.

Why it fails. misrepresents coverage.

When it would be right. never.

Proposal. assume the subagent would have flagged it.

Why it attracts. defers.

Why it fails. the coordinator has the broader view.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the interested party but the answer holds. A mutation testing whether the coordinator should address bias probes the fabrication versus framing boundary.
R19

Label synthesis-level interpretation separately

When the coordinator draws connections across findings ("the recent pricing reduction combined with the new launch suggests an aggressive strategy"), that interpretive content is valuable but distinct from directly-sourced facts. It should be clearly labeled as the system's analysis or interpretation, so the reader can weigh sourced findings and synthesized judgment differently.

Conflating interpretation with sourcing misrepresents the confidence and nature of the content. A reader who cannot tell "what was found" from "what we think it means" cannot calibrate trust. The coordinator's synthesis role is real and should not be reduced to mechanical concatenation, but its interpretive additions must be marked.

Boundary. The boundary is sourced versus inferred. The nearby opposite case is presenting interpretive analysis "with the exact same confidence and framing as directly-sourced facts": that hides the epistemic gap. Another opposite is forbidding the coordinator from adding any analysis: that undervalues the synthesis role.

Recurring specifics. The phrasing "distinguish sourced findings from synthesis-level interpretation" and "label as the system's analysis" recurs. The correct option preserves both while marking the distinction.

Wrong answers written against this rule

Proposal. only concatenate findings verbatim.

Why it attracts. safe.

Why it fails. undervalues synthesis.

When it would be right. never as the sole role.

Proposal. present interpretation at equal confidence.

Why it attracts. seamless.

Why it fails. misrepresents nature.

When it would be right. never.

Proposal. only subagents produce content.

Why it attracts. clean roles.

Why it fails. removes coordinator synthesis.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the inferred section but the answer holds. A mutation testing whether the coordinator may interpret probes the boundary between valuable synthesis and mislabeled confidence.
R20

Do not fabricate or borrow a citation

When an extracted value cannot be backed by any passage in the source text, the honest output is to return the value as unknown with a note that no citable source passage supports it. The subagent must not infer the most likely value, must not quietly borrow support from file metadata that is not a source passage, and must not skip the citation requirement.

A citation is a claim that a passage supports a value. If no passage does, manufacturing or borrowing one is a false claim that undermines every downstream consumer. Saying "unknown, with a note" preserves integrity and lets the coordinator decide.

Boundary. The boundary is source-text passage versus external metadata. The nearby opposite case is extracting a department from file metadata and citing the metadata field as the supporting source: when the application supplies metadata (not the document text), the tested material says the honest move is to flag the value as unsupported by a source passage, not to treat metadata as a citable passage. I note a subtlety: structured metadata passed alongside raw text (Rule 3, Rule 149-style) is legitimate attribution when the system owns that metadata; the distinction is whether the metadata is itself a genuine source of truth versus a stand-in for missing text. the tested material treats "no source passage supports it" as the trigger to flag unknown.

Recurring specifics. The phrasing "when no genuine source passage supports an extracted value, say so explicitly rather than manufacturing or borrowing a citation" recurs across policy-department and carrier-extraction framings.

Wrong answers written against this rule

Proposal. infer the most likely value.

Why it attracts. fills the field.

Why it fails. fabrication.

When it would be right. never.

Proposal. borrow support from file metadata.

Why it attracts. has a clue.

Why it fails. not a source passage.

When it would be right. only when the metadata is the authoritative source of truth by design.

Proposal. skip the citation requirement.

Why it attracts. unblocks validation.

Why it fails. hides the gap.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the extracted field but the answer holds. A mutation testing whether the candidate invents a citation probes the fabrication boundary.
R21

Consolidation preserves original references

When duplicate records are merged (exception reports, medication instructions, obligations), each merged result must still carry every original source reference and the original text it came from. Consolidation is useful, but only if a merged entry can be traced back to each contributing document.

Merging without traceability forces investigators to reopen every source to verify anything under dispute, which is the exact problem consolidation was meant to reduce. Preserving references keeps both benefits: one clean entry and full auditability.

Boundary. The boundary is merge versus drop. The nearby opposite case is stopping the merge entirely "so every entry stays tied to one document": that preserves traceability but forfeits the consolidation benefit. Another opposite is a summary note at the end saying "came from multiple sources": that does not bind a specific merged entry to its origins.

Recurring specifics. The phrasing "preserve traceability to original sources even after combining" recurs across carrier incidents, discharge instructions, and obligation matrices.

Wrong answers written against this rule

Proposal. stop merging.

Why it attracts. safe.

Why it fails. loses consolidation benefit.

When it would be right. only if merge is impossible to trace.

Proposal. merge only when three sources agree.

Why it attracts. confidence.

Why it fails. arbitrary threshold, still loses refs.

When it would be right. never as the traceability fix.

Proposal. add an end summary note.

Why it attracts. simple.

Why it fails. no per-entry binding.

When it would be right. as a supplement only.

How the same rule gets re-asked
  • Mutations change the record type but the answer holds. A mutation testing whether the candidate drops traceability probes the merge boundary.
R22

Normalize source footprint before synthesis

In a coordinator context, verbose sources and last-appended sources disproportionately influence the output through length-based attention and recency bias. The fix is to normalize each source to a fixed-length summary before synthesis so every source has an equal context footprint, and to randomize or importance-weight the order of appended results.

Attention naturally scales with token volume, so a verbose source dominates regardless of instruction. Recency bias makes last-appended content disproportionately influential. Normalizing footprint and order counters both structurally rather than via probabilistic instructions.

Boundary. The boundary is context influence. The nearby opposite case is instructing the synthesizer to "weight all sources equally": that is probabilistic and does not overcome token-volume attention. Another opposite is placing verbose sources last to "let recency compensate": that swaps one bias for another.

Recurring specifics. The phrase "normalize each source to a fixed-length summary before passing to the synthesizer" recurs. Recency bias is documented as the stronger, more systematic effect than length.

Wrong answers written against this rule

Proposal. instruct equal weighting.

Why it attracts. fair.

Why it fails. probabilistic, beaten by token volume.

When it would be right. never as the structural fix.

Proposal. place verbose sources last.

Why it attracts. balances.

Why it fails. substitutes recency bias.

When it would be right. never.

Proposal. increase context window.

Why it attracts. fits all.

Why it fails. does not fix over-representation.

When it would be right. only for truncation, not bias.

How the same rule gets re-asked
  • Mutations change the bias source but the answer holds. A mutation testing whether the candidate recognizes recency bias probes the boundary.
R23

Surface inconsistencies with a conflict flag

Structured extraction output should include a conflict_detected boolean plus a detail field so that internally inconsistent documents (a header date contradicting a body date, two figures in one source) are flagged in the output rather than silently resolved by the model.

result.json
json
{
  "conflict_detected": true,
  "conflict_detail": "Header date 2024-01-15 contradicts body date 2023-11-02",
  "values": [
    { "value": "2024-01-15", "location": "header" },
    { "value": "2023-11-02", "location": "body" }
  ]
}

A model that silently picks one value hides the inconsistency from every downstream consumer. A flag makes the conflict explicit and lets the coordinator or validator decide. Rejecting any conflicting document is over-broad; lowering temperature does not surface the conflict.

Boundary. The boundary is flag versus resolve. The nearby opposite case is having the model pick the value it thinks correct and output only that: that hides the conflict. Another opposite is rejecting the document: that discards a partially useful source.

Recurring specifics. The conflict_detected boolean with a detail field recurs. The wrong moves are silent pick, blanket reject, and temperature lowering.

Wrong answers written against this rule

Proposal. let the model pick the correct value.

Why it attracts. decisive.

Why it fails. hides conflict.

When it would be right. never.

Proposal. reject conflicting documents.

Why it attracts. clean.

Why it fails. over-broad.

When it would be right. only for truly unparsable input.

Proposal. lower temperature.

Why it attracts. consistent.

Why it fails. does not surface conflict.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the inconsistency type but the answer holds. A mutation testing whether the candidate surfaces or hides probes the boundary.
R24

Citations provide traceability, not truth verification

A citations feature attaches exact source passages to claims, improving traceability: a reader can check that a statement is supported by the supplied material, and unsupported statements become easier to spot. It does not validate that the source material itself is accurate or current. Safety-critical outputs therefore still need human review.

Grounding moves the trust question from "did the model make this up?" to "is the document right?" That is real improvement but not risk elimination. If the supplied document is outdated, ambiguous, or wrong, a confidently cited answer can faithfully reflect a bad source. Citations currently cover text, not charts or maps, so visual content is outside the mechanism entirely.

Boundary. The boundary is traceability versus verification. The nearby opposite case is eliminating human review because "citations guarantee the most current version": citations cite whatever you provide, stale or not. Another opposite is assuming cited text is "independently verified as true": grounding is not truth.

Recurring specifics. The phrasing "citations show which passages support a claim but do not verify the documents themselves" recurs. High-stakes categories named include trail closures and wildlife guidance.

Wrong answers written against this rule

Proposal. remove review because citations guarantee currency.

Why it attracts. automation.

Why it fails. citations track provided docs, stale or not.

When it would be right. never.

Proposal. trust cited text as verified.

Why it attracts. confidence.

Why it fails. grounding is not truth.

When it would be right. never.

Proposal. review only charts and maps.

Why it attracts. scoped.

Why it fails. text claims also need review for high stakes.

When it would be right. never as the sole review.

How the same rule gets re-asked
  • Mutations change the domain but the answer holds. A mutation testing whether review can be dropped probes the truth-versus-traceability boundary.
R25

Few-shot conflict handling generalizes

When a synthesis agent is shown few-shot examples of preserving both values with attribution on one topic (say finance), it generalizes the underlying judgment to novel domains (say public health) rather than matching the demonstrated topic. The examples teach the pattern, not the subject.

Few-shot examples that demonstrate "annotate both with source attribution" encode a decision rule the model can apply wherever a conflict appears. Good examples show the behavior across edge cases, which transfers better than topic-specific wording.

Boundary. The boundary is pattern versus topic. The nearby opposite case is assuming the subagent "memorized the finance examples and coincidentally applied the wording": that misreads generalization as mimicry. Another opposite is claiming few-shot examples are unnecessary because conflict handling is inherent: the base model still needs the pattern demonstrated.

Recurring specifics. The phrasing "well-designed few-shot examples let the model generalize the underlying judgment pattern to novel domains" recurs. The wrong moves are memorization claims and necessity denials.

Wrong answers written against this rule

Proposal. the model memorized the examples.

Why it attracts. superficial.

Why it fails. misreads generalization.

When it would be right. never.

Proposal. few-shot is unnecessary.

Why it attracts. simplicity.

Why it fails. pattern still needs demonstration.

When it would be right. never.

Proposal. remove examples due to topic mismatch.

Why it attracts. controlled test.

Why it fails. mismatch is the point.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the demonstrated and tested domains but the answer holds. A mutation testing transfer probes the pattern-versus-topic boundary.
R26

Match source type to the question

For a question about an organization's own policy, internal documents are the source of truth and outrank third-party speculation. Citing a blog that guesses at the policy when an authoritative internal source was available is a provenance error. The source type must fit the question's nature.

Different questions have different authoritative sources. Treating an authoritative internal record and a speculative external post as equally valid lets the wrong source win. Provenance includes choosing the right class of source, not just linking whatever was found.

Boundary. The boundary is authority fit. The nearby opposite case is giving both sources equal weight as "perspectives": that treats authoritative and speculative as equivalent. Another opposite is refusing to research internal-policy questions at all: that abandons the task.

Recurring specifics. The phrasing "match source type to the question's nature" recurs. The wrong move is citing third-party speculation when an internal source was available.

Wrong answers written against this rule

Proposal. any topically related source is fair.

Why it attracts. breadth.

Why it fails. ignores authority fit.

When it would be right. never.

Proposal. equal weight as perspectives.

Why it attracts. balanced.

Why it fails. equates authoritative and speculative.

When it would be right. never.

Proposal. refuse internal-policy questions.

Why it attracts. safe.

Why it fails. abandons task.

When it would be right. never.

How the same rule gets re-asked
  • Mutations change the policy type but the answer holds. A mutation testing whether the candidate weights a blog equally probes the boundary.
R27

Parallel agents need a reconciliation stage

When several parallel specialist agents produce findings, contradictory causal claims can reach the final synthesis as established facts unless a structured reconciliation stage compares evidence, marks unresolved conflicts, and triggers targeted follow-up before the synthesizer concludes. Parallelism needs a convergence mechanism.

Parallel agents remove explicit inter-agent conflict; a single synthesizer may simply pick a narrative. A reconciliation stage preserves disagreement as explicit uncertainty and focuses evidence gathering where material conflicts exist. More context alone makes conflicts visible but does not ensure handling; self-reported confidence can be poorly calibrated.

Boundary. The boundary is convergence versus delegation. The nearby opposite case is replacing parallel agents with one generalist "so there are no disagreements": that removes specialization and hides uncertainty inside one output. Another opposite is averaging confidence scores into a decision rule: that converts unsupported certainty into a false rule.

Recurring specifics. The phrasing "insert a structured reconciliation stage that compares evidence, marks unresolved conflicts, and asks for targeted follow-up" recurs. The danger is treating contradictory causal claims as established facts.

Wrong answers written against this rule

Proposal. one generalist agent.

Why it attracts. no conflict.

Why it fails. loses specialization, hides uncertainty.

When it would be right. never.

Proposal. average confidence scores.

Why it attracts. quantitative.

Why it fails. miscalibrated, false rule.

When it would be right. never.

Proposal. larger context window.

Why it attracts. fits all.

Why it fails. no conflict protocol.

When it would be right. only for truncation.

How the same rule gets re-asked
  • Mutations change the specialist set but the answer holds. A mutation testing whether the candidate suppresses disagreement probes the boundary.
R28

Escalations hand off decision-ready context

When a pipeline escalates a conflict to a human, the handoff must bundle the research topic, the specific disputed claims, source citations, the analysis already performed, and a recommended reviewer decision. A bare flag ("conflicting data found") forces the reviewer to redo work the system completed.

An escalation with no context wastes the reviewer's effort and risks inconsistent decisions. Packaging the disagreement, sources, and prior analysis lets the reviewer continue exactly where the system left off. The general principle is that escalations hand off decision-ready context, not just a flag.

Boundary. The boundary is context richness. The nearby opposite case is escalating immediately to a human "as soon as any two sources return different values": that floods reviewers with low-value flags. Another opposite is having the synthesis agent auto-pick the larger sample and proceed: that resolves a human decision without context.

Recurring specifics. The phrasing "escalations should hand off decision-ready context, not just a flag" recurs across merger-valuation and vaccine-efficacy framings.

Wrong answers written against this rule

Proposal. escalate on any numeric difference.

Why it attracts. safe.

Why it fails. floods reviewers.

When it would be right. only for material, undecidable conflicts.

Proposal. auto-pick and proceed.

Why it attracts. autonomous.

Why it fails. resolves a human decision.

When it would be right. never.

Proposal. retry the same subagent.

Why it attracts. second chance.

Why it fails. may return the same result.

When it would be right. only if the first run was incomplete.

How the same rule gets re-asked
  • Mutations change the domain but the answer holds. A mutation testing whether the candidate ships a bare flag probes the boundary.
R29

Track supersession with dates

When documents form a regulatory timeline (a base rule, an amendment that supersedes part of it, a guidance note that qualifies the amendment), the system tracks each source's publication date and supersession relationships. Where provisions conflict, it applies the most recent in-force provision and notes what it replaced, rather than blending all three equally.

A superseded provision is no longer operative; presenting it alongside the current position as if equal gives outdated advice. Dating and linking supersession lets the system apply the correct in-force rule while preserving the audit trail of what changed.

Boundary. The boundary is explicit supersession. The nearby opposite case is retrieving only the most recent document and discarding earlier ones: that loses the explanatory history a reader may need. Another opposite is weighting by word count: longer documents are not more authoritative. The contrast with Rule 5 is precise: here a newer document explicitly replaces an older one, so applying the newer is correct; there, two credible sources merely disagree, so both are preserved.

Recurring specifics. The phrasing "track each source's publication date and supersession relationships; apply the most recent in-force provision and note what it replaced" recurs. Wrong moves are equal blending and word-count weighting.

Wrong answers written against this rule

Proposal. keep all three equal.

Why it attracts. complete.

Why it fails. gives outdated advice.

When it would be right. only with explicit "historical" labeling.

Proposal. weight by word count.

Why it attracts. quantitative.

Why it fails. length is not authority.

When it would be right. never.

Proposal. discard earlier documents.

Why it attracts. current only.

Why it fails. loses history.

When it would be right. only when history is irrelevant.

How the same rule gets re-asked
  • Mutations change the document chain but the answer holds. A mutation testing whether the candidate blends superseded text probes the boundary with Rule 5.
R30

Preserve native table structure and coordinates

Tabular data (financial tables, time series, equipment logs) must be represented with headers and row context, indexed as structured units, and carry source coordinates, so values are bound to their row labels and column headers. Flattening tables to prose or discarding headers loses the relationships that make a number meaningful.

instructions.md
markdown
| Quarter | Revenue |
|---------|---------|
| Q1      | 3.1M    |
| Q2      | 3.8M    |
| Q3      | 2.4M    |
| Q4      | 4.2M    |

A value without its row and column context is ambiguous; the model frequently associates "2.4M" with the wrong quarter when the spatial relationship is lost. Markdown tables explicitly show the row-label-to-value relationship and are reliably parsed, striking the best balance of token efficiency and structural clarity.

Boundary. The boundary is structured versus flattened. The nearby opposite case is plain text with spaces: that is the exact format causing misattribution. Another opposite is converting tables to prose before indexing: that weakens the representation. JSON arrays are machine-readable but harder for human authors to debug; HTML tables cost more tokens.

Recurring specifics. The phrase "represent tables with headers and row context, index structured units, and preserve source coordinates" recurs. Misattribution of quarters is the canonical failure.

Wrong answers written against this rule

Proposal. plain text with spaces.

Why it attracts. cheap.

Why it fails. ambiguous row-value link.

When it would be right. never.

Proposal. convert to prose.

Why it attracts. natural.

Why it fails. weaker representation.

When it would be right. never for exact lookup.

Proposal. JSON arrays.

Why it attracts. machine-readable.

Why it fails. hard for humans to debug.

When it would be right. behind a human-readable layer.

How the same rule gets re-asked
  • Mutations change the table type but the answer holds. A mutation testing whether the candidate preserves coordinates probes the boundary.
Global investment synthesis where two sources disagree and both values survive
A production walkthrough with the reasoning chain made explicit.

A multi-agent research system produces a synthesis report on global renewable energy investment. Two research subagents operate in parallel. Subagent A investigates IEA and OECD datasets. Subagent B investigates Bloomberg NEF and Wood Mackenzie datasets. Both output findings in the structured claim-source format with all five fields.

Subagent A returns global renewable energy investment reached 495 billion in 2023 from the IEA World Energy Investment Report 2024 published 2024-06-15 with excerpt total investment reached approximately 495 billion in calendar year 2023 representing a 17 percent increase over 2022, and investment growth has averaged 14 percent since 2020 with its excerpt. Subagent B returns 478 billion in 2023 from the Bloomberg NEF Annual Review 2024 published 2024-03-20 and growth averaged 11 percent since 2020 with its excerpt. The values differ slightly and the naive synthesis would pick one, average them, or pick the more recent publication, each of which destroys information.

The correct synthesis preserves both values with full attribution and explains the likely difference as different methodologies or coverage of technology categories. A financial data section renders as a table with columns for source, year, investment in billions, growth rate, and publication date, with one row for 495 at 14 percent from IEA on 2024-06-15 and one row for 478 at 11 percent from Bloomberg NEF on 2024-03-20. The table makes the difference immediately visible and each row carries its source and date.

A prose section follows the table and states investment growth estimates vary by source and restates each figure with its report name, publication month, and methodology note such as using calendar year 2023 data, then notes that both sources agree on the directional trend that investment is growing significantly above historical averages. Coverage annotations close the report with global renewable energy investment as well supported with two credible sources and slight disagreement, regional breakdown as limited with single source coverage from IEA, and technology-specific investment as unavailable where no subagent retrieved data in the session. A verifier walks the synthesis output, extracts every claim, looks up its citation, and confirms the citation contains the original source URL, document name, and publication date, with all claims passing and no claim paraphrased away from its source.

Distinctions that decide answers

ThisNot thisHow to tell them apart
Structured claim-source mappingFree-form prose findingA mapping is a JSON object with five required fields. Free-form prose is unstructured text with no traceable attribution. Structured mappings survive summarisation, free-form prose loses attribution on first paraphrase.
Arbitrary selection of one valueAnnotated both values with preservationArbitrary selection picks the more recent, more authoritative, or averaged value and discards the other. Annotated preservation keeps both values with full attribution. Selection destroys information, preservation lets the consumer decide.
Conflict explained by different datesTrue contradictionA conflict explained by different publication or measurement dates is a temporal trend not a data quality issue. A true contradiction is two sources reporting different values for the same measure and time period. Temporal context prevents false contradictions.
Financial data as a tableFinancial data as proseTables make numbers, comparisons, and trends scannable. Prose paragraphs bury numbers in sentences and make comparison harder. Tables are the appropriate rendering for financial data, prose is not.
Inline citationEnd-of-document referenceInline citations place the source immediately after the claim. End references require the reader to scan back. Inline preserves proximity between claim and source, end references lose proximity during reading.
Synthesis merge with mappings preservedSynthesis merge without mappingsMerge with mappings preserves every claim-source mapping through combination. Merge without mappings collapses findings into summary text and destroys attribution. The prompt must explicitly require mapping preservation.

Traps

Ask the prompt to preserve citations when the API can enforce them

The tempting answer. Instruct the model to cite every claim, because the mapping is application data and prompting is the flexible tool that works everywhere.

Why it fails. For content that enters the request as a document or a search result, the API owns the claim-to-source binding and does it better than an instruction can. Enabling citations makes the API return structured citation objects whose supporting passage is guaranteed to point into the document you supplied, and that passage does not count toward output tokens. A prompt-produced citation is generated text and can be plausible rather than valid.

What is correct. Enable citations on document blocks and use search result blocks with their required source and title for internally retrieved content, then use application-side mappings only for content that arrives some other way. Keep carrying attribution through your own multi-step merge either way, since nothing stops a later synthesis step from paraphrasing a citation away.

Select the most recent source when two credible sources conflict

The tempting answer. Pick the newer publication because recency feels like reliability and produces a single clean answer.

Why it fails. Arbitrarily selecting one value destroys information and presents false certainty. The difference may reflect different reporting periods or methodologies that the consumer needs to see.

What is correct. Annotate both values with source attribution, publication dates, and a possible explanation, letting the consumer decide how to interpret the difference.

Treat different numbers from different sources as contradictions

The tempting answer. Flag conflicting numbers as a data quality issue because they look inconsistent on the surface.

Why it fails. Different publication or data collection dates often explain different numbers as a trend. Without dates a 2023 report at 8 percent growth and a 2024 report at 12 percent look contradictory, with dates they show acceleration.

What is correct. Require dates in structured outputs and group findings by date when temporal context matters so the trend is visible.

Allow the synthesis agent to paraphrase without preserving claim-source mappings

The tempting answer. Let the synthesis agent paraphrase because paraphrasing feels like summarisation and keeps the output concise.

Why it fails. Attribution dies during paraphrasing. The synthesis produces statements like investment has grown significantly with no amount, no source, and no date, even though the subagent had all three. Downstream output becomes untraceable plausible text.

What is correct. Require the synthesis agent to explicitly preserve and merge claim-source mappings through every combination step and verify that every claim in the final output is traceable to a source.

Render all content types in a uniform format

The tempting answer. Use a single format such as all prose or all tables because uniform rendering feels consistent.

Why it fails. Different content types benefit from different formats. Financial data is best as tables for comparison, news reads naturally as prose for narrative, and technical findings are clearest as structured lists. Flattening degrades readability and comprehension.

What is correct. Select table, prose, or list based on content type and detect the type by topic label or schema marker before rendering.

Resolve conflicts during synthesis rather than preserving them

The tempting answer. Resolve the conflict in the synthesis step because producing a definitive answer feels more helpful than preserving ambiguity.

Why it fails. The synthesis agent does not have the context to resolve conflicts such as different methodologies, different reporting periods, or different coverage. Resolution belongs to the coordinator or consumer with full information.

What is correct. Complete analysis with conflicts intact, returning a structured conflict object with both values, both sources, both dates, and a possible explanation, without silently picking one.

Assume the synthesis agent will preserve attribution by default

The tempting answer. Trust that preservation is the natural behaviour because the subagents provided structured mappings.

Why it fails. Summarisation and paraphrasing actively destroy attribution unless explicitly instructed to preserve it. Without an explicit instruction the synthesis output becomes untraceable on first paraphrase.

What is correct. Add an explicit instruction to the synthesis prompt that every claim must be traceable to a specific source and that mappings are preserved through the merge process.

Going deeper than the task statement
Adjacent material that shows up in harder scenario questions.
Verifiability principle and changelog-aware synthesis

Mappings must enable verification that the excerpt actually supports the claim, and when two sources describe the same subject at different times the synthesis presents both with dates and explains how the newer changelog supersedes the older documentation.

Retrieval-Augmented Generation
Per-source attribution for the six context sources

External knowledge carries source URLs and publication dates, tools carry execution logs and tool identifiers, memory carries storage location and retrieval timestamp, and state carries session identifier and update history for end-to-end provenance.

Context Sources: A Six-Level Taxonomy
Evaluation, audit trail, and inline streaming citations

Periodic re-fetch verifies that excerpts still support claims to catch citation drift, an audit trail logs input mappings and merge operations for post-hoc verification, and streaming includes citation markers inline so attribution is visible as synthesis arrives.

Context Evaluation, Observability, and Governance
Build it
Prove provenance survives from subagent to synthesis to rendered output
  1. Define a structured claim-source mapping schema with five required fields of claim, sourceUrl, documentName, relevantExcerpt, and publicationDate and reject any finding with missing fields at the source.
  2. Implement two research subagents that each return arrays of claim-source mapping objects with all fields populated in ISO 8601 date format, each researching a different aspect of the same topic with publication dates included.
  3. Build a synthesis agent that merges findings from both subagents while explicitly preserving all claim-source mappings through the merge, combining related findings under topic headings but maintaining inline citations or a reference section linking each claim to its source URL, document name, and publication date without discarding any mapping.
  4. Handle conflicting sources by annotating both values with full attribution and possible explanations via a conflict object with conflictDetected true, values array, and possibleExplanation, never silently picking one value.
  5. Implement content-appropriate rendering that detects content type and renders financial data as tables with year, value, source, and publication date columns, news as prose paragraphs, and technical findings as bulleted lists, then verify end to end by walking the final output and confirming every claim traces to its original source URL, document name, and publication date.

Verify. Every claim in the final output traces to a source URL, document name, and publication date, conflicts preserve both values with explanation, and each content type renders in the format that maximises comprehension while gaps are marked rather than silently omitted.

Answer real questions on this task
8 exam-style questions written against the rules above. You get the reasoning and a note on every wrong option after each answer.
Context Management and Reliability exam
9 questions, the same number this domain contributes to the real 60-question paper. Drawn fresh from a larger pool each attempt, so a retake is a different paper. You can reveal the answer to any question while taking the exam, which locks that question. Otherwise answers stay hidden until you submit.
Cross-cutting decision table
DecisionChoose thisOver thisWhy
How to shrink a long conversationServer-side compaction with an explicit trigger, or tool result clearing when the bulk is old tool outputA hand-written client-side summarisation loopCompaction is the documented primary strategy: the API triggers on measured input tokens, produces a documented five-part continuation summary, and emits a compaction block it then uses as the truncation boundary.
Where to keep critical facts across long sessionsPersistent case facts block prepended to every promptProgressive summarisation of the history aloneAbstractive summarisation destroys amounts, dates, and identifiers that the block preserves by being excluded from compression. This holds whether the summariser is your code or server-side compaction.
Where to place critical content in a long inputKey findings at the very top, headers throughout, current question at the endCritical facts buried in the middle with an instruction to pay attention to everythingAttention is positional not instruction-driven, so top and end receive strong attention while middle is degraded.
Cost versus attention for a large static prefixPrompt caching with cache_control ephemeral at the static to volatile boundaryCaching without attention discipline or no caching at allCaching discounts price to about 10 percent but does not reduce the 100K attention footprint per call, so size discipline remains.
When to escalate a support caseExplicit human request, policy gap where policy is silent, inability after genuine failed attemptsNegative sentiment, politeness, or self-reported confidence below a thresholdSentiment and self-report measure feeling not difficulty and invert priority, routing easy frustrated cases while leaving hard calm cases automated.
How to handle ambiguous customer matchesAsk for an additional identifier via clarification requestSelect most recent or most active record heuristicallyHeuristic selection risks privacy violations and wrong-account actions, clarification is the only safe response.
How a failing subagent reports to its coordinatorStructured error with failure type, attempted action, partial results, and alternatives plus shouldRetryEmpty results marked as success or killing the whole pipelineStructured context enables retry or alternative use of partial results, silent suppression hides the gap and termination wastes completed work.
Whether to retry an empty resultDistinguish access failure where retry is warranted from valid empty where it is notRetry every empty or retry no emptyAccess failure means the query did not execute, valid empty means the query executed and found no matches. The fields isError and shouldRetry encode the decision.
How to keep codebase exploration precise across turnsScratchpad files plus isolated subagents plus summary injection between phasesLarger context window or restart without saved stateDegradation is burying of precise references under verbose output, not a capacity limit. Structure outside the main conversation contains it.
How to decide what goes into human reviewCalibrated per-stratum thresholds with stratified sampling that includes high-confidence automated itemsAggregate 97 percent accuracy or raw 0.95 confidence across all fieldsAggregate hides 45 percent segments and raw confidence is not comparable across fields until calibrated per stratum on labelled data.
How to allocate limited reviewer capacityDynamic priority queue ordered by uncertaintyEven distribution across all extractions or chronological orderEven distribution wastes time re-verifying easy high-confidence items while uncertain items that need judgement wait.
How to keep attribution through synthesisCitations on document blocks and search result blocks where the content allows it, then five-field claim-source mappings preserved through every mergeFree-form prose findings merged by paraphrasing, or a prompt instruction to cite carefullyFor documents and search results the API owns the binding and guarantees the cited passage points into the source. For everything else the mapping enforced at ingestion is the only guarantee, and either way a later merge can paraphrase attribution away unless application code carries it.
What to do when two credible sources disagreePreserve both values with full attribution, dates, and possible explanationPick the more recent, more authoritative, or averaged valueSelection destroys information and presents false certainty. Preservation lets the consumer decide and temporal context often explains the difference as a trend.
How to render synthesised sectionsFinancial data as tables, news as prose, technical findings as listsUniform format such as all prose or all tablesComparison, narrative, and hierarchy each benefit from a different format. Uniform flattening degrades comprehension.
Why wrong answers keep looking correct
Progressive summarisation alone will manage a transactional history without losing critical facts. Abstractive summarisation systematically collapses amounts, dates, and identifiers into vague generalities that look reasonable but cannot drive correct action. The exam uses a multi-issue session to show the loss and expects the persistent facts block as the structural fix, not a better summariser.
Telling the model to pay attention to all parts of the context defeats the lost-in-the-middle effect. Positional attention is shaped by position not by intent. The model attends more reliably to the beginning and end regardless of instruction, so telling it to pay attention does not change the primacy and recency pattern. Placement at the top and end plus explicit headers is the fix.
Prompt caching eliminates the cost of a large system prompt so size no longer matters. Caching discounts repeat-call price to about 10 percent but does not reduce per-call attention footprint or the degradation that comes with a larger request. A 100K cached prompt still occupies 100K of attention budget every call, so size discipline under 10 percent still applies.
Managing a long conversation means writing your own summarisation loop, because history is application state. Server-side compaction is the documented primary strategy. The API triggers on measured input tokens at a threshold you set, defaulting to 150,000 with a floor of 50,000, generates a five-part continuation summary, and emits a compaction block that it then treats as the truncation boundary on later requests. When the bulk of the window is old tool output rather than dialogue, tool result clearing is the finer instrument. A hand-rolled loop reproduces all of that and typically gets token accounting wrong, because accumulated cache-read counts from server-side tool calls inflate a naive total and fire compaction far too early.
Preserving citations through synthesis is a prompting problem, so instruct the model to cite carefully. For content that enters the request as a document or a search result, the API owns the binding. Enabling citations returns structured citation objects whose supporting passage is guaranteed to point into the document you supplied and does not count toward output tokens, which a generated citation cannot guarantee. Prompt-preserved five-field mappings are the mechanism for content that arrives some other way, and application code still has to carry attribution across a multi-step merge in both cases.
Frustration or a low self-reported confidence score is a reliable signal to escalate to a human. Frustration measures emotional state not case complexity, and self-reported confidence is poorly calibrated and often confident on hard cases and hesitant on easy ones. The exam builds an agent that escalates simple frustrated cases while attempting complex calm ones to show the inversion, and expects explicit gap and inability criteria instead.
A larger context window fixes context degradation during codebase exploration. Degradation is verbose output burying precise class names and file paths under newer output, not exhaustion of capacity. A larger window still fills with verbose output and the same burying recurs. Scratchpad files and isolated subagents are the fixes, not capacity.
Returning empty results marked as success keeps the pipeline moving and is therefore safe. That is silent suppression, the worst anti-pattern, because the coordinator believes the search succeeded and found nothing and will never retry or try alternatives. The synthesis silently omits an entire area and the gap is invisible. Structured error propagation with partial results is the correct middle ground.
A 97 percent aggregate accuracy means the system is ready to automate high-confidence extractions. Aggregate hides per-type and per-field accuracy. Standard invoices at 99.5 percent can coexist with handwritten receipts at 60 percent and international documents at 45 percent inside the same 97 percent aggregate. Decisions must be made from the per-stratum matrix, not the aggregate.
When two sources report 12 percent and 8 percent for the same measure you should pick the more recent value or average them. Both values may be credible with different methodologies or reporting periods and temporal context often explains the difference as a trend. Selecting or averaging destroys information and presents false certainty. Preserve both with full attribution and possible explanation.
Last five minutes
Rules
  • If the history is summarised and the task is transactional, look for a persistent case facts block prepended to every prompt and excluded from compression. Summarisation without it is the trap.
  • If a fact is missed in the middle of a long context, look for positional placement with key findings at the top and current question at the end, not an instruction to pay attention to everything.
  • If a prompt is large, look for cache_control ephemeral at the boundary between static and volatile content starting at index 0 with about five minute TTL, and remember cached reads still occupy full attention budget.
  • If a customer is frustrated, acknowledge and resolve when the issue is straightforward. Escalate only on explicit I want a human, a policy silence where the policy does not speak, and genuine inability after failed attempts.
  • If a name search returns multiple records, ask for an additional identifier. Never select by recency or activity regardless of how recent or active the record looks.
  • If a subagent times out, expect a structured error with failureType, attemptedAction, partialResults, and alternativeApproaches with isRetryable. Empty success is silent suppression and killing the pipeline is termination, both are wrong.
  • If a query returns empty, distinguish access failure where the query did not execute and should retry from valid empty where the query executed correctly and found no matches and should not retry.
  • If exploration spans many files and the answer cites typical patterns, choose scratchpad files, isolated subagents, and summary injection between phases over a larger window or a restart without saved state.
  • If accuracy is reported as 97 percent aggregate, demand the per-type and per-field matrix before automating and require calibration per stratum on labelled data before any confidence threshold is set.
  • If review capacity is limited, choose a dynamic priority queue ordered by uncertainty over even distribution or chronological order, and require high-confidence automated items in the stratified sample.
  • If synthesis combines multiple subagents, require five-field claim-source mappings preserved through every merge with inline citations, preserve both values when sources conflict with dates and explanation, and render financial data as tables, news as prose, and technical findings as lists.
Trigger phrases

Look for summarisation versus persistent facts block, lost in the middle versus positional placement, caching at the end versus at the static to volatile boundary, sentiment versus explicit request and gap and inability, heuristic match selection versus clarification, empty success versus structured error with partial results, access failure versus valid empty, larger window versus scratchpad and isolated subagents, aggregate 97 percent versus per-stratum matrix, raw 0 point 95 confidence versus calibrated per field, even review distribution versus priority queue, paraphrased synthesis versus five-field mapping preserved through merge, single value picked versus both values annotated, and uniform prose versus content-appropriate tables and lists.

If you see X, think Y

If you see a multi-issue session where summarisation turns 247.83 on March 3rd into recent order, think protected facts block outside compression. If you see a 180K context where a policy buried in the centre is missed, think lost in the middle plus context rot and move key findings to the top. If you see volatile user message at index 0 and static system at index 1 with a cache marker at the end, think no hit because caching is prefix from index 0. If you see frustrated and confidence below 0 point 7 used to escalate, think inversion trap and choose explicit gap and inability criteria. If you see three John Smith records and most recent selected, think privacy violation and choose clarification. If you see results empty with status success after a timeout, think silent suppression trap and choose structured error with partial results and shouldRetry. If you see larger window proposed to fix typical repository pattern answers after ten files, think isolation and scratchpad not capacity. If you see high-confidence automated extractions excluded from sampling, think novel pattern missed. If you see investment reported as 495 billion with no source URL, document name, excerpt, or date, think attribution died and require the five fields through every merge.