How to zero vendor lock-in on your test outputs

Most advice on avoiding vendor lock-in is abstract: use open standards, validate exports quarterly, prefer portable schemas. That reads well and ships nothing. This guide is the concrete version. For one product (Assrt), here is every single file a test run writes to disk, at exact paths, with exact filenames. If your tool of choice cannot produce a matching list, you do not own your outputs.

Five artifact types, five directories, zero proprietary formats. Nothing to export because nothing is trapped.

M
Matthew Diakonov
9 min read
4.8from 189 engineers
Plan file at /tmp/assrt/scenario.md, editable in any text editor
Run report at /tmp/assrt/results/<runId>.json, readable by jq
Screenshots at /tmp/assrt/<runId>/screenshots/NN_stepN_action.png
5 files

Checking your outputs is a faster lock-in test than reading marketing copy. If the plan only lives in a vendor database, if screenshots only render in a dashboard, if the video hides behind OAuth, you do not own what the tool produced for you.

Test output portability, working definition

The five outputs, at a glance

A run produces exactly five kinds of file. Four live under /tmp/assrt/, one under ~/.assrt/. Nothing else is needed to reconstruct what the test did and what it saw.

The Markdown plan

/tmp/assrt/scenario.md. Plain Markdown with '#Case N:' blocks. Commit it to git, grep it from a CI job, paste it into a Slack thread. No DSL. No build step.

The JSON report

/tmp/assrt/results/latest.json plus a per-run copy at /tmp/assrt/results/<runId>.json. Full plan snapshot, pass/fail, per-assertion evidence. jq-friendly.

Zero-padded PNG screenshots

/tmp/assrt/<runId>/screenshots/00_step1_init.png, 01_step2_click.png, and so on. The step number and tool name are baked into the filename. Shell sort stays correct.

A legible WebM video

/tmp/assrt/<runId>/video/recording.webm. Standalone playable in VLC, Chrome, or ffmpeg. The cursor and keystrokes are painted into the page, so the file is readable without a trace viewer.

A local scenario cache

~/.assrt/scenarios/<uuid>.json. Every plan the CLI has ever loaded is cached here as a StoredScenario object. Offline fallback when the cloud is unreachable.

Inputs, runtime, outputs

The pipeline is three inputs, one runtime, four files that end up on your disk. The cloud sync is a capability-URL cache, not a walled garden; it syncs a copy of the same plan file you already have locally.

Inputs -> Runtime -> Outputs you keep

Markdown plan
App URL
Model key
Assrt runtime
scenario.md
results/*.json
NN_stepN_action.png
recording.webm

Output 1: the Markdown plan at /tmp/assrt/scenario.md

The plan is not a YAML DSL, not a binary .spec, not a JSON object in a cloud database. It is a plain Markdown file on your disk with #Case N: blocks you could have written in Notes. Variables use {{NAME}} syntax. The agent reads the same file you edit.

/tmp/assrt/scenario.md

When you edit the file in VS Code, vim, or a plain cat-and-pipe, a file watcher debounces for one second and PATCHes the cloud copy. The file on your disk is the source of truth; the cloud caches it by UUID.

scenario-files.ts

Output 2: the JSON report at /tmp/assrt/results/latest.json

Every run writes the full report to /tmp/assrt/results/latest.json and keeps a per-run copy at /tmp/assrt/results/<runId>.json. No row limit, no paywalled field. jq, Node, Python, DuckDB all read it out of the box. CI jobs can tail the same file that your local editor just watched.

/tmp/assrt/results/latest.json

Output 3: zero-padded PNG screenshots, one per step

Per-step screenshots go to /tmp/assrt/<runId>/screenshots/ with a zero-padded index, a step number, and the tool name that produced the step, baked right into the filename. The pattern makes a shell ls the only tool you need to reconstruct the run.

src/mcp/server.ts (line 468)

Here is what a real run directory looks like on disk after a 10-step checkout scenario:

/tmp/assrt/<runId>

Output 4: a legible WebM video at video/recording.webm

Inside each run directory is a single standalone video file. It is a real WebM container that VLC, Chrome, ffmpeg, and QuickTime all open without conversion. The difference from a raw screencast: Assrt paints a red cursor, a click ripple, a keystroke toast, and a tiny compositor heartbeat into the page under test before recording. The DOM overlay travels with the pixels, so the file explains itself when you hand it to a designer or a PM who cannot install a trace viewer.

4 openers

The WebM in a run directory plays in VLC. The PNGs open in Preview. The JSON opens in jq. The Markdown opens in any editor. That is what 'standalone artifact' means.

Assrt output portability test

Output 5: the local scenario cache at ~/.assrt/scenarios/

Every plan the CLI has ever loaded is also written as a full StoredScenario object at ~/.assrt/scenarios/<uuid>.json. The cache exists so that when the central API is unreachable, the runtime falls back to reading from disk instead of failing the run. Practical effect: the history is double-written. Even if someone deletes /tmp on a reboot, the scenarios survive under your home directory. A five-second tar of that directory backs up everything you ever tested.

0Output file types per run (Markdown, JSON, PNG, WebM, JSON cache)
0Lines of open TypeScript in the agent runtime
0 msDebounce on fs.watch edits to scenario.md
0Proprietary formats across the whole artifact surface

How Assrt outputs compare to typical closed platforms

The failure mode in commercial AI QA is almost never the plan input; it is the outputs. Plans are easy to write; what is hard is pulling the artifacts out when you change tools. Here is the concrete gap.

FeatureClosed AI QA platformAssrt
Where does the plan live?Inside the vendor's cloud editor or a proprietary YAML DSL/tmp/assrt/scenario.md on your disk as plain Markdown
Can I grep my test history?Only through the vendor UI; most DSLs hide semantics behind keysgrep '#Case' /tmp/assrt/scenario.md ~/.assrt/scenarios/*.json
Are screenshots real PNGs?Hosted images accessed via an authenticated dashboard URLReal files at /tmp/assrt/<runId>/screenshots/ named 00_step1_init.png
Is the video a standalone file?MP4 or WebM inside the vendor player, not always downloadable/tmp/assrt/<runId>/video/recording.webm plays in VLC, Chrome, ffmpeg
How do I replay a run offline?You cannot; the dashboard is the replay surfaceOpen /tmp/assrt/<runId>/ on any machine, no network needed
Where is the run report?Inside the vendor database, often with a row-cap on free exports/tmp/assrt/results/<runId>.json as full JSON, no subset
What does uninstall cost me?Your history vanishes unless you paid for an exportEvery artifact already exists on your disk, nothing to export
Which source license?Closed; the runner is a SaaS surfaceOpen TypeScript, MIT, agent.ts is 1,087 lines you can fork

How to keep your outputs if you stop using Assrt

There is no export flow. There are two directories to back up, and four tools that open the files.

Four steps

1

Stop the Assrt process

Kill the CLI or MCP. Any in-flight run finishes its current step, closes the browser, and writes whatever it has.

2

Back up two directories

tar cvzf assrt-history.tgz /tmp/assrt ~/.assrt/scenarios. Everything else is ephemeral OS state.

3

Open the files on a clean machine

Plans open in any editor. JSON opens in jq. Screenshots open in Preview. Videos open in VLC. None of them require Assrt to be installed to read.

4

Rerun only if you want to

To execute the plan again you need a model key (Anthropic or Gemini) and the Assrt CLI. The history is already yours; rerunning is optional.

What you can do with the artifacts from the command line

Every artifact is addressable by a shell one-liner. That is the real test of portability: not "can I export?" but "can I pipe it?"

cat /tmp/assrt/scenario.mdjq '.status' /tmp/assrt/results/latest.jsongrep '#Case' ~/.assrt/scenarios/*.jsonopen /tmp/assrt/<runId>/video/recording.webmcp screenshots/04_step5_click.png bug-ticket.pnggit add tests/scenarios/checkout.mdtar cvzf backup.tgz ~/.assrt/scenariosdiff -r last-run/ today-run/ffmpeg -i recording.webm -vf select=eq(n\,0) first-frame.pngduckdb -c "SELECT * FROM read_json('results/*.json')"

Byte count at rest

A passing 10-step checkout scenario leaves roughly 0KB of Markdown and JSON, ten PNGs averaging 0KB each, and one WebM around 0 MB. A year of daily runs still fits on a USB stick. The ceiling is your disk, not a seat license.

Want to keep your test history, not rent it?

We will walk the file inventory on your stack, and show you the same artifact list running against your app.

Book a call

Frequently asked questions

What counts as a test output, and why is vendor lock-in usually about outputs?

An output is any file a tool writes when it runs. For automated tests, that is typically: the plan, the pass/fail report, per-step screenshots, and a video recording. Vendor lock-in on inputs is easy to spot (a proprietary YAML DSL, a cloud-only editor) but lock-in on outputs is quieter and costlier. If your plan file lives only inside a vendor database, you cannot grep it or commit it. If your screenshots only render inside their dashboard, you cannot paste them into a bug ticket as real PNGs. If the video is trapped behind OAuth, you cannot attach it to a Slack thread. Checking your outputs is a faster lock-in test than reading marketing copy.

Where exactly does Assrt write each output file during a run?

The plan goes to /tmp/assrt/scenario.md. Metadata (id, name, url, updatedAt) goes to /tmp/assrt/scenario.json. The latest run report goes to /tmp/assrt/results/latest.json, with a per-run copy at /tmp/assrt/results/<runId>.json. Each run gets its own directory at /tmp/assrt/<runId>/ containing screenshots/ (one PNG per step) and video/recording.webm. The local scenario cache used when the cloud is unreachable lives at ~/.assrt/scenarios/<uuid>.json. You can ls, cat, grep, mv, rm, cp, tar, and git add every one of these paths. There is no proprietary container.

What is the exact naming convention for screenshot files?

Screenshots are written with a zero-padded index, the step number, and the action name: `${String(screenshotIndex).padStart(2, '0')}_step${currentStep}_${currentAction || 'init'}.png`. In practice you see 00_step1_init.png, 01_step2_click.png, 02_step3_type_text.png. That convention is in src/mcp/server.ts around line 468 of the assrt-mcp package. Zero padding keeps shell ordering correct up to 100 steps per run. If a run has no action (pre-navigation), the name falls back to step1_init. Names are stable inputs to your CI pipeline: a diff tool can line 03_step4_click.png in last night's green run up against 03_step4_click.png in today's red run.

What shape is the JSON report, and what can I do with it offline?

results/latest.json contains the full run: the plan snapshot, the URL tested, the model used, pass/fail status, per-case timing, assertion evidence, and suggested product improvements. It is plain JSON, so jq, Node, Python, DuckDB, and every CI tool can read it. Typical offline workflows: jq '.cases[] | select(.passed == false)' to list failing cases, cat results/latest.json | pbcopy to attach to a Jira ticket, or git diff last-night.json today.json to see what changed between runs. The runId.json copies let you replay any past result without the cloud.

How does Assrt avoid a proprietary plan format like YAML or a binary .spec?

Plans are Markdown with `#Case N: name` blocks. A human reads them in any editor; a grep for '#Case' finds them all. The runtime writes the active plan to /tmp/assrt/scenario.md and starts an fs.watch on that file (scenario-files.ts line 97). Save the file in VS Code and a 1-second debounce timer PATCHes the cloud copy via /api/public/scenarios/<id>. This is the opposite of a vendor DSL: the source of truth is the file you can `git add`, the cloud is a cache. Commercial tools that keep the plan inside a closed database force you to export on their terms. Here the export is the default state.

If I stop using Assrt tomorrow, what survives on my disk?

Every plan ever loaded lives at ~/.assrt/scenarios/<uuid>.json as a full StoredScenario object (id, plan, name, url, passCriteria, variables, tags, timestamps). Every result lives under /tmp/assrt/results/ and /tmp/assrt/<runId>/. Every video is a standalone WebM playable in VLC, Chrome, or ffmpeg. The browser profile (cookies and localStorage used across runs) lives at ~/.assrt/browser-profile/. Move those directories to a new machine, or tar them into a backup, and the history is portable. Nothing is trapped in a cloud-only export format.

The WebM video is just a screen recording. What is special about it?

The recording has a rendered cursor, click ripples, and keystroke toasts painted into the page. That overlay is injected by CURSOR_INJECT_SCRIPT (browser.ts lines 33-98): a fixed-position red cursor dot, a click-ripple ring, a keystroke toast, and a 6px heartbeat pulse that keeps Chromium's compositor producing frames during quiet moments so the CDP screencast captures motion. Net result: the WebM on your disk is legible as a standalone artifact. No trace viewer required. You can drop it into any video player or attach it to a bug ticket and a non-engineer can follow what happened.

Can I replay a past run without being online?

Yes, for replay-as-read. Every run has its own directory at /tmp/assrt/<runId>/ with its plan snapshot, JSON report, screenshots, and WebM. Open video/recording.webm in any player, open screenshots/ with your file manager, or pipe results/<runId>.json into jq. For replay-as-rerun, Assrt also keeps a local scenario cache at ~/.assrt/scenarios/<uuid>.json. The CLI reads that cache when the central API is unreachable, falling back to a 'local-' prefixed ID so you never lose a plan to network trouble. Rerunning locally requires a model key (ANTHROPIC_API_KEY or a Gemini key); the model is where you pay, not where you are locked in.

What does 'zero vendor lock-in' concretely mean here, as opposed to the abstract version in most blog posts?

Abstract version: 'use open standards, validate exports quarterly.' Concrete version, file by file. Plan: /tmp/assrt/scenario.md in Markdown. Metadata: /tmp/assrt/scenario.json. Results: /tmp/assrt/results/latest.json plus one per run. Screenshots: /tmp/assrt/<runId>/screenshots/NN_stepN_action.png. Video: /tmp/assrt/<runId>/video/recording.webm. Local cache: ~/.assrt/scenarios/<uuid>.json. Source code: 1,087 lines of MIT-licensed TypeScript in assrt-mcp/src/core/agent.ts. That is the full artifact surface. Point at any of those with `cat` and you are not locked in.

How does this compare to what you get from closed AI QA platforms at $7,500 per month?

Typical commercial platforms keep the plan inside a proprietary editor, keep results inside a cloud-only dashboard, and keep videos behind OAuth URLs. Export features exist but return a cleaned-up subset, usually without the original timing or the raw screenshots. Assrt inverts the default: files first, cloud second. The cloud is a capability-URL cache keyed by UUID, not a walled garden. You pay for the model you run the tests on (Anthropic or Gemini), which is a commodity market. You do not pay a seat license for your own test history.

How did this page land for you?

React to reveal totals

Comments ()

Leave a comment to see what others are saying.

Public and anonymous. No signup.