A Graph of Loops
The AI coding conversation is moving from loops to graphs. What I've been running for months is both: a dependency graph of tasks walked by an autonomous driver, where every node contains its own nested loops.
After months of iteration I finally have an autonomous driver that can run for days or weeks on a genuinely large, enterprise-grade build, pulling me in only when the work surfaces a question it can’t resolve on its own. Zoomed all the way out, the system is a dependency graph of tasks walked by that driver, and every node in the graph contains its own nested loops with their own deterministic exits.
The AI coding conversation has a new word for systems shaped like this. Parts of the community on X have started pushing “graph engineering” as the next step past “loop engineering,” on the argument that a single loop is only the entry point, and real systems need dependencies, fan-out, and ordering. The term is contested. At least one newsletter has openly questioned whether it’s a genuinely coined discipline or content-marketing gravity, and right now the debate on X has more heat than sourcing.
The loop wave came first. Boris Cherny, who created Claude Code at Anthropic, said something that has been ricocheting around the community since: “I don’t prompt Claude anymore. I have loops that are running.” Peter Steinberger has been posting about designing loops that prompt your agents, and those posts have pulled millions of views. IBM has an explainer up titled “What Is Loop Engineering?” and the trade press has covered it as an emerging discipline. The definition everyone converges on is simple. An agentic loop has a trigger and a verifiable goal. The agent works toward the goal, checks whether the goal is met, and goes again until it has, without waiting for a new prompt at each step.
I don’t have a stake in the name. What I can report from my side of the screen is that what I’ve been running for months is both at once: a loop, nested inside a small graph, nested inside an outer graph that spans an entire roadmap. It was a graph of loops before either term existed. It was just the way I discovered to build robust software with agents, and it survived because everything simpler kept failing on me. If the field settles on a word for that shape, good. The pattern was doing the work before it had one.
This post is that structure, from the smallest loop to the largest, and then the part I think matters most: the eval conditions that make it safe to let the outer loop run without me.

The innermost loop: Ralph, a builder and a breaker
At the bottom of the stack sits the loop most people picture when they hear the term. A builder subagent implements one scoped task. When it finishes, a separate reviewer subagent examines the change, and here is the first detail that took me a while to learn: the reviewer is told explicitly to try to refute the change rather than assess its quality. A reviewer told to find problems behaves measurably differently from one told to evaluate. Ask for an assessment and you get a book report. Ask for a refutation and the model goes hunting: edge cases, hidden invariants, states nobody handled, the input that was never considered.
The refuter works read-only, so it can never “fix” things into agreement with itself. And it operates under an evidence rule: every claim it makes has to be pinned to a file and line number, or to a concrete reproduction. “This might have concurrency issues” doesn’t count. “This function releases the lock before the write on line 214, and here is the interleaving that corrupts state” counts.
If the reviewer requests changes, the same task goes back to the builder for another attempt, and each attempt runs in a fresh context. This retry pattern, the same task fed back into a clean context until the breaker is satisfied, is what the community calls Ralph, after Geoffrey Huntley’s name for it. I wrote in an earlier post that the context window is the unit of failure, and the fresh context per attempt is the structural answer: each retry starts clean, seeing the previous attempt in the files and git history instead of dragging it along in an increasingly polluted conversation. The loop is bounded on purpose: at most two fix-and-re-review rounds, and then it escalates instead of spinning. An unbounded adversarial loop between two models is a reliable way to burn tokens polishing a change that needed a human decision three hours earlier.
One level up: a feature is a small graph
A single feature doesn’t just run through that builder-breaker loop and ship. It passes through a small pipeline that is itself a tiny dependency graph rather than a strict line.
First, a planning pass on the top reasoning tier decomposes the spec into a machine-usable build brief: the target files, an acceptance criterion, a test plan, and a complexity tag. In code, a stage is just a call:
await agent(PLAN_PROMPT, {
model: 'fable', effort: 'high', schema: BRIEF_SCHEMA, label: 'plan',
})
That complexity tag is doing real work. The build-and-review loop gets routed to a cheaper or a stronger model depending on what the planner assigned:
const buildModel = brief.complexity === 'mechanical' ? 'sonnet' : 'opus'
The rule underneath is that mechanical work never gets to decide it’s mechanical. The planner decides, from above, with the whole spec in view.
Second, Ralph runs on the brief: the builder implements, the breaker reviews, and if the breaker rejects the change, the same task goes back for another fresh-context attempt, bounded at two rounds.
Third, a different vendor’s model reviews the same diff independently. This cross-vendor review earns its cost for a specific reason: reviewers from the same lab share training-data blind spots. When the builder and the reviewer learned from largely overlapping corpora, they tend to miss the same class of problem in the same way. A model from a different lab catches a different class of miss. I have watched it flag things the same-vendor reviewer sailed past.
Fourth, a synthesis pass back on the top reasoning model weighs the verdicts, which sometimes conflict, against the original acceptance criteria and decides what actually needs to change.
The stages that don’t depend on each other fan out in parallel rather than queuing politely. That graph is expressed directly in code. Independent branches run under parallel():
const [rubric, breaker] = await parallel([
() => agent(rubricPrompt(build), { ... }),
() => agent(refutePrompt(brief, build), { ... }),
])
And a backlog of items advances through the same stages with overlap under pipeline(), so the planner can be briefing item four while item two is under adversarial review:
const results = await pipeline(briefs, planStage, buildStage, reviewStage)
And one hard rule holds the whole thing together: the actual pass/fail gate lives in code, downstream of the synthesis step. A prompt never holds the gate. If the cross-vendor review was configured but genuinely failed to run, no synthesis model can talk its way past that fact:
if (codex.configured && !codex.reviewed) {
log('cross-review configured but failed; forcing REQUEST_CHANGES')
return { ...verdict, verdict: 'REQUEST_CHANGES' }
}
The code checking for the review’s output doesn’t negotiate.
The outermost loop: a driver that walks the roadmap
The top level is where the graph stops being an implementation detail and becomes the whole shape of the work.
An entire spec’s worth of work gets decomposed, up front, into a dependency graph of tasks. Some of those tasks can run in parallel. Most have real ordering dependencies, because real software has real coupling. A top-level driver then walks that graph automatically: for each task whose dependencies are satisfied, it invokes the feature graph, which runs Ralph at its core. A graph, holding a graph, holding a loop, three levels deep, and every level exists because a specific failure taught me it had to.
Running like this, a genuinely large build proceeds continuously for days or weeks. My involvement collapses to a handful of check-ins per day, and the character of those check-ins changed in a way I didn’t expect. Very little of it is approval. The check-ins are specific questions the work surfaced along the way: an ambiguity two tasks discovered they resolve differently, a tradeoff the plan didn’t anticipate, a decision that genuinely belongs to a human because it’s about the product rather than the code. The questions queue up, I answer them in a batch, and the driver keeps walking the parts of the graph that were never blocked on me. The routine bulk of the roadmap never touches me at all.
That sentence should make you suspicious. An agent system running a roadmap unattended for weeks sounds like a machine for generating confident garbage at scale. It would be, except for the gate.
Why it can run unattended
Letting an agent system advance itself through a roadmap is not a leap of faith, or at least it had better not be. The whole arrangement rests on one condition: the driver only auto-advances to the next task when a specific set of deterministic checks all pass. Deterministic is the operative word: the checks are code, they run the same way every time, and no amount of persuasive prose in an agent’s summary changes their output. A model’s opinion of whether the work turned out well never enters the gate.
The full test suite is green. Necessary and non-negotiable, but nowhere near sufficient on its own. A green suite that asserts the wrong things is a comfortable lie, which is why the gate doesn’t stop here.
A spec-drift check passes. The code gets diffed against the actual current plan document, mechanically. My convention: a repo is “governed” if a single master plan document, SPEC.md, exists at its root. A governed repo has exactly two source-of-truth documents, the plan and a record of what is actually built, and never more. That “never more” is load-bearing. A corrected agent that is allowed to write a second, competing plan document instead of fixing the real one will do exactly that (I learned this the hard way). A machine-readable position marker inside the plan says precisely where the work stands, so a fresh context doesn’t have to guess.
The enforcement is hooks and a git pre-commit gate, never a polite request in a prompt. And the failure behavior of each enforcement layer is a deliberate design decision that I’d argue matters as much as the checks themselves. The per-turn hooks that inject spec context are engineered to fail open: silent, never blocking a session. The pre-commit gate that stops a bad commit is engineered to fail closed. The asymmetry is intentional. A blocked commit is visible, local, and cheap to recover from. A hung per-turn hook would silently degrade every session on the machine, and you’d spend a day discovering why everything feels broken.
The product invariants hold. This third gate is specific to Neutron Enterprise, and it’s the one I want to spend the rest of this post on, because it’s the difference between “the loop ran” and “the loop produced something I’d put in front of a regulated customer.”
Encoding the product into the eval
Neutron Enterprise is the flagship proof of this whole approach: a real enterprise platform, built this way, for demanding and regulated environments where “it mostly works” is disqualifying. The promise I make about it is bulletproof enterprise software development, and a promise like that has to be enforced by machinery, because no human is going to re-verify it across weeks of autonomous commits.
So the system encodes twelve hard product invariants directly as automated, CI-enforced checks. They are requirements with teeth, and they run on every advance of the outer loop. Among them:
- Sovereignty. The system self-hosts fully in the customer’s own infrastructure. No phone-home. Every outbound network call is deny-by-default and provably blockable.
- Model independence. No module may import a specific AI vendor’s SDK directly. This is a linter rule, so it cannot decay into a convention that new code politely ignores.
- License cleanliness. Nothing copyleft-viral ships as core.
- Loose coupling through typed contracts, so modules integrate through declared interfaces instead of ad hoc reach-ins.
- Non-bypassable governance. Every agent action passes through a single policy checkpoint. There are no side doors, and the checks verify there are no side doors.
- Full read/write permission enforcement on every piece of knowledge the system touches.
- Deploy-anywhere portability, tamper-evident audit logging, software supply-chain hygiene, and an assume-hostile-input posture on every agent action.
The sovereignty invariant is my favorite example of what “enforced” means here, because it isn’t a design review checkbox. There is a literal test that boots the entire platform inside a network namespace with no route out at all, and asserts that the system still works using only its offline, open-weight capability. That test proves the claim instead of stating it. If some future refactor sneaks in a dependency on a cloud call, the build goes red before the outer loop takes another step.
This, more than any clever prompting, is what let the loop run unattended on a real enterprise build without producing garbage. When the definition of done includes “the platform boots with zero network access and still functions,” the system can drift in a hundred small ways and still get caught, because done is measured, every time, by code that doesn’t care how confident the agent felt.
The name arrived after the work
Loop engineering, graph engineering, whatever the field settles on: I’m not claiming I invented any of this. Patterns like these get discovered in parallel by everyone who pushes agents past the demo stage, and when Boris Cherny says he has loops running, I’d bet those loops predate his post by a long way too. Naming waves flatten history like that. The useful part is that we can finally compare notes with shared words.
What I’d add to those notes, from months of running this on production systems: a loop is easy to build and hard to trust. The demo version, agent works, agent checks, agent repeats, takes an afternoon. The version you can leave alone with a roadmap for weeks takes an adversarial reviewer that has to cite line numbers, a planner that decides complexity from above, a reviewer from a rival lab, a gate that lives in code rather than in a prompt, a fresh context for every attempt, two source-of-truth documents and never a third, and your actual product requirements compiled down into checks that run on every advance.
The check-ins on a long build are now a few moments a day when the system surfaces a question only I can answer. Everything else, it loops.
I'm productizing this substrate as Neutron for operators who want a real agent system without rebuilding it themselves. Separately I take on a small number of consulting engagements per quarter for teams shipping into production. Neutron Enterprise →