Domain 1 carries the most weight on the Claude Certified Architect – Foundations (CCA-F) exam — 27% of your score — and it is where scenario questions get most demanding. The domain is not about writing prompts or wiring a single API call; it is about architecting the loop and the guardrails around it so that an autonomous agent behaves predictably, recovers gracefully, and never strands a user with an unfinished task.
Everything in this domain traces back to one mechanical fact: an agent is a loop driven by the model's `stop_reason` signal. The Anthropic Messages API is stateless — it returns a response and a reason it stopped, and your orchestration layer decides what happens next. Whether the loop continues executing tools, terminates cleanly, escalates to a human, or hangs is a property of the code you write around the model, not the model itself. The exam tests whether you understand that boundary precisely.
This guide works through all six Domain 1 objectives in order. For each, we cover the core concept, why it matters architecturally, a concrete agent scenario (a customer-support resolution agent recurs throughout), and the distractor an exam item is most likely to test. Ground yourself in the mechanics — stop_reason values, hook execution order, handoff payloads — and the scenario questions become much easier to reason about.
Orchestration-Layer Safeguards: Every Session Resolves or Escalates
An agentic session can stop for many reasons, and only one of them means "the task is genuinely finished." The core architectural safeguard for Domain 1 is a terminal guarantee: no matter how the loop exits, the session must end in either a completed resolution or an explicit human escalation — never a silent hang.
Why it matters: the Messages API is stateless and the loop is yours. If your loop only handles the happy path (stop_reason: "tool_use" → run tools; stop_reason: "end_turn" → done) and the model instead stops with max_tokens, refusal, or pause_turn, a naive loop can break, spin, or return a half-finished answer with no owner. At scale, "the agent quietly gave up" is the failure mode that erodes user trust the fastest.
Consider a customer-support resolution agent with tools get_customer, lookup_order, process_refund, and escalate_to_human. It's mid-way through issuing a refund when the response comes back with stop_reason: "max_tokens". The orchestration layer must treat this as an incomplete state — resume with a higher token budget or continue the turn — not as a resolution. If it cannot make progress (repeated failures, a policy refusal, or an ambiguous state it can't safely act on), the safeguard is to route to `escalate_to_human` with the accumulated context rather than return nothing.
Practical mechanisms: a max-iteration cap (e.g. stop after N loop turns and escalate), a wall-clock or token budget, and a fallback branch that fires escalate_to_human whenever the loop exhausts its retries. The point is that termination is designed, not left to chance.
Session Resumption: Restoring State Without Repeating Work
Long-running agents get interrupted — a process restarts, a user steps away, a rate limit pauses a run. Session resumption is the technique of restoring an agent to its prior state accurately, so it continues from where it left off instead of restarting from scratch. Two ideas dominate this objective: targeted re-analysis and context injection.
Because the Messages API is stateless, resumption means reconstructing the working context. The naive approach — replay the entire history and re-run every tool — is slow, expensive, and can cause harmful re-execution (imagine re-calling process_refund on resume). The architect's approach is surgical: inject the accumulated findings as context, and re-analyze only what has actually changed since the interruption.
Concrete example: a code-review agent was analyzing a 40-file pull request when it was interrupted. On resume, it doesn't re-read all 40 files. It injects its prior findings (which files it cleared, which had issues) into the new context, then performs targeted re-analysis of only the files whose contents changed since it last looked. For the support agent, resumption means injecting the verified-customer and pending-refund state rather than re-verifying identity and re-querying every order.
- Context injection — feed prior findings, decisions, and authorization state into the restored session as input so the agent starts "knowing" what it already established.
- Targeted re-analysis — re-examine only the inputs that changed (changed files, updated order status), not the entire prior workload.
- Idempotency awareness — never blindly replay side-effecting tools on resume; a completed
process_refundmust not fire twice.
Dynamic Task Decomposition: Subtasks That Adapt to Discovery
Complex work must be broken into subtasks — but when and how you decompose is the architectural decision. Dynamic decomposition generates and revises subtasks as new information surfaces, rather than committing to a fixed, pre-planned sequence that runs regardless of what the agent discovers along the way.
This matters because real tasks are contingent. A static plan ("step 1, step 2, step 3, always") is brittle: if step 1 reveals the problem is different than assumed, steps 2 and 3 are now wrong but still execute. Claude's agentic loop is naturally suited to dynamic decomposition — the model reasons over each tool result and chooses the next action, so the plan evolves with the evidence.
Support-agent example: the agent is asked to "resolve this billing complaint." It doesn't run a canned refund script. It calls get_customer, then lookup_order — and discovers the charge was a legitimate subscription renewal, not an error. A static plan would have refunded anyway. Dynamic decomposition lets the agent pivot: the newly discovered fact changes the remaining subtasks from "issue refund" to "explain the renewal and offer to cancel future billing."
- Fixed sequence (fragile): plan all steps up front, execute in order, ignore intermediate findings.
- Dynamic decomposition (robust): decide the next subtask from the result of the last one; add, drop, or reorder subtasks as discovery warrants.
- Where it lives: the agentic loop is the engine — each
tool_useresult feeds back into the model, which generates the next step rather than following a frozen script.
PreToolUse and PostToolUse Hooks: Deterministic Enforcement Outside Model Discretion
Some rules must always hold — they cannot be left to the model's judgment. Hooks are the mechanism for deterministic enforcement: shell commands the harness runs at defined points in the loop, outside model discretion. A PreToolUse hook runs before a tool executes and can block or allow it; a PostToolUse hook runs after a tool completes and can inspect the result and feed structured feedback back to the agent.
The architectural principle: policy that must never be violated does not belong in the prompt. A prompt instruction like "never write to the production database" is a strong suggestion the model usually follows — but "usually" is not a guarantee. A PreToolUse hook that inspects the pending tool call and denies it is a deterministic barrier that fires 100% of the time, regardless of how the model was prompted or jailbroken.
For the support agent, a PreToolUse hook on process_refund can enforce the hard rule "refunds over $500 require human approval" by denying the call and returning a reason the agent sees:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Refunds over $500 require human approval. Route to escalate_to_human."
}
}In Claude Code's hook system, a PreToolUse hook signals its decision either by exit code (a non-zero blocking exit feeds stderr back to the model) or, for precise control, by emitting JSON with hookSpecificOutput.permissionDecision set to allow, deny, or ask, plus a permissionDecisionReason. (The older top-level decision/reason shape is deprecated for PreToolUse — use hookSpecificOutput.) PostToolUse runs after execution: it can't un-run the tool, but it can validate the output — for example, run a linter or a schema check on a file the agent just wrote — and block/return feedback so the agent corrects course.
| PreToolUse | PostToolUse | |
|---|---|---|
| When | Before the tool runs | After the tool completes |
| Can prevent execution? | Yes — deny blocks the call | No — the tool already ran |
| Typical use | Policy gates, permission checks, input guards | Validation, linting, formatting, result feedback |
| Enforcement nature | Deterministic, outside model discretion | Deterministic post-check with feedback to the agent |
The Agentic Loop: How stop_reason Drives Continue-vs-Terminate
The engine underneath everything in Domain 1 is the agentic loop, and it is driven by the stop_reason field on each Messages API response. After every model turn, the orchestration layer reads stop_reason and decides: execute the requested tools and loop again, or terminate and return the final answer.
The two central signals: stop_reason: "tool_use" means the model wants to call one or more tools — the harness executes them, appends the results as a new user message, and calls the API again to continue. stop_reason: "end_turn" means the model finished naturally — the loop terminates and returns the response. A minimal loop is literally "while the model asks for tools, run them and continue."
messages = [{"role": "user", "content": user_query}]
while True:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=16000,
tools=tools,
messages=messages,
)
if response.stop_reason == "end_turn":
break # model is done — return the final response
if response.stop_reason == "pause_turn":
# server-side tool loop paused — resend to resume
messages.append({"role": "assistant", "content": response.content})
continue
if response.stop_reason == "tool_use":
messages.append({"role": "assistant", "content": response.content})
tool_results = [run_tool(b) for b in response.content if b.type == "tool_use"]
messages.append({"role": "user", "content": tool_results})
continue
# max_tokens, refusal, stop_sequence — handle explicitly, never fall through
handle_termination(response.stop_reason)
breakA complete design accounts for every value stop_reason can take. The Messages API can return: end_turn, tool_use, max_tokens, stop_sequence, pause_turn, and refusal. pause_turn appears in long-running server-side tool flows (e.g. web search hitting an iteration limit) — you resume by resending the conversation, without adding a "continue" message. max_tokens means the response was cut off at the output cap — resume or raise the limit. refusal means the model declined for safety reasons. This is exactly where Objective 6 connects back to Objective 1: the loop's stop_reason handling is how you deliver the terminal guarantee that every session resolves or escalates.
| stop_reason | Meaning | Loop action |
|---|---|---|
| tool_use | Model wants to call tools | Execute tools, append results, continue |
| end_turn | Model finished naturally | Terminate, return final response |
| max_tokens | Hit the output token cap | Resume / raise max_tokens — not a resolution |
| pause_turn | Server-tool loop paused | Resend conversation to resume automatically |
| refusal | Declined for safety | Terminate gracefully; escalate if needed |
| stop_sequence | Hit a custom stop sequence | Terminate per your logic |
Key takeaways
- 01The agentic loop is your orchestration code reading
stop_reason; the stateless Messages API only returns a response and a reason it stopped. - 02
tool_usecontinues the loop (run tools, append results, resend);end_turnterminates it. Handlemax_tokens,pause_turn, andrefusalexplicitly. - 03Design a terminal guarantee: every session must end resolved or escalated to a human with context — never a silent hang.
- 04Handoff packages must preserve findings AND authorization state, so the next step or human doesn't re-verify or skip a required approval.
- 05Resume by injecting prior state and re-analyzing only what changed — never blindly replay history and re-run side-effecting tools.
- 06Dynamic decomposition adapts subtasks to discoveries; fixed sequences execute obsolete steps after new information invalidates them.
- 07PreToolUse hooks deny/allow before a tool runs; PostToolUse validates after. Hooks enforce hard rules deterministically, outside model discretion.
Common mistakes
Building a loop that only handles tool_use and end_turn, letting max_tokens/refusal/pause_turn fall through as dead ends.
Branch on every stop_reason value; map non-terminal stops to resume-or-escalate so the session always resolves or hands off to a human.
Enforcing a non-negotiable business rule (spending limits, no prod writes, PII redaction) with a strongly-worded system prompt.
Prompts guide probabilistically. Put hard constraints in a PreToolUse hook that deterministically denies the tool call regardless of how the model was prompted.
Handing off to a human or next step by copying the transcript but omitting authorization state.
Package authorization explicitly (identity_verified, approved_by, requires_human_approval) so the receiver neither re-runs nor skips required checks.
Resuming an interrupted session by replaying the full history and re-invoking every tool.
Inject accumulated findings as context and re-analyze only changed inputs; guard side-effecting tools (e.g. process_refund) against duplicate execution.
Hard-coding a fixed subtask sequence that runs regardless of intermediate findings.
Let the agentic loop decompose dynamically — choose the next subtask from the last tool result so the plan adapts when discovery changes the situation.
Frequently asked
What exactly is the difference between `stop_reason: "tool_use"` and `"end_turn"`?
tool_use means the model has requested one or more tool calls — your harness executes them, appends the results as a user message, and resends to continue the loop. end_turn means the model finished its response naturally, so the loop terminates and you return the final answer. The orchestration layer, not the model, acts on these signals.
How is a hook different from a tool the model can call?
A tool is invoked at the model's discretion — the model decides whether and when to call it. A hook is run by the harness at a fixed point (PreToolUse before a call, PostToolUse after), deterministically and outside the model's control. Use tools for capability; use hooks to enforce rules that must always hold.
Can a PostToolUse hook stop a tool from running?
No. PostToolUse runs after the tool has already executed, so it cannot prevent the action — it can only inspect the result and feed feedback back to the agent (e.g. flag a lint failure so the agent fixes it). To block an action before it happens, use a PreToolUse hook, which can return a deny permission decision.
What is `pause_turn` and how do I handle it?
pause_turn appears in long-running server-side tool flows (like web search) when the server-side sampling loop reaches its iteration limit. You resume by resending the conversation — including the assistant's response — without adding a manual "continue" message; the server detects the trailing server-tool activity and picks up where it left off.
Why does Domain 1 emphasize that a session must "resolve or escalate"?
Because the loop can exit for reasons that don't mean success — a token cutoff, a refusal, an unrecoverable tool error. Without a designed terminal guarantee, those exits leave the user with an empty or half-finished result and no owner. Routing unresolved cases to a human (with a structured handoff) closes that gap.
Independent, unofficial study material. Not affiliated with, endorsed by, or authorized by Anthropic. Every example is original and written to teach the public exam objectives — no real exam questions are reproduced. Technical details reflect Claude, the Anthropic API, Claude Code, and MCP as of July 25, 2026; always confirm specifics against current official documentation.
Test yourself
Turn what you just read into answers you can check.