# Concepts

muhar's vocabulary is small. Each term below is used with exactly this meaning everywhere on this site and in muhar's output.

## Guard

A guard is a piece of behaviour that must hold: a check that refuses a bad input, a limit, an authorisation test, an invariant in a config file. Guards are what you care about; mutations are how muhar asks whether a test protects them.

## Mutation

A mutation is one exact text edit that removes or weakens a guard: in file `file`, replace the first occurrence of `old` with `new`. Each mutation has a `name`, which is how it appears in the report — so name it after the bug it simulates ("zero is accepted as an amount"), not after the edit.

muhar does not generate mutations. You write them. That is the trade: fewer mutations than a generator would produce, each one chosen, named and meaningful, with no equivalent-mutant noise to triage. See [Writing good mutants](writing-mutants.md).

## Suite

The suite is the spec's `test` command, run with `sh -c` in the spec's `root` (or the current directory). A non-zero exit status means the suite failed, which, with a mutation in place, means the mutation was caught. muhar does not parse test output; the exit status is the whole signal.

## Baseline

Before anything is mutated, muhar runs the suite once on pristine source. It must pass. If it fails, any later "caught" could be the pre-existing failure rather than the mutation, so the run is discarded.

## Positive control

The control is a mutation the suite must catch: a fault so central that a working harness and a working suite cannot miss it. muhar plants it after the baseline. If it cannot be planted, or the suite stays green with it in place, then either muhar is not mutating what the suite builds or the suite cannot see the source. Either way, no verdict from the run can be trusted, and the run is discarded.

A harness with no failing control is a green light with no bulb: it looks the same whether it works or not. The control is the bulb test, run on every invocation.

## The proven-landed guarantee

A mutation counts as applied only when muhar has proven it on disk. Before writing, muhar refuses an edit that cannot change the file: `old` is empty, `old` equals `new`, or `old` is absent. After writing, it reads the file back and requires the bytes on disk to equal the intended mutated content and to differ from the original. Only then does it run the suite. Every failure along the way becomes `COULD_NOT_MUTATE`, with the reason, and the suite is never run against that mutation.

The point is that `NOT_CAUGHT` always means "the fault was in place and the suite still passed". It never means "the edit silently did nothing".

## Verdicts

Each mutation gets exactly one of three verdicts. The third is a separate state on purpose: a fault that was never planted has not been tested, and folding it into either of the other two would report something that did not happen.

| Verdict | Condition | Called in the source |
|---|---|---|
| `CAUGHT` | Mutation proven in place; suite exited non-zero. | checked-failed |
| `NOT_CAUGHT` | Mutation proven in place; suite exited zero. A real finding. | checked-clean |
| `COULD_NOT_MUTATE` | Mutation could not be planted or proven; the suite did not run. | could-not-check |

A mutation that does not compile makes `go test` fail, so it is reported `CAUGHT` today. That is a known limitation, and the [roadmap](roadmap.md) has the fix.

## Harness healthy, harness broken

A run is healthy when the baseline passed and the control was caught. Only a healthy run prints per-guard verdicts and a `Totals:` line, and exits 0. A broken run prints `HARNESS BROKEN — run discarded.` and one reason, and exits 2.

A healthy run is not a clean result. Exit 0 says the instrument worked; the verdicts say what it found. A gate needs both: exit status 0, and a `Totals:` line with `0 NOT CAUGHT, 0 could-not-mutate`. See [Output and exit codes](output.md).

## Why exit 2 is never clean

Exit 2 means muhar could not establish that it was measuring anything. Reading it as a pass — "no guards were reported missing" — repeats the failure muhar was built to prevent: a harness that silently tests nothing and reports confidently. A CI step must fail on exit 2. Because Go's flag parser also exits 2 on an unknown flag or a malformed flag value, the safe rule is to fail on any non-zero exit.

## Run order

Figure: The order of one run. A failure in the first two steps discards the run with exit 2; a mutation that cannot be planted is recorded and the run continues.

1. Baseline: run the suite on pristine source. It must pass, or the run is discarded (exit 2).
2. Positive control: plant, prove on disk, run the suite, restore. It must be CAUGHT, or the run is discarded (exit 2).
3. Each mutation, in spec order: plant, prove on disk, run the suite, restore. A mutation that cannot be planted or proven is COULD_NOT_MUTATE and the suite is not run.
4. Report: per-mutation verdicts in spec order, then the Totals line. Exit 0.

The details of each step, including what happens with `-j` above 1, are in [How a run works](internals.md).

---

Source of this page: https://muhar.assay.guide/concepts
