Row 4 of a typical agent control table holds write attempts: one row per side effect the agent wants to make in an external system. The naive version has an id, a tool name, a payload, and a status of pending, running, succeeded or failed. That schema collapses three different facts into one column, and the collapse is what makes resumption dangerous.
The three facts are: what the agent intended, whether the request reached the service, and what the service did with it. A timeout after dispatch tells you the second fact is probably true and the third is unknown. That gap between the response channel (what the agent sees) and the effect channel (what actually happened on the server) is the root of duplicate writes, and it is exactly the failure class a verification-aware tool wrapper is built to handle (the arXiv paper on verified tool calls).
The HTTP specification gives the cleanest vocabulary for the fix. An operation is idempotent when the intended effect on the server of many identical requests is the same as the effect of one request, and RFC 9110 notes that the responses themselves are allowed to differ (restfulapi.net on idempotent REST APIs). Clients may repeat an idempotent request after a connection failure even if the first one succeeded, and they should not repeat a non-idempotent one on their own. Most writes an agent wants to make (charge a card, create a ticket, post a comment, send a transfer) are POST-shaped and non-idempotent by default. So the row has to supply what the HTTP method does not.
Here is a schema that separates the three facts and carries the key:
CREATE TABLE write_attempt (
id uuid PRIMARY KEY,
run_id uuid NOT NULL,
step_index int NOT NULL,
tool_name text NOT NULL,
target_system text NOT NULL,
payload_hash bytea NOT NULL, -- sha256 of canonical payload
idempotency_key text NOT NULL, -- generated once, at intent
state text NOT NULL, -- proposed|approved|dispatched|confirmed|failed|unknown
approval_id uuid,
dispatch_count int NOT NULL DEFAULT 0,
last_error_code text,
last_error_msg text,
response_body jsonb,
created_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL,
UNIQUE (target_system, idempotency_key)
);
The unique constraint on (target_system, idempotency_key) is doing quiet work. It makes a second dispatch of the same logical write impossible to record as a separate row, so a retry path that accidentally creates a fresh row fails loudly at insert time instead of silently at the payment provider.



