
# State Machines

State machines and workflows solve different problems.

A workflow answers, "what work runs next?" A state machine answers, "what state is this thing in, and what events are valid from here?" That difference matters when the thing you are building is not just a sequence of steps, but a long-lived entity that reacts to outside input over time, like an agent session, a chat thread, a review lifecycle, a ticket, or an approval flow.

## Quick example

```php
use Queuety\Enums\StateMachineStatus;
use Queuety\Queuety;

$machine_id = Queuety::machine( 'agent_session' )
    ->version( 'agent-session.v1' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'awaiting_review' )
    ->state( 'awaiting_review' )
    ->on( 'approve', 'completed', ReviewApprovedGuard::class )
    ->on( 'reject', 'awaiting_user' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );

Queuety::machine_event(
    $machine_id,
    'user_message',
    [ 'message' => 'Find competitors for this product' ]
);

Queuety::machine_event(
    $machine_id,
    'approve',
    [ 'approved' => true, 'reviewer' => 'editor@example.com' ]
);
```

This machine starts in `awaiting_user`, moves into `planning` when a user message arrives, lets the queued action emit `planned`, and then waits in `awaiting_review` until an external approval completes the session. The important point is not the code syntax by itself. It is that the lifecycle is explicit, inspectable, and event-driven.

## How a machine runs

When a machine instance is dispatched, Queuety persists the current state name, the public state payload, the definition version and hash, the valid incoming events for the current state, and a durable event timeline.

If the entered state has an `action()`, Queuety enqueues a state-entry job. That action can simply update public state and leave the machine waiting for the next event, or it can emit `_event` and optionally `_event_payload` to drive the next transition immediately. If the state has no action and no terminal status, the machine just waits for external events sent with `Queuety::machine_event()`.

## Choosing between workflows and state machines

Use a workflow when the run is fundamentally step-oriented. Fetch data, for each work, wait, synthesize, finish. That model is a good fit when there is a clear start and finish and the main question is what work should execute next.

Use a state machine when valid behavior depends on the current lifecycle phase. That is the better fit when events may arrive in different orders, when the entity may live for a long time, or when you need explicit lifecycle inspection with states like `awaiting_user`, `planning`, `running_agents`, `awaiting_review`, and `completed`.

## How the two fit together

The two primitives work best together rather than competing with each other. A state action can dispatch a workflow when a transition needs heavier execution, and a workflow can emit an event back into a machine when that work is done. In practice, that lets the state machine govern the lifecycle while workflows perform the expensive or highly structured execution.

## Builder shape

`StateMachineBuilder` stays deliberately small. `initial()` and `state()` define the lifecycle graph. `action()` attaches queued work to one state. `on()` declares which events can move the machine into the next state. `on_queue()`, `with_priority()`, and `max_attempts()` control how state actions are executed, while `version()` and `idempotency_key()` attach durable dispatch metadata. `dispatch()` creates the machine instance.

State actions can also expose an optional `config()` method, using the same resource-policy keys that workflow steps and handlers use. That makes it possible to keep one expensive state transition inside a shared concurrency group or to attach a higher cost weight when an action consumes more budget than a normal step.

The broader resource model is described in [Resource Management](/docs/features/resource-management).

## Contracts

State-entry actions implement `Queuety\Contracts\StateAction`:

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [] ): array|string;
}
```

State actions may also define an optional `config()` method with `concurrency_group`, `concurrency_limit`, and `cost_units` when the queued entry action should participate in worker admission, weighted queue or provider budgets, and other resource policies.

Transition guards implement `Queuety\Contracts\StateGuard`:

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event ): bool;
}
```

## Inspection APIs

Use the facade to inspect running machines:

```php
$status   = Queuety::machine_status( $machine_id );
$machines = Queuety::list_machines();
$events   = Queuety::machine_timeline( $machine_id );
$trace    = Queuety::machine_trace( $machine_id );
$state    = Queuety::machine_state_at( $machine_id, $event_id );
```

Those APIs expose the current state name, lifecycle status, public state, available events, definition metadata, the full transition timeline, and a normalized trace bundle for debugger UIs. Machine list rows include definition version, definition hash, and idempotency key so list screens can show the source/runtime version without loading each machine individually. The trace bundle includes grouped states, raw events, correlated action jobs, logs, artifact references, chunk references, handler input/output, state before/after, context, timings, and structured errors.

## Agent sessions

State machines are especially useful for agent systems that behave more like sessions than pipelines. A session that moves through `awaiting_user`, `planning`, `running_agents`, `awaiting_review`, and `completed` is usually easier to reason about as an explicit lifecycle than as one very long workflow definition with many waits and back-edges.

## See also

- [Neuron Session State Machine](/docs/examples/neuron-session-state-machine)
- [Neuron Memory + Review State Machine](/docs/examples/neuron-memory-review)
- [Agent Orchestration](/docs/workflows/agent-orchestration)
- [Workflow Signals](/docs/workflows/signals)
- [Workflow Dependencies](/docs/workflows/dependencies)
- [Repeats](/docs/workflows/repeats)
- [Artifacts](/docs/features/artifacts)
- [PHP API](/docs/api)
