Plan, build, review

The course delivery loop is plan mode, an isolated worktree, a focused change, automated checks, and review by someone else.

How to Code Agentically

Coding used to be expensive, so we planned carefully to avoid wasting engineering time. With coding agents, code is cheap and fast but unreliable, so human review becomes the bottleneck. Most failed agent projects began with an unquestioned plan. (Source: Brendan Graetz, “Hard Parts: Building with Gen AI”.)

The bottleneck shifts to problem scoping, planning, verification, and validation.

  • Scoping: What should I build?
  • Planning: How should I build it?
  • Verification: Did I build it right?
  • Validation: Did I build the right thing?

The developer’s leverage sits in choosing the problem, shaping the plan, constraining the work, and judging the evidence.

Reliable-use habits

  • Describe the outcome and constraints before asking for implementation.
  • When requirements are fuzzy, have Claude interview you first: Ask me questions, 3 at a time, until you are 90% sure about what to build.
  • Ask the agent to plan the user behaviour before planning the code change.
  • Ask the agent to inspect the repository and propose a plan before editing.
  • Treat generated code as untrusted until tests, review, and direct inspection support it.
  • Keep changes small enough to explain, verify, and reverse.
  • Put repeatable rules in repository instructions or deterministic checks instead of repeating them in chat.
  • Preserve human ownership of requirements, risk decisions, and acceptance.

Claude Code features

We introduce Claude Code features as we go. They release very quickly - ostensibly because Claude Code makes Claude Code - so keep an eye on the commands. If you think of a feature we missed, let us know - and when you use one, post it on the Padlet.

Here are some features we will be using today:

  • Use plan mode to inspect the repository and challenge an underspecified request.
  • Put durable repository conventions in CLAUDE.md.
  • Use a worktree so concurrent agent work does not share uncommitted files.
  • Enforce repeatable rules with hooks: deterministic checks that run after every edit.
  • Trust the undo: /rewind steps back to any snapshot Claude Code took as it worked.
  • Ask @claude for adversarial review, then decide which findings are supported by evidence.
  • Publish a plan or report as an artifact: a live claude.ai page that updates in place as you work and shares to the class.

Comic: git worktree lets you check out two branches at once, sharing one .git directory

Comic by Julia Evans, from wizardzines.com.

Blindspot pass

Your prompts are a map; the codebase is the territory, and projects fail in the gap between the two: your unknowns, especially the unknown unknowns. Before planning anything, give Claude this, modified freely:

I am building a dispatch agent for the HADR game: a partially observable fire-dispatch simulator played through typed tools. I have never worked with this system. Do a blindspot pass: find my unknown unknowns about this problem, and explain them so I can prompt you better.

Post your most surprising unknown to the Padlet. (Source: Thariq, “A Field Guide to Fable: Finding Your Unknowns”, with worked examples)

Spoiler: one unknown your blindspot pass should surface

The stores treat reports and incidents as separate things, yet nothing specifies how a report maps to an incident. That underspecification is deliberate: the location and time-window find functions hint at the shape of an answer, and you will design the actual matching rules on Day 2.

Product thinking: what problem should the agent solve?

You know the setting: uncertain reports and too few fire trucks. Now decide who you are helping and which decision you want to improve. Do this before you choose fields, prompts, or architecture.

Open Claude on the web or use plan mode. Begin with:

Help our team decide which operator problem this HADR agent should solve first. Interview us one question at a time. Do not propose an architecture or write code yet.

Stay in the conversation for at least eight questions. Claude should help your team examine:

  • who uses or depends on the system;
  • which decisions are difficult today;
  • what information arrives late or cannot be trusted;
  • the consequences of a false alarm, a missed incident, or a late dispatch;
  • what the agent may decide by itself;
  • what should remain visible to, or controlled by, a person;
  • how the operator would know that the system helped; and
  • what the first useful version should do.

Then ask Claude to turn the discussion into a one-page HADR product brief:

Include the primary user, their problem, the decision the system supports, the most consequential failure, the human-agent boundary, success measures, the first useful slice, and explicit non-goals. Do not propose implementation details. End by asking us what we would cut.

Review the brief as a team and cut anything you cannot defend. Save the agreed version as product-brief.md in your repository, then have Claude publish it as an Artifact. Keep implementation details out for now. Use the brief to record the problem and the product decisions your implementation must support.

Activity

We’re going to begin by cloning the starter code and building the agent’s memory layer in pure code. Your agent needs to remember two different kinds of thing:

  1. Reports are contacts from the public. This is append-only evidence (add, get, find) your engine will receive.
  2. Incidents are your agents’ mutable beliefs (adds: update, link, close).

A minimal starting schema ships in the starter: the Report and Incident dataclasses in src/hadr_agent/stores/reports.py and stores/incidents.py. It is the smallest thing that stores evidence and beliefs. Severity, headcount, contradiction flags, channel trust, and - above all - how a report maps to an incident are deliberately absent. Those you discover as scenarios force your hand.

We’re going to build this following best practices for an agentic workflow. Pay attention to the mechanics of what we do.

Cloning and initial setup

Preflight already had you clone the course starter into your own private repository, install it with uv sync, and write your .env. If you skipped it, do it now - everything below happens in that repository.

You are each building a separate agent, but you review each other’s pull requests, so every teammate needs read and review access to everyone else’s repo. And everyone uses their own OpenCode Go key (OPENAI_KEY=sk-...) - runtime model calls bill it, no sharing.

Requirements and Implementation

Because code is cheap and your time is expensive, we’ll do “spec driven development” here.

  1. We’ll turn the product brief into requirements for what the code needs to do.
  2. We’ll get Claude to propose a specification for how to achieve that in code.
  3. We’ll get Claude to implement that specification, and finally
  4. We’ll get Claude to review that specification.

All this is designed to minimize your time working on the code and maximize Claude’s.

1. Requirements

Do this as a team: crowd around one laptop and argue through the requirements. Each of you will ship your own agent, but you should settle disagreements before anyone writes code. Agree on the same scenarios, then commit them to each repository.

Open product-brief.md before you write the scenarios. For each scenario, name the user decision, serious failure, or constraint it protects. If you cannot name one, leave that scenario out of the first version.

Product decision Possible scenario
Avoid acting twice on the same report A duplicate contact does not open another incident
Keep uncertainty visible Conflicting reports preserve the contradiction
Protect life before property An incident with people trapped outranks an empty building
Keep major reversals accountable A redirect records what the agent is giving up

We’ll be using Gherkin for this. It’s a great way to express formal software requirements in a way that’s understandable for your product/project managers and is also directly testable. The starter ships initial scenarios in starter/features/. These are a starting point, not a finished spec: grow them as you discover new cases - add scenarios and edge cases as your understanding deepens.

Once you’ve figured out the fixed requirements:

  1. Push the requirements in formal code up to your own repository.
  2. Have agent publish an artifact describing the design, including a mermaid diagram, and have one of your teammates review this.
uv run behave                # check everything (this will fail)
uv run behave --tags=@warmup # check just this stage

2. Specification

This task is too simple for a full specification, but watch out for the agentic loop and reconciliation - you’ll need to vet the specification there.

Ask Claude to write a spec for how to satisfy your requirements, it will just write you the exact code (for this exercise.)

3. Implementation

Fan out and fill in the functions

Both find functions filter by location and time window. The starter repository supplies the dataclasses, the stubbed signatures, and the requirements: each function’s contract and the acceptance scenarios your PR must satisfy. Plan against those requirements rather than inventing scope; keep each store to a dict plus a JSON file.

Start in plan mode: have Claude propose the implementation and publish the plan as an artifact. Review the page yourself - push back on anything underspecified - then approve the plan and let the build run.

The work splits into four independent units: reports, open and close, get and find, and update and link. Build all four in your own repository, but build them concurrently - one worktree and one agent per unit, one pull request each, each reviewed by a teammate. Running four agents at once is the point of the exercise; you will be doing it all week. To work in an isolated worktree:

git worktree add ../hadr-reports -b reports   # new branch in a sibling folder
cd ../hadr-reports && claude                   # run the agent there
# ...build, commit, push, open the PR...
git worktree remove ../hadr-reports            # clean up after the merge

Each worktree shares the same git history but has its own working files, so concurrent agents never fight over uncommitted changes.

Take the time to get this right, we’ll be using this for your agent’s memory in the agentic loop.

4. Merging and Review

Basic principles are:

  • Nobody merges without a teammate’s approval, even in their own repository.
  • PRs reference the scenario or requirement they satisfy.
  • Generated code is held to the same tests and repository conventions as hand-written code.

Push each unit as a branch, open a pull request against your master, and get a teammate to review it before you merge. Four small PRs reviewed by four different people beats one big PR nobody reads.

In Claude Code, run /install-github-app to connect the repo, then mention @claude in a PR to request a review.

Adversarial PR (Optional)

Once your good PRs are in, open a destructive one against a teammate’s repository that should not merge - fonts to Comic Sans, committed OpenCode key, deleted tests, etc. Run an agent review loop over the pull requests landing in your own repo and see whether it catches what your teammate planted. Claude can catch obvious bugs, but not always.

Agents write code fast; your review process and extensive test suite is the guardrail. We’ll learn more about this in backpressure engineering at the end of the day.

When you’re done

  1. Get all four units merged and confirm the requirements are fulfilled from a fresh pull of your own master:

    uv run behave --tags=@warmup # check just this stage
    uv run pytest                # plumbing + smoke
    
  2. Post what you planted to the Padlet: the destructive pull request you opened against a teammate, and whether @claude caught it. The ones that slip through are the useful data - that is the shape of the review your test suite has to cover instead.