# Check recordings offline

A recording can be checked without a simulator: run a flow's asserts against it, or compare two recordings to see what the app did differently. Both work in CI with no device.

## Run a flow’s asserts against a recording

```shell
squiggle replay checkout --assert .squiggle/flows/checkout.yaml
```

`--assert` checks the flow’s asserts against what the recording shows. It skips the taps and other actions, because a recording can’t be changed. The command exits with code 1 when an assert fails, so it can gate CI with no simulator.

| Asserts | Pass when |
| --- | --- |
| `route · visible · exists · store · query` | They held at any point in the recording. The report says when they first held. |
| `notVisible · rerenders · droppedFrames` | They hold at the end of the recording. |

`${VAR}` values come from the flow’s `env`; override them with `--env`.

> **Text Squiggle can't see offline**
>
> Text your components render, including values that change, is in the recording. Text only native views know about needs the live accessibility tree, and a replay has no device to ask. Those asserts report that they couldn’t be checked, instead of failing.

## Compare two recordings

```shell
squiggle diff before.sqrec after.sqrec
squiggle diff before.sqrec after.sqrec --json | jq '.renders'
```

`squiggle diff` lists what changed between two recordings: renders, screens, network requests, timings, actions and store changes. Record the same journey on two builds to see what a change did, with no app running.

## Compare one recording with many

```shell
squiggle diff intent-mv2q7tnc-1 --journey checkout
squiggle diff checkout-after --cohort 'checkout-*'
```

Output:

```text
diff intent-mv2q7tnc-1 against 40 runs Cart (renders) 42 — 3.0× the most of any of the 40 runs (14)

OUTSIDE EVERY RUN OF THE COHORT
  ▌ renders Cart: 42 — 3.0× the most of any of the 40 runs (10..14, median 12)
```

Two runs of the same journey always differ a little, so a diff of two is mostly noise. With `--journey`, Squiggle judges one recording against every run of that intent program that passed and still has its recording, and lists only the numbers outside the range of all of them, with the range and how many runs it covers. It also says when the recording took a route none of them took. Each run of an intent program keeps its recording under its run id, so the journey is the program's `intent:` name. To pick the runs yourself, use `--cohort` with recording names, paths, or a pattern with `*`. The recording being judged is never part of its own cohort.

The command exits with code 0 when the recording is inside its cohort and 1 when something falls outside, so CI can gate on it. A count the recording never made counts as 0 for it, so a request every other run made and this one skipped shows up too. A span time has to be more than 5% outside the range to count.

To get a recording, see [Record and replay](https://squiggle.sh/docs/replay).
