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

# Caching agent steps

> A passing agent.act records its actions, and the next run replays them with no model call.

In `read-write` mode, the trace cache stores verified `agent.act()` steps.
The default file store uses `.e2e/cache/`, configurable through `cache.dir`;
with `cache.store`, entries go to that custom store instead. The next run
replays their actions with no model call and hands back to the live agent
when the app stops matching the recording. Judgments (`assert`, `waitFor`,
`extract`) are never cached.

The cache is on by default. Turn it off per run to check whether a failure
comes from the recording rather than the app:

```bash theme={"theme":"catppuccin-mocha"}
npx --no-install e2e run --no-cache
```

## When a step is recorded

In `read-write` mode, an entry is written only once a recorded verification
step after the `act` passes: a locator or engine assertion, a locator
`waitFor`, or an `agent.assert` or `agent.waitFor` judgment. Plain-value
assertions such as `expect(data.total).toBe(3)` are not recorded and do not
verify a trace. Pair every `agent.act()` with a recorded check of its outcome:

```ts title="tests/cart.e2e.ts" theme={"theme":"catppuccin-mocha"}
import { expect, test } from '@e2edev/e2e';

test('adds an item', async ({ app, agent, screen }) => {
  await app.open('/cart');
  await agent.act('add one item to the cart');
  await expect(screen.getByRole('status')).toHaveText('1 item');
});
```

A trailing `act` with no later recorded verification is never cached. In
`read-write` mode, a failed attempt writes only traces verified before the
failure and attempts to evict unverified entries. Automatic eviction requires
the store's optional `delete` method, which the file store provides. Custom
stores without it may retain stale entries.

`read-only` mode neither evicts nor records. File-store entries can be removed
with `e2e cache clear` and recorded again in a `read-write` run. That command
rejects custom stores; use that store's own tools for cleanup.

## When a replay hands off

A replay checks the recorded end path when the engine reports one, plus any
captured end anchors, the element descriptions that appeared or changed
during the step. If a recorded path or anchor does not match after the
actions run, the step hands off to the live agent to check its outcome. In
`read-write` mode, an entry the agent must repair with further actions is
submitted for eviction through the same optional `delete` method instead of
being rewritten with the repairs.

Some steps produce no end anchors, for example when only an input value or
checkbox state changes. An empty anchor set passes the anchor check. Such a
replay can finish after running the actions and checking only its recorded
path, without verifying the effect itself. Later assertions in the test
still execute on every run; keep those outcome checks in place.

Volatile anchors, such as minted ids, countdowns, dates, and clock times, are
omitted when stable anchors are available. If every captured anchor is
volatile, they are retained and compared exactly. The replay hands off when
their values differ, but can finish when they still match. A recorded end
path matches up to ids the app mints per record, so a flow that creates a
project can replay even when the next project has a new id.

Each agent step in the report records how the cache took part under
`step.cache`: `self-finalized` when a recording replayed the whole step,
`agent-concluded` when a replay handed off mid-step, `missed` otherwise with
a reason. A retry attempt records but never replays.

## Cache-friendly tests

* Verify the outcome of every `act` with a recorded assertion or wait.
* Create your own data under names unique to the run, so a replay never finds
  two rows with the recorded name.
* Clean up in `afterEach`, not in the last steps, so a run that fails before
  its cleanup leaves no duplicates for the next one.
* Give repeated controls distinct accessible names. A target that matches
  several controls is recorded with its position among them, and a replay
  honors that position only when the live screen shows exactly as many.

## Modes

| Mode           | Behavior                                                                                          |
| -------------- | ------------------------------------------------------------------------------------------------- |
| `'read-write'` | Default locally. Replays entries and records new ones.                                            |
| `'read-only'`  | Default in CI when unset. Replays but never writes, because a committed cache is untrusted input. |
| `'off'`        | Every step runs live. `--no-cache` is the per-run equivalent and wins over the config.            |

Set one of these alternatives inside the exported config object:

```ts title="Config fragment" theme={"theme":"catppuccin-mocha"}
cache: 'off',
// or
cache: { mode: 'read-write', dir: '.e2e/cache' },
```

`cache.store` replaces the file store with your own, for a shared remote
cache. See the [config reference](/reference/config#cache).

## Commit your traces

Committing cache entries is opt-in. `e2e init` adds `.e2e/cache/` to
`.gitignore`, so every machine records its own entries and CI starts with
none.

To share replays, remove that line and commit the directory. Entries a
passing local run wrote then replay in CI and on every teammate's machine.
CI stays `read-only` unless you set `cache: 'read-write'`, which is the right
call only when CI restores the directory from its own cache service rather
than from git.

Treat committed entries as test data: review them in the same pull request
as the test change that re-recorded them, and delete the directory when a
flow changes enough that the next passing run should record it from scratch.

## Inspect the cache

The file store names entries by digest, so the directory listing says nothing.
The CLI inspects and clears file-store entries only:

```bash theme={"theme":"catppuccin-mocha"}
npx --no-install e2e cache ls
npx --no-install e2e cache stats
npx --no-install e2e cache clear
```

```text theme={"theme":"catppuccin-mocha"}
TEST                                 TARGET  INSTRUCTION   AGE  ACTIONS
tests/billing.e2e.ts::upgrades       web     4f1c0a9d3b62  2h   4
tests/signup.e2e.ts::signs up        web     9ab7715ce004  3d   7 (truncated)
```

Entries are disposable: deleting `.e2e/cache/` slows the next run and
changes nothing else.

<CardGroup cols={2}>
  <Card title="Config reference" href="/reference/config#cache">
    `mode`, `store`, `dir`.
  </Card>

  <Card title="CLI reference" href="/reference/cli#e2e-cache">
    `e2e cache ls`, `stats`, `clear`.
  </Card>
</CardGroup>
