Skip to content

Reports & test runs

Actions are recorded into a test run. One auto-starts on the first action — set VERIKUN_NO_RUN=1 to disable.

Every command becomes a step with its timing, the selector and the identifier it resolved through, and pass/fail. A failing step also captures a screenshot and the UI hierarchy of the page.

./.verikun/run/ active run (run.json + artifacts/) — auto-created on first action
./.verikun/runs/<id>/ archived runs (report.xml, report.html, run.json, artifacts/)
./.verikun/suites/<id>/ suite overviews (index.json, index.html)
./.verikun/run-<lane>/ active run of one lane of a PARALLEL suite (VERIKUN_LANE)

Run state is on disk, not in memory, so a later vk process can pick it up — that is what lets vk device reset undo a change after the flow that made it has died.

Actions plus wait and assert. Not inspection — ui, find, devices — because reading the screen is not a test step. The one exception is log, which is recorded so its device-log capture lands in the report.

Terminal window
vk run archive smoke

Finalizes the run into ./.verikun/runs/<id>/:

File Contents
report.xml JUnit: one <testcase> per step with timings, <failure> for failed assertions, <error> for environment errors, and the resolved identifier in <system-out>. Drops straight into CI.
report.html Self-contained report — see below
artifacts/logcat.txt Full device log for the run window (default)
artifacts/logcat-app.txt App-scoped log, when a package or bundle was launched
run.json The raw recording

vk run archive exits non-zero when the run contained failures, so the same command both produces the report and gates CI.

Every step, the identifiers used, any screenshots taken, the screenshot and hierarchy of any failed page, a link to the full device log, an app-scoped log accordion when the run launched an app, and any per-step logs from vk log. A run that did not pass says so in a banner at the top.

An archived HTML report with the device-log accordion collapsed

Expanding the accordion shows the captured device log inline:

The same report with the device-log accordion expanded, showing logcat output

Logs are pulled at archive time into artifacts/logcat.txt, scoped to the run’s window where possible — no explicit vk log is required.

  • Opt out on green runs with vk run archive --no-logs or VERIKUN_NO_LOGS. Failed runs still capture.
  • Capture is best-effort and never blocks sealing.
  • An on-demand vk log still attaches into its own step, kept tail-first so a crash trace survives the size cap.
  • Over --server the same data is served by the server, so a remote CI archive matches a local one.

Step names never include typed text. vk text redacts the value into the step message when the field carries the password flag.

Redaction is detection-based, so a field the platform does not flag as a password is stored verbatim. Two known gaps: on iOS a Flutter obscured field is never flagged (#44; see Platform support), and a value typed from {{env.NAME}} is not masked by name (#57). Failure evidence — screenshots and hierarchies — is raw as well.

The returned exit code, or a thrown CliError’s code:

Exit code Report outcome
0 passed
1 <failure> — an assertion
≥ 2 <error> — environment or usage

A failure the vk ai engine produces with no command to attach it to — a guard giving up, a budget or timeout abort — is recorded as one synthetic failed step (only when no step is already red), so the archive and the [ai] … verdict on the console always agree. Its evidence is a screenshot plus hierarchy locally; over --server the hierarchy only (#48). The rule: Contracts.

So an implicit run never silently merges unrelated activity, the active run auto-closes (archives) and a fresh one starts when the context changes:

Trigger Applies to Tune with
Idle too long (default 30 min) implicit runs only VERIKUN_RUN_IDLE_MIN (minutes; 0 disables)
Different device serial any run —
Different session any run VERIKUN_SESSION (falls back to TERM_SESSION_ID)

A run you named with vk run start is sticky to idle — only a hard context change (device or session) rolls it over.

Rollover always archives the old run, never discards it, and prints the reason and destination to stderr.

Device-state snapshots live in the run file, so a rollover has to carry them:

  • On a same-device rollover, unrestored overrides are carried forward into the new run.
  • On a device-change rollover, verikun warns with the exact vk device set … --device <serial> needed to undo them — it cannot drive the old device from a process pointed at the new one.

A stable, schemaVersioned manifest written to ./.verikun/suites/<id>/. Publish steps in CI compose over it.

Field Meaning
schemaVersion Currently 1. Additive changes do not bump it.
verikun Version of the process that wrote the manifest — the client’s. For a --server run that is not what drove the device; see server.
server --server runs only: {url, verikun, reads} — which server drove the device, on which version, and whether it read the hierarchy via the companion or the stock dump
tests[] Per-test: pass/fail, step count, cost, duration, run id
tests[].modelRepairs How many steps the model repaired. 0 on a clean replay — gate on it to fail a build that needed healing
tests[].planSteps Top-level steps in the plan that ran — the compile’s size, unlike steps, which counts what executed. Comparable across a pass and a failure, so an unusually short compile is visible when diffing runs of the same test
tests[].flaky / .attempts Present when --retries recovered the test
tests[].device Which device ran this test. Set on a parallel suite, where the assignment is dynamic
concurrency How many devices ran the suite at once. Absent (or 1) for a serial suite
totals Suite-level counts
totals.durationMs Device time — the sum of every test’s duration. Equal to elapsed time serially; across N devices it is what the gate cost in device-seconds
totals.wallClockMs Elapsed time, startedAt → finishedAt — how long you actually waited
warnings[] Recovered flakes, environment retries, and devices retired mid-suite
aborted {reason, notRun, kind} when the suite stopped early. kind is environment (exit 3) or budget (exit 1, from --max-suite-cost-usd)

On an abort, the not-run tests get no rows and no place in totals, so passed + failed === tests still holds and nothing downstream mistakes a skipped test for a regression.

Fields added since the first schema are all additive. A manifest written before they existed simply omits them — treat a missing concurrency as 1.

Terminal window
VERIKUN_NO_RUN=1 vk tap @x

Nothing is recorded, captured or attached. Useful when driving a device interactively and you do not want a report.