Every test has a third answer.

Ternary runs your suite and sorts each test into pass, fail or unsure. Unsure tests rerun on a clean worker until they pick a side. The ones that never do go to quarantine with the seed that broke them.

npx ternary init

✓ found vitest.config.ts, 1,184 tests

✓ wrote ternary.toml

next: npx ternary run

v2.14.0, for Vitest, Jest, Playwright and pytestPress ⌘K to jump anywhere

01 · unsure.test.tsruns as it lands

Unsure is a result, not a retry.

A test that passes on its second try did not pass. Ternary marks it unsure, reruns it 3 times in isolation and records which seed flipped it.

  1. passcheckout › applies coupon212ms
  2. passcheckout › rejects expired card98ms
  3. failcart › merges guest cart1.4s
  4. unsuresearch › debounces input3 reruns

2 pass, 1 fail, 1 unsure. Seed 7731 saved.

02 · ternary.toml8 lines

Quarantine without a ticket.

A test that stays unsure for 5 runs leaves the blocking suite by itself. It keeps running, it stops failing your merges, and it comes back after 20 clean passes in a row.

1[unsure]2reruns = 33isolate = "worker"4 5[quarantine]6after = 5    # unsure runs7release = 20 # clean passes8notify = "#ci-flakes"

✓ 3 tests quarantined, main is green, 0 tickets opened

03 · report.jsonper branch

One number per branch.

Every run ends in a single file. The number to watch is unsure tests on main; everything else is detail you can open when you need it.

1,184tests run
3unsure on main
11minutes saved per run
{ "branch": "main", "unsure": 3,
  "seed": 7731 }

Six tools for the third answer.

Each one is a subcommand or a flag. None of them needs a dashboard, an account or a change to your tests.

replay

Seeds, not screenshots

Every unsure result stores its seed, worker and clock offset, so the exact run plays again on your laptop.

ternary replay 7731
blame

Blame the commit

Bisects history to the commit where a test first went unsure, in 6 runs on a 300-commit branch.

ternary blame search
shards

Shards by history

Splits the suite by last week's timings, so shards land within 8 seconds of each other.

--shards auto
watch

Local first

The same three answers on your machine, rerunning only what your diff touched.

ternary watch
runners

Any runner

Vitest, Jest, Playwright, pytest and go test. Ternary reads their output; your tests stay as they are.

runner = "auto"
report

Plain JSON out

One report.json per run. Pipe it into the dashboard you already have, or into nothing.

--report report.json

Shipped on Mondays.

31 releases in the first 40 weeks of 2026. Every one has a migration note, even when the note says none.

  1. v2.14.0

    addedUnsure results carry the seed that last flipped them.

  2. v2.13.2

    fixedQuarantine release no longer counts skipped runs as passes.

  3. v2.13.0

    addedternary blame bisects to the first commit where a test went unsure.

  4. v2.12.1

    fixedPlaywright shards keep their trace files on rerun.

Run it on your slowest suite.

Free for public repos and teams of up to 5. Unlimited workers from $12 a seat a month.

npx ternary init

← nightmarqueefootage: Higgsfield⧉ Copy this prompt