Every distributed system eventually does the same thing twice, and an agent system is no exception. A task gets dispatched, the caller waits, the call takes too long, the caller assumes it failed and retries. Now the same unit of work is running in two places. Or a worker picks a job off a queue, starts on it, and crashes before it can mark the job done, so another worker picks it up again. Or a process is restarted and replays the step it was on.

With a plain web request, duplicate execution is usually just wasted compute. With an AI agent, it is worse, because agent actions have real side effects. Duplicate execution of AI agents does not just burn double the tokens. It can open two pull requests, run a deploy twice, post the same comment twice, or apply a migration that was only ever meant to run once. The token waste is annoying. The corrupted state is the actual problem.

This is a solved problem in distributed systems, and none of the solutions are new or exotic. They just have to be applied deliberately, because an agent will execute a duplicated action with the same confidence it executes the first one.

Why "just deliver once" does not save you

The instinct is to make the queue deliver each task exactly once and call it done. It will not work, because exactly-once delivery is not something a network can actually promise. Any system that retries on failure is, underneath, an at-least-once system: it would rather deliver a message twice than risk delivering it zero times. That is the right default for not losing work, and it is precisely why duplicates happen.

So the goal is not to prevent the task from ever arriving twice. The goal is to make arriving twice harmless: the second execution should produce the same result as the first, without doing the side effect again. The jargon for this is idempotency, and it is the foundation everything else builds on.

Idempotency keys: check before you act

The core move is to give every unit of work a stable, unique identifier that travels with it, and to make each side-effecting action check that identifier before it runs.

Concretely: before the agent opens a pull request for task 7894, it asks the record "has the PR for 7894 already been created?" If yes, it returns the existing one instead of opening a second. If no, it creates the PR and records that it did, keyed to 7894. A retry of task 7894 now finds the recorded result and returns it, rather than creating a duplicate.

The key has to be stable across retries (derived from the work itself, not generated fresh each attempt) and the check-and-record has to be reliable. This turns "do the action" into "do the action at most once for this key," which is exactly what you want for anything with a side effect. It pairs directly with the retry logic covered in idempotency and retries when your worker is an LLM, because a retry is only safe when the thing being retried is idempotent.

Leases: one owner at a time

Idempotency keys stop a side effect from being applied twice. They do not, on their own, stop two workers from grinding through the same expensive agent task in parallel and both trying to commit at the end. For that you want a lease.

A lease is a claim: a worker takes ownership of a task, and while it holds the lease, no other worker may pick that task up. To survive a dead worker, the lease has a timeout, so a task whose owner vanished can be reclaimed and retried rather than stranded forever.

The timeout is also where the classic hazard lives, and it is worth stating plainly because it catches people. Say worker A takes a lease, then pauses (a long garbage-collection pause, a slow model call, a network stall) for longer than the timeout. The system assumes A is dead and hands the task to worker B. But A was not dead, only slow. Now A wakes up, still believing it owns the task, and both A and B try to finish it. The lease alone did not save you, because a slow worker looks exactly like a dead one.

Fencing tokens: stop the zombie from committing

The textbook fix for that hazard is a fencing token. Each time the lease is granted, it comes with a number that only ever increases. Worker A holds token 33; when A is presumed dead and B takes over, B holds token 34. The resource that actually accepts the work (the store, the merge, the deploy) remembers the highest token it has seen and rejects anything carrying a lower one.

So when the zombie worker A wakes up and tries to commit with token 33, the resource sees it has already accepted token 34 and refuses. A cannot corrupt anything, because the point where work becomes real checks the token first. The lease decides who should be working; the fencing token enforces who is allowed to commit. You need both, and the enforcement has to live at the resource, not in the worker, because a confused worker cannot be trusted to police itself.

Make the commit the single source of truth

The pattern underneath all of this is to funnel every side effect through one point that can say no. A conditional write (only apply if the state is still what I expect) or a compare-and-set on the final transition means that even if two workers run to completion, only one of them wins the commit and the other is cleanly rejected. Modeling the flow as explicit states with guarded transitions, as in state machines for agent workflows, gives you a natural place to put that single guarded step, and replaying a step after a restart (see resumability) stays safe because the guard rejects the stale attempt.

The honest limitation

None of this delivers true exactly-once execution, because that does not exist. What it delivers is exactly-once effect: the work may run more than once, but the world only changes once. That distinction is the whole game, and it means your protection is only as good as the boundary that enforces it. An external system that is not idempotency-aware (a third-party API with no dedup, a webhook that fires side effects on every call) can still double-apply, so the check has to sit at every boundary where a duplicate would matter, not just the ones you control.

It also is not free. Idempotency keys need a store and a discipline about deriving them. Leases and fencing tokens add moving parts and a resource that enforces them. The cost is real, and for a low-stakes action that is cheap to repeat, it may not be worth it. The judgment is to spend this complexity where a duplicate is expensive or destructive, and to skip it where a duplicate is merely a shrug.

This is a property we build into Loopsfinity by construction rather than hope for: a unit of work runs under a single owner, and the point where a change becomes real rejects a stale attempt, so a timeout or a restart cannot turn one deploy into two. The mechanism is ours, but the principles are the standard ones, and any agent system touching real state should be able to say how it survives the same task arriving twice. The wider picture is in AI agent architecture.