Every run writes its own evidence to disk. The folder is the run's record. It is what you read when something went wrong in a place you cannot see. This page covers the layout and the files inside.
#The run directory
One run gets one directory under the artifacts root.
artifacts/<automationId>/<runId>/
logs.jsonl structured log stream, one JSON object per line
logs.txt the same stream, human-readable
state.json machine-readable status the Studio polls while a run is live
summary.json the final report
run-diagnostics.json what to do next, derived from the summary
trace.zip the Playwright trace, when tracing is on
screenshots/ captures taken on failures and on request
The root is config.artifactsRoot, which resolves to artifacts/ in the repository. Override it for one run with
--artifacts <dir>, or point the runner at another root from code. The runId is generated from the automation id, a
timestamp and four random characters unless the caller supplies one.
#What each file is for
state.json is written before and after every phase, so it always reflects the run's current position. It carries
status, phase, currentTransaction, the running totals and any fatal error. The Studio polls it, which is how
a live run shows where it is.
summary.json is the finished report. It has the automation id, the run id, the final status, the start and end
times, the totals and one entry per transaction. status is one of success, completed-with-errors, failed or
stopped.
run-diagnostics.json is derived from the same summary rather than written beside it, so the two cannot disagree. It
carries a one-sentence headline, a failures list with the code and reason of each failed unit and a repairs list
of the distinct recorded hints for those codes. A clean run has an empty failures list.
trace.zip is the Playwright trace of the run, when tracing is enabled. Open it with the Studio or with
npx playwright show-trace. The trace is stopped before the browser context closes.
#The log stream
Every entry is one JSON object, written to logs.jsonl and mirrored as a human line in logs.txt.
{"ts":"2026-10-03T09:14:51.805Z","level":"info","msg":"Transaction demonstrated-1 → SUCCESS","data":{"tx":"demonstrated-1"}}
The entry shape is fixed. ts is an ISO timestamp, level is debug, info, warn or error. msg is the
message. tx marks the transaction an entry belongs to, step names the step when one is in scope. attempt is
the 1-based attempt inside a retry. data carries arbitrary structured fields. In logs.txt the same entry becomes:
09:14:51.805 INFO Transaction demonstrated-1 → SUCCESS {"tx":"demonstrated-1"}
A CLI run can mirror these lines to the console with --console. The log level floor defaults to info.
#Screenshots
On a business or system failure the runner captures the page as screenshots/fail-<txId>-<outcome>.png. A fatal run
error captures screenshots/fatal.png. An automation can also ask for one with ctx.screenshot(name), which sanitises
the name and writes it into the same folder.
#Reading a run from the terminal
npm run robot -- show process-orders <runId>
That prints the recorded state. The exit code tells a scheduler what happened: 0 means the run finished, including a
run that completed with per-transaction errors, 2 is a fatal failure, 3 is a stopped run and 1 is a usage or
configuration error.
#Things that catch people out
artifacts/is gitignored. Runs are local evidence and are not committed.logs.jsonlis the file to read programmatically.logs.txtis a convenience mirror and can be read by eye.run-diagnostics.jsonand a failure screenshot are written best-effort. A run that cannot write them still finishes, because the summary is the contract and the rest is help.