Brian Gill

Harness Engineering · Part 3 of 3

Fixed Point in a Dumb Loop

A convergence architecture for agentic development

In The Future Loops I wrote that the three pillars of an agentic loop stumble into a 100+ year old mathematical formalism, and promised more in another post. This is that post.

I have been running Ralph-style loops (agent, task, repeat) as my primary development method for a while now. Long enough to have watched them work spectacularly, and long enough to have watched them fail in ways that took me weeks to diagnose. I am open-sourcing the harness, and this is the theory of operation: not what the code does, but why this shape of loop converges when the obvious shape doesn’t.

The short version: the loop is not the interesting part. Anyone can wrap an agent in a while loop. What separates a loop that converges on a correct product from one that produces plausible motion forever is a small set of structural properties. I arrived at them empirically, by failure, and only later realized I had re-derived the hypotheses of Banach’s fixed-point theorem, badly and one at a time.

That framing turned out to be worth more than a retrospective tidy-up. It tells you which failures are bugs, which are design errors, and which are proofs that you asked for something impossible. It gives you a number to instrument. And it tells you when to stop iterating, because more loops will not help.

The pillars, again

I covered these in The Future Loops so I will be brief here. The loop works on three foundational pillars, discovered in this order:

  • Eventual consistency. Individual iterations are unreliable and I stopped trying to fix that. A pass will misread the requirement, patch the wrong module, hallucinate an API. What matters is not per-iteration correctness but whether the sequence converges. Under the right conditions the loop is self healing: it wanders, the wander gets corrected, and the corrections accumulate faster than the errors. Design for the limit, not the step.
  • Back pressure. Self healing requires something to heal toward. There must be a target the agents can be measured against and falsified by. Not a vibe, not a prompt, an artifact. I use canonical specifications with a hard firewall: the loops that establish canon and the loops that implement against it are separate processes with separate permissions. Implementation reads canon and tests against it, but may never edit it.
  • Context management. Each loop’s context is explicitly curated: what is in, what is out, how it is gathered (typically summarized via subagents), and how it is loaded. The invariant is that real work always happens in the upper half of the window, the smart zone, before quality degrades. Context is not a budget to fill, it is a resource whose composition determines output quality.

Three good ideas that compose well, I thought. They are not. They are the three hypotheses of one theorem, one each.

The theorem

Banach’s fixed-point theorem (1922) says: take a complete metric space, meaning a set of points with a well behaved notion of distance d, and a map T from the space to itself that is a contraction, meaning there is some factor q < 1 such that d(T(x), T(y)) ≤ q · d(x, y) for every pair of points. Then T has exactly one fixed point x*, and iterating T from any starting point converges to it, with the error after n iterations bounded by qⁿ times the starting error.

Three hypotheses: a metric, a fixed map, and a contraction factor strictly below one. Here is how they map onto the loop.

Canon supplies the metric. Without a falsifiable target, “distance from correct” is not just hard to compute, it is undefined. The theorem cannot even be stated, x* does not exist, and “converge” is a word with no referent. I think this is the actual reason unspecified agent loops fail. It is not that the models are weak, it is that the space has no notion of distance. You are asking a system to descend a gradient in a space that has none, and what you get is exactly what you would predict: motion without progress, which is very hard to distinguish from progress when every individual step looks reasonable. Canon isn’t documentation that happens to help. It is the thing that makes convergence a meaningful idea at all.

The firewall keeps the map fixed. Banach requires one map, iterated. Not a sequence of maps that each seemed reasonable at the time. The moment the implementation loop can edit the spec, you are no longer iterating T on the artifact, you are iterating on the joint space of artifact and spec together, and that system has a much cheaper fixed point available: rewrite canon to bless whatever code already exists. Distance goes to zero by moving the target.

This is the failure mode I lost the most time to, because it is nearly invisible from the inside. Every test passes. Every commit message is honest. The spec and the code agree completely. And it means nothing, because the agreement was achieved from the wrong side. The firewall isn’t hygiene, it is a hypothesis of the theorem: a spec the loop can touch is a metric the loop can collapse.

Context management sets the contraction factor. Each iteration has to actually contract, and what determines the factor q is whether the agent’s edits are signal or noise. In the degraded tail of a context window, edits drift toward random perturbation, and random perturbation doesn’t contract. It random-walks at best and expands at worst, and q creeps toward 1.

And q enters the bound as qⁿ, which is brutal in both directions. At q = 0.5, ten iterations shrink your error about a thousand fold. At q = 0.9, about three fold. At q = 0.99 you have a loop that appears busy and is statistically standing still. It will burn tokens indefinitely and produce a changelog. The return on context hygiene isn’t linear, it compounds exponentially in iteration count. That is the mathematical content of “stay in the smart zone,” and it is why context curation is worth more engineering effort than almost anything else in the harness.

Why “eventual” isn’t a cope. The starting point appears in the error bound only as a constant factor. The theorem is indifferent to where you start! A bad first pass costs you a constant, not a guarantee. That is the formal license for letting the loop flail early, and it is also why the system self heals: a hallucinated change knocks you to some perturbed state, and the very next application of T pulls back toward x*, because contraction doesn’t care how you got there. History is irrelevant, only position matters. Systems without fixed canon are expansive maps, where that same perturbation compounds instead. Same models, same agents, same prompts, opposite sign on the exponent. That is the entire difference between the loops people report as magic and the loops people report as toys.

The architecture

The harness is three loops, one canon, and strict permissions.

  • The New Spec Loop intakes new requirements, reconciles them against existing canon, and updates canon. It is the only process with write access to canon.
  • The Plan Loop intakes canon completely, fans out via subagents to read the full codebase, and emits fix_plan: an enumerated list of everything that must happen for canon and codebase to agree. It may not edit canon and it may not edit code. Items are deliberately decomposed to be small and self contained, sized for entry level models.
  • The Build Loop intakes canon, the relevant codebase slice, and fix_plan. It executes the one next most important item only, wipes context, and repeats.

The permission tiers are the whole design. New Spec writes canon and nothing else. Plan reads canon and code and writes only fix_plan. Build reads canon and fix_plan and is the only one that writes source. Canon legislates, Plan judges, Build executes, and nobody does two of those jobs.

The oracle

You never have x* in hand; if you did you would be done. So distance from correct is not directly evaluable, and every practical convergence system needs a stand-in: something that takes an artifact and returns a set of violations, which serves as your only observable proxy for distance. That is the oracle. Canon defines the metric, the oracle computes it. Back pressure is the pillar, and the oracle is where the pillar touches the ground. A canon with no oracle is just a wish, because pressure only exists at the point of adjudication.

In this architecture the Plan Loop is the oracle, and fix_plan is the residual, the measurement it produces. That distinction earns its keep immediately, because an oracle is a function while a residual is a value, and values have provenance and expiry. fix_plan is a measurement of distance, enumerated coordinate by coordinate, and the Build Loop consumes it while operating on a codebase that is already several steps past when the measurement was taken. Every build step partially invalidates the document steering the next one. Calling fix_plan a measurement makes “how stale is this reading” an obvious question with a tunable answer: re-plan cadence.

The Plan Loop makes an unusually clean oracle. It is read-only on canon, read-only on code, and its output is a report. It is structurally incapable of the two failure modes that matter: it cannot collapse the metric and it cannot fake the artifact. Most harnesses can’t say that, because their judging is entangled with their generating. I got the separation almost by accident, by making planning a distinct loop rather than a phase inside building. The subagent fan-out buys something too. The Plan Loop sees the whole codebase in summarized breadth while the Build Loop sees one slice in depth, and different context composition means different blind spots. That decorrelation is most of what you want from an independent judge.

Two constraints turned out to be load bearing. The Build Loop may edit fix_plan, and convergence looks like fix_plan emptying, and those two facts together mean the defendant can close its own case. Not through malice, but through the entirely reasonable “this item is already handled” and “this is no longer relevant.” That is how a ruler gets shortened. What makes it recoverable:

  1. The Plan Loop regenerates from canon and code. It never amends the existing fix_plan. If planning re-derives from scratch, a Build Loop deletion is transient noise the next plan pass restores: eventual consistency applied to the oracle itself. If planning amends, deletions become permanent and the metric genuinely collapses. This is the highest leverage constraint in the entire system, and exactly the kind of thing that quietly degrades to “amend” for efficiency reasons.
  2. Convergence is certified by a Plan pass, not by Build exhausting the list. An empty fix_plan at the end of a build run is a claim. The verdict is a fresh Plan Loop against canon and current code returning nothing. The delta between what Build thought remained and what Plan finds is a direct read on Build’s self-assessment bias, and worth logging on its own.

A few properties make an oracle worth having at all. Decorrelation: an oracle’s value comes entirely from its errors being independent of the generator’s. The same model with the same context and the same prompt asked twice is not an oracle, it is a repeat, and correlated errors don’t cancel, they ratify. Bounded error: an oracle that is wrong with some small probability puts a floor under convergence, which is tolerable, but a systematically biased oracle is qualitatively worse. It doesn’t add noise around the target, it relocates the target, and you converge cleanly and confidently to the wrong artifact with a green board the whole way. And verification cheaper than generation: the entire loop economy rests on this asymmetry. Where it holds (does it compile, do invariants survive, does the output match golden) oracles are strong and cheap. Where it collapses (is this the right architecture, will this scale, is this the correct abstraction) verifying is as hard as building, no oracle exists, and you are at the human tier whether you admit it or not.

One gap I suspect most people have: if your implementation loop writes its own conformance tests, part of your oracle is defendant-authored. It is the same metric collapse as spec editing, one level down, because an agent that can write the assertion can write the assertion its code already passes. It presents as a tautological suite, asserting observed behavior rather than specified behavior, and it is nearly invisible in review because every test is green and every test is about the right function. The fix is two tiers: conformance tests derive from canon and are not editable by the implementer, and scaffolding tests below that line are the implementer’s business.

Where the analogy strains

The strain points are precisely the failure modes, which is why the frame is worth carrying.

Your metric is really a pseudometric. Tests give you distance zero meaning “indistinguishable to the canon,” not “identical.” You converge to an equivalence class, not a point, and every behavior canon doesn’t discriminate is a free coordinate the loop random-walks in forever. The sharp version: canon defines not just the target but the resolution at which “converged” is a meaningful claim. Blind spots are directions in which the map doesn’t contract at all, and no number of iterations will fix that. When you find a converged-but-wrong artifact, the fix is always adding to canon, never nagging the loop. The loop behaved correctly with respect to the metric you gave it.

The map is stochastic. The honest version is contraction in expectation plus bounded variance, and what you converge to is a distribution concentrated near the target, not the target itself. The width of that distribution is your residual defect rate, and it never reaches zero. You buy tightness with better canon and better context management, not with more iterations.

Contraction is local. The real map is piecewise contractive with basins. When the architecture is wrong, no local edit reduces distance. The loop hill-climbs into a basin and sits there, converged by the metric and wrong by intent. Escape requires a discontinuous jump, permission to demolish, and a patch-only loop structurally cannot make it. This interacts badly with the Plan Loop’s decomposition mandate: maximally digestible items are strictly local moves, and fine granularity cannot express “delete this subsystem and rebuild it.” It will happily enumerate a hundred discrete patches to something that should not exist. If you want the demolition move reachable, it has to be a distinct item class the Plan Loop is licensed to emit and Build is licensed to execute at full context.

The fixed point might not exist. Contradictory canon has no satisfying artifact, the theorem’s conclusion is vacuous, and the loop oscillates. This gives you a free diagnostic: sustained oscillation between two states means infeasible canon, or a cycle between competing sub-specs. It never means the agent needs to try harder.

Operating it

The most immediately useful thing the frame gives you is that q is measurable. Log the count of failing assertions (or open fix_plan items) per iteration and plot it on a log axis. A straight line means you are contracting, and the slope is log q. Flat means q is about 1 and you are paying tokens for a random walk. You now have a health metric for the loop itself rather than for the product, and you can A/B your context strategy directly against it. Prompt changes, summarization strategies, subagent fan-out width, item granularity: all of them become measurable interventions on a single number that predicts cost to convergence.

The diagnostics fall out cleanly:

  • Geometric decay toward zero: working as designed, do nothing.
  • Decay that stalls above zero: a non-contractive region, probably the wrong basin. The answer is an architectural jump, not more loops.
  • Oscillation between states: no fixed point, infeasible canon. Fix the contradictions.
  • Flat from the start: q near 1, so context has degraded or the target is undefined. Fix the context composition, or metrize the space.
  • Converged but wrong: pseudometric blindness. Add to canon, and never nag the loop.

That last one is the one to internalize. The reflex when output is wrong is to improve the prompt. Usually the loop did exactly what the metric asked, and the metric was silent.

Step size is a knob on q as well. By sizing items for entry level models the Plan Loop selects the step size, which makes it a preconditioner as well as an oracle: it sets both what you measure and how you move. Decompose finely enough that each item fits comfortably in a clean context and a modest model, and per-step error drops sharply. The extra iterations are cheap, because qⁿ rewards a small q far more than it punishes a large n. Buying a lower q with more iterations is almost always the right trade, and it is also why, as I noted in The Future Loops, the vast majority of the workload runs great on commodity priced models.

One extension I am taking seriously: the ratchet. If you enforce that green tests may never go red, you have made the map monotone on a partial order, and a different theorem (Knaster–Tarski) gives you a least fixed point with no metric and no q at all. That is a strictly weaker hypothesis than contraction, which means a strictly more robust guarantee. You don’t need every loop to get closer, you only need no loop to get worse, and convergence follows from the structure. Regression suites are exactly this monotonicity enforcement, and most of us already have them and think of them as a safety net rather than a convergence proof. I now suspect the deepest version of this architecture is order-theoretic rather than metric, and that the metric framing is the approachable shadow of it. That is the direction I am exploring next.

Design principles

Everything above, compressed:

  1. Metrize the space first. Unspecified loops cannot converge, because convergence is undefined. Canon before code.
  2. Never let the defendant write the verdict. Firewall canon from implementation, firewall conformance tests from the implementer, and certify convergence with a fresh judging pass rather than an exhausted worklist.
  3. Regenerate the residual, never amend it. The oracle must re-derive from canon and current state, or deletions become permanent.
  4. Context composition is the contraction factor. Curate ruthlessly. Nothing else in the harness pays this well.
  5. Design for the limit, not the step. Individual passes are allowed to be wrong. Sequences are not.
  6. When output is wrong, suspect the metric before the model. Converged-and-wrong is a canon bug every time.
  7. Ratchet everything you can. Monotonicity is a cheaper guarantee than contraction and a stronger one.

Closing

The takeaway I would want a reader to leave with isn’t the theorem. It is that agentic development has a shape, and the shape is not “better prompts.” It is a metric, a fixed map, and a contraction factor, and every architectural decision in a loop harness is a decision about one of those three, whether or not you know it. Guess wildly, check strictly, forget completely.

The harness is at [repo link]. Issues and disagreement welcome, particularly from anyone who has pushed the order-theoretic version further than I have. And if you want the version of this with no mathematics at all, that is the companion post: Wrong Every Time, Right in the End.