Pro Domain 1 Β· Solution Design & Architecture (17%)
In this domain you act as the architect, not the builder. The exam guide describes the minimally qualified candidate in two quotes. This person "translates business requirements into scalable, secure, and reliable architectures". This person also "understands trade-offs related to cost, latency, performance, safety, and maintainability." Every item in this domain starts from a business situation: a backlog, a missed SLA, a cost line, or a promise from an executive. The item then asks which design solves it. Foundations Domain 1 taught the mechanics: the agent loop, workflows versus agents, and harness controls. Professional Domain 1 asks you to defend a choice among those mechanics to a business owner. You explain why this pattern, why this split, why this SLA, and what it costs if you are wrong. So the artifacts (the written documents you produce) are different: an architecture decision record, an SLA table, and a decomposition tree. This page shows each one in its real shape.
π New here? Halden Freight and the 40,000 exception emails
The business problem arrives as a number, not as a technology. Halden Freight is a mid-size logistics company. It receives about 40,000 "shipment exception" emails a month: delays, damaged goods, customs holds, and wrong addresses. Twenty-two operations staff read these emails. They look up the shipment in the tracking system, decide what to do, and reply. The median time to the first reply is nine hours. Three key customers have contracts that promise two hours. The VP of Operations asks: "Can Claude fix this?"
The architect's first step is to not accept the question in this form. "Fix this" is not a requirement. The architect writes down what a fixed system looks like: 90% of exceptions get a correct first reply within two hours; no reply promises money without a human; ops staff handle only the cases the system cannot handle. That sentence is the whole design brief. Every later decision is a trade-off measured against it.
Then the architect draws the loop. Input: the email plus the shipment record. Processing: classify the exception, fetch the shipment, and draft the reply and the next action. Output: a reply to the customer and an internal ticket. Feedback: every case where an operator overrides (corrects) the system becomes a labeled example, and every customer rating becomes a signal. Without the feedback arrow, the system is only a demo. It will slowly get worse as carriers, routes, and customers change.
Next comes the pattern choice, and the exam's favorite trap. Most exceptions follow a known path: classify β look up β draft β send. That is a workflow with one augmented-LLM step, not an agent. Only the long tail of rare cases justifies letting the model choose its own steps, inside a budget. An example is a customs hold that needs three lookups and a judgment. The exam writes its distractors (tempting wrong answers) about the architect who chooses "an agent with all the tools".
Multi-agent appears once, for a different job. Every Friday, Halden wants a root-cause report across the week's exceptions: which carriers, which lanes, and which customers caused them. That job really does split into independent research subtasks with separate contexts: a coordinator and three workers. The daily triage does not need this, and the architect must be able to explain why.
Finally, the architect names the value and signs the SLA. Efficiency: 22 staff hours per day recovered. Performance SLA: p95 first reply under two hours. Cost: a token budget per exception, with a ceiling. Transformation: customers check status themselves. Each pillar gets a number, an owner, and a measurement. The ADR records these choices. The exam expects you to be able to write this artifact, and to defend it when the VP asks "why not just an agent?"
At a glance β the six decisions this domain grades
Objective (verbatim)
The decision you make
The artifact
The distractor
Translate business problems into Claude-based AI solutions
What outcome, measured how, for whom
Problem statement + solution brief
Starting from a capability ("use tools") instead of an outcome
Design end-to-end architectures
Where input, processing, output, and feedback each live
Click any technical term. Its dialog shows the plain meaning, the real document or payload, and where the idea stops being true.
Translate business problems into Claude-based AI solutions
Objective: "Translate business problems into Claude-based AI solutions."
The translation has four parts, and the architect writes all four. The business owner supplies the pain and the constraints. The architect supplies the outcome statement, the scope boundary, the measurement, and the first candidate architecture. The result is a one-page solution brief. It lives in the project's design repository. In this KB's house convention, that is a docs/ folder in the architecture project, which is created before the code repository.
Outcome before capability. Suppose a brief starts with "we will use an agent with MCP tools". That brief has skipped the translation. Now suppose a brief starts with "reduce median first-reply time from 9 hours to under 2 for 90% of exceptions". People can discuss it, measure it, and scope it. The exam's stems (question texts) describe the pain. The correct answer names the outcome first, and then chooses the smallest mechanism that reaches it.
Scope is a list of what is out. For Halden Freight, address corrections and damage claims are in scope. Refunds and legal disputes are out of scope (people keep handling them). Because you wrote the out-of-scope list, you can later say "the agent does not need a refund tool". The [least privilege](term:least privilege) argument starts here, not in the tool configuration.
Here is the brief in the form the architect really writes it:
MARKDOWN
# Solution brief β Halden Freight exception triage
## Business problem
40,000 shipment-exception emails/month are triaged by 22 operations staff.
Median first reply: 9 h. Three enterprise contracts require 2 h.
## Outcome (measurable)
- 90% of exceptions receive a correct first reply within 2 h (p95 < 2 h).
- Correct = operator would not have changed the classification or next action.
- Zero replies promise money, credit, or a claim decision without human approval.
## In scope
Delay, damage report, customs hold, address correction, missing paperwork.
## Out of scope (stays with people)
Refund decisions, legal disputes, contract renegotiation.
## Constraints
- Shipment data never leaves the tracking system region (EU tenant).
- Per-exception model spend ceiling: set by finance; reviewed monthly.
- Existing ticketing system remains the system of record.
## First candidate architecture
Routing workflow β augmented-LLM draft step β deterministic send gate.
Long-tail customs holds β bounded agent (max 6 turns) β human queue.
Who owns each line. The VP owns the outcome numbers (the company will judge the VP by them). Legal owns the "no money without a human" line. The platform team owns the data-residency constraint. The architect owns the candidate architecture. Only the architect may change it, and only through an ADR, which this page covers below.
The wrong choice and its failure. Imagine a team that starts from capability ("agent + all tools"). It ships something that looks impressive in the demo, but nobody can measure it in production. Six weeks later, nobody can say whether it helped. The refund tool has been called twice. The VP has no number to justify the spending. The exam writes this distractor as "deploy a general-purpose agent with access to all support tools". It is wrong because the team never translated the problem, not because agents are bad.
"End-to-end" means the model call is only one box among many. The exam draws the architecture as four stages. Each stage has a concrete place in Halden's system:
flowchart LR
IN[Input: email + shipment record] --> CL[Classify: routing call]
CL --> AUG[Augmented LLM: draft reply + next action]
AUG --> GATE{Deterministic send gate}
GATE -->|allowed| OUT[Output: customer reply + ticket]
GATE -->|blocked| HQ[Human queue]
OUT --> FB[Feedback: ratings, overrides, outcomes]
HQ --> FB
FB --> EV[Eval set + drift dashboard]
EV -.->|prompt / routing changes| CL
Input is more than the email. The application builds the input from three parts: the raw email, the shipment record fetched from the tracking system, and the customer's contract tier. It matters who builds it. The harness (the code that runs and controls the model calls) builds it, in a fixed and predictable way, before any model call. In the fixed path, the model never fetches the shipment record by itself. If it did, the path would become an agent, and its latency would become unpredictable.
Processing is a chain with checks between the calls. Classification is one small call. Drafting is one augmented call with retrieved context and a lookup tool. Code sits between the two calls. If the classifier returns refund_request, the chain stops and sends the case to a person. This is prompt chaining with a [routing](term:workflow routing) step. The code between the calls is where the policy lives.
Output passes through a gate that the model cannot argue its way past. The send gate is a deterministic check (code that gives the same result every time). It checks three things: the reply text has no currency amounts, the next action is in the allowed list, and the customer tier matches the reply template. The gate is code. When it fails, it fails closed: the case goes to the human queue. This is the harness control idea from Foundations, now placed at one specific point in one specific loop.
Feedback is a planned data flow, not a hope. Every operator override creates an event. Every customer rating creates an event. The events go into a store that the eval process reads. Here is the override event as the application records it. This is application state, not an API payload:
Two things can set the promote_to_eval_set flag: a rule (for example, overrides on in-scope categories) or a human reviewer. Each promoted event becomes a row in the [golden set](term:golden set). The operator's correction is the expected answer. That is the feedback-loop design. The loop closes through a dataset and an eval run. A change to the prompt or the routing ships only after the eval passes.
Event store β [golden set](term:golden set) β eval run
The wrong choice and its failure. Imagine a pipeline drawn as input β model β output, with no feedback arrow. The team measures its accuracy once, during the pilot, and never again. Then a carrier changes its status codes. Classification gets worse without any visible sign. Nobody finds out until a key customer complains to management. The exam distractor is "monitor latency and error rates". That monitoring is necessary, but a latency dashboard cannot see a classification that is wrong but confident. Only captured overrides can show it.
Start with the building block. Anthropic's article Building Effective Agents names the augmented LLM as the unit that every pattern is built from. It is a model call with three additions: retrieval (the application supplies relevant documents), tools (the model can request an action), and memory (state that the application carries between calls). Halden's draft step is exactly this. It is one call inside a sequence that code defines, and that is why the whole path stays a workflow.
Here is the request for the draft step. The application has already retrieved the shipment record (retrieval) and offers one lookup tool (tools). The summary of the customer's previous case is memory that the application chose to include. This request is illustrative; replace the model placeholder with a supported model.
JSON
{
"model": "YOUR_SUPPORTED_MODEL",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "You draft first replies to shipment-exception emails for Halden Freight. Use only the shipment record and carrier data supplied. Never state or promise a refund, credit, or claim outcome. Choose next_action from: send_eta_update, request_commercial_invoice, confirm_address, open_damage_report, escalate_to_operator.",
"cache_control": {"type": "ephemeral"}
}
],
"tools": [
{
"name": "get_carrier_eta",
"description": "Return the carrier's latest estimated delivery date for a tracking number. Use when the customer asks when a delayed shipment will arrive.",
"input_schema": {
"type": "object",
"properties": {
"tracking_number": {"type": "string"}
},
"required": ["tracking_number"],
"additionalProperties": false
}
}
],
"messages": [
{
"role": "user",
"content": "<shipment_record>\n{\"tracking_number\":\"HF88213\",\"status\":\"in_transit\",\"origin\":\"Rotterdam\",\"destination\":\"Lyon\",\"promised_date\":\"2026-09-10\"}\n</shipment_record>\n<customer_history>\nPrevious case EXC-2026-088102: delay, resolved with ETA update.\n</customer_history>\n<classification>delay</classification>\n<email>\nHi, order HF88213 was due yesterday and nothing has arrived. When will it get here?\n</email>\n\nDraft the reply and choose next_action."
}
]
}
Not supplied in this example β defaults and disabled features
Where each augmentation sits in that JSON. Retrieval is the <shipment_record> block inside messages[0].content; the application put it there. Tools are tools[0]; the application offered exactly one. Memory is the <customer_history> block; the application chose what to remember. The system block holds the standing policy. It also has a cache marker, because it never changes from one exception to the next.
The model may request the tool. Here is an illustrative response with one tool_use block and a stop_reason of tool_use:
The harness runs get_carrier_eta, adds a tool_result block, and calls the model again. That second call returns the draft. Two calls do not make this an agent. The code decided that there would be a lookup step, and it limited that step to one tool round. The model only chose whether to use the one tool it was offered.
Now the decision rule. Foundations taught the workflows-versus-agents rule: use the simplest thing that works. Professional asks you to apply that rule to each path in the same system, and to write down the reasons:
Workflow + [routing](term:workflow routing) to a vision-capable prompt
Still fixed; one extra branch
Customs hold with missing documents
No: which lookups are needed depends on what the first lookup reveals
Bounded [agent](term:agentic system) (max 6 turns, 3 tools, then human)
Path cannot be hardcoded; budget contains the risk
Refund request
Not applicable
Route to human; no model action
Out of scope by the brief
The pattern choice is written down, not kept in someone's memory. The artifact is an architecture decision record (ADR). It states the context, the decision, the rejected alternatives, and the consequences. A Professional-level reviewer asks for this document when the VP says "why not just an agent?"
MARKDOWN
# ADR-003 Β· Triage path is a workflow; customs holds are a bounded agent
Status: Accepted Β· Date: 2026-09-08 Β· Owner: Solutions architect
## Context
85% of exceptions follow a known sequence (classify β look up β draft β gate).
Customs holds require a variable number of lookups depending on which
documents are missing. Enterprise SLA: p95 first reply < 2 h.
## Decision
- Fixed-sequence exceptions run as a routing workflow with one augmented-LLM
draft step (one offered tool, one tool round).
- Customs holds run as an agent bounded to 6 turns and 3 read-only tools;
on budget exhaustion the case goes to the human queue with the trace.
- A deterministic send gate sits between every model output and the customer.
## Alternatives considered
1. Single agent for all exceptions with all tools β rejected: unpredictable
latency against a 2 h SLA; refund tool exposure contradicts the brief.
2. Pure workflow for customs holds β rejected: three hardcoded branches
still missed 40% of the pilot's hold cases.
3. Rule-based classifier without a model β rejected: 61% accuracy on the
pilot set versus 94% for the routing call.
## Consequences
+ Per-exception cost is bounded and predictable for 85% of volume.
+ Each workflow step is unit-testable against the golden set.
- Two execution paths to operate and monitor.
- The agent path needs its own trajectory evals and a turn-budget alert.
The wrong choice and its failure. "One agent, all tools" fails in two ways, and the exam names them separately. Latency: the agent may take nine tool rounds on a simple delay. That breaks the 2 h p95 for exactly the customers the project was built for. Authorization: an agent that holds a refund tool breaks [least-privilege](term:least privilege), and no prompt can fix that. The opposite error is a pure workflow for customs holds. It fails because it is too rigid: the hardcoded branches cannot cover all the cases, so the operators stop trusting the system. The exam explains these answers with two pairs of words: predictability versus flexibility and bounded versus unbounded autonomy.
Design multi-agent systems and orchestration strategies
Objective: "Design multi-agent systems and orchestration strategies."
The reason for multi-agent is context, not volume. Foundations made this point clearly in two axes, two owners. Throughput belongs to the harness (a pool, a queue, the [batch API](term:batch API)). Decomposition belongs to the pattern. Professional adds the orchestration strategy: the concrete plan for who does what, with which inputs, and what comes back.
Four strategies, one decision table. Each strategy has a real coordinator and a real result contract (an agreed shape for what comes back).
Enterprise-tier replies where tone and completeness are checked before sending
The coordinator's plan is data, and the harness checks it. Here is what the coordinator model outputs for the weekly report. It is illustrative. The model generates it under a schema that the application enforces through output_config:
JSON
{
"goal": "Weekly root-cause analysis of shipment exceptions, week 37",
"subtasks": [
{
"id": "carriers",
"instruction": "Group week-37 exceptions by carrier; report exception rate per 1,000 shipments and the top two causes per carrier.",
"inputs": ["exceptions_week37.jsonl", "shipments_week37.csv"],
"tools": ["read_dataset"]
},
{
"id": "lanes",
"instruction": "Group week-37 exceptions by origin-destination lane; flag lanes whose rate rose more than 30% versus week 36.",
"inputs": ["exceptions_week37.jsonl", "exceptions_week36.jsonl"],
"tools": ["read_dataset"]
},
{
"id": "customers",
"instruction": "List enterprise customers with any SLA breach in week 37 and the exception category of each breach.",
"inputs": ["exceptions_week37.jsonl", "sla_breaches_week37.csv"],
"tools": ["read_dataset"]
}
],
"merge": "Synthesize into a one-page report ranked by business impact."
}
Who owns what in that plan. The coordinator model chose the three subtasks and their instructions. The harness owner defined three things: the plan schema, the allowed tool names (only read_dataset, so the design is read-only), and the schema for worker results. The harness starts one worker per subtask, each with its own [context](term:context isolation). It rejects any subtask that names a tool outside the allowlist.
The orchestration sketch: coordinator, fan-out, merge, budgets. This is illustrative Python. The model calls sit behind a call_model function that this page does not define, and this code reads no data.
PYTHON
import asyncio
import json
MAX_WORKERS = 4
WORKER_TURN_BUDGET = 8
ALLOWED_TOOLS = {"read_dataset"}
def validate_plan(plan):
subtasks = plan["subtasks"]
if not 1 <= len(subtasks) <= MAX_WORKERS:
raise ValueError("plan size outside budget")
for task in subtasks:
bad = set(task.get("tools", [])) - ALLOWED_TOOLS
if bad:
raise ValueError(f"subtask {task['id']} requests disallowed tools {bad}")
return subtasks
async def run_worker(task, call_model, run_tool):
messages = [{"role": "user", "content": task["instruction"]}]
for _ in range(WORKER_TURN_BUDGET):
response = await call_model(messages, tools=task["tools"])
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return {"id": task["id"], "status": "done", "summary": response.text}
results = [await run_tool(block) for block in response.tool_uses]
messages.append({"role": "user", "content": results})
return {"id": task["id"], "status": "budget_exhausted", "summary": None}
async def weekly_report(goal, call_model, run_tool):
plan_response = await call_model([{"role": "user", "content": goal}], schema="plan")
subtasks = validate_plan(json.loads(plan_response.text))
worker_results = await asyncio.gather(
*(run_worker(task, call_model, run_tool) for task in subtasks)
)
failed = [r["id"] for r in worker_results if r["status"] != "done"]
if failed:
return {"status": "partial", "failed": failed, "results": worker_results}
merge_prompt = json.dumps({"goal": goal, "findings": worker_results})
report = await call_model([{"role": "user", "content": merge_prompt}])
return {"status": "done", "report": report.text, "trace": worker_results}
π Production shape: typed plan, SDK tool loop
Two parts of the sketch change. The plan is not read with json.loads: messages.parse sends a schema built from a Pydantic model and gives back a typed object. Each worker's hand-written turn loop becomes the SDK tool runner, with max_iterations as the turn budget. The budget and allowlist check (validate_plan above) stays your code.
PYTHON
import asyncio
import os
import anthropic
from pydantic import BaseModel, ConfigDict
from app.reports.tools import read_dataset # a @beta_tool function; read-only
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY
MODEL = os.environ.get("CLAUDE_MODEL", "claude-sonnet-5")
WORKER_TURN_BUDGET = 8
class Subtask(BaseModel):
model_config = ConfigDict(extra="forbid")
id: str
instruction: str
inputs: list[str]
tools: list[str]
class Plan(BaseModel):
model_config = ConfigDict(extra="forbid")
goal: str
subtasks: list[Subtask]
merge: str
def make_plan(goal: str) -> Plan:
response = client.messages.parse(
model=MODEL, max_tokens=2048,
messages=[{"role": "user", "content": goal}],
output_format=Plan,
)
if response.stop_reason != "end_turn": # max_tokens or refusal: no plan to trust
raise RuntimeError(f"planner stopped: {response.stop_reason}")
plan = response.parsed_output
validate_plan(plan.model_dump()) # same size + allowlist check as above
return plan
def run_worker(task: Subtask) -> dict:
runner = client.beta.messages.tool_runner(
model=MODEL, max_tokens=4096, tools=[read_dataset],
messages=[{"role": "user", "content": task.instruction}],
max_iterations=WORKER_TURN_BUDGET,
)
final = runner.until_done()
if final.stop_reason != "end_turn": # still asking for tools when the budget ran out
return {"id": task.id, "status": "budget_exhausted", "summary": None}
text = "".join(b.text for b in final.content if b.type == "text")
return {"id": task.id, "status": "done", "summary": text}
async def fan_out(plan: Plan) -> list[dict]:
return await asyncio.gather(*(asyncio.to_thread(run_worker, t) for t in plan.subtasks))
Not supplied in this example β defaults and disabled features
Where it lives: app/reports/plan.py (models + planner), app/reports/workers.py (fan-out), app/reports/tools.py (the tool).
Read the sketch for its decisions, not for its syntax. In validate_plan, the harness refuses a plan that has too many subtasks or asks for too many permissions. run_worker gives each worker a fresh messages list; that one line is context isolation. It also gives a turn budget that ends in a named failure state, never in a silent loop. asyncio.gather is the throughput axis; it would work the same way for a fixed partition. The merge call receives summaries, not full transcripts, and this protects the coordinator's [context window](term:context window). A partial status goes to a person, so the report is never published with a missing part.
Always hub-and-spoke. Workers do not send messages to each other. All coordination goes through the coordinator or the harness. This is the hub-and-spoke rule from Foundations. It means you can always tell which worker caused a failure, and you can replay the trace.
π See it run β Friday's report, one worker exhausting its budget
Step 1: the goal enters. The scheduler calls weekly_report("Weekly root-cause analysis of shipment exceptions, week 37", ...). The coordinator call returns the three-subtask plan shown above. validate_plan passes: there are three subtasks, and all tools are in the allowlist.
Step 2: fan-out. Three workers start at the same time. Each worker holds only its own instruction; no worker sees another worker's data. The lanes worker reads two weekly files (about 18,000 tokens of JSONL). Those tokens exist only in its own context.
Step 3: one worker fails cleanly. The customers worker keeps reading the breach file again and again. The reason: the customer IDs in that file do not match the format in the exception file. At turn 8 the budget runs out. The worker returns:
Step 4: the harness refuses to merge a partial result.weekly_report sees failed == ["customers"] and returns {"status": "partial", ...}. Nothing is published. The ops lead gets a partial-report alert with the trace of the customers worker attached. The ops lead finds the ID-format mismatch in ten minutes.
Step 5: the fix is a data fix, not a prompt fix. Someone corrects the breach export, and Monday's rerun completes. The team logs the event as a data-quality failure in the risk register that Pro Domain 5 maintains. The report was never wrong, because the design treated partial as a normal, named outcome.
What this proves. Budgets end in states, not in loops. Isolation kept the two healthy workers healthy. The merge step has a precondition: "all workers are done". The exam prefers this kind of harness rule over "ask the model to check whether all workers finished."
The wrong choice and its failure. Some teams choose multi-agent for speed ("three agents will finish a delay email faster than one"). This triples the token cost and adds coordination latency. It also lets three contexts disagree about one shipment. The exam explains this with two pairs of words: decomposition benefit versus coordination overhead, and context isolation versus shared state. If the stem's only reason for multiple agents is parallelism, the answer is a pool of copies of one workflow, not a multi-agent system.
Coordinator-worker, parallel, or sequential pipeline
Objective: "Evaluate multi-agent orchestration patternsβcoordinator-worker, parallel execution, and sequential pipelinesβto select the structure best satisfying research coverage, latency, and reliability requirements."
A sequential pipeline is the multi-agent form of a prompt chain. Each stage is a separate model context with its own instructions and tools. The harness passes one stage's validated output to the next stage as input. Halden uses one for the monthly carrier-contract review. Stage 1 extracts the promised service levels from each contract PDF. Stage 2 compares them with the month's measured performance. Stage 3 drafts the findings. Stage 4 checks the draft against legal wording rules. Stage 2 cannot start before stage 1, because it needs the extracted terms. Between stages, code checks the output against a schema, the same way as in a prompt chain.
Read the stem for three kinds of evidence: coverage, latency, and reliability. Each structure is strong on some of them and weak on others:
Best when the areas are not known in advance. The coordinator plans the areas, compares results with the goal, and can send a follow-up subtask for a gap
Planning call + slowest worker + merge, plus one more round for each gap
The coordinator is the single point of judgment. Workers return validated results; a missing or failed worker becomes a named partial state
Fixed by the partition list. Complete only if the list is complete
About the slowest slice, not the sum
One failed slice is isolated. Retry that slice only
Sequential pipeline
Fixed by the stage list
The sum of all stages
Easiest to check and audit: a gate after each stage. A failed stage stops everything after it (fail fast)
Now the selection rule, keyed on the evidence in the stem.
Situation
Evidence
Choose
Why the tempting alternative is wrong
Weekly root-cause report: which carriers, lanes, and customers?
The areas worth researching depend on what this week's data shows; the requirement is coverage
Coordinator-worker
Parallel over a fixed list cannot add an area the list forgot. Sequential would make each area wait for the previous one for no reason
Re-classify 30 days of exceptions, one slice per carrier
Partition known; slices independent; the requirement is latency
Parallel execution (code fan-out)
A coordinator adds a planning call and a non-deterministic plan, with no coverage benefit. Sequential multiplies the wall-clock time by the number of carriers
Each stage needs the previous output; the requirement is reliability and an audit trail
Sequential pipeline with a validation gate between stages
Parallel starts stage 2 without the extracted terms, so it guesses. A coordinator that "decides the order" adds variation to an order that never changes
Report misses a whole area, yet every worker reports success
Workers did their assignments; nobody was assigned the missing area
Fix the coordinator's decomposition (and add a coverage check against the goal)
Re-prompting or replacing the workers cannot help. They were never given that area
Structures combine. A coordinator can run its workers in parallel. A stage in a pipeline can itself fan out. Choose the structure at each point by the evidence at that point. Say in the ADR which requirement each choice protects.
π See it run β the report that missed customers
The symptom. In week 38 the report covered carriers and lanes well. It said nothing about the enterprise customer whose SLA was breached four times. Every worker returned status: "done".
The evidence. The coordinator's plan for week 38 had only two subtasks: carriers and lanes. No worker failed. No worker was asked about customers.
The fix. The failure sits in the coordinator's decomposition, so the fix sits there too. The harness owner adds a coverage rule that code can check: every plan must include the three required areas (carriers, lanes, customers). The coordinator may add more areas, but it may not drop these. Before the merge, the coordinator also compares the findings with the goal and lists the open questions. Each open question becomes one more worker round, inside the plan budget.
What this proves. Coverage is the coordinator's job. Worker success says nothing about whether the right subtasks existed.
Goal-oriented or procedural delegation
Objective: "Evaluate subagent delegation strategiesβgoal-oriented versus procedural instructionsβand select the approach that enables adaptive behavior while maintaining coordinator visibility and control."
Goal-oriented does not mean vague. The brief has four parts. The goal says what the result is for. The success criteria say how the coordinator will judge it. The constraints say what the worker may not do: scope, tools, turn budget. The return contract says what comes back. The contract asks for findings, but also for the method the worker used, its confidence, and anything it could not resolve. The worker adapts inside these limits. The coordinator keeps visibility (it can see how each result was reached) and control (it judges the result against criteria it wrote).
Here is the brief for the customers worker. It is illustrative application data: the coordinator model writes it under a schema, and the harness sends it as the worker's first user-role message. A person does not type it.
JSON
{
"subtask_id": "customers",
"goal": "Explain the week-37 SLA breaches for enterprise customers so the ops lead can act on them.",
"success_criteria": [
"Every row in sla_breaches_week37.csv is either explained or listed as unmatched",
"Each explanation names the exception category and case_id"
],
"constraints": {
"inputs": ["exceptions_week37.jsonl", "sla_breaches_week37.csv"],
"tools": ["read_dataset"],
"max_turns": 8,
"out_of_scope": "Do not propose credits or contract changes"
},
"return_contract": {
"findings": "list of {customer, case_id, category, cause}",
"unmatched": "list of breach rows you could not link, with the reason",
"method": "two or three sentences: how you joined the files",
"confidence": "high | medium | low"
}
}
Why this brief handles the week-37 surprise. The customer IDs in the breach file had a different format from the exception file. A procedural script ("join on customer_id") gets zero matches, or it loops until the budget runs out. A goal-oriented worker can notice the mismatch, try matching by case_id instead, and report what it did in method. Rows it still cannot link go into unmatched. The coordinator sees the changed method and the gaps, so the adaptation is visible, not hidden.
Who enforces what. The brief only states the constraints to the model. The harness enforces the ones that must hold: it gives the worker only the read_dataset tool and stops it after 8 turns. The schema check on the returned object is also code. A sentence in the brief is guidance; a missing tool is a boundary.
When procedural instructions are the right choice. Use a fixed script when the steps themselves are the requirement. An example is a customs-compliance check where regulation sets the order of the checks, and each step must be done and recorded. Then adaptivity is a risk, not a benefit. Ask a second question too: if every step is fixed, does the step need a model at all? Often the right design is code for the fixed steps, with a model call only for the one step that needs judgment (reading a free-text document, for example).
Situation
Evidence
Choose
Why the tempting alternative is wrong
Research where the data may surprise you
Inputs vary; the right method depends on what the worker finds
A step-by-step script breaks on the first surprise, and the worker cannot report why
Regulated check with a mandated order
The order and evidence of each step are the requirement
Procedural steps, each recorded (or plain code)
"Let the worker decide" can skip or reorder a mandated step
Coordinator cannot tell how a worker reached its result
Results arrive as free text with no method or gaps
Add a return contract (method, confidence, unmatched)
Writing more detailed steps removes adaptivity and still does not report gaps
Worker uses a tool it should not have
A constraint was only written in the brief
Remove the tool in the harness
A stronger sentence in the brief is still a request, not a boundary
Persist pipeline state for reliable resumption
Objective: "Design state persistence strategies for multi-agent pipelines that enable reliable resumption after interruption without repeating completed work or losing prior findings."
Name the runtime first. Halden's report harness is a custom application on the Messages API. The API keeps no state between requests: the application sends the whole messages array every time. So the application must keep the run's state. Claude Code and the Agent SDK can resume a session (claude --resume <id>; in the SDK, resume=<session_id>). Session resume restores the conversation, including its tool calls. It does not tell you which subtask outputs were saved. The Claude Code docs also say: "Sessions persist the conversation, not the filesystem." A session is not a work manifest.
The manifest is the checkpoint, and the coordinator's harness owns it. Workers never write it directly. The harness writes one entry per subtask. Each entry has a status, the attempt count, a key built from the inputs, and a pointer to the stored output. Here is the manifest as the application stores it (application state, not an API payload; the storage path is illustrative):
The key is subtask id + input_key. A rerun with the same key overwrites the same output; it never adds a second one
Duplicate findings or duplicate side effects
Resume = read the manifest
Skip done entries whose input_key still matches. Rerun pending, running and failed entries
Repeating finished work, and silently skipping unfinished work
Merge from stored outputs
The merge call loads summaries through output_ref, not from any earlier context
A merge that depends on a context that no longer exists
Why running is rerun. After a crash, a running entry means "started, but no saved output". The harness cannot know whether the worker was nearly finished. Because the subtask is idempotent, running it again is safe. If the inputs changed since the first run (the input_key no longer matches), a done entry is also stale, and the harness reruns it.
Situation
Evidence
Choose
Why the tempting alternative is wrong
Run stops after 2 of 3 workers finish
Two findings are saved; one subtask has no output
Resume from the manifest: run only customers, then merge
"Re-run everything" pays for the two finished workers again, takes longer, and can produce different findings for work that was already accepted
Coordinator context is replaced or compacted mid-run
Earlier worker findings existed only in the coordinator's messages
Store each finding outside the context; keep pointers in the manifest
"Rely on the conversation history" fails exactly when history is lost or summarized
Rerun after a crash creates duplicate rows in the report
The rerun added a second copy of the same subtask's output
Idempotent writes keyed by subtask id + input key
"Be careful not to rerun" is not a control; crashes decide when reruns happen
A stage's input file was corrected after the first run
The input_key of a done subtask no longer matches
Rerun that subtask; keep the others
Trusting the old done status merges findings built on the wrong data
π See it run β Friday's report survives a restart
Step 1: the interruption. At 06:13 the report job's container is restarted during a deploy. carriers and lanes are done, with saved outputs. customers is running, with no output.
Step 2: the scheduler starts the job again with the same run_id. The harness finds runs/rca-2026-w37/manifest.json and does not create a new plan. It recomputes each input_key; all three still match.
Step 3: only unfinished work runs. The harness skips carriers and lanes. It starts customers again with a fresh context and the same brief, as attempt 2. The worker finishes. The harness writes customers.json, then marks the entry done.
Step 4: the merge reads from storage. The merge call loads the three stored summaries through their output_ref values. No earlier context is needed.
What this proves. The cost of the restart was one worker, not three. No accepted finding changed, and none was lost.
Apply decomposition techniques for complex problem solving
Objective: "Apply decomposition techniques for complex problem solving."
Decomposition is the point where the architecture stops being a sketch. There are four techniques, and each one creates a different kind of seam:
money-related intents leave the model path entirely
Data split
A change in what the step needs to see
Separate calls or [isolated contexts](term:context isolation)
the classifier never sees the customer history; the drafter never sees other customers' cases
Skill split
A change in which model or prompt is best
[Routing](term:workflow routing) to a specialised prompt or a different [model tier](term:model tiering)
damage reports with photos route to a vision-capable prompt
The decomposition tree is the artifact. The architect writes it before any prompt. Each leaf holds the contract that the prompt owner must meet. The house format is YAML, because it stays easy to read in a review:
YAML
problem: Reply correctly to a shipment-exception email within 2 h
decomposition:
- step: classify
kind: model_call
input: email text only
output: {category: enum, confidence: number}
check: confidence >= 0.8 else route human
owner: prompt owner
- step: retrieve
kind: code
input: tracking number parsed from email
output: shipment record snapshot
check: record exists and belongs to sender's account
owner: platform team
- step: draft
kind: model_call
input: shipment record, classification, allowed next_actions
output: {reply_text: string, next_action: enum}
check: schema-validated; next_action in allowed list
owner: prompt owner
- step: verify
kind: code
input: reply_text
output: pass | block
check: no currency amounts; no claim language; tier template matches
owner: platform team
- step: send
kind: code
input: verified reply, next_action
output: ticket update, customer email
check: idempotency key = case_id
owner: platform team
Read the owner field the way the exam reads it. Model calls belong to the prompt owner, who tests them with the golden set. Code steps belong to the platform team, who test them with unit tests. A stem may ask "who should own the accuracy of the classification step". It is testing whether you kept these two kinds of owner apart.
Decomposition also decides what is cached and what is retried. The output of the retrieve step is a snapshot. The draft call can reuse it if the draft is retried. The send step carries an idempotency key, so a retry after a timeout cannot email the customer twice. You do not add these later; they come directly from the seams. Foundations covers the mechanics in scale a fixed workflow across many documents.
The wrong choice and its failure. Imagine one prompt that says "1. classify the email, 2. look up the shipment, 3. draft a reply, 4. check for refund language, 5. send". That is not decomposition. It is a numbered list inside a single call. Without a tool, the model cannot look anything up and cannot send anything. Its "check" is only a sentence, not code. The exam distractor says "add step-by-step instructions to the system prompt". It fails because steps inside a prompt are not seams with contracts. The exam explains this with two pairs: written instruction versus enforced boundary, and single-call versus chained with checks.
Select the review architecture (plan mode, direct execution, multi-phase)
Objective: "Select the appropriate agentic review architectureβplan mode, direct execution, or multi-phase workflowβbased on task scope, risk level, and human approval requirements."
Name the runtime. This section is about Halden's platform team using Claude Code (the coding agent) to change Halden's own code. Plan mode is a Claude Code permission mode. The Claude Code harness enforces it, not the model. A custom Messages API application has no plan mode; there you build the same review step yourself (for example, a proposal that waits in an approval queue).
What plan mode does in Claude Code (from the Claude Code docs). Claude "reads files, runs shell commands to explore, and writes a plan, but does not edit your source." Edits stay blocked until you approve the plan. Commands outside the read-only set ask for permission. When you approve, Claude Code leaves plan mode and enters the mode that the approve option names. Ways to turn it on:
BASH
# Start a session in plan mode
claude --permission-mode plan
# In a running session: press Shift+Tab to cycle default -> acceptEdits -> plan,
# or start one prompt with /plan
To make plan mode the project default, set it in .claude/settings.json (illustrative file, one key shown):
JSON
{
"permissions": {
"defaultMode": "plan"
}
}
One documented exception. In interactive terminal sessions where bypass permissions are available, plan-mode blocks are not enforced. In -p (print, headless) runs and Agent SDK sessions, plan mode keeps its blocks. So if a team relies on plan mode as a control, it must check how its sessions are configured.
Three review architectures, three kinds of evidence.
Architecture
Scope
Risk
Human approval
Where the review happens
Direct execution
Small, clear, well specified (one file, known fix)
Low; tests cover it
None before the change; normal code review after
Tests + the usual pull-request review
Plan mode
Several files, or several valid approaches, or a sensitive file
Medium to high
The developer approves the plan inside the session
Read-only exploration, then the plan, before any edit
Multi-phase workflow
Large (many files, several services), or regulated
High; mistakes are costly or hard to undo
Recorded approval, often by someone other than the developer, before implementation
A gate after each phase: explore β plan β approve β implement (in stages) β verify
Multi-phase is more than plan mode. Plan mode gives one review point inside one session. A multi-phase workflow makes each phase produce an artifact that people can check. Exploration produces a written map (an Explore subagent keeps that long output out of the main context). Planning produces a plan document. Approval produces a record, for example a change ticket or an ADR, signed by the owner the brief names. Implementation runs in stages. Verification runs the tests, the eval set, and a review against the plan. This is a design practice built from Claude Code features; Claude Code does not have a single "multi-phase" switch.
Situation
Evidence
Choose
Why the tempting alternative is wrong
Typo in the delay-reply template
One file; no logic change; tests exist
Direct execution
Plan mode adds a review step that finds nothing; it only slows the fix
Add offer_redelivery to the send gate's allowed actions
One file, but it is the money-safety control; Legal owns that line of the brief
Plan mode, and the plan names the eval cases to add
"It's one line, edit directly" treats small scope as low risk. The risk is in what the file controls
Replace the ticketing integration (40 files, customer data, an EU data-residency rule)
Large scope, regulated data, approval needed from the platform owner
Multi-phase: explore β plan β recorded approval β staged implementation β verify
Plan mode alone gives no recorded sign-off by the owner and no verify phase. Direct execution "lets the design emerge" and finds the dependencies in the middle of editing
Team asks for "always ask before editing"
The rule is written in CLAUDE.md
Use plan mode or permission settings, which the harness enforces
The Claude Code docs treat CLAUDE.md as context, not enforced configuration. A sentence there is a request
Align solutions to business value pillars (efficiency, transformation, productivity, cost, performance SLAs)
Objective: "Align solutions to business value pillars (efficiency, transformation, productivity, cost, performance SLAs)."
The exam's stems use the five pillars as their vocabulary. Learn to recognise them:
Pillar
The stem saysβ¦
The number
Halden target
Efficiency
"staff spend hours onβ¦", "backlog"
Hours or cases per person per day
22 staff-hours/day recovered; ops handle only escalations
Transformation
"customers cannotβ¦", "new capability"
Something that was not possible before
Customers check exception status themselves; 24 h coverage
Productivity
"each analyst can onlyβ¦", "throughput per person"
Output per person per unit time
Each operator handles 3Γ more cases per day (escalations only)
Cost
"spend", "per-request", "budget"
Money spent per unit of work
Model spend per exception stays under the finance ceiling; reviewed monthly
Performance SLA
"must respond within", "p95", "uptime"
A threshold with a consequence
p95 first reply < 2 h; correct-reply rate β₯ 90%
An SLA is a contract, so write it like a contract. The SLA table names six things: the metric, the threshold, the measurement window, how it is measured, what happens when it is missed, and which design choice protects it. The last column belongs to the architect, and the exam looks there.
SLA
Threshold
Window
Measured from
Breach action
Design that protects it
First-reply latency
p95 < 2 h
Rolling 7 days
Email received β reply sent (ticket timestamps)
Alert the ops lead; send enterprise-tier cases to the human queue automatically
Fixed workflow path with one tool round; [prompt caching](term:prompt caching) on the policy prefix
First-reply correctness
β₯ 90%
Weekly
A sample of cases, graded against what an operator would decide
Stop prompt changes; run the eval; find the root cause
Feedback loop β [golden set](term:golden set); eval gate before any prompt change
Money-safety
0 replies with financial promises
Continuous
Send-gate block log + weekly audit sample
Stop automatic sending at once; open an incident
Deterministic send gate; refund intents routed to humans
Agent-path containment
100% of customs-hold runs end within 6 turns
Continuous
Run trace stop_state
Alert; send the case to the human queue with its trace
Turn budget in harness; budget exhaustion is a named state
Review the model tier; retrieve less, more precise context
[Model tiering](term:model tiering); caching; batch API for the weekly report
SLOs are the internal targets behind the SLA. The team works toward an internal objective that is stricter than the contract (p95 < 90 min), so that breaches are rare. In a repository, the objectives sit next to the alerts:
YAML
service: exception-triage
slos:
- name: first_reply_latency
objective: p95 <= 90m
sla_threshold: p95 <= 120m
window: 7d
source: ticket.created_at -> ticket.first_reply_at
alert: p95 > 100m for 30m
- name: first_reply_correctness
objective: ">= 0.93"
sla_threshold: ">= 0.90"
window: 7d
source: weekly graded sample of 200 cases
alert: "< 0.92 on any weekly sample"
- name: money_safety
objective: 0 financial promises sent
window: continuous
source: send_gate.blocked + audit_sample
alert: any audit finding
Pillars conflict, and the ADR records the ranking. Halden's cost pillar pushes for the faster model tier everywhere. The correctness SLA pushes for the larger tier on customs holds. The architect ranks them: money-safety and correctness above cost, and cost above latency (as long as latency stays inside the SLA). The architect writes this ranking into the consequences section of the ADR. When a stem shows two pillars in conflict, it asks for that ranking, not for a way to satisfy both.
The workload shape decides which lever helps which pillar. Interactive triage is limited by latency and makes thousands of small calls. Caching and a fast tier help it. The weekly report runs offline and makes a few large calls. The [batch API](term:batch API) helps it, at lower cost, because there is no latency pressure. Using the batch API for the triage path would break the SLA. Using real-time calls for the weekly report wastes money against the cost pillar.
π See it run β the VP asks "why not just one big agent?" and the ADR answers
The question. Two weeks after launch, the VP reads about agents. The VP asks why Halden built "a pipeline" instead. The architect answers with ADR-003 and the SLA table, not with a slide deck.
Pillar 1: performance SLA. Last week, the p95 of the workflow path was 71 minutes. The p95 of the bounded agent path was 3 h 40 min. That is acceptable only because customs holds are 6% of volume and the enterprise contracts exclude them. One agent for all volume would put the 71-minute number at risk for the 85% of cases that bring in the money.
Pillar 2: cost. The usage fields show this. On average, the workflow path uses one cached policy prefix plus about 700 uncached input tokens per case. The agent path uses six model calls per case on average. One agent everywhere would multiply the spend on the 85% by about the agent's number of calls.
Pillar 3: money-safety. Last week, the send gate blocked 14 replies that contained the word "credit." All of them came from the workflow path. An unbounded agent with a refund tool has no gate that can block it. The team would need to redesign the harness, and until then the system would break the constraint in the brief.
The answer the VP hears. "We use an agent where nobody can know the path in advance (customs holds), and a workflow where we can know it. The ADR lists the single-agent option as alternative 1, and the numbers above are why it lost. If the mix of exceptions changes, we replace the ADR with a new one; we do not ignore it."
What this proves. Value alignment is not a slide. It is the last column of the SLA table plus the alternatives section of the ADR, updated with live numbers. That is what "align solutions to business value pillars" looks like as an artifact.
The wrong choice and its failure. Imagine a solution justified only as "faster and more accurate", with no baseline. When the spending appears on the finance report, nobody can defend it. The exam distractor is the design that optimises a pillar the stem did not ask for. Examples: a cheaper model when the stem said accuracy is in the contract, or maximum autonomy when the stem asked for predictability. The exam explains this with two phrases: stated constraint versus assumed preference, and optimising the wrong pillar.
Exam lens β how CCAR-P asks about this domain
Tells: phrases that name the objective being tested.
"stakeholders cannot agree what success looks like" / "no baseline exists" β translation objective. The answer writes the measurable outcome first.
"accuracy degraded months after launch with no code change" β end-to-end loop objective. The answer adds or fixes the feedback path (captured overrides β eval). A bigger model is not the answer.
"the steps are known and repeatable" β workflow. "the next step depends on what the previous step finds" β bounded agent. "one call needs documents and a lookup" β augmented LLM.
"independent research areas" / "each subtask needs its own large context" β orchestratorβworkers. If the only reason is "it would be faster in parallel" β a pool around one workflow, not multi-agent.
"a single prompt with numbered steps" / "the model sometimes skips a step" β decomposition objective. The answer chains calls, with code checks between them.
"contract requires p95 underβ¦" / "finance set a per-request ceiling" β value pillars and SLA. The answer names the design choice that protects the threshold.
"the report must not miss any relevant area" β coordinator-worker with a coverage check. "independent slices, results needed fast" β parallel execution. "each step needs the previous step's output" / "auditable order" β sequential pipeline with gates between stages.
"every worker succeeded but an area is missing" β the coordinator's decomposition is too narrow. Fix the plan, not the workers.
"the worker must adapt to the data" + "the coordinator must see how results were reached" β goal, success criteria, constraints, and a return contract. "regulation fixes the order of the checks" β procedural steps (or plain code).
"the pipeline was interrupted" / "without repeating completed work" β a coordinator-owned manifest in durable storage with idempotent subtasks. Not "re-run everything", and not "rely on the conversation history".
"small, well-specified, low-risk change" β direct execution. "several valid approaches" / "sensitive file" β plan mode. "large, regulated, sign-off required" β multi-phase workflow with a recorded approval.
In this domain, "which TWO" items usually pair a pattern choice with the control that makes it safe (bounded agent + turn budget; workflow + send gate).
Trade-off words that the rationale uses. Learn to recognise these pairs. A distractor is always the other half of a pair:
Pair
The correct answer usually sits onβ¦
Predictability vs flexibility
Predictability, unless the stem says the path cannot be known
Bounded vs unbounded autonomy
Bounded β a budget that ends in a named state
Decomposition benefit vs coordination overhead
Overhead wins unless isolation or independence is stated
Written instruction vs enforced boundary
Enforced β code gates, not prompt sentences
Stated constraint vs assumed preference
The stated one; never optimise a pillar the stem did not name
Removal vs compensating control
Removal β leave the tool out β when the role does not need the capability
Detective vs preventive control
Preventive for money and safety; detective (logs, dashboards) is necessary but insufficient
How the three official sample questions connect to this page. Sample 1 (remove unneeded tools) is scope translation: the out-of-scope list in the brief decides the tool list. Sample 2 (stable prefix + caching) is about the cost pillar and the workload shape. Sample 3 (retrieval after a refresh) is about the end-to-end loop: the input stage changed, so look there first. "A better model" is not the answer to any of them, and it is not the answer to the items in this domain either.