Known, extremely common, and mostly an architecture problem rather than a prompting one. Your nine steps live in a message that is now thirty turns back, competing with a pile of tool output. The plan needs to be state your loop owns, not a paragraph in the transcript.
What this looks like in practice:
- the loop holds a checklist as real data, and after every step you re-render it into the prompt:
[x] fetch [x] validate [x] transform [ ] write file 1 ... - the model does not get to declare completion in prose. It calls
mark_step_done(id, evidence)and your code checks the evidence - the file exists, the row count matches. finishis a tool that your loop rejects if unchecked items remain, with a message saying which ones.
Once completion is something your code decides rather than something the model asserts, this failure stops being possible. Prompting harder gets you from 3 steps to maybe 5.