Capstone: End-to-End Architecture Review
The exam guide's own preparation advice is to build and operate at least one end-to-end Claude solution, including RAG, evaluation, and observability. This lab is that instruction made concrete. You will be handed a real-looking design for a global bank, seeded with one defect in every domain, and asked to do what an architect is actually paid for: read a proposal and say what is wrong with it before it ships. Work it as a review. Then compare with the annotated findings.
The capstone exercises all seven CCAR-P domains at once; the review below plants a defect in each, weighted as the exam weights them:
- Domain 1 — Solution Design & Architecture · 17%
- Domain 2 — Claude Models, Prompting & Context Engineering · 13%
- Domain 3 — Integration · 19%
- Domain 4 — Evaluation, Testing & Optimization · 16%
- Domain 5 — Governance, Safety & Risk Management · 14%
- Domain 6 — Stakeholder Communication & Lifecycle Management · 14%
- Domain 7 — Developer Productivity & Operational Enablement · 7%
1. How to Use This Lab
This is an architecture review, not a tutorial. There is nothing to install and no notebook to run. The spine of the lab is a single realistic system, presented the way a design doc would present it: neutrally, confidently, as if a competent team wrote it. It is not competent. It contains at least one seeded defect in every exam domain.
Do the work in this order:
- Read the brief and the proposed architecture as a reviewer. Do not scroll ahead to the findings. Open a blank page and write down every defect you can name, tagged with the domain it belongs to. Aim for seven.
- Force yourself to say the repair out loud. Finding "the retrieval is wrong" is worth little; "swap dense-vector retrieval for an exact clause lookup because the query is a key, not a concept" is the answer the exam rewards.
- Then read the annotated review. Score yourself: one point per defect found and correctly repaired. Fewer than five, and the linked lab for each miss is your revision list.
The defects are architectural: wrong pattern, wrong retrieval, missing gate, wrong metric, wrong prompt order. None of them is a deprecated API parameter. (A reviewer would of course also reject temperature= or a budget_tokens thinking budget on sight, because both now return HTTP 400 on current models, but that is a syntax error, not an architecture defect, and it is not what this lab is testing.)
2. The Brief
Self-driven lab Lab11_Self_Driven_Lab.ipynbA global bank runs a Regulatory Change Assistant. Analysts track regulatory publications across 14 jurisdictions. For each new publication they must determine which of the bank's internal policies the change affects, draft an impact memo, and route that memo for approval. The bank receives roughly 300 documents a day. Regulators require that every assertion in a memo be traceable to a source: a claim with no citation is a compliance defect on its own.
The team building the assistant has written a design doc. Before you read their architecture, hold their own requirements in front of you, because the requirements are what the architecture will be judged against.
| Requirement | Value | Why it constrains the architecture |
|---|---|---|
| Volume | ~300 documents/day | Enough repetition that per-request cost matters; a stable, cacheable prefix pays off. |
| Task | Map change → affected policies → impact memo → route for approval | The steps are fixed and enumerable. That is the tell for a workflow, not an agent (Lab 1). |
| Traceability | Every assertion cites a source | Citation is a hard output contract, not a nicety. It also creates the evaluation seam. |
| Precision of retrieval | Must resolve exact clauses, e.g. "Article 17(2)" | The query is a key, not a concept. Dense-vector search is the wrong tool (Lab 4). |
| Privacy | GDPR applies (EU staff); one jurisdiction mandates data residency | PII handling and regional routing are governance constraints, not features (Lab 8). |
| Latency | p95 answer under 30 seconds | A real SLA. Caching helps it; batching would destroy it. |
| Blast radius | A wrong "no impact" verdict is a regulatory finding | The high-risk branch must have a human gate. This is architecture, not UX. |
3. The Proposed Architecture
Here is the team's design, in their words. Read it as written; it sounds reasonable. That is the point.
"We built an autonomous agent. On each new publication it decides for itself which tools to call: it searches the policy corpus, reasons about impact, and files the memo. We give it a broad tool set so it can handle anything a regulator throws at us. Retrieval is a vector search over the embedded policy corpus. If the agent finds no affected policy, it files a 'no impact' verdict automatically so analysts only ever see the memos that matter. We measure success by average answer latency and by a rubric score from an LLM judge. We have promised the regulator 99% accuracy on live traffic."
The component table below is how the design doc summarizes it. Every row hides at least one defect.
| Component | As designed |
|---|---|
| Control flow | Autonomous agent chooses tools per turn |
| Prompt assembly | Date-stamped preamble at the very top; today's publication, then the 60K-token policy corpus after it |
| Tools | search_policies, file_impact_memo, delete_policy |
| Identity | One service account holding the union of every jurisdiction's permissions |
| Retrieval | Dense-vector similarity search over embedded clauses |
| Egress | "No impact" verdicts auto-file; PII passed to the model unredacted; outputs retained indefinitely |
| Evaluation | Average latency + an uncalibrated LLM-judge rubric; no held-out set |
| Stakeholder commitment | 99% accuracy SLA to the regulator on live traffic |
| Developer setup | Secrets pasted into the project CLAUDE.md so the whole team shares them |
And here is the core call, roughly as it appears in the codebase. It uses the current API (adaptive thinking, no removed sampling parameters) so nothing here throws a 400. The defect is architectural: look at the order of the system blocks.
import anthropic
from datetime import datetime
client = anthropic.Anthropic()
# The regulatory publication we are analysing today. It changes every run.
incoming_publication = load_todays_publication()
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=8000,
thinking={"type": "adaptive"},
system=[
# A fresh timestamp at the very top "so the model knows the date".
# This byte changes every request, so nothing after it can ever cache.
{"type": "text",
"text": f"You are a regulatory analyst. Today is {datetime.now().isoformat()}."},
# The VARYING document, placed before the static corpus...
{"type": "text", "text": incoming_publication},
# ...and the 60,000-token policy corpus AFTER it, with a cache breakpoint
# that is therefore downstream of two invalidators and never gets reused.
{"type": "text", "text": POLICY_CORPUS,
"cache_control": {"type": "ephemeral"}},
],
tools=[
{"name": "search_policies", "description": "...", "input_schema": {...}},
{"name": "file_impact_memo", "description": "...", "input_schema": {...}},
{"name": "delete_policy", "description": "...", "input_schema": {...}}, # never needed
],
messages=[{"role": "user",
"content": "Decide which policies this change affects, then file the memo."}],
)
4. The Review, Domain by Domain
Seven findings, one per domain. Each has the same shape: the finding, why it fails, the repair, and the lab that covers it in full.
D1 — Solution Design: an agent where a workflow was sufficient
The finding. The design hands control flow to the model: an autonomous agent that decides which tools to call. But the brief's own task description enumerates the steps: retrieve affected policies, draft a memo, route it. The path never changes.
Why it fails. An agent buys non-determinism, higher cost, higher latency, and an audit trail nobody can reconstruct, all to rediscover a sequence the team already wrote down. For a regulated process, the un-reconstructable audit trail is the worst part: you cannot tell a regulator why a given memo was produced.
The repair. A workflow whose classification step is a single augmented-LLM call over the retrieved policy terms, with code owning the sequence. Reserve the agent for the genuinely-unknown-path case, which this is not. See Lab 1 and Lab 2.
D2 — Prompting & Context: the cache can never hit
The finding. Two invalidators sit in front of the corpus. A datetime.now() preamble at the very top changes on every request, and the varying publication is placed before the static 60K-token corpus. The cache breakpoint is downstream of both.
Why it fails. Caching is a prefix match: any byte change before a breakpoint invalidates everything after it. Render order is tools → system → messages, so the stable content must physically come first. As written, the bank re-processes 60K tokens at full price 300 times a day and blows the p95 latency SLA that a warm cache would have protected.
The repair. Put the corpus first with the cache_control breakpoint; move the date into the user turn where it belongs; put the varying publication last, after the breakpoint. See Lab 3.
D3 — Integration: capability bloat, a confused deputy, and the wrong retrieval
The finding. Three integration defects in one row. The agent holds a delete_policy tool it never needs. It authenticates as one service account holding the union of every jurisdiction's permissions. And it retrieves exact clauses like "Article 17(2)" with dense-vector similarity search.
Why it fails. The unused destructive tool is pure blast radius: a tool that can only hurt you. The union-permission service account is the classic confused deputy: a prompt-injected publication could make the assistant act with privileges far beyond the task, across jurisdictions it should never touch. And dense vectors answer "what is similar to this?" when the query is an exact key: "Article 17(2)" is a lookup, and embedding it is architecture theatre that will silently return near-miss clauses.
The repair. Delete delete_policy (least privilege). Scope credentials to the jurisdiction of the document being processed. Replace vector search with an exact clause lookup (a keyed index), keeping semantic search only for the genuinely fuzzy "what else might this touch?" pass. See Lab 4 and Lab 5.
D4 — Evaluation: measuring the wrong things, with an uncalibrated judge
The finding. Success is measured by average latency and by a rubric scored with an LLM judge that was never calibrated against human graders. There is no held-out set.
Why it fails. Average latency hides the tail; the SLA is p95, and an average can look healthy while one memo in twenty times out. An LLM judge that has never been checked against humans is an opinion of unknown correlation with ground truth: you do not know whether a high rubric score means a good memo. And with no held-out set, any prompt change is tuned against the same data it is scored on, so improvements are indistinguishable from overfitting.
The repair. Track p95, not the mean. Calibrate the judge against a human-labelled sample and report the agreement rate before you trust it. Freeze a held-out regression set that no prompt change is allowed to see. See Lab 6 and Lab 7.
D5 — Governance: an auto-filed verdict on the highest-blast-radius branch
The finding. A "no impact" verdict auto-files with no human gate, on the exact branch the brief says produces a regulatory finding when wrong. PII is sent to the model unredacted, and outputs are retained indefinitely.
Why it fails. The brief spends its words on one fact: a wrong "no impact" is a regulatory event. That is a governance constraint, not a UX preference, and the design has automated precisely the decision that most needs a human. Unredacted PII to the model widens the GDPR surface for no benefit; unbounded retention turns every stored memo into a standing liability under data-residency and right-to-erasure rules.
The repair. Put a named human on the "no impact" path (auto-file only positive, low-risk "impact found & routed" cases if at all). Redact PII before the call where it is not needed for the decision. Set a retention policy tied to the residency requirement. See Lab 8.
D6 — Stakeholder & Lifecycle: an accuracy SLA nobody can honour
The finding. The team promised the regulator 99% accuracy on live traffic.
Why it fails. "Accuracy on live traffic" has no denominator you control: you cannot know the true label on a novel publication until an analyst rules on it, so you cannot measure the number you promised in real time, let alone guarantee it. It also anchors the relationship to a figure that a single hard month will breach, converting a capability into a liability. This is a lifecycle-management failure: a commitment made without an evaluation methodology that can support it.
The repair. Commit to what you can evidence: a measured accuracy on a frozen, human-labelled benchmark, a documented human-in-the-loop gate on the high-risk branch, and a monitored p95 latency. Promise the process, not a live-traffic percentage. See Lab 9.
D7 — Developer Productivity & Ops: secrets in CLAUDE.md
The finding. API keys and credentials are pasted into the project CLAUDE.md so the team shares one setup.
Why it fails. CLAUDE.md is committed to the repository and loaded into context on every run; it is documentation, not a vault. Secrets there are exposed to everyone with repo access, leak into logs and traces, and are near-impossible to rotate cleanly. For a bank this is an audit finding by itself.
The repair. Move secrets to environment variables or a managed secret store; reference them from code, never from context. Keep CLAUDE.md for the operational knowledge it is good at: how to run the eval suite, where the runbook lives. See Lab 10.
5. The Repaired Architecture
The repaired core call inverts the two things that mattered most in the code: the prompt order (stable corpus first, behind a cache breakpoint) and the output contract (structured output forcing a citation on every assertion). The date and the varying publication both move into the user turn, after the breakpoint.
import anthropic
client = anthropic.Anthropic()
# Every assertion must carry a source. The schema makes that a hard contract:
# an uncited claim cannot validate, so the regulator's rule is enforced by shape.
IMPACT_SCHEMA = {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"verdict": {"type": "string", "enum": ["impact", "no_impact"]},
"affected_policies": {"type": "array", "items": {"type": "string"}},
"cited_sources": {
"type": "array",
"items": {
"type": "object",
"properties": {
"clause": {"type": "string"}, # e.g. "Article 17(2)"
"quote": {"type": "string"},
},
"required": ["clause", "quote"],
"additionalProperties": False,
},
},
},
"required": ["verdict", "affected_policies", "cited_sources"],
"additionalProperties": False,
},
}
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=8000,
thinking={"type": "adaptive"},
output_config={"effort": "high", "format": IMPACT_SCHEMA},
system=[
# STABLE prefix first: the 60K-token corpus, cached across all 300 docs/day.
{"type": "text", "text": POLICY_CORPUS,
"cache_control": {"type": "ephemeral"}},
],
messages=[
# Everything volatile goes AFTER the breakpoint: the date and today's doc.
{"role": "user", "content": build_user_turn(today_iso, incoming_publication)},
],
)
To prove the fix rather than assume it, read the usage back. If cache_read_input_tokens is zero across repeated runs, a silent invalidator is still sitting in the prefix, and remember that input_tokens reports only the uncached remainder, so the corpus tokens should show up under the cache fields, not here.
u = response.usage
print("written to cache:", u.cache_creation_input_tokens) # ~1.25x on the cold write
print("served from cache:", u.cache_read_input_tokens) # ~0.1x on every warm hit
print("uncached remainder:", u.input_tokens) # today's doc + user turn only
# The corpus should appear under the cache fields, never under input_tokens.
assert u.cache_read_input_tokens > 0, "prefix still has a silent invalidator"
cited_sources a required field of the structured output turns the regulator's "every assertion is traceable" rule into something a validator can reject, before any human reads the memo. Do not enforce a hard output contract with a polite instruction in the prompt; enforce it with the shape of the response. That is the difference between hoping for a citation and being unable to produce output without one.
The rest of the repaired design maps cleanly onto the five planes from Lab 1. Every plane now answers its question with the defect closed.
| Plane | Repaired decision | Defect it closes |
|---|---|---|
| Ingress | Per-jurisdiction credentials; PII redacted before the call; untrusted publication text isolated from the tool-calling turn | D3 confused deputy, D5 PII |
| Context | Static corpus first behind a cache breakpoint; exact clause lookup for keyed queries, semantic search only for fuzzy recall | D2 caching, D3 retrieval |
| Reasoning | A workflow with a single augmented-LLM classification call; delete_policy removed; adaptive thinking, effort: high | D1 pattern, D3 tool bloat |
| Egress | Structured output forces cited_sources; a named human gates every "no impact" verdict; retention bounded to the residency rule | D4 contract, D5 gate & retention |
| Feedback | p95 latency and a human-calibrated judge over a frozen held-out set; analyst overrides flow back as labels | D4 metrics, D6 evidence |
6. The Artifacts You Would Hand Over
A design review is not finished when the code is right; it is finished when the next engineer, the regulator, and the on-call responder can each do their job from what you leave behind. This is the lifecycle deliverable set (Lab 9). Treat it as a checklist: a missing row is a defect the exam will ask about.
| Artifact | Answers | Present? |
|---|---|---|
| Architecture diagram (five planes) | What the system does and where each control lives | Required |
| ADR log (decision records) | Why the agent was rejected, why the lookup replaced vectors | Required |
| Eval suite + frozen regression set | How we know a change did not regress accuracy | Required |
| Runbook | How on-call recovers when a memo fails to file | Required |
| Cost model | What 300 docs/day costs, cached vs uncached | Required |
| Risk register | The residual risks after every repair, and who owns each | Required |
| On-call rotation | Who responds, and within what window | Required |
7. The Cost Model
Do the arithmetic, because "caching saves money" is not a number and a CFO does not fund adjectives. The figures below are an illustrative model, not a price quote: Claude Opus 4.8 at $5.00 per million input tokens and $25.00 per million output, cache reads at roughly 0.1× input and cold cache writes at 1.25× (5-minute TTL). Assume a 60,000-token policy corpus, a 4,000-token publication, and a 2,000-token memo, at 300 documents/day.
| Per request | Uncached (proposed) | Cached (repaired) |
|---|---|---|
| Corpus, 60K tokens | 60,000 × $5/M = $0.300 | 60,000 × $5/M × 0.1 = $0.030 |
| Publication, 4K tokens | 4,000 × $5/M = $0.020 | 4,000 × $5/M = $0.020 |
| Memo output, 2K tokens | 2,000 × $25/M = $0.050 | 2,000 × $25/M = $0.050 |
| Per request | $0.370 | $0.100 |
| × 300 docs/day | $111.00/day | $30.00/day + cache writes |
| Cache writes (corpus re-warmed ~12×/day) | — | 12 × 60,000 × $5/M × 1.25 = $4.50/day |
| Daily total | ~$111/day | ~$34.50/day |
That is the cache hit turning a large daily number into a small one: roughly a 3× reduction, driven almost entirely by the corpus moving from full price to a tenth of it. The single ordering defect in D2 was costing the bank about $76 a day, or ~$28,000 a year, for nothing.
One more distinction the exam likes: cost per request is not cost per resolved task. If 20% of memos need a second pass (a rerun, an analyst correction fed back), you make ~360 model calls to resolve 300 business tasks. At the cached rate:
| Metric | Calculation | Value |
|---|---|---|
| Cost per request | $0.100 per call | $0.100 |
| Cost per resolved task | $0.100 × 360 calls ÷ 300 resolved | $0.120 |
The CFO cares about the second number, because it is the one tied to a completed unit of work. Report cost per resolved task and you are speaking the pillar (cost per document) the business actually funds.
8. Final Self-Assessment
This is your exit criterion before you sit the exam. For each domain, can you (unprompted, with no scenario pointing at it) do the thing in the right column? If any row is a "no", the linked lab is where you go before booking the test.
| Domain | Can you, unprompted, … |
|---|---|
| 1 · Design | …name the sentence that kills the agent and choose the workflow, and defend it in one line? (Lab 1) |
| 2 · Prompting | …spot every silent cache invalidator in a prompt and put the stable prefix first? (Lab 3) |
| 3 · Integration | …recognise capability bloat, a confused deputy, and vectors used for a keyed lookup? (Lab 4, Lab 5) |
| 4 · Evaluation | …reject a mean-latency metric and an uncalibrated judge, and demand a held-out set? (Lab 6) |
| 5 · Governance | …find the branch that most needs a human gate and refuse to automate it? (Lab 8) |
| 6 · Stakeholder | …replace an unmeasurable live-traffic SLA with a commitment you can evidence? (Lab 9) |
| 7 · Dev Productivity | …get secrets out of CLAUDE.md and name the deliverables you hand over? (Lab 10) |
Check Yourself
Synthesis items that cross domains: the shape the exam uses. Commit before expanding.
The Regulatory Change Assistant auto-files every "no impact" verdict without review, and the team has promised the regulator 99% accuracy on live traffic. A reviewer flags both. Which single change addresses the most serious defect the two share?
Correct: B. Both defects stem from the same root: the highest-blast-radius decision has been automated and then wrapped in a promise nobody can measure in real time. A human gate on the "no impact" branch closes the governance hole (Lab 8), and committing to that gated process instead of a live-traffic percentage fixes the unhonourable SLA (Lab 9). One change, both domains.
A spends tokens to raise quality on a decision that should not be automated at all: more accuracy does not discharge the regulator's requirement for a human on the finding path. C improves an eval instrument that is downstream of the actual problem. D trades latency for cost on a system with a 30-second p95 SLA, and does nothing about the gate or the promise; it is a real fact attached to the wrong requirement.
The assistant re-processes its 60K-token policy corpus at full price on every one of 300 daily requests, and separately the team reports success using average answer latency. Which two changes together fix the cost and give an honest read on the SLA? (Choose two.)
Correct: A and B. A fixes the cache defect (Lab 3): a stable prefix, cached, drops the corpus from full price to ~0.1× and cuts time-to-first-token, which also helps the SLA. B fixes the metric (Lab 6): the SLA is p95, and an average can look healthy while one memo in twenty blows the bound; only the percentile tells the truth.
C halves cost but destroys latency: batches have no SLA and can take hours, which is disqualifying under a 30-second p95. D is the wrong retrieval for exact-clause queries (Lab 4) and does not address the metric. E prevents truncation but touches neither cost nor the latency read; it answers a question nobody asked.
The assistant authenticates as one service account holding every jurisdiction's permissions, and it carries a delete_policy tool it never invokes. A prompt-injected publication is the specific threat the reviewer raises. Which framing best captures the defect?
Correct: B. The union-permission account plus untrusted publication text is the textbook confused deputy (Lab 5): the injection borrows the assistant's authority to reach across jurisdictions it should never touch, and a delete_policy tool that is never legitimately needed can only ever be a weapon in that scenario. The repair is least privilege: scope credentials to the document's jurisdiction and delete the tool (Lab 4).
A and D name real but trivial costs and miss the security failure the scenario is built around. C is a distraction: better eval coverage might detect the symptom, but it does not remove the over-privilege that makes the attack possible in the first place.
The team measures memo quality with an LLM judge that was never checked against human graders, tunes prompts against the same data it scores on, and then promises the regulator 99% accuracy. Which statement is the strongest single objection?
Correct: B. This is the evaluation-to-stakeholder chain (Lab 6 to Lab 9). Without calibration against humans, the rubric score is an opinion of unknown value; without a frozen held-out set, any reported accuracy is contaminated by the tuning loop. A number built that way cannot be falsified, and a commitment resting on it is a liability the first bad month exposes.
A gets it backwards: a judge that shares the assistant's blind spots is worse, not more consistent. C misunderstands ground truth: promising 100% against labels humans themselves disagree on is promising to overfit one grader. D notes a real gap but ignores the far larger defect that the headline accuracy figure is meaningless.
9. Where to Go Next
You have now audited a full end-to-end system across every domain the exam weights. Two things remain before you sit CCAR-P. Take the practice exam under time (63 items, 120 minutes) and treat any miss as a pointer back to the lab that owns it. Keep the quick reference open while you review; it is the condensed form of everything the eleven labs built. When the practice exam feels slow because you keep finding the defect before you finish reading the options, you are ready.
Key Takeaways
- Design (D1): fixed, enumerable steps mean a workflow; an autonomous agent on a known path buys non-determinism and an audit trail you cannot reconstruct: fatal in a regulated process.
- Prompting (D2): caching is a prefix match. A
datetime.now()preamble or a varying document placed before the static corpus invalidates the cache on every request: stable content first, volatile content after the breakpoint. - Integration (D3): delete tools you never need, scope credentials to the task (not the union of all permissions), and use an exact lookup (not dense vectors) when the query is a key like "Article 17(2)".
- Evaluation (D4): track p95 not the mean, calibrate an LLM judge against humans before trusting it, and freeze a held-out set so improvement is not overfitting.
- Governance (D5): put the human gate on the branch whose wrong answer is a regulatory finding; redact PII before the call; bound retention to the residency rule.
- Stakeholder (D6): never promise a live-traffic accuracy figure you cannot measure in real time: commit to a gated process and a benchmark you can evidence.
- Dev Productivity (D7): secrets belong in a secret store, never in
CLAUDE.md, which is committed and loaded into context every run. - The through-line: across all seven domains the correct repair subtracts: an agent, a tool, permissions, a promise, a secret. When two options differ by a capability, the one that removes it is usually right.
- The economics: report cost per resolved task, not per request: it is the number tied to a completed unit of work, and the one the business funds.