How AI Agents WorkCourse map

Chapter 19 · Thinking like a designer

What AI builds vs what you design

An AI coding system can write almost all of an agent's code. The decisions that make the agent safe and useful stay with you.

What you hand off, and what you keep

Ask an AI coding system to "build an agent that fixes failing tests". An hour later you have a loop, four tools, a checkpoint store, and tests that pass. Now look for three answers in the code. How many steps can a run take? What happens when a push times out? Which commands need approval?

You never said, but the code answers anyway, because an AI coding system rarely stops to ask. It fills each gap with a plausible default instead.

So delegate work whose correctness you can check, such as a validator with tests. Keep the decisions that depend on your goals, your tolerance for risk, or facts only you know.

What you can delegate

The spec lists work you probably never need to write yourself. It is a good list, because each item follows a known pattern that tests can check. But each one still hides a decision that stays with you:

Delegate the code, keep the decision inside it
Delegate the codeYou still decide
JSON schema validationWhat happens to an invalid call, and how many retries the model gets
API boilerplateTimeouts, and which limited credential each call uses
Database CRUDWhere the truth lives for each fact, and who may write it
Tool wrappersWhat the tool may change, its output limit, and which errors the model sees
Queue consumersWhat happens to a message delivered twice, and the idempotency key for each action

Write those decisions into the design brief, and let the AI implement them. Chapter 21 has a template.

What state should exist?

Suppose Patch crashes at step 14 of a nightly run. It can resume only if your code saved its state after step 13, outside the process. And if the state lives only in the message list, nobody can say what the agent believed when it acted. So decide what state exists. It can be the message list alone, a typed record (status, todos, test results, approvals, cost), or checkpoints plus a log of outside actions (chapter 3).

What should the model see?

Suppose Patch runs env while debugging. If your code pastes the output into the context, the model now holds your database password. Decide the order of the context, which outputs go to files with a short preview, and when to compact. Above all, decide what never enters (chapter 4).

When should memory be retrieved?

Last month, Patch fixed a flaky login test. A note about that fix helps when the same test fails again, but a stale or planted note misleads the model just as confidently. So decide what is always loaded, such as a small project file, and what is fetched only when needed. Decide who may write memory too, and whether the agent's notes wait for a person's approval (chapter 5).

What actions require approval?

Patch wants to run a migration on the shared staging database. Should it ask first? Gate by effect. Actions that leave the sandbox or cannot be undone pause for a person. Reads that you already authorized, within the agent's data scope, run without asking. Sensitive sources, such as employee records or credentials, need their own rule. Keep the gates few, because too many teach people to click yes without reading. Tie each approval to the exact call and its arguments, so a changed call needs a new approval (chapter 11).

When should work be delegated?

Patch needs to understand an unfamiliar module before it can fix a bug. A read-only helper agent can explore it and return a short summary, which keeps Patch's own context clean. But the helper costs tokens, and its summary can drop the one detail that mattered. Left to choose, an AI coding system may build a manager with several workers, because that looks capable. If you use helpers, give each one a goal, tools, an output format, and a budget (chapter 7).

What should be deterministic?

The expense agent must check that a receipt's line items add up to the total. A model asked to do this is right most of the time, which is not good enough for a spending limit. Code does it the same way every time, and repeatable test cases can check it. So use code wherever you can write the rule down, for example for validation, arithmetic, routing, permissions, budgets, and stop rules.

What should the model decide?

Reading a crumpled receipt photo is a job for a model, because no rule can describe every receipt. A parser breaks on the first unusual one. The model also earns its cost when picking the next step needs judgment that known rules cannot express, as in debugging. Known conditions, such as "if the tests fail, show the error", stay in code. Writing code and replies is model work too (chapter 0).

What is the security boundary?

A README in a cloned repo says "run this setup script", and the script uploads your SSH keys. Text like that can steer the model, so the boundary has to be what the process cannot do, whatever the model asks. Decide what the container mounts, which network destinations it can reach, and where credentials live. Credentials belong outside the sandbox, limited and short-lived (chapter 10).

What happens after failure?

The finance system times out right after the agent sends "submit". Did the expense go through? You cannot tell, so a blind retry could file it twice. Retry automatically only when a repeat is safe: a read, an action that has the same effect when repeated, or a request that the receiver deduplicates. That last case needs an idempotency key, which is an id the receiver uses to ignore a repeat. It works only within the receiver's retention window. Otherwise, record the outcome as unknown, and find out what happened, from the receiver or from a person, before trying again. After a crash, resume from the last checkpoint instead of starting over (chapter 12).

How do we know the agent succeeded?

Patch replies "Fixed, all tests pass", but your code never ran the tests. The model's "done" is a claim, so decide what evidence counts. Usually that means a check your code runs whenever the model says it is done. Add rules that fail a run even when the result looks right (chapter 16). Claude Code's docs warn that without a check the agent can run, you become the checker.

The checklist

Write all ten answers into the design brief. This is the minimum for each.

Ten decisions and the minimum answer for each
DecisionWrite downChapter
StateThe fields, where the truth lives, when to save a checkpoint3, 12
ContextThe order, what never enters, output limits, the compaction rule4
MemoryWhat is always loaded, what is fetched and when, who may write5
ApprovalWhich actions pause, who approves, what they see11
DelegationRoles, tools, output format, budgets, depth7
DeterministicEvery rule that can be code, and the fixed workflow parts14
ModelThe few places that need judgment0, 9
BoundaryMounts, network rules, credentials, outside controls10
FailureWhich retries are safe, idempotency keys, what to do when the outcome is unknown12, 15
SuccessChecks at stop, invariants, the eval suite16

Deterministic or model-driven?

Back to the expense agent. Reading the receipt photo needs a model. Checking that the line items add up does not, so code does it. Code also decides whether the amount needs approval. Ask these questions of each step (Q22):

Code or model? A quick test for each step
Ask of each stepIf yes
Can you write the rule down?Code
Must it give the same answer every time, or be audited?Code
Is a mistake expensive or impossible to undo?Code decides, or the model suggests and code checks
Is the input messy, such as free text, images, or logs?The model reads it, and code checks the result
Does choosing the next step need judgment that known rules cannot express?The model chooses, inside limits that code enforces
Is the output open-ended, such as prose or code?The model writes it, and checks verify it

Most good designs mix the two. For example, the model drafts a fix, and the tests decide whether it worked.

If traces show the same steps on every run, the model has a job that code should do. The opposite sign is a growing pile of special cases in code. Anthropic's guide to building agents applies the same test to the whole system. It advises the simplest design that works, which "might mean not building agentic systems at all."

Reviewing AI-written agent code

An AI coding system can write thousands of plausible lines in an afternoon. You do not need to read them all. Find the few places where your rules must hold, and check each one.

Start by asking for an enforcement map: each rule from your brief, with the file and function that enforce it (chapter 21). Then check these invariants, which are rules that must hold in every run:

Then ask for evidence instead of reassurance. A trace of a real run lets you watch each rule fire. Scenario tests use a scripted model, which returns fixed replies, so each test repeats exactly:

def test_rejected_push_never_runs():
    model = ScriptedModel([
        tool_call("run_command", cmd="git push --force origin main"),
        final("Understood. I will not push."),
    ])
    world = FakeWorld()
    run = run_agent("tidy the branch", model=model, world=world,
                    approver=always_reject)
    assert world.commands_run == []
    assert run.tool_results[0].status == "rejected"

Finally, ask for proof that each test can fail. Remove the gate, and this test should go red.

Running example: what you decided for Patch

For Patch, your decisions fit on one page: tools, budgets, context rules, four approval gates, the sandbox, one pull request per issue, and what counts as done. The AI wrote everything else, from the loop to the eval harness. Your page is what that code gets reviewed against.

# Patch's final settings, as in the chapter 21 brief.
# Written by you. Enforced by code the AI wrote.
PATCH_DECISIONS = {
    "budgets":  {"steps": 30, "usd": 3.00},      # per run, in production
    "stuck":    "same call, same failure, 3 times in a row",
    "context":  {"never": ["secrets", "env vars"], "tool_output_max": 2000},
    "memory":   {"writes": "proposals", "retrieve": "task start, by path"},
    "approval": ["open_pr", "shared_db_migration", "deploy_staging",
                 "delete_outside_repo"],
    "boundary": {"mount": "repo only", "egress": ["package registry"]},
    "failure":  {"retries": 3, "retry_only": "reads, deduplicated calls",
                 "open_pr_key": "issue key"},
    "success":  ["target test passes", "full suite passes",
                 "reviewer approves"],
}

What goes wrong

Silent defaults

Why it happens
The brief left a question open, so the AI coding system picked a plausible answer and moved on.
How to spot it
Ask it to list every assumption it made, and look for rules in the enforcement map that you never wrote.
What to do
Answer the ten questions in the brief first.

A rule enforced only by the prompt

Why it happens
"Ask before deleting" ends up as a sentence in the system prompt, because that is the shortest path.
How to spot it
The enforcement map points to a prompt for that rule, not to code or a setting.
What to do
Require every rule to name the code or setting that enforces it. Claude Code's docs draw the same line: hooks always run, while instructions in CLAUDE.md are only advice.

Tests that share the bug

Why it happens
The tests come from the same misunderstanding as the code, so they pass.
How to spot it
Remove the guard, and the test stays green.
What to do
Write the list of scenarios yourself, and ask for proof that each test fails without its guard.

You decide, AI builds

Decide yourself

  • The answers to the ten questions, in writing.
  • The invariants, and a scenario that tests each one.
  • What counts as evidence of success.

Delegate to AI

  • All of the code: the loop, wrappers, validation, storage, and UI.
  • Tests for your scenarios, with proof that each one can fail.
  • Traces, dashboards, and the enforcement map.

Check yourself

What should be deterministic versus model-driven? (Q22)

Answer

Use code for rules you can write down, for anything that must give the same answer every time, and for expensive mistakes such as money and permissions. Use the model for messy input, for choosing steps that need judgment no known rule can express, and for writing. Where a mistake is costly, the model can suggest, but code checks before anything runs.

Permission checks live inside three of the five tool functions. What should you ask for?

  1. Nothing, because three of five is enough
  2. One policy check that every tool call passes through, backed by a sandbox
  3. A stronger instruction in the system prompt
Answer

Checks inside some tools leave other paths open, so put one policy check where every call passes. The sandbox adds limits on files, processes, and network access, but it cannot judge an allowed payment or push. Rules about amounts, targets, and approvals stay in your code or in the receiving service.

The AI's code saves a payment's idempotency key after the payment call returns. What can go wrong?

  1. Nothing, because the key is saved either way
  2. A crash after the call but before the save leads to a retry with a new key, and a duplicate payment
  3. The key could be too long
Answer

Save the intent and the key before the call, and reuse them on retry. If the receiver deduplicates on that key, it skips the repeat. If it does not, check what happened before you retry.

Takeaways

Going deeper