Lionshead

I handed my backlog to an agent fleet for ten days. Three of its safeguards were lying to me.

I have a launch with a date that does not move. I also have a job between eight and five, a two year old, and a four month old. Everything I build, I build around those. So the binding constraint is not taste or architecture. It is how many stories I can carry from "ready" to "merged" in the hours that are left, which for the next couple of months is most of the hours I am not at work or holding a baby.

The obvious lever is to stop being in the loop for every story. Ten days ago I wired up a fleet that pulls from my story queue, builds each story, reviews it, and merges it. Then I pointed it at the product with the deadline and let it run.

It worked, in the sense that 503 commits landed across 168 pull requests in the ten days that followed.

I should say plainly what that means, because it is the fact most likely to be misread. I did not write any of them. Not one line of that product's code has been hand-written or hand-edited by me in months. I instruct, and I review. Claude writes. Every commit below is an agent's work, including the three failures.

It also lied to me three times, in three different ways, and not one of the lies announced itself. This is a post about the three. I have written two posts in a row about the gap between a check existing and a check running: green is not evidence, then make it fail on purpose. I did not expect to find the same disease in a system I built with that lesson still fresh.

The shape of the machine

Two roles, deliberately unequal.

The planner is the expensive one, and the only part allowed to hold the whole graph: what is in the backlog, what the written strategy says matters, what depends on what, which stories produce something another one consumes. It fills the ready queue itself rather than waiting for me to hand-pick the next card, because a machine that needs me to curate its input every cycle has relocated the work rather than removed it. It behaves like a scrum master rather than a filter: when the roadmap needs a story that does not exist, it writes the stub.

The executor is the cheap one, and it is deliberately dumb. It carries stories through create, build, review, merge. It does not re-derive dependencies. It does not resolve ambiguity. Every unclear thing is an escalation rather than a judgment call, either back to the planner or to me. The rule I wrote at the top of its instructions is the only one that really matters: cheap but humble, never cheap but guessing.

Three things about how it spends money, because they are what people ask about and they are the reason it can run unattended at all:

  • It picks the model per step. Spec-writing and mechanical work go to a cheaper tier, implementation to a stronger one. The code reviewer is pinned to the top tier and nothing is permitted to cheapen it, ever, because a cheap reviewer defeats the only reason any of this is allowed to merge without me.
  • It watches its own spend. My usage telemetry exports to Grafana, the fleet reads it back, and it compares this week's burn against a target line. When it is running hot it cuts its own concurrency first, before it touches anything else. It throttles itself so that I do not have to notice.
  • It works the gaps between stories, not just the stories. Every story declares what it produces and what it consumes, and the fleet checks that anything being consumed has a producer that already landed.

I am going to write the full build-out as its own post, because the mechanism deserves more room than this one can give it. What matters here is that third bullet, because that is precisely where the third lie turns out to live.

Around that sit the safeguards, and the safeguards are the whole reason I was willing to let it merge anything without reading it first. Some of them police what the fleet is allowed to start:

  • A scope gate, so it only builds work inside the milestone I am currently shipping: the card that serves the launch, not the most interesting one.
  • A cap on work in flight, so a bad pattern can only contaminate a few stories before I see it.
  • Dependency gating, so a story does not start until the things it depends on have landed.

Others police what it is allowed to finish:

  • No self-review. The thing that reviews the code is a different instance than the thing that wrote it. Non-negotiable, and the reason review is a separate spawn rather than a continuation.
  • A live design review on any story that touches a real user-facing surface. A reviewer drives the running preview deployment, looks at the rendered result in light and dark, checks the empty and error states, and returns a verdict with evidence.
  • A human-eyes lane. Anything touching auth, billing, money movement, schema migrations, or the merge gates themselves gets built and driven to green automatically and then parked, unmerged, for me to click.

And one polices the whole run: the budget throttle above.

That is the design. Three of those safeguards were lying, and a safeguard lies by reporting a protection it is not providing.

What I am not going to claim

I went and counted, instead of trusting my memory.

The last full week before the fleet existed: 403 commits. The first full week after it: 401. Flat. If you were hoping for a throughput multiplier, it is not in this data, and I am not going to manufacture one out of a chart that does not show it.

One thing makes that flat line more interesting than it looks. I had been out of work for four months. My first day back at an eight-to-five was July 29th, and the fleet landed on the 30th. So the 403 week was a week when this product was my full-time job, and the 401 week was the first week it emphatically was not.

That is the honest claim. Not that the fleet made me faster. That it held my pace steady through losing most of my waking day to something else, which for the next couple of months is the only result I actually need.

What changed underneath the flat line is what a single unit of work costs me.

Before, every story meant opening three sessions by hand: one to write the spec, one to implement it, one to review it. Dozens of times a week. The cost was never the typing. It was that each one required me to stop what I was doing, decide it was time, and start something. That is a tax on attention, and attention is the resource I am actually short of.

The fleet instantiates all three the moment they are due, in order, without asking. I read outcomes instead of driving steps. The same output for a fraction of my attention is a trade I will take every single time, and it is the honest version of what happened here. It is also not the version that fits in a headline, which is exactly why I want it stated before the interesting part.

Lie one: the instructions the fleet was running were not the instructions I had written

The fleet's behavior lives in instruction files. Those files live in a company module that gets distributed to every product repo, and the copy the agent runtime actually loads is generated separately, into a different directory.

Except it was not generated. Nothing generated it. The copies the fleet was really loading had been made by hand months earlier, and they had drifted thousands of bytes behind the source I kept carefully editing.

So a real batch ran against an executor missing two of the safeguards I listed above as though they were operating: the scope gate and the live design review. Neither fired, because neither was in the file it was reading. The planner's copy was worse, missing four of its sections and its entire milestone model.

This is a known hazard in the underlying tooling, not a clever novel bug of mine. The same class of gap has been reported upstream more than once: skills installed into a custom module's own directory without the registration that makes them loadable (BMAD-METHOD issue #2107), and custom-module agents installed without the commands that make them invocable (issue #1131). Both are closed now, so I am not pointing at an open ticket and calling it my excuse. I am pointing at prior art: this is a well-worn hole, and my module fell in it.

What kept me in the hole was not the tooling. The instruction file's own header stated, in confident prose, that the installer generated that copy "exactly as it does" for the built-in modules. That sentence sat at the top of the file for months. It was wrong. A document asserting that a mechanism is wired, sitting inside the mechanism it is wrong about, is the purest version of the failure I wrote up two weeks ago.

Lie two: the gate that should have caught lie one read silence as approval

Two stories touching user-facing surfaces merged with no design review at all. Not a review that passed when it should have failed. No review.

That is the same incident as lie one, viewed from the other side. The drift explains why the reviewer never ran. It does not explain why nothing noticed. The gate looked for a rejection, found none, and let the story through, which means a missing reviewer and a clean pass produced identical output.

Two independent bugs stacked on one control: the reviewer was absent, and the gate was built to be silent about absence. Either one alone is survivable. Together they are indistinguishable from working. A gate that only blocks when it produces a rejection is not a gate, because the most common way for a gate to fail is not to run at all.

Underneath both sits a third temptation, and it is the one I would warn you about most: letting the code reviewer "also cover the UX." It cannot. A code reviewer reads class names and token references off a diff. A broken layout, an unreadable dark mode, an empty state that renders as a blank rectangle: none of those are visible in a diff. Folding a check into a reviewer that structurally cannot perform it is not a cheaper version of the check. It is the absence of the check, wearing its name.

The rule now is one line, and it is the rule from two posts ago in a new costume. Exactly two artifacts satisfy that gate: a recorded pass with live evidence attached, or my explicit written waiver. Anything else, silence very much included, is a block.

Lie three: a merge that made another story's premise false, with nothing to notice it

The first two came down to a control that was missing, and a gate that could not tell it was missing. This one was there, ran exactly as designed, and reported a clean board anyway. It is the one I find genuinely hard, and the one I would not have predicted.

The fleet watches the seams between stories by checking dependencies. When a story merges, it asks what that merge unblocked. That is a set-membership question and it is cheap, which is why a mechanical executor is allowed to do it.

But a merge can break another story without touching a single dependency edge. A design document is full of statements about the world: this thing is not wired up yet, nothing reads this yet, this path is inert. Every one of those was true when it was written. A merge can turn any of them false while the story that rests on it is still being built, and nothing in the dependency graph moves at all.

That happened. One story merged and quietly falsified a premise that another in-flight story's design rested on. The dependency check saw a clean board and kept pulling. A human caught it by reading, which is not a mechanism I can schedule.

The fix is embarrassingly small, which is the good kind of small. After a merge, take the names of the surfaces that the merge touched, and grep the in-flight branches' design docs and changelogs for those names. Do not evaluate whether the claim still holds in spirit, because that is judgment and the cheap executor is not allowed to have any. Report that an in-flight document names something this merge just changed, and let the planner decide. One command, and it would have caught it.

What is not built

I have ended the last few of these with an honest list of what I have not finished, because a half-built system that thinks it is finished is worse than one that knows it is halfway. Here is the list for this one.

The design calls for a retrospective pass after each batch: a set of specialist lenses that look at ten merged stories together and catch the drift that no single story's review could possibly see, because the defect only exists in the space between them. Three stories that each independently invented a slightly different pattern for one job, and each passed review on its own terms. A feature that is now complete and undocumented because no single story owned the seam. That review is written down, it is accepted, and it does not exist. Only the planner and the executor are real.

Which means the compounding half of this system, the half where what batch ten teaches gets fed back so batch eleven starts from a higher floor, is currently a document. The throughput half is running. The learning half is a plan.

There is one detail I cannot leave out. That unbuilt retrospective specifies six lenses, and one of them is a documentation lens whose stated job includes mining these posts out of what a run just taught. This post is that lens's output, produced by hand, because the thing that was supposed to produce it has not been built. I am not sure whether that is funny or just accurate.

What it costs

The fleet spends my weekly model allowance, which is why the throttle exists and why it cuts parallelism before it does anything else. What it may never do is cheapen the reviewer.

The real cost is subtler. Every safeguard I listed is a claim about a system I could not watch directly, and I was making those claims to myself, in documentation, and believing them. Three of them were reporting a protection they were not providing, and I did not learn that from an alert. I learned it from reading, from a batch behaving oddly, and in one case from a person noticing something no check was positioned to notice.

The part I did not expect

The honest summary of ten days is not "agents built my product." It is that agents built my product, and the part of the system I trusted most, the safeguards, was the part with the highest rate of quiet failure.

Which follows, if you sit with it. The feature code announces its failures, because a broken feature is visible the moment someone uses it. The safeguards are the only components whose success looks exactly like doing nothing, so they are the only ones that can fail for months in complete silence. I have now found that same shape in a Vale rule, in a CI check, and in an agent fleet, and I no longer think it is a coincidence about any of those three things. It is a property of anything whose job is to sit quietly and not fire.

What's next

The unbuilt half. I am going to build the retrospective pass and run it against a real batch. Then I will write up whether specialist lenses reading ten merged stories at once find the drift the design promises, or whether that is a fourth thing I believed because I wrote it down.

In this series: Building Lionshead

  • building-lionshead
  • ai-agents
  • process

Get new notes by email

Occasional posts on building Lionshead. No spam, unsubscribe anytime.