# How we brief a coding agent to finish a pull request

Most bad agent output starts as a bad brief. "Add a fetch client" gives the agent a task but no finish line, so it stops when the code looks finished.

We are building Hafen, a data platform for AI agents. The roadmap runs to dozens of pull requests, so each one needs a brief that a coding agent can finish alone. This is the format we use.

The brief

Every PR gets one message with the whole task in it:

[ THE BRIEF ]
Depends on
PRs that must merge first
Scope
What to build
Out of scope
What not to build, so the run doesn't sprawl
Done when
The finish line, as checks someone can run
Stop and ask if
Decisions the agent must not make alone
Test
Only when the PR protects a named invariant

We write the brief just before the PR starts, so it reflects what has already merged. We link to the docs instead of pasting them, because the agent can read the repo.

"Done when" is the part that matters

If a reviewer can't run it or measure it, it isn't a finish line. "The crawler is polite" can't be checked. This can:

hafen-fetch bench urls.txt over a 10k-URL fixture list
reports success rate, p50/p95 latency and cost by tier.

The important detail is where the evidence comes from. Our politeness check doesn't ask the fetch client how it behaved. It reads the access log of the host being fetched.

That check found a real bug. The rate limiter from the original plan spaced out when requests started. A first request slowed by cold DNS therefore arrived late, and the next one arrived close behind it. The fixture host logged 393 gaps of 0.963 s against a limit of 1 request per second. You only see that from the receiving side.

[ FIXTURE HOST ACCESS LOG ]
  • 393

    gaps under the limit

  • 0.963 s

    logged gap

  • 1.000 s

    closest gap after the fix

We replaced the limiter so the next request starts a full period after the previous one ends. After that, the log showed no two requests to a host closer than 1.000 s.

Name the stops

An agent that stops too often wastes your day. An agent that never stops can spend your money. So we list the stops and leave everything else to the agent:

  • Money above a set budget
  • Creating accounts, keys or production secrets
  • Infrastructure outside the repo, such as DNS
  • Licence or terms surprises
  • Design choices the docs don't settle
  • Anything destructive
  • Publishing anything public

"Stop if you're unsure" doesn't work, because it gives the agent no way to tell a real decision from ordinary uncertainty.

End every run with three headings

Each run finishes with the same report:

[ END OF RUN ]
Blocked on me
What needs us. We read this first
Changed
What the run changed
Found
Anything discovered outside its scope

The agent must also mark anything it couldn't confirm and say where it looked.

That last rule protects the reviewer. A confident summary of unverified work is worse than a short list of what still needs checking.

Don't test what you only imagined

Agents happily write tests that restate the code. Our testing policy says a test must name the failure it would catch, and a mock isn't evidence when it only returns what the test told it to return.

We also check the tests themselves. In the fetch PR, removing the loopback range or the per-host wait made the matching test fail. A test that still passes after you remove the feature protects nothing.

Review with a different model

The reviewer runs on a different model from the one that wrote the PR, and it gets one instruction: list only the findings that would block the merge, with the file, the line and how to show it fails. Without that limit, reviews fill up with style notes and the real problems get lost.

What this doesn't solve

We agreed the larger version of this on 27 October: one conductor thread runs up to five PR threads and checks them every two hours. It's still a new process, so treat that part as a design and not as a result.

The brief is the part we've actually been running. Writing a finish line someone else can check is also the same skill you need when an agent goes into production for a customer.