Tutorial
DraftAdd a human approval boundary
Split preparation from execution so an agent can assemble consequential work while a person controls whether that exact action proceeds.
- For
- Builders whose agents can publish, send, merge, spend, or change live state
- You will finish with
- A fail-closed approval step tied to one reviewed action and its evidence
Before you start
- An agent task that produces a saved proposal
- A way to identify proposal content without relying on a transcript
- A separate component or role that can authorize the live action
A human approval boundary belongs immediately before the action whose consequences matter. The agent may prepare the work, explain it, and collect evidence. It may not cross that boundary with a general “approved” flag.
This tutorial uses a customer-facing status update as the example. The same shape applies to merging code, issuing a refund, changing access, or starting a deployment. The risk and reviewer will differ, but the boundary should still authorize one exact action.
1. Name the action that needs approval
Write the boundary as a verb and object:
publish status update STATUS-218 to the public status page
Avoid broad boundaries such as “approve the run” or “allow production.” They do not say what the person is permitting. If a workflow has several consequential actions, give each one a separate boundary.
For the example, the agent may:
- read incident notes supplied to the run;
- draft the status update in a private workspace;
- identify which claims are supported by which note;
- request approval to publish the saved draft.
The agent may not publish, edit the approval record, choose the approver, or change the draft after approval and reuse the same authorization.
Expected outcome: you can point to one operation that cannot occur without a valid approval.
2. Separate preparation from execution
Expose two operations with different permissions:
prepare(input) -> proposal
commit(proposal_ref, approval_token) -> action_result
prepare must be safe to run without approval. It writes only to a private or
reversible location. commit is the narrow operation that can affect the live
system.
Do not hide both operations behind one agent tool that pauses for a click and then continues with its existing authority. The commit operation should verify the approval itself. That way, bypassing the user interface does not bypass the boundary.
Expected outcome: a prepared draft can wait, expire, or be rejected without publishing anything.
3. Build the approval request
The reviewer needs the proposed action, its consequences, and enough evidence to make a decision. They should not have to infer the action from a long agent transcript.
Use a compact approval request:
request_id: APR-551
run_id: RUN-8860
action: publish status update STATUS-218
target: public status page
proposal_ref: sha256:...
summary: report delayed imports and the active mitigation
evidence_refs:
- incident/observation-14
- checks/claim-support.json
known_limits:
- recovery time is not yet confirmed
requested_by: agent-runner
expires_at: 2026-08-13T15:30:00Z
Render the actual content or diff beside this metadata. A hash proves which content was reviewed, but a person cannot review a hash.
Do not mark the request approved by default. The allowed states should make
waiting and rejection explicit, for example PENDING, APPROVED, REJECTED,
EXPIRED, and CANCELLED.
4. Ask a person to make the decision
The reviewer should see three controls:
- approve this exact action;
- reject it with a reason;
- return it for revision without authorizing execution.
The screen or message should show the target, visible proposal, evidence, known limits, and expiry. It should also state what will happen after approval. For example: “Approval permits one attempt to publish this exact draft before 15:30 UTC.”
The reviewer must not rely on the agent’s confidence as evidence. If the draft says service is restored, the request should point to the observation that supports that claim. If there is no supporting observation, the reviewer can reject the request or revise the claim.
Expected outcome: approval is an informed decision about a concrete action, not a reward for a persuasive explanation.
5. Issue a narrow, single-use token
After approval, the approval authority issues a token or signed record bound to the decision:
approval_id: APPROVAL-992
request_id: APR-551
action: publish
target: status-page/STATUS-218
proposal_ref: sha256:...
expires_at: 2026-08-13T15:30:00Z
max_uses: 1
approved_by: operator-17
The approval authority should not share signing credentials with the agent. The commit operation validates the action, target, proposal reference, expiry, and use count. A token for a status update must not authorize a deployment. A token for one draft must not authorize a later revision.
If a signed token is not appropriate for your environment, store the approval
in a protected service and let commit query it. The important property is
the same: the agent cannot create or widen the permission it consumes.
6. Commit the action and record the result
Before execution, commit should check:
- The approval is valid and unused.
- The current proposal still matches
proposal_ref. - The action and target match the approved scope.
- The approval has not expired or been cancelled.
- Required evidence is still within its validity window.
Consume the approval at the point where the action starts, or use an atomic reservation if the target supports retries. Record both the attempt and the result. A timeout after the target accepted the action is not safe to retry blindly.
approval_id: APPROVAL-992
action_attempt_id: ACTION-4401
proposal_ref: sha256:...
started_at: ...
result: succeeded
target_ref: status-page/event-781
Expected outcome: the record connects the reviewed proposal to the action that actually occurred.
Check the boundary
Test refusal paths as seriously as the happy path:
- Call
commitwithout approval and confirm no live action occurs. - Change one character in the proposal after approval and confirm the token is rejected.
- Reuse a consumed token and confirm the second action is refused.
- Present an expired or cancelled approval and confirm the workflow returns to a non-executing state.
- Change the target while keeping the proposal unchanged and confirm the token cannot authorize the new target.
- Approve a request, make the supporting evidence stale, and confirm execution pauses for a new review when freshness matters.
Handle failures
If the request expires while waiting, create a new request from the current proposal. Do not extend the old authorization silently.
If the reviewer asks for changes, produce a new proposal reference and a new approval request. Keep the rejected request so the history explains why the work changed.
If execution fails before the target accepts the action, record the failure and request a new token for a retry. If acceptance is uncertain, reconcile against the target first. Retrying an uncertain payment, publication, or permission change can duplicate the consequence.
If the approval service is unavailable, wait. A human approval boundary that defaults to execution during an outage is not an approval boundary.
Next steps
- agent-systems
- trust-boundary
Keep exploring