Variance Authority
Understand the code. Investigate the behavior. Check the work.
Variance Authority keeps fine-grained evidence of what your code did — across hundreds of thousands of files and tests, over time — so that a question is answered from that record instead of by running the whole suite again to find out. The record has several readings, and none of them is the centre the others depend on.
Find the names a workspace already publishes. Inspect a test while it runs. Trace a changed interface back to its source. Select the tests an edit reaches. Give your coding agent the same material to work from. Nothing is hosted; compute, storage, browser capacity, credentials, and deployment stay yours.
What are you working on?
Start with the question costing you time. Each path states the evidence it needs and where its answer stops; take one on its own, or connect more of them as the investigation grows.
Give your coding agent the same evidence
Your agent keeps its editor, shell, and test runner. Variance supplies the source facts and observations it reads, through the interfaces it already has.
- From the shell — query the current workspace, a completed report, or a live watcher from the command line.
- Over MCP — the observations you supply become callable tools, including a test paused at an authored inspection point.
- As guided workflows — source discovery, live investigation, UI review, test selection, and test reduction, each carried through an edit and a check.
Point your coding agent at llms.txt.
One change, traced end to end
The report names the cause and the source location:
1 root(s): 0 authorized, 1 to review, 0 violation(s).
[needs-review] Button — Button
undeclared component change: `Button` (token/paint) reached 1 subject(s)
examples/readme-case/src/Button.js:9
That is not a hand-written sample. It is
examples/readme-case/artifacts/report.txt,
committed beside the three PNGs above, and
examples/readme-case regenerates all four. From a
checkout of this repository, after yarn install:
yarn workspace @variance-authority/example-readme-case generate
A cause that reaches several screens is decided once: accept a matching change across the states it reached, and states carrying additional differences stay open. Other examples cover source selection, structural changes, and flake diagnosis. The external cases exercise the packages against real hosts and runners.
Add it to a Playwright test
The fastest way in is a test that already gets the app into the UI state you care about. Two installs, one call, one assertion:
npm install --save-dev @variance-authority/playwright-test @playwright/test
npx playwright install chromium
import { test } from '@playwright/test';
import { assertUnchanged, observe } from '@variance-authority/playwright-test';
test('the cart survives an empty basket', async ({ page }, testInfo) => {
await page.goto('https://example.test/cart');
await page.getByRole('button', { name: 'Clear' }).click();
const observation = await observe(page, page.getByTestId('cart'), testInfo, {
subjectId: 'cart/empty',
});
assertUnchanged(observation);
});
cart/empty is a subject id. A subject is one named UI state you asked for
and can ask for again — one Storybook story, one route at one viewport, one
component mounted in a test — captured and compared under an id you choose. The
first run reports new, because no baseline has been approved for that id yet;
promote that image with npx playwright test --update-snapshots=all and the next
run reports unchanged. Nothing accepts a first baseline on your behalf.
The test keeps its own runner, navigation, fixtures, and existing assertions.
@variance-authority/playwright-test
has the rest.
Or from the host you already have
These packages add observation to the environment you already own. They do not replace its test runner, fixtures, routing, or mounting.
To drive the whole loop from a config file instead of from inside a test, use
@variance-authority/cli:
npm install --save-dev @variance-authority/cli
npx variance run --config variance.config.json
npx variance accept --config variance.config.json cart/empty
npx variance run --config variance.config.json
The first durable run exits 1 because its subjects are new. Accept the
subject ids you meant, then rerun; an unchanged run exits 0.
What else the same evidence answers
Once a run records what it rendered and what it executed, and the source index records what the source declares, the questions below are answerable from the same material. None of them sits under another, and none requires the rest; take the one you need.
Each row has its own requirements — a browser, a live DOM, a filesystem, a socket, a readable checkout — and none of them is imposed on the others.
It reads more than JavaScript
The scan that answers what can this change reach is not limited to the language the tool is written in. JavaScript and TypeScript in every dialect, stylesheets, Python, Rust, Java, Kotlin and Swift are read into one graph, and a diff that spans several of them is answered in one walk.
npx variance reach --since origin/main
That prints the files the change reaches, one per line, so you can hand them to whatever runs them:
npx variance reach --since origin/main | grep '_test\.py$' | xargs pytest
There is no framework integration behind this and none is implied: the answer is
a list of paths on stdout, and what you do with it is yours. Because a run list
that comes back empty would look like a green build, reach never exits 0 with
an empty list — an empty answer is an error, not a pass.
How different languages are handled states what the graph
covers and what it leaves out.
How the packages are cut
A package is cut around what its consumer must supply, which is why there are many of them rather than one dependency graph you take whole.
The packages above are built from smaller ones you can also use directly:
@variance-authority/core, @variance-authority/dom,
@variance-authority/react, @variance-authority/jsx-source,
@variance-authority/raster, @variance-authority/png,
@variance-authority/png-sharp, @variance-authority/session,
@variance-authority/playwright, @variance-authority/storybook,
@variance-authority/store, @variance-authority/report, and
@variance-authority/remote. These are supported entrypoints, not internals. A
custom integration can:
- acquire a document without rendering it
- compare existing rasters without a browser
- consume a report without reopening the system under test
The architecture package map names what every package requires and what contract it owns. The information map shows how visual, source, runtime, scenario, presentation, history, and review evidence remain separate and meet only on identities their producers emitted.
Where it fits
The adoption gates state where the tool fits Playwright, Storybook, Jest, and Vitest — under jsdom and in browser mode. The product comparison states what Percy, Chromatic, Argos, and Applitools provide that this project does not.
Documentation
The full documentation is published at
variance-authority.dev, and its source is the
docs/ directory of this repository. The
documentation index routes by question rather than by package;
use it to find the contract, measurement, or limitation behind any of the paths
above.
Related projects
Two questions meet in this repository — what did this change look like and what did this change reach — and each has its own prior art.
Visual review
- Chromatic — hosted visual review built around Storybook, with TurboSnap deciding from the module graph which stories a change can reach. We ran on it for years, and it is still the reference for what a managed review service delivers.
- Argos — visual review over the screenshots your own suite already takes, with an open-source self-host path. Its work on screenshot stabilization and masking is cited in our own notes on flakiness and ignores.
- testivai — local-first visual regression that pairs every screenshot with a DOM and computed-style snapshot so a diff arrives as a verdict rather than red pixels. The nearest thing to this project in spirit.
Coverage, graphs, and test selection
- Wallaby.js — the deepest work anyone has done on per-test coverage. It instruments your source, keeps a matrix of which test covered which region, and re-runs the minimal affected set as you type; a line answers which tests reached it, in what order, carrying which values, and a profiler and a time-travel debugger read the same instrumentation. Nothing about per-test coverage or fine-grained selection is novel, and Wallaby is why. The difference is what the relation is for: Wallaby owns a live execution world and keeps it valid from keystroke to keystroke, and the execution record here is the same relation written down — a durable dataset a reviewer, a CI job, or a machine that never ran the suite can query later.
- Istanbul / nyc — the instrumentation and the file format the rest of the ecosystem reads. This repository runs V8's counters over its own suite for exactly that reason: a percentage from a counter with no stake in our answer.
- Jest
--changedSince, Vitest--changed, Playwright--only-changed— selection from the import graph, which needs no recording and cannot see a route the graph does not include. They walk it from every file a diff touched;variance reachwalks it from what the edit changed, so an edit to a comment or a type selects nothing, and a file that imports only an unchanged export of a changed file is left out. Over sixty commits of TanStack Query, that is 441 test files where the file graph selects 1,470; over sixty of Zod, whose tests import it through one namespace, 4,142 where it selects 4,531. What a record shows that no graph can is where a record's answer differs from any graph's. - Nx affected, Turborepo, Bazel — selection at the project or target grain, from dependencies you declare rather than executions anyone observed. Coarse, correct, and orthogonal: they decide which packages to build, and the record decides which tests inside one of them had a reason to run.
- TraceDecay — source
extraction across some sixty languages into one graph. The same appetite for
reading a whole polyglot repository that
variance reachhas.
Licence
MIT — see LICENSE. Copyright (c) 2026 Machine Garden. GitHub.


