In 1821, Charles Babbage and John Herschel sat comparing two independently computed sets of astronomical tables, looking for discrepancies. They found them. Babbage's reaction, as he told it later, was the sentence every engineer eventually says in some form: "I wish to God these calculations had been executed by steam."

<figure style="margin: 2.5rem auto; text-align: center;">
<img src="/static/img/half_life_babbage_herschel.webp" alt="Two men in Regency dress at a desk, each bent over a separate stack of handwritten tables, comparing them line by line by candlelight" loading="lazy" decoding="async" style="max-width: 100%; height: auto;">
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.5rem auto 0;">Two sets of tables, computed separately, read against each other line by line. It is the oldest verification method there is, and it tells you only that an error exists somewhere.</figcaption>
</figure>

Look past the wish, at what they were doing. Two computations, run by hand, compared against each other, because a table is a long chain of small steps and every step carries a small chance of going wrong. Nobody could point at the error. They could only detect that one existed somewhere, and then go find it.

That procedure has a cost structure, and two centuries later it is the cost structure of every unsupervised agent run.

## The schedule

Take a loop of n steps. Each step has some probability of going wrong, call it ε, and a step that goes wrong is not corrected: the mistake rides along to the end. Then the chance of a clean run is (1 − ε)ⁿ.

That formula is not the useful thing. The useful thing is the question you get by setting it to one half. After how many steps has the chance of a clean run been cut in half?

<figure style="margin: 2.2rem auto; text-align: center;">
<svg viewBox="0 0 400 108" role="img" aria-label="The loop half-life equals the natural logarithm of two divided by the negative natural logarithm of one minus epsilon, which is approximately 0.69 divided by epsilon" style="max-width: 320px; width: 100%; height: auto;">
  <g fill="var(--fg, #e6e4df)" style="font-family: var(--serif, serif); font-size: 21px;">
    <text x="14" y="60" font-style="italic">n</text>
    <text x="27" y="66" style="font-size: 13px;">½</text>
    <text x="45" y="60">=</text>
    <text x="118" y="47" text-anchor="middle">ln 2</text>
    <text x="118" y="87" text-anchor="middle"><tspan>−ln(1 −</tspan><tspan font-style="italic"> ε</tspan><tspan>)</tspan></text>
    <text x="188" y="60">≈</text>
    <text x="254" y="47" text-anchor="middle">0.69</text>
    <text x="254" y="87" text-anchor="middle" font-style="italic">ε</text>
  </g>
  <g stroke="var(--fg, #e6e4df)" stroke-width="1.1">
    <line x1="76" y1="55" x2="160" y2="55"/>
    <line x1="226" y1="55" x2="282" y2="55"/>
  </g>
</svg>
</figure>

I call this the loop half-life, and the approximation is good to under one percent for any ε below two percent. At a one percent error rate per step, the half-life is 69 steps. At a tenth of a percent, 693.

<figure style="margin: 2.5rem auto; text-align: center;">
<svg viewBox="0 0 580 250" role="img" aria-label="Three decay curves showing the probability of a clean run against number of steps, for per-step error rates of one percent, one tenth of a percent and one hundredth of a percent" style="max-width: 520px; width: 100%; height: auto;">
  <line x1="60" y1="30" x2="60" y2="210" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.35"/>
  <line x1="60" y1="210" x2="545" y2="210" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.35"/>
  <line x1="60" y1="120" x2="545" y2="120" stroke="var(--fg-soft, #999)" stroke-width="1" stroke-dasharray="3 4" opacity="0.5"/>
  <text x="52" y="34" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">100%</text>
  <text x="52" y="124" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">50%</text>
  <text x="52" y="214" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">0</text>
  <text x="60" y="230" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">0</text>
  <text x="300" y="230" text-anchor="middle" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">300 steps</text>
  <text x="545" y="230" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">600</text>
  <polyline fill="none" stroke="var(--accent, #059669)" stroke-width="2" opacity="0.95" points="60.0,30.0 84.0,76.9 108.0,111.5 132.0,137.1 156.0,156.1 180.0,170.1 204.0,180.5 228.0,188.2 252.0,193.9 276.0,198.1 300.0,201.2 324.0,203.5 348.0,205.2 372.0,206.4 396.0,207.4 420.0,208.0 444.0,208.6 468.0,208.9 492.0,209.2 516.0,209.4 540.0,209.6"/>
  <polyline fill="none" stroke="var(--accent, #059669)" stroke-width="2" opacity="0.6" points="60.0,30.0 84.0,35.3 108.0,40.5 132.0,45.5 156.0,50.4 180.0,55.1 204.0,59.7 228.0,64.1 252.0,68.4 276.0,72.6 300.0,76.7 324.0,80.6 348.0,84.4 372.0,88.2 396.0,91.8 420.0,95.3 444.0,98.6 468.0,101.9 492.0,105.1 516.0,108.2 540.0,111.2"/>
  <polyline fill="none" stroke="var(--accent, #059669)" stroke-width="2" opacity="0.32" points="60.0,30.0 84.0,30.5 108.0,31.1 132.0,31.6 156.0,32.1 180.0,32.7 204.0,33.2 228.0,33.7 252.0,34.3 276.0,34.8 300.0,35.3 324.0,35.8 348.0,36.4 372.0,36.9 396.0,37.4 420.0,37.9 444.0,38.4 468.0,39.0 492.0,39.5 516.0,40.0 540.0,40.5"/>
  <circle cx="115.2" cy="120" r="3.5" fill="var(--accent, #059669)"/>
  <text x="126" y="112" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg, #e6e4df)">half-life: 69 steps</text>
  <text x="150" y="200" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)">ε = 1%</text>
  <text x="424" y="92" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)" opacity="0.75">ε = 0.1%</text>
  <text x="424" y="34" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)" opacity="0.55">ε = 0.01%</text>
</svg>
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.4rem auto 0;">Probability of a clean run, plotted against steps. The curves are computed, not drawn. A loop does not stop working at some length: it halves on a fixed schedule set entirely by the per-step error rate.</figcaption>
</figure>

Nobody says "my agent gets unreliable somewhere past a few hundred steps" and means a decay constant. They mean a feeling. The half-life turns that feeling into a number you can compute from failure logs you already have, and the number tends to come in well under the ambition.

## Why verification looks like overhead until it doesn't

Now the part that actually changes decisions. Suppose you add a check that catches nine out of ten mistakes. You have improved ε by a factor of ten. Linear, unglamorous, and expensive: in the language of [the last post in this series](/posts/the-untrusted-searcher/), you climbed one rung of the verifier gradient, and rungs cost real work.

At 300 steps, that factor of ten takes your chance of a clean run from 4.9% to 74.1%.

<figure style="margin: 2.5rem auto; text-align: center;">
<svg viewBox="0 0 600 180" role="img" aria-label="Bar chart comparing probability of a clean 300-step run at three per-step error rates: 4.9 percent, 74.1 percent and 97.0 percent" style="max-width: 520px; width: 100%; height: auto;">
  <text x="130" y="24" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px; letter-spacing: 1px;" fill="var(--fg-soft, #999)">ε PER STEP</text>
  <text x="145" y="24" style="font-family: var(--sans, sans-serif); font-size: 11px; letter-spacing: 1px;" fill="var(--fg-soft, #999)">CLEAN RUN AT 300 STEPS &#8594;</text>
  <g style="font-family: var(--serif, serif); font-size: 15px;">
    <text x="130" y="62" text-anchor="end" fill="var(--fg, #e6e4df)">1%</text>
    <rect x="145" y="46" width="16.7" height="22" rx="2" fill="var(--accent, #059669)" opacity="0.32"/>
    <text x="495" y="62" style="font-size: 13px;" fill="var(--fg-soft, #999)">4.9%</text>
    <text x="130" y="106" text-anchor="end" fill="var(--fg, #e6e4df)">0.1%</text>
    <rect x="145" y="90" width="251.8" height="22" rx="2" fill="var(--accent, #059669)" opacity="0.72"/>
    <text x="495" y="106" style="font-size: 13px;" fill="var(--fg-soft, #999)">74.1%</text>
    <text x="130" y="150" text-anchor="end" fill="var(--fg, #e6e4df)">0.01%</text>
    <rect x="145" y="134" width="330.0" height="22" rx="2" fill="var(--accent, #059669)" opacity="0.95"/>
    <text x="495" y="150" style="font-size: 13px;" fill="var(--fg-soft, #999)">97.0%</text>
  </g>
</svg>
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.4rem auto 0;">The same three error rates, read at one fixed run length. Each tenfold improvement in the per-step rate is linear work. What it buys at 300 steps is not linear at all.</figcaption>
</figure>

So the value of a check scales with the length of the loop it sits in. In a five-step workflow, hardening verification is close to invisible, and the people calling it ceremony are describing their own experience accurately. In a six-hundred-step run it is the only thing that matters. Both camps are reading the same graph from different x-positions and concluding the other one is naive.

## Detection is not correction

Here is where the arithmetic stops helping and starts misleading, because it suggests the strategy is to keep pushing ε down. That strategy is a treadmill. Every decade of improvement costs a new class of infrastructure, and all you have bought is a longer schedule of the same decay.

The way out is not a smaller ε. It is a different shape.

Suppose that instead of accumulating mistakes, each round of your loop takes whatever is still wrong and makes it less wrong, by some factor L. If L is below one, the remaining error after n rounds is Lⁿ times what you started with, and it goes to zero. Banach proved the general version in 1922: a contraction on a complete space converges to exactly one fixed point, from any starting position, regardless of how many steps you take.

<figure style="margin: 2.5rem auto; text-align: center;">
<svg viewBox="0 0 580 210" role="img" aria-label="Two loop shapes side by side: a chain in which per-step errors accumulate, and a contraction in which each iteration shrinks the remaining error toward a fixed point" style="max-width: 540px; width: 100%; height: auto;">
  <text x="20" y="22" style="font-family: var(--sans, sans-serif); font-size: 11px; letter-spacing: 1px;" fill="var(--fg-soft, #999)">CHAIN</text>
  <text x="320" y="22" style="font-family: var(--sans, sans-serif); font-size: 11px; letter-spacing: 1px;" fill="var(--fg-soft, #999)">CONTRACTION</text>
  <g stroke="var(--fg-soft, #999)" stroke-width="1.2" opacity="0.5" fill="none">
    <rect x="20" y="40" width="46" height="26" rx="3"/><rect x="78" y="40" width="46" height="26" rx="3"/><rect x="136" y="40" width="46" height="26" rx="3"/><rect x="194" y="40" width="46" height="26" rx="3"/>
  </g>
  <g style="font-family: var(--sans, sans-serif); font-size: 10px;" fill="var(--fg-soft, #999)" text-anchor="middle">
    <text x="43" y="57">step</text><text x="101" y="57">step</text><text x="159" y="57">step</text><text x="217" y="57">step</text>
  </g>
  <g stroke="var(--fg-soft, #999)" stroke-width="1.2" opacity="0.6">
    <line x1="66" y1="53" x2="76" y2="53"/><line x1="124" y1="53" x2="134" y2="53"/><line x1="182" y1="53" x2="192" y2="53"/>
  </g>
  <text x="248" y="58" style="font-family: var(--sans, sans-serif); font-size: 13px;" fill="var(--fg-soft, #999)" opacity="0.7">…</text>
  <g fill="#e05252" opacity="0.85">
    <circle cx="43" cy="78" r="3"/><circle cx="101" cy="78" r="4.2"/><circle cx="159" cy="78" r="5.4"/><circle cx="217" cy="78" r="6.6"/>
  </g>
  <text x="20" y="110" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)">error accumulates</text>
  <text x="20" y="132" style="font-family: var(--sans, sans-serif); font-size: 12px;" fill="var(--fg-soft, #999)">p(n) = (1 − ε)ⁿ</text>
  <text x="20" y="176" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)">longer is worse</text>
  <line x1="290" y1="30" x2="290" y2="190" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.2"/>
  <g fill="none" stroke="var(--accent, #059669)" stroke-width="1.5">
    <circle cx="380" cy="53" r="26" opacity="0.85"/><circle cx="380" cy="53" r="17" opacity="0.6"/><circle cx="380" cy="53" r="9" opacity="0.4"/>
  </g>
  <circle cx="380" cy="53" r="3" fill="var(--accent, #059669)"/>
  <text x="418" y="50" style="font-family: var(--sans, sans-serif); font-size: 10px;" fill="var(--fg-soft, #999)">fixed point</text>
  <g fill="#e05252" opacity="0.85">
    <circle cx="320" cy="115" r="6.6"/><circle cx="352" cy="115" r="4.6"/><circle cx="384" cy="115" r="3.2"/><circle cx="416" cy="115" r="2.2"/><circle cx="448" cy="115" r="1.5"/>
  </g>
  <text x="320" y="145" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)">error shrinks by L each round</text>
  <text x="320" y="167" style="font-family: var(--sans, sans-serif); font-size: 12px;" fill="var(--fg-soft, #999)">eₙ = Lⁿ · e₀,  L &lt; 1</text>
  <text x="320" y="189" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)">longer is better</text>
</svg>
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.4rem auto 0;">The same number of iterations, two different shapes. On the left, length is exposure. On the right, length is progress. Nothing about the model changed between these two pictures, only where the findings go.</figcaption>
</figure>

The sign flip is total. In a chain, running longer is exposure you have to survive. In a contraction, running longer is how you get there. Success stops being a die roll you passed and becomes an attractor you fall into.

Which gives a test you can apply to a single check, without any of the mathematics:

Does this step reduce the error, or does it only detect it?

A linter whose failure aborts the run is detection. It bounds the damage, it tells you something true, and it leaves ε exactly where it was. The same linter, with its findings fed back into the next iteration, is a contraction. Same tool, same output, opposite effect on how long the loop can safely run.

<figure style="margin: 2.5rem auto; text-align: center;">
<svg viewBox="0 0 580 190" role="img" aria-label="Two wirings of the same check: on the left the finding exits to a human and the work stops, on the right the finding returns into the next iteration of the work" style="max-width: 540px; width: 100%; height: auto;">
  <text x="20" y="20" style="font-family: var(--sans, sans-serif); font-size: 11px; letter-spacing: 1px;" fill="var(--fg-soft, #999)">GUARDRAIL</text>
  <text x="320" y="20" style="font-family: var(--sans, sans-serif); font-size: 11px; letter-spacing: 1px;" fill="var(--fg-soft, #999)">FEEDBACK LOOP</text>
  <g stroke="var(--fg-soft, #999)" stroke-width="1.3" fill="none" opacity="0.55">
    <rect x="22" y="40" width="78" height="30" rx="4"/>
    <rect x="130" y="40" width="78" height="30" rx="4"/>
    <rect x="130" y="118" width="78" height="30" rx="4"/>
  </g>
  <g style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)" text-anchor="middle">
    <text x="61" y="60">work</text><text x="169" y="60">check</text><text x="169" y="138">human</text>
  </g>
  <g stroke="var(--fg-soft, #999)" stroke-width="1.4" opacity="0.7">
    <line x1="100" y1="55" x2="126" y2="55"/>
    <line x1="169" y1="70" x2="169" y2="114"/>
  </g>
  <polygon points="126,55 120,52 120,58" fill="var(--fg-soft, #999)" opacity="0.7"/>
  <polygon points="169,114 166,108 172,108" fill="var(--fg-soft, #999)" opacity="0.7"/>
  <text x="22" y="172" style="font-family: var(--serif, serif); font-size: 13px;" fill="#e05252">ε unchanged</text>
  <line x1="290" y1="28" x2="290" y2="175" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.2"/>
  <g stroke="var(--accent, #059669)" stroke-width="1.4" fill="none">
    <rect x="322" y="40" width="78" height="30" rx="4" opacity="0.85"/>
    <rect x="430" y="40" width="78" height="30" rx="4" opacity="0.85"/>
  </g>
  <g style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--fg, #e6e4df)" text-anchor="middle">
    <text x="361" y="60">work</text><text x="469" y="60">check</text>
  </g>
  <g stroke="var(--accent, #059669)" stroke-width="1.5" fill="none">
    <line x1="400" y1="55" x2="426" y2="55"/>
    <path d="M 469 70 L 469 100 L 361 100 L 361 72"/>
  </g>
  <polygon points="426,55 420,52 420,58" fill="var(--accent, #059669)"/>
  <polygon points="361,72 358,79 364,79" fill="var(--accent, #059669)"/>
  <text x="415" y="118" text-anchor="middle" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">finding</text>
  <text x="322" y="172" style="font-family: var(--serif, serif); font-size: 13px;" fill="var(--accent, #059669)">error shrinks by L</text>
</svg>
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.4rem auto 0;">The only difference is where the arrow goes. Left, the finding leaves the system and the run ends. Right, the finding re-enters the work. One caps your damage, the other buys you length.</figcaption>
</figure>

Babbage and Herschel were doing detection. Two tables, compared, discrepancy found, error hunted by hand. His engine went after the structure instead: a procedure with no room to drift, rather than a better check bolted onto one that did.

<figure style="margin: 2.5rem auto; text-align: center;">
<img src="/static/img/half_life_difference_engine.webp" alt="Close detail of the stacked brass gear columns of a nineteenth century mechanical calculating engine, front wheels sharp, the columns receding into soft focus" loading="lazy" decoding="async" style="max-width: 100%; height: auto;">
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.5rem auto 0;">The engine was not a faster clerk. Each column constrains the next, so the arithmetic has nowhere to drift, which is a different thing from checking it afterwards.</figcaption>
</figure>

## The dangerous middle, again

[The previous post](/posts/the-untrusted-searcher/) landed on a warning: a weak verifier is worse than no verifier, because it launders trust into something that resembles evidence. The contraction view adds a second, independent way for a verifier to be weak, and this one is easier to miss because nothing about it looks broken.

A check can be perfectly valid and still buy you nothing. It reports correctly, every time, and the next iteration is no better than the last, because the report went into a log nobody reads or into a pass/fail gate with no path back into the work. Your dashboard is green about the check. Your loop is still a chain.

And the failure mode below that one has no name in most engineering vocabularies: L above one. A loop that breaks slightly more than it fixes on each pass. It does not stall, which is what makes it hard to spot. It runs, it looks busy, every round produces a diff, and after fifty rounds the error is eleven and a half times what you started with.

<figure style="margin: 2.5rem auto; text-align: center;">
<svg viewBox="0 0 580 200" role="img" aria-label="Curves of remaining error over twenty iterations for contraction factors of 0.5, 0.7 and 0.9, alongside a flat line at L equals 1 and a rising curve at L equals 1.05" style="max-width: 520px; width: 100%; height: auto;">
  <line x1="60" y1="20" x2="60" y2="160" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.35"/>
  <line x1="60" y1="160" x2="330" y2="160" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.35"/>
  <text x="52" y="34" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">e₀</text>
  <text x="52" y="164" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">0</text>
  <text x="60" y="180" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">0</text>
  <text x="330" y="180" text-anchor="end" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">20 iterations</text>
  <polyline fill="none" stroke="var(--accent, #059669)" stroke-width="2" opacity="0.95" points="60.0,30.0 72.0,90.0 84.0,120.0 96.0,135.0 108.0,142.5 120.0,146.2 132.0,148.1 144.0,149.1 156.0,149.5 168.0,149.8 180.0,149.9 192.0,149.9 204.0,150.0 216.0,150.0 228.0,150.0 240.0,150.0 252.0,150.0 264.0,150.0 276.0,150.0 288.0,150.0 300.0,150.0"/>
  <polyline fill="none" stroke="var(--accent, #059669)" stroke-width="2" opacity="0.62" points="60.0,30.0 72.0,66.0 84.0,91.2 96.0,108.8 108.0,121.2 120.0,129.8 132.0,135.9 144.0,140.1 156.0,143.1 168.0,145.2 180.0,146.6 192.0,147.6 204.0,148.3 216.0,148.8 228.0,149.2 240.0,149.4 252.0,149.6 264.0,149.7 276.0,149.8 288.0,149.9 300.0,149.9"/>
  <polyline fill="none" stroke="var(--accent, #059669)" stroke-width="2" opacity="0.35" points="60.0,30.0 72.0,42.0 84.0,52.8 96.0,62.5 108.0,71.3 120.0,79.1 132.0,86.2 144.0,92.6 156.0,98.3 168.0,103.5 180.0,108.2 192.0,112.3 204.0,116.1 216.0,119.5 228.0,122.5 240.0,125.3 252.0,127.8 264.0,130.0 276.0,132.0 288.0,133.8 300.0,135.4"/>
  <text x="118" y="142" style="font-family: var(--serif, serif); font-size: 12px;" fill="var(--fg, #e6e4df)">L = 0.5</text>
  <text x="200" y="142" style="font-family: var(--serif, serif); font-size: 12px;" fill="var(--fg, #e6e4df)" opacity="0.8">0.7</text>
  <text x="252" y="120" style="font-family: var(--serif, serif); font-size: 12px;" fill="var(--fg, #e6e4df)" opacity="0.6">0.9</text>
  <line x1="360" y1="20" x2="360" y2="160" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.35"/>
  <line x1="360" y1="160" x2="545" y2="160" stroke="var(--fg-soft, #999)" stroke-width="1" opacity="0.35"/>
  <line x1="360" y1="110" x2="545" y2="110" stroke="var(--fg-soft, #999)" stroke-width="1.6" stroke-dasharray="4 4" opacity="0.55"/>
  <text x="470" y="126" style="font-family: var(--serif, serif); font-size: 12px;" fill="var(--fg-soft, #999)">L = 1</text>
  <polyline fill="none" stroke="#e05252" stroke-width="2" opacity="0.9" points="360.0,110.0 369.2,108.0 378.4,105.9 387.6,103.7 396.8,101.4 406.0,98.9 415.2,96.4 424.4,93.7 433.6,90.9 442.8,87.9 452.0,84.8 461.2,81.6 470.4,78.2 479.6,74.6 488.8,70.8 498.0,66.8 507.2,62.7 516.4,58.3 525.6,53.7 534.8,48.9 544.0,43.9"/>
  <text x="470" y="38" style="font-family: var(--serif, serif); font-size: 12px;" fill="#e05252">L = 1.05</text>
  <text x="366" y="180" style="font-family: var(--sans, sans-serif); font-size: 11px;" fill="var(--fg-soft, #999)">busy, and getting worse</text>
</svg>
<figcaption style="font-size: 0.85em; color: var(--fg-soft, #999); max-width: 34em; margin: 0.4rem auto 0;">Left: any L below one converges, and how fast matters far less than whether. Right: at L = 1 the loop is pure activity, and just above it the loop is actively destructive while producing a diff every round.</figcaption>
</figure>

The practical consequence is a bound rather than a policy. If you cannot show that your loop contracts, cap the iterations and escalate when the cap hits. That is the correct move under an unknown L, not a concession, and L is unknown far more often than people assume.

## What I still can't measure

ε is countable: failures per step, over enough runs, and you have it. L is the part I have not solved. Measuring contraction needs a distance to the goal, something you can evaluate against the target every iteration, and a diff between consecutive outputs is not that.

The diff is what everyone reaches for, though. A common convergence heuristic compares iteration N against N−1 and declares convergence when the change drops below a threshold. I have used it. It cannot distinguish a loop that arrived from a loop that got stuck, which are exactly the two cases at issue: a confidently wrong output that stops changing passes with full marks.

My best candidate is unglamorous. Where a loop already checks deterministic criteria, the satisfaction rate across iterations *is* a distance to the goal, and its slope from round one to round two estimates L. No new instrumentation, just arithmetic over logs that already exist. I have not run it yet.

Mathematics needed 2,300 years to stop trusting the searcher. It never had to think hard about whether its checking improves the next attempt, because there it always did: a rejected proof comes back with a reason attached, and the next version answers that reason. The feedback is so natural nobody bothered to name it.

We have to name it. The last post asked what would catch your agent being wrong. One more question, in the same breath: when it catches something, does the next round know?

---

*A follow-up to **The Verification Changelog**, five posts on 2,500 years of verification history.*  
[← The Untrusted Searcher](/posts/the-untrusted-searcher/)
