Helpful first: Tool design, Timeouts and retries.
The idea
Idempotency means repeated identical requests have the same intended effect as one request. It does not promise identical responses, eventual success, or that all incidental effects, such as logging, happen once. HTTP defines PUT and DELETE as idempotent; a POST tool needs an explicit service contract before automatic retries are safe. See RFC 9110, section 9.2.2.
Where it helps
An agent runner can lose a tool response after the service commits. Treating that timeout as proof of failure can create another ticket on retry. Delegation makes this recovery decision part of your software: establish what the tool guarantees before enabling unattended writes.
Teach this before agents gain permission to create shared resources, because uncertain outcomes require a deliberate retry policy. Even teams consuming hosted tools need to ask for that contract and route unresolved outcomes to a person.
Imagine an in-house release-ticket tool backed by one PostgreSQL database. A runner saves operation key k7 with release v1 before calling it. The service creates ticket #47 and records the key together. Its reply disappears. Repeating k7 with v1 retrieves #47; reusing k7 with v2 is rejected. This is a proposed contract, not a claim about a commercial issue tracker.
What you will be able to check
- Distinguish a retry from a newly intended action and preserve its operation key.
- Write a tool contract covering changed parameters, concurrent attempts, and expiry.
- Fault-test a committed write with a dropped response and inspect the resulting resource count.
What changes in the team
- Today
- An engineer checks the ticket list after a failed submission and decides whether to submit again.
- With an agent
- A durable runner retains the operation key and arguments across retries and restarts, then follows the tool’s documented recovery policy.
- Still your responsibility
- Engineers define intent, approve write permissions, and investigate outcomes the service cannot reconcile.
Work through an example
- Identify one intended release ticket. Generate its key once in the runner and persist it with the arguments. A later, genuinely separate ticket needs a new key even when its contents match; equal text alone does not establish identity.
- For this local design, require non-null caller, operation, and key fields under a composite, non-deferrable unique constraint. Within one transaction, claim that identity, insert the ticket, and save its identifier and parameters. PostgreSQL documents uniqueness and all-or-nothing transaction visibility; the application still needs a duplicate-handling branch.
- PostgreSQL’s unique-index check waits for an uncommitted competing insertion. If the competing transaction commits, roll back the losing transaction and read the saved record in a fresh one. Return its ticket identifier for matching arguments; reject a mismatch. Bound the wait and report uncertainty instead of issuing an uncoordinated write.
- Drop the first reply after commit and restart the runner. Its retry should take the lookup path. This single-database design does not make an external ticket API transactional with your local records: use that provider’s documented deduplication or reconciliation mechanism and test its failure window.
The engineering decision
Specify caller and operation scope, parameter comparison, retention, and concurrent-request behavior. Stripe illustrates why contracts matter: its API v1 can replay saved failures and permits pruning keys after 24 hours; v2 has different retry semantics. Never infer either policy from the word idempotent. A key is not authorization; keep access checks in the tool.
- Generating a new key for every attempt defeats duplicate detection.
- Checking for a key and then writing without database-enforced coordination lets concurrent callers both proceed.
- Pruning records before delayed retries arrive can turn an old intent into a new action.
- Putting “do not duplicate” in a prompt supplies neither durable coordination nor enforced permissions.
Try it with your team
Lose the reply, keep one ticket
- Build a local test endpoint with ticket and operation tables; document the duplicate-response contract.
- Inject a disconnect after commit, then retry with the same key and payload.
- Submit two matching calls concurrently, then retry that key with changed release data.
- Inspect stored rows and results; document how a retry after retention expiry is handled.
You have an answer when…
- Lost replies and concurrent matching calls leave exactly one ticket and converge on its identifier.
- Changed parameters produce a mismatch without altering the original ticket.
- The team can identify the recovery path for expired keys and external side effects.
Read further
At the service boundary, can your team demonstrate one ticket after a lost reply and two concurrent attempts, then explain what happens after the key expires?
Original video comparisons