Post

AI Loop Engineering: Four Loops From a GitHub Issue to a Merged Feature

AI Loop Engineering: Four Loops From a GitHub Issue to a Merged Feature

A few weeks ago I stopped babysitting a pull request and let an agent do it instead.

The task was the boring, exhausting kind every team knows. A PR is open, CI runs, something fails, you fix it, you push, you wait ten minutes for CI to come back, and then the next gate fails. Coverage dropped below the threshold. Checkstyle is unhappy. A flaky test failed on retry. The static analysis tool found one new code smell. Each round is five minutes of work and ten minutes of waiting, and you do it five times before the PR is finally green.

So I wrote a loop. It checks every quality gate on the PR, and if anything is red, it fixes the code, commits, pushes, and waits for the next CI run before checking again. It stops when the PR is fully green and merge-ready, or after ten iterations, whichever comes first. The ten-iteration cap is the important part: it is the difference between “an agent that helps me” and “an agent that burns tokens forever on a problem it cannot solve.”

Then I realized the same idea belonged earlier in the process too. The pull request is the end of the story. Before it exists, there is a whole build phase: turning a spec into tasks, implementing each task, validating, and reviewing. I already run that phase through my specs-driven development framework, so the sensors were already there. Wrapping a loop around them gave me a second loop that drives a feature from spec to a clean, reviewed branch, ready for the first loop to take over.

This post is about that pattern: loop engineering, designing agentic loops that converge on a goal instead of running forever or giving up too early. And it is about how a handful of loops compose into a pipeline that takes a feature from a raw issue all the way to merged, with human checkpoints placed exactly where judgment matters.

Think of a familiar scheduling problem: your application needs to send a report to a client every day at 9 AM. You write the logic that generates the report, then you schedule it to run at 9 AM, and it does the same thing every time. Loop engineering is that same idea, scheduling a repetitive task, except the thing running on that schedule is an AI agent that observes, decides, and acts instead of executing a fixed script.

In this post, we cover:

  • What an agentic loop actually is, and the six parts every good one has
  • A loop shape you can reuse for almost any “drive this to done” task
  • Loop 1, the ship loop: a Claude Code loop that takes a GitHub PR to merge-ready
  • Four different loop shapes (quality gate, coverage climber, dependency upgrade, flaky-test stabilizer) composed inside that loop
  • Loop 2, the build loop: an SDD loop that takes a spec to a clean, reviewed branch with auto-commit
  • Why the build loop is the more advanced one, and the trust you have to earn before you run it
  • Loop 3, the spec-sharpening loop: turning a raw issue into a review-passing spec
  • Loop 4, the review-response loop: working a human reviewer’s comments until none remain
  • How all four compose into an issue-to-merged pipeline with human judgment at exactly two points
  • The guardrails that keep every loop safe, bounded, and cheap

What an Agentic Loop Actually Is

A loop is not “run the agent again.” A loop is a control system. The agent observes the world, compares it to a goal, acts to close the gap, and then observes again. The skill is not in the acting. Coding agents are already good at acting. The skill is in defining what the agent observes and when it is allowed to stop.

Every loop worth building has six parts. If you can name all six for your task, you have a loop. If you cannot, you have a prompt you are running by hand.

  • Goal / success criteria. The precise, checkable definition of “done.” Not “make the PR good.” Something a script could answer yes or no to: all checks green, coverage at or above 90%, zero new static-analysis issues.
  • Sensors. How the agent observes the current state. This is the part people skip, and it is the part that matters most. A loop is only as good as what it can measure. CI status, a coverage report, a Sonar API response, the output of a test run repeated twenty times.
  • Action. What the agent changes when state does not match the goal. Fix the code, write the missing test, bump the dependency, commit, push.
  • Cadence. How often the loop runs. Some loops are event-driven (wait for CI to finish). Some run as fast as the work allows (drain a task queue locally). Picking the wrong cadence either wastes money or misses the signal.
  • Bounded budget. The escape hatch. Maximum iterations, a wall-clock deadline, or a token cap. This is what prevents a loop from grinding forever on a problem it cannot solve. My ship loop stops at ten.
  • Guardrails. The rules about what the agent must not do. Never merge on its own. Never force-push. Never touch files outside the change. Never disable a failing test to make it pass.

Most failed agent loops I have seen fail on the last two. They have a clear goal and good sensors, and then they run away because nobody defined the budget or the guardrails.

The Shape of a Loop

Here is the control flow, drawn out. This same shape works for almost any “drive this artifact to a desired state” task, which is why it is worth internalizing once.

The shape of a loop: observe, check the goal, act, check the budget, wait, repeat

Read it as a sentence: observe the PR, and if every gate is green, stop because it is merge-ready. Otherwise fix the code and push. If we still have iterations left in the budget, wait for the next CI run and observe again. If the budget is exhausted, stop and escalate to a human instead of looping forever.

That escalation branch is not a failure. It is the loop doing its job. A loop that knows when to hand the problem back is more useful than one that pretends it can solve everything.

Loop 1: The Ship Loop, a PR That Drives Itself to Green

Let me make this concrete with Claude Code, because its /loop command and Skills map almost one-to-one onto the six parts above. The mechanics translate to any agent that can run on a schedule and call your CI; the structure is what matters.

The plan is simple. We define a skill that knows how to check and fix one PR’s quality gates (that is the goal, sensors, action, and guardrails). Then we wrap it in a loop that runs the skill on an interval until the PR is green or the budget runs out (that is the cadence and the bounded budget).

A Skill That Knows the Gates

A Claude Code skill is just a markdown file with a little frontmatter and a set of instructions. Here is a trimmed-down version of the idea. The real value is that the success criteria and the guardrails live in the skill as plain English the agent has to follow on every iteration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
---
name: pr-quality-gate
description: Check all quality gates on a GitHub PR and fix anything failing. Use when driving a PR to merge-ready.
---

You are driving pull request #$1 to merge-ready. Do exactly one pass.

## Sensors: gather the current state first
- Run `gh pr checks $1` to get the CI check status.
- Run `gh pr view $1 --json statusCheckRollup,mergeable` for the rollup.
- Pull the coverage report from the latest CI run artifacts.
- Query the Sonar API for new issues introduced by this PR.

## Success criteria (the goal)
The PR is merge-ready ONLY when ALL of these are true:
- Every CI check is passing (unit, integration, architecture tests).
- Checkstyle reports zero violations.
- Line coverage is >= 90%.
- Sonar reports zero new issues on this PR.

If all are true, post a comment `✅ All gates green — ready to merge` and STOP. Do not merge the PR yourself.

## Action: fix exactly what is failing
For each failing gate, make the smallest correct change that fixes the root cause, then commit with a conventional-commit message and push to the PR branch.

## Guardrails — non-negotiable
- NEVER merge, approve, or close the PR.
- NEVER force-push or rewrite history.
- NEVER disable, skip, or delete a test to make a gate pass.
- NEVER touch files unrelated to this PR's diff.
- If a failure is ambiguous or you are not confident in the fix, post a comment explaining it and STOP for a human.

Notice what the skill is not. It is not a clever prompt that says “make the PR good.” It is a checklist of sensors, an unambiguous definition of done, and a hard list of things the agent may not do. That is the harness around the loop.

Wrap It in a Loop

Now the cadence and the budget. In Claude Code, /loop runs a prompt or another command on a recurring interval:

1
/loop 10m /pr-quality-gate 1234

That runs the pr-quality-gate skill against PR #1234 every ten minutes. The ten-minute interval is the cadence, chosen to roughly match how long CI takes to come back. There is no point checking every thirty seconds when the signal you are waiting for only changes once per CI run. Polling faster than your sensors update is the most common way to waste money on a loop.

The bounded budget lives in the loop’s stop criteria. You tell it to stop when the skill reports the PR is green or after ten iterations:

Stop when the PR posts the “ready to merge” comment, or after 10 iterations, whichever comes first. On the tenth iteration without success, summarize what is still failing and what you tried, and stop.

That is the whole loop. Six parts, two files, and a PR that walks itself to the finish line while you do something else.

Four Gates, Four Different Loop Shapes

Here is the part that took me a while to appreciate. The ship loop above looks like one loop, but the four gates inside it are four different kinds of loop, each with its own definition of progress. Understanding the shape of each one is what lets you tune it, because a loop that climbs toward a number behaves nothing like a loop that processes a queue.

Gate Loop shape “Done” means Failure mode to watch
Tests + Checkstyle + Sonar Converge-to-green Every check passes Oscillating: fixing A breaks B
Coverage climber Incremental-to-a-number Metric crosses a threshold Gaming the metric with empty tests
Dependency upgrade Queue / batch The work queue is empty Getting stuck on one hard item
Flaky-test stabilizer Repeat-until-confident Confidence over many runs Declaring victory after one run

Shape 1: Converge-to-Green

This is the default and the simplest. State is binary per check: red or green. The agent’s job is to flip every red to green and keep it there. The classic failure mode is oscillation: the fix for the failing integration test introduces a Checkstyle violation, and the fix for that breaks a unit test. A good converge-to-green loop re-runs all the sensors after each change, not just the one it was working on, so it notices when it traded one red for another.

Shape 2: The Coverage Climber

Coverage is not binary. It is a number you are pushing toward a threshold, which makes the loop fundamentally different. Done is not “the test passed,” it is “the metric crossed 90%.” The natural way to run this loop is to always attack the lowest-covered file next:

  1. Read the coverage report and find the class furthest below the line.
  2. Write meaningful tests for its uncovered branches.
  3. Re-run coverage. Did the number go up?
  4. If still below 90%, repeat with the next-lowest file.

The danger here is unique and worth calling out in the skill: an agent told to “increase coverage” will happily write tests that execute code without asserting anything, because that moves the metric. The number climbs and the tests prove nothing. Your success criteria has to demand meaningful assertions, and your converge-to-green gate (Shape 1) is what keeps the new tests honest, because empty tests still have to pass review and not break the build.

Shape 3: The Dependency Upgrade (a Queue)

Upgrading dependencies is a batch loop. You are not converging on a single state; you are draining a work queue, one item at a time, until it is empty.

  1. Build the queue: list every outdated dependency.
  2. Take the next one. Bump it.
  3. Build and run the tests. If the upgrade broke something, fix the breakage (a small converge-to-green loop nested inside this step).
  4. Commit that single upgrade on its own so it is easy to review or revert.
  5. If the queue is not empty, go back to step 2.

The “done” condition is an empty queue, not a green metric. The failure mode is getting stuck: one dependency has a breaking change the agent cannot resolve, and a naive loop will hammer it forever. This is exactly where the per-item budget matters. Give each queue item its own small iteration cap, and when an item exhausts it, skip it, leave it in the queue, and report it instead of letting one hard upgrade block the other nineteen easy ones.

Shape 4: The Flaky-Test Stabilizer (Repeat-Until-Confident)

This is the subtlest shape, because the thing you are measuring is non-deterministic. A flaky test passes most of the time, so a single green run tells you almost nothing. Done here is statistical confidence, not a single observation.

  1. Run the suspected test (or the whole suite) N times in a row.
  2. Record the pass/fail ratio. A test that fails 3 out of 20 runs is flaky.
  3. Diagnose the root cause: a race condition, a shared fixture, a time or ordering dependency.
  4. Fix it.
  5. Run it N times again. Only declare it stable when it passes all N runs.

The defining mistake is declaring victory too early. A loop that runs the test once after the fix and sees green will report success on a test that still fails one time in fifty. The success criteria has to be “passed N consecutive runs,” and N is a knob you turn based on how flaky the test was to begin with. The more intermittent the original failure, the more runs you need to trust the fix.

The reason all four live inside one loop is that they share the same outer structure: observe, compare to goal, act, check the budget, repeat. The outer loop does not care that coverage is a number and the upgrade queue is a list. It just asks each gate “are you green?” and lets each gate answer in its own way. Hold on to the queue and converge-to-green shapes in particular, because the second loop is built out of both.

Loop 2: The Build Loop, From Spec to a Clean Branch

The ship loop runs at the end of the feature, on an existing PR. The build loop runs at the beginning, and it is the one I am more careful about, because it writes the feature instead of polishing it.

I run my build phase through the specs-driven development framework I wrote about in Specs-Driven Development in Practice. The short version: a feature starts as a spec, gets reviewed, becomes a design, and is decomposed by /plan into a queue of small, TDD-shaped tasks (T-001, T-002, …) tracked in a .tdd-state.json state machine. Each task is implemented with /build T-00X, which moves it pending → red → green → refactor → done and commits when its gates pass. When the queue is empty, /validate runs the full harness (tests, coverage, lint) and /review audits the implementation against the spec, returning a verdict with must-fix, should-fix, nit, and praise findings.

Read that again and you will notice it is already a loop with all six parts, run by hand. The build loop just closes it.

  • Goal: every task done, /validate clean, /review returns APPROVE with zero must-fix findings.
  • Sensors: .tdd-state.json, the /validate output, the /review verdict. The framework is the sensor layer. I did not have to build one for the loop.
  • Action: run /build on the next task; after the queue drains, fix whatever /validate and /review flag.
  • Cadence: as fast as the work allows. There is no CI to wait for here, so unlike the ship loop, this one does not poll on an interval. It runs the next step the moment the last one finishes.
  • Bounded budget: a cap on the review-fix rounds, so a finding the agent cannot resolve does not loop forever.
  • Guardrails: branch only, never merge, never weaken a test or edit the spec to make /review pass, escalate on low confidence.

Structurally it is two of the shapes you already met, nested: a queue (drain the /build tasks) wrapping a converge-to-green (fix until /validate and /review are clean).

The Build-Loop Skill

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
name: sdd-build-loop
description: Drive an SDD feature from its task queue to a clean, reviewed branch. Trust-gated; runs only on a feature branch.
---

You are building the feature in `.specs/$1` to a reviewed, committable state on the CURRENT feature branch. Confirm you are NOT on main first.

## Phase 1 — drain the task queue (queue shape)
- Read `.tdd-state.json` for the next task whose phase is `pending`.
- Run `/build` for that task. Let it go red → green → refactor → done and commit on completion.
- Repeat until no `pending` tasks remain.
- If one task fails its gates twice in a row, STOP and escalate. Do not skip it silently — a missing task is not a done feature.

## Phase 2 — validate and review (converge-to-green)
- Run `/validate`. If anything fails (tests, coverage, lint), fix the root cause, commit, and run `/validate` again.
- Run `/review`. If the verdict has ANY must-fix findings, fix each one with the smallest correct change, commit, and run `/review` again.
- Stop Phase 2 when `/validate` is clean AND `/review` returns APPROVE with zero must-fix. Leave should-fix and nit findings in a summary for the human; do not gold-plate.

## Success criteria
All tasks done, `/validate` clean, `/review` APPROVE with zero must-fix. Then STOP and write a handoff summary. DO NOT open a PR or merge.

## Guardrails — non-negotiable
- Work ONLY on the feature branch. NEVER commit to main.
- NEVER merge, and NEVER open the PR — that is the human's call.
- NEVER edit the spec, weaken a test, or lower a threshold to pass a gate.
- After 5 review-fix rounds without reaching APPROVE, STOP and escalate.

And the loop. Because there is no CI to wait on, this one self-paces instead of polling a clock — it just keeps going until the success criteria are met or the budget runs out:

1
/loop /sdd-build-loop 2026-05-09-create-new-customer

When it stops, you do not have a merged feature. You have a feature branch with a drained task queue, a clean /validate, and an APPROVE from /review, plus a handoff summary of the should-fix and nit items it deliberately left for you. That is precisely the input the ship loop wants.

The Part You Have to Earn: Trust and Auto-Commit

I want to be honest about something, because it directly contradicts advice I have given before. In my SDD posts I am emphatic that you review every task and give the agent feedback per task. “At the end of the day, it is your name in the commit.” “Oh, the AI created the code like this” is not an acceptable explanation in a review. I still believe that.

So how do I square that with a loop that auto-commits six tasks without me looking at each one?

The answer is that the build loop does not remove human review. It relocates it. The default SDD flow puts a human checkpoint after every task. The build loop replaces those per-task checkpoints with two things: automated sensors (/validate and /review) doing the per-task checking, and one deliberate human validation round at the end. That trade is only safe once the sensors are trustworthy enough to stand in for you on the small stuff. Earning that trust is the prerequisite, and it is not automatic. You graduate into this loop; you do not start here.

Where does that trust come from? For me, it comes from an extensive set of skills I have been building for months, driven entirely by manual code review. Every time I find a piece of code or a pattern I do not like, I update the skill so that mistake stops happening. Do that consistently and, eventually, Claude Code (or whatever tool you use) starts generating code that is remarkably close to what you would have written yourself, because it is following the exact standards and patterns you have documented. That is the point where relocating human review from “after every task” to “after a handful of tasks” stops being a risk and starts being the natural next step. But that trust has to be earned first: build your own set of skills before you reach for this loop. Do not skip that step, or you will spend far more time correcting the agent’s output than you would have spent reviewing each task yourself.

Here is the checklist I use to decide whether a given feature is allowed to run through the build loop:

  • You have shipped enough features with the framework that /review rarely surfaces something you disagree with. If you are still arguing with the reviewer, it is not ready to be your proxy.
  • Your skills and agents encode your team’s standards. The framework ships with my opinions baked in. Until you have tuned the agents, checklists, and review rules to match what you would write yourself, the loop is enforcing someone else’s taste.
  • Your sensors actually catch regressions. Your coverage threshold, lint rules, and /review checklist have to fail loudly on the mistakes you care about. A green /validate has to mean something before you let it gate an auto-commit.
  • The spec passed /spec-review with no open questions. The loop amplifies whatever the spec says. A vague spec does not produce vague code; it produces confident, wrong code, faster. Garbage in, garbage in bulk.
  • The feature is a pattern you have done before. A CRUD slice with a clear contract is a good candidate. A novel architectural decision is not — that is exactly the kind of thinking you should not be delegating to a loop.

And then the non-negotiables, regardless of how much you trust the model:

  • Always on a feature branch, never main. The loop’s whole output is a branch you can throw away.
  • A mandatory human validation round before the PR opens. Run the app, click through the feature, read the diff. The build loop hands you a clean branch; you decide it is worth a pull request. The loop never opens one for you.
  • A bounded budget on the review-fix rounds, so a finding it cannot resolve escalates instead of looping.

If those conditions are not met, run the framework the normal way, task by task, with your eyes on every diff. The loop is an optimization for the cases you already understand, not a shortcut around understanding them.

The same discipline applies further upstream. In the SDD framework, I spend the majority of my time reviewing the spec. The spec has to produce something at least as good as what I would have written without AI, ideally better. Do not skip that review either; a sharp spec is what makes every downstream loop trustworthy in the first place. Loop engineering is an advanced technique, one you reach for only after you trust your agents, your skills, and your sensors. And no matter how much of that trust you have earned, you still have to own what you ship. Whether the code was written by AI or by hand, it is your name on it. Trusting AI 100% is a disaster waiting to happen: the moment something breaks in production, you need to know exactly what changed and why, not discover that nobody, human or agent, actually understands the code that shipped.

Extending the Pipeline at Both Ends

The ship loop and the build loop cover the middle of delivery: spec to merge. But a feature does not begin at a finished spec, and it does not end the moment CI goes green. Two more loops extend the pipeline outward, and each one wraps a loop around tedious, checkable work you were already doing by hand.

Loop 3: The Spec-Sharpening Loop (Front of the Pipeline)

The build loop’s trust gate has a hidden dependency: it is only safe if the spec going in is sharp. A vague spec does not produce vague code, it produces confident, wrong code, faster. So the loop that feeds the build loop is the one that turns a raw GitHub issue into a spec that passes /spec-review with zero open questions.

This is already a loop you run by hand. My SDD workflow tells you to keep running /spec, answer the open questions it surfaces, and re-run it until none remain. The sensors already exist: the open-question list from /spec, and the PASS/FAIL verdict from /spec-review.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
name: spec-sharpen-loop
description: Turn a GitHub issue into a review-passing spec, resolving what it can and escalating product decisions. Front of the pipeline.
---

You are turning issue #$1 into a spec that passes /spec-review with zero open questions. Work in rounds.

## Sensors
- The open questions /spec surfaces for this issue.
- The /spec-review verdict (PASS/FAIL) and its findings.

## Each round
1. Run `/spec` for the issue (or re-run after answers are added).
2. Triage every open question into one of two buckets:
   - RESOLVABLE from the issue, linked tickets, or existing specs: atomic acceptance criteria, missing non-goals, ambiguous wording, contract or format gaps. Resolve these and update the spec.
   - PRODUCT DECISION you cannot ground in existing material: pricing, policy, UX intent, scope trade-offs. Collect these.
3. If any PRODUCT DECISION questions remain, STOP and post them to the human as a numbered list. Wait for answers; do not invent them.
4. Run `/spec-review`. If FAIL, address the structural findings and start another round.

## Success criteria
/spec-review returns PASS with zero open questions remaining. Then STOP. The spec is ready for /plan and the build loop.

## Guardrails — non-negotiable
- NEVER answer a product-decision question yourself to force a PASS. A clean spec built on guessed intent is the most expensive failure.
- NEVER start design or code. This loop produces a spec, nothing else.
- If the same question reappears after being answered, escalate; the issue itself may be underspecified.
1
/loop /spec-sharpen-loop 1

This loop has a cadence the others do not: it is human-paced. It advances a round each time you answer the product questions it surfaces, then blocks again. You are not waiting on a clock or on CI; you are the sensor it is waiting on. That is by design. This is the one loop that pauses inside itself for a human on purpose, because the forbidden shortcut here is the most dangerous one in the whole pipeline: answering a product question with a plausible guess just to make /spec-review pass. Every downstream loop trusts this spec. If it is wrong, they will build, test, and ship the wrong thing with great efficiency.

Loop 4: The Review-Response Loop (End of the Pipeline)

The ship loop hands a green PR to a human reviewer. That reviewer leaves comments, and then someone has to work through them: make the change, push, reply, repeat, until every thread is resolved. That “work through the comments” step is a queue loop, and it is the mirror image of the ship loop. The ship loop converges the PR on CI; this one converges it on the humans.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
name: pr-review-response
description: Resolve actionable human review comments on a PR until none remain and gates stay green. End of the pipeline.
---

You are responding to human review on PR #$1. Do exactly one pass.

## Sensors
- Run `gh pr view $1 --json reviews,comments,reviewThreads` and list every UNRESOLVED thread.
- Run `gh pr checks $1` for current gate status.

## Triage each unresolved thread
- CHANGE REQUESTED (do X, rename Y, handle case Z): make the smallest correct change, commit, push, and reply in the thread linking the commit. Do NOT resolve the thread — the reviewer does that.
- QUESTION (why did you...?): reply in the thread with an explanation. Change code only if the honest answer is "you're right, fixing it".
- DISAGREEMENT or ambiguous intent: STOP and escalate to your human. Do not argue in the thread, and do not silently comply.

After any code change, re-run the ship-loop gates so a review fix does not re-break CI.

## Success criteria
No unresolved actionable threads remain AND all gates are green. Then STOP. Do not merge.

## Guardrails — non-negotiable
- NEVER merge, approve, or resolve a reviewer's thread.
- NEVER push back on a reviewer; escalate disagreements to your human.
- One logical change per commit; never force-push.
- If a comment needs a product or architecture decision, escalate.
1
/loop 15m /pr-review-response 1234

Reviewers leave comments over hours, not seconds, so this loop polls on an interval like the ship loop. The difference is what it polls: human review activity instead of CI. The hard part is not the editing, it is the triage. A loop that treats every comment as “change requested” will dutifully rewrite code in response to a reviewer who was only asking a question, and a loop that treats every comment as a question will ignore real change requests. Spelling out the difference, and forcing an escalation on disagreement, is what keeps the loop from arguing with your reviewer on your behalf.

Composing the Loops: An Issue-to-Merged Pipeline

Now all four chain into one flow. Human judgment lands at exactly two points: answering genuine product questions at the front, and deciding a branch is worth a PR in the middle. Everything else is a loop converging on a checkable goal.

The issue-to-merged pipeline: four loops chained together with two human checkpoints

Look at the cadence column. Every loop runs at a different speed, and not one of them is arbitrary. The spec-sharpening loop is human-paced because it waits on your decisions. The build loop self-paces because its sensors are local and instant. The ship loop polls every ten minutes because its sensor is CI. The review-response loop polls on review activity because that is how fast humans comment. Each loop’s cadence is dictated by how fast the thing it watches can actually change. Match cadence to your sensors and you never pay for a check that cannot tell you anything new.

The two human gates are the smallest they can responsibly be. You are not writing the spec from scratch, reviewing every task, babysitting every CI fix, or hand-editing every review nit. You are making two decisions — “is this intent right?” and “is this branch worth a PR?” — each backed by a loop that did the mechanical work and stopped to ask. That is the whole point of loop engineering: push the convergence into loops, and spend your attention on the judgment calls no loop should make.

Guardrails for Any Loop

All four loops above lean on the same small set of rules. A loop runs unattended; that is the whole point, and also the whole risk. These are the guardrails I would not ship a loop without.

  • Bound everything. Every loop needs a maximum iteration count, and ideally a wall-clock deadline too. A loop without a budget is not autonomous, it is a runaway process.
  • Never let a loop merge. Both loops drive toward merge-ready and stop. A human owns the merge. This single rule prevents the worst-case outcome of an automated change going straight to main.
  • Forbid metric-gaming explicitly. Any loop pointed at a number or a verdict will try to satisfy it the easy way: disabling a flaky test, writing assertion-free tests, editing the spec so /review stops complaining. Spell out the forbidden shortcuts, because the agent will not infer them.
  • Make every action reviewable and reversible. One logical change per commit, conventional messages, branch only, never force-push. When a loop does something wrong on iteration 7, you want to revert one commit, not untangle a squashed mess.
  • Escalate on low confidence. The most valuable thing a loop can do when it is stuck is stop and say so. “I tried these three fixes and none worked, here is what I observed” beats a tenth desperate commit.
  • Match cadence to your sensors. Poll on an interval only when you are waiting on something slow like CI. When the sensors are local, let the loop run as fast as the work allows.

When to Use a Loop, and When Not To

A loop is the right tool when three things are true: the goal is objectively checkable (a script or a sensor can tell you yes or no), progress is incremental (each iteration gets measurably closer), and the work is tedious enough that the convergence dominates the thinking. Driving a PR to green qualifies. Draining a well-specced task queue qualifies, once you trust the harness.

A loop is the wrong tool when the goal is subjective (“make this API design elegant”), when there is no reliable sensor, or when the hard part is a single decision rather than a sequence of mechanical steps. If you would not be able to write the “are we done?” check as something that returns a boolean, you do not have a loop yet. You have a conversation, and you should just have the conversation.

The mistake I see most often is reaching for a loop to avoid thinking about the success criteria. But the criteria is the work. Once you can state precisely what “done” means and how a sensor would detect it, the loop almost writes itself. Until you can, no amount of iteration will save you, because the agent has nothing to converge on.

Final Thought

Prompting an agent to fix a failing test is easy. Designing a system where one loop sharpens the spec, another builds the feature to a clean, reviewed branch, a human makes the two calls that matter, and two more loops drive the pull request through CI and human review — that is the engineering.

The loops are not the clever prompts in the middle. They are the goals you can check, the sensors that let the agent see, the budgets that let it stop, and the guardrails that keep it honest. Get those right and the agent in the middle barely matters.

We spent years learning to write code that runs. Loop engineering is learning to write the conditions under which an agent’s code is allowed to ship — and being honest about which of those conditions you have actually earned. That is the more durable skill, and it is the one I would invest in now.

If you want the foundations this builds on, the rest of the series covers them:

This post is licensed under CC BY 4.0 by the author.
This site uses cookies. Please choose whether to accept analytics cookies. Privacy Policy