One shape for what a test run produced
A browser agent, a Playwright suite, a Jest run, an API check — whatever made it, the .evidence pack is one readable, sealable format. Understandable by a CI dashboard, an auditor, or a human, without knowing the framework that wrote it.
$ npm install -g @testmuai/evidence-cli
The problem → the pack
Every framework reports differently. Read one shape instead.
A pack is a <name>.evidence/ directory (it zips to a
<name>.evidence file), anchored by a top-level
run.yaml. At L0 — the minimal profile — only three artifacts are
load-bearing.
evidence-cli knows nothing about any framework's definition format. It references and hashes the definition; it never parses it. The format scales by adding optional files and profiles — never by rewriting the core.
<name>.evidence/ run.yaml # required — manifest anchor tests/<id>/ <definition> # required, OPAQUE — the framework's own artifact # (a Markdown spec, *.spec.ts, an API suite, …) result.yaml # required — structured per-step outcomes
Try it yourself
Download a sample pack, drop it in the viewer
Grab the real .evidence pack that kane-cli produced from a browser
run — a 27-step, nested-iframe e-commerce checkout — and drop it straight into the viewer
at evidence.lambdatest.com to see it rendered.
.evidence (2.8 MB)
then drag it into the viewer ↓
The sample is a real pack — 27 steps, 27 screenshots, a 110-request HAR. Drop it into the viewer above, or open the viewer full-screen ↗.
kane-cli already speaks this format
The pack above wasn't made for a demo — kane-cli emits it on every run. It's the
worked example: run.yaml, result.yaml, the opaque definition,
and the L1 artifacts, with a walkthrough of who writes what.
Point your own framework — Playwright, Jest, an API suite, anything — at the example and
emit the same shape. There's no viewer to build: any pack that conforms
loads in evidence.lambdatest.com, exactly like this one.
Profiles — a ladder on one 0.1 contract
Start minimal. Add layers, never rewrites.
A profile only adds requirements — it never changes the version. Only a
breaking change to an existing meaning bumps evidence (0.1 → 0.2).
Framework-agnostic minimum
The smallest thing that's still complete — three load-bearing artifacts.
- run.yaml — run identity, lifecycle, derived totals
- definition — the framework's own opaque artifact, hashed
- result.yaml — structured per-step outcomes
L0 + captured artifacts
Everything in L0, plus the evidence the run actually captured.
- execution logs — per-test, with metadata
- step screenshots + a global coverage directory
- video — optional
Two ways to use it
A library and a CLI, same core
validate checks a pack against a profile; finalize rolls up
totals, hashes definitions, and seals the directory into a flat, range-addressable
.evidence zip — atomically.
import { validate, finalize } from "@testmuai/evidence-cli"; // Validate a directory or a sealed .evidence zip. const report = await validate("my-run.evidence", { profile: "L1" }); if (!report.valid) { for (const d of report.diagnostics) console.error(`${d.severity} ${d.location}: ${d.message}`); } // Seal a live directory into a sealed .evidence zip. const { totals, sealedPath } = await finalize("my-run.evidence", { endedAt: new Date().toISOString(), });
# check a directory OR a sealed .evidence zip evidence validate my-run.evidence --profile L0 # L1 = L0 + the captured-artifact layer evidence validate my-run.evidence --profile L1 # roll up totals, hash definitions, seal → .evidence evidence finalize my-run.evidence/ # machine-readable report evidence validate my-run.evidence --json
exit codes 0 valid · 1 invalid · 2 usage error