Why Your Agent Keeps Redoing Finished Work
Why Your Agent Keeps Redoing Finished Work
An agent that repeats finished work is almost never confused about what finished. It is reading a different place than the one where finishing got recorded.
This looks like a memory problem and it is filed as one constantly. Give the model a bigger window, feed it more history, remind it what it already did. None of that addresses the actual shape of the failure, which is that completion was written somewhere the next run does not look.
The question is not whether the agent remembers. It is whether the record of "done" sits on the path the agent walks before it decides to act.
Writing after is not the same as reading before
Most systems record completion faithfully. A log line, a summary, a closing message. All of it written at the end of the work, which is exactly the moment it stops mattering.
The next run does not open the log. It opens the queue, the ticket, the file, the row it is about to operate on. If that object carries no mark, the object looks untouched, and an agent looking at an untouched object correctly concludes there is work to do.
Two different surfaces: the one that receives the record and the one that gets consulted. Completion has to land on the second. When it only lands on the first, every run rediscovers the same task and every run is behaving reasonably given what it can see.
The test is short. For any task an agent can pick up, ask what it reads before deciding to start. Then ask whether the finish writes anything there. If those are different places, the work repeats, and no amount of context will fix it because context is not what is being consulted.
State in a transcript evaporates
Conversation history feels like storage. It is not, in any sense that survives.
Transcripts get truncated, compacted, and reset. A session ends. A different worker picks up the same queue with no view of the first one's conversation. The fact was recorded, genuinely, and then the medium holding it went away, which is the difference between writing something down and telling someone.
This is worse when the agent is confident. It says clearly that it published the post, and the statement is true. The next session inherits none of it and operates on a world where the post looks unpublished. Both runs are honest. The handoff between them was never durable.
The rule that survives: if a fact must outlive the turn that produced it, it goes in a file, a row, or a field. Never only in a message. A message is a report about state, not state.
Where the marker actually goes
The marker belongs on the smallest object that the work is about, in the same store the work is selected from.
Queue item consumed? The marker goes on the item. Post published? On the post record. Invoice sent? On the invoice. Not in a parallel ledger that only the write path touches, because a second store means two things to keep in agreement and eventually they disagree, usually when something crashes between the two writes.
Same store, same object, ideally the same write that does the work. That last part is what keeps the marker from lying: if the work succeeds and the marker write fails separately, the system is back to repeating. Where the two cannot be atomic, make the marker the thing that gates the action, and write it first, so a crash leaves a task skipped rather than a task duplicated. Which of those you prefer is a real decision and worth making on purpose. Skipping is usually recoverable by a human noticing. Duplicating may not be recoverable at all, especially when the action leaves the building.
A marker needs enough detail to answer the question actually being asked. Not just that something happened, but which run did it and when. A bare boolean cannot distinguish work completed from work abandoned halfway, and it cannot tell you whether the completion is recent enough to trust.
Idempotency is the backstop, not the fix
The standard answer here is to make every action idempotent so that repeating is harmless. Good advice. It is also insufficient on its own.
Idempotency handles the case where the same action lands twice through the same path with the same key. It does very little when the second attempt is a fresh run that has genuinely decided, from scratch, that new work is needed, because that run is not retrying anything. It is starting something, and it will happily generate a new identifier while doing so.
Keep idempotency for the transport layer, where duplicate delivery is a fact of life. But the reason an agent redoes work is a decision made upstream of any send, and the fix is upstream too. Fix the read path so the decision is right, then let idempotency catch what slips through. The order matters: a system relying only on idempotency is one where every run still does all the reasoning, all the token spend, and all the side effects that happen before the deduplicated call.
Making it cheap to answer
The reason done markers get skipped is rarely disagreement about whether they are a good idea. It is that checking is slightly awkward and skipping it is invisible for a long time.
So make the check the path of least resistance. If selecting work already filters out completed items, no discipline is required and nothing depends on remembering. If it requires a separate lookup that a busy run can omit, it will eventually be omitted, and the failure surfaces weeks later as a duplicate nobody can explain.
Then make the absence of a marker legible. An item sitting in a queue with no completion mark and no active owner is either unstarted or abandoned, and those want different handling. A timestamp plus an owner turns that into a question you can answer by looking rather than by guessing.
None of this is sophisticated. It is bookkeeping, and it is the specific bookkeeping that determines whether an unattended system converges on finished or keeps circling the same work forever. The agent is not forgetful. It is reading the only thing you gave it to read.
Getting done markers onto the read path, and choosing deliberately between skipping and duplicating when a crash lands between two writes, is one of the state patterns in One Agent, One Company ($9.97). Worth a look if your automation keeps rediscovering work it already finished.