Skip to main content
L10.6

State Machines for Tool Workflows

Goal

Represent a multi-step tool workflow as explicit states, events, allowed transitions, and terminal outcomes so invalid loops and side effects can be detected.

A board game makes some moves legal only from certain positions. If your piece is still at START, you cannot claim the reward reserved for FINISH just because someone writes "finished" on a card. The game board and transition rules determine which moves are valid.

A state machine gives a software workflow that same explicit structure. A state says where the workflow currently is, an event proposes what happened next, and a transition rule decides whether that move is legal. A guard is an extra condition on an otherwise possible move—for example, approval must match the current order and refund amount before the workflow may enter an approved state.

This explicit structure matters as soon as a workflow can call more than one tool, because a free-form “model decides the next step” loop is difficult to audit.

State answers “where are we now?”​

For a refund workflow, states might be:

START
ORDER_LOADED
ELIGIBILITY_CHECKED
AWAITING_APPROVAL
REFUND_REQUESTED
DONE
FAILED

An event moves the workflow between states only when a defined transition allows it.

For example:

START + order_loaded → ORDER_LOADED
ORDER_LOADED + eligible → ELIGIBILITY_CHECKED
ELIGIBILITY_CHECKED + approval_required → AWAITING_APPROVAL

The model can help choose or describe an event, but application code owns the transition table.

Invalid transitions should fail closed​

If the workflow is in START, a direct refund_succeeded event should not jump to DONE. That trace skipped the lookup, eligibility check, and approval boundary.

Rejecting impossible transitions protects both reliability and security.

State carries trusted workflow facts​

State can include trusted fields such as:

order_id
authenticated_user_id
approval_status
attempt_count
last_tool_result_id

Do not rebuild these solely from model prose on every turn. The application should maintain durable structured state.

Terminal states stop the loop​

A workflow needs explicit stop conditions. DONE and FAILED should not keep asking the model what to do next.

A maximum-step guard is also useful when unexpected transitions repeat without reaching a terminal state.

State machines make tests concrete​

Instead of testing only final text, you can test:

  • which transitions are permitted;
  • which state requires approval;
  • which events increment retry counters;
  • which tool can run in each state;
  • whether terminal states reject further actions.

These checks remain deterministic even when model suggestions vary.

Guards add conditions to otherwise valid transitions​

Sometimes the same event is legal only when a trusted condition holds. For example, approved may move AWAITING_APPROVAL to APPROVED only if the stored approval matches the current refund amount and order ID. That condition is a guard on the transition.

Keeping guards explicit prevents a state name from becoming misleading. A workflow should not enter APPROVED merely because the model emitted the word “approved.” The transition function should read the trusted approval record and reject mismatches.

Separate workflow state from conversation history​

Conversation text can help the model understand what happened, but it is a poor source of truth for operational state. Messages can be summarized, truncated, duplicated, or manipulated. Store current state, attempt counts, tool result IDs, and approvals in structured application data, then render only the necessary parts into model context.

This pattern becomes especially important in Level 11 agents, where loops can span more steps. If the application can reconstruct the state machine without rereading free-form model prose, recovery and testing become much simpler.

Predict

A workflow is AWAITING_APPROVAL and the model proposes execute_refund before approval is recorded. What should the transition logic do?

Run the local Lab​

python labs/notebooks/level-10/l10-06-state-machine.py
  1. Run the command. The valid trace ends with valid final state: DONE, and the approval-skipping trace prints blocked: illegal transition: AWAITING_APPROVAL + refund_requested.
  2. Add one dangerous transition to TRANSITIONS, directly after the "approved" line:
("AWAITING_APPROVAL", "refund_requested"): "REFUND_REQUESTED",
  1. Rerun. The script now stops with AssertionError: approval skip should be blocked: a single extra row in the table made the skip legal, and the script's safety check caught it.
  2. Remove the line again. The whole approval rule lives in that table, which is why it should be reviewed like code.

Loading lab…

Write the core logic yourself​

Open:

labs/notebooks/level-10/l10-06-state-machine-exercise.py

Implement the transition loop and reject any event that is not legal from the current state. The exercise must reach DONE on the valid trace and raise on the approval-skip trace.

Run the starter after each change:

python3 labs/notebooks/level-10/l10-06-state-machine-exercise.py

A correct implementation ends with a PASS: marker. Only after you have a working version, compare your approach with the solved deterministic script used by the Level smoke tests.

Quick Check

1. What does an explicit state machine add to a tool workflow?
2. Where should trusted approval status live?
3. Why define terminal states?

0 of 3 questions answered.

Key Takeaways

  • A state machine makes workflow position and legal movement explicit.
  • Application code owns transitions and trusted state.
  • Impossible transitions should be rejected.
  • Terminal states and step budgets prevent endless loops.
  • State-level tests are deterministic even when model proposals vary.

Next Lesson

Complete the mini checkpoint. Then apply these boundaries to web and API tools, where network behavior and remote data add another failure surface.

References

Lesson actions

Completion is stored locally on this device.

View progress