Skip to content

async/witness

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

85 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Async Witness

See what your Vite pipeline actually did β€” with receipts.

Restart, hard-refresh, console.log, pray πŸ™ β€” Vite knew what happened the whole time, it just never told you. Async Witness runs your real pipeline and writes a receipt for every run: proof you, CI, or an AI agent can act on.

⚠️ Early release β€” the specs are the product truth. Designed by Jack, implemented by Mythos, reviewed by Codex.

A box in 30 seconds

A box is a small file that runs inside your real Vite pipeline and asserts what the pipeline did, in the pipeline's own vocabulary:

import { box } from '@async/witness';

export default box('message updates without reload', async ({ browser, project, expect }) => {
	// Visit a real route β€” this auto-starts your real Vite dev server.
	const page = await browser.visit('/demo');

	// Edit a real source file, like a developer saving in their editor.
	const change = await project.edit('src/message.ts', {
		replace: ['before', 'after'],
	});

	// Declare what Vite should have done about it.
	await expect.edit(change, {
		client: { hmr: 'accepted' }, // hot update applied, no full reload
	});

	// Confirm the browser actually shows the new text.
	await expect.page.text(page, '#message', 'after');
});
witness hmr

Async Witness runs the box, restores the edited file, and writes a receipt to .witness/receipts/ β€” pass or fail, human- and machine-readable. If the box fails, the receipt explains why in Vite's own terms: what payload Vite sent, whether the server restarted, what the console said.

you edit src/message.ts β†’ your real Vite pipeline (dev server, HMR, SSR, build) β†’ receipt.json: hmr accepted, 0 reloads

What a box can prove

  • A route renders with every asset loaded and a clean console
  • An edit hot-updated, full-reloaded, or silently did nothing β€” per environment
  • A server-only edit left the browser alone
  • A config edit restarted the server with the new plugin active
  • SSR renders and hydrates without console errors
  • The built app behaves like dev (build + preview parity)
  • Artifacts are right β€” manifest entries, no stale placeholders, no node:fs in worker bundles
  • A workflow stayed inside a performance budget

Every receipt has witnesses

Assertions only answer the questions you thought to ask. The interesting failures live in what you didn't ask about β€” the console error that didn't break your selector, the request that died while the page still rendered, the full reload that happened to land on the right text anyway. Async Witness treats a box run like a case: the receipt is the case file, and everyone who observed the run testifies in it.

Three witnesses watch every browser box from different vantage points. The pipeline witness is Vite's server side β€” it knows what hot payloads were actually sent, whether the server restarted, what each environment did with your edit. The client witness is the page itself β€” its console, its uncaught errors, the custom events your framework fired. The driver witness watches from outside the page over CDP β€” failed requests with their real net::ERR_* reasons, navigations the page can't hide, the screenshots. None of them can see what the others see, which is the point: they corroborate or contradict each other independently.

Each witness gets a verdict on every box. When a box passes but a witness holds evidence against the run, the pass is marked contested:

pass noisy page records console and network evidence  [pipeline+ client! driver!]  contested
     client reported: 1 console error   driver reported: 1 failed request

Every assertion passed here. The page also logged an error and a request died β€” facts no assertion asked about, preserved instead of swallowed. That's the gap between "tests pass" and "the app is fine", made visible on every run.

Cross-examine any box straight from a stored receipt:

witness evidence 'noisy page'
crimes reported (2):
  ! console error   Failed to load resource: net::ERR_ABORTED   β€” reported by client
  ! request failed  GET http://127.0.0.1:5173/missing.js        β€” reported by driver

Witness identity and verdicts are plain data in the receipt JSON β€” stable, greppable, safe for CI and agents to consume. Color is just how your terminal paints them.

Why not Vitest / Playwright / Storybook?

They're great at what they own β€” but they see the page, not the pipeline that produced it. That gap is how "all tests pass" and "the app is broken" happen at the same time. Async Witness owns the pipeline: the chain from an edit to a Vite environment event to what you see, with a receipt preserving the whole story.

There's no automation framework underneath, either. Async Witness drives the browser itself over the Chrome DevTools Protocol β€” a couple thousand lines of WebSocket and JSON it owns outright, zero dependencies. One pooled browser serves the whole run and every box gets its own isolated browser context, the same architecture playwright uses internally, which is how witness ended up faster per test than playwright-core on every platform CI measures.

Bring your own browser

Async Witness drives a Chromium-family browser already on your machine over the Chrome DevTools Protocol β€” no playwright dependency, no download at install time. Discovery order:

  1. WITNESS_BROWSER_PATH β€” explicit override. If it's set but doesn't point at an executable, discovery fails with an error instead of silently falling through to another browser.
  2. System installs of Chrome, Edge, or Chromium, in the usual per-OS locations.
  3. Playwright's browser cache as a courtesy fallback (~/Library/Caches/ms-playwright on macOS, $XDG_CACHE_HOME or ~/.cache/ms-playwright on Linux, %LOCALAPPDATA%\ms-playwright on Windows) β€” full chromium-<revision> downloads only, newest first, the headless shell is skipped.

macOS, Linux, and Windows are all exercised in CI. When nothing is found, witness fails with the exact paths it checked.

Docs

  • Specs β€” product direction and the source of truth

Status

Built in slices. Box authoring, dev/build/preview runs, browser evidence, witness verdicts with the witness evidence drill-down, the CLI, and JSON receipts work today. The state-gallery UI, generated types, and receipt replay are coming.

Contributing

The workspace runs on pnpm (the library itself is runtime-agnostic β€” it runs wherever Vite runs):

pnpm install        # install dependencies
pnpm run test       # run the test suite (drives real Vite pipelines)
pnpm run check      # format check + lint + typecheck

See CONTRIBUTING.md for the full setup. Start with specs/ for intent, and .ruler/ for the working agreements.

About

Restart, hard-refresh, console.log, pray πŸ™ β€” Vite knew what happened the whole time, it just never told you. Async Witness runs your real pipeline and writes a receipt for every run: proof you, CI, or an AI agent can act on.

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors