Running agents as a delivery team

Eight defined roles, a written backlog, and seventeen checks that reject work breaking the architecture. Most of what I've learned is about the guardrails, not the prompts.

Most writing about coding agents is about prompting. Almost none of it is about what happens on the fiftieth story, when the agent has no memory of the architectural decision it made on the twelfth.

I’ve been building a consumer product this way for months. Not autocomplete — agents with defined roles, working stories out of a written backlog, opening pull requests that get rejected by CI when they break something. The setup now has eight roles, seventeen enforcement checks (thirteen of them wired into CI), and a backlog of 359 stories, 231 of them done.

Almost everything I’ve learned sits in the checks, not the prompts.

The loop

The delivery loopA story moves from the backlog to an agent, which opens a pull request. Seventeen checks either pass it to merge or send it back to the agent.Backlog359 storiesAgent8 rolesPull requesttestedChecks17 of themMergefails — back to the agent, with the reason
Nothing merges because an agent said it was finished.

Roles, not one long conversation

There are eight agent definitions: product owner, business analyst, tech lead and architect, developer, QA, infrastructure, security reviewer, and a founder role that holds decisions the others aren’t allowed to make.

Each has its own written rules and is entered deliberately. This matters more than it sounds. A single conversation that drifts from “what should we build” to “how should we build it” to “ship it” produces an agent that is optimistic about its own work at exactly the moment it should be sceptical. Separating the roles means the thing reviewing the code is not the thing that just wrote it.

The backlog is the interface

Nothing gets built from a chat message. Stories live in a versioned file with acceptance criteria attached, and a story that isn’t ready doesn’t get picked up — there’s a check for that.

bin/check_ticket_readinessruns before a story is selected

Why this exists

In one autonomous run, five stories marked ready turned out not to be buildable, and every one was discovered only after the loop had selected it, read its spec and started looking at the code. “Ready” had been asserting that a human groomed the story, not that the things it needs exist.

So the check asks three questions of every story before it can be picked up:

  1. Is every dependency it declares actually finished?
  2. Does its description or its acceptance criteria name a story it does not declare?
  3. Does it tell the agent to build on code that does not exist anywhere in the repository?

Advisory until its false-positive rate is understood; then it gates. A heuristic that can be wrong must not be able to stop a run.

The discipline this forces is on me, not the agent. If a story is ambiguous, the correct move is to raise it as a gap rather than let something plausible get built. Most of my bad outcomes came from underspecified stories, not from bad code generation.

The guardrails are a record of every surprise

This is the part I’d tell anyone starting. The seventeen checks weren’t designed up front. Each one exists because something went wrong once, and I decided a written instruction wasn’t enough to stop it happening again.

They fall into four groups:

Architecture. A module may not reach into another module’s tables. The presentation layers may not reference each other. The boundaries are enforced at build time, so the architecture is a fact rather than an agreement.

Discipline the agent will otherwise quietly skip. No sleep in tests. No bare constants in specs. No story shipping with empty acceptance tests. Every one of these is a shortcut an agent takes when it is trying to make a suite go green, and none of them is visible in a diff review.

Provenance. Branch provenance, detached-HEAD detection, migration versions, schema provenance. Agents are startlingly good at producing a change that works and is impossible to trace afterwards.

Drift. Design tokens — no raw colour outside the tokens file. Module READMEs going stale against the code they describe. Documentation that quietly stops being true is worse than no documentation, because the next agent reads it and believes it.

The pattern underneath all four: an instruction is a hope, a failing build is a fact. Anything you would have to remember, the agent will eventually not remember either.

bin/check_*seventeen scripts, by name
  • module_boundaries
  • protected_files
  • design_tokens
  • migration_versions
  • branch_provenance
  • no_sleep
  • no_bare_constants_in_specs
  • spec_directory_coverage
  • i18n_hardcoded_copy
  • i18n_date_time_usage
  • module_readme_freshness
  • backlog_test_coverage
  • os_conformance
  • schema_provenance
  • detached_head
  • product_boundary
  • ticket_readiness

■ runs in CI  ·  the rest run locally, before a commit or a pick-up

What this does not fix

It doesn’t make product decisions. The proportion of my time spent on “what should this do” versus “how do I build it” has moved dramatically toward the former, and that work has not gotten easier — it’s just now the whole job.

It doesn’t help with taste. The agent will happily build a well-tested, cleanly bounded, correctly provenanced version of the wrong feature.

And it isn’t free. Seventeen checks is real infrastructure with its own maintenance cost. I think it pays for itself somewhere around the point a codebase outgrows what one person can hold in their head — which, with agents, arrives much sooner than it used to.

What I’d take to a team

The transferable part isn’t the tooling. It’s the idea that when work is produced faster than it can be reviewed, review has to become mechanical to survive. That’s true of a team of agents. I suspect it’s about to be true of a lot of engineering teams.

In the index underagentsguardrailsprocessleadership

Revised