witness.

A deterministic build gate — catch test-theatre and throw-on-malformed before you push.

github.com/sjgant80-hub/witness →

Four adversarial LLM audit passes across the estate kept surfacing the same two bug classes, build after build. Auditing for them cost ~1.5M tokens each time and still missed some. Both are catchable deterministically, for free, in seconds — no language model, no network, zero dependencies.

Mutation gate witness mutate

Flip one operator in the source — >>=, &&||, return truereturn false — then run the project's own tests. If they still pass, that line is test-theatre: a hole a real regression falls straight through.

Fuzz gate witness fuzz

Throw the hostile-input battery — null, BigInt, a circular object, a toxic getter, a 200k-element array — at a function. If a tolerant function throws, it fails the never-throw boundary contract.

Use it

# mutation gate: mutate a file, run the tests against each mutant
npx witness mutate src/thing.mjs

# fuzz gate: throw the hostile battery at one exported function
npx witness fuzz ./src/thing.mjs verifyThing

Exit code 0 when the gate is clean, non-zero when it isn't — drop it straight into CI.

The lesson it caught in itself

witness spawns your project's test runner. The first time it ran under its own node --test suite, every mutant falsely "survived" — the spawned node --test inherited NODE_TEST_CONTEXT from the parent and exited 0 even on failure. And a hard kill mid-run once left a live mutant baked into a fixture. Both are now designed out — the child env is scrubbed, and the gate self-heals from a sidecar backup after any interrupt. A build gate has to be hardened against the exact bug classes it hunts. This one is dogfooded on itself.

Verdict shape

runMutations(src)  {
  total: 14, killed: 14, survived: [],
  score: 1.0, clean: true
}
fuzz(verifyThing)  {
  throwsOn: [{ input: 'circular object', error: '…' }],
  neverThrows: false
}