The Two-Stage Rocket Fills the Hole the Thought-Leaders Identified

Stage one, the structure, is a deterministic function of the spec. Stage two, the decisions, is where the agent works.
Four recent interviews with industry thought-leaders, four sharp diagnoses, one undrawn conclusion: the LLM is doing the lion’s share of writing the code, and it should not be.
TL;DR. Gorman, Böckeler, DHH and Horthy each diagnosed a different failure of agentic coding: compounding tokens, markdown sprawl, architecture wrecked one reasonable PR at a time, and no oracle for maintainability. All four follow from one decision: an LLM writes all the code. The two-stage SDD rocket splits that. Stage one, a deterministic source generator, derives the structure (commands, events, errors, dispatch, scenarios as tests) from a small formal spec, inside the compiler, never in the repo, so spec and code cannot drift. Stage two, an agent in a harness, writes only the decisions, against the compiler, the spec’s scenarios and a code-health gate, in a context small enough for a lower tier model. It runs in a public workbench and in the software factory at my day job.
Four voices, one hole
Four podcast episodes and one long essay I took in recently each put a finger on something real about building software with agents.
Jason Gorman, on Software Engineering Radio [1], explained the mechanics of why output gets unreliable. Every token an LLM emits is a probabilistic step. And because attention is spread across every token in the context, the effective limit at which reliability falls off a cliff is orders of magnitude below the advertised window. Put those two together, and the conclusion is that more tokens make it worse: the steps compound, and a bigger context window is more fuel for the same fire, not a fix.
Birgitta Böckeler, also on Software Engineering Radio [2], said out loud what many of us have felt: “I just don’t see the future as being like 50 markdown files in our codebase. I mean that can’t be it, right? … Can we still call ourselves engineers if that’s how we’re doing stuff?” Her distinction is between guides (prose the model interprets) and sensors (checks that fire deterministically), and her instinct is that sensors are underused.
DHH, with Lex Fridman [3], told the story of the Basecamp 5 sprint. The designers were let loose to vibe code, since they knew what features they wanted and what shape they should take. The result was “a lot of PRs that individually perhaps could have been justified for a hot moment, but taken all together, destroyed the architecture of the system”, which then had to be mopped up by hand. The lesson he draws is that to vibe code on an existing, substantial codebase and keep its architecture intact, you have to be a programmer.
Dexter Horthy, in “Why Software Factories Fail” and on the High Leverage podcast [4], went after the factory itself. The agentic software factory, he observes, looks mostly like swapping one box, “someone builds the thing”, for “an agent builds the thing”. Building drops to minutes, review still takes hours, so review is the bottleneck; the lights-off factory answers by dropping review, and it fails. His root cause is the sharpest of the four: models “can’t maintain and improve codebase quality over time”, because “maintainability has no fast oracle”, so nothing in training ever rewarded it. His subtitle says the rest: “harness engineering is not enough”.
Each of the four is right. And each is describing a symptom of the same arrangement: in mainstream agentic development and spec-driven development, an LLM performs the transformation from intent to code, for all of the code, and the resulting code is both version-controlled and the product. Gorman’s compounding tokens, Böckeler’s markdown sprawl, DHH’s PR ceiling and Horthy’s missing oracle are four views of that one design decision. This article is about the conclusion none of the four drew, and about what happened when we built on it.
The factory, before and after the swap
Horthy describes the pre-AI software factory better than anyone I have read, so I will borrow his description with credit. People decide what to build. It goes into a tracker. Someone grabs a ticket and builds it. A pull request gets automated checks and a human review. Anything wrong loops back to the builder. The change ships, monitoring pages someone at three in the morning when it breaks, users complain and ask for things, and the asks go back into the tracker. Several loops, and no AI in sight yet.
He also names the thing teams learned decades ago about that factory: building takes hours or days, and so does review, so alignment is front-loaded. Planning, architecture proposals and sprint planning happen together, before anyone writes code, because a well-aligned PR reviews fast and a misaligned one reviews slowly or twice.
The agentic factory, in his telling, keeps every box on that diagram and swaps one: an agent builds the thing. Building collapses to minutes. Review still takes hours, so review is now the bottleneck. So review gets agents too: agentic code review, agentic regression testing. Still the bottleneck. So incidents and user feedback are routed straight into the factory as well, and the job becomes two questions: how much can you stuff into the queue, and how fast can you review what comes out. The lights-off factory is the last step on that path: a human reads none of the code, and the effort goes into testing, sandboxes, automated review, monitoring and rollout instead.
His verdict is that the lights-off factory does not work, and he has run one. He also runs HumanLayer, which sells tooling for the human-in-the-loop answer he arrives at, and he says so himself. His reason goes below the harness. Models are trained by reinforcement against fast verifiers, and the fast verifier for code is whether the tests pass. “There is no penalty for eroding codebase maintainability.” Tests give a signal in seconds; the cost of bad architecture arrives weeks or months later, the first time someone opens a file for a one-line change and finds they have to make “the same edit in eleven places and hope nothing quietly breaks three files over”. No oracle for that exists that is fast enough to train on, and “if a model could reliably tell good code from bad, it might have written the good version to begin with”. More review agents raise the floor; they do not move the ceiling.
Readers of “The Spec Is the Product” Is a Slogan Until the Code Leaves Your Repo will recognise every piece of that. The eleven-place edit is representational redundancy: in a layered codebase, one domain fact restated across entity, DTO, mapper, schema, validator and test, with no compiler holding the restatements together. And the oracle Horthy says nobody has is the one that never lived between the sites. He arrived at the same hole from the training side that the first article reached from the architecture side.
What mainstream spec-driven development actually ships
Spec-driven development is the same swap with a spec bolted onto the front of the factory.
Böckeler herself wrote the clearest map of the territory, in her comparison of Kiro, Spec Kit and Tessl on martinfowler.com [5]. She sorts spec-driven development into three levels: spec-first (write a spec, then code), spec-anchored (keep the spec alive alongside the code) and spec-as-source (the spec is the primary artifact and the code is generated, never hand-edited). Her spec-as-source is the first article’s third rung, deterministic derivation, minus the word deterministic. That missing word is most of this article.
Against that map, the tools most teams reach for today all sit on the first level.
Kiro produces three markdown files per feature: requirements in EARS notation (“WHEN … THE SYSTEM SHALL …”), a design document and a task list. An agent then implements the tasks. The docs advise storing specs “alongside the code they describe”; keeping the two in agreement is a matter of discipline, not mechanism.
GitHub Spec Kit adds a constitution and a pipeline of slash commands: specify, plan, tasks, implement. Its README says specifications “become executable, directly generating working implementations”. Executable here means an LLM reads them. It also ships a converge command that assesses the codebase against the spec and appends the drift as new tasks: Horthy’s agentic review loop, in spec form. Colin Eberhardt at Scott Logic put Spec Kit through its paces and reported “a sea of markdown documents, long agent run-times and unexpected friction”, including a module contract four times longer than the module it eventually described [6].
OpenSpec keeps requirements as SHALL statements with WHEN/THEN scenarios and archives each change after it is applied. Its headline promise is “plain Markdown, no special syntax to learn”. That is an honest description of the trade: nothing to learn, and nothing a machine can check.
Böckeler’s verdict on the one tool that attempts spec-as-source, Tessl, is the telling detail: it marks its output with // GENERATED FROM SPEC - DO NOT EDIT. Anyone who lived through the CASE tools of the nineties or the MDA generators of the two-thousands recognizes the comment fence: a promise not to edit, with nothing but a comment to keep it. Her list of what remains unsolved is equally telling: non-determinism (identical specs, different code), agents ignoring their instructions, and the risk that spec-as-source repeats Model-Driven Development’s collapse by combining “inflexibility and non-determinism”.
Put gently: this is AI bolted onto the existing way of working. The workflow is the one humans followed, the architecture is the one humans built, the artifacts are the ones humans reviewed, and an LLM has been substituted for the human at each step. The sociotechnical system was not rethought; its operators were replaced.
The two-stage SDD rocket
The alternative I have been writing about, and that is now part of the software factory at my day job, splits the transformation in two.
Stage one is deterministic. A formal spec (a small DSL, event model in our case) is transformed by a function, not by a model. In “The Spec Is the Product” Is a Slogan Until the Code Leaves Your Repo that function was a Roslyn source generator emitting the domain vocabulary, the Given–When–Then scenarios as tests and the decider dispatch, inside the compiler, on every build. The generator was written by an agent too, with a frontier model; what makes it stage one is not who wrote it but that it was reviewed once, its output is a deterministic function of the spec, and no model runs at build time.
Reviewing a generator once has a name and a technique. A generator emits text, and text is exactly what Llewellyn Falco’s approval testing was made for: approve the emitted output once, and from then on every change to the generator or the spec shows up as a diff to be read, not a behaviour to be guessed at. The workbench proves its emitter that way in all but name, round-tripping the emitted surface against the spec it came from with zero divergences allowed, and pins the DSL’s parsed shapes as approved snapshots.
There is a precondition that makes the function writable at all: the DSL maps one to one onto the architecture. A command in the spec is a command record in the core, an event is an event, a scenario is a decider test, with no interpretation in between. That mapping is what lets a generator, rather than a model, carry the lion’s share. The output is never in the repository. Same spec in, same system out, and drift between spec and code is not discouraged but unrepresentable, because the derived representation has no independent existence to drift in.
Stage two is agentic. What stage one cannot derive, the actual decisions (what a command does to state, e.g. whether a manuscript submitted after the window closed is accepted or refused), is written by an agent inside a harness, against the oracles stage one produced. A new event in the spec is a compile error at every site that fails to handle it. A scenario in the spec is a test the agent’s code must pass. The harness itself prefers tools with hard signals over agents with opinions: MCP and CLI tools rather than a review agent imitating a human reviewer. One such tool is CodeScene’s Code Health, and by construction it never sees the generated output, only what the agent wrote. When a residual pass lands below the health threshold, the eval loop records why and codifies the lesson, so the harness improves with use instead of with prompt archaeology. The agent writes the residual, and the residual is small.
Small in a specific way. Vertical slice architecture (VSA) combined with event sourcing and the decider pattern together mean that the unit of work for the agent is one slice, removing the need for shotgun surgery: one command, the event(s) it may produce, the state it reads, and the scenarios that pin it. The agent’s context holds that slice, the generated records it compiles against, and the harness rules, and nothing else, because nothing else is relevant. In our experience that context is small and clear enough that a smaller model, rather than a frontier model, usually does the residual faster without losing quality: the rules are few, the verification is immediate, and there is no eleven-place edit to hold in mind.
One question follows naturally, and it is worth answering before a reader asks it. If the residual is held by the compiler, the scenarios and the health gate, why keep it in git at all; why not regenerate it every build too? Because version control is for decisions, and the residual is exactly the decisions. The structure is a function of the spec, so it has no independent existence and leaves the repository. The residual is where a human or an agent chose what a command does to state, and the scenarios pin only what they assert; every prop and every branch they leave open would vary from build to build if a model regenerated it. Regenerating the residual would put a stochastic transformer back on the build path and spend fresh tokens on every build, which is the arrangement the first stage exists to end. So the contract differs by stratum: equality where the transformer is frozen, conformance where an agent writes against oracles, and only the equality side leaves git.
The rocket metaphor is deliberate. The first stage carries the mass and burns out early; the second stage is light and does the precise work. What the first stage carries is everything in the system that is pure structure: the commands, events and business errors as closed unions (one type per kind, with a fixed set of cases the compiler can check exhaustively), the decider’s dispatch over them, and every Given–When–Then scenario as an executable test. This is where the representational redundancy of the first article goes to die: in a functional core with vertical slices and deciders there is nowhere for those facts to be restated, so the vocabulary exists once, in the spec, and the compiler sees it on every build. The LLM never writes it, and the code-health gate never has to read it. What remains for the second stage is the part that is actually a decision: what this command does to that state.
This is where Horthy and I part ways, and it is worth being precise about where. His answer to the missing oracle is to turn the lights back on: humans in the loop at four phases, product requirements, system architecture, program design and delivery in vertical slices (his term for tracer bullets, a delivery order, not the architecture I mean by the same words), and then read the code. Program design is the phase he calls “criminally underemphasized”: before anyone writes the implementation, humans and models draft the types, the method signatures, the file layout and the call stacks, as pseudocode the agent then follows. Look at that list against stage one. The types, the signatures and the dispatch are exactly what the generator emits. He keeps the shape of the code as a document the model is asked to obey; the rocket makes it an output the model never touches. In Böckeler’s terms, his program design is a guide and stage one is a sensor. And that is why “harness engineering is not enough” is right without being the end of the story: stage one is not harness engineering at all. It is meta-programming, and it takes the model out of most of the flight.
Why the rocket answers all four
Gorman. If every token is a probabilistic step and the steps compound, the lever is not a smarter prompt or a bigger window. It is fewer tokens. Stage one emits zero LLM tokens for the structure; the tokens it did cost were spent once, on the generator, and have been amortised over every build since. Stage two emits tokens only for the residual, into a context that holds one slice, its generated records and a compiler’s opinion rather than a sea of markdown; small enough, as noted above, that a smaller model usually does the job faster with no loss in quality. Eberhardt’s four-to-one contract is four times more LLM output for a human to verify, and the rocket never asks for it to be written at all.
Böckeler. Her two unsolved problems for spec-as-source are non-determinism and the ghost of MDD. Stage one is deterministic by construction, and the ghost is kept out by the repo boundary: the failure of MDD was protected regions rotting inside committed generated code, and there is no committed generated code here. And the harness described above is her sensors-over-guides instinct applied as a design rule: hard signals from tools, not opinions from agents.
The harness rules in stage two are still markdown. Her complaint stands for that layer. The rocket shrinks the layer the complaint applies to; it does not yet abolish it.
DHH. He is right that a stream of individually reasonable PRs from people who are not holding the architecture in their heads will wreck it. He is right because a PR is code, code is the engineer’s medium, and the architecture lives nowhere but in the heads of the people who keep it. The rocket’s answer is that the PR is not the unit of contribution, and that the architecture does not have to live in the head of whoever is contributing. It lives in the heads of the people who build the factory, its architect and its technical leadership, and they fix it in place once, in the first stage: the DSL maps one to one onto the architecture, and a spec change can only ever produce what the generator emits. A designer writing a spec cannot wreck what a designer writing PRs did.
The unit of contribution is the spec. The DSL it is written in (an emlang dialect, in our case) is domain-neutral; the spec written in it is all domain: business facts as events on a timeline, the commands that cause them, the views that read them, and what must be refused. That is not the engineer’s medium; it is everyone’s. No role owns any part of it. Anyone who writes a good spec adds to the product, because the structure is derived from that spec deterministically and the scenarios it carries pin whatever the agent writes on top. Nothing checks that the spec itself is right except the people reading it, which is exactly why it matters that the spec is small and shaped like the domain rather than like code. That is a very different thing from handing a natural-language spec to an agent and hoping the code comes out right, because here there is very little LLM-written code left to hope about. For a team, that is staff liquidity: the work goes to whoever understands the problem, not to whoever holds the keyboard the codebase demands. DHH says as much himself in the same conversation: the large gains come when you interact with the agents directly, and “you cannot intermediate that bandwidth with another human”. No one outside engineering has shipped this way yet. The mechanism permits it; the organisation has not exercised it. I expect that to change, and I will report when it does.
Horthy. His root cause is that the model cannot design and nothing fast can tell it when it has designed badly. The rocket’s answer is not to ask it to. The design decisions RL never taught the model are made once, in the generator and in the one-to-one mapping between DSL and architecture, and a spec change cannot undo them because it can only produce what the generator emits. That leaves his oracle problem confined to the residual, and there the picture is better than he allows. The compiler is a fast oracle for structure once the structure is generated. Code Health is a fast, deterministic oracle for the code-level half of maintainability, and it reads only what the agent wrote. What neither of those measures is architecture, and architecture is the part that is derived. His “read the dang code” survives, redirected. At work, human reading is allocated by risk, and the risk sits in the generators, so what humans read most is the generators and, more than the generators, the diffs their approval tests produce. The residual is held by the compiler, the spec’s scenarios and the health gate rather than by eyes, and that is a choice he would argue with. He may be right, and the argument is now about a fraction of the code instead of all of it. Two of his points stand untouched. The generator is code too, and decays like any code. It is held small, changes rarely, and every change is an approved diff, but there are two known ways for it to stop being small, and both have to be watched. One is over-generalisation: a transformer shared across every bounded context is where the mega-model temptation relocates once the specs are kept small, and it shows up as special cases accumulating inside the generator. The other is abstraction leakage: platform-level concerns handled in the generator because it was the convenient place at the time. The parsers and linters behind the DSL grow the same way, and at the scale of the factory at work, rather than the workbench, they are the maintenance surface that replaced the one the rocket removed. And his clock, by which agent-built codebases turn brownfield in three to six months, has not run long enough on either the workbench or the factory for me to claim anything except that the architecture cannot drift, because it is derived.
What it costs
The spec is written in a small DSL rather than in semi-structured English, and people have to learn it. OpenSpec’s “no special syntax to learn” is the exact inverse of this trade, and I understand its appeal. The counter is that the dialect is an arrangement notation over concepts every engineer, and every model, already knows: commands, events, views, scenarios. The syntax is local; the semantics are mainstream.
And the approach suits AI-native architecture, which mostly means greenfield. The one-to-one mapping between DSL and architecture is the whole reason a function can do the lifting, and it only holds when the architecture was shaped for it: a functional core, vertical slices, deciders. A ten-year-old layered monolith has no such shape to map onto, and retrofitting one is a different article. What might change is the economics of the rewrite. An agent that can read a legacy system and reverse-engineer it into a structured spec, the events it actually records, the commands that cause them, the rules it actually enforces, hands the rewrite its whole structural stratum for free, because the generator emits it. And the spec would be worth having even if the rewrite never happened: ten years of tacit knowledge, held in code and in the heads of whoever is left, made explicit in a few hundred lines a product manager can read. That is a hypothesis, not a report. Horthy’s answer, by contrast, works on the brownfield most teams actually have today, and that is a real advantage of his over mine.
Nobody has published the benchmark. What we have is a small public workbench where every line is agent-written and three strata exist only as functions of the spec, and a production factory at work that has adopted the same arrangement. That is an argument with a running example, not a measurement.
The undrawn conclusion
Gorman told us why more tokens hurt. Böckeler told us markdown cannot be the whole answer and named the rung where the spec becomes the source. DHH told us where the PR ceiling sits for non-engineers. Horthy told us the factory swapped one box and that no fast oracle exists for what went wrong. Put the four together and the conclusion is that the LLM should not be the transformer for the parts of the system that a function can derive, and that those parts should never be in the repository. Everything else follows: the token budget, the sensors, the roles, the oracle.
The spec is the product. The generator writes the structure. The agent writes what is left.
[1] SE Radio 732: Jason Gorman on the Effective Use of AI for Software Development. [2] SE Radio 730: Birgitta Böckeler on Harness Engineering for AI Agents. [3] Lex Fridman Podcast #501: DHH on the Future of Programming, AI, Agentic Engineering, Vibe Coding and Linux. [4] Dexter Horthy, Why Software Factories Fail, 2026-07-25, and High Leverage Ep. 12: The Limits of Lights-Out Coding, with Joe Ruscio, Heavybit, 2026-08-05. [5] Birgitta Böckeler, Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl, martinfowler.com, 2025-10-15. [6] Colin Eberhardt, Putting Spec Kit Through Its Paces: Radical Idea or Reinvented Waterfall?, Scott Logic, 2025-11-26.
