If part 4 built the walls, the next question is: how many cycles run inside them—and what stops the run? Tools, sandboxes, and CI still won’t save you if the loop has no stop condition. You’ll burn tokens replaying the same mistake.
Takeaway: Loop engineering is not “keep thinking.” It is designing plan → act → observe → (retry | stop | escalate) on purpose.
This is part 5 of Coding Is Conversation, after harness engineering. Roles were part 3; tool choice was part 1.
Harness and loop are different axes
| Axis | Question | Parts 4 / 5 |
|---|---|---|
| Harness | What may happen? | Tools, sandbox, permissions, eval walls |
| Loop | How many times—and when stop? | Turn caps, WIP, retry policy |
Harness shrinks the blast radius. Loop shrinks time, cost, and thrash. Without both, defaults become “mash Allow” or “paste the same prompt ten times.”
Rendering diagram…
Plan → Act → Observe (ReAct in working clothes)
Yao et al.’s ReAct (2022) interleaved Thought with Action and fed tool results back as Observation. Product names differ; most agent runtimes you use today still sit on that skeleton.
In practice:
- Plan — one goal for this turn (one file, one failing test, one draft section)
- Act — tool call / patch / command (inside the harness)
- Observe — exit code, diff, lint, human feedback, API payload
- Decide — done / retry with new input / escalate to a human
A long soliloquy is not a plan. A plan is the next verifiable step.
Without stop conditions, it isn’t a loop
The OpenAI Agents SDK runner is roughly: call the model → run tools or hand off → stop on final output. max_turns bounds the cycle; exceeding it raises MaxTurnsExceeded unless you handle it with a controlled fallback (Running agents, as of 2026-08-03). A turn is roughly one model invocation (including its tool calls).
LangGraph caps graph super-steps with recursion_limit and raises GraphRecursionError when exhausted. Pass {"recursion_limit": N} in the run config (Graph API, GRAPH_RECURSION_LIMIT, as of 2026-08-03). Check the default for your version; never treat “unlimited” as the default policy.
| Stop signal | Meaning | Example |
|---|---|---|
| Success gate | Definition of Done met | Tests green, quality ≥85, PR checks |
| Budget gate | Turns / tokens / time gone | max_turns, 25-minute box |
| Pattern gate | Same failure N times | Same file + same error ×3 → escalate |
| Human gate | High risk / judgment | Deploy, schema, secrets |
If stop rules live only in the prompt, the model can claim “almost done.” The harness (part 4) should judge via exit codes, hooks, and CI; the loop must treat that judgment as Observe.
WIP=1 — one thing at a time
Agents often look “slow” because WIP exploded, not because the model is weak. One turn that mixes refactor + tests + docs + deploy scripts makes Observe unreadable noise.
PapaCoder Labs keeps exactly one card in Now (WIP=1). Two cards is an orchestrator bug. The editorial series also runs one slug per run. Apply the same rule to human and agent teams and half of “why won’t this loop end?” disappears.
Working rules:
- One loop goal = one verifiable artifact
- Parallelism only for read-only research; writes stay serial
- When stuck, don’t add goals—split or escalate the current one
Retry policy — don’t press the same button
Unbounded retry isn’t a loop; it’s a spin. Example policy:
retry_budget: 3
on_fail:
1) change_input # fold error logs / failing asserts / narrower scope into next Plan
2) change_tool # different search / different file / read-only
3) escalate # human / reviewer role / ticket
never:
- identical_prompt_replay
- silent_ignore_of_observe
Ignoring Observe and saying “try again” only grows context. A good retry is a retry with changed input.
I once watched an agent patch the same import error four times. The fifth “fix” wasn’t another patch—it was reading Observe and stopping: the real issue was a missing env var.
Practice: a copy-paste loop contract
Drop this into AGENTS.md or your orchestrator prompt:
# Loop contract
## Goal (WIP=1)
- One verifiable outcome per run (e.g. "tests for auth middleware green").
## Cycle
1. Plan: state the next smallest step.
2. Act: only harness-allowed tools.
3. Observe: paste exit codes / failing asserts / diff summary.
4. Decide: stop | retry-with-new-input | escalate.
## Budgets
- max_turns: 8 (or team default)
- max_identical_failures: 2
- wall_clock: 25m then escalate
## Done when
- [ ] Automated checks required by harness pass
- [ ] No new scope added mid-loop without human OK
With the OpenAI SDK, set an explicit cap (confirm defaults for your version, as of 2026-08-03):
result = Runner.run_sync(
agent,
"Fix the failing unit tests in packages/core only.",
max_turns=8,
)
Cursor notes
- In long Agent sessions, lock a one-sentence goal and demand mid-run Observe (test output, file list).
- If the same file thrash-loops, Stop, paste the failure, and open a new “retry with new input” thread—cheaper than infinite continuation.
- Without hooks/CI (part 4), Cursor loops degrade into fluent spin. Move stop judgments outside the chat.
- If Composer/Agent proposes several large edits at once, refuse under WIP=1 and re-sequence.
Loops at PapaCoder
- Development: board
Now= 1, small per-cycle outputs, acceptance before Done (agents/development/workflow.md). - Editorial: Research → Draft → Review → Fact-check → (revise with new input on fail) → Internal API draft only. Humans publish.
accuracyFailblocks regardless of score—that’s a success gate. - Series orchestrator: one queued slug per run; next post comes from the plan’s Next action.
A loop isn’t aesthetic automation. It’s a cheap rhythm for failure.
FAQ
Q. Loop vs multi-agent?
A. Multi-agent (part 3) is roles and handoffs. Loop is repetition and stopping inside one role (or the orchestrator). A handoff is still an Act in an outer loop.
Q. Does a huge max_turns make the agent smarter?
A. Usually it only raises cost. Pair budget with a narrow goal and honest Observe.
Q. When do we need graphs (part 6)?
A. When branching dominates and “who speaks next” no longer fits a straight cycle. First make one cycle trustworthy.
Q. Is per-turn human approval a loop?
A. It’s supervision, and it doesn’t scale (part 4 approval fatigue). Prefer automatic gates for normal stops; reserve humans for escalation.
Sources
- Yao et al., ReAct (2022) — https://arxiv.org/abs/2210.03629
- OpenAI Agents SDK — Running agents — https://openai.github.io/openai-agents-python/running_agents/ (as of 2026-08-03)
- OpenAI — Running agents (API guide) — https://developers.openai.com/api/docs/guides/agents/running-agents (as of 2026-08-03)
- LangGraph — Graph API / recursion limit — https://docs.langchain.com/oss/python/langgraph/graph-api (as of 2026-08-03)
- PapaCoder — development WIP=1 —
agents/development/workflow.md
What’s next in the series
| # | Topic |
|---|---|
| 01–04 | Tools → vibe → multi-agent → harness |
| 05 | Loops (this post) |
| 06 | Graphs/DAGs — when to branch, parallelize, escalate |
| 07 | PapaCoder field notes |
Next: graph engineering—when a linear loop breaks and you need nodes and edges.
One line to keep: Inside the walls, one goal at a time—retry with new input, escalate when the budget ends.