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

# The journal and replay

> Every interaction of a run with the outside world is an entry of its journal. Durability, replay, tests and observability all come from it.

## What the journal records

A run's journal is the ordered list of everything that crossed the sandbox's walls:

| Entry | Records |
| - | - |
| `run_started` | The input, the script and toolset digests, the run's random seed, its deadline |
| `schedule_tool_call`, `tool_call_completed` | A call and its arguments, then its result or failure |
| `start_timer`, `timer_fired` | A `sleep`, a `setTimeout`, a signal's timeout |
| `signal_awaited`, `signal_received` | A wait for a signal, and the signal that resolved it |
| `log` | A `console.*` line |
| `complete_run`, `fail_run`, `run_aborted` | How the run ended |

Each entry carries its **callsite**, the line of the script that caused it, which is how the dashboard puts each call
beside its line. Values (arguments, results, signals, the input and output) are identified by the SHA-256 of their
canonical JSON; values above 64 KiB are stored apart, in S3.

> [!NOTE]
>
> **The journal is the only source of truth.** The dashboard, the OpenTelemetry traces, the metrics and the webhooks
> are projections of it. No feature has a side channel.

## Replay

What runs inside the sandbox is computed again; what came from outside is read from the journal. To rebuild a run, a
worker starts the script from the beginning with the run's input and seed. Each `await` that has a recorded outcome
returns it immediately. When the script reaches the end of the journal, it continues live.

This is how a run survives a crash, a deploy, or ten days of `sleep`: nothing but the journal is needed. Workers keep
recently active runs in memory, so most activations need no replay at all; replay is what makes losing that memory
harmless.

It also means that **any run can be re-executed**, anywhere, with the same result:

* `rewind replay <run>` re-executes a run locally and checks that it matches its journal.
* `rewind replay <run> --script fixed.ts` tells whether a corrected script is compatible with that history, and if
  not, where it diverges.

## Forks: fix a run without repeating its effects

A **fork** is a new run started from an existing one with a corrected script. Rewind replays the new script against
the parent's history: while its calls match the parent's, the recorded results are reused and **no tool is called
again**. At the first difference, or at the end of the parent's history, the fork continues live.

An agent fixes step 7 of a failed run without redoing the side effects of steps 1 to 6. The model does it with the
agent tool `retry_run`; your code with [`runs.fork`](https://docs.rewind.codes/sdk/runs#fork-a-run-with-a-corrected-script).

```ts theme={null}
const fixed = await rewind.runs.fork(failed.id, { script: correctedScript, wait: "30s" });
```

A fork keeps its parent's seed, input, toolset and namespace, and has its own id and TTL.

## Cassettes: tests from real runs

A run's calls and signals, exported as a **cassette**, become the mocks of a test. A test against a cassette answers
each tool call with what the real tool answered, in virtual time, without calling anything. See
[Testing](https://docs.rewind.codes/sdk/testing.md).

```ts theme={null}
const cassette = await rewind.runs.journal(run.id, { as: "cassette" });
```

## Logical time

Inside a script, `Date.now()` returns the time of the last event delivered to the run: its start, a tool's result, a
timer, a signal. Time only moves when the outside world speaks, and it moves identically on replay. After
`await sleep("10d")`, `Date.now()` is at least ten days after the start.

In tests (mock mode), time is **virtual**: the clock jumps straight to the next timer, so a ten-day workflow runs in
milliseconds.

## Determinism epochs

A replay only works if the script computes exactly what it computed the first time. The script cannot be
non-deterministic: the sandbox controls time, randomness and every input. What can change is Rewind itself: a new V8,
a new version of the `rewind` library, a new sandbox global.

These components are versioned together by one number, the **determinism epoch**, recorded in each run. A build of
Rewind keeps the versions of every epoch that may still have live runs, and CI replays a corpus of recorded journals
for each one.

If a replay ever diverges from its journal, that is a fault of the platform, never of the script. The run is not
failed: it is **quarantined**, and an operator releases it after a fix, or gives up on it. Quarantined runs appear on
the [Overview](https://docs.rewind.codes/dashboard/overview.md) of the dashboard.

## How a run ends

| The script | The run |
| - | - |
| `main` returns | `completed`, with the returned value as output |
| `main` throws, or a promise is rejected without a handler | `failed`, with the error, its line and code frame |
| Waits for nothing that can still happen (no call, timer or signal pending) | `failed` with `DeadlockError` |
| Is cancelled | `cancelled`: every pending `await` rejects with `CancelledError`, and the script gets one activation to clean up |
| Exceeds a limit (CPU per call, heap, history length) | `aborted` |
| Reaches its deadline (TTL, 30 days by default) | `cancelled` with the reason `ttl` |

A run never ends while a tool call is in flight: a forgotten `await` still lets the call finish and records its
outcome. So when a run ends by itself, every call it made has a recorded outcome, which is what lets a fork know
which effects happened.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.