Frank Mogensen

For founders · 1 September 2026 · 9 min

Agent proposes, code disposes

A capable model can write your software. It cannot be accountable for it. Here is what I built instead, and the one line the whole design turns on.

I gave a very capable model a repository and a task. It worked for about forty minutes and produced something that ran, and passed its own tests, and looked like the thing I had asked for.

I could not tell you which part of it I trusted.

Not because the code was bad. Because there was nothing to point at. No step I could name as the one that failed. No moment where something was checked and found acceptable. No way to run it again and get the same result. I had forty minutes of transcript and a working directory, and the only honest thing I could say about the outcome was that the agent had stopped talking.

That is the problem I have spent this year on. It is not a model problem, and it will not be solved by a better model.

Two arrangements side by side. On the left, a single agent owning its own loop, with no phase boundary and no acceptance criterion. On the right, the chain owns the loop: four bounded phase kinds — agent, code, panel and human — each followed by a gate, with a failed gate re-prompting the same session as a correction.
Who owns the loop is the whole argument.

The machine with no seams

Hand a capable model your entire development lifecycle and you get something with no joints in it. Four consequences follow, and every one of them is a governance problem before it is an engineering problem.

You cannot say which step failed. There are no steps. There is one long act of work, and when the result is wrong the only available diagnosis is the agent got it wrong, which tells you nothing about what to change.

Done means the agent stopped talking. No acceptance criterion was named in advance, so none was checked at the end. The model's own summary is the only evidence that anything succeeded, and it is the least independent witness available.

A retry is a cold start. Something goes wrong at minute thirty-five, and the standard remedy is to run the whole thing again from nothing — throwing away everything the model had just learned about the codebase, and paying for it a second time.

The only record is a transcript. You read it like a novel. It is not queryable, not comparable between runs, and not something you could put in front of an auditor, a board, or a customer who has asked a reasonable question about how their software was made.

And the one that ends the conversation: run it twice on the same task and you get two different systems.

One line, and everything falls out of it

The fix is not a better prompt. It is a decision, made deliberately and then enforced:

Code owns sequencing, retries and acceptance. A model owns only the work inside one bounded phase.

Everything else in the design is a consequence of that sentence.

The phase becomes the unit of the trace, because it is now the smallest thing with a beginning and an end. The envelope becomes the only way context crosses a seam, because there is now a seam. The gate becomes the definition of done, because acceptance has been moved out of the model's hands and into code that runs after the fact. And a correction becomes cheaper than a restart, because the session is still alive when the check comes back red.

There is a corollary that gets skipped, and it is where most of the money goes:

If you can write the invocation down, it is not a model call.

bun test is not a judgement call. It has one correct way to run and no interpretation. So it runs as a code step, and its result reaches the next agent through the same envelope a model's report would have used — through the same door, in the same shape. The consumer cannot tell which produced it. You have simply stopped paying a model to do arithmetic.

A chain is a file

One chain running left to right on a deterministic rail: seven phases — spec, plan and build as agent phases, test as a code phase, review and document as agent phases, then commit as the code phase that stamps the run identifier into the commit trailer — each with a gate beneath it, and every phase dropping events into a tracer that writes them to a database the interface reads.
Seven phases, seven gates, one trace. The picture is generated from the file, so it cannot drift from it.

This is the part people find strange, and it is the part that matters most. The sequence is not something the model decides. It is a file:

export default chain('prompt', [
  agent('answer', {
    role: 'scout',
    description: 'answer the request from what is actually in the repository',
    gates: ['artifactsExist'],
  }),
  code('confirm', {
    run: 'test -s "{{handoffDir}}/answer.md"',
    description: 'confirm the answer file exists and is not empty, with our own command rather than the agent\'s word',
    gates: ['testsPass'],
    retries: 0,
  }),
])

That runs today. Note what the second phase is doing: it does not ask the agent whether it wrote the file. It looks.

The chain is deliberately thin. It declares the graph and nothing else, so it fits on one screen and a reviewer can see a missing phase at a glance. There is nowhere to put a condition, because a condition is where the sequence starts becoming an opinion. Four kinds of phase exist: one calls a model, one runs a command, one fans out to a panel, and one stops the run and asks a human.

Every phase must carry a description of its intent, and a blank one — or one that merely restates the phase's own name — is rejected when the file is loaded, before a single model call is paid for. A mistyped gate name is caught in the same pass. Not at phase five, forty minutes and several euros in. At import.

Success is earned, not reported

One shape crosses every seam, whoever produced it:

type Envelope = {
  status: 'success' | 'fail'
  summary: string
  artifacts: string[]
  notesForNextAgent: string
}

A phase defaults to fail. It does not report success; it earns it. An envelope that parses perfectly and says fail still fails the phase — which sounds obvious until you notice how many agent systems treat well-formed output as evidence of good work.

Gates verify claims, never predictions. Nobody knows which files a model will touch before it finishes, so a gate runs after the fact, against what the envelope actually declared. Nine of them ship. Each returns one result per thing it looked at, so a green gate does not just say pass — it says what it checked.

And when a gate comes back red, nothing restarts. The runner re-prompts the same live session, naming exactly what was wrong. A cold restart throws away everything the model has just learned about the codebase; a correction costs one message. This single choice is the difference between a system that is expensive to run and one that is not.

Judgement is a quorum, not a voice

A plan is reviewed before the build, by a roster, not after it by one reviewer.

Advisors are dispatched in a single batch so that no member sees another's answer first. Each returns a position with its evidence. A majority of responders decides — and below a floor of two the phase fails rather than deciding on one voice. A genuine split escalates to a role that acts as the operator's proxy, which rules on the constitution first and preference second, and must say which of the two it used.

If that sounds like a board, that is not an accident. It is the same instrument for the same reason: on questions where reasonable parties disagree, the decision is more trustworthy than any single decider, and the reasoning survives the meeting.

A rule nothing enforces is decoration

Ten directives govern every role. Find the real problem behind the request. Every action worth more than it costs. Secure by design, as a precondition rather than a later pass. Verify every input and output against reality, your own included. On conflict, the lower number wins.

They are prepended to every role before its own instructions, which means every byte is paid for on every call in every phase. That is why they are written short.

But the line I would defend hardest is this one: a directive that is only recited is decoration. Anyone can write a values document for their AI system. The question is what happens when it is violated. So each directive that can be enforced is a check that fails the build — one refuses to let a single operator's setup ship inside an engine meant to be generic; another refuses to let the documentation name a file that does not exist. And the directives that nothing enforces are listed, by name, as directives that nothing enforces.

The factory is allowed to argue with its own constitution. A phase that declares in advance that it intends to amend it may do so — on the run's own branch, with the previous wording preserved, and with the run parked for a human before that branch means anything. It cannot adopt its own argument. That is the design, not a limitation.

What I will not claim

The write boundary is the part I am most often asked about, and the part where it would be easiest to overstate.

A capability list is not a boundary. A shell runs anything; a write tool reaches any path. No permission grant makes this phase changes nothing true. What actually exists is an after-the-fact check: the tree is snapshotted before the call, and anything written outside the phase's declared paths is reverted, with the content preserved so you can see what it tried to do.

It detects and reverts. It does not prevent. The isolation is not complete, and I will not describe it as complete.

I write that down because it is the same discipline as everything above. A system that tells you what it has not verified is worth more than one that implies it has verified everything — and in this field, right now, almost nobody writes that sentence.

Why this is a board's problem

Boards are being asked to approve AI in the delivery of software. The question that reaches them is usually framed as capability: can it write the code? That question is settled, and it was the easy one.

The question that is not settled is the one every board already knows how to ask about every other part of the business. Who decided this. What was checked. What is the record. What happens when it is wrong. Can you do it again and get the same answer.

A model cannot hold any of those. It has no memory of the decision, no independent check on its own output, and no obligation it can be held to. So the accountability has to live somewhere else — in code that owns the sequence, in gates that define done, in a trace that outlives the conversation.

That is the whole idea, and it fits in four words: agent proposes, code disposes.

The interesting work in AI right now is not making the model more capable. It is deciding what the model is not allowed to decide.


I am Frank Mogensen. I have built and sold three technology companies, most recently Cloudeon to Devoteam in 2021. I now build systems like the one above, and I work with boards that need AI experience with a governance spine, and with founders who need a technical co-founder who has been through it. If any of this is your problem too, I would like to hear about it — frank@fmogensen.dk.

More

Everything else, in both streams.