Run only the tests a change touches.
Trust the green.

CI reruns your whole suite for a one-line change, then a flaky test fails at random and blocks it. testrazor fixes both from artifacts your suite already emits. No account, no API key, no network.

0API keys or accounts
1runtime dependency
JUnitany language flakes
27/27tests, dogfooded
ci run, one line changed in discount.ts
$ testrazor impact --changed src/discount.ts
  selected 2 of 3 test files, 1 skipped (33%)
    run  test/cart.test.ts
    run  test/discount.test.ts

$ testrazor flake --report run1.xml --report run2.xml
  1 flaky of 3 tests over 2 runs
    flaky  cart :: sums a cart  score 1.00

$ testrazor gate --report run2.xml -q q.json
  result: PASS  # flaky red is quarantined
$ testrazor gate --report run2.xml  # no quarantine
  result: FAIL  # a real failure still blocks

The problem

Green does not mean what you think

Two everyday CI failures have nothing to do with your code and both cost you the same way: slow feedback and lost trust in the build.

01

The suite is too slow

A one-line change reruns thousands of tests for minutes. Most of them could not possibly be affected by what you touched.

02

Flaky tests cry wolf

A test fails at random, turns the build red and blocks a correct change. People start re-running CI until it is green, so real failures get ignored too.

03

You stop trusting green

Once red is sometimes noise, green stops being a signal. testrazor makes the fast path safe and keeps a real failure loud.

How impact works

A dependency graph, not a coverage report

Standard coverage is aggregated across a whole run, so it cannot say which test touched which line. A static import graph can, deterministically. testrazor selects the test files that transitively import a changed source file.

  1. Read the diff. git diff --name-only (or an explicit --changed list from your CI).
  2. Build the graph. Resolve relative import, export ... from, require and dynamic import() specifiers across the source tree.
  3. Select. Any test that reaches a changed file through the graph runs. Everything else is skipped.
  4. Fail safe. A change to a non-source file (config, lockfile) selects every test, so an unmeasured change never slips through. A changed file no test imports is reported, never hidden.
changed → src/discount.ts
cart.ts → discount.ts → price.ts
RUN · reaches the change
test/discount.test.ts
test/cart.test.ts
cart imports discount
SKIP
test/price.test.ts
nothing it imports changed

How flake works

A real failure is sacred

Across two or more runs, a test that both passes and fails is flaky and gets quarantined. A test that only ever fails is a real bug, so it is never quarantined and the gate always shows it.

verdicts across 2 runs
test              run1  run2   verdict
cart::sums        pass  fail   flaky → quarantine
auth::expiry      fail  fail   real failure → gate fails
price::clamp      pass  pass   stable

score = min(passes,fails) / floor(runs/2)
flaky = passes > 0 and fails > 0
  1. Group across runs. Feed two or more JUnit files from your run history.
  2. Score. A test that alternates evenly scores near 1, one that fails once in ten scores low but non-zero.
  3. Quarantine. Flaky ids are written to a committed file with their score and the runs they failed in. The gate excludes them.
  4. Release. A quarantined test that passes across the whole window is flagged to let back in.

In your pipeline

One gate for CI, one PR comment

The composite Action runs the CLI, writes the report to the job summary and upserts a single PR comment keyed by a hidden marker so re-runs update in place. It exits with the CLI's own code, so a real failure fails the job.

.github/workflows/ci.yml
- uses: zkasuran/testrazor@v1
  with:
    args: gate --report reports/junit.xml --quarantine .testrazor/quarantine.json
    comment: "true"

Built with Kiro

Spec-driven, with the specs in the repo

testrazor was built with Kiro's spec-driven workflow. The .kiro/ directory is committed so you can see the process, not just the result.

.kiro/specs

requirements → design → tasks

Behaviour as EARS acceptance criteria, a fixed architecture and a task plan. Every module maps back to a numbered requirement.

.kiro/steering

product, tech, structure

The rules the agent followed on every file: one dependency, pure engines, the honest-scope rule.

.kiro/hooks

typecheck and traceability

One hook runs typecheck and tests on save, one checks that every test still maps to an acceptance criterion.

The design changed mid-build through the spec: an early draft used coverage for impact, but standard coverage cannot say which test hit which line, so it moved to an import graph. Every test name carries a @covers <req>.<n> tag, so the suite is a live traceability matrix over the requirements.

Honest scope

What actually works

Flake and gate: any language

Anything that emits JUnit XML. jest, vitest, pytest, mocha, go test through a converter.

Impact: JavaScript and TypeScript

By resolving relative import, export and require specifiers, including .js specifiers that resolve to .ts sources.

Quick start

60 seconds, no key

bash
git clone https://github.com/zkasuran/testrazor && cd testrazor
npm ci && npm run build
node dist/cli.js impact --root fixtures/sample-repo --changed src/discount.ts
# then try flake and gate on fixtures/sample-repo/runs/*.xml