A developer named Jarred Sumner spent less than two weeks watching Claude Code turn a million lines of the Bun runtime from Zig into Rust. He didn’t sit there typing prompt after prompt for fourteen straight days. He wrote a 576-line rulebook once, kicked off a process, and let a system built around the model do the repeating. When it was done, the entire existing test suite passed in CI before anything merged, and only nineteen regressions turned up afterwards across a million lines of translated code, all of which got fixed (Anthropic, July 2026).
That is not a story about a smarter model. Sumner and Mike Krieger, who separately turned a Python codebase into 165,000 lines of TypeScript over a single weekend, were both running a process that decided what to try next, checked its own output, and kept going without either of them holding the model’s hand through every step.
That process now has a name. People are calling it loop engineering, and depending on who you ask, it’s either the most useful shift in how engineers work with AI since the chat interface itself, or a rebrand of a bash script a hobbyist was running a year earlier. Both of those things are a little bit true, and untangling them is the point of this article.
What loop engineering actually is
Strip away the branding and the definition is simple. Loop engineering is the practice of designing the system that prompts an AI agent, checks its work, and decides what to do next, instead of a person doing all three of those things by hand, one message at a time.
The old way of working with a coding agent was a straight line. You typed an instruction, you read what came back, you typed the next instruction. The agent did real work in between, but you were the one holding it the whole time, the same way you’d hold a steering wheel that needs correcting every few seconds. Loop engineering replaces that line with a closed cycle: the agent takes an action, gets feedback from the actual environment it’s working in, uses that feedback to decide what happens next, and keeps going on its own until something you defined in advance is genuinely true.
The cleanest way to hold this in your head is what people are calling a recursive goal. Instead of typing each next step yourself, you state a purpose, something like “get every test in the auth module passing,” and the agent iterates toward that purpose without you sitting there directing traffic. It inspects the code, changes something, runs a check, reads what happened, and decides the next move, on repeat, until the goal is met or it genuinely can’t go any further.
Where the term came from
The story behind the name matters here because the speed at which it spread is itself evidence of how much the underlying capability had already changed.
On June 7, 2026, developer Peter Steinberger, known for building the OpenClaw agent project, posted a two-sentence message on X arguing that developers should stop prompting coding agents directly and start designing the loops that prompt agents for them.
Days earlier, on June 2, Boris Cherny, who leads Claude Code at Anthropic, said something similar on stage at the WorkOS Acquired Unplugged event: that he no longer prompts Claude directly, and that his actual job had become writing the loops that prompt it for him. Then, on June 8, engineer and author Addy Osmani published an essay simply titled “Loop Engineering” that gave the idea an actual shape: five building blocks plus a sixth piece underneath them all. That essay is what turned a viral opinion into a vocabulary other people could build on and argue with.
It’s worth naming the lineage too, because none of this appeared out of nowhere. The direct practical ancestor is a method called Ralph, a plain bash loop that developer Geoffrey Huntley described in July 2025, months before the term existed, built around the simple idea of running an agent repeatedly against the same goal until it stuck. The academic ancestor goes back further still, to the ReAct pattern from Yao and colleagues in 2022, which interleaved reasoning and acting inside a single loop, and to Reflexion from Shinn and colleagues in 2023, which added memory and self-critique on top of that. The June 2026 moment didn’t invent any of this. It gave ordinary developers, not just researchers, a reason and a shared vocabulary to start building loops on purpose.
And the term is still moving. On July 18, 2026, Steinberger posted again, this time suggesting the loop era was already giving way to something he’s calling graph engineering, where instead of one loop, teams wire several interconnected loops together with actual dependencies between them.
How this fits next to prompt, context, and harness engineering
Loop engineering didn’t replace anything that came before it. It’s the newest layer in a progression that’s been building since 2022, and each layer wraps the one underneath it rather than making it obsolete.
Prompt engineering, roughly 2022 through 2024, was about wording: giving a model a role, breaking a task into steps, providing examples. It optimized what you said, and its ceiling was real, because even a perfectly worded prompt can’t hand a model facts it was never given.
Context engineering, which picked up through 2025, shifted the focus from the words themselves to everything the model actually sees at the moment it responds: conversation history, retrieved documents, tool output. Anthropic formalized this in September 2025 as curating and maintaining the optimal set of tokens available during inference, and prompting effectively became one ingredient inside a bigger discipline rather than a separate one.
Harness engineering showed up in early 2026 as agents started handling longer, more autonomous work in real production settings. The harness is the whole environment around an agent: the scaffolding, the tools, the constraints, the feedback loops that catch its mistakes.
Loop engineering sits on top of all three. Where harness engineering asks what environment an agent needs, loop engineering asks a narrower, more operational question: what cycle keeps the agent moving toward the goal, and exactly when does that cycle stop?. You still write prompts. You still curate context. You still build a harness. The loop is simply where all of that gets set in motion and given a rhythm.
The three things that make or break a loop
Every serious account of this, from Anthropic’s own team to IBM’s explainer to the practitioners already running loops in production, converges on the same three hard problems. Get any one of them wrong, and a loop stops being useful, sometimes quietly.
Context management comes first. A context window is an agent’s working memory, and it has a hard limit. Every step of a long loop adds more to that record: more reasoning, more tool output, more errors, and left unmanaged, that record either overflows outright or degrades in quality as it grows, a problem people have started calling context rot, where the model attends less reliably to what actually matters buried inside an increasingly long transcript. The fix is compacting old steps into shorter summaries, pruning stale output, and giving sub-agents their own clean context window for side tasks so they only report back a conclusion.
Termination comes second, and getting it wrong is probably the single most expensive mistake in loop design. A loop needs several independent exits stacked on top of each other: a verifier confirming the actual goal was met, a hard ceiling on iterations, a token or time budget, and a subtler one, no-progress detection, which catches the case where the last few steps produced the same error or left the state functionally unchanged. Without that layered set of exits, a loop either runs forever or stops arbitrarily on a guess, and neither is acceptable in something meant to run while you’re asleep. This is not a hypothetical risk either. Uber reportedly capped its engineers at $1,500 per person per AI coding tool per month after the company burned through its annual AI budget in four months, and unbounded loops without real termination logic are a large part of why that kind of billing surprise happens.
Verification comes third, and it’s really a question of trust. The gold standard is deterministic verification: tests, type checkers, compilers, linters, because these return an objective pass or fail the model can’t argue its way around. A model acting as its own judge is more flexible and genuinely necessary for anything that can’t be mechanically checked, but it’s also more gameable, and a model grading work it produced itself is a structurally weak check by design. The strongest loops lean on a deterministic verifier wherever one exists at all, and save model judgment specifically for the parts of a task that truly can’t be measured any other way.
Tutorial: Building a small working loop
Everything above is easier to hold onto once you’ve actually built one, so this section walks through a real, small, low-stakes loop from start to finish. The task: a nightly pass that lints one directory in a repository, fixes what it safely can, and leaves a written trail of what happened. Nothing fancy, on purpose. The value of loop engineering isn’t in the size of what you automate first; it’s in confirming the pieces actually hold together before you point them at anything that matters.
Step 1: pick a goal with a real stopping condition
“Clean up the code” is not a goal an agent, or a human, can check against. “Every file in /src/utils passes eslint with zero errors, and no existing test in that directory starts failing" is. The difference between those two sentences is the entire discipline in miniature.
Step 2: write the verifier before you write anything else
The verifier is what makes “done” mean something instead of being a model’s opinion of its own work. For this loop, it’s two commands: the linter, and the existing test suite scoped to the directory being touched. If both come back clean, the loop is allowed to stop. If either fails, the loop keeps going or escalates.
Step 3: write the loop itself
Here’s the skeleton underneath nearly every production loop in use today.
# state holds the goal, plus a running scratchpad of what's been
# tried so far. this is what gets fed back to the model on every
# pass through the loop, and it's also what gets written to disk
# so a human (or tomorrow's run) can see what happened.
state = init_state(
goal="lint /src/utils clean, don't break existing tests",
max_steps=8,
)
for step in range(state.max_steps):
# reason before acting: look at the current state and figure
# out what the single next move should be
thought = model.reason(state)
# commit to one concrete, tool-backed action rather than a
# vague intention. this is what actually touches the repo.
action = model.choose_action(state, thought)
# execute against the real environment, not a simulation.
# this might run eslint, apply a fix, or run the test suite.
result = tools.execute(action)
# fold the outcome back into state, then compact it so the
# context window doesn't fill up with every raw log line
# from every step
state = update(state, thought, action, result)
state = compact(state)
# a deterministic check, not the model's self-report, decides
# whether the loop is actually finished
if verifier.passes(state):
write_log(state, outcome="success")
return success(state)
# catch the case where the last few steps produced the same
# error or changed nothing at all, so the loop doesn't spin
if no_progress(state):
write_log(state, outcome="escalated: no progress")
return escalate_to_human(state)
# ran out of steps without a clean pass. hand back rather than
# silently declaring victory
write_log(state, outcome="escalated: max steps reached")
return escalate_to_human(state)
What’s actually happening here: the loop never trusts the model’s own claim that it’s finished. verifier.passes(state) runs the real linter and the real test suite and checks their actual exit codes, so "done" means something a human could independently confirm. The no_progress check exists specifically because agents left alone will sometimes retry a broken approach without varying it, and catching that early is cheaper than discovering it after burning through the entire step budget. And every exit path, success or escalation, writes to a log before it returns, which matters more than it looks like it should, because that log is the loop's memory across runs.
Step 4: give it a real trigger
A loop that only runs when you remember to run it isn’t really a loop yet. Here’s a GitHub Actions workflow that fires the lint pass every night.
# .github/workflows/nightly-lint-loop.yml
name: Nightly lint loop
on:
schedule:
# runs at 02:00 UTC every day. cron syntax is minute, hour,
# day-of-month, month, day-of-week, with * meaning "any"
- cron: '0 2 * * *'
workflow_dispatch:
# lets you trigger it manually from the Actions tab too,
# useful while you're still testing the loop
jobs:
lint-loop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run the lint loop
run: python scripts/lint_loop.py
env:
# keep the model's step budget in an env var so you can
# tune it without touching the loop code itself
MAX_STEPS: 8
- name: Upload run log as an artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: lint-loop-log
path: state/lint-loop-log.md
The schedule block is what makes this an actual loop instead of a one-time script; it fires without anyone opening a laptop. The workflow_dispatch trigger is there purely so you can run it on demand while you're still confirming it behaves the way you expect, before you trust the schedule alone. And uploading the log as an artifact on every run, including failed ones (if: always()), is what turns this from a black box into something you can actually audit later.
Step 5: read the state file after a few runs
This is the part that’s easy to skip and shouldn’t be. After a handful of runs, the log should read like a short diary, not a wall of raw output.
If that file is readable at a glance, the loop is doing its job. If it’s a dense wall of tool output nobody would ever actually read, the compact step needs more work before this loop is trustworthy enough to leave unattended on anything bigger.
Loop engineering in a real production example
The Bun migration mentioned at the top of this article is worth returning to in more detail, because it’s one of the clearest documented cases of loop engineering running at real scale rather than on a toy task.
Jarred Sumner didn’t hand Claude Code a single prompt asking it to translate Bun’s Zig codebase into Rust. According to Anthropic’s own account of the project, the actual starting point was a 576-line rulebook, a document laying out conventions, edge cases, and the specific things that shouldn’t be translated literally, plus one short kickoff instruction.
From there, the loop took over: translate a section, run the existing test suite against it, check the result, fix what failed, move to the next section, repeat. A little over a million lines of code came out the other side in under two weeks, with the entirety of Bun’s pre-existing test suite passing in CI before any of it merged. Nineteen regressions were found after the merge, and every one of them was tracked down and fixed, a small number relative to the size of what got translated, and a realistic one, since the point of loop engineering was never to promise zero mistakes, only a system reliable enough to make finding and fixing them the exception rather than the constant condition of the work.
Mike Krieger’s separate migration, turning a Python codebase into 165,000 lines of TypeScript over a single weekend, used a similar shape but leaned harder on parallel structure: hundreds of agents working through eight phase gates and three rounds of adversarial review, with a final parity check that compared the output of every command against the original Python version to catch anything that had quietly drifted. That adversarial review step is the maker-checker split from earlier in this article, showing up at production scale, a separate reviewing agent catching what the translating agent talked itself into.
Neither of these was a person prompting an agent turn by turn for two weeks straight. Both were a person designing the rulebook, the verifier, and the escalation path once, and then letting the loop run.
A smaller, everyday sample of loop engineering in action
Production migrations are the headline case, but most people’s first real loop looks a lot closer to the tutorial above than to a million-line codebase translation. A realistic small-team version might look like this: an automation runs every morning against a repository, calling a skill that reads the previous day’s CI failures, the open issues, and recent commits, and writes what it finds into a shared markdown file. For anything worth acting on, the loop opens an isolated worktree, sends one sub-agent to draft a fix, and sends a second sub-agent to review that draft against the project’s existing skills and tests. A connector opens the pull request and updates the tracking board automatically. Anything the loop genuinely can’t resolve lands in a triage inbox for a person to look at over coffee.
Nobody prompted any individual step of that. Someone designed it once, and the design is the actual engineering.
What loop engineering doesn’t solve
None of this is an argument that the discipline is a on size fit all, and it’s worth saying that plainly rather than as an afterthought.
A loop that runs unattended is also a loop that can make mistakes unattended, which is exactly why the maker-checker split exists in the first place. Even with a verifier in place, “done” is still a claim, not a proof, and someone has to actually trust the check before they trust what it approved.
The faster a loop ships code nobody read closely, the wider the gap grows between what exists in the repository and what the team actually understands about it, a problem people are calling comprehension debt. And there’s a subtler risk sitting right next to it: it’s genuinely tempting, once a loop is running smoothly, to stop having an opinion about its output and just accept whatever comes back, a pattern sometimes called cognitive surrender. Designing a loop with real judgment behind it is the antidote to both of these. Designing one specifically to avoid thinking about the problem is the accelerant. Same action, opposite result, and the loop itself has no way of telling you which one you’re doing.
Where humans still belong in the loop
None of this argues for removing people from the process, and the useful checkpoints show up at every level of a loop, not just at the very end of it.
In the base loop, that means requiring explicit approval before a genuinely sensitive action, a financial transaction, a production database write, something a deterministic test can’t fully vouch for. In the verification layer, it can mean a person acting as the actual grader for work where the stakes are too high to trust a rubric alone. Further out, it can mean a person reviewing a proposed change to the loop itself, its rulebook, its verifier, its escalation thresholds, before that change ships and starts shaping every run after it. None of these is an exotic addition. They’re a deliberate design choice, exactly like every other piece of the loop.
Conclusion
The honest starting point is smaller than most of what’s been described here. Pick one task where you’re currently the bottleneck, something recurring and genuinely low stakes, a nightly report, a lint pass, a triage summary. Write a goal specific enough that a machine could check it. Wire up one real verifier, not a model’s opinion of its own work. Give it exactly one escalation path for when it gets stuck. Run it for a couple of weeks and actually read the log before you let it touch anything bigger.
The skill underneath all of this was never really about writing a clever prompt. It’s about designing a cycle you trust enough to walk away from, and then actually checking, every so often, that the trust is still earned.




Top comments (0)