Human approval in an AI system is a lifecycle, not a yes/no field. The minimum useful model separates draft, pending, changes requested, approved, rejected, expired and failed states. It also defines which transitions are legal and which data becomes immutable at each transition.
This structure prevents stale approvals, duplicate side effects and the quiet loss of a request during revision.
Model the proposal before the decision
An approval record should contain an immutable proposal identifier, action type, destination, material payload, source context reference, creator, creation time, expiry and current state. The proposed connector call belongs in the record before the reviewer sees it.
The reviewer interface can present a readable summary, but the decision must bind to the machine-readable action. If the summary says “send the reply” while the stored action points to another recipient, the interface has failed its core job.
Use seven states with explicit meaning
Draft
The system is still constructing the proposal. No reviewer decision is valid and no side effect can execute.
Pending
The proposal is frozen and available to an authorized reviewer. Its expiry clock has started. Any change to a material field creates another version.
Changes requested
The reviewer has returned the proposal with an attributable note. The pending version is superseded immediately so it cannot be approved in another browser tab.
Approved
An authorized reviewer accepted the exact stored version. The action is eligible for one execution. Approved does not mean executed.
Rejected
The reviewer refused the proposal. The stored action is permanently ineligible for execution.
Expired
The decision window closed. A late approval is refused, and the system must create a fresh proposal if the action is still needed.
Failed
The approved action reached execution but did not complete. This state preserves the approval while making recovery visible.
Keep decision state and execution state distinct
A single status column often becomes overloaded. “Approved” gets replaced by “sent,” and the system loses the evidence that a person authorized the action. A cleaner model has an approval state and an execution state.
The approval state answers whether the proposal may run. The execution state answers whether it has not started, is running, succeeded, failed or entered reconciliation. This split supports safe retry. An operator can see that authorization is still valid while the outcome is uncertain.
Define invariants before endpoints
Four invariants carry most of the safety value:
- Only pending, unexpired proposals can receive a decision.
- A material revision creates a new version and supersedes the old one.
- An approved proposal can create at most one intended side effect.
- Every transition records actor, time and reason.
Enforce these in database constraints or transactional application logic. A disabled button in the browser cannot resolve two concurrent approvals arriving at the server.
Design request changes as a first-class transition
Reviewers often need to correct a figure, recipient or tone. If the interface offers only approve and reject, they will move the revision into email or chat, breaking the evidence chain.
Skybridge accepts an explicit change request with a note. The old proposal moves out of pending. A revision process creates a new draft while preserving routing from the original action. The new proposal receives its own approval link and identifier.
The revision process also needs a safety net. A model may return corrected text without calling the tool expected to create a new request. Skybridge handles the proposal construction deterministically after the model supplies the revised content. If revision produces no usable replacement, the workflow restores a recoverable state.
Make delivery safe for real messaging infrastructure
Approval links travel through systems that inspect them. Email-security products and messaging previews can fetch a URL automatically. A GET request must therefore display the proposal without changing its state.
Approve, reject and request-changes actions should use authenticated or token-scoped state-changing requests with a confirmation step. Tokens need high entropy, expiry and single-use behavior. The reviewer identity and authorization model should match the action's consequence.
For sensitive actions, a bearer link alone may be inadequate. Require an authenticated session, enforce workspace membership and restrict the approval permission to the right profile.
Test race conditions deliberately
Open two sessions on the same pending proposal and run these tests:
| Race | Expected result |
|---|---|
| Approve twice | One execution, one rejected duplicate transition |
| Approve and reject together | Exactly one decision wins |
| Revise and approve together | Superseded version cannot execute |
| Approve at expiry | Server time decides consistently |
| Timeout after remote success | Reconciliation occurs before retry |
| Reviewer loses permission | Decision is refused at request time |
The last test matters because access can change after the link was sent. Authorization should be evaluated when the decision is made, not only when the request is created.
Show the reviewer the material decision
The interface should display the destination account, recipient or target record, proposed content, consequential parameters, source context and expiry. It should name what approval will do in plain language.
Long source material can be summarized, but the material fields should not be hidden behind a vague agent explanation. If a value changed since the proposal was created, mark it and require a new version where relevant.
How Skybridge uses the state machine
Skybridge stores the proposal and intended action, routes the review through configured channels, supports revision, prevents old versions from executing and records downstream failure separately. Workspace roles decide who can review. The pattern is applied to approval-gated actions inside each delivered Compsia production system.
Start the design by listing the exact actions that require a reviewer. Then implement the invariants and race tests before polishing notifications. The human-in-the-loop comparison shows how the operating models differ, while the agent tool authority guide defines which actions should reach this state machine at all.
Continue reading: How to give an AI agent tools without giving it unlimited authority.