Output and exit codes
muhar's stdout is a report meant to be read by people and parsed by scripts. Its format is fixed: everything on this page is taken from the code that prints it. The suite's own output goes to stderr (Streams).
A healthy run
muhar -spec mutations.jsonHarness healthy: baseline GREEN, positive control CAUGHT. CAUGHT zero is accepted as an amount CAUGHT the limit ignores what was already spent today NOT_CAUGHT a withdrawal that lands exactly on the limit is refused COULD_NOT_MUTATE stale anchor (mutation "stale anchor": Old text not found in limits.go — edit did not land) Totals: 2 caught, 1 NOT CAUGHT, 1 could-not-mutate. NOTE: could-not-mutate is NOT a clean gate — those guards were never actually tested.
| Line | Format | Present |
|---|---|---|
| Health | Harness healthy: baseline GREEN, positive control CAUGHT. | Always, first. |
| Verdict | Two spaces, the verdict left-aligned in a 16-character field, one space, the mutation's name. A COULD_NOT_MUTATE line adds two spaces and the reason in parentheses. | One per mutation, in spec order. |
| Totals | Totals: <c> caught, <n> NOT CAUGHT, <m> could-not-mutate. | Always, after the verdicts. |
| Note | NOTE: could-not-mutate is NOT a clean gate — those guards were never actually tested. | Only when <m> is above 0. |
The verdict words are CAUGHT, NOT_CAUGHT and COULD_NOT_MUTATE, with underscores. The Totals: line spells the middle count NOT CAUGHT, with a space; grep for exactly what is printed. The reasons are listed in the spec reference.
With -shard, the counts cover the shard's own mutations only.
A broken run
muhar -spec mutations.jsonHARNESS BROKEN — run discarded. baseline suite is RED on pristine source — cannot distinguish a caught mutation from a pre-existing failure; discard the run
A broken run prints two lines: HARNESS BROKEN — run discarded. and the reason, indented two spaces. It prints no verdicts and no Totals: line, because those verdicts are exactly what cannot be trusted. A gate that looks for a Totals: line will therefore never pass a broken run, even if it ignores the exit status.
| Reason | Cause |
|---|---|
baseline suite is RED on pristine source — cannot distinguish a caught mutation from a pre-existing failure; discard the run | The suite failed before anything was mutated. Fix the suite or the test command first. |
positive control could not be planted (<reason>) — the harness is not mutating; discard the run | The control's edit could not be made or proven. Usually its old text has drifted. |
positive control was NOT CAUGHT — a mutation the suite must catch stayed green, so the harness is broken (not mutating, or the suite cannot see the source); discard the run | The suite passed with the control in place. The control is too weak, the suite does not build the mutated file, or root points at a different tree. |
could not provision isolated workspace <i> (<error>) — a parallel sweep cannot share a tree; discard the run | With -j above 1, a worker's copy of the tree could not be made: disk full, unreadable file, or a go.mod rewrite that failed. |
Exit codes
| Status | Meaning | Report on stdout |
|---|---|---|
0 | Harness healthy. Verdicts are trustworthy. This includes runs with NOT_CAUGHT or COULD_NOT_MUTATE lines. | Full report with Totals:. |
1 | Usage, spec or setup error: missing -spec, unreadable or invalid spec, missing test or control, bad -shard, negative -j, temp directory not creatable. | None. Message on stderr. |
2 | HARNESS BROKEN: no verdict from this run may be used. Also returned by Go's flag parser for an unknown flag or a malformed flag value such as -j two. | HARNESS BROKEN and reason, or nothing for a flag error. |
130 | Interrupted by SIGINT or SIGTERM. | None. |
Exit 0 is not a pass. It says the instrument worked. Whether the guards held is in the Totals: line.
Exit 2 is never a pass. A broken harness reports nothing about your tests, and reading its silence as "no missing tests" is the failure muhar exists to prevent. The flag parser sharing status 2 is a known collision, tracked as assay#2644; it does not change the rule below.
Gating on the result
The rule: fail on any non-zero exit status, then require a Totals: line with zero NOT CAUGHT and zero could-not-mutate. In bash:
status=0
report=$(muhar -spec mutations.json) || status=$?
printf '%s\n' "$report"
if [ "$status" -ne 0 ]; then
echo "muhar exited $status: harness broken or usage error, no verdict" >&2
exit 1
fi
if ! grep -qE '^Totals: [0-9]+ caught, 0 NOT CAUGHT, 0 could-not-mutate\.$' <<<"$report"; then
echo "a guard has no test, or a mutation did not land" >&2
exit 1
fi
Against the runs in the worked example, this script passes only the run after the boundary test is added. It fails the NOT_CAUGHT run, the COULD_NOT_MUTATE run and all three broken runs.
Parsing the report
For scripts and agents, every verdict line matches:
^ (CAUGHT|NOT_CAUGHT|COULD_NOT_MUTATE) +(.+)$
The name runs to the end of the line, except on a COULD_NOT_MUTATE line, where the reason follows two spaces and an opening parenthesis: (mutation "<name>": …). The name is free text, so keep names free of the sequence two-spaces-parenthesis to keep the split unambiguous. The report has no machine-readable format other than this text today.