When a run fails, unotest writes a failure bundle — everything needed to understand what happened, for you or the agent.
What’s captured
| Tier | Contents | Default |
|---|---|---|
| 1 | error + stack, console log, semantic DOM snapshot, DSL trace | always on |
| 2 | screenshot (viewport + element-focused) | on |
| 3 | HAR (network) + video | off — not wired yet |
Always-on failure artifacts
Independently of the bundle tiers, any scenario failure also writes
screenshot.png + page.html to the run’s failure/ directory under
unotest/.runs/ — no debug flag needed. page.html is the runtime DOM (open shadow roots included) with
secrets redacted, so it shows what the page actually was at the moment of
failure.
Step screenshots
Set UNOTEST_STEP_SCREENSHOTS=1 to capture a PNG after every executed step
into the run’s screenshot/ directory. In the
viewer a step row with a capture shows a camera icon —
click to open it full-screen. Captures settle first (network quiet + a painted
frame, capped at 2s) so a step ending on goto() isn’t shot as a blank
pre-render frame; the failure screenshot deliberately skips the settle.
Where it lives
A bundle is the failure/ directory of the run that produced it, under the
project’s unotest/.runs/ (or unotest/.runs.<env>/ for a run started with
--env; UNOTEST_ARTIFACTS_ROOT moves the whole tree to another root). The
run’s steps.jsonl sits beside it and also carries every assertJudge
verdict as a judge:verdict event — rubric, judged text, verdict, reasoning,
model — so a red judge step is explainable without a
re-run.
The MCP-readable copy of a bundle has a root of its own: .unotest/failures/
under the artifact root by default, and wherever UNOTEST_FAILURES_ROOT says
when that is set. It is set where the process running the tests cannot write
the default — a box runs each test in a container that mounts the
environment’s artifacts and nothing else, so its bundles live inside that
tree. Nothing to set for a local run: the default is already writable, and
the failure-artifact MCP tools and the viewer follow
the root wherever it points.
Retention is one window for the whole run history, bundles included:
UNOTEST_RUNS_RETENTION_DAYS (default 180; 0 keeps everything) removes
day directories older than that and then collects the blobs no run points
at any more. It runs after a collection, never before, so housekeeping
cannot delay a run or change its exit code. The viewer
renders each artifact per run; the agent reads them through the
failure-artifact MCP tools.
How the agent uses it
On failure the agent calls agent_fix, which bundles the trace, console,
semantic snapshot and scenario source plus a classification
(rewrite-selector / add-waitfor / change-assertion). It proposes a diff —
you approve it.