Skip to content
Domain 6
11%

Quick Reference: Domain 6 — Prompt and Context Engineering

Bloat and Drift Are Different Faults (6.1)

Two symptoms, two causes, two fixes, and half the sub-skill turns on telling them apart.

FaultSymptomCauseFix
BloatCost and latency climbing per step, request eventually will not fitFull tool payloads, whole files and complete search results accumulatingPrune spent tool output, compact the settled transcript, isolate the sprawling step
DriftOutput changing character while the task stays the same: formats vary, a rule gets skipped, an established fact gets contradictedStanding rules stated once, hundreds of turns backMove the rules to a surface present on every request

Anthropic's framing is an attention budget: "Every new token introduced depletes this budget by some amount", so as volume grows "the model's ability to accurately recall information from that context decreases".

The standing distractor in every degradation item is a bigger context window. It postpones the size failure, does nothing for recall, and costs more per step on the way. Window size fixes neither fault.

Load just in time. The cheapest cure for bloat is to keep material out of the window until the task needs it. Keep "lightweight identifiers (file paths, stored queries, web links, etc.)" and load the data through tools when the task needs it, rather than pasting everything up front. Runtime lookups are slower, so the engineering guidance (current as of September 2026) also describes a hybrid: a small core up front, the rest fetched as needed.

Pruning Spent Tool Output (6.1)

The guide calls it tool output pruning. Current docs implement it as context editing, a Messages API beta behind the header context-management-2025-06-27, strategy type clear_tool_uses_20250919.

ParameterWhat it does
triggerInput-token count at which clearing starts (default 100,000)
keepHow many of the most recent tool use and result pairs stay intact (default 3 tool uses)
clear_at_leastMinimum reclaimed before clearing is worth doing
exclude_toolsTools whose uses and results are never cleared
clear_tool_inputsWhether the call's arguments go with its result (default false)

Three mechanics matter more than the list. Clearing is server-side, so your client keeps the full unmodified history. Cleared results are replaced with placeholder text telling Claude something was removed, so nothing vanishes silently. And the response reports what happened in context_management.applied_edits, with cleared_tool_uses and cleared_input_tokens: the numbers worth logging per step. The token-counting endpoint previews the effect, returning original_input_tokens beside the post-clearing input_tokens.

Why clear_at_least exists: clearing invalidates the cached prefix from the clearing point, so a trivial reclaim costs a cache write and saves nothing. clear_thinking_20251015 does the same job for extended-thinking blocks and must be listed first in the edits array when both are present.

Compaction and the Preservation Contract (6.1)

When a long transcript has to continue rather than end, compaction replaces the settled portion with a structured summary. As of September 2026 it comes in two beta forms, and the docs are plain about the choice: "Use on-demand compaction wherever it is available."

FormWho decides whenHeader and parameterWhat you send back
On demandYour code, by sending a compaction requestcompact-2026-09-04; top-level "compaction": {"type": "summarize"}; the reply is one compaction block with stop_reason compactionThe block first in messages, in place of the messages it summarises
At a token thresholdThe API, when input tokens reach the triggercompact-2026-01-12; a compact_20260112 edit in context_management (minimum trigger 50,000 input tokens, default 150,000)The response, appended as usual; the API ignores every content block before the compaction block

The two stay apart: the compaction parameter cannot be sent with context_management.

Three facts decide whether the technique is used correctly:

  • The summarised turns stop reaching the model, and nothing is archived anywhere retrievable. Documents, images and fetched URLs inside them are lost too, so re-send anything a later turn still needs.
  • A custom instructions string replaces the default summarisation prompt entirely. Anything the default kept is no longer asked for unless your string names it: "Summarise the code changes" gets you the code changes and little else.
  • Billing catches people out. The compaction pass is its own iteration in usage.iterations, and the top-level input_tokens and output_tokens exclude it (on an on-demand request they read zero), so a cost model reading only those undercounts every compacting run.

Context awareness. As of September 2026, Claude Sonnet 5, Claude Sonnet 4.6, Claude Sonnet 4.5 and Claude Haiku 4.5 track how much of their window is left and may start wrapping up as it nears the limit. In a harness that compacts, say so in the prompt and tell the model not to stop early.

The preservation contract is something you write down: keep decisions and their reasons, established facts and sources, constraints and prohibitions still binding, open questions, and the state of unfinished work. Drop raw payloads already reduced to facts and superseded attempts. Anthropic's warning is blunt: "overly aggressive compaction can result in the loss of subtle but critical context whose importance only becomes apparent later". What that produces is a quiet contradiction fifteen steps on, not a crash.

Isolation, and Where a Fact Should Live (6.1)

Isolation is not summarising the sprawl afterwards. It is keeping it out of the main window in the first place: each subagent "runs in its own conversation, which starts fresh unless the subagent is a fork", and "either way, intermediate tool calls and results stay inside the subagent; only its final message returns to the parent".

The handover is the half that gets skipped, and it runs both ways. Unless the subagent is a fork, its context window "starts fresh, with no parent conversation, but isn't empty": it gets its own system prompt and the Agent tool's prompt string, and no parent conversation history, tool results or system prompt. So every path, constraint and convention goes into that prompt, and the return shape is specified as precisely as the inputs.

Reach for a subagent when the task produces verbose output you do not need in the main context, when you want to enforce specific tool restrictions or permissions, or when the work is self-contained and can return a summary. Stay in the main conversation when the task needs frequent back-and-forth or iterative refinement, when multiple phases share significant context, when the change is quick and targeted, or when latency matters, since a subagent that is not a fork starts fresh and may need time to gather context. The same shape without subagents is a multi-step workflow: run the sprawling step as its own API call, keep the structured result, start the next step from that rather than from the transcript that produced it.

Transcript versus memory: a transcript records how you got here; memory is the set of facts that must still be true tomorrow. Replaying a prior conversation so the model "remembers" pays full input cost for everything that ever happened, into the most crowded possible window, every session. The memory tool is the documented alternative. It is generally available with no beta header, declared as {"type": "memory_20250818", "name": "memory"}, and client-side: Claude issues view, create, str_replace, insert, delete and rename against paths under /memories, and your handler executes them. The injected memory protocol names the design assumption: "ASSUME INTERRUPTION: Your context window might be reset at any moment, so you risk losing any progress that is not recorded in your memory directory."

The three techniques compose: prune the spent payloads, compact the settled transcript, and write anything that must outlive the compaction into memory before it fires. (The docs say compaction needs no client-side bookkeeping. That holds for the threshold form. On demand, the swap is your code's job.)

Ordering Is a Context Decision (6.1)

With a long document and a question about it, the document goes first. For inputs over about 20k tokens: "Place your long documents and inputs near the top of your prompt, above your query, instructions, and examples", with the measured claim that "queries at the end can improve response quality by up to 30 percent in tests, especially with complex, multidocument inputs".

The mechanism is that the instruction the model acts on is the last thing it read. Two refinements travel with the rule: wrap each document in its own tags with source metadata so multi-document inputs stay individually addressable, and ask Claude to quote the relevant parts first, before carrying out the task, which narrows attention and leaves you a string you can search for in the source.

Placement Beats Wording (6.2)

The Messages API is stateless: you re-send the whole conversation every request, so where an instruction sits is a decision you re-make on every call.

  • Top-level system field carries durable material: who Claude is here, standing rules, tone, scope limits, refusal boundaries. It sits ahead of every message, so it is present on every turn.
  • User turn carries per-request material: this task, this document, this question.

The recurring scenario is standing rules put in the opening user message of a long session and ignored by the twentieth exchange. The answer is placement. Capitalising the rule, adding ALWAYS, or repeating it three times does not move it closer to the turn where it has to apply, and every repetition spends context on the drift it is trying to cure.

Mid-Conversation System Messages (6.2)

Generally available, no beta header. As of September 2026: Claude Fable 5.1, Claude Mythos 5.1, Claude Fable 5, Claude Mythos 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8 and Claude Sonnet 5.5. Not available on Claude Sonnet 5; use the top-level system field there. Append a message with role system where the new instruction becomes relevant instead of editing the top-level field, which keeps the earlier turns byte-identical and the cached prefix intact.

Authority is the point: "a user message is treated as coming from the end user, while a system message is treated as coming from you, the application operator. When the two conflict, system instructions take precedence." Later system messages take precedence over earlier ones and over the top-level field for the turns that follow.

Placement rules, all examinable, all returning a 400 when broken:

  • It cannot be the first entry in messages. Anything applying from the start belongs in the top-level field.
  • It must immediately follow a user turn (a turn carrying tool_result blocks counts) or an assistant turn ending in a server tool result.
  • It must either end the array or be immediately followed by an assistant turn.
  • Between a tool_use block and its tool_result is the position named explicitly as invalid.
  • These rules bind a message that carries content. As of 14 October 2026 an empty-content message that only sets output_config.effort is accepted anywhere, unless it sits next to one that carries content.

Turn-scoped messages (beta as of September 2026, header mid-conversation-system-clear-at-2026-08-21). Set clear_at to "next_user_message" and the message renders only until a later user message exists (a tool_result-only user message counts). Then it stays in the array, renders nothing and costs no input tokens. The use is a per-turn reminder in a tool loop: append a fresh copy after each round of tool results and re-send the old copies unchanged.

Never a place for untrusted content. System content is treated as operator instruction and followed, so raw tool output or a fetched document put there gains operator-level authority.

Instruction Surfaces (6.2)

A Claude application carries instructions on more surfaces than the system prompt: tool descriptions, schema field descriptions, and in Claude Code the CLAUDE.md hierarchy and rules files.

The rule for choosing is mechanical: put the instruction on the surface guaranteed to be present at the moment it must apply. A constraint on how one tool's arguments are built belongs in that tool's description, where it is read every time the model considers that tool, and is wasted in a system prompt carrying twenty other things. A constraint on the whole application's tone belongs in the system prompt and is invisible inside one tool's description.

One rule stated three ways is three rules. That is a conflict the model resolves at run time, and the resolution changes whenever any one copy is edited. Pick the surface, state it once, delete the others.

Clarity, and Examples (6.2)

The golden rule is a usable test: "Show your prompt to a colleague with minimal context on the task and ask them to follow it. If they'd be confused, Claude will be too."

  • Say what to do, not what to avoid.
  • Give the motivation. The documented example replaces "NEVER use ellipses" with a sentence explaining that a text-to-speech engine will read the output, which also covers the adjacent cases you did not think to prohibit.
  • Decompose compound instructions into ordered steps; number them when order or completeness matters.
  • State the audience, the format and the length explicitly.

How current models read wording (as of September 2026):

  • Literal scope. Claude Sonnet 5 and Claude Opus 4.8 do not silently generalise an instruction from one item to the next. State the scope: "Apply this formatting to every section, not just the first one."
  • Suggest versus act. "Can you suggest some changes" may get only suggestions. Say "Change this function", or add a standing default-to-action instruction.
  • Post-tool summaries. Current models may skip the summary after tool calls. Ask for one if someone is watching.
  • Prompt style. Match your prompt's formatting to the output you want. Less markdown in the prompt can mean less in the response.
  • Emphasis overshoots. Claude Opus 4.5 and Claude Opus 4.6 respond more strongly to the system prompt, so "CRITICAL: You MUST use this tool when..." can over-trigger. Write "Use this tool when...".

Examples are the strongest signal you can put in a prompt: "examples are one of the most reliable ways to steer Claude's output format, tone, and structure". Three to five, each relevant (mirroring the real use case), diverse (covering the edge case you care about, varied enough that no unintended pattern is learned) and structured (wrapped in example tags, multiple examples inside an examples wrapper).

The failure modes are as tested as the technique. Inconsistent examples teach inconsistency. Examples of the wrong shape outrank your description. An undelimited example is indistinguishable from the task, which is how a model answers the example instead of the question. Three consistent examples beat eight that disagree.

Output Constraints, and Two Techniques That Are Gone (6.2)

State the shape you want: describe the format explicitly, give a template or worked example of the exact shape, use XML format indicators to demarcate a section, and where the output feeds a machine move to structured outputs, which are enforced rather than requested.

max_tokens is not a length control. It is a hard cap on generation. The response does not become shorter and tighter; it stops mid-sentence, and the API says so with stop_reason of max_tokens. In a tool-use turn it can leave an incomplete tool_use block that has to be retried at a higher cap. For brevity, ask for two sentences with no preamble and show an example of two sentences.

Two older techniques need dating rather than quiet omission:

TechniqueCurrent stateReplacement
Prefilling the assistant turn"Starting with Claude 4.6 models and Claude Mythos Preview, prefilled responses ... are no longer supported"; such requests return a 400Structured outputs for format; "Respond directly without preamble." in the system prompt
temperature, top_p, top_kAny non-default value on Claude Opus 4.7 or later returns a 400; the Python SDK (v1.0 and later) no longer defines them and raises a TypeError firstOmit them from the payload entirely

The keyed reasoning is unchanged by the removals: sampling never addressed format, focus or caution in the first place.

Structuring Untrusted Input (6.2)

The guide files input sanitization here, and the half that belongs to this sub-skill is ordinary hygiene: making sure the model can tell your instructions from the material you asked it to work on. Four documented techniques.

  • Delimit. Wrap each type of content in its own XML tag, use consistent descriptive names, nest when there is a hierarchy.
  • Label. Say what the content is and where it came from, so the model can calibrate how far to trust it.
  • JSON-encode. Where possible, wrap third-party strings in a JSON object rather than concatenating them, because "JSON escaping provides unambiguous delimiters between the untrusted payload and the surrounding structure, so an attacker cannot close a quote or tag to 'break out' into an instruction context".
  • Route it. Third-party content goes in tool_result blocks, never in system prompts or plain user text. The corollary catches people out: your own instructions must not live inside tool results either, because content there is treated as untrusted data and may be ignored or flagged. Send them in the user turn that follows the tool result, or in a mid-conversation system message.

Beyond the four: strip or escape stray markup that would break your own structure, never concatenate incoming text against an instruction sentence, and truncate or chunk deliberately rather than letting an oversized paste push your instructions out of view. Domain 7 owns the adversarial threat model; this sub-skill owns doing the structuring correctly whether or not anyone is attacking you.

Iterating So a Change Can Be Attributed (6.2)

Two cheap things, usually skipped:

  • A fixed evaluation set. Real inputs, including the two edge cases that actually bite, scored the same way every run. The docs put this ahead of any technique.
  • One variable at a time. Move the rules into the system field and re-run. Add two consistent examples and re-run. Replace the described format with an enforced schema and re-run.

The set stays still while the prompt moves. Adding cases mid-experiment because the new prompt exposed a new failure makes every earlier score incomparable: note the case, finish the comparison, then extend the set and re-baseline.

Asking the model which change helped is not evidence. It has no privileged access to why its own distribution shifted, and a fluent post-hoc rationalisation reads exactly like an explanation. The score on the fixed set is the evidence. Where a change targets a rare failure, sample the same case repeatedly, because one pass through a non-deterministic system cannot tell a fix from luck.

A model move is an iteration too. As of September 2026, Claude Opus 5 verifies its own work unprompted, and verification steps carried over from older prompts cause over-verification. Remove them rather than reword them, then re-score.

Three Ways to Produce Structured Output (6.3)

The ranking is about who enforces the shape.

MechanismHowWhen
JSON outputsoutput_config.format with type json_schema; "Structured outputs guarantee schema-compliant responses through constrained decoding"The response itself is the structured value
Strict tool usestrict set to true on a tool definition, constraining tool names and inputsThe structured value is an action's arguments
Instruction plus exampleDescribe the schema, show one worked exampleNeither of the above is available

Both enforced mechanisms can be used in one request. The docs are blunt about the ordering: if you need Claude to always output valid JSON conforming to a schema, use structured outputs instead of the prompt-engineering techniques.

Four operational facts. The Python helper is client.messages.parse(...) with a Pydantic model, returning response.parsed_output. The helper still takes the model as output_format and sends output_config.format for you. On a raw request, as of September 2026, "The output_format parameter is deprecated and will be removed in the future. To use it anyway, add the structured-outputs-2025-11-13 beta header. Without it, the API returns a 400 error." The first request with a new schema pays grammar-compilation latency. Compiled grammars cache for 24 hours from last use, and changing output_config.format invalidates the prompt cache for that conversation thread.

Two more, as of September 2026. Required properties come first in the output, then optional ones, so a streamed or positional reader sees that order. If order matters, mark everything required. Strict mode has request-wide complexity limits: 20 strict tools, 24 optional parameters, 16 union-typed parameters, and a 400 ("Schema is too complex for compilation") when the compiled grammar is still too large.

The Schema Is Not the Validator (6.3)

Parsing answers "is this well-formed?". Validation answers "is this right?". Structured outputs collapse the first question, which is precisely why the second gets forgotten.

Unsupported in the schema subset: numerical constraints (minimum, maximum, multipleOf), string constraints (minLength, maxLength), recursive schemas, complex types within enums, external $ref, and array constraints beyond minItems of 0 or 1. additionalProperties must be set to false. A schema can guarantee thickness_mm is present and a number; it cannot guarantee it falls between 8 and 40.

Everything the schema cannot express is the validation layer's job, in four checks:

CheckCatches
StructuralMissing required fields, wrong types, enum values outside the declared set (compare case-insensitively: as of September 2026 enum and const capitalisation is not guaranteed)
Range and plausibilityNumbers outside domain bounds, dates outside the possible window, malformed formats
Cross-field consistencyA total that does not equal its parts, an end date before a start, a status incompatible with its own fields
ReferentialAn identifier that names nothing real; the only check that catches a confidently invented one

Write it as code that runs on every response, with one behaviour on failure: raise, with the raw response body and the request id attached. The substitute-a-default branch converts a loud failure that would have been fixed into a quiet wrong record trusted for months.

Check stop_reason Before the Payload (6.3)

Defensive parsing starts with the stop reason, not the content blocks. Seven documented values: end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal, model_context_window_exceeded (a compaction request adds an eighth, compaction; see 6.1). Three of them mean the content is not what you asked for.

  • max_tokens: the response hit your limit, so it is truncated.
  • model_context_window_exceeded: the response filled the model's window, and the documented instruction is to treat it as truncated.
  • refusal: Claude declined. It arrives as a normal HTTP 200 with a stop_details object naming the policy category, so an integration branching only on status sees a success with no useful content.

What makes this defensive parsing rather than error handling is that a truncated response can be syntactically valid up to the cut. A JSON object stopped after its fourth field still parses if your parser is lenient.

Never take everything from the first brace to the last. Taking everything between the first opening brace and the last closing brace succeeds on truncated output, on output wrapped in prose, and on output followed by commentary, turning three distinct failures into one silently wrong record. It also swallows a draft written before the final JSON. Without structured outputs, when JSON is asked for in the prompt, the documented parse (as of September 2026) reads only the text blocks, treats a max_tokens stop as failed, keeps the last complete JSON value, then checks its fields and retries once.

Scepticism, and Failed Validation (6.3)

Tone carries no information about accuracy. Fluent, well-formatted, appropriately hedged output is what the system produces whether or not the standard it cites exists.

  • Give permission to decline. "Allow Claude to say 'I don't know'... This simple technique can drastically reduce false information", and give the exact wording so the abstention is machine-detectable rather than a paragraph of hedging.
  • Ground in quotes. For documents over about 20k tokens, extract word-for-word quotes first, then perform the task. Make each claim carry a supporting quote and remove claims that lack one.
  • Best-of-N verification. Run the same prompt several times; disagreement between runs is a fabrication signal.
  • Citations. For documents you supply, set citations.enabled and the API extracts cited_text itself, so citations "are guaranteed to contain valid pointers to the provided documents". As of September 2026, citations with output_config.format in the same request return a 400.
  • The closing caveat sets the expectation: these techniques "don't eliminate them entirely. Always validate critical information".

When validation fails, re-asking with the identical request is the weakest move available: it samples the same distribution again and, if the schema or prompt is at fault, fails the same way indefinitely. Attach the specific errors: which field was missing, which value fell outside its range, which two fields disagreed. Cap the attempts explicitly, because an uncapped retry loop on a systematic failure is a cost incident. Then treat repeated failure as a defect: the same field failing across many inputs means the schema asks for something the source does not contain, the prompt has not said where to find it, or the validator asserts something the domain does not guarantee. Log the raw response, the stop reason, the usage block, the request id, the validation error and the input, so you can tell which.