# Preview and Replay

Run a change against real data without committing it, then compare it with recorded work before you publish.

Changing an action that agents and callers depend on is safest as a loop: run the change against your real data with nothing committed, compare what it would do with what the current version did, and publish only when the difference is the one you meant. Twisp gives you three tools for that loop, and every one of them is recorded.

- **Preview** runs work for real, reads real data, and discards its writes, recording each one.
- **Replay** reruns a recorded agent turn or MCP request with another version or a draft, and commits nothing.
- **Version pinning** makes sure a change reaches callers only when you update them.

## What a preview is

A preview runs everything exactly as a live run would. Tools run their programs, reads see your real data, each call sees the writes of the calls before it, and agents make their real model calls. When the work ends, its business writes are discarded. Each call is recorded with `preview: true` and lists in its `writeSet` every record version it would have written.

The record of the preview itself commits: the execution, its calls, an agent's transcript, and its session history. That is what makes a preview useful: you can read exactly what it would have done after the fact. A write set lists only the records you may read, and counts the rest in `writeSetUnreadable`, so a preview never shows you a record your policies hide.

A preview can read other tenants, but it cannot write to them, and it cannot run an operation that acts outside the ledger, such as a warehouse export. A call to a built-in [model](https://www.twisp.com/docs/actions/models.md) is the exception: a preview sends it to the provider like a live call, so it uses tokens and can get a different answer than a live run would. Work that would follow a commit does not happen: a definition created inside a preview is never published. See [Preview calls](https://www.twisp.com/docs/reference/actions/executions.md#preview-calls).

Filter on `preview` to separate what committed from what did not:

```graphql
query Previews($id: UUID!) {
  actions {
    executions(where: { actionId: $id, preview: true }, first: 20) {
      nodes {
        executionId
        created
        status
        writeSet {
          recordType
          recordId
          data
        }
        writeSetUnreadable
      }
    }
  }
}
```

## Ways to preview

| Preview         | How                                                                                              | What runs                                                                                                   |
| --------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| A program       | `program` with `commit: false` on an [MCP server](https://www.twisp.com/docs/actions/mcp-servers.md#atomic-work-with-program) | One Starlark program over the server's tools, in one transaction. The result lists the rows it would write. |
| A tool or agent | `invoke` with `commit: false`, the default, on an MCP server                                     | A stored version, a disabled one, or an unpublished draft, on an input the caller chooses.                  |
| An MCP server   | `mcp.mode: PREVIEW`, the default for every new server                                            | Every call every external agent makes through the server.                                                   |
| An agent        | `config.mode: preview`                                                                           | Every turn the agent runs on real events. Agents default to `live`.                                         |

### Draft previews

A **draft** is an unpublished definition. `invoke` with a `definition` and `commit: false` runs one without storing it, validated as `createAction` would validate it. So an agent, or a person through an agent, can build a tool and run it before anything depends on it:

1. Create the tool with `enabled: false`. It is never triggered or called, but it can be previewed.
2. Preview drafts of it with `invoke` until the output, calls, and writes are right.
3. Publish the draft that worked with `updateAction` and `enabled: true`.
4. Where a real run is safe, `invoke` it with `commit: true`.

For a change to a live action, preview the change as a draft before `updateAction` publishes it to every caller. A draft's execution records the definition it ran in `draft`. A draft's preview is never rerun or replayed; preview the draft again. See [Invoking an action](https://www.twisp.com/docs/reference/actions/mcp.md#invoke).

### Preview mode on MCP servers

Every new MCP server starts in preview mode. External agents get real outputs from your data, and none of their calls commit. Each result carries a preview marker in `_meta`, so a host can tell its user. The request's execution, its session history, and its uses still commit, and so does a task's cancellation. Point a new agent at a preview server, review what it would have written, then promote the server to `LIVE`. See [MCP servers](https://www.twisp.com/docs/actions/mcp-servers.md#preview-mode-then-live).

### Preview mode on agents

An [agent](https://www.twisp.com/docs/actions/agents.md) with `config.mode: preview` runs on real events and changes nothing. Its turns do everything a live turn would, and their writes are discarded when each turn ends. The agent's execution, transcript, and session still commit, so a preview session evolves as a live one would.

A call to a tool that requires [approval](https://www.twisp.com/docs/actions/sessions.md) does not wait in preview mode: it runs at once, as if approved, and the execution lists the request a live turn would have made with `grantedInPreview: true`, so the record shows that no person approved it. To let the agent act, update `config.mode` to `live`.

### What preview turns remember

The [memory](https://www.twisp.com/docs/actions/memory.md) and session history a preview turn writes are kept for audit, marked `preview`. Later preview turns read them, but a live turn never does: once the agent goes live, it does not act on what it learned about effects that never happened.

A preview turn never changes a memory record a live turn wrote. To correct or delete one, it writes a preview record of its own whose `replaces` names it. Preview turns stop recalling the original; live turns still recall it, unchanged. `Action.memories` lists these records with `preview: true`, so you see exactly what the preview would have changed. See [Agent memory](https://www.twisp.com/docs/reference/actions/memory.md#agent-memory).

## Replaying against another version

A **replay** reruns recorded work with another version of an action, or with an unpublished definition of it, and records the result as a new execution beside the original. `replayExecution` accepts two kinds of source:

- **An agent turn.** A finished execution of an agent: a triggered turn, a session turn, or a run a client called or started directly. The replay receives what the original turn received: its event, or for a session turn the same inputs and the session checkpoint the turn started from.
- **An MCP request.** A request's execution of an [MCP server](https://www.twisp.com/docs/actions/mcp-servers.md). The replay reruns the request, such as a tool call with its recorded arguments, against a later version of the server or a draft.

A replay commits no business writes. Every call runs as it would live, and its writes are discarded and recorded in its write set, as for a preview. It is never part of the live session: it admits no inputs and leaves the session and the agent's memory unchanged. It starts nothing and cancels nothing: a replayed request that would start a task records the start as not started.

```graphql
mutation Replay($sourceId: UUID!, $replayId: UUID!, $version: Int!) {
  actions {
    replayExecution(
      id: $sourceId
      input: { executionId: $replayId, actionVersion: $version, model: RECORDED }
    ) {
      executionId
      status
      replayOfId
    }
  }
}
```

### What a replay holds fixed

|                         | In a replay                                                                                                                                                                                                                                                                                              |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Input                   | The original's: its event, its session inputs and starting checkpoint, or its request.                                                                                                                                                                                                                   |
| Tool and skill versions | A reference without a `version` resolves to the version the original used, and to the latest only for a tool or skill the original did not use. A pinned reference uses its pin, so a draft that pins a new tool version tests exactly that change.                                                      |
| Data                    | Current. A replay's tools read the data as it is when the replay runs, not as the original saw it; `replay.data` is `CURRENT` to say so.                                                                                                                                                                 |
| Model                   | For an agent turn, `LIVE` calls the model the replayed version configures; `RECORDED` answers with the model outputs the original recorded, so a change of tools or skills is compared without the model's variability. A request to an MCP server involves no model, so `RECORDED` is rejected for one. |
| Built-in model calls    | Made again, as every tool call is, even with `RECORDED`: each sends its input to the provider, uses tokens, and can answer differently. A call to a [model](https://www.twisp.com/docs/actions/models.md) version retired since the original fails with `action_not_callable`.                                                        |

### Comparing outputs and write sets

A replay has its own output, calls, transcript, and uses, which you compare with the original's. `replayOf` reads the original from the replay, so one query reads both:

```graphql
query Compare($id: UUID!) {
  actions {
    execution(id: $id) {
      invocation {
        output
      }
      children(first: 50) {
        nodes {
          actionId
          invocation {
            output
          }
          writeSet {
            recordType
            recordId
            data
          }
        }
      }
      replayOf {
        invocation {
          output
        }
      }
    }
  }
}
```

For a session turn, `replay.session` shows the status, reason, and state the turn would have given the session, to compare with what the original committed. Before publishing a change to an MCP server or a tool it uses, replay the server's recent requests against a draft that pins the new version, and compare request by request. [Building actions with a coding agent](https://www.twisp.com/docs/guides/mcp-action-builder.md#replaying-before-you-publish) shows that loop driven by an agent.

A replay of work that had a caller, such as a client's call or an MCP request, runs under both you and that caller, so it can do nothing either of you could not; a replay of a triggered or session turn runs under the agent's own policies, as the turn did. See [Authority and audit](https://www.twisp.com/docs/actions/authority-and-audit.md#reruns-and-replays) and [Replays](https://www.twisp.com/docs/reference/actions/executions.md#replays).

## Reruns, replays, and previews

A **rerun** runs work again for real. A **replay** reruns recorded work with another version and commits nothing. A **preview** runs work once without committing it.

|                    | Rerun                                                                            | Replay                                                                                  | Preview                                                                          |
| ------------------ | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Operation          | `rerunExecution`                                                                 | `replayExecution`                                                                       | `program` or `invoke` with `commit: false`, or preview mode on a server or agent |
| Source             | A finished execution: triggered, called, or started                              | A finished agent turn or MCP request                                                    | New work                                                                         |
| Version            | The source's pinned version, or one you choose                                   | The source's version by default, another stored version, or an unpublished `definition` | The current or a chosen version, or a draft through `invoke`                     |
| Business writes    | Committed, unless the original was a preview                                     | Discarded, and recorded in write sets                                                   | Discarded, and recorded in write sets                                            |
| Session and memory | A session turn is not rerun                                                      | Unchanged                                                                               | Preview mode keeps a preview session and preview memory                          |
| Authority          | The action's own policies for a triggered execution; yours for a call or a start | You and the original principal, then the action's policies                              | The same as a live run of the same work                                          |
| Not available for  | Session turns, MCP requests, replays, and drafts' previews                       | Calls made by another execution, other replays, intake records, and drafts' previews    | Operations that act outside the ledger or write to other tenants                 |

Use a rerun to finish work that failed for a reason you have since fixed, such as a policy an update now grants: it runs under the update's policies at once. See [Manual reruns](https://www.twisp.com/docs/reference/actions/executions.md#manual-reruns).

## Pin what others depend on

A [tool reference](https://www.twisp.com/docs/actions/tools.md) with a `version` always calls that version: its program, schemas, and policies. One without a `version` calls the latest version, resolved when each execution starts. Pin the tool versions your MCP servers and agents depend on, so a new tool version reaches them only when you update them, after a replay has shown what changes. Every invocation records the exact version each reference resolved to, so a replay and an audit always know what ran. See [Versions](https://www.twisp.com/docs/reference/actions/tools.md#versions).

## Where to go next

- [Authority and audit](https://www.twisp.com/docs/actions/authority-and-audit.md): Who each action runs as, and what Twisp records about everything it does.
- [Executions reference](https://www.twisp.com/docs/reference/actions/executions.md#replays): Preview calls, reruns, replays, and their fields.
- [Building actions with a coding agent](https://www.twisp.com/docs/guides/mcp-action-builder.md): Let a coding agent validate and replay changes before a person publishes them.
