# Understanding coverage
Supercov answers a more useful question than “which lines ran?” It shows which
behaviors were exercised, which paths still need a test, and where measurement
was incomplete.
## Gaps and measurement limits are different
An **uncovered gap** is behavior Supercov measured but did not observe. A test
may be able to close it.
A **measurement limit** means Supercov could not establish a complete boundary
for some code or execution. Common causes include ambiguous source scope,
dynamically created source, self-inspecting code, and an unsupported process or
runner boundary.
Supercov keeps those states separate. It does not turn “unknown” into
“uncovered,” and it does not round either one away to produce a reassuring 100%.
```sh supercov
npx supercov runs latest
npx supercov runs latest gaps
npx supercov runs latest scope
```
## What Supercov measures
| Line | Did execution reach this source line? |
| Statement | Did this executable statement run? |
| Function | Was this function entered? |
| Branch | Did each alternative execute? |
| Decision vector | Which combinations of boolean conditions occurred? |
| MC/DC witness | Was each condition shown to affect the decision independently? |
| Assertion coverage | Which measured statements are linked to passing checks by the agent-authored map? |
| Value path | Did defaults, optional chains, logical assignments, and similar constructs take each meaningful path? |
The exact obligations depend on the language and source construct. You do not
need to reason about all of them at once. Start with a file, then open a decision
or line only when the missing behavior needs explanation:
```sh supercov
npx supercov runs latest file app/checkout/session.ts
npx supercov runs latest decision app/checkout/session.ts:64
npx supercov runs latest line app/checkout/session.ts:64
```
## Why line coverage is not enough
Consider:
```js
The line can run even if the suite never proves that administrators are allowed,
owners are allowed, and everyone else is denied. Branches, decision vectors, and
MC/DC expose those missing cases instead of treating one executed line as proof
that the decision is safe.
The same principle applies to Rust, Python, and Ruby boolean expressions and
control flow.
## What 100% means
Supercov derives the coverage denominator from source structure before the test
run. Adding or removing tests cannot silently change the definition of 100%.
A complete result means every declared obligation was measured and covered. It
does not mean the product has no bugs, the assertions are meaningful, or every
possible input was tested. Review test quality and user-visible behavior, not
only the percentage.
TypeScript imports known to disappear during compilation do not add runtime
statement obligations. The assertion report's `excludedStatements` view lists
these locations. Imports that still execute, including side-effect imports,
remain in the denominator.
To review what your JavaScript and TypeScript tests actually check, see
[Understanding assertions](assertions.md).
If source cannot be measured safely, Supercov reports a measurement limit
instead of claiming completeness.
## Exact and aggregate evidence
When a runner exposes test and attempt boundaries, Supercov can show which test
covered an obligation. When it cannot, Supercov records aggregate background
coverage without guessing which test caused it.
Both are useful:
- exact evidence helps you inspect or minimize individual tests;
- aggregate evidence still shows whether the whole suite reached the source.
Code that runs while no test is running counts in the run's total and in no
test's: a server starting before the first test, modules loading, work between
tests. When a run has any, the summary's `By test kind` table ends with a
`no test` row for what only that covered, which is why a run with one kind of
test can show a total above that kind's own:
```
By test kind
e2e 3 test(s) lines 80.00% branches 66.67% MC/DC 25.00%
no test lines 17.14% branches 0.00% MC/DC 0.00%
```
`files --group dir` has the same figure as a `no test` column wherever a
directory has any, so a directory's `All` never stands above its kinds
unexplained.
A test that made assertions and has no covered line to its name is counted in
a warning on the summary. `npx supercov runs latest tests without-evidence`
lists every such test, with its file, kind and outcome.
See [Supported suites](supported-suites.md) for the attribution available from
each runner.
## Recalculate a view from selected tests
The same stored run can answer different questions:
```sh supercov
npx supercov runs latest --filter all
npx supercov runs latest --filter passed
npx supercov runs latest --filter failed
```
`all` matches the usual whole-run view. `passed` shows evidence from successful
attempts. `failed` isolates failed attempts, including failed retries.
You can also focus on a test level or runner:
```sh supercov
npx supercov runs latest gaps --kind e2e
npx supercov runs latest gaps --runner playwright
```
These views are recalculated from stored evidence. Supercov is not filtering a
percentage that was computed from a different set of tests.
## Fix source scope before chasing gaps
If the summary reports ambiguous source scope, inspect it:
```sh supercov
npx supercov runs latest scope
```
It prints the scope by directory and reason, the ambiguous places first, and
the `SUPERCOV_SOURCE_ROOTS` value that would settle them. The summary names
the same places under `Instrumentation`. Add `--files` to list every file.
```
Files Status Directory Reason
79 AMBIGUOUS components unclassified first-party source
227 INCLUDED app discovered package source root
311 EXCLUDED tests test or fixture source
```
When first-party source lives in unusual directories, declare it explicitly:
```sh supercov
SUPERCOV_SOURCE_ROOTS=src,app npx supercov -- npm test
```
Supercov never treats these as project source, so they are neither measured
nor reported as limitations: another checkout nested in the tree (a directory
with its own `.git`, such as an agent worktree or a vendored clone), hidden
directories at the project root (`.shopify/`, `.vercel/`, `.idea/`), directories
named `generated`, and hashed bundler output inside `assets/`, `static/`, or
`public/`. Packages the root manifest declares in `workspaces` (or
`pnpm-workspace.yaml`) are discovered wherever they live; a declared package
without a conventional source directory is measured as a whole. Functions passed
to compile-time style macros (`stylex.create(...)`) are left as written because
the bundler consumes them at build time; nothing about them runs.
Inside the roots, tests, fixtures, tool scripts and configuration files are
still recognised by their path and left out. A root you name settles what it
names: with `SUPERCOV_SOURCE_ROOTS=src,scripts` the `scripts/` directory is
source, and so is `src/test-utils` when it is named as a root. Only what lies
below a named root is still judged by its path, and a root that is a single
file is always measured.
In a Next.js application, root `components/` and `pages/`, and the root files
Next.js reads by name (`proxy`, `middleware`, `instrumentation`) are source
without being declared. Under `app/` a folder is a URL segment, so the files
the App Router serves (`route.ts`, `page.tsx`, `layout.tsx` and the rest) are
source whatever their folders are called: `app/api/wordpress/test/route.ts` is
the handler of `/api/wordpress/test`, not a test.
Choose roots that describe code the repository owns. Do not include dependencies
or generated output merely to make a warning disappear.
## Assertion percentage
When a run has been assessed with `assertions assess`, its regular summary also
shows assertion coverage: executed statements a test is judged to catch
breaking, divided by all assessed statements. This row always describes the
whole run, independently of `--filter`, `--kind` and `--runner`, and is separate
from the structural metrics above. See [assertion coverage](assertions.md).