TL;DR
My autonomous coding agent once opened the same pull request three times because it crashed mid-task and, on restart, had no idea it had already done the work. Here's the idempotency pattern I built to fix that — check-before-act, stable keys, and a local ledger — plus the five lessons that came out of chasing down duplicate side effects.
The Problem
My agent runs long stretches unattended: multi-step tasks, several tool calls deep, sometimes minutes between the first action and the last. That's fine until something interrupts it mid-flight — a network blip, an out-of-memory kill, me closing my laptop lid at the wrong moment. When that happens, the process comes back and does the only thing it knows how to do: pick up the task and retry it from the top.
The first time this actually bit me, the agent was partway through a task that ended with opening a pull request. It successfully created the branch, pushed the commit, and opened the PR — and then the process got killed before it recorded that the task was finished anywhere. On the next run, it saw a task with no "done" marker, retried it end to end, and opened a second PR for the same change. Two days later, a slightly different crash in the same code path gave me a third one.






