---
title: Time-travel API
description: Programmatic entry points to roll a run back to a previous task attempt.
icon: "clock-rotate-left"
---

Every successful task attempt records the workspace's [JJ](https://jj-vcs.github.io/jj/)
change ID into `_smithers_attempts.jj_pointer`. The time-travel API replays a run
to one of those points: it restores the filesystem from the captured pointer and
discards the graph snapshots recorded after the attempt began, so the run's
timeline rolls back to that attempt.

The public, facade-exported surface is two functions. `revertToAttempt` is the
low-level VCS restore; `timeTravel` is the higher-level reset that can also reset
the target node and its dependents so the engine re-runs them.

```ts
import { timeTravel, revertToAttempt } from "smithers-orchestrator";
import type {
  TimeTravelOptions,
  TimeTravelResult,
  RevertOptions,
  RevertResult,
} from "smithers-orchestrator";
```

Both take the run's `SmithersDb` adapter as their first argument. Neither throws
for a missing attempt or a missing pointer; they report failure on the returned
`success` boolean. Inspect `result.success` rather than wrapping the call in
`try`/`catch`.

## timeTravel

Restore VCS to a previous attempt, then reset the target node (and optionally its
dependents) back to `pending` so the engine re-runs from there. This is the entry
point you want when you intend to resume the run: it cancels later attempts,
deletes the affected output rows, discards frames recorded after the attempt
started, and flips the run back to `running`.

```ts
function timeTravel(adapter: SmithersDb, opts: TimeTravelOptions): Promise<TimeTravelResult>;
```

<ParamField path="adapter" type="SmithersDb" required>
  The database adapter for the run, as returned by the authoring factory's `db`
  layer. See [`SmithersDb`](/reference/types).
</ParamField>

<ParamField path="opts" type="TimeTravelOptions" required>
  <Expandable title="TimeTravelOptions">
    <ParamField path="runId" type="string" required>
      The run to roll back.
    </ParamField>
    <ParamField path="nodeId" type="string" required>
      The node whose attempt is the rollback target.
    </ParamField>
    <ParamField path="iteration" type="number" default="0">
      Loop iteration of the node. Defaults to `0` for non-looped nodes.
    </ParamField>
    <ParamField path="attempt" type="number">
      Attempt number to restore. Omit to use the latest recorded attempt.
    </ParamField>
    <ParamField path="resetDependents" type="boolean" default="true">
      Also reset every node that ran after the target (later iterations, attempts
      started after the target's start time, or ordered after it) back to
      `pending`. Set `false` to reset only the target node.
    </ParamField>
    <ParamField path="restoreVcs" type="boolean" default="true">
      Restore the working copy from the attempt's JJ pointer before resetting
      nodes. Set `false` to reset DB state without touching the filesystem.
    </ParamField>
    <ParamField path="onProgress" type="(event: SmithersEvent) => void">
      Callback for `TimeTravelStarted` / `TimeTravelFinished` events. See
      [`SmithersEvent`](/reference/types).
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="TimeTravelResult" type="Promise<object>">
  <Expandable title="TimeTravelResult">
    <ResponseField name="success" type="boolean">
      `true` once the reset committed. `false` if the attempt or node was not
      found, or the VCS restore failed.
    </ResponseField>
    <ResponseField name="jjPointer" type="string">
      The JJ change ID restored from, when one was recorded for the attempt.
    </ResponseField>
    <ResponseField name="vcsRestored" type="boolean">
      Whether the working copy was actually restored. `false` when `restoreVcs`
      was off or the attempt had no pointer.
    </ResponseField>
    <ResponseField name="resetNodes" type="string[]">
      The node ids flipped back to `pending`, deduplicated.
    </ResponseField>
    <ResponseField name="error" type="string">
      Failure reason. Present only when `success` is `false`.
    </ResponseField>
  </Expandable>
</ResponseField>

```ts
const reset = await timeTravel(adapter, {
  runId: "RUN_ID",
  nodeId: "NODE_ID",
  attempt: 2,
  iteration: 0,
  resetDependents: true,
  restoreVcs: true,
});

if (!reset.success) throw new Error(reset.error);
// reset.resetNodes are now pending; resume the run to re-execute them.
```

**Source** [`timetravel.js`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/src/timetravel.js) · [`TimeTravelOptions.ts`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/src/TimeTravelOptions.ts) · **Tests** [`timetravel.e2e.test.jsx`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/tests/timetravel.e2e.test.jsx) · **See also** [Revert](/runtime/revert), [`revertToAttempt`](#reverttoattempt)

## revertToAttempt

Restore the working copy to an attempt's JJ pointer and discard DB frames
recorded after the attempt started. This is files-only: it does not reset nodes,
cancel attempts, or change the run status, so it leaves the graph untouched. Use
it when you only need the filesystem state of a past attempt. Unlike `timeTravel`,
both `iteration` and `attempt` are required.

```ts
function revertToAttempt(adapter: SmithersDb, opts: RevertOptions): Promise<RevertResult>;
```

<ParamField path="adapter" type="SmithersDb" required>
  The database adapter for the run. See [`SmithersDb`](/reference/types).
</ParamField>

<ParamField path="opts" type="RevertOptions" required>
  <Expandable title="RevertOptions">
    <ParamField path="runId" type="string" required>
      The run to revert.
    </ParamField>
    <ParamField path="nodeId" type="string" required>
      The node whose attempt is the revert target.
    </ParamField>
    <ParamField path="iteration" type="number" required>
      Loop iteration of the node. Pass `0` for non-looped nodes.
    </ParamField>
    <ParamField path="attempt" type="number" required>
      Attempt number to restore.
    </ParamField>
    <ParamField path="onProgress" type="(event: SmithersEvent) => void">
      Callback for `RevertStarted` / `RevertFinished` events. See
      [`SmithersEvent`](/reference/types).
    </ParamField>
  </Expandable>
</ParamField>

<ResponseField name="RevertResult" type="Promise<object>">
  <Expandable title="RevertResult">
    <ResponseField name="success" type="boolean">
      `true` once the working copy was restored. `false` if the attempt was not
      found, had no recorded pointer, or the JJ restore failed.
    </ResponseField>
    <ResponseField name="jjPointer" type="string">
      The JJ change ID restored from. Present on success and on a failed restore
      where a pointer existed.
    </ResponseField>
    <ResponseField name="error" type="string">
      Failure reason. Present only when `success` is `false`.
    </ResponseField>
  </Expandable>
</ResponseField>

```ts
const result = await revertToAttempt(adapter, {
  runId: "RUN_ID",
  nodeId: "NODE_ID",
  attempt: 2,
  iteration: 0,
});

if (!result.success) throw new Error(result.error);
```

<Note>
`revertToAttempt` and `timeTravel` need JJ in `PATH` and a JJ-initialized
workspace, and the target attempt must have completed while JJ was available
(otherwise no pointer was captured). See [Revert](/runtime/revert) for the
requirements and the equivalent CLI command.
</Note>

**Source** [`revert.js`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/src/revert.js) · [`RevertOptions.ts`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/src/RevertOptions.ts) · **Tests** [`revert.test.js`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/tests/revert.test.js) · **See also** [Revert](/runtime/revert), [`timeTravel`](#timetravel)

## CLI surface

<Note>
The richer time-travel surface (fork, replay, rewind, snapshots, and timeline)
is driven through the CLI and the rewind RPC, not as facade exports. Reach for
those rather than importing non-exported functions from the package.

- [CLI overview](/cli/overview) for `fork`, `replay`, `rewind`, `snapshots`, and `timeline`.
- [Revert](/runtime/revert) for the `revert` command that mirrors `revertToAttempt`.
- [`rewind-run` RPC](/rpc/rewind-run) to rewind a run over the gateway.
</Note>

```bash
# Browse the timeline, then rewind to a checkpoint and resume.
bunx smithers-orchestrator timeline RUN_ID
bunx smithers-orchestrator rewind RUN_ID FRAME_NO
```

**Source** [`time-travel/src`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/src) · **Tests** [`time-travel/tests`](https://github.com/smithersai/smithers/blob/main/packages/time-travel/tests) · **See also** [CLI overview](/cli/overview), [Revert](/runtime/revert), [`rewind-run` RPC](/rpc/rewind-run)
