Course · Part 1, The loop · Lesson 1.4 · 12 min

Planning: the plan that never ends

An agent asked to fix a failing build wrote a plan, read one file, wrote a new plan, and did that until the framework stopped it at step 25 with nothing changed.

Before you read: find the line

Read the run below once. Decide which line is the mistake. The lesson tells you.

Coding agent, fix the nightly build, default framework limits
  1. 1userThe nightly build is red. Find the cause and fix it.
  2. 2llmplan: 1 read the build log, 2 find the failing test, 3 fix it, 4 run the tests
  3. 3toolread_log(run="nightly") -> 3 failures in tests/test_billing.py
  4. 4llmplan: 1 read tests/test_billing.py, 2 read the billing module, 3 understand the data model, 4 fix, 5 run
  5. 5toolread_file(path="tests/test_billing.py") -> 412 lines
  6. 6llmplan: 1 list every file that imports billing, 2 read each one, 3 map the data flow, 4 then decide
  7. 7toollist_files(path="src/") -> 61 files
  8. 8systemrecursion limit 25 reached, run stopped
  9. 9donestatus: stopped, 0 files changed

What broke

A coding agent was asked to fix a red nightly build. It wrote a four-step plan, read the log, and found three failing tests in test_billing.py. Good start. Then it wrote a new plan, five steps now, and read the test file. Then a new plan again, this time to map every file that imports the billing module. At line 8 the framework stopped it: LangGraph's default recursion limit is 25 steps, and the run hit it. Files changed: zero. The build was still red, and the run had spent 25 steps of model calls and file reads to learn nothing it did not know at line 3.

This is the plan that never ends. The agent was never idle and never wrong in any single line. It just never got to step 3.

Why it broke

Line 4 is the first bad move. The model had a plan and a result that matched step 1. The right next action was step 2. Instead it re-planned, and the new plan was wider than the old one. That happened because the plan existed only as text in the model's last message. On every turn the model saw the whole history and was asked what to do next, so it did the thing models do with a lot of context: it summarized and planned again. Nothing said "you are on step 2 of 4". Nothing said "the plan is done when the tests pass".

Each re-plan grew because each new file added things to understand. A 412-line test file suggests reading the module. The module suggests the data model. The data model suggests every importer. The agent was exploring, and exploring has no natural end. ReAct, the 2022 paper that gave this loop its shape, interleaves a thought and an action on every turn, and reports success rates 34 points above imitation learning on ALFWorld and 10 points above it on WebShop. But ReAct says nothing about when to stop. That part is yours.

The only stop condition was the framework's. LangGraph's default is 25 steps, the OpenAI Agents SDK's default max_turns is 10, and both raise an error when hit. A limit like that is a smoke alarm, not a plan. It tells you the run failed. It does not tell the run how to succeed.

What a senior engineer does

Separate the plan from the prompt. Store it as a small list with a status per step: todo, doing, done. The model updates one status per turn through a tool, and the loop reads the list to decide what to show the model next. Re-planning is allowed, but it is an explicit action with a reason, and you count them. A run that re-plans three times without finishing a step is stuck, and the loop can say so and stop.

Write the stop condition before the first step. For this task it is: the three tests in test_billing.py pass, and no other test broke. That is checkable by a tool, so the loop checks it rather than asking the model whether it feels done. Anthropic's guide on building effective agents puts it plainly: the task usually ends on completion, and it is common to add stopping conditions such as a maximum number of iterations to keep control. Note the order. The real stop condition first, the turn limit as a backstop.

Budget the exploration. Reading is cheap and feels productive, so cap it: three files before the first edit, then edit, then run the tests and let the failure tell you what to read next. The test failure is a better guide than a map of 61 files. In the trace, line 3 already pointed at the test file and the three failures. One edit and one test run would have taught the agent more than the next twenty steps did.

Log the plan at every turn. When a run gets stopped at the limit, the first question is which step it was on. If the answer is step 1, for the fourth time, you have found the bug in one look.

What to say in the interview

"Line 4. The agent had a result for step 1 and re-planned instead of doing step 2, because the plan was only text in its last message and nothing tracked progress. It kept widening the plan until the framework's 25-step limit stopped it. I would keep the plan in state with a status per step, define done as the tests passing, cap reads before the first edit, and count re-plans so a stuck run is caught early." If they ask about the turn limit, say it is a backstop, not the stop condition.

The line

Coding agent, fix the nightly build, default framework limits
  1. 1userThe nightly build is red. Find the cause and fix it.
  2. 2llmplan: 1 read the build log, 2 find the failing test, 3 fix it, 4 run the tests
  3. 3toolread_log(run="nightly") -> 3 failures in tests/test_billing.py
  4. 4llmplan: 1 read tests/test_billing.py, 2 read the billing module, 3 understand the data model, 4 fix, 5 run
  5. 5toolread_file(path="tests/test_billing.py") -> 412 lines
  6. 6llmplan: 1 list every file that imports billing, 2 read each one, 3 map the data flow, 4 then decide
  7. 7toollist_files(path="src/") -> 61 files
  8. 8systemrecursion limit 25 reached, run stopped
  9. 9donestatus: stopped, 0 files changed

Line 4. After the first result the model threw its plan away and wrote a longer one instead of ticking off step 1 and doing step 2. The plan lived only in the model's last message, so every turn re-planned from a bigger context, each plan wider than the last. Nothing tracked which steps were done, and the only stop was the framework's step limit, which arrived before any work.

The pause

The run was stopped by the recursion limit. Why is that not the stop condition a senior engineer would write, and what is?

In the interview

“This agent ran 25 steps and changed nothing. Where did it go wrong, and how would you design the loop?”

Say your answer out loud, in under a minute. Then compare.

Notes