hollow-test
Diff-scoped mutation gate. Mutates only the lines your change touched and fails if any mutant survives your test suite, which proves the coverage is load-bearing.
hollow-test
ADLC phase: P3 Rail (C4) · Gate: every changed line is constrained by an assertion, so no mutant survives.
What it defends against
Executed lines are not tested lines. A suite can run every changed statement and still
assert nothing about its behavior. That is hollow coverage, and a reward-seeking model
will happily produce it. hollow-test mutates only the lines in your diff (invert a comparison,
flip a boolean, null a return), reruns the suite against each mutant, and fails if any
survives. A survivor is proof the line's behavior is unconstrained. Diff-scoping keeps
the run in seconds-to-minutes instead of the hours whole-codebase mutation testing costs.
Usage
adlc hollow-test --test-cmd "node --test test/" [--base <ref>] [--max <n>] [--timeout-ms <n>] [--target <file>] [--rails <ticket-file>] [--json]| Flag | Description |
|---|---|
--test-cmd <cmd> | (required) Shell command to run the suite; must exit non-zero on failure. |
--base <ref> | Git base ref for the diff. Defaults to the merge-base with a trunk; fails closed if none resolves. |
--max <n> | Max mutants across all files (default 20), spread round-robin. |
--timeout-ms <n> | Per-mutant test-command timeout (default 120000). |
--target <file> | Mutate this file whole, independent of the diff (repeatable). For rails-authoring / characterization tickets where the behavior file itself did not change. |
--rails <ticket-file> | Expand a ticket's declared rails globs against tracked files and mutate them (repeatable). |
--json | Machine-readable output for orchestrators. |
--help | Show usage and exit 0. |
Refuses to run on a dirty working tree, and requires a green baseline before mutating so a failing suite cannot masquerade as killing every mutant.
Exit codes
--target/--rails, or a selected file received no mutation budget (see below).2: gate fails. One or more mutants survived (hollow coverage).--max too small for the diff: a starved file fails closed
--max spreads the mutant budget round-robin across every selected file, and a small
--max against a wide diff can leave one or more diff-derived files with zero quota —
never mutated at all. That file's changed code is then unverified, so hollow-test refuses
to report a pass: it exits 1 and names every starved file on stderr rather than silently
passing on whatever other files did get budget. Raise --max to cover every selected file
(one per file changed, at minimum), or narrow the diff. This does not apply to a file that
received budget but has no mutable line at all (a comment-only or import-only change) —
that case still only warns, since the file genuinely has nothing to mutate rather than
having been skipped for lack of budget.
Example
$ adlc hollow-test --test-cmd "node --test test/" --base main
src/calc.mjs:7 null-return survived return a + b; → return null;
hollow coverage — 1 mutation(s) pass your tests
exit 2Go deeper
- packages/hollow-test
- Why prosecution beats code review: Prosecution, Not Code Review.
gate-manifest
A hash-chained evidence ledger recording what each ADLC gate verified, with optional HMAC signing so the chain attests authorship, not just consistency.
prosecute
A P5 review-evidence recorder. It validates, hashes, and records ticket- and revision-bound reviewer findings, and passes only after two dry passes across three lenses.