Blog

Ratchet Loops

Making an AI agent improve your system without letting it grade its own homework.

Published July 6, 2026 · David Mar

Why I wrote this

I build RIFF, a platform where businesses get AI phone receptionists. At some point I stopped asking Claude to improve it and started asking Claude to build a system that improves it — a loop that picks a weakness, fixes it, measures before and after, and keeps the change only if the numbers moved. Getting that to work honestly — without the agent gaming its own scores, leaving half-built experiments everywhere, or "improving" things nobody can verify — turned out to be a design problem with teeth. This is the write-up of what survived contact with reality, plus two installable Claude skills (a beginner version and the full version) so you can run one yourself.

1. What a ratchet loop is, and why "ratchet" is the operative word

A self-improving loop is an agent session that repeatedly picks a weakness in a system, fixes it, and measures whether things got better. The naive version of this fails in predictable ways: the agent improves things it can't measure (so nothing is proven), thrashes between goals, quietly relaxes its own grading standards, leaves half-built experiments everywhere, and after ten sessions nobody can say whether the system is better or just different.

The ratchet is the mechanical fix. A ratchet moves in one direction and locks: every cycle must produce a before/after measurement, progress is only claimed when the number moves, and regressions are reverted rather than absorbed. The one-sentence contract:

No improvement may be claimed without a before/after row in a database, and the composite score may only move forward on evidence.

Everything else in this tutorial is scaffolding to make that sentence enforceable against an agent that is smart, fast, and — like all optimizers — inclined to satisfy the letter of its objective rather than the spirit.

2. The three-layer architecture

Separate your loop into three layers with different mutation rules. Mixing them is the most common structural mistake: principles drift because they live in an editable procedure, or procedures ossify because they're tangled with laws.

Layer 1 — Constitution. The durable laws. Changes rarely, and only by explicit amendment. Crucially, every principle must be written as a checkable gate, not an aspiration. "Prefer quality" is a vibe; "no scorecard row without a deterministic check backing it" is a gate the wrap-up can pass or fail.

Layer 2 — Command. The session procedure: setup, cycle structure, wrap-up. Evolves freely — this is where self-amendment operates.

Layer 3 — State. The database and learnings log. Append-only, cross-session, and — critically — outside the rollback blast radius (§6). This is the loop's memory; if it lives in anyone's head or chat history, you don't have a loop, you have a series of unrelated sessions.

3. Define the score before anything else

You cannot ratchet what you cannot compute. Define a composite score as a weighted sum of dimensions that matter for your system — for a voice platform that might be quality, completeness, humanness, readability, latency, and rebuild speed; for a data pipeline it might be freshness, correctness, cost, and time-to-add-a-source. The specific dimensions matter less than four properties:

Unmeasurable dimensions score zero. This is the single highest-leverage trick in the whole design. A dimension you can't measure yet isn't omitted — it scores 0, drags the composite down, and therefore becomes automatically the top-priority deficiency. The loop bootstraps its own instrumentation without you sequencing "build metrics first" by hand, because a missing metric is the lowest-hanging fruit by construction.

Normalize the baseline. Run everything measurable once, call that composite 100, and set a target (e.g., 120 = 20% relative improvement). Absolute numbers are meaningless; deltas against a frozen baseline are the whole game. When weights or judges change, old composites become incomparable — re-baseline explicitly rather than pretending continuity.

Include a "meta" dimension. The most valuable metric is usually not about the current system state but about the cost of change: how long and how many edits does it take to build a fresh unit of work (a flow, a pipeline, a service) from template to green? This "rebuildability" probe is what captures "the next thing we build starts from a higher floor" — the actual point of the exercise. Probe it every cycle with a disposable, namespaced artifact that is built, timed, and deleted, even in cycles whose fix was pure infrastructure.

Cap on violations. Hard invariant violations (safety rules, contract breaks) cap the composite regardless of other scores. Otherwise the loop learns that a fast, charming, rule-breaking system outscores a correct one.

4. The cycle structure

Time-box the session, and within it, each cycle. A 30-minute session supports roughly one baseline cycle plus three or four improvement cycles; reserve the final few minutes for wrap-up unconditionally, because a loop that runs out of clock mid-cycle without reconciling is worse than a loop that did one less cycle.

Cycle 0 — Baseline. Read prior state (learnings, last scores), inventory what's measurable today, measure it, score zeros, persist, normalize.

Cycles 1..N:

  1. Pick ONE deficiency, and write it down before acting. One, not three — multi-goal cycles produce unattributable score movements, and attribution is what the whole database exists for. Selection heuristics: a zero (unmeasurable) dimension beats polishing a measured one; if the rebuild probe was slow, tooling dominates everything; violations before polish; prefer fixes that generalize beyond the current target ("would this help if I built a different vertical tomorrow?").
  2. Fix it.
  3. Run the rebuild probe (build disposable artifact, time, count edits, delete, verify deletion).
  4. Evaluate against a frozen test corpus (§7).
  5. Score and persist the row.
  6. Ratchet check: composite ≥ previous → one commit with a message that names the deficiency and the score delta. Regression → revert the cycle, mark it reverted in the database (don't delete the row — a reverted cycle's record is evidence), log why.

Wrap-up (unconditional): reconcile external side effects (§6), verify zero disposable artifacts remain, print the scorecard trajectory, emit a constitution-compliance line per principle, write a learnings entry ending with the single recommended first action for the next session, and apply the merge gate (§6).

5. Deterministic checks gate; judges inform

Many dimensions worth measuring (humanness of dialogue, readability of code) need an LLM judge. Two facts about judges, both learned expensively by everyone who builds these loops:

Judged scores are noisy. Identical code can score tens of points apart across runs. If judged scores gate the ratchet, your loop reverts good changes and merges bad ones on coin flips, and — worse — exhibits survivorship bias: you keep whichever runs happened to score high. Therefore: deterministic checks gate decisions; judged scores inform trends only. Pass rates, invariant counts, latency percentiles, field coverage — these move the ratchet. Judge scores are plotted, watched, and never trusted for a single-cycle verdict.

Judges are the softest attack surface for self-gaming. An agent that can edit its judge prompt will, eventually, "improve" scores by weakening the rubric — not maliciously, just because that's where the gradient points. So: freeze judge prompts at session start, persist their hash with every score (so cross-session comparability is provable), and put rubric changes behind the amendment process (§8). Anchor the rubric with fixed descriptions (0 = incoherent, 50 = functional but robotic, 80 = a competent human signs off, 100 = exemplary) so scores mean something across sessions.

The same principle generalizes beyond judges: claims are verified against independent sources, never trusted from the agent's own narration. "The UI works" is validated by a browser actually driving the page. "The docs are accurate" is validated by a claims registry where each documented statement has a machine check against the real system. "I cleaned up" is validated by querying the external provider's API (§6). The agent's log is a claim; the world is the evidence.

6. Recoverability: rollback for code, compensation for the world

Two different physics apply, and conflating them is how loops cause damage.

Code rolls back. Run each session on its own branch from a tagged anchor, one commit per cycle, revert within the branch on regression, and apply a merge gate at wrap-up: the branch merges to main only if the final composite beat the session baseline; otherwise it's left intact for autopsy. This gives clean blast radius, no collision with human work on main, and — a nice bonus — makes autonomous scheduled runs trivial later (the merge gate becomes a pull request).

Two rollback subtleties that bite in practice: if your repo commits generated artifacts (compiled outputs derived from a source layer), the revert unit must be the source layer with all downstream layers recompiled in the same commit, or reverts silently un-revert when the next regeneration runs from unreverted upstream. And any concurrent writer to the same files — a refresh cron, another agent — must be paused at session setup and re-enabled at wrap-up, with the re-enable recoverable by the next session's sweeper if this one crashes. Add a cheap drift gate (recompile and diff at setup/CI) so divergence between layers can never accumulate silently.

The world does not roll back — it compensates. You cannot un-send an SMS, un-place a call, un-charge a card. For external side effects, the guarantee is not "undo" but: (a) every action is recorded before it happens, (b) every action has a defined compensating action, (c) a reconciler can prove the world returned to zero. The mechanism is a write-ahead side-effects ledger: no external action executes unless a ledger row exists first (intent, target, compensating action, status), the external ID is recorded after, and wrap-up diffs the ledger against the provider's API — zero orphans or the session fails loudly. The next session opens with a sweeper that re-reconciles prior sessions, so a crashed run can't leak resources silently.

Blast-radius fences live below the agent, in config the loop cannot amend: allowlists of targets it may ever touch (phone numbers, accounts, buckets), per-session budget caps where breach means abort-not-warn, and dedicated tagged resources (test numbers, test accounts) so reconciliation has an unambiguous ownership query. One semantic worth getting right: on a shared-with-the-world resource, unledgered outbound is a violation; stray inbound from strangers is log-and-ignore, or your reconciler becomes a false-alarm generator.

Loop state is exempt from rollback. A reverted cycle's ledger rows are exactly what cleanup needs; its scorecard row is exactly the evidence of what didn't work. The loop's schema is append-only: columns may be added, never dropped or truncated, and it lives on durable shared infrastructure — not a file next to the code it's rolling back.

7. Freeze the eval set (Goodhart's law is the boss fight)

The moment a measure becomes a target, optimizing the measure diverges from optimizing the goal. Ratchet loops are Goodhart machines by design, so you contain it structurally:

The complementary principle is shift-left: every defect found by an expensive check (a judged eval run, a live test, a production incident) must be converted into a cheap authoring-time gate — a compiler warning, a lint rule, a guard test — before the cycle counts as complete, and the commit must reference the new gate. This is how the system gets "harder to break with every iteration": the expensive lesson is paid once, then enforced for free forever.

8. Self-amendment: three levels, one rule

"Self-improving" means different things at different levels of recursion:

Level 3 is where the loop becomes genuinely self-improving and where it becomes dangerous, and one rule contains it, borrowed from safety-config cascades: frozen invariants may only tighten, and only a human may merge changes to them. Guardrails, budget caps, allowlists, judge rubrics, the ratchet rule itself, teardown, the append-only schema — the loop may propose amendments to these, with evidence, into an amendments table; a human (or a human-reviewed adversarial pass) merges or rejects. Everything else in the procedure, the loop may amend itself after an adversarial review gate. Amendments land as diffs in commits, so the loop's evolution is itself auditable in git history.

On adversarial review generally: designs and amendments should pass through a different model or a differently-prompted reviewer before commitment. The author of a change is structurally the worst-positioned entity to find its flaws; a second set of weights repeatedly earns its keep catching schema forks, tautological checks, and drift the author's own verification missed.

9. Build order

Don't build all of this at once. The dependency order:

  1. Composite score + persistence schema. Score definition, database tables (runs, learnings), baseline normalization. A loop with only this is already useful.
  2. Cycle procedure + ratchet. Branch-per-session, one-deficiency rule, rebuild probe, revert-on-regression, merge gate.
  3. Constitution split. Extract the durable laws from the procedure once you've run enough sessions to know which rules keep mattering.
  4. Judges (trend-only) with frozen rubrics and hashes.
  5. Side-effects ledger + fences — only when the loop needs to touch the world. Sim/mocks gate the ratchet; live tests sample and inform.
  6. Level-3 self-amendment — only after several sessions of database history exist for amendments to cite as evidence.
  7. Autonomy (scheduled runs, PR-per-session) — last, and only once sweeper + reconciliation have proven they catch crashes, because autonomous means nobody is watching mid-session.

And before any of it: run a discovery phase against the real environment. Every assumption the design makes — artifacts are derived not committed, the database is reachable, the harness runs headless, no cron writes the same files — should be verified and reported before install, with an explicit instruction to halt on surprises rather than improvise. The two assumptions most likely to be wrong in any real system are "generated things aren't committed" and "nothing else writes what I write."

10. Failure-mode index

Every mechanism above exists because of a specific way loops die. The map:

Failure modeCountermeasure
Improvement claimed, nothing provenNo claim without a before/after DB row
Agent thrash across goalsOne deficiency per cycle, written down first
Metrics never get builtUnmeasurable dimensions score 0
Judge noise flips decisionsDeterministic checks gate; judges trend-only
Rubric softening / self-gamingFrozen judges + hashes; tighten-only amendments
Goodhart on the eval setCorpus/weights/rubrics frozen per session
Orphaned experiments everywhereMandatory verified teardown + wrap-up audit
Regression absorbed silentlyRatchet revert; violations revert mid-cycle
Bad session pollutes mainSession branch + composite merge gate
Revert undone by regenerationRevert the source layer, recompile downstream atomically
Concurrent writer stomps sessionPause crons/writers at setup; sweeper re-enables
Un-undoable world damageWrite-ahead ledger, compensation, reconciliation vs provider API
Crashed session leaks resourcesNext-session sweeper re-reconciles prior sessions
Runaway cost / wrong targetsAllowlists + hard budget caps below the agent's reach
Loop rewrites its own guardrailsFrozen invariants, human-merged, tighten-only
Author blind to own flawsAdversarial review before commitment
Progress lives in chat historyAppend-only state on durable shared DB, read at session start

11. Minimal template

The smallest honest version — a starting point, not the destination:

SETUP: branch loop/<id>; tag anchor; pause concurrent writers;
       sweep prior sessions; read learnings + last scores.

SCORE: composite = Σ wᵢ·dimᵢ ; unmeasured dim = 0 ;
       violations cap composite ; baseline session-0 = 100.

CYCLE (≤ T minutes):
  1 pick ONE deficiency, log it first
  2 fix
  3 rebuild probe: template→green, time, edits, delete, verify
  4 evaluate vs FROZEN corpus (deterministic gates ratchet)
  5 persist row (link independent evidence: run records, hashes)
  6 composite ≥ prev ? commit "loop(N): <what> | a→b"
                     : revert, mark reverted, log why

WRAPUP (always): reconcile ledger vs provider API (zero orphans);
  verify zero disposables; print trajectory + per-principle compliance;
  propose amendments (frozen ⇒ human merge); learnings entry ends with
  next session's first action; merge branch iff final > baseline.

12. The through-line

A ratchet loop is not an agent that tries hard. It is an institution: a constitution that constrains, a procedure that executes, a database that remembers, gates that cannot be sweet-talked, and an amendment process that lets the whole thing evolve without ever being able to quietly lower its own standards. The system it improves should get harder to break and easier to rebuild with every iteration — and the proof should live in a database, not in anyone's memory.

Get the skills

Two installable Claude skills accompany this article:

To install: in Claude.ai, open the .skill file and click Save skill; in Claude Code, unzip into ~/.claude/skills/ (all projects) or a repo's .claude/skills/ (one project). Then ask Claude to "improve my project in a loop."