The Four-Loop Pattern: Running Autonomous Coding Agents Without Drift
How to keep AI engineering disciplined when the agents work faster than you can review them — including when the author of the discipline…
The Four-Loop Pattern: Running Autonomous Coding Agents Without Drift
How to keep AI engineering disciplined when the agents work faster than you can review them — including when the author of the discipline drifts from it.
This is an exploratory article, not a prescription. Its value is not that the Four-Loop Pattern is already proven, but that it gives language to a real failure mode: autonomous agents can be busy, correct locally, and still not move the product forward.
If you’ve spent more than a week running coding agents on a real codebase, you’ve probably hit the same wall I did. The agents are productive — they ship commits, they pass CI, they update docs. The repo grows. Tests get added. New mechanisms get built.

And then you step back, look at your product, and realize: nothing actually got better.
The agents are busy. The output is real. But the user-facing experience is roughly where it was a month ago. The codebase is bigger, the doctrine is thicker, the meta-architecture is more elegant — and the actual feature you wanted to ship still doesn’t work end-to-end.
This article describes the pattern I’ve converged on for running autonomous coding agents in a way that actually moves the product forward. It’s not a silver bullet. It’s a discipline. And — important — it’s a discipline I caught myself violating multiple times within the same session that produced this article. That’s part of what’s in here too: the failure modes that survive even after you write the rules.
The failure mode
The pathology has a name: drifting-neutral fires. A “fire” is a single execution of an agent loop. A drifting-neutral fire is one where the agent does work, ships commits, passes CI, and changes nothing the user can see or measure.
Common variants:
- Doctrine inflation. The agent writes a new style guide, a new architectural rule, a new naming convention. The codebase doesn’t change; the doctrine to be read by future agents grows. After a few weeks, every new session spends 20% of its context window reading rules that don’t directly serve any feature.
- Stub proliferation. The agent ships a “feature” — a new screen, entity type, API endpoint. The shape is correct (file exists, schema validates, CI green). The behavior is empty: the screen has no data, the entity has no methods, the endpoint 404s on real inputs. The product surface grows wide and stays shallow.
- Mechanism without measurement. The agent builds a testing framework, an observability layer, a CI gate. The framework runs. Nothing it would catch ever fires. The framework’s output: green-checkmark, every time, on a product that’s broken.
- Plan thrashing. The agent edits the roadmap, reclassifies priorities, adds new backlog items. The roadmap grows. The work shipped against it stays small.
Each of these feels productive because the agent IS shipping commits. The fire is “successful” in the trivial sense — it didn’t crash, CI was green, the diff merged. But the platform’s measurable state didn’t change. Multiply by 50 fires per week and you have an expensive treadmill.

The discipline below exists to make this failure mode impossible to hide.
The four-loop architecture
Refuse the “one big agent” pattern. A single loop trying to do everything (implementation, review, monitoring, user testing) drifts because the priorities compete and there’s no separation of concerns to enforce.
Four loops, each with a closed responsibility:
- Implementer loop — runs every 60 minutes. Picks a backlog item; ships code + tests + docs in one PR. Cannot: review its own PR; bypass CI gates.
- Reviewer loop — runs every 30 minutes. Bot-reviews every PR against discipline rules; approves or requests changes. Cannot: open new feature work; modify the rules it enforces.
- Analyst loop — runs every 90 minutes. Drift detection, doc audits, industry research, backlog proposals. Cannot: write production code; modify the roadmap directly.
- User-shaped tester loop — runs every 4 hours. Walks the deployed product as a domain user with no developer knowledge; grades it; files findings. Cannot: fix what it finds; review its own findings.
Each loop runs in its own session. They communicate through the file system (git for shared state; per-loop directive files for handoffs). They do not call each other directly.
The orthogonality matters. The implementer cannot rubber-stamp itself; the reviewer cannot do its own implementation; the analyst cannot ship the changes it proposes; the tester cannot fix what it grades. Each loop is forced to use the other loops’ outputs.

This is the same separation a healthy human engineering team has — developer, reviewer, QA, ops — codified for agents. The loops are not “more flexible” than humans. They’re more rigid. That’s the point.
The convergence law
Parallelism without progress is worse than no agents at all. To get progress, a hard rule:
Every fire ends in one of three states:
CONVERGENT (shipped a measurable platform improvement with before/after evidence),
EXPLICITLY-NEUTRAL (deliberately did nothing, with reason surfaced), or
REGRESSIVE (made something worse — requires immediate revert plus a written lesson). A fire that ran, did things, but produced no measurable change is DRIFTING-NEUTRAL and is a failure mode the reviewer loop flags as a discipline violation.
“Measurable” is load-bearing. It needs a canonical state matrix every loop reads on cold start: feature-area grades, success rates, queue depths, deploy lag — whatever your product’s primary axes are. A fire is convergent if some cell in that matrix improved AND no cell worsened. Improvement must be supported by before/after evidence in the PR body.

In practice:
- Convergent example. “Workspace X went from grade F to grade B (before: no list, no create button; after: 93 entities visible plus working create button). Before screenshot attached; after screenshot attached. State matrix updated.”
- Explicitly-neutral example. “Investigation only — root cause not yet known; no fix possible this fire. Findings logged in analysis/…”
- Regressive example. “Attempted Y; broke Z. Reverted commit ABC. Lesson written.”
The reviewer loop enforces this on every PR. PRs that don’t fit one of the three states get REQUEST_CHANGES. The author has to either find the measurable improvement, reclassify as neutral with reason, or revert.
This rule alone catches roughly 80% of the drift problem. The rest needs more.
The pay-down rule
Rules accumulate. Each new rule a future agent must read is a tax on every future fire’s context budget. So:
Adding a new doctrine document, agent definition, backlog item, or process step REQUIRES retiring at least one existing one (delete it, fold it into another, or archive it). Net complexity cannot grow without explicit operator approval. Exception: if the new item replaces multiple old items via convergence, that’s net-negative and acceptable.
This is the rule I personally violate the most. It’s hard to delete things you wrote. But the alternative is a doctrine doc that takes 30 minutes to read at the start of every session — which means agents skim it, which means they don’t follow it, which means they drift.

The reviewer loop checks every PR: did it add new doctrine files or backlog items? If yes, did it also retire something of equivalent weight? If not, REQUEST_CHANGES with: “convergence-discipline: this PR adds N new doctrine items but retires 0. Either retire one or document why net-add is justified by a measurable improvement landing in this PR.”
The no-stub-shipping rule
Convergence-discipline catches per-fire drift. It doesn’t catch per-artifact drift. A single fire can ship 10 artifacts where 3 work and 7 are stubs, count as “convergent” in aggregate, and quietly grow the platform’s stub count.
Per-artifact rule:
Every artifact you ship — endpoint, screen, widget, capability, agent definition, manual section, anything — MUST be in one of three states:
WORKING (verified end-to-end with test, smoke output, or screenshot in the PR body),
READY-TO-WORK (gap explicitly named with completion item queued in the same PR), or
STUB-WITH-QUEUE (explicit status:stub marker plus cited completion item plus operator-readable explanation when reached).
STUB-WITHOUT-QUEUE is a discipline violation.
The classic violation: shipping a screen with a “Queue unavailable — Retry” widget. The widget’s shape is fine; the data behind it doesn’t exist; no completion item is queued. The user hits a wall with no explanation and no path forward.

A subtler violation: shipping a docs file with markdown links to sibling files that don’t exist. The link target should be a stub-with-queue file but it isn’t even that — it’s a void. The reviewer’s link-checker fails CI; the deploy gate refuses; the platform stalls. I shipped this exact failure mode in the session that produced this article — and the analyst loop caught it within hours. More on that below.
The universal cold-start protocol
Each loop, at the start of every fire, runs the same six probes before doing any work:
- Sync to current trunk. Pull. See what landed since prior fire.
- Read the canonical state matrix. What’s the platform’s measurable state right now?
- Probe deploy lag. What SHA is in production vs trunk? The most common chokepoint is right here.
- Probe deploy-gate health. Did recent deploys succeed? Or is the test gate refusing rollouts?
- Probe review backlog. How many open PRs? How old?
- Probe CI signal. Are recent trunk runs green?
Plus a seventh in the monitoring loop:
- Probe rate-limit headroom. How much of the current billing window is consumed? What’s the burn rate?
After the probes, each loop applies its response rules in rationale-anchored priority order: reachability before correctness, deploy gate before content, master CI before backlog, backlog before new work, platform metric before doctrine.

This sounds bureaucratic. It’s the opposite — it removes the agent’s freedom to choose poorly. A loop confronted with “deploy gate refused, four PRs unreviewed, CI red, backlog growing, lowest-grade feature area at F” no longer has to think about priority. The protocol decides: deploy gate first.
Thirteen robustness invariants
State-probing protocols are brittle by default. Probes time out, files are missing, networks fail. So:
- I-1. Every probe has a hard timeout (10 seconds default, 30 seconds for SSH).
- I-2. Every probe has a degraded fallback (probe failure does not mean fire failure).
- I-3. Conflict resolution by rationale-anchored priority, not first-match.
- I-4. State staleness detection (more than 24 hours old triggers fresh re-walk).
- I-5. Crash recovery on cold start (left-open branches, expired locks, uncommitted state).
- I-6. Lease-based concurrency. Each loop type holds its own lock file. Two simultaneous fires of the same loop type cannot collide.
- I-7. No-action default equals explicitly-neutral with surface. Silent no-op fires are impossible.
- I-8. Audit log per fire. Probe results, decision, action, verification all recorded.
- I-9. Verification-after-action mandatory. Failed verification means REVERT, not retry.
- I-10. Cost cap per fire (200,000 tokens). Runaway loops bounded.
- I-11. Operator interrupt graceful yield. A STOP file mid-fire means clean exit, no half-state.
- I-12. Bootstrap-safe. Works on first-ever run when state files don’t exist yet.
- I-13. Worktree isolation. Each loop operates in its own git worktree — separate working directory, same shared
.gitrepository. Without this, two loops sharing a working tree collide on every checkout and pull. Don't discover this lesson the hard way; configure it from the start.

I-13 is the one I added after this session. The other twelve I had right. The collision pattern I-13 prevents is described in the “What still breaks this pattern” section below.
The model and thinking-mode matrix
Modern coding-agent platforms let you choose model size AND thinking-mode (normal vs extended-thinking with explicit reasoning budget). The cost spread between cheapest and most expensive is roughly 60×. If every activity defaults to “the smartest model with deepest thinking,” you burn money on routine work that doesn’t benefit from depth.
The activity-to-model mapping:
- Routine PR bot-review — smaller model, normal thinking. Pattern-match against rules; no novel reasoning needed.
- Code review of a substantial change — largest model, extended thinking (16k+ tokens). Subtle correctness needs depth.
- Cold-start state probe — smaller model, normal thinking. Mechanical; depth wasted.
- Cross-pole synthesis like drift detection — smaller model, extended thinking (~8k tokens). Multi-source comparison benefits from a bit more reasoning.
- Convergence-stalled root cause — largest model, extended thinking (16k tokens). Diagnosing system-level failures.
- Architectural decision that calcifies — largest model, extended thinking (16k+ tokens). Wrong architecture costs weeks; pay for depth.
- Routine debugging with known patterns — smaller model, normal thinking. Look-up shape.
- Novel debugging beyond 30 minutes — largest model, extended thinking (16k+ tokens). Depth pays off.
- Security audit — largest model, extended thinking (16k+ tokens). Subtle threat-model reasoning.

Default: smaller model + normal thinking. Escalate when depth genuinely helps. Never default to “biggest + deepest just in case” — the biggest cost line in any agent system is subagents inheriting the parent’s model when they should override to something cheaper.
The agent-vs-operator boundary
The most important rule: agents do not silently transfer work to the human operator.
When an autonomous coding system can’t do something — domain expertise, market positioning, real-user testing, business model decisions — there’s a temptation to write “operator should…” into the docs. That’s not honest. It’s offloading the hard parts at exactly the time when the operator is least equipped to take them on (early in the product’s life, when there’s no MVP yet).
What agents WILL deliver: lifting feature grades from broken to working; design polish; competitive teardowns from public sources; integration depth; pre-customer UX testing (via the user-shaped tester loop); onboarding flows; reporting features.
What only the operator can decide: strategic priority (which feature matters most for the market they know); business model; final aesthetic preference between two compliant designs; explicit bypass authorization (when an agent says “this test is a flake, can I bypass”); reclassification of priority levels.
Real customer testing is DEFERRED, not transferred. Bringing real users in pre-MVP burns goodwill on a broken product. The user-shaped tester loop substitutes until MVP threshold is reached. Then real users come in to find what the simulated tester missed.

This boundary is the one operators most need to enforce. Without it, the agent system becomes a thing that demands operator attention rather than a thing that does work.
The single canonical state file
All four loops, all the discipline rules, all the protocols — they’re useless if there’s no shared definition of “what’s the platform’s state right now.”
One file. Append-only trend table. Each row a snapshot in time. Columns are the metrics the product cares about: feature grades by area, success rates, blocker counts, queue depths.
Every loop reads this file on cold start. Convergent fires update it. Drifting-neutral fires don’t. After a week or two, the trend in this file IS the answer to “are the agents producing convergence.”

If 7 consecutive snapshots show no movement, the reviewer loop surfaces “convergence stalled — pause loops, meta-conversation needed.” The file is the safety valve that catches the case where every individual fire claims to be productive but the aggregate isn’t.
What still breaks this pattern
This is the section the original draft of this article didn’t have. It cost me a session of drift to add it.
Author drift
The person (or agent) who writes a rule routinely violates it before the ink dries. I shipped a per-artifact discipline that said “every artifact ships WORKING, READY-TO-WORK, or STUB-WITH-QUEUE” — and in the same session I shipped a documentation tree with 16 dangling markdown links that fit none of those states. The analyst loop caught it within hours. I was the author of the rule that flagged my own violation.
The implication: self-enforcement of discipline does not work. The reviewer and analyst loops are not nice-to-have; they are the only thing that catches author drift. Build them first. The rules you write yourself will not bind you without external enforcement.
Shared-working-tree collisions
If you run all four loops out of the same physical directory, they’ll collide on every git operation. One loop does git checkout <branch> and the other loop's git status suddenly reports a different reality. Half-merged commits get attributed to the wrong session. Untracked files appear and disappear depending on which branch the most recent loop checked out.
The fix is invariant I-13 above: each loop operates in its own git worktree. Separate working directory, shared .git database. The git worktree add command is one line and it fixes the entire class of failure permanently.
I learned this by colliding repeatedly with my own analyst loop while running an operator-direct main session in the same directory.
Launcher pitfalls
Three traps you’ll hit before the loops even start firing.
- Trap 1: Multi-line prompts get truncated. Windows process-argument passing terminates at the first newline. A 3,300-character brief passed as a positional argument to the agent binary reaches the binary as the first 70 characters. Use the file-based input flag (
--append-system-prompt-file <path>or equivalent) or pipe via stdin — anything that doesn't put newlines in argv. - Trap 2: Slash commands are UI-only. If your brief instructs the agent to invoke
/rename "Dev /loop"as a first action, it can't. Slash commands are handled by the user interface, not by the agent. Set the session name at launch instead (most platforms have a-nor--nameflag) and avoid the directive entirely. - Trap 3: PowerShell 5.1’s native-arg passing is unreliable. Spaces, apostrophes, and slashes in argument values can be mangled when PS 5.1 hands them to a native exe through
-Filemode. The diagnostic is unambiguous: invoking the same exe manually from interactive PowerShell works; invoking through cmd → powershell -File script.ps1 -Param "value" fails. The fix: drop the PowerShell layer; have cmd invoke the native exe directly.
All three of these I hit in one session. None of them are exotic — they’re load-bearing for any Windows-host setup. None of them are documented in agent-platform README files. Budget a day to find them.
The cost-vs-value disconnect
The same session that produced this article cost roughly $157 in API-equivalent compute (the actual subscription is fixed; the dollar figure is what the same work would cost on pay-as-you-go). The session produced nine merged pull requests of doctrine, infrastructure, and process. It produced zero user-visible product improvements. The product-fix I committed early in the session never reached production because the deploy gate refused — due to 15 failing tests plus the 16 dangling markdown links I shipped.
This is the cost-vs-value disconnect named directly. The discipline I describe in this article was built up across that exact session. The platform’s measurable state did not improve. The infrastructure for future sessions to improve it now exists — and you should be honest with yourself (and your operator) when that’s the trade you made.

The convergence-discipline rule, applied retroactively, would have flagged the entire session as DRIFTING-NEUTRAL. That’s the rule working as intended. It also tells you something uncomfortable: a session built to produce a discipline article cannot easily also produce product progress, because every minute spent on doctrine is a minute not spent on the feature. Plan accordingly.
What this WON’T do
Honest boundaries on what the discipline can and can’t deliver:
- It will lift every feature area from broken to working. It won’t reach the absolute top grade without domain-level workflow methods plus state-change verification.
- It will ship a production-ready aesthetic via a design-system gate. It won’t create the first-time aesthetic foundation — the operator picks the design language.
- It will assemble competitive feature-parity teardowns from public sources. It won’t decide strategic positioning vs incumbents — that’s the operator’s market knowledge.
- It will run pre-customer UX testing via a simulated dumb-user agent. It won’t find real customer pain points — real users come in post-MVP.
- It will sustain convergence at known cost. It won’t take you from MVP to “best on market” — that needs inputs the loops don’t have.

Naming what won’t happen is part of the discipline. Without it, operators expect more than the system can deliver and lose trust when it under-delivers.
What it costs
A four-loop session running 8 hours per day at matrix-respecting cadence costs roughly the equivalent of a single mid-tier engineering hire’s tooling budget per month. The actual subscription is fixed; the constraint is rate limits per billing window, not dollars.
Defaulting everything to the largest model with deepest thinking? Roughly 5–7× that. The matrix discipline is the difference between sustainable autonomous engineering and a runaway bill.
What it costs on a per-session basis is more variable. A session that ships pure doctrine — like the one that produced this article — burns budget without producing product improvement. That’s not waste if the doctrine is going to pay back in convergence over future sessions, but it IS waste if the doctrine sits unenforced.

The reviewer loop is what makes doctrine pay back. Without it, doctrine accumulates as dead weight.
The lesson I keep relearning
Every time I add a new rule, agent, or process to this system, I’m adding load to the future. The convergence-discipline rule is the only one that’s net-negative on load — it forces every other addition to pay for itself in measurable improvement.
The pathology I described at the start of this article — “the agents are productive but the product isn’t moving” — is a discipline failure, not a tooling failure. The agents do exactly what you tell them: ship commits, pass CI, update docs. They just weren’t told the commits had to move a measurable axis.
Once you tell them that, with explicit per-fire and per-artifact rules, with a single canonical state file, with four-loop separation of concerns, the honest boundary about what only humans can do, and worktree isolation so they don’t collide — the agents become useful.
Not magical. Useful. Sometimes.
The “sometimes” is the part the original draft of this article missed. The discipline is the target. Even with it written down, even with rules explicit, even with the reviewer loop running, you (and your agents) will drift. The drift gets caught — that’s what the analyst loop is for — but it doesn’t get prevented. The mechanism is self-correcting, not self-preventing.

That distinction matters. The literature on AI engineering tools is full of “magical” claims. The reality is: a small set of disciplined patterns, rigorously enforced by other agents (not by self-discipline), that make autonomous agents do real work most of the time, with caught-and-corrected drift the rest of the time. Everything else is theatre.

If you’re running coding agents on a real codebase and seeing the failure modes I described, the patterns above might help. They’re not novel; most are codifications of what good engineering teams already do. The novelty is making them load-bearing for autonomous loops where there’s no human in the immediate path to enforce them implicitly — and naming the failure modes that survive the discipline anyway.
Addendum. Cue card

메타데이터
- post_id
- d9906b633d65
- slug
- the-four-loop-pattern-running-autonomous-coding-agents-without-drift-d9906b633d65
- url
- https://medium.com/@mark.emelianov/the-four-loop-pattern-running-autonomous-coding-agents-without-drift-d9906b633d65
- canonical_url
- https://medium.com/@mark.emelianov/the-four-loop-pattern-running-autonomous-coding-agents-without-drift-d9906b633d65
- author_url
- https://medium.com/@mark.emelianov
- status
- ok
- fetched_at
- 2026-06-09 15:37:30