An approval expires when the state it describes changes
An agent can execute exactly the action a person approved and still be wrong because the target changed while the request waited. Bind approval to the reviewed state, and treat a version conflict as a new decision.
A hypothetical accounts agent finds an unpaid invoice, drafts a reminder and places it in an approval queue. A person reviews the account and approves the message. Payment arrives before the queue releases it. The reminder still goes out, because the tool call is the one the person saw and the approval record is valid.
Nothing changed in the instruction. The fact that justified it changed. The system treated approval as permission to perform an action whenever execution eventually became possible, rather than permission to perform that action while the reviewed conditions still held.
An approval should be conditional on the state a person reviewed. When a relevant record changes before execution, the system should stop, obtain the new state and ask for a new decision. Otherwise human oversight can approve the right action for a world that no longer exists.
The gap has a name
Software security calls this a time-of-check to time-of-use race. A program checks a property of a resource, another process changes the resource, and the program acts as though the checked property were still true. The Common Weakness Enumeration defines the failure in those terms and recommends making the operation atomic where possible, or limiting the gap when it is not. CWE-367
An approval queue lengthens the gap deliberately. The check includes the agent’s retrieval, reasoning and presentation of the proposal. The use happens after a person has found time to review it and after a worker has collected the approved job. Minutes or days can separate the two. A longer expiry time changes how often the race appears. It does not close it.
- 01 Observe The agent reads the target and the facts that justify a change.
- 02 Propose The action is generated against that observed version.
- 03 Approve A person accepts the action and the evidence shown with it.
- 04 Wait The target or a relevant dependency changes.
- 05 Execute The tool applies the old action unless the receiver checks a precondition.
The fix belongs at the receiving system, immediately before the effect. Reading the record again in the orchestrator and then calling an unconditional tool only creates a smaller race. The comparison and the mutation need to be one operation at the layer that owns the state.
HTTP already provides this contract. A client can send a strong entity tag in
an If-Match header with a state-changing request. RFC 9110 requires the origin
server to evaluate that precondition before performing the method and not to
perform it when the current representation no longer matches. The normal failure
response is 412 Precondition Failed.
RFC 9110, section 13.1.1
Kubernetes applies the same pattern through resourceVersion. A client updating
an object supplies the version it read. If the object has changed, the API server
rejects the stale update with 409 Conflict, leaving the client to retrieve the
new object and decide what to do next.
Kubernetes API concepts
The rejection is not an operational nuisance to hide with an automatic retry. It is the signal that the evidence behind the decision has expired.
Three identities answer three different questions
Recent agent designs increasingly carry an actor identity and an operation ID. Both are necessary, and neither binds an action to current state.
| Value | Question it answers |
|---|---|
| Actor and delegation | Who is acting, for whom, and with which authority |
| Operation ID | Whether this is a retry of the same intended effect |
| State precondition | Whether the facts reviewed for that effect still hold |
The distinction matters during recovery. As argued in Every agent retry needs the same operation ID, reusing an operation ID lets the receiver recognise another attempt at one intended effect. A state version asks a separate question before the first successful attempt. An idempotent request can repeat safely and still carry out a stale decision exactly once.
The approval record therefore needs more than the proposed tool name and arguments. It needs a digest of the exact proposal shown, the versions of the records on which the proposal depends, the actor and delegation, and the operation ID that will survive a retry. Approval signs that bundle. Execution submits the same bundle.
This is also why regenerating a plan after approval is not a harmless convenience. Terraform’s documentation distinguishes a speculative plan from the saved plan that is actually applied. It warns that intervening changes can make a speculative result differ from the final effect. HCP Terraform goes further and automatically discards a saved plan when another run changes the state before confirmation. HCP Terraform run modes
The useful pattern is the same outside infrastructure. Preserve the reviewed plan as an executable artefact. Reject it when its declared state no longer holds. Produce a new plan for a new approval rather than quietly updating the old one.
Decide which changes invalidate the decision
Binding an approval to a version of one record is straightforward. Agent decisions often depend on several records and on retrieved material. Rejecting execution whenever anything in that set changes can be safe and needlessly disruptive. Ignoring dependencies because a single version is inconvenient returns to the original failure.
The design work is to state the business preconditions. For the hypothetical reminder, the invoice is still unpaid and no dispute hold exists. For an access change, the person’s employment state, current role and target group membership might all matter. For a configuration update, the reviewed current configuration and policy version might be the relevant set.
Test the unhappy path. Hold an approved action in the queue, change one material fact, then release it. The test passes when no effect occurs and the trace names the failed precondition. Change an irrelevant field and decide whether the same approval should remain usable. That second case checks whether the version is a thoughtful business condition or merely a lock on the whole record.
Where a source offers no version or conditional operation, the adapter can sometimes create one by storing a digest and using a transaction at the write boundary. Where it cannot, the architecture should say so. A short approval expiry and a final re-read reduce exposure, but they do not establish atomicity.
What this does not tell you
A matching state version does not make the approved action correct. The agent may have misunderstood the record, omitted a relevant dependency or presented the proposal badly. The person may have approved it carelessly. Preconditions preserve the conditions that were reviewed. They do not improve the review.
Exact version matching can also reject safe work. A record may change in a field that has no bearing on the decision, and systems with frequent updates can create repeated conflicts. The answer is to identify narrower business preconditions where the receiving API supports them, then keep the conservative rejection when it does not. Availability is not a reason to execute against unknown state.
Nor does every output need this machinery. A draft that remains visibly a draft can be refreshed by its reader. The boundary matters when an agent sends, changes, deletes, releases or commits something after the person who reviewed it has left the path.
The architect approving an effectful agent workflow should ask for the state version next to the approval record. If there is none, the approval describes what a person once saw, not what the system is about to do. The queue has turned human oversight into historical evidence.