Skip to main content
All posts

Why AI Coding Agents Say "Done" When They Aren't

AI agents claim “done” on fuzzy tasks because completion isn’t well-defined. The fix is structural: define objective outputs and enforce workflow loops for verification and handoffs.

Seena Nadimi

· 8 min read

Reliant - ralph wiggum and other workflows

This is the first post in a series examining the philosophies behind popular AI coding workflows — what they get right, where they break down, and what happens when workflows become a programmable primitive

Ask an AI coding agent to remove dead code and you usually get something that looks right. It deletes the obvious unused function, removes one import, updates a call site or two, then says the cleanup is complete. Then you open the diff and find three more helpers with no callers, a test fixture that still points to the old path, and one branch that still imports the deleted module.

Once you start noticing it, that failure mode is all over the place. A refactor updates the main implementation and still misses the shadow copy in a helper package. A test rename changes the test file names, but CI config still points at the old targets. The agent says it edited four files. One of those edits never landed in the branch.

This is why "done" from an agent often means review is just starting. The frustrating part is that the agent usually did real work. The branch is better than before, but the task still is not done.

It helps to give this a name: the repetition pattern. The human sees partial completion, asks again with a narrower prompt, gets another slice of progress, checks the repo again, and keeps going until the leftovers stop showing up. The Ghost Task Problem describes a similar symptom from another angle: claimed completion drifting away from branch state. This is not a weird edge case. It is a routine operating condition for AI coding work on brownfield codebases.

The natural reaction is to blame the prompt. Write clearer instructions. Add more context. Specify the directories to check. That helps on the margins, but it misses the structural issue: no prompt can give an agent the ability to verify its own fuzzy work. The agent's strongest skill — generating plausible, working code — is exactly what makes this failure mode so dangerous. It is not that agents are bad at coding. It is that they are too good at looking done. The problem is not linguistic. It is architectural.

The real distinction is objective completion vs fuzzy completion

The most useful question is whether the task has a clear stop condition, not whether the model seems smart in the abstract. Once you sort tasks that way, a lot of confusing agent behavior starts to make more sense.

Objective completion means there is outside evidence the work is done: the test suite passes, the build succeeds, the linter is clean, or the generated file matches the schema. You can argue about the implementation, but you do not have to guess whether the requested state was reached.

Fuzzy completion means the stop condition depends on judgment, coverage, or discovery. "Remove all dead code" sounds clear until you realize the repo has helper directories, test fixtures, generated stubs, half-migrated modules, and old scripts that only show up in deploy paths. "Finish this refactor" sounds clear until the remaining work is scattered across naming conventions, comments, docs, config, and edge-case call sites. The agent can make visible progress and still not have real evidence that it found everything.

This is why the same model can look solid on one task and shaky on another. Ask it to add a failing test, fix the implementation, and stop when the test passes, and the model gets a strong external signal. Ask it to find every stale pattern tied to a seven-month migration, and the model has to search and certify its own search. That is a harder problem than raw code generation.

task table

The distinction matters because it tells you what kind of control loop you need. Objective tasks can often converge with repetition plus verification. Fuzzy tasks usually need narrower scopes, explicit checklists, or a human review step that accepts you may not get a perfect stop signal.

objective vs fuzzy completion

Why single-pass work breaks down

Single-pass workflows break down when the agent has to solve the problem and certify the result in one shot. It sounds efficient in chat. In practice, it lumps together three different jobs: finding the work, doing the work, and checking whether anything subtle remains.

The first problem is self-assessment. Models are good at recognizing local progress, but they are less reliable at proving global completeness when the search space is broad. If the agent removed ten stale references, that visible progress becomes a source of false confidence. The branch looks cleaner, the diff is non-trivial, and the model leans toward declaring success.

The second problem is attention. Large repos do not present all evidence with equal weight. The loud files are the ones that fail tests, import the old symbol directly, or sit near the files already in context. Quiet leftovers live in helper scripts, fixture data, deployment paths, copied utility modules, and low-traffic tests. A single pass hits the loud evidence first.

The third problem is context pressure. Long sessions pile up plans, diffs, failed ideas, and partial scans. Once the conversation gets heavy, the model starts reasoning through a compressed version of the work instead of the full raw state. Reliability degrades because the session now contains a summary of the repo problem, not the repo problem itself.

Put those three together and you get a common sequence. The agent does something meaningful. The remaining work gets less salient after the first cleanup. The model reads its own progress as stronger evidence than it deserves, then emits a confident completion statement.

Repetition as workaround, and where it stops working

Once engineers notice the pattern, they reach for the same fix. They ask the agent again with a tighter question. Check the diff. Search for the old symbol. Paste the remaining matches back into chat. Ask for another pass focused on leftovers.

This works more often than a single pass suggests it should. The repo changed after the first pass, so the remaining evidence stands out more clearly. A second prompt aimed at one blind spot can direct the model toward files it ignored.

The problem is scale. A strong engineer can run that loop on one important task. A team cannot afford to do it by hand for every migration, cleanup, refactor, and bug fix. And the cost is not just the review time — it is what organizations choose not to attempt. Teams unconsciously limit AI to the tasks where single-pass works: write a test, fix this bug, add this endpoint. The hardest and most valuable work — large migrations, cross-cutting refactors, deep cleanup of technical debt — stays manual or does not happen, because the review cost of running an agent on fuzzy tasks is too high to justify.

That hidden cost is the real impact of the repetition pattern. It is not that agents waste twenty minutes of review time. It is that the pattern silently caps the return on AI-assisted development at the easy tasks.

Repetition also has a hard ceiling. It does not fix a bad plan, a wrong abstraction, or a design mistake the model keeps preserving. If the agent misunderstood the architecture, extra passes produce a more complete version of the wrong solution. A practical diagnostic: after two or three passes, is the system finding new territory or working over the same ground? New territory looks like previously missed files, newly surfaced search hits, or a shrinking set of concrete leftovers. Same ground looks like reworded explanations, churn in the same files, or cosmetic edits around an unresolved core issue.

What the structured version looks like

Once repetition becomes routine, the question is how to make the loop and the stop condition explicit instead of relying on memory and patience.

Two strategies keep appearing. One resets context between passes — each run reads the repo fresh, takes a bounded step, and checks exit conditions. This works well when the task can be verified objectively. The other preserves accumulated state — each pass builds on the previous one's findings. That approach matters more for fuzzy, diagnosis-heavy work, but the challenge is keeping the session coherent as state piles up.

Reliant's Ralph Wiggum workflow is a context-reset loop that makes all of this explicit. Each iteration runs three steps: orient (read the repo, pick the next task), implement (execute that one task, test, commit), and verify (structured check against acceptance criteria). The loop continues while tasks remain, up to a configurable cap.

The key design decisions are visible in the workflow definition:

yaml definition

Three things matter here for the repetition pattern. First, the while condition is an explicit exit rule — tasks_remaining == true && iter.iteration < max_iterations — not the agent deciding it feels done. Second, the verify step forces a structured boolean through a response schema instead of letting the agent write prose about completion. The agent must commit to tasks_remaining: true or tasks_remaining: false. Third, each step forks its own thread (thread.mode: fork, memo: false), so the orient step re-reads the repo fresh each iteration rather than reasoning from accumulated context. Iteration summaries are saved to the main thread, but the working context resets.

ralph wiggim workflow
ralph wiggim iteration

Loading it sends your IP address to YouTube, which may also store data in your browser.

Watch on YouTube (opens in a new tab)

This is not a magic fix. It is the same loop practitioners already discovered — orient, implement, verify, repeat — but with the stop condition, the context strategy, and the verification schema encoded as workflow primitives instead of living in the engineer's head. For recurring work where review cost is already high, the explicit version is easier to trust and cheaper to run than the manual version.

A practical decision framework

Before handing work to an agent, classify the task by completion type and choose the evidence that will stop the loop.

Three questions:

  1. Is completion objective or fuzzy?
  2. What observable evidence would prove the task is done?
  3. If the evidence stays fuzzy, what is the stop rule?

That last question matters because every loop needs a bound. The bound might be three passes, a fixed checklist, a search with zero remaining matches in named directories, or a required human review before merge.

Task table

The framework is simple, but it prevents a lot of avoidable frustration. Many bad experiences with agents come from handing them fuzzy tasks while expecting objective-task reliability, or from treating a weak proxy like a hard stop signal. Once you separate those cases, the behavior gets easier to predict.

Where Workflows Go Next

Once you treat premature "done" as a structural failure mode — not a prompting problem — the next question is how to prevent it systematically. Two camps have emerged. One bets on tighter process: enforced TDD, role separation, structured reviews. The other bets on context management: fresh state, file-based memory, decomposition into bounded chunks.

The next post examines the discipline camp — Superpowers and BMAD — and what happens when you treat enforced process as the primary defense against agent failure.

Try Reliant

Agents that implement, verify and review in a loop until the work is done.