Skip to main content
The core workflow: pick a run you trust, make it the baseline, and every future run is diffed against it. This is what turns evals from “did it pass?” into “is it getting worse?”

Baselines

  • Automatic: in Pest, the first run that passes its gate becomes its suite’s baseline. Nothing to configure.
  • Manual: promote any completed run from the dashboard, or:
Terminal
One baseline per suite; promoting a new one demotes the old one transactionally.

How runs are joined

Rows are matched across runs by content hash — a fingerprint of the row’s input, prior messages, and expected data, computed when the dataset is read. Reordering your JSONL file, adding rows, or renaming files changes nothing; the same logical row lines up across months of runs. (For single-model runs the reported provider/model id is deliberately ignored in the join — providers rotate dated model ids like gpt-5-mini-2025-08-07, and that must not sever your history.)

Classification

For each row present in both runs: Rows only in the current run are new; rows only in the baseline are removed.
Why epsilon? You’re sampling a nondeterministic system — small score wobble is expected. A pass-rate drop is always a regression, but a 0.93 → 0.90 score drift shouldn’t page anyone. Tune evals.compare.epsilon to your tolerance.

Failing the build on regressions

In Pest
What a regression failure looks like
Or without Pest:
Terminal
--compare also accepts a run id or latest. The dashboard’s compare view renders the same diff — regressed and improved tables with before → after scores. See Running in CI for the full pipeline recipe.