Claude Certified Architect Study Guide & Mock Exams
A 35-lesson curriculum and 240 practice questions covering all five CCA‑F domains — every claim traced back to Anthropic’s own documentation.
Overview
The path
Three steps, in this order
1
15 min
Diagnose cold
Take the diagnostic before reading anything. The breakdown shows where your gaps actually are.
2
~157 min
Study weak domains
35 lessons with reference tables and the exam trap called out. Weakest domain first, not front to back.
3
120 min
Sit the full mock
60 questions scored out of 1000. Repeat until you clear 800, then book the real thing.
Where the marks are
Click a domain to open its lessons.
Exam at a glance
Questions60
Time120 min
Pass mark720 / 1000
Scenarios4 of 6
Cost$125
The agentic loop
Domain 1’s core mental model — and the single most-tested idea on the exam.
MCP architecture
One client per server, each with a dedicated connection. Domain 4 leans on this.
The seven anti-patterns
Most wrong answers are one of these. Learn to spot them and the distractors become obvious.
01 Enforcing a hard rule in the prompt instead of in backend code
02 Routing on the model’s self-reported confidence
03 Using the Batch API for a blocking, user-facing path
04 Reaching for a bigger context window to fix an attention problem
05 Subagents that fail silently instead of returning error context
06 Giving every agent the full tool set instead of role-scoping it
07 Flat all-to-all agent topology instead of a coordinator
Practice
Choose a mode
Questions are sampled fresh and options reshuffled on every attempt — repeating a mode is real practice, not memorising positions.
Your domain mastery
Tracked locally in your browser. Nothing is uploaded.
Mock exam
Question 1 of 60
120:00
Navigator
Answered
Unanswered
Flagged
Full contents
Complete curriculum outline
Every lesson on one page — what it covers, the diagrams, the exam trap and the comprehension checks. Good for a final revision pass or for printing. Click any lesson to open it interactively.
Domain 1: Agentic Architecture & Orchestration
27% of the exam · 9 lessons · ~40 min · 64 practice questions
1. The agentic loop and stop_reason 5 MIN
Every agent you build on the Messages API is the same four-step loop, and one response field drives it.
An agent is not a special API. It is an ordinary loop you write around the same POST /v1/messages endpoint you would use for a single question. What makes it agentic is that you keep going: you send a request, and if Claude asks for a tool you run it, hand back the result, and send again — as many times as it takes. The API is stateless, so nothing carries over between requests except the message history you resend. That is why the loop is yours to write, and why getting its exit condition right is the whole game.
The loop is: send request → check stop_reason → execute the requested tool → return the result → repeat. Nothing else controls continuation.
Continue looping while stop_reason is tool_use. Stop on end_turn.
Append the full response.content to your message history, not just the text. Dropping the tool_use blocks breaks the pairing the API requires on the next turn.
Each tool_result must carry the tool_use_id of the block it answers.
Bound the loop with a maximum-iteration counter. An agentic loop with no ceiling is an unbounded cost risk.
One iteration of the loop
What one turn actually looks like: You ask "what is the weather in Paris?" and pass a get_weather tool. Claude replies with stop_reason: "tool_use" and a tool_use block containing {"location":"Paris"} and an id like toolu_01A. Your code calls the real weather API, then sends the whole conversation again: original question, Claude's assistant turn including that tool_use block, and a user turn holding a tool_result with tool_use_id: "toolu_01A". Claude now has the data and replies with prose and stop_reason: "end_turn". Loop exits. Two API calls, one answer.
Check your understanding
Which response field decides whether the agent loop continues? Only stop_reason carries control meaning. Loop while it is tool_use; exit on end_turn.
What must you append to the message history after a tool_use turn? Dropping the tool_use blocks breaks the tool_use_id pairing the next request requires.
Exam trap: Distractors offer text-block presence or token counts as the control signal. Only stop_reason is.
Server-side tools run their own sampling loop. When it hits its limit you get a pause, not an error.
Server-side tools such as web search run their own loop inside a single API call — Claude searches, reads, searches again, all before you get a response. That inner loop has an iteration cap so a runaway search cannot spin forever on Anthropic's side. When it hits the cap the response comes back mid-thought, and the signal for that is pause_turn. It is not an error and nothing has gone wrong; the work is simply incomplete and the server is offering to carry on.
Server-side tools (web search, web fetch, code execution) run on Anthropic infrastructure — you declare them and results arrive as content blocks in the same response.
That server-side loop has a default iteration limit. Reaching it returns stop_reason: "pause_turn".
To resume: re-send the user message plus the paused assistant response. The server detects the trailing server-tool block and continues.
Do not append a "Continue." user message — it is unnecessary and pollutes the conversation.
Set a max_continuations ceiling (e.g. 5) so a pathological turn cannot loop forever.
Note the SDK tool runners do not auto-resume a pause — a paused turn ends the runner and is returned as the final message, silently truncated.
Check your understanding
stop_reason is pause_turn. What does it mean? pause_turn is not a failure. Re-send the conversation including the paused assistant turn and the server continues.
How do you resume a paused turn? The API detects the trailing server-tool block and resumes on its own. An extra 'Continue.' turn is explicitly unnecessary.
Exam trap: "Restart from scratch" and "increase max_tokens" both look reasonable. Restarting discards completed server-side work; max_tokens exhaustion is a different stop reason entirely.
One assistant message can request several tools. How you return the results decides whether it ever does so again.
When several tool calls do not depend on each other, Claude will often request them all at once in a single assistant message rather than one per turn. This is a large latency win — three lookups become one round trip instead of three. But it depends on a convention: the model learns from the shape of the conversation whether parallel calls actually get handled well. If your harness returns results in a shape that implies the calls were processed one at a time, the model adapts and stops batching them.
Parallel tool use is on by default — a single assistant message may contain multiple tool_use blocks.
Execute them concurrently, then return alltool_result blocks in a single user message.
Splitting results across multiple user messages silently trains Claude to stop making parallel calls, quietly destroying your concurrency.
A failed tool still gets a result block, with is_error: true and an informative message. Never drop it.
To force at most one tool per response, set disable_parallel_tool_use: true inside tool_choice.
The failure mode is silent: Claude requests get_weather, get_traffic and get_events in one message. Your code awaits all three, then appends three separate user messages, one result each. Nothing errors — the answer is correct. But you have just modelled a conversation in which those three calls were resolved sequentially. Over subsequent turns the model drifts toward requesting one tool at a time, and your parallelism quietly disappears with no failure to debug. The fix is one user message containing all three tool_result blocks.
Check your understanding
Claude returns three tool_use blocks. How do you return the results? Splitting results across messages trains the model to stop batching parallel calls.
A tool in a parallel batch fails. What do you send? Never drop the block — an omission breaks the turn, and an empty string looks like a successful empty result.
Exam trap: "Return them in completion order, one message each" sounds tidy and is exactly the anti-pattern.
4. Workflow patterns: chaining, routing, parallelisation 6 MIN
Three of the five canonical patterns. The exam tests which one fits a described workload.
Most production "AI systems" are not agents at all — they are workflows, where you decide the control flow in advance and the model fills in the reasoning at each step. That distinction is worth holding onto, because workflows are cheaper, faster and far easier to test. The named patterns below are just the handful of shapes that control flow tends to take. The exam gives you a scenario and expects you to recognise the shape, so read each one for its tell: what is it about the work that makes this pattern the right fit?
Prompt chaining — fixed, sequential steps where every stage always runs. Use when the workflow is predictable and each step is a distinct cognitive task. Isolating steps prevents attention dilution.
Routing — classify the input, then dispatch to exactly one specialised follow-on. Use when inputs fall into distinct categories best served by different prompts or tool sets.
Parallelisation — run independent work concurrently. Two flavours: sectioning (split a task into independent subtasks) and voting (run the same task several times and aggregate).
The tell for chaining is "all of these checks must always run". The tell for routing is "each ticket is one of these categories".
Voting triples cost, so it only earns its keep when you genuinely need consensus or higher confidence.
Prompt chainingRoutingParallelisation
Same task, three different shapes: "Review this pull request." If style, security and docs must all be checked every time, that is prompt chaining — three focused passes, merged. If the PR should go to whichever specialist matches its type, that is routing — classify once, dispatch to one. If you want three independent security opinions and will take the majority verdict, that is parallelisation by voting. The words in the scenario — "always", "whichever", "consensus" — are what tell you which.
Check your understanding
Style, security and docs must all be checked on every PR. Which pattern? All steps always run, so chain them. Routing would pick one branch and skip the other two.
Tickets are billing, returns or technical, each needing a different prompt. Which pattern? Classify once, then dispatch to exactly one specialist path.
Exam trap: Routing is offered where all branches must run (wrong — it picks one), and a single mega-prompt is offered where three concerns should be isolated.
5. Orchestrator-workers and evaluator-optimizer 4 MIN
The two patterns for work you cannot fully specify up front.
The first three patterns all assume you know the steps when you write the code. These two are for when you do not. In orchestrator-workers a model looks at the specific input and decides what the subtasks are — you cannot enumerate them in advance because they depend on the input. In evaluator-optimizer you cannot specify the output in one shot, but you can recognise a good one, so you generate, critique, and revise. The cost of both is real: dynamic planning and extra loops burn tokens and latency, so they only earn their place when the work genuinely resists being specified up front.
Orchestrator-workers — a central model decomposes the task dynamically and delegates to workers. Use when the subtasks cannot be predicted in advance. If you already know the steps, chaining is cheaper and more testable.
Evaluator-optimizer — one call generates, another critiques against criteria, and the loop repeats. Use when you can articulate what "better" means clearly enough to act on.
Vague criteria produce noisy evaluator loops that never converge. If you cannot state the bar concretely, this pattern will not help.
A separate fresh-context verifier generally outperforms in-context self-critique, because self-critique inherits the assumptions that produced the error.
Orchestrator-workersEvaluator-optimizer
Check your understanding
When does orchestrator-workers beat prompt chaining? If you already know the steps, chaining is cheaper, faster and easier to test.
What does evaluator-optimizer require to work? Vague criteria produce noisy loops that never converge.
Exam trap: "Ask the model to double-check its own work" is offered as the reliability answer. Independent verification beats self-review.
The single most-tested idea in the heaviest domain: subagents do not inherit context.
This is the heaviest-weighted idea in the heaviest-weighted domain, and almost every mistake traces back to one wrong assumption: that a subagent can see what the coordinator saw. It cannot. A subagent gets a fresh context window containing only the prompt you hand it. It has no access to the coordinator's conversation, its tool results, or its earlier findings, and there is no session id or handle that grants that access. Once you internalise this, the rest follows — every architectural rule in this lesson is a consequence of context not being inherited.
Subagents start with a fresh context window. They inherit nothing — not the coordinator's history, not its findings, not its tool results. There is no mechanism for one agent to read another's session.
Therefore the coordinator must inject all prior output explicitly, in a structured form that preserves attribution (claim, evidence, source, date) rather than prose that destroys it.
Use hub-and-spoke: a coordinator mediates between subagents. Flat all-to-all topology is a named anti-pattern — unpredictable state, duplicated work, nowhere to observe or enforce policy.
If the coordinator already holds the information, do the work in place. Spawning a subagent to restate what you are already holding is pure overhead.
Delegate only when the payoff exceeds the fixed cost: fresh context establishment, re-exploration, a report, and the coordinator re-reading it.
Emit independent Task calls in a single turn so they run concurrently. Wall-clock then equals the slowest task rather than the sum.
Agent topology
Why prose hand-offs lose information: A search subagent finds "$50B market size, McKinsey 2024, based on bottom-up sizing." It reports back to the coordinator as prose: "the market is around $50 billion." The coordinator passes that sentence to a report agent, which now has a number with no source, no date and no methodology — so when it needs a citation it invents one, and when a second subagent reports $35B the coordinator has no basis to reconcile them. Nothing errored. The information was destroyed at the first hand-off, by summarising into prose instead of passing a structured object.
Check your understanding
What does a subagent inherit from its coordinator? Context is never inherited. Everything the subagent needs must be injected into its prompt.
Which agent topology is recommended? Flat all-to-all is a named anti-pattern: unpredictable state, duplicated work, nowhere to observe or enforce policy.
Exam trap: "Point the subagent at the coordinator's session id" is an architectural impossibility dressed up as configuration.
How you brief a subagent, and what you let it touch.
Two levers decide whether a subagent is useful: what you tell it, and what you let it touch. On the first, there is a real tension — precise instructions feel safer, but they are exactly what makes an agent brittle, because a script that meets an unanticipated situation has nowhere to go. Delegating intent plus a quality bar lets the subagent adapt when the first approach fails. On the second, the instinct to hand over every tool "just in case" is a mistake twice over: it widens what a mis-selection can damage, and a larger tool surface measurably degrades selection accuracy.
Over-specified procedural instructions (exact queries, exact filters) make subagents brittle — when the script fails they report failure instead of adapting.
Prefer goal-oriented prompts: state the research intent, the minimum quality bar, and source-credibility criteria. That grants authority to adapt.
Scope tools by role. A research subagent should not hold deploy or delete tools. Universal tool access is a named anti-pattern — it widens the blast radius and degrades selection accuracy.
Subagent failures must return structured error context, never an empty result. An empty result is indistinguishable from "searched and found nothing", so the coordinator draws a false conclusion.
Brief precisely the first time. Launch → wait → re-brief cycles cost more than getting the brief right.
Check your understanding
A subagent gives up when its exact queries return little. Best fix? Over-specified procedural instructions are what make agents brittle. State intent and let it adapt.
Why scope tools per subagent role? Universal tool access is an anti-pattern, and a smaller tool set measurably improves selection.
Exam trap: "Add a rule to report failure if fewer than five results" reinforces the rigidity rather than fixing it.
There are two different situations that look similar and have opposite answers. If a session crashed, its tail is untrustworthy — a tool may have half-written its result, leaving the transcript in a state the model will read as fact. Resuming on top of that propagates corruption, so you extract what you have verified into a durable checkpoint and start clean. If a session is healthy but the world changed underneath it (files edited out of band), resuming is right — but the agent has no way to detect those edits, so you must name them. Ask yourself: is the session's own record damaged, or merely stale?
A crashed session's trailing tool results are unreliable — some may be partial or orphaned. Resuming directly on top of that state propagates the corruption.
The correct recovery is: extract verified findings into a durable structured checkpoint, start a fresh session, and inject the checkpoint — explicitly labelling which items are complete, partial and pending.
For a clean session where files changed underneath you, resuming is right — but you must name the delta. The agent cannot detect out-of-band file changes and will report stale findings as current.
Forking is for exploring divergent branches from a known-good point, not for recovering from a failure.
Do not discard the completed work: dropping the finished items destroys the cross-item context the synthesis step depends on.
Check your understanding
A pipeline crashed after 12 of 18 documents. Best recovery? A crashed session's tail is unreliable; resuming propagates corruption, and dropping the 12 loses cross-document context.
Files changed out of band on a healthy session. What must you do? The agent cannot detect out-of-band edits and will report stale findings as current.
Exam trap:--resume on the crashed session is the intuitive answer and the wrong one.
9. Agent or workflow? Surfaces, tiers and effort 5 MIN
When an agent is justified at all, which surface to build it on, and how to tune spend.
Before choosing how to build, decide whether to build an agent at all. Agents trade cost, latency and predictability for the ability to handle work you cannot fully specify — a bad trade when you can specify it. Once you have decided you need one, the surfaces differ mainly in how much of the harness you own: the manual loop gives you everything and nothing, the tool runner drives the loop over tools you define, and the Agent SDK hands you the whole Claude Code harness with built-in tools. All three run on your infrastructure; the difference is how much you write yourself.
Build an agent only when all four hold: complexity (multi-step, hard to specify up front), value (justifies cost and latency), viability (the model is capable here), and recoverable cost of error. Any "no" means stay at a simpler tier.
Claude Agent SDK — Claude Code as a library: built-in tools (read/write/edit/bash/glob/grep/web), the agent loop, context management, hooks, subagents, sessions. You host it.
Tool runner — an SDK helper that drives the loop over tools you define. No built-in tools, no filesystem. Still supports approval gates and per-turn intervention, so "I need control" is rarely a reason to hand-write the loop.
Manual loop — when you want to own everything, or avoid a beta dependency.
effort lives inside output_config and tunes thinking depth and overall token spend. It is not a hard cap — max_tokens is.
Adaptive thinking lets Claude decide when and how much to think, including between tool calls.
Check your understanding
Which set of criteria justifies building an agent? If any of the four is a no, stay at a simpler tier.
You need built-in file, bash and search tools plus subagents, self-hosted. Which surface? The tool runner drives a loop over tools you define and ships no built-in tools.
Exam trap: Spending the top tier at maximum effort on a simple high-volume classification. Match the tier to the step, not to the project.
20% of the exam · 7 lessons · ~33 min · 48 practice questions
1. Settings files and precedence 5 MIN
Five scopes, one order, and one important exception.
Claude Code reads configuration from several files at once, and the whole system only makes sense once you see what each layer is for. Managed settings are how an organisation enforces policy on a fleet of machines, so they must win. Local settings are your personal, gitignored overrides for one repo, so they beat the shared project file. Project settings are the team's committed defaults. User settings are your cross-project preferences, the weakest because they are the most general. Precedence is not arbitrary — it runs from most specific and most authoritative down to most general.
Managed settings cannot be overridden — that is the entire point of the scope.
The exception: permission rules merge across scopes rather than a higher scope replacing a lower one. A deny added at any layer still applies.
Managed-only controls exist to lock things down: allowManagedPermissionRulesOnly, allowManagedHooksOnly, allowManagedMcpServersOnly.
~/.claude.json is not a settings file in this hierarchy — it holds the OAuth session, MCP server config, and per-project state such as trust settings.
Precedence, highest first
The exception that catches people out: Your project settings allow Bash(git push:*). Your company's managed settings deny it. With ordinary override semantics the higher layer would simply replace the lower one — but permissions merge, so both rules are live and the deny still applies. This is deliberate: if a higher layer could silently drop a lower layer's deny rule, a project could weaken a personal safety rule you had set for yourself. Merging means a deny added anywhere is a deny everywhere.
Check your understanding
Which settings scope wins a conflict? Precedence runs managed → CLI args → local → project → user.
How do permission rules behave across scopes? Permissions are the documented exception to normal override semantics.
Exam trap: Assuming permissions override like every other setting. They merge.
The distinction the exam returns to again and again.
The single most important distinction in this domain is between things that are enforced and things that are merely suggested. Settings rules and hooks are enforced by the client: they run outside the model's decision-making and apply whatever Claude concludes. CLAUDE.md is delivered to the model as text, so following it is a judgement the model makes — usually correctly, but never with a guarantee. Any exam question phrased as "must never", "always", or "guarantee" is asking you to reach for enforcement, and any answer that reaches for stronger prose is wrong.
Three lists: permissions.allow, permissions.ask, permissions.deny.
Rules use a Tool(specifier) shape — Bash(npm run test *), Read(./.env), Read(./secrets/**).
Settings rules are enforced by the client regardless of what Claude decides. CLAUDE.md is context and carries no guarantee.
So: for a hard requirement use permissions.deny or a PreToolUse hook. For behavioural guidance use CLAUDE.md.
MCP servers have their own controls: allowedMcpServers, deniedMcpServers (which wins), enableAllProjectMcpServers, and enabledMcpjsonServers / disabledMcpjsonServers.
Why prompt wording cannot be the control: You write "NEVER read .env files" in CLAUDE.md. It works most of the time. Then a task involves debugging a config problem, the model reasons that reading .env is the helpful next step, and it does. Nothing malfunctioned — the instruction was context competing with a plausible goal. Worse, if Claude is reading a file that itself contains injected text, the prompt is exactly the layer an attacker targets. A permissions.deny rule for Read(./.env) is evaluated by the client and cannot be reasoned around or injected past.
Check your understanding
You must guarantee .env is never read. Where do you enforce it? Settings rules are client-enforced regardless of what the model decides; prose is guidance.
What is CLAUDE.md best used for? It is context delivered to the model, so compliance is a judgement, not a guarantee.
Exam trap: "Write it more forcefully in CLAUDE.md" is offered whenever a guarantee is needed. Emphasis never becomes enforcement.
Where memory files live, in what order they load, and why nested files behave differently.
Memory files are how you give Claude Code standing context about a codebase without repeating yourself every session. The design has two halves worth understanding separately. Discovery: Claude walks up from your working directory to the filesystem root collecting files, so a monorepo package inherits the repo-wide conventions above it. Delivery: everything collected is concatenated and sent as a user message after the system prompt — which is precisely why it is guidance and not configuration. Files below your working directory are handled differently, loading only when Claude actually reads a file there, so a large monorepo does not flood the context at launch.
Claude Code walks up the directory tree from your working directory, collecting CLAUDE.md and CLAUDE.local.md at each level.
All discovered files are concatenated, not overridden. Order is filesystem-root-first, so instructions nearest your working directory are read last.
Within a directory, CLAUDE.local.md is appended after CLAUDE.md.
Files in subdirectories below your working directory load on demand — when Claude reads a file in that directory — not at launch.
CLAUDE.md content is delivered as a user message after the system prompt. That is precisely why it is context rather than enforced configuration. For system-prompt-level text use --append-system-prompt.
Target under 200 lines per file. Longer files consume context and measurably reduce adherence.
After /compact, the project-root CLAUDE.md is re-read and re-injected. Nested files are not — they reload lazily.
Discovery walks up, loads root-first
Reading the order: You launch in repo/services/api/. Claude collects repo/CLAUDE.md (company-wide conventions), repo/services/CLAUDE.md (service conventions), then repo/services/api/CLAUDE.md (this service's specifics) — root first, nearest last. Nothing overrides anything; all three are in context together. If the root says "use tabs" and the API file says "use spaces", you have not configured a winner, you have handed the model a contradiction to resolve. Keep narrower files additive rather than contradictory.
Check your understanding
Where does a personal, all-projects CLAUDE.md live? ~/.claude.json holds OAuth session and MCP state — it is not a memory file. Several third-party guides get this wrong.
Three CLAUDE.md files exist up the tree. What happens? Nothing overrides anything; instructions nearest your working directory are simply read last.
Exam trap: Several third-party guides claim user CLAUDE.md lives at ~/.claude.json. It does not — it is ~/.claude/CLAUDE.md.
How to keep instructions out of context until they are relevant.
A root CLAUDE.md loads on every single session, so anything you put there costs context in every unrelated conversation. Rules files solve that. Dropping a markdown file into .claude/rules/ gives you the same instruction mechanism, but adding a paths glob in its frontmatter makes it conditional: the rule enters context only when Claude works with a file that matches. It is the difference between telling someone your API conventions every morning and handing them the note when they open the API folder.
Put markdown files in .claude/rules/. All .md files are discovered recursively, so you can organise into subdirectories.
A rule without a paths field loads unconditionally at launch, at the same priority as .claude/CLAUDE.md.
A rule withpaths frontmatter loads only when Claude works with files matching those globs. This is the mechanism for scoping instructions by file type or directory.
Path-scoped rules trigger when Claude reads a matching file, not on every tool use.
User-level rules live in ~/.claude/rules/ and load before project rules, giving project rules higher priority.
Rules vs skills: rules load every session (or on path match); skills load only when invoked or judged relevant.
Check your understanding
How do you load a rule only for TypeScript files under src/api/? Path-scoped rules are the purpose-built mechanism; a root CLAUDE.md loads every session regardless.
A rules file has no paths field. When does it load? Omitting paths is the normal way to write an always-on rule.
Exam trap: "Put a conditional sentence in the root CLAUDE.md" — that loads on every session regardless, costing context in every unrelated conversation.
Imports let you compose memory from several files, which is good for organisation — but it is worth being clear about what they do not do. An @path import is expanded at launch, so splitting one long CLAUDE.md into five imported files leaves exactly the same amount of text in context. If your goal is a smaller context, imports are the wrong tool and path-scoped rules are the right one. Imports are for structure and reuse: sharing a conventions file across repos, or pulling in a file another tool already owns.
@path/to/file imports a file, expanding it into context at launch. Relative paths resolve against the file containing the import.
Imports can nest to a maximum depth of four hops.
Import parsing skips code spans and fenced blocks — wrap a path in backticks to mention it literally without importing.
Splitting into imports helps organisation but does not reduce context, since imported files load at launch anyway. Use path-scoped rules for that.
Claude Code reads CLAUDE.md, not AGENTS.md. If your repo uses AGENTS.md, create a CLAUDE.md whose first line is @AGENTS.md.
claudeMdExcludes takes glob patterns matched against absolute paths, configurable at any settings layer, with arrays merging across layers. Managed policy CLAUDE.md cannot be excluded.
Check your understanding
Does splitting CLAUDE.md into @imports reduce context usage? Imports help organisation, not context size. Use path-scoped rules to reduce context.
Your repo already has AGENTS.md. Best approach? Renaming breaks the other tool; Claude Code reads CLAUDE.md, not AGENTS.md.
Exam trap: Renaming AGENTS.md to CLAUDE.md — it breaks the other agent. Import it instead.
The enforcement layer. Exit codes are near-guaranteed exam content.
Hooks are the enforcement layer. A hook is a shell command Claude Code runs at a fixed point in the lifecycle — before a tool call, after one, when a session starts — and because the harness runs it rather than the model choosing to, it happens every time. The exit code is how the hook talks back, and the semantics are worth memorising precisely because they are unusual: 2 is the special one. Zero means success and your stdout may be parsed for structured decisions; two means block, and your stderr is fed to Claude so it can react; anything else is a non-blocking complaint that gets logged while execution continues.
Hooks run as shell commands at fixed lifecycle events, regardless of what Claude decides. That is what makes them enforcement rather than guidance.
PreToolUse runs before a tool call and can block it. PostToolUse runs after success and cannot — the tool already ran.
Beyond exit codes, hooks can return JSON. PreToolUse supports permissionDecision of allow / deny / ask / defer, plus updatedInput to rewrite the tool arguments before execution.
Universal JSON fields include continue, stopReason, suppressOutput and systemMessage.
Exit codes decide what happens
Blocking a dangerous command: You register a PreToolUse hook on Bash. It inspects the command, sees rm -rf /, writes "refusing: destructive path" to stderr and exits 2. The tool call never runs, and Claude receives your stderr as feedback so it can choose a different approach. Had the script exited 1 instead, the message would have appeared in the transcript and the command would have run anyway — the difference between a guardrail and a log line is one digit.
Check your understanding
A hook exits with code 2. What happens? Exit 0 is success, 2 blocks, anything else is non-blocking.
Which event can block a dangerous command before it runs? PostToolUse fires after the tool already ran, so it cannot block.
Exam trap: Assuming any non-zero exit blocks. Only 2 blocks; everything else is advisory.
Sharing servers with a team, and running Claude Code without a human.
Two practical concerns close out this domain. First, sharing: .mcp.json is the committed, project-scoped MCP configuration, so a teammate cloning the repo gets the same servers — as against ~/.claude.json, which is personal and holds your OAuth session and per-project state. Second, automation: CI has no terminal and nobody to answer a prompt, so Claude Code runs non-interactively and emits machine-readable output the pipeline parses to decide pass or fail. The temptation there is to disable permissions so nothing can block; resist it, because CI is precisely where no human is watching.
.mcp.json at the project root is the shared, committed MCP server config for the team.
~/.claude.json holds user-scope MCP config alongside OAuth session and per-project state — personal, not shared.
enableAllProjectMcpServers auto-approves everything in the project .mcp.json; enabledMcpjsonServers / disabledMcpjsonServers give per-server control.
For CI, run non-interactive print mode with a machine-readable JSON output format the pipeline can parse. CI runners have no TTY, so scripting keystrokes is brittle.
Do not blanket-bypass permissions in automation — that removes the safety boundary exactly where no human is watching.
Subagents live in .claude/agents/ (project) and ~/.claude/agents/ (user).
Debugging: /context shows which instruction files actually loaded this session; /memory lists and opens memory file locations.
Check your understanding
Which file shares MCP servers with the whole team? ~/.claude.json is personal; local settings are gitignored.
How should Claude Code run in CI? CI has no TTY, and bypassing permissions removes the safety boundary exactly where nobody is watching.
Exam trap: In a CI review job, "instruct the model to only report high-severity issues" depresses measured recall — it still finds the bugs, then declines to report them. Ask for coverage with confidence and severity, and filter downstream.
20% of the exam · 7 lessons · ~30 min · 48 practice questions
1. Structured outputs: two distinct features 5 MIN
Constraining the response body and constraining tool parameters are separate knobs.
There are two separate things you might want to constrain, and the exam expects you to keep them apart. One is the response body — the text Claude sends back — which you shape with output_config.format and a JSON schema. The other is tool parameters — the arguments Claude passes when calling your function — which you lock down with strict: true on the tool definition. They solve different problems and compose freely in the same request. The classic slip is putting strict inside tool_choice; tool_choice only decides which tool runs, never how its inputs are validated.
JSON outputs — output_config.format with a json_schema constrains the response body.
Strict tool use — strict: true as a top-level field on the tool definition (beside name and input_schema, not inside tool_choice) guarantees tool parameters validate exactly.
The two are complementary and can be used in the same request.
The primary benefit is a stable contract: consumers read known JSON keys directly instead of regex-parsing prose.
The old top-level output_format parameter is deprecated — use output_config.format.
A schema must declare additionalProperties: false and list its required fields.
Check your understanding
Where does strict: true belong? tool_choice only decides which tool runs; it carries no validation flag.
What does output_config.format constrain? Tool parameters are constrained by strict: true on the tool definition. The two compose in one request.
Exam trap: Putting strict inside tool_choice. It belongs on the tool definition.
Structured outputs are enforced during generation, not checked afterwards, and that is what makes them reliable. It also explains the limits. The engine supports the parts of JSON Schema that describe shape — types, enums, unions, references — and not the parts that describe ranges, like minimum or maxLength. The Python and TypeScript SDKs paper over this by stripping unsupported keywords before sending and validating them client-side, so your constraint still holds; it is simply enforced in a different place than you might assume.
Supported: basic types, enum, const, anyOf, allOf, $ref/$defs, common string formats (date-time, date, email, uri, uuid…), and additionalProperties: false.
Not supported: recursive schemas, numeric constraints (minimum, maximum, multipleOf), string-length constraints, and complex array constraints.
The Python and TypeScript SDKs strip unsupported constraints before sending and validate them client-side instead.
A new schema incurs a one-time compilation cost on first use; later requests hit a 24-hour schema cache. That explains a slow first call.
Incompatible with citations (returns 400) and with message prefilling.
On stop_reason: "max_tokens" the JSON may be truncated — the schema cannot manufacture tokens after the cap.
Why the first call is slow: Your extraction endpoint takes 4 seconds on its first request of the morning, then 1.2 seconds for the rest of the day, then 4 seconds again the next morning. Nothing is wrong. A schema the engine has not seen must be compiled into a grammar before generation, and the result is cached for 24 hours. If you are benchmarking latency, discard the first call per schema — and if a schema is used rarely enough that every call is a cold one, that compilation cost is part of its real price.
Check your understanding
Which is NOT supported by structured outputs? minimum/maximum and minLength/maxLength are also unsupported; the SDKs validate them client-side.
stop_reason is max_tokens on a schema-constrained call. What do you conclude? Schema enforcement cannot manufacture tokens after the cap. Raise max_tokens or stream.
Exam trap: "It is schema-valid so it must be complete" — a truncated response is neither.
The highest-value idea in this domain: schemas guarantee shape, never truth.
This is the most valuable idea in the domain and the one most likely to be tested as a trap. A schema guarantees the shape of the output: the keys exist, the types match, the JSON parses. It says nothing whatsoever about whether the values are true. A perfectly schema-valid invoice can carry a total that no line item supports, and the API will return it without complaint because it never claimed to check that. Once you separate syntax from semantics, the design follows: schemas handle syntax, deterministic code handles arithmetic and business rules, and genuine ambiguity gets preserved for a human rather than collapsed by the model.
A JSON schema prevents syntax errors. It does nothing about semantic ones — a perfectly schema-valid response can contain entirely wrong values.
Classic case: extracted invoice line items that do not sum to the stated total. The fix is to extract both a stated_total and a calculated_total, compare them, and flag mismatches for human review.
Never let the model silently reconcile a discrepancy by adjusting values — that fabricates data no source supports.
A validation-retry loop is the deterministic layer: validate programmatically, and on failure re-prompt with the specific validation error. An identical blind retry gives the model no new information.
When sources genuinely conflict, capture all candidate values with their source locations and let downstream business logic reconcile. Forcing a premature collapse destroys the evidence.
Hard business rules belong in deterministic backend code, not in schema booleans the model populates and not in prompt text vulnerable to injection.
Two different checks, two different layers
Extract both numbers, never reconcile silently: An invoice lists items summing to $4,850 but states a total of $4,580 — a transposition in the source document. If your schema has one total field, the model must pick, and whichever it picks looks authoritative downstream. Instead give it stated_total and calculated_total. Your code compares them, sees a mismatch, and flags the document. You have converted an invisible data-integrity failure into an explicit exception, which is the only honest outcome when the source itself disagrees.
Check your understanding
Line items do not sum to the stated total. Best design? Adjusting fabricates data no source supports; examples shift probabilities but cannot make arithmetic deterministic.
A JSON schema guarantees which of these? Schemas prevent syntax errors, never semantic ones.
Exam trap: A requires_approval boolean on the tool schema looks like a control. The model sets it, so it is not one.
Prefilling — putting a partial assistant turn at the end of messages so Claude continues from it — was the old way to force a shape. Current models reject it with a 400, so any code carrying that pattern needs migrating. The right replacement depends on what the prefill was actually doing, which is why this shows up as a scenario rather than a lookup: forcing JSON shape maps to structured outputs, forcing a label maps to an enum, and suppressing a chatty preamble maps to a plain system-prompt instruction.
Last-assistant-turn prefills return a 400 on current models. Adding assistant messages elsewhere (few-shot examples) still works.
Pick the replacement that matches what the prefill was doing — the exam frames this as a scenario, not a lookup.
For classification specifically, an enum in a schema or strict tool is stronger than any prefill ever was: it constrains the value before generation rather than the first few characters.
Check your understanding
Assistant prefills now return 400. What replaces a prefill used to force JSON? A partial JSON fragment in a user turn constrains nothing.
What replaces a prefill used to force one of six labels? An enum resolves the value before generation rather than constraining opening characters.
Exam trap: "Move the prefill into a user message" — a partial JSON fragment in a user turn constrains nothing.
Where each kind of content belongs, and why placement is a caching decision too.
Where you put text in a prompt is not only a comprehension decision, it is a caching decision, and the two point the same way. Stable material — role, conventions, output expectations, few-shot examples — belongs early, because it forms a reusable cache prefix and because the model reads it as framing. Volatile material — the document, the user's question, an id — belongs after the last cache breakpoint, because anything before it that changes invalidates everything downstream. There is also a behavioural shift worth knowing: prompts written to bully older models into using a tool now overtrigger, and the fix is to soften the wording rather than pile on guardrails.
The system prompt should hold stable material: role, conventions, output expectations. It then forms a reusable cache prefix.
Put volatile per-request content — the document, the user question, identifiers — after the last cache breakpoint. Never interpolate a per-user id into the system prompt.
Few-shot examples earn their tokens on genuinely ambiguous edge cases, unusual formats, and conveying the desired level of detail. More is not better — volume biases the model toward the shown cases.
Examples are stable content and belong early, before the volatile question.
Current models follow the system prompt closely. Wording written to overcome older models' reluctance (CRITICAL: You MUST…, If in doubt, use X) now overtriggers. The fix is to soften the language, not to add guardrails.
Check your understanding
Where does volatile per-request content belong? Anything volatile early in the prefix invalidates everything after it.
'CRITICAL: You MUST use this tool' now overtriggers. Best fix? Current models follow the system prompt closely; wording written for older models overtriggers.
Exam trap: Adding exceptions to an over-forceful instruction instead of dialling the instruction back.
Numbers worth memorising, and the one workload Batches must never serve.
The Batch API trades latency for money: half price, in exchange for asynchronous processing that may take up to 24 hours. Understanding it as a trade rather than a discount tells you immediately where it must not go — anything a user is waiting on. The numbers here are unusually concrete for this exam, so they are worth memorising: 50% off, most batches inside an hour, 24-hour maximum, results retained 29 days, up to 100,000 requests or 256MB, and results returned in arbitrary order keyed by custom_id.
50% off standard pricing on all token usage, in exchange for asynchronous processing.
Most batches complete within 1 hour; the maximum is 24 hours. Results remain available for 29 days.
Up to 100,000 requests or 256 MB per batch.
Results arrive in any order — key them by custom_id, never by position. Each carries a type of succeeded, errored, canceled or expired.
Never route a blocking, user-facing path through Batches. The discount is compensation for latency, and this is a named anti-pattern.
SLA arithmetic: worst case = submission interval + 24h processing. For a 30-hour SLA, a 6-hour interval is the longest that qualifies (and therefore the fewest submissions).
Batches stacks with prompt caching — a shared cached prefix compounds with the 50% discount.
SLA arithmetic the exam actually asks: You have a 30-hour SLA and want the fewest submissions. Worst case for any item is the wait until the next submission window plus the full 24-hour processing ceiling. A 6-hour cadence gives 6 + 24 = 30 hours exactly — it fits. A 12-hour cadence gives 36 and breaches. A 4-hour cadence gives 28, which fits but submits 50% more batches for no benefit. The answer is the longest interval that still satisfies the SLA.
Check your understanding
What does the Batch API trade for its 50% discount? Most batches finish within an hour, but the ceiling is 24 hours — never put a user-facing path through it.
How do you match batch results to inputs? Results arrive in arbitrary order; indexing positionally silently mis-associates them.
Exam trap: "Submit everything immediately and resubmit failures" destroys the cost efficiency. Refine on a representative sample first.
Handling partial failure, and the statistics trap before you automate.
Two failure modes bracket a batch pipeline. On the way out, partial failure: some requests error, and the instinct to resubmit the whole batch throws away everything that worked. custom_id exists precisely so you can identify and resubmit only the failures. On the way in, measurement: an aggregate accuracy figure is an average across subgroups, and averages hide collapse. A system that is 97% accurate overall can be 99% on clean typed invoices and 61% on handwritten forms, and if you automate on the headline number you have quietly automated the 61%.
When a subset fails, isolate those items by custom_id, fix the cause (e.g. chunk oversized inputs), and resubmit only the failures as a new batch. Reprocessing successes wastes almost the entire spend.
Switching failures to the synchronous API costs roughly double per token and is unnecessary for a non-interactive retry.
Aggregate accuracy hides subgroup collapse. "97% overall at ≥90% confidence" can conceal standard invoices at 99% and handwritten forms at 61%.
Before automating, segment accuracy by document type and by field. That is what reveals whether automation is safe.
A uniform random sample review under-represents rare document types — precisely where failures concentrate.
Raising the confidence threshold does not fix the aggregation illusion; the weak subgroup may still clear the higher bar.
Check your understanding
300 of 10,000 batch requests failed. Most cost-effective recovery? Reprocessing the 9,700 successes wastes almost the entire spend.
97% accurate overall at high confidence. What matters before automating? Aggregates hide subgroup collapse, and a uniform sample under-represents the rare types where failures concentrate.
Exam trap: "Raise the threshold to ≥95%" feels rigorous but leaves the measurement error untouched.
18% of the exam · 6 lessons · ~27 min · 44 practice questions
1. MCP architecture: host, client, server 4 MIN
The participant model and the two layers, which the exam states precisely.
MCP exists to solve an M×N problem: without it, every AI application needs bespoke glue for every data source. MCP standardises that boundary so any compliant client can talk to any compliant server. The vocabulary matters because the exam uses it precisely. The host is the AI application itself. It creates one client per server, and each client holds a dedicated one-to-one connection — there is no multiplexing. The server is the program exposing capabilities. Underneath sit two layers: a data layer speaking JSON-RPC 2.0, and a transport layer handling connection and auth.
Host — the AI application (Claude Code, Claude Desktop, an IDE). It coordinates one or more clients.
Client — the host creates one client per server, each holding a dedicated connection. Connections are one-to-one; there is no multiplexing.
Server — a program that provides context, running locally or remotely.
MCP has two layers: a data layer (the JSON-RPC 2.0 protocol, primitives, notifications) and a transport layer (connection, framing, authorisation).
The wire protocol is JSON-RPC 2.0 — not gRPC, not REST, not SOAP. Notifications are used where no response is required.
"Local" and "remote" describe where the server runs and which transport it uses, not what it can expose. Both can offer all primitives.
Check your understanding
How do MCP hosts, clients and servers relate? Each client holds a dedicated one-to-one connection to its server.
What protocol does the MCP data layer use? Transports differ (stdio vs Streamable HTTP) but the message protocol does not.
Exam trap: "One client multiplexes all servers" — it is one dedicated client per server.
Three server primitives, and knowing which one a scenario actually calls for.
Servers expose three kinds of thing, and choosing correctly between them is a recurring exam question. Tools are actions the model invokes — they do something. Resources are context the application reads — they are data, not verbs. Prompts are reusable templates for structuring an interaction. The tell is usually the word "available": if a scenario describes exposing what data exists so the agent can orient itself, that is a resource, and reaching for a tool means reinventing something the protocol already gives you.
Servers expose three primitives: tools (executable functions), resources (data sources providing context), and prompts (reusable interaction templates).
Each has discovery via */list, retrieval via */get, and for tools execution via tools/call.
Discovery is a live protocol call, not a static manifest — which is why servers can notify clients when the tool list changes.
The key judgement call: a catalogue of available data is context, so expose it as a resource. Adding a discover_data tool to every server reinvents what the protocol already provides.
Elicitation is a client-side primitive — servers use it to request additional input or confirmation from the user.
Two transports: stdio (local process, no network overhead, typically one client) and Streamable HTTP (HTTP POST with optional SSE, typically many clients, standard HTTP auth with OAuth recommended).
Tool or resource?: An agent burns three turns discovering what a documents server holds before it can query anything. The instinct is to add a list_available_datasets tool. But a catalogue of what exists is context, not an action — expose it as a resource and the client can surface it up front, so the agent orients without spending a tool call. Keep search_documents as the tool, because searching is something you do.
Check your understanding
An agent needs to see what datasets exist before querying. Which primitive? A catalogue of available data is context, not an action.
Which transports does MCP define? stdio for local processes; Streamable HTTP with optional SSE for remote.
Exam trap: Reaching for a tool when the scenario describes exposing available data. That is a resource.
When the model picks the wrong tool, the description is almost always the fix.
When an agent picks the wrong tool, the reflex is to add error handling or a confirmation step. Both are reactive — they clean up after a decision that was already made badly. The description is what the model actually reads when deciding, so it is where the problem is caused and where it should be fixed. The most effective descriptions are prescriptive about when to call, not just what the tool does, and they say explicitly what the tool is not for when a sibling tool could be confused with it.
Tool and parameter descriptions are the main mechanism the model uses to decide what to call and how to format inputs. They act before generation.
For confusable tools (delete_file vs archive_file), expand both descriptions to state purpose, boundaries, and explicit negative constraints — what the tool is not for.
Be prescriptive about when to call, not just what the tool does: "Call this when the user asks about current prices or recent events."
For input-format problems, clear per-parameter descriptions beat complex regex. Regex validates but does not guide — the model still guesses, then gets told it guessed wrong.
A confirmation gate inside a destructive tool is worthwhile protection, but it does not correct the upstream selection error.
Check your understanding
The agent keeps calling delete_file instead of archive_file. Best fix? Descriptions are what the model reads when selecting; a confirmation does not correct the choice.
What makes a tool description most effective? Prescriptive trigger conditions measurably improve should-call rate.
Exam trap: Solving a selection problem with error handling or a confirmation prompt. Both are reactive; the description is the cause.
The cluster of design rules the exam tests most heavily.
Most tool-design rules on this exam come from one principle: the model reasons over text, so give it text that is unambiguous and stable. Natural-language keys like a team nickname or a loosely formatted date are ambiguous by construction, so separate discovery from mutation — one tool resolves messy input to an id, and the mutating tool accepts only that id. Bounded value sets belong in enums so resolution happens before execution rather than after a failed call. And outputs should carry what the next call needs: identifiers, not URLs; a total count and cursor, not a silent truncation.
Machine identifiers over natural language. Ambiguous attributes (team nicknames, free-text dates) fail. Separate discovery from mutation: a lookup tool resolves messy input to an unambiguous id, and the mutating tool accepts only that id.
Enums over freeform strings for bounded value sets. An enum makes the model map natural language to an exact backend value before execution, rather than failing at runtime or fuzzy-matching downstream.
Return structured data for chaining. If downstream tools need a document_id, return it explicitly — not a URL, not prose, not a title.
Pagination with metadata. Return the first page plus total count and a cursor, so the agent knows whether more exists. Silent truncation is dangerous: the agent cannot distinguish a complete set from a truncated one.
Normalise heterogeneous outputs. Multiple carrier APIs with different shapes should be normalised into one schema by code, not translated by prompt instructions.
Remove needless hops. If a tool exists only to convert an id to a string that the next tool needs, internalise that lookup — it removes a round trip, its tokens, and its failure mode.
Silent truncation is worse than an error: A search tool returns thousands of rows, so you cap it at the top five to protect the context window. The agent receives five results with nothing to indicate more exist, concludes it has seen everything, and confidently reports that only five records match. No error is raised and the answer is wrong. Returning the same five alongside total: 1,240 and a cursor costs a few tokens and turns a false conclusion into an informed decision about whether to page further.
Check your understanding
A search tool feeds share_document(document_id). What should it return? Downstream tools need machine-usable identifiers, not strings to parse.
Results are too many for the context window. Best design? Silent truncation is indistinguishable from a complete result set, so the agent reasons as if it saw everything.
Exam trap: "Silently cap at the top five results" — the agent then reasons as if it saw everything.
Who fixes which failure, and what you are allowed to believe.
Two independent ideas share this lesson because both are about placing responsibility correctly. On errors, split by who can fix it: a network timeout is the tool's problem and belongs in an internal retry, while a malformed argument is the agent's problem and must surface with enough detail to correct — a generic "tool failed" guarantees the next attempt repeats the mistake. On trust, remember that MCP annotations such as a read-only hint are declared by the server about itself. They are a claim, not a guarantee, so a policy that skips confirmation must rest on your trust in the vendor rather than on the label — and a server running locally is not thereby trustworthy, since a local process has full access to the machine.
Split errors by who can actually fix them. Transient failures (network timeouts) are the tool's problem — retry inside the tool. Syntax errors need the agent to reason and correct its input, so surface them with specific validation detail.
Making the agent handle predictable infrastructure failures burns turns and tokens on work code does better.
A generic "Tool execution failed" strips the information needed to self-correct, guaranteeing another failed attempt.
In the Messages API a failed tool returns a tool_result with is_error: true — never omit the block, and never return an empty string (indistinguishable from a successful empty result).
MCP annotations are self-reported, untrusted metadata. A readOnlyHint is a label the server chose for itself, not a guarantee. Base any confirmation-bypass policy on explicit vendor trust — not on the label, and not on the server running locally (local ≠ trustworthy).
Check your understanding
A tool hits a transient network timeout. Where is it handled? Syntax errors go back to the agent with detail; infrastructure failures are the tool's problem.
An MCP server advertises a read-only annotation. How much do you trust it? Bypass policy must rest on explicit vendor trust; local does not mean trustworthy.
Exam trap: "Trust it because the server runs locally." A local process has full access to the machine.
What to do when there are too many tools to put in context.
There is a real ceiling on how many tools an agent can choose between accurately, and past it selection degrades no matter how good the descriptions are. When a library genuinely needs to be large, the answer is not to write better prose for fifty tools or to merge them into one monolith — it is to stop putting all fifty in context at once. Tool search loads only the handful relevant to the current turn. It has the pleasant side effect of preserving the prompt cache, because discovered schemas are appended rather than swapping out the tool list that sits at the very front of the prefix.
A large tool surface degrades selection accuracy. Keeping the set focused is standard guidance.
When a large library is genuinely needed, use dynamic discovery (the tool-search pattern) so only the handful of relevant schemas enter context per turn.
Constraint: the search tool itself must not be deferred, and at least one tool must remain non-deferred. Deferring everything is rejected — the model would have no entry point.
Tool search appends schemas rather than swapping them, which preserves the prompt cache. Changing the top-level tool list does not.
Rewriting 50 descriptions helps at the margin but does not fix the systemic overload. Merging 50 connectors into one monolith destroys composability and moves the ambiguity into parameters.
Check your understanding
50+ tools and the agent picks wrong. Best structural fix? A large surface degrades selection regardless of description quality; merging destroys composability.
Which constraint applies to deferred tool loading? Deferring everything leaves no entry point and is rejected.
Exam trap: "Give the agent everything so it is never blocked" — universal tool access is a named anti-pattern.
15% of the exam · 6 lessons · ~27 min · 36 practice questions
1. Prompt caching mechanics 5 MIN
One invariant explains every caching question on the exam.
Almost everything about prompt caching follows from one mechanical fact: the cache key is the exact bytes of the prompt up to each breakpoint, so a match is a prefix match. Change one character anywhere in that prefix and everything after it is invalidated, because the cached state was computed against bytes that no longer exist. This is why render order matters — tools, then system, then messages — and why tool definitions are the most destructive thing to change: they sit at position zero, so touching them invalidates the lot. Stable content first, volatile content last, is not a style preference but a direct consequence.
Caching is a prefix match. Any byte change anywhere in the prefix invalidates everything after it. Everything else follows from this.
Render order is tools → system → messages. Tools sit at position zero, which is why changing one invalidates the whole cache.
A breakpoint on the last system block therefore caches tools and system together.
Maximum 4 cache_control breakpoints per request. They can sit on any content block — system text, tool definitions, message text, images, tool results, documents.
Place breakpoints at stability boundaries: end of the shared prefix, not the end of the whole prompt. Marking the end of a varying prompt writes a distinct entry every time and never reads.
A fork (summarisation, sub-agent) must copy the parent's system, tools and modelverbatim and append only fork-specific content. Semantic equivalence is irrelevant — bytes are compared.
The prefix and the breakpoint
One line that costs you every cache hit: Your system prompt opens with f"Today is {datetime.now()}". Every request now has a unique prefix from its very first line, so nothing after it can ever be reused — you pay the write premium on every call and read nothing back. The tell is cache_read_input_tokens sitting at zero across requests that ought to be identical. Move the timestamp into the user message, after the last breakpoint, and the entire system prompt becomes cacheable again.
Check your understanding
Why does changing one tool definition invalidate the whole cache? Caching is a prefix match, so a change at position zero invalidates everything after it.
What is the render order for cache purposes? This is why a breakpoint on the last system block caches tools and system together.
Exam trap: "The prompt is basically the same" is not a caching argument. Bytes, not meaning.
The numbers, and how to prove caching is actually working.
Caching is not free, and knowing the shape of the economics tells you when to bother. Reads are roughly a tenth of the base input price, which is the win. Writes cost more than base — about 1.25× on the five-minute TTL and 2× on the one-hour — which is the price of admission. So caching pays off only when enough reads follow a write: two requests break even on the short TTL, three on the long one. Verification matters as much as configuration, and the field to watch is cache_read_input_tokens: if it is zero across repeated identical prefixes, something is silently invalidating them.
Cache reads cost roughly 0.1× base input price. Cache writes cost more than base — about 1.25× for the 5-minute TTL and 2× for the 1-hour TTL.
That write premium is why break-even matters: with the 5-minute TTL two requests break even; with the 1-hour TTL you need at least three.
Verify with usage.cache_read_input_tokens. If it is zero across repeated identical-prefix requests, a silent invalidator is at work.
Total prompt = input_tokens + cache_creation_input_tokens + cache_read_input_tokens. A small input_tokens after a long session means caching is working, not that the conversation was truncated.
Concurrency trap: a cache entry becomes readable only once the first response begins streaming. Firing 20 identical-prefix requests at once means 20 misses and 20 full-price writes. Send one, wait for the first token, then fire the rest.
Why parallel fan-out misses the cache: You fire twenty requests at once, all sharing a large cached prefix, and every one of them is billed as a full-price write. A cache entry only becomes readable once the first response begins streaming, so twenty simultaneous requests all arrive before any entry exists. Send one, wait for its first streamed token — not the whole response — then release the other nineteen. They read what the first one just wrote.
Check your understanding
What does a cache read cost relative to base input price? Writes cost more than base — about 1.25x on the 5-minute TTL and 2x on the 1-hour.
input_tokens is small after a long session. What does that mean? Total prompt = input_tokens + cache_creation + cache_read. Check the sum, not one field.
Exam trap: Reading input_tokens alone and concluding the context was truncated.
3. Invalidation hierarchy and silent invalidators 5 MIN
Not every change invalidates everything — and the ones that do are predictable.
Not every change is equally destructive, and the hierarchy is worth knowing because it tells you what you can safely vary per request. Changes invalidate their own tier and everything below it. Tool definitions and the model sit at the top: change either and the whole cache is gone. The system prompt sits below them, and message content below that. The useful consequence is that tool_choice and toggling thinking are below the tools-and-system tier, so you can vary them freely per request while keeping that expensive prefix intact.
Changes only invalidate their own tier and below. So tool_choice can vary per request while the tools-and-system cache survives.
Tool definition changes and model switches invalidate everything. Caches are model-scoped.
Consequence for "modes": do not swap the tool set mid-conversation. Signal the mode through message content, or use tool search, which appends rather than swaps.
Consequence for cheaper sub-tasks: keep the main loop on one model and delegate to a subagent rather than switching models mid-conversation.
Silent invalidators to grep for in prompt-building code: datetime.now() or a UUID in the system prompt, json.dumps() without sort_keys, iterating a set, a per-user id interpolated into the system prompt, and conditional system sections.
The fix is always the same shape: keep the system prompt frozen, move volatile content after the last breakpoint.
Check your understanding
Which change can vary per request without losing the tools-and-system cache? tool_choice sits below tools and system in the invalidation hierarchy.
A sub-task needs a cheaper model. Best approach? Caches are model-scoped, so switching invalidates in both directions.
Exam trap: Assuming a per-user id is "too small to matter". A one-character difference at the front kills cross-user sharing entirely.
Three mechanisms, three different jobs. Expect a "which one" question.
Three mechanisms manage long-running context and they are routinely confused, so anchor them by what they physically do. Context editing prunes: stale tool results are removed and not replaced. Compaction summarises: earlier turns are condensed into a summary block so the conversation can continue past the window. Memory persists: files written to a memory directory outlive the session entirely. The first two operate within one session; only memory crosses sessions. A scenario asking about state surviving a restart has exactly one answer.
Context editing — prunes. Clears stale tool results and thinking blocks. The content is removed, not summarised.
Compaction — summarises. When a conversation approaches the window limit, earlier context is condensed server-side.
Memory — persists. State survives across separate sessions via files in a memory directory.
Critical compaction implementation detail: append the full response.content back to your message history. The compaction blocks must be preserved — extracting only the text silently loses the compaction state.
Editing and compaction operate within a session; memory is across sessions. Many long-running agents use all three.
None of these is max_tokens, which is only a per-response output cap.
Prune, summarise, or persist
Check your understanding
Stale tool results are cluttering a long session. Which mechanism removes them? Context editing prunes; compaction summarises.
State must survive across separate sessions. Which mechanism? The first two operate within one session only.
Exam trap: Compaction and context editing are offered interchangeably. One summarises, one deletes.
Two distinct failure modes with two distinct signatures.
Long contexts fail in two distinct ways with two distinct signatures, and telling them apart is the diagnostic skill being tested. "Lost in the middle" degrades recall of material in the middle of a long context while the beginning and end stay strong. Boundary degradation is different: as the total approaches the window limit, the tail gets dropped. So a symptom of "the last third of every long document is consistently missed" points at the boundary, not at a general attention span — and the fix is to find what else is consuming the window, remembering that a detailed tool schema can easily cost a couple of thousand tokens before the document is even loaded.
"Lost in the middle" degrades recall of material in the middle of a long context.
Boundary degradation is different: when the total approaches the window limit, the tail is systematically dropped. A consistently missing final third points here, not to general span limits.
Remember that tool schemas consume the window too. A 12-field schema with detailed descriptions can cost ~2,500 tokens; add a system prompt and your usable space shrinks measurably.
Diagnostic pattern to recognise: accuracy fine below a size threshold, sharp cliff near the limit, tail of the document missing → the fixed overhead plus the document is crowding the boundary.
When passing work downstream, do not send everything. Pass a dense narrative plus a compact structured citation index (citation id, claim, source, key quote) rather than 120k of raw content or a bare summary with no attribution.
Check your understanding
The final third of every long document is missed. Most likely cause? Lost-in-the-middle degrades the middle; a consistently missing tail points at the boundary.
What else consumes the context window besides the document? A detailed 12-field schema can cost around 2,500 tokens before the document loads.
Exam trap: "Longer documents exceed the attention span" is offered for a missing-tail symptom. That symptom is the boundary, not the span.
Reliability practices that keep multi-agent output trustworthy.
Reliability in a multi-agent pipeline is mostly about not losing information that you already had. Provenance is the clearest case: the moment a synthesis step turns structured findings into prose, source, date and methodology are gone, and a downstream report agent asked for citations will invent them — not because it is unreliable, but because you deleted the answer. The same logic applies to disagreement. Two figures that differ may be a conflict or may be the same measure at different dates, and without a publication_date you cannot tell, so the system flags a contradiction where there is only change over time.
Prose destroys metadata. If a synthesis step summarises into prose, attribution is gone and the report generator will invent sources.
Structural fix: assign a citation_id at the earliest stage, have synthesis emit a narrative with inline markers, and pass a structured citation index alongside it.
Distinguish temporal change from contradiction. Require publication_date in structured output, and treat differing values across distinct dates as progression. Reserve conflict detection for disagreement within the same period.
When sources genuinely conflict, return structured objects carrying methodology, confidence and date plus a conflict_detected flag — do not average, do not pick arbitrarily, do not silently prefer the newest.
Escalate on deterministic conditions — policy category, monetary threshold, repeat contact, explicit user request. Self-reported model confidence is not calibrated and is a named anti-pattern for routing.
On long autonomous runs, require every progress claim to be auditable against a tool result from the same session. Unverified items must be stated as unverified.
Check your understanding
Synthesis summarised findings into prose and citations were lost. Best fix? Inference from claim text produces fabricated attributions.
Two sources give different market sizes. What single field resolves most cases? Without a date you cannot distinguish genuine conflict from change over time.
Exam trap: Averaging two conflicting figures produces a number no source supports and hides that they disagreed.
The real exam draws four of these six. Every question here is written inside one of them.
Independent study aid. AITraining2U is not affiliated with, authorised by, or endorsed by Anthropic. “Claude” and “Anthropic” are trademarks of Anthropic PBC. These are original lessons and practice questions, not real exam content. Exam logistics change — confirm current format, pricing and policy on Anthropic’s official certification page before you book.
Reference
Study the source, not the summary
Every lesson and answer here links back to one of these. Third-party guides for this exam contain real factual errors — when in doubt, check the docs.
720 on a scaled range of 100–1000. Because scoring is scaled rather than a raw percentage, 720 does not mean exactly 72% of items correct — harder items can carry more weight. Our mocks use a linear scale, so treat the number as a comparable signal rather than a prediction.
60 scenario-based questions in 120 minutes — about two minutes each. Questions are drawn from four scenarios selected from a pool of six, spanning customer support, multi-agent research, Claude Code team configuration, CI/CD automation, developer tooling with MCP, and structured data extraction.
The curriculum here is about 157 minutes of reading. Most candidates with real hands-on Claude experience need roughly 15–20 hours in total including practice; if you are new to the Agent SDK, Claude Code or MCP, allow considerably more and build something first.
No, and be wary of anyone claiming otherwise. These are original questions and lessons written against the published domain blueprint and the official documentation. They train the same decision-making the exam tests without reproducing protected content.
Because several popular third-party guides contain factual errors — for example claiming user-scope CLAUDE.md lives at ~/.claude.json (it is ~/.claude/CLAUDE.md; the JSON file holds OAuth and MCP state), or inventing MCP error fields that are not in the specification. Every lesson and answer here cites its source so you can verify rather than trust.
Yes, in your browser’s local storage only. Lessons you mark as read, an in-progress exam attempt, and your per-domain mastery all persist. Nothing is uploaded and there is no account to create.
Want the hands-on version?
Certification proves you know the patterns. Our instructor-led Claude training gets your team building agents, MCP servers and Claude Code workflows on your own codebase — HRDC claimable for Malaysian employers.