> ## Documentation Index
> Fetch the complete documentation index at: https://docs.duraton.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Durable execution

> Why a long agent picks up where it stopped instead of starting over: each step's result is recorded the moment it completes, and replay skips it.

A workflow makes progress by running its handler again and again. Duraton records the result of every
step the moment it completes, so each pass replays the finished steps from their recorded results and
executes only the next unfinished one.

```ts theme={null}
const charge = await ctx.step.run("charge", () => chargeCard(order));
await ctx.step.sleep("settle", "1h");
const ship = await ctx.step.run("ship", () => createShipment(charge));
```

Interrupt this run after `charge` - a crash, a deploy, an hour of sleep - and the next pass replays
`charge` from its recorded result without calling `chargeCard` again, then continues at `ship`. The
card is charged exactly once.

## When a step fails

Only the failed step retries. Every step before it already has a recorded result, so it is not
re-executed. If `charge` succeeds and `ship` throws, Duraton retries `ship` alone (see
[Retries](/core/retries)).

## When work must go inside a step

The code **between** steps runs on every pass. Only work inside a step is recorded and replayed.

Anything with a side effect, or a result that can change between passes - network calls, database
reads, randomness, reading the clock - must live inside a step, or it repeats on every pass and its
value drifts:

```ts theme={null}
const drifts = Date.now();                                  // re-read on every pass
const stable = await ctx.step.run("now", () => Date.now()); // recorded once, replayed after
```

## The code between steps must be deterministic

Given the same recorded step results, the orchestration around your steps - `if` branches, loops,
building arguments, choosing step ids - **must** reach the same next step every pass. This is a
requirement, not a preference: a branch that takes a different path on a later pass asks for a step
that has no recorded result at that position, and the run's recorded history no longer describes the
code that produced it.

<Warning>
  Never branch on a value the handler reads outside a step. `if (Math.random() > 0.5)` and
  `if (Date.now() > deadline)` decide differently on each pass. Record the value in a step first, then
  branch on the recorded result.
</Warning>

<Note>
  See the **durable execution** example running end to end in [Examples](/start/recipes#retry-a-flaky-call-fail-fast-on-a-bad-one).
</Note>
