Loop Engineering Took Over X. The Useful Part Is the Stop Condition
Loop engineering moved from X posts into serious engineering blogs in June. LangChain's stacked-loop framing makes the idea more concrete: agent loops, verification loops, event-driven loops, and trace-driven improvement loops.
Loop engineering became the phrase of the month on X, then immediately escaped into engineering blogs.
Peter Steinberger's line was the spark: you should not be prompting coding agents anymore; you should be designing loops that prompt your agents. Addy Osmani expanded the idea into a practical framework. Firecrawl wrote the operator's version. Armin Ronacher wrote the cautionary version. LangChain added the missing systems framing: loop engineering is not one loop. It is a stack of loops, each with a different job, a different failure mode, and a different place where humans should stay involved.
The discourse got noisy fast, but the useful idea is simple:
A loop is a system that keeps an agent working until an external stopping condition says the work is done.
That external stopping condition is the whole game. Without it, a loop is just a model call in a while statement. With it, the loop becomes an engineering surface you can test, observe, budget, and improve.
Prompting vs. Looping
Prompting is one turn. You ask, the model answers, you decide what comes next.
An agent workflow is a chain. The system has multiple steps and tools, but the route is mostly predefined.
Loop engineering is one level up. You design the system that chooses the next prompt, runs the agent, observes the result, checks progress, records state, and decides whether to continue.
That means the human moves from "person typing the next instruction" to "person designing the control system."
LangChain's Useful Framing: Loops Stack
LangChain's article is useful because it breaks loop engineering into four layers. Each layer wraps the one below it.
1. The agent loop. This is the familiar model-tool loop: call the model with context and tools, execute the requested tool, feed the result back, repeat until the model returns a final answer. This is the minimum viable agent.
2. The verification loop. The first loop gets work done, but it does not guarantee the work is correct. A verification loop runs tests, rubric checks, link checks, type checks, policy checks, or an LLM judge after each attempt. If the output fails, the verifier sends structured feedback back into the agent loop.
3. The event-driven loop. This is where the agent stops being something you manually invoke. A cron schedule, webhook, Slack message, GitHub event, CI failure, support ticket, or database change starts the run. Now the loop is part of a larger application.
4. The hill-climbing loop. This is the most interesting layer. Every run produces traces: model inputs, tool calls, observations, costs, latency, grader feedback, final diffs, and human annotations. A separate improvement loop studies those traces and changes the harness: prompts, tools, middleware, skills, graders, routing, or model selection.
This is the difference between "an agent that loops" and "a system that gets better because it loops." The outer loop is not asking the agent to try again. It is changing the conditions under which future runs happen.
What the Core Loop Actually Looks Like
At the implementation level, a loop needs a contract. I would model it as six things:
- Task spec: the user request plus acceptance criteria.
- State: messages, tool results, files touched, decisions made, and budget consumed.
- Tools: the actions the model is allowed to request.
- Verifier: deterministic checks and/or a judging model.
- Policy: permissions, approval gates, spend caps, retry caps, and scope limits.
- Trace: a structured record of what happened.
A simple version looks like this:
type LoopState = {
task: string;
acceptanceCriteria: string[];
attempts: number;
maxAttempts: number;
tokenBudgetRemaining: number;
changedFiles: string[];
lastVerifierResult?: VerifierResult;
};
async function runLoop(state: LoopState) {
while (state.attempts < state.maxAttempts && state.tokenBudgetRemaining > 0) {
const planOrAction = await callModelWithTools(state);
const toolResults = await executeAllowedTools(planOrAction, state);
state = updateState(state, planOrAction, toolResults);
const verifierResult = await verifyAgainstCriteria(state);
await writeTrace({ state, toolResults, verifierResult });
if (verifierResult.passed) {
return { status: "passed", state };
}
if (verifierResult.noProgress || verifierResult.outOfScope) {
return { status: "halted", state };
}
state.lastVerifierResult = verifierResult;
state.attempts += 1;
}
return { status: "budget_exhausted", state };
}
That skeleton is boring on purpose. The hard part is not the loop. The hard part is making the verifier honest, the tools safe, the trace useful, and the halt conditions strong enough that the system stops before it burns money or makes the code worse.
The Pieces That Keep Showing Up
- Automation: Something wakes up the loop on a schedule or event.
- State: A file, issue, Linear ticket, or database records what has happened.
- Isolation: Worktrees or sandboxes keep parallel agents from corrupting each other.
- Skills/context: The agent reads stable project knowledge instead of guessing it every run.
- Connectors: The loop reaches GitHub, Slack, CI, docs, databases, or production systems.
- Subagents: One agent drafts, another reviews, another investigates.
- Evals: A verifier decides whether the work passed.
- Traces: The system records enough execution detail to debug and improve later.
None of those pieces are new by themselves. The new part is that they are becoming a named engineering layer.
The Stop Condition Is What Separates a Loop From a Bill
The weak version of loop engineering is "run the agent again and hope it gets better."
That is not a system. That is a token burn.
The production version needs hard limits:
- An iteration cap.
- A token or dollar budget.
- A no-progress detector.
- A test or rubric the writer did not grade itself.
- A human approval gate before risky actions.
Without those, a loop can keep making local changes forever. It can add fallbacks, duplicate abstractions, satisfy the wrong test, or convince itself the task is done. The more autonomous the loop gets, the more important the halt condition becomes.
This is why I think the phrase "loop engineering" is useful despite the hype. It forces the right question: what tells the loop to stop?
Strong Stop Conditions Are Usually Composite
A real stop condition should not be one Boolean. It should combine hard constraints, task-specific checks, and change-quality signals.
For a coding loop, I would usually include:
- Budget stop: max wall time, max turns, max tool calls, and max spend.
- Scope stop: fail if changed files drift outside the allowed paths or the diff exceeds a size threshold.
- Progress stop: hash the diff or test output; stop if two or three iterations produce no meaningful movement.
- Correctness stop: tests, type checks, lint, snapshot checks, link checks, or benchmark thresholds pass.
- Spec stop: verifier confirms the implementation addresses the original task, not a nearby easier task.
- Risk stop: destructive operations, data writes, deploys, schema changes, and customer-visible changes require explicit approval.
The important detail is that the verifier should not be the same agent that wrote the patch. The writer is biased toward declaring success. Use deterministic checks where possible, and use a separate grader model only where the thing being checked is semantic: relevance, tone, explanation quality, UX fit, or whether the diff actually answers the issue.
Traces Are the Feedback Layer
LangChain's trace-driven improvement loop is the piece most teams underinvest in.
If you cannot inspect traces, you cannot improve the loop systematically. You will argue from anecdotes. You need records like:
- Run ID, task ID, trigger source, branch, commit, and worktree.
- Model, reasoning effort, prompt version, skill versions, tool list, and routing decisions.
- Every tool call with inputs, outputs, errors, duration, and approval status.
- Token usage, latency, retry count, and cache hit rate.
- Files read, files modified, tests run, and commands executed.
- Verifier result, rubric scores, failure reasons, and human review notes.
- Final outcome: passed, halted, escalated, abandoned, or merged.
That trace becomes the raw material for the fourth loop. You can cluster failures, find prompts that cause tool misuse, identify tools that produce noisy context, detect recurring test failures, and build new evals from real production mistakes.
This is where loop engineering starts to look less like prompt engineering and more like site reliability engineering. You are not just asking "was the answer good?" You are asking "what class of failure did this run represent, how often does it happen, and which harness change would prevent it?"
Event-Driven Loops Need Idempotency
The event-driven layer is where loops become dangerous if you build them casually.
A cron job that wakes every hour and scans issues is fine. A webhook that reacts to every Slack message, GitHub comment, failed CI check, and document update can easily duplicate work, race itself, or write contradictory patches.
Production loops need normal distributed-systems hygiene:
- Idempotency keys so the same event does not start duplicate work.
- Leases or locks so only one loop owns a task at a time.
- Run queues so expensive tasks do not stampede the model provider.
- Worktree or sandbox isolation per run.
- A state store that records whether a task is pending, running, blocked, failed, or complete.
- Backoff rules when tools, APIs, or model providers fail.
This is why "loop engineering" is not just an AI skill. It is application engineering around a probabilistic worker.
Where Loops Actually Work
Loops are strongest when the work is repetitive, observable, and cheap enough to retry:
- CI triage.
- Dependency update attempts.
- Security scan reproduction.
- Benchmark exploration.
- Data cleanup.
- Docs refreshes against changing APIs.
- Porting code where tests provide a clear target.
They are weaker when the output is long-lived architecture that requires taste, ownership, and deep system understanding. You can loop on architecture, but you should not outsource judgment to the loop.
That is the balance Armin Ronacher gets right: loops are powerful, but they can also amplify code you do not understand. The more a loop writes, the more seriously you need to take comprehension debt.
A Practical Production Shape
If I were wiring a repo-maintenance loop today, I would make it look like this:
- A scheduled trigger scans open issues, recent CI failures, dependency advisories, and stale PR comments.
- A triage agent writes a task card with owner, scope, acceptance criteria, risk level, and suggested model.
- A dispatcher starts one isolated worktree per approved task.
- The implementation agent reads project skills, edits files, and must produce a verification plan before changing code.
- A verifier runs tests, checks diff scope, checks the task card, and either passes, fails with feedback, or escalates.
- The loop opens a draft PR only if deterministic checks pass.
- A human reviews anything with high-risk tags: auth, billing, data migration, production config, customer-visible behavior, or security.
- Every run writes a trace. Failed traces become candidates for new evals, prompt changes, tool changes, or skill updates.
Notice the important part: the loop is not "agent writes code until done." The loop is a queue, a state machine, a verifier, a permissions model, and a trace pipeline wrapped around an agent.
My Take
Loop engineering is real, but not because the phrase is new. It is real because the products now expose the primitives: schedules, worktrees, skills, subagents, connectors, evals, traces, and long-running goals.
LangChain's framing makes the useful version clearer. The agent loop automates action. The verification loop automates quality checks. The event-driven loop automates when work starts. The hill-climbing loop automates how the harness improves.
The practical move is not to chase the meme. It is to pick one narrow workflow and make it loop-shaped:
- Define the trigger.
- Define the state store.
- Define the agent's skill/context.
- Define the verifier and rubric.
- Define the stop condition and budget.
- Define the trace schema.
- Define the human approval point.
If you cannot define numbers four, five, and six, do not build the loop yet. You do not have an autonomous system. You have an expensive retry button.
Sources
Bhaulik Patel
Forward deployed AI engineer and creator of Deployed Engineer.