AI agents
State Machines, Policies, and Explicit Orchestration
Represent agent workflows with durable states, guarded transitions, timers, and policy checks.
By the end you can
- Define explicit agent orchestration as an operational contract rather than a capability label
- Contrast Conversation-led control with State-machine control in “A case-management agent reopened a closed investigation after a late tool result”
- Trace “A stale message can resurrect work that policy already terminated” through a concrete execution path
- Produce “Model a durable task lifecycle” with evidence for “The authoritative task status exists outside the transcript”
Example
One part of Knight's system knew the orders were filled; the part still trading did not
On 1 August 2012 a new deployment at Knight Capital repurposed a flag. The flag woke dormant “Power Peg” routing code that Knight had stopped using in 2003 and left on one of eight SMARS servers. The retired path started routing and did not stop. In about 45 minutes, 212 incoming parent orders produced 4 million executions in 154 stocks for more than 397 million shares. The loss was $460 million.
The authoritative state existed. It was simply held somewhere the acting component could not read. The SEC's order puts the split in one sentence: “Although one part of Knight’s order handling system recognized that the parent orders had been filled, this information was not communicated to SMARS.” SMARS was not confused about the state of the task. It had no state of the task to be confused about. So it went on filling an obligation that was already discharged. On 16 October 2013 the SEC ordered Knight Capital Americas LLC to pay a $12 million penalty for violating the Market Access Rule.
An agent that reads its lifecycle out of a transcript is in the same position as SMARS. The finished state may be perfectly well known to the system and still not be known to the component that can still act.
- Decision at stake: Represent agent workflows with durable states, guarded transitions, timers, and policy checks — so that “filled”, once true, is readable by every component that can still take an action.
- Hidden assumption: That the component doing the work can see what the rest of the system already knows. At Knight the parent orders were recognized as filled and SMARS was never told.
- Primary control question: A stale or absent status can resurrect work that policy already terminated — here, a 2003 code path reactivated in 2012 by a flag the new deployment had repurposed.
- Evidence to collect: The authoritative task status exists outside the transcript, and every actor that can still move the task can query it. The SEC's order is what its absence bought: 4 million executions across 154 stocks in about 45 minutes.
Comparison
Which form of explicit agent orchestration fits the task?
Conversation-led control, state-machine control, and a workflow language disagree about where the truth of a task lives. The choice is working when the authoritative task status exists outside the transcript. Anyone arriving late can then ask the system what state the task is in instead of inferring it from messages. It has failed when a stale message can resurrect work that policy already terminated.
The third column is not a hypothetical. Its properties are published rather than argued. AWS Step Functions models a workflow as a state machine, and the documentation prints the contract in numbers: “Standard workflows have exactly-once workflow execution and can run for up to one year.” Express workflows take the opposite side of the same trade: at-least-once execution, up to five minutes. One year against five minutes is what “strong long-running support” actually measures.
Exactly-once against at-least-once matters more for this lesson. At-least-once is a documented delivery property. Duplicate and late events are therefore a design input for the guard, not an unlucky edge case to be surprised by in production.
Underneath the service, the Amazon States Language defines the machine as a JSON object. It terminates only on a Terminal State — Succeed, Fail, or an End State. The operational overhead in the third column is the price of that. Someone has to write the states down.
Conversation-led control
The transcript implicitly determines what happens next.
- Flexible prototype
- Ambiguous authority
- Hard recovery
State-machine control
Explicit states and guards govern transitions.
- Auditable
- Supports replay
- Requires lifecycle design
Workflow language
A durable orchestrator represents tasks, timers, and compensations.
- Strong long-running support
- Operational overhead
- Good enterprise fit
Key idea
A stale message can resurrect work that policy already terminated
Long-running tasks accumulate delayed results, retries, and human responses. If every event is accepted without checking the current state and version, old information can trigger invalid transitions.
The requirement is far older than any agent framework. There is, in general, no fact of the matter about which of two events in different processes came first. The relation “happened before” is only a partial ordering, and nothing in the arriving message tells you. Leslie Lamport showed that in 1978, in Communications of the ACM.
His remedy is mechanical rather than interpretive. It comes as two implementation rules, IR1 and IR2. The second of them reads: “To meet condition C2, we require that each message m contain a timestamp Tm which equals the time at which the message was sent.” The receiver must then advance its own clock past Tm. Those two rules together are what makes the Clock Condition — if a → b then C(a) < C(b) — hold.
A version stamped on every event and checked at the guard is that same discipline under a different name. Without it there is no ordering to appeal to. The system will do what the last message says.
Use versioned state, event correlation, and guards that reject stale or already-consumed results.
A late reply looks exactly like a current one when there is no version to check it against, and the system will act on it.
The transcript is not the authoritative lifecycle state
A state machine defines valid task states and the events that permit transitions between them. It can encode approvals, timers, retries, cancellation, escalation, and terminal conditions outside the model.
That the durable record should outrank the transcript is not merely a preference. For at least one production orchestrator the equivalence has been proved. A 2021 paper, Durable Functions: Semantics for Stateful Serverless, formalises Azure Durable Functions. It shows that a record-replay execution model is equivalent to the fault-free high-level model. Record-replay persists progress as a logged history rather than as a language-runtime checkpoint. The abstract states the two problems it takes on: “Next, we demystify how the DF runtime can (1) execute in a distributed unreliable serverless environment with compute-storage separation, yet still conform to the fault-free high-level model, and (2) persist execution progress without requiring checkpointing support by the language runtime.”
Notice what that architecture makes impossible. The logged history is not a summary of what happened, kept alongside the real state. It is the only authority, because the code is re-executed from it. Nothing can be true of the workflow that is not in the record.
The model can still decide among valid transitions or produce artifacts. It should not infer the authoritative lifecycle state from a transcript when durable state exists.
When the transcript and the durable record disagree, only one of them can be argued into a different answer.
Visual
The guard decides whether a late event counts — and a standards body already named it
State, event, guard, transition: four words, and they are not this lesson's coinage. The W3C fixed them in State Chart XML (SCXML), a Recommendation since 1 September 2015. The specification puts the whole model in one sentence: “Transitions between states are triggered by events and conditionalized via guard conditions.”
In that same specification the guard is not a concept but an attribute you can look up. It is called cond. It sits on the <transition> element. It is Required: false, its type is a Boolean expression, and its default value is 'true'. The documentation describes it as “The guard condition for this transition. See 3.13 Selecting and Executing Transitions for details.”
The default is the line to read twice. A transition whose guard nobody wrote is not an unguarded transition awaiting a decision. It is a transition whose guard evaluates to true. That is exactly how a late event gets accepted.
The formalism behind the notation is older. Statecharts arrived in 1987, after three years of applying the formalism to the specification of one particularly complex system. David Harel's abstract says precisely what they add: “Our diagrams, which we call statecharts, extend conventional state-transition diagrams with essentially three elements, dealing, respectively, with the notions of hierarchy, concurrency and communication.” Hierarchy, concurrency and communication are the three things a flat transition table cannot express. They are also the three that an agent lifecycle with nested waits, parallel branches and cross-task signals requires. Transitions and terminal states still need separate owners and separate tests.
- 1
State
A durable representation of the task lifecycle.
- 2
Event
A tool result, user action, timer, policy change, or model proposal.
- 3
Guard
A deterministic condition required for a transition.
- 4
Transition
An allowed movement that updates state and may trigger work.
- 5
Terminal state
Success, partial success, refusal, cancellation, or failure.
Steps
Model a durable task lifecycle
Model the full lifecycle of one long-running task, including its terminal states and the events that must be refused after them. Then test it with a message that arrives after a terminal state. See whether the model quietly restarts the work. SMARS kept routing for about 45 minutes because nothing it could read said the parent orders had been filled.
Borrow the shape instead of inventing one. The Amazon States Language ends a machine only at a Terminal State — Succeed, Fail, or an End State. A2A separates the four states that end a task from the two it labels interrupted. SCXML gives every transition a cond that defaults to 'true', so the states you decline to guard are the ones that will accept anything. Lamport's rule supplies the version: each message carries the clock value it was sent with, and the receiver advances past it.
The lifecycle is good enough when every component that needs to know the state of the task can read it from the record rather than replay the conversation.
- 1
List lifecycle states
Include pending input, ready, executing, waiting, paused, and terminal states.
- 2
Define events
Name tool results, approvals, cancellations, timers, and policy changes.
- 3
Write transition guards
Require correct version, authority, evidence, and budget.
- 4
Handle stale events
Decide whether to ignore, reconcile, or alert on late messages.
- 5
Test every terminal state
Verify no later event can restart work without an explicit new task.
Named terminal states let a system refuse late work
Use state machines for obligations and lifecycle. Use models for judgments that cannot be encoded as fixed transitions. When a late message arrives, the engineer who owns the orchestration checks two things. Would accepting it restart work the policy has already ended? And does the state it claims agree with the state the system holds outside the conversation?
The agent protocols reached the same conclusion as the workflow engines, and wrote it into a machine-readable file. The Agent2Agent (A2A) protocol reached version 1.0.0 on 12 March 2026, and 1.0.1 on 28 May 2026. Its TaskState enum has nine values, and the specification does not treat them as a flat list. TASK_STATE_COMPLETED, TASK_STATE_FAILED, TASK_STATE_CANCELED and TASK_STATE_REJECTED are marked terminal states. TASK_STATE_INPUT_REQUIRED and TASK_STATE_AUTH_REQUIRED are labelled interrupted states — a task that has stopped moving but has not ended. The protocol file carries the distinction itself, in the comment on TASK_STATE_COMPLETED: “Indicates that a task has finished successfully. This is a terminal state.”
That is what makes refusal possible. A state named terminal in an enum is something a receiver can check an arriving event against and decline. A state that is only implied by the last few messages gives it nothing to decline with, and the work resumes.
Every obligation left to a model's discretion is one nobody can later prove was met.
Key takeaways
- The authoritative state can exist and still never reach the component that acts: one part of Knight's system recognized that the parent orders had been filled, SMARS was not told, and about 45 minutes cost $460 million.
- A state machine defines valid task states and the events that permit transitions between them. SCXML names the guard cond, marks it Required: false, and defaults it to 'true', so an unwritten guard is a guard that always passes.
- Lamport's 1978 rules IR1 and IR2 require every message to carry a timestamp Tm and the receiver to advance past it. A version stamped on every event and checked at the guard is that same discipline.
- Harel's statecharts (1987) add hierarchy, concurrency and communication to a conventional state-transition diagram: the three things a nested, parallel agent lifecycle needs.
- Durability is a published contract, not an aspiration. Step Functions Standard workflows are exactly-once for up to one year, Express at-least-once for up to five minutes, and at-least-once means stale duplicates are expected input.
- Terminal states have to be named to be enforceable. A2A marks four of its nine TaskState values terminal and labels two others merely interrupted, so a late event has something to be refused against.