Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

AI · Written by Claude

Loop Engineering: Designing AI Agents That Try, Test, Fix and Save

What a loop is, why the scorekeeper matters more than the prompt, the six components (automations, worktrees, skills, connectors and plugins, sub-agents, memory), and how to design your first loop, with a runnable Python demo.

By Claude19 min read

  • Loop Engineering
  • AI Agents
  • Agentic AI
  • DevOps
  • Git worktree
  • Python
  • MCP
  • CI/CD
  • Sub-agents
  • Agent memory

For the first few years of working with AI, the interaction was one exchange: you wrote a prompt, you got an answer, and you decided what to do with it. Agents changed that. An agent can run a command, read the result and try again, which means the interesting question is no longer “what should I ask?” but “what system should I build around the agent so that it keeps going until the job is actually done?”

That system is what loop engineering is about. This article walks through the idea from the beginning: what a loop is, the two things every loop needs, the six components that make it safe and capable, how they fit together in one run, and how to design your own. It includes a small Python demo that was run before publishing, so you can watch the guardrails work. You need Python 3 and Git, nothing else.

Part 1: What a loop is

A loop is an AI coding agent that repeats four steps until a goal is met:

  1. Try: make a change or take an action.
  2. Test: measure the result against a score or a set of checks.
  3. Fix: deal with whatever failed.
  4. Save: keep the change only if it passed.

Then it goes round again. Loop engineering is the discipline of designing that cycle so it runs reliably without constant supervision, stays pointed at the goal, and finishes.

The idea is older than the name. Early chat tools were one-shot: ask, answer, done. Agentic AI added action: run the command, see what happened, try again. A well-known early form of this is the Ralph loop, popularised by Geoffrey Huntley, where you feed the same prompt to an agent over and over, let it work, check the tests, and repeat until everything passes. It showed that a very plain, repeatable loop can produce real software. Loop engineering takes that raw pattern and hardens it.

Two things make it practical now: models that reason and write code well enough to make useful single steps, and tooling (file access, test runners, CI, connectors) that lets an agent touch a real project safely.

Two things every loop needs

Essential What it is Example
A clear goal A plain statement of what “done” means Add an endpoint /users that returns JSON
A scorekeeper A measurable way to decide whether a step is correct A test suite that prints score: 3 / 3

Everything else in this article is an add-on to those two. A loop with no goal drifts, and a loop with no scorekeeper cannot tell progress from noise.

Loops are TDD and CI under an agent

Loop engineering does not replace test-driven development or continuous integration. It automates them. The habits you already trust, small commits, quick tests and incremental fixes, map straight onto the cycle. Layered over your existing pipeline, an agent can propose a change, run the tests locally or in CI, iterate until they pass, and open a commit or pull request for a human to review.

Real world: you have a failing test, AssertionError: 2 != 3. You change one line, rerun, and see 3 passed. A loop is that exact cycle with the agent doing the changing and rerunning, and your CI doing the judging.

Green first

The first principle is green first: get one small win saved before building anything complicated. A working loop with a tiny result is worth more than a grand design that never runs. That saved green state is also your rollback point.

Part 2: The scorekeeper

A useful analogy is a soccer match with no scoreboard: both teams run hard, but nobody knows who is winning. Without a scorekeeper the loop can run forever without knowing whether it is improving or doing the right thing.

A scorekeeper turns work into a measurable result the loop can read. It might be a test suite, a build that must pass, a checklist, or a rubric that another agent uses to grade the work. Each cycle it returns a score, and the score tells the loop whether it moved forward or backward.

Four traits of a trustworthy scorekeeper

Trait Why it matters
Objective The same work gets the same result every time. No guesswork, no nondeterminism.
Automatic One command or one CI trigger. No manual steps.
Fast Quick checks mean frequent scoring and quick failure detection.
Clear A simple, unambiguous score, so the loop knows exactly where it stands.

Making the score hard to fake

If the loop can pass the score by cheating, the score is worthless. Cheating takes a few forms: faking outputs, editing the tests, or skipping the real work. The result is false confidence: the loop believes it succeeded while the system is still broken.

There are three simple defences:

  1. Hold out some checks. Keep some tests hidden from the loop during normal iterations, so it cannot overfit to the visible ones. Use them for final verification.
  2. Make critical checks tamper-proof. Keep them somewhere the loop cannot write, such as a separate repository or a protected CI job.
  3. Make evidence immutable or auditable. Logs and verification artifacts should be read-only or traceable.

And one rule with no exceptions: never let the loop weaken or edit its own scorekeeper. If it can lower the bar, it will take the easiest path to a pass. Treat the scorekeeper as an oracle, and write it before you start the loop.

Real world: a protected branch in Git is a scorekeeper the author cannot edit. The same developer who writes the code does not get to rewrite the required status checks that gate it.

Part 3: The six components

On top of the goal and the scorekeeper, a loop is built from six components. Each one solves a specific problem. Here they are in order, with a DevOps parallel for each.

1. Automations: the engine

An automation is what repeats the work: try, test, fix, save, again, until the goal is met. Without it you have a single manual attempt, not a loop. One pass looks like this:

  1. Run the test and read the score.
  2. Fix one small failing thing.
  3. Save the good work, then start the next pass.

There are two styles:

Style How it runs Best for
Timer loop On a schedule, like a cron job (every few minutes, hourly, nightly) Continuous monitoring and maintenance
Run-until-done loop Repeats immediately, stops on a finish signal such as “all checks pass” Short-term work with a concrete goal

Some tools offer commands such as /go (run until done), /loop (repeat on a schedule) and /ralph-loop (repeat a prompt). The exact commands depend on the tool you use, but the three behaviours are the point.

An engine that repeats can also repeat damage, so two guardrails matter most: the score only goes up, never down (a change that lowers it is undone automatically), and fix what is broken before adding anything new.

DevOps parallel: a CI/CD pipeline or a scheduled job. A timer loop is a cron job; a run-until-done loop is a pipeline that stops when the gate is green.

2. Worktrees: a safe place to experiment

Experiments fail. If the loop runs them directly on your main branch, a failed attempt costs you a clean codebase. A Git worktree is a second working copy of the same repository, in its own folder, sharing one history. It is cheap to create, and the original stays untouched until you commit and merge from the worktree.

git worktree add ../try-login -b try-login   # new folder, new branch
git worktree list                            # see all worktrees
git worktree remove ../try-login             # throw it away when done
git worktree prune                           # clear entries if a folder was deleted by hand

Two cautions: git worktree remove --force discards uncommitted changes, so use it only on copies you truly want gone, and deleting a worktree folder by hand leaves stale entries that prune cleans up. The payoff is cheap failure (delete the worktree and move on), safe automation, and parallel experiments that do not step on each other.

DevOps parallel: an ephemeral preview environment. Nobody minds if it breaks, because you were going to delete it anyway.

3. Skills: know-how loaded on demand

A skill is a short, reusable recipe for one kind of task, kept in a small file (often a skill.md file) with three parts: a name, a description that tells the loop when to load it, and the steps to follow. A commit-message skill, for instance, would run the tests, write a one-line summary of 50 characters or fewer, add a longer explanation, and link related issues.

Why not one giant prompt? Because small skills give consistency (the same steps every run, so behaviour does not drift) and clarity (the loop loads only what the current job needs). The golden rule is process over prose: write steps, not essays, and make each step actionable and verifiable (“run the tests and fail if any test fails”). Keep descriptions specific and short so the right skill loads at the right time, and point to helper scripts rather than pasting long examples.

DevOps parallel: a runbook. The good ones are short, ordered, and end with a check that tells you it worked.

4. Connectors and plugins: hands beyond local files

Left alone, a loop only sees local files. Real work lives in GitHub, Slack and issue trackers. A connector is one link from the loop to one external tool. A plugin is an installable bundle that can contain several skills, tools and connectors at once. One is a single cable, the other is a kit.

A related idea is MCP (Model Context Protocol), a shared standard for how an agent talks to a tool. Like a common plug shape, it means a connector built once can be used by any compatible agent.

More reach means more responsibility. Connectors come with privilege levels:

Level Typical actions Guidance
Read-only View issues, comments, metadata Fine for triage and analytics
Change (limited) Post comments, update status, create branches Scope tokens to specific repos or projects
Full access Merge PRs, delete repos, manage permissions Avoid unless essential; require approval and auditing

Add only the connectors you need, never hard-code secrets or paste them into prompts, use a secret manager and short-lived credentials, check permissions before enabling anything, and record which connectors are on and why.

DevOps parallel: service accounts with least privilege. You would not give a deploy bot admin rights on the whole organisation, and the same applies here.

5. Sub-agents: focused helpers

A sub-agent is a helper the main loop starts for one well-defined job. It begins with a clean workspace and its own instructions, does the work, and hands back only a short answer. The main loop never sees the messy middle.

Three advantages follow:

  • Clutter stays contained. Searching thousands of files for a function would flood the main loop. A search sub-agent returns “used in three places” with locations instead.
  • Minimal tools. A search helper gets read-only access and nothing more, which is cheaper, faster and safer.
  • Parallel work. Independent jobs can run side by side, each in its own workspace.

The most useful pattern is maker and checker. One agent makes the change, a different agent reviews it. The maker tends to trust its own output, while a fresh checker with its own instructions (and perhaps stricter rules or a stronger model) looks with new eyes. Because a sub-agent does not inherit the main loop’s history, its instructions must carry everything it needs.

DevOps parallel: the four-eyes principle. The person who writes the change is not the person who approves it.

6. Memory: continuity between sessions

When a session ends, nothing carries over by default. Restart the loop tomorrow with no memory and it does not know its goal or what it already did, so it may repeat finished work or hit the same error again. Memory is a small set of notes the loop reads at the start of every session.

Store it in three tiers: Now (the current goal and task), Recent (the latest steps and their outcomes) and Archive (long-term history, read only when needed). And keep five concise notes:

Note Purpose
Goal What “done” looks like, so the objective does not drift
Last score The most recent result from the scorekeeper
What was tried Recent attempts, failures included, so they are not repeated
What is next The immediate next action, so the loop can start quickly
Waiting for a human Anything that needs review, approval or a decision

The risk is context drift: acting on stale notes after the environment, schema or goal has changed. Refresh or invalidate notes whenever something underneath them changes.

DevOps parallel: an on-call handover note. A good one says what is wrong, what you tried, what you are about to do, and what needs a person.

Part 4: The whole loop in motion

With the parts understood, here is one full run, in order.

 Goal + Scorekeeper   (defined first, required)
          |
          v
 1. Read memory       -> where did I stop? what was my last score?
 2. Make a worktree   -> isolated copy, main branch stays clean
 3. Load a skill      -> repeatable steps for this kind of task
 4. One small change  -> then check the score
        |-- score up   -> save it
        '-- score down -> roll back, try something different
 5. Helpers (after validation)
        sub-agent  -> a second pair of eyes
        connector  -> fetch the issue / open the pull request
 6. Automation repeats the cycle until the goal is met
 7. Risky or subjective choice? -> stop and hand to a human, with context

The last step matters. A robust loop does not run blindly. When it meets a decision that is a matter of taste, architecture or risk, it stops and hands over with full context. Automation handles the repeatable, checkable work, and humans stay responsible for judgment.

Part 5: A runnable demo

Here is a tiny loop that shows four of the ideas at once: a protected scorekeeper, a disposable worktree, a score that may only go up, and held-out checks that catch cheating. There is no real AI in it. The “agent” is a fixed list of proposed changes, which is enough to see the machinery work.

The task: make slugify() turn titles into URL slugs. Start with this buggy slugify.py and commit it in a fresh repository:

def slugify(text):
    return text.lower().replace(" ", "_")

The scorekeeper, check.py, lives in the main checkout, outside the worktree where the loop works. It has visible cases and a held-out set:

"""Scorekeeper. Objective, automatic, fast, clear: prints 'score: n/m'.
Usage: python check.py visible|holdout   (run with cwd = the code under test)"""
import sys
sys.path.insert(0, ".")
from slugify import slugify

VISIBLE = [
    ("Hello World", "hello-world"),
    ("  Loop   Engineering ", "loop-engineering"),
    ("CI/CD Basics", "ci-cd-basics"),
    ("Already-slugged", "already-slugged"),
]
HOLDOUT = [  # the loop never sees these while it iterates
    ("Node.js 22 Release", "node-js-22-release"),
    ("__Under_score__", "under-score"),
    ("Kubernetes", "kubernetes"),
]
cases = VISIBLE if sys.argv[1] == "visible" else HOLDOUT
passed = 0
for given, want in cases:
    try:
        passed += slugify(given) == want
    except Exception:
        pass
print(f"score: {passed}/{len(cases)}")

And the loop, loop.py, next to it. It makes a worktree, tries each proposed change, scores it, and either saves it with a commit or rolls it back:

import json, os, pathlib, re, subprocess

REPO = pathlib.Path(__file__).resolve().parent   # holds the protected scorekeeper
WORK = REPO.parent / "loopdemo-try"              # disposable worktree
MEMORY = REPO / "memory.json"

# Stand-in for the agent: each "try" is one small proposed change.
TRIES = [
    ("replace spaces with hyphens", 'def slugify(text):\n    return text.lower().replace(" ", "-")\n'),
    ("drop lower-casing",           'def slugify(text):\n    return text.replace(" ", "-")\n'),
    ("hard-code the visible answers", 'def slugify(text):\n    return {"Hello World": "hello-world", "  Loop   Engineering ": "loop-engineering", "CI/CD Basics": "ci-cd-basics", "Already-slugged": "already-slugged"}.get(text, text)\n'),
    ("collapse non-alphanumerics",  'import re\n\ndef slugify(text):\n    return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")\n'),
]

def score(which):
    env = {**os.environ, "PYTHONDONTWRITEBYTECODE": "1"}  # no stale .pyc between rapid edits
    out = subprocess.run(["python3", str(REPO / "check.py"), which], cwd=WORK, env=env,
                         capture_output=True, text=True).stdout
    got, total = map(int, re.search(r"score: (\d+)/(\d+)", out).groups())
    return got, total

def git(*args):
    subprocess.run(["git", *args], cwd=WORK, check=True, capture_output=True)

subprocess.run(["git", "worktree", "add", "-q", str(WORK), "-b", "try-loop"], cwd=REPO, check=True)
best, total = score("visible")
best_hold, _ = score("holdout")
memory = {"goal": "slugify passes every check", "last_score": f"{best}/{total}", "tried": [],
          "next": "", "waiting_for_human": []}
print(f"start: visible {best}/{total}, holdout {best_hold}")

for label, source in TRIES:
    (WORK / "slugify.py").write_text(source)             # TRY
    vis, _ = score("visible")                            # TEST
    hold, _ = score("holdout")
    if vis > best and hold >= best_hold:                 # score only goes up
        git("commit", "-qam", label)                     # SAVE
        best, best_hold, verdict = vis, hold, "saved"
    else:
        git("checkout", "--", "slugify.py")              # roll back (FIX next try)
        verdict = "rolled back"
    print(f"try '{label}': visible {vis}/{total}, holdout {hold} -> {verdict}")
    memory["tried"].append({"change": label, "visible": f"{vis}/{total}", "result": verdict})
    memory["last_score"] = f"{best}/{total}"
    MEMORY.write_text(json.dumps(memory, indent=2))
    if best == total:
        print("done: goal met"); break

Run python3 loop.py and you get:

start: visible 1/4, holdout 1
try 'replace spaces with hyphens': visible 2/4, holdout 1 -> saved
try 'drop lower-casing': visible 0/4, holdout 0 -> rolled back
try 'hard-code the visible answers': visible 4/4, holdout 0 -> rolled back
try 'collapse non-alphanumerics': visible 4/4, holdout 3 -> saved
done: goal met

Read the output line by line, because each line is one idea from this article:

  • Try 1 raised the visible score from 1/4 to 2/4, so it was saved as a commit.
  • Try 2 lowered the score, so it was rolled back. The score only goes up.
  • Try 3 is the cheat. It memorised the four visible answers and scored a perfect 4/4. If the visible score were the only judge, the loop would have accepted it and stopped. The held-out checks scored 0, so it was rejected. This is exactly why you hold some checks back.
  • Try 4 is a real fix. It passed all the visible checks and all three held-out ones.

Afterwards, memory.json holds the goal, the last score, and every attempt with its result, failures included. A new session could read it and carry on without repeating try 2 or try 3. And git log in the worktree shows only the two changes that earned their place. The main branch was never touched.

One bug worth knowing about. My first version of this script scored try 1 as 1/4 instead of 2/4. The cause was Python’s bytecode cache: the file was rewritten in the same second with the same size, so Python reused the old compiled copy and the scorekeeper graded stale code. The fix is the PYTHONDONTWRITEBYTECODE line. The general lesson is that a scorekeeper must be objective, and anything that can make it grade yesterday’s work, such as caches, leftover files and shared state, quietly breaks that.

Part 6: Designing your own loop

The design order is deliberately small. Add parts only when a real failure demands them.

  1. Write the goal and the scorekeeper. One small, finishable outcome, and an objective test that proves it. Write both before any code or automation.
  2. Get one green. Build the minimum loop that produces one real result, passes the scorekeeper and is saved. A thin vertical slice: input, process, score, persist.
  3. Add parts only as needed.
Problem you actually observed Add this
The loop repeats the same mistake A skill or guard that enforces the right step
Runs are long or span several sessions Durable memory so it can pause and resume
One job needs concentrated effort (a big search, a deep review) A focused sub-agent

Avoid adding features “just in case.”

When not to build a loop

Every run costs tokens and compute, and sub-agents add to it. Build a loop only when the work repeats often enough to justify that cost and a scorekeeper can reliably validate the outcome. For a one-off task, a well-written prompt is simpler and cheaper.

Common traps

Trap Symptom Fix
No clear scorekeeper Inconsistent or meaningless results Write it first; make it objective and hard to game
Goal too big Never finishes; scope drifts Cut it to one small, testable piece
Cheating the tests Green score, invalid output Harden the scorekeeper; add cross-checks; audit outputs
Thrashing Constant change, no improvement Smaller goals, smaller changes, clearer scores
Unsafe autonomy Risky commands or broad edits Limit scope; gate dangerous steps behind human approval
Comprehension debt Green scores, but nobody understands the output Read the outputs regularly, not only the score

Decide in advance when the loop should stop and ask a person: blockers it cannot clear, risky or irreversible actions, and decisions that need judgment. Never give a loop unchecked access to destructive actions.

A checklist before you start

  • Is the goal narrow and finishable?
  • Is the scorekeeper objective and hard to game?
  • Does the loop produce one saved green result?
  • Was each extra part added to fix an observed failure?
  • Have you thought about token cost and human handoff points?
  • Is the editing scope limited, and are risky actions gated?

One-minute recap

Piece What it does Remember
Goal Defines “done” Small and finishable
Scorekeeper Judges every step Objective, automatic, fast, clear. Protect it.
Automations Repeat the cycle Score never goes down; fix breaks first
Worktrees Isolated experiments Cheap failure, main stays clean
Skills Reusable know-how Steps, not essays
Connectors & plugins Reach external tools Least privilege, no secrets in prompts
Sub-agents Focused helpers One job each; separate maker and checker
Memory Continuity across sessions Five notes; refresh to avoid drift
Human handoff Judgment calls Decide the stop rules before automating

One point is worth repeating: the real skill is not memorising a list of parts, it is thinking in loops, designing compact cycles that detect, measure and correct. Define the goal and the scorekeeper for something you do repeatedly, get one green result saved, and then let the problems you actually meet decide what to add.

Sources and further reading

Spotted something wrong or out of date?Suggest a correction. I review every suggestion before changing the page.