Write the state contract before you automate the workflow
When an AI feature hands work between a model, a person, and software, define which states are real and who can change them. A practical contract prevents silent drift.
On this page 5 sections
- Name the states a user can observe, not every internal model step.
- Define the allowed transitions and their owners before building the interface.
- Make uncertain and interrupted work recoverable, not merely visible.
A workflow can look complete while its state is nonsense. The model says “done,” the interface says “review needed,” and the underlying record still looks untouched. Each component may be behaving as designed; the product is not.
I treat this as a contract problem. Before specifying screens or prompts, write down what states mean, who may move between them, and what evidence makes a transition valid. This is useful anywhere an AI feature hands work between a model, a person, and ordinary software.
Start with user-visible states#
A common mistake is to copy internal implementation steps into the product vocabulary. “Prompt sent,” “tool returned,” and “response parsed” may help an engineer debug, but they rarely tell a user what they can do next.
Instead, list the states that change the user’s choices. A document assistant might have:
| State | User meaning | Available next action |
|---|---|---|
| Draft | No work has been submitted | Edit or submit |
| Processing | Work is underway; no final result yet | Wait or cancel |
| Review required | A proposed result exists, but needs a decision | Accept, edit, or reject |
| Applied | The accepted change is reflected in the target record | Inspect or undo, if supported |
| Blocked | The workflow cannot continue without new input | Provide input or abandon |
This is a product model, not a claim that every system needs these exact labels. The test is practical: can a user infer whether the system is waiting, asking, finished, or unable to proceed?
Keep hidden technical detail out of the state list unless it changes a user decision. A retry counter might be operationally important without needing its own user-facing state. Conversely, “waiting for approval” deserves a distinct state if the user must act before anything else can happen.
Specify transitions, not just destinations#
A state diagram without transition rules is decoration. For each move, specify the trigger, the actor, and the evidence that permits it.
For example:
Draft → Processing: a user submits; the system records the submitted input.Processing → Review required: the model returns a proposal that passes basic structural checks.Review required → Applied: an authorized person accepts the proposal and the target system confirms the change.Processing → Blocked: a required dependency fails or the workflow reaches a defined limit.
The distinction between a proposal and an applied change matters. A model returning plausible text is not proof that a record was updated. Likewise, a button click is not proof that the server accepted the action. The transition should depend on the authoritative event, not whichever component reports success first.
A useful specification row is: from state; trigger; authorized actor; validation; resulting state; user-visible evidence; recovery path. Fill one row for each transition that changes a user’s options. If two transitions have different permissions or consequences, they are not the same transition just because they end in the same state.
Decide who owns the truth#
Distributed workflows often have several versions of “current”: the model’s conversation, a browser view, an application record, and a queued job. If those disagree, the product needs a rule for which one wins.
Write the rule in plain language. For instance: “The application record is authoritative for whether a change was applied; the model’s response is only a proposal.” Then specify what the interface does when the record cannot be checked. It should not quietly display “Applied” based only on the model’s assertion.
This also clarifies what counts as evidence. A transition might require a saved record identifier, a confirmed status from the destination service, or a human approval event. Avoid vague conditions like “when the task succeeds.” Success must name an observable signal and its source.
For asynchronous work, include timestamps or freshness cues where stale information could mislead. A status read five minutes ago may be accurate enough for a long-running report and dangerously stale for a payment or permission change. The product requirement should state the acceptable freshness based on the consequence of acting on old state.
Make interruption a first-class path#
Users navigate away, networks fail, and workers restart. If the workflow has no defined behavior for interruption, users will interpret missing progress as either failure or success, often incorrectly.
For every in-progress state, answer three questions:
- Can the work continue without the user’s session?
- If the user returns, how is the same task found rather than duplicated?
- If the outcome is unknown, what can the user safely do next?
Suppose a request times out after submission. The system may not know whether the downstream action completed. Offering “try again” without checking can create a duplicate. Better behavior is to show “Checking status,” use a stable task identifier to reconcile the result, and only offer a retry when the original action is known not to have applied—or when the action is safe to repeat.
Not every product needs sophisticated recovery. A one-off draft generator may simply let the user restart. But that should be a deliberate decision based on the cost of duplicated work and lost progress, not an accidental consequence of the implementation.
Put the contract in the acceptance criteria#
A state contract becomes useful when it changes what the team tests. For each transition, define at least one normal case and one case where the expected evidence is missing or contradictory.
A compact acceptance checklist:
- The visible label matches the authoritative state.
- Only permitted actors can trigger the transition.
- A transition is not shown as complete before its evidence arrives.
- Refreshing or reopening the workflow does not invent a new state.
- A timeout or partial failure leaves a safe next action.
- Repeating an action cannot silently duplicate a consequential change.
These are not all model-quality tests. They are workflow tests around the model. That distinction helps teams avoid treating every inconsistency as a prompt problem when the real gap is ownership, persistence, or permissions.
The next time a feature crosses a model boundary, ask in review: What is the source of truth for each state, and what evidence authorizes the next transition? If the answer is unclear, the workflow is not ready for a polished status badge.