supercov 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,17 @@ suites.
7
7
  npx supercov -- npm test
8
8
  ```
9
9
 
10
+ For coding agents, put the same rule in the repository instructions they read
11
+ before running commands (for example `AGENTS.md` or `CLAUDE.md`):
12
+
13
+ ```md
14
+ Measure coverage with `npx supercov -- npm test`. Prefix the project's full
15
+ test command; do not substitute a single unit, integration, or E2E script.
16
+ ```
17
+
18
+ `npx supercov --help` explains the full-command rule, and
19
+ `npx supercov docs agent-loop` prints the bounded query workflow as Markdown.
20
+
10
21
  For local development before publication, a Supercov contributor can expose
11
22
  the checkout globally. Consumer repositories still remain untouched:
12
23
 
@@ -141,8 +152,9 @@ Use `--filter passed` for verified coverage from successful attempts of
141
152
  ultimately passing tests, or `--filter failed` to inspect only execution from
142
153
  failed attempts (including failed retries of flaky tests). Evidence records
143
154
  attempt status and classify each test as passed, failed, flaky, skipped, timed
144
- out, interrupted, or unknown. Passed and failed views are derived from the
145
- same immutable archive rather than duplicated into presentation files.
155
+ out, interrupted, unknown, or selected but unstarted after fail-fast. Passed
156
+ and failed views are derived from the same immutable archive rather than
157
+ duplicated into presentation files.
146
158
 
147
159
  The run ID is positional because all coverage queries operate on one immutable
148
160
  run. `latest` is a convenience selector for interactive use. Every query
@@ -191,12 +203,13 @@ configuration, instrumenter, schema, and denominator fingerprints. It rewrites
191
203
  the run scope inside every evidence record, namespaces shard paths, publishes a
192
204
  new immutable run atomically, and leaves all input runs untouched. This is the
193
205
  distributed/multi-host primitive; incompatible shards fail clearly instead of
194
- producing a plausible but invalid aggregate.
206
+ producing a plausible but invalid aggregate. A rejection names each exact
207
+ domain that differs instead of presenting a generic list of possibilities.
195
208
 
196
209
  For a JavaScript or TypeScript project, the CLI:
197
210
 
198
211
  1. refreshes a stable isolated source namespace under
199
- `.supercov/cache/workspace/<project>/`, links the existing
212
+ `supercov/workspace/<project>/`, links the existing
200
213
  dependency tree, and creates generated runner configuration and build output
201
214
  only there; file data uses copy-on-write reflinks where the filesystem
202
215
  supports them, and falls back to copying where it does not; the stable path
@@ -229,9 +242,10 @@ For a JavaScript or TypeScript project, the CLI:
229
242
  rebuilt afterward.
230
243
 
231
244
  Only the Supercov-owned `.supercov/` run store and marker-protected
232
- `.supercov/cache/workspace/` cache are modified in the user's checkout. A user-created
233
- `supercov/` directory without Supercov's ownership marker is never treated as
234
- storage. A per-project lock rejects overlapping runs before either can build. Run state is durably written
245
+ `supercov/workspace/` cache are modified in the user's checkout. A user-created
246
+ `supercov/` directory without Supercov's ownership marker remains ordinary
247
+ project source; Supercov selects a deterministic non-dotted fallback container
248
+ instead. A per-project lock rejects overlapping runs before either can build. Run state is durably written
235
249
  through preparing/building/testing/publishing phases; SIGINT, SIGTERM,
236
250
  and SIGHUP are forwarded to the entire child process group. If the process is
237
251
  killed without a cleanup opportunity, the next invocation marks the dead PID's
package/bin/supercov.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { spawn } from "node:child_process";
4
+ import { dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
4
6
  import { resolveNativeBinary } from "./native.js";
5
7
 
6
8
  let rustBinary;
@@ -15,10 +17,17 @@ const child = spawn(
15
17
  process.argv.slice(2),
16
18
  {
17
19
  stdio: "inherit",
18
- env: process.env,
20
+ env: {
21
+ ...process.env,
22
+ SUPERCOV_PACKAGE_ROOT: resolvePackageRoot(),
23
+ },
19
24
  },
20
25
  );
21
26
 
27
+ function resolvePackageRoot() {
28
+ return dirname(dirname(fileURLToPath(import.meta.url)));
29
+ }
30
+
22
31
  for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
23
32
  process.once(signal, () => {
24
33
  try {
@@ -0,0 +1,159 @@
1
+ # Agent loop
2
+
3
+ Supercov exists because coverage reports are built for people and coding agents
4
+ need something different: small answers, a stable format, and a way to prove
5
+ that the test just written actually changed the result. This page describes the
6
+ loop, the prompt, and the failure modes worth guarding against when nobody is
7
+ watching.
8
+
9
+ ## The shape of the loop
10
+
11
+ ```text
12
+ run the suite -> ask what is open -> write one test -> re-run -> diff
13
+ ^ |
14
+ +-------------------------------------------------------------------+
15
+ ```
16
+
17
+ Each pass should close a small number of related obligations and end with
18
+ evidence that it did. An agent that writes ten tests before re-running has no
19
+ way to attribute the outcome; an agent that re-runs after every trivial edit
20
+ spends its budget on test execution instead of thinking.
21
+
22
+ ## One pass, in commands
23
+
24
+ ```sh
25
+ # 1. Establish a baseline. Only needed once per session.
26
+ npx supercov -- npm test
27
+
28
+ # 2. Orient without loading a report into context.
29
+ npx supercov runs latest --json
30
+ npx supercov runs latest gaps --limit 5 --json
31
+
32
+ # 3. Understand one target.
33
+ npx supercov runs latest file app/checkout/session.ts --json
34
+ npx supercov runs latest decision app/checkout/session.ts:64 --json
35
+
36
+ # 4. Check what already exercises that line, to avoid writing a duplicate.
37
+ npx supercov runs latest line app/checkout/session.ts:64 --json
38
+
39
+ # 5. Write one test. Then re-run and prove the gain.
40
+ npx supercov -- npm test
41
+ npx supercov diff <previous-run-id> latest --json
42
+ ```
43
+
44
+ Step 4 is the one agents skip and should not. `line` answers "what already
45
+ executes this line", which usually reveals either an existing test to extend or
46
+ the exact reason nothing reaches it.
47
+
48
+ ## A prompt you can paste
49
+
50
+ ```text
51
+ You are improving test coverage for this repository using Supercov.
52
+
53
+ Baseline:
54
+ npx supercov -- npm test
55
+
56
+ Then repeat this loop until coverage completeness stops improving, the target
57
+ is met, or you run out of time:
58
+
59
+ 1. npx supercov runs latest gaps --limit 5 --json
60
+ 2. Pick the file with the highest-value open obligations.
61
+ 3. npx supercov runs latest file <path> --json
62
+ npx supercov runs latest decision <path>:<line> --json
63
+ npx supercov runs latest line <path>:<line> --json
64
+ 4. Write ONE focused test that closes the specific obligations you just read.
65
+ The assertion must be meaningful on its own; never assert something trivial
66
+ just to execute a line.
67
+ 5. npx supercov -- npm test
68
+ 6. npx supercov diff <previous-run-id> latest --json
69
+ If the diff shows no gain, revert the test rather than keeping it.
70
+
71
+ Rules:
72
+ - Never modify application source to make coverage easier.
73
+ - Never weaken or delete an existing assertion.
74
+ - If a decision cannot be reached from any public entry point, say so and move
75
+ on instead of exporting internals to reach it.
76
+ - Report the run ids you compared and the obligations you closed.
77
+ ```
78
+
79
+ The last rule matters more than it looks. An unattended agent that cannot reach
80
+ a branch will otherwise start reshaping the code so it can, which is exactly
81
+ the failure mode that gives coverage targets a bad name.
82
+
83
+ ## Budgeting an overnight session
84
+
85
+ Test execution dominates the wall clock, so the number of passes is roughly the
86
+ time budget divided by suite duration. Two adjustments help:
87
+
88
+ - Narrow the command while iterating. `npx supercov -- npx vitest run
89
+ app/checkout` produces a valid run over a smaller denominator; use the full
90
+ suite for the baseline and the final verification.
91
+ - Let the build cache work. When the source, configuration and toolchain
92
+ fingerprint is unchanged, the instrumented build is reused and that phase
93
+ costs approximately nothing. Changing a dependency or a build config in the
94
+ middle of a session throws that away.
95
+
96
+ ## Choosing what to attack
97
+
98
+ `gaps` is ordered to be useful, but not every open obligation deserves a test.
99
+ For a project that prefers end-to-end evidence, start with the existing
100
+ projection rather than inventing a new test taxonomy:
101
+
102
+ ```sh
103
+ npx supercov runs latest gaps --kind e2e
104
+ ```
105
+
106
+ Each file distinguishes obligations covered by another test kind from those
107
+ uncovered everywhere. The former are candidates for stronger E2E coverage;
108
+ the latter are gaps in the combined suite. When an error path cannot be reached
109
+ through E2E, first check whether the test double can express that failure before
110
+ falling back to a narrower unit test.
111
+
112
+ Two queries help an agent argue about value rather than count:
113
+
114
+ ```sh
115
+ # What does the suite prove today, minus redundancy?
116
+ npx supercov runs latest minimize --filter passed
117
+
118
+ # Reach a target with the smallest possible subset.
119
+ npx supercov runs latest minimize --filter passed --metric mcdc --target 80
120
+ ```
121
+
122
+ `minimize` is an exact branch-and-bound solver, not a greedy approximation: the
123
+ subset it returns is a proved minimum. It refuses to answer for a view that
124
+ contains background or unattributed evidence, because there is no honest way to
125
+ name an exact subset of tests when the runner never exposed test boundaries.
126
+
127
+ ## Reading a run that is not the newest
128
+
129
+ `latest` is a convenience for interactive use. An agent that resumes work later,
130
+ or that compares across a session, should use the immutable run id:
131
+
132
+ ```sh
133
+ npx supercov runs --limit 10 --json
134
+ npx supercov runs run_0123456789abcdef gaps --json
135
+ ```
136
+
137
+ Queries compare the stored fingerprint with the current workspace and mark a run
138
+ stale when the code has moved on. Treat a stale run as history, not as a
139
+ description of the working tree.
140
+
141
+ ## What to do about honest gaps
142
+
143
+ Some obligations are open because the tooling says so, not because a test is
144
+ missing:
145
+
146
+ - **Background or unattributed evidence.** An unsupported runner, or work that
147
+ arrived without a carrier, is recorded under a first-class background scope.
148
+ It appears in the default all-attempt view and is excluded from per-test
149
+ passed-only coverage. Writing more tests will not move it; adding runner
150
+ support will.
151
+ - **Ambiguous source scope.** A candidate file that Supercov could not
152
+ confidently classify as first-party blocks a complete verdict. Inspect with
153
+ `coverage scope` and set `SUPERCOV_SOURCE_ROOTS` to declare the authoritative
154
+ scope.
155
+ - **Semantic-safety blockers.** A function whose source is coerced or reflected
156
+ on at runtime is left uninstrumented on purpose, and direct `eval` cannot have
157
+ a stable denominator at all. Both are recorded with their exact location.
158
+
159
+ An agent should surface these rather than grind against them.
package/docs/cli.md ADDED
@@ -0,0 +1,145 @@
1
+ # CLI reference
2
+
3
+ Every command is local. Nothing is uploaded, and no command runs your test
4
+ suite unless you ask it to.
5
+
6
+ ```sh
7
+ supercov --help
8
+ ```
9
+
10
+ ## Creating a run
11
+
12
+ ```sh
13
+ supercov -- <test command>
14
+ ```
15
+
16
+ Everything after `--` is executed as written. Supercov propagates coverage
17
+ through every Node child process the command launches, then publishes one
18
+ immutable run.
19
+
20
+ ```sh
21
+ npx supercov -- npm test
22
+ npx supercov -- npx playwright test --project=chromium
23
+ npx supercov -- npx vitest run app/checkout
24
+ ```
25
+
26
+ A per-project lock rejects overlapping runs before either can build.
27
+
28
+ ## Listing runs
29
+
30
+ ```sh
31
+ supercov runs [--limit N] [--json]
32
+ ```
33
+
34
+ Runs are listed newest first with their id, duration, phase timings and
35
+ integrity state. Use the id — not `latest` — when work spans a session.
36
+
37
+ ## Coverage queries
38
+
39
+ All coverage queries take the form:
40
+
41
+ ```sh
42
+ supercov runs <run-id> [query] [options]
43
+ ```
44
+
45
+ `<run-id>` is positional because every coverage view belongs to exactly one
46
+ immutable run. `latest` selects the newest local run.
47
+
48
+ | Query | Answers |
49
+ | --- | --- |
50
+ | no query | Overall completeness for the selected view |
51
+ | `kinds` | Completeness split by semantic level (`unit`, `e2e`, …) |
52
+ | `runners` | Completeness split by executing runner |
53
+ | `scope` | Which source files are included, excluded or ambiguous |
54
+ | `files` | Every included source file, ranked |
55
+ | `gaps` | Only files with unresolved obligations or measurement limits |
56
+ | `file <path>` | Every open obligation in one file |
57
+ | `decision <id \| path:line>` | Observed vectors and missing witnesses for one decision |
58
+ | `line <path:line>` | Line state, nested obligations, covering tests, and phases |
59
+ | `test <id \| name fragment>` | What one test contributes |
60
+ | `minimize` | The smallest test subset that preserves coverage |
61
+
62
+ ### Options
63
+
64
+ | Option | Applies to | Meaning |
65
+ | --- | --- | --- |
66
+ | `--filter all \| passed \| failed` | most queries | Which attempts contribute. `all` is the default and matches conventional tools. |
67
+ | `--kind <kind>` | most queries | Restrict to a semantic level, for example `--kind e2e`. |
68
+ | `--runner <runner>` | summary | Restrict to one executing runner, for example `--runner playwright`. |
69
+ | `--metric all \| lines \| statements \| functions \| branches \| mcdc` | `minimize` | Which obligations the solver must preserve. |
70
+ | `--target 0..100` | `minimize` | Stop once the metric reaches this level. |
71
+ | `--limit N`, `--offset N` | collections | Pagination. Collections default to 20 items and print a copyable next-page command. |
72
+ | `--json` | every query | The stable machine format. |
73
+
74
+ ### Examples
75
+
76
+ ```sh
77
+ # Orient in a few lines.
78
+ npx supercov runs latest
79
+ npx supercov runs latest --filter passed
80
+ npx supercov runs latest kinds
81
+
82
+ # Find and open one target.
83
+ npx supercov runs latest gaps --kind e2e --limit 10
84
+ npx supercov runs latest file app/routes/example.ts
85
+ npx supercov runs latest decision app/routes/example.ts:42
86
+ npx supercov runs latest line app/routes/example.ts:57
87
+
88
+ # Understand contribution and redundancy.
89
+ npx supercov runs latest test "checkout retry"
90
+ npx supercov runs latest minimize --filter passed
91
+ npx supercov runs latest minimize --filter passed --metric mcdc --target 80
92
+ ```
93
+
94
+ With `--kind`, gap and file queries additionally distinguish obligations covered
95
+ only by other test levels from obligations uncovered everywhere. On a combined
96
+ unit/E2E run, the default summary also prints the line count reached by other
97
+ test kinds but not by E2E, followed by the exact `gaps --kind e2e` query.
98
+
99
+ ## Comparing runs
100
+
101
+ ```sh
102
+ supercov diff <older-run> <newer-run> [--limit N] [--json]
103
+ ```
104
+
105
+ Reports what the newer run covers that the older one did not, and what it lost.
106
+ Both runs remain untouched.
107
+
108
+ ## Combining shards
109
+
110
+ ```sh
111
+ supercov merge <run-id> <run-id> [...]
112
+ ```
113
+
114
+ Accepts only runs with identical source, test, dependency, configuration,
115
+ instrumenter, schema and denominator fingerprints. It rewrites the run scope
116
+ inside every evidence record, namespaces shard paths, and publishes a new
117
+ immutable run atomically. Input runs are never modified. Incompatible shards
118
+ fail clearly rather than producing a plausible but invalid aggregate; the
119
+ error names each exact fingerprint domain that differs.
120
+
121
+ ## Retention
122
+
123
+ ```sh
124
+ supercov clean [--keep N] [--dry-run]
125
+ ```
126
+
127
+ `clean` removes all history and the isolated build workspace by default.
128
+ `--keep N` preserves the N newest runs. It never runs automatically, takes the
129
+ same lock as a coverage run, refuses to race an active run, and deletes only
130
+ exactly marker-owned Supercov storage.
131
+
132
+ ## Environment variables
133
+
134
+ | Variable | Effect |
135
+ | --- | --- |
136
+ | `SUPERCOV_SOURCE_ROOTS` | Declares the authoritative first-party source scope, resolving ambiguity that would otherwise block a complete verdict. |
137
+ | `SUPERCOV_TEST_KIND` | Declares the semantic level of the tests in this command, overriding every inference. |
138
+
139
+ ## Exit codes
140
+
141
+ | Code | Meaning |
142
+ | --- | --- |
143
+ | `0` | The run or query succeeded. |
144
+ | The test command's own code | A coverage run exits with the status of your command, so `supercov -- npm test` remains usable as a CI gate. |
145
+ | `2` | Supercov itself failed: an unknown command, an unreadable run, an incompatible merge, or a lock conflict. |
@@ -0,0 +1,136 @@
1
+ # Coverage model
2
+
3
+ Line coverage answers a question nobody actually has. "This line executed" says
4
+ nothing about whether the interesting thing on that line was ever true, ever
5
+ false, or ever mattered. Supercov measures **completeness**: of everything the
6
+ structure of the code obliges a suite to exercise, how much has been exercised,
7
+ and with what quality of evidence.
8
+
9
+ ## Obligations
10
+
11
+ An obligation is one thing the code structure requires a test to demonstrate.
12
+ The denominator is fixed before the run from the source itself, so a percentage
13
+ cannot drift when tests are added or removed.
14
+
15
+ | Family | Obligation |
16
+ | --- | --- |
17
+ | Lines | Each executable line executes |
18
+ | Statements | Each statement executes |
19
+ | Functions | Each function is entered |
20
+ | Branches | Each alternative is taken: `true`, `false`, switch fallthrough, and the implicit no-match arm |
21
+ | MC/DC | Each atomic condition is shown to independently determine its decision |
22
+ | Value selection | Optional-chain short-circuits, logical assignments, and parameter or destructuring defaults each resolve both ways |
23
+ | Control flow | `try` versus `catch`, and zero-iteration versus entered `for-in` / `for-of` |
24
+
25
+ The value-selection and control-flow families are the ones most tools omit.
26
+ `a?.b`, `x ??= y` and `function f(a = 1)` each hide a decision that never
27
+ appears as a branch in a conventional report, and a `for-of` that never runs
28
+ with an empty collection is an untested path even though every line inside it
29
+ is green.
30
+
31
+ ## MC/DC in one example
32
+
33
+ Modified condition/decision coverage asks more than "was this condition true and
34
+ false at some point". It asks whether each condition was shown to *independently
35
+ change the outcome*, which requires a pair of executions differing in that one
36
+ condition and producing different decisions.
37
+
38
+ For `isAdmin || (total > limit && !locked)`:
39
+
40
+ | Vector | `isAdmin` | `total > limit` | `!locked` | Decision |
41
+ | --- | --- | --- | --- | --- |
42
+ | v1 | F | T | T | true |
43
+ | v2 | F | F | T | false |
44
+ | v3 | F | T | F | false |
45
+ | v4 | T | F | T | true |
46
+
47
+ - v1 and v2 differ only in `total > limit` and disagree, so that condition is
48
+ proven.
49
+ - v1 and v3 do the same for `!locked`.
50
+ - v4 and v2 do the same for `isAdmin`.
51
+
52
+ Remove v2 and two of the three proofs collapse, even though every condition has
53
+ still been observed both true and false, and every line is still green. That is
54
+ the gap MC/DC exists to catch, and it is why the criterion is required for the
55
+ highest software assurance levels in avionics.
56
+
57
+ Supercov stores **vector-level provenance**: which test produced each observed
58
+ vector, not just which tests touched the decision. A filtered query therefore
59
+ recomputes valid witness pairs for the tests it selected, rather than filtering
60
+ a percentage computed for a different set. A witness assembled from one unit
61
+ vector and one end-to-end vector counts for the combined suite and for neither
62
+ level alone — and Supercov reports it that way.
63
+
64
+ ## Quality of evidence
65
+
66
+ Not all coverage is equally convincing. Each line, branch alternative, vector
67
+ and condition records how it was reached:
68
+
69
+ | Level | Meaning |
70
+ | --- | --- |
71
+ | Unexecuted | No evidence |
72
+ | Executed | Reached during a test, with no explicit causal link |
73
+ | Action-linked | Reached inside a recognised browser action such as `locator.click()` |
74
+ | Assertion-linked | Reached inside an `expect()` matcher, or in the code path an assertion depends on |
75
+
76
+ Only an explicit browser or server event can raise confidence to
77
+ assertion-linked. Where Supercov has to fall back on timing correlation — an
78
+ early cross-origin iframe probe, for example — the evidence stays
79
+ execution-only and is labelled as such. Code reached outside a recognised
80
+ action, such as setup work or a helper making its own HTTP requests, still has
81
+ exact test attribution but may carry no action phase at all.
82
+
83
+ In Playwright, the phase travels with the request: an action opened in the
84
+ browser is still the active phase inside the server route it triggers, so a
85
+ chain of `click → application decision → visible assertion` is queryable.
86
+
87
+ ## Provenance
88
+
89
+ Every test carries two independent labels.
90
+
91
+ **Runner** is the process that executed it — `playwright`, `vitest`, `jest`,
92
+ `node`.
93
+
94
+ **Kind** is its semantic level — `e2e`, `integration`, `component`, `unit`.
95
+ Kind is resolved in descending confidence from an explicit `SUPERCOV_TEST_KIND`,
96
+ then the Playwright project name, then the test path, then the runner default
97
+ (Playwright is end-to-end, Vitest is unit). Queries preserve how the label was
98
+ established, so an inferred kind is never presented as a declared one.
99
+
100
+ Vitest module-import and setup execution is retained as a separate setup scope
101
+ rather than being attributed to whichever test happened to run first.
102
+
103
+ ## Attempts and filters
104
+
105
+ Evidence records attempt status, so a test is classified as passed, failed,
106
+ flaky, skipped, timed out, interrupted, unknown, or selected but unstarted
107
+ after fail-fast. `--filter` selects which attempts contribute to a view:
108
+
109
+ - `all` — every executed attempt, including attempts that later failed. This is
110
+ the default and matches conventional coverage tools.
111
+ - `passed` — successful attempts of tests that ultimately passed.
112
+ - `failed` — failed attempts only, including failed retries of a flaky test.
113
+
114
+ Passed and failed views are derived from the same immutable archive rather than
115
+ duplicated into separate report files, so they cannot disagree.
116
+
117
+ ## When completeness is blocked
118
+
119
+ A verdict is only useful if it refuses to be complete when it cannot be:
120
+
121
+ - **Ambiguous scope.** Every candidate source file is retained as included,
122
+ excluded, or ambiguous. Ambiguity blocks a complete verdict and is
123
+ inspectable with `coverage scope`. Set `SUPERCOV_SOURCE_ROOTS` to declare the
124
+ authoritative scope.
125
+ - **Semantic-safety blockers.** When application code coerces or observes a
126
+ function's own source, Supercov leaves that body uninstrumented and records
127
+ the blocker rather than transforming code whose text is being read.
128
+ - **Unknowable denominators.** Direct `eval` and `Function` source cannot
129
+ receive a stable pre-run denominator. Their exact locations are recorded as
130
+ completeness blockers instead of being silently excluded.
131
+ - **Unattributed evidence.** Execution that arrives without a carrier is stored
132
+ under a first-class background scope, visible in the all-attempt view and
133
+ excluded from per-test passed-only coverage.
134
+
135
+ None of these are rounded away. A blocked verdict is more useful than a
136
+ comfortable 100%.
@@ -0,0 +1,124 @@
1
+ # Evidence and runs
2
+
3
+ A coverage number is only as trustworthy as the thing it was computed from.
4
+ Supercov keeps exactly one artifact per run and derives every view from it on
5
+ demand, so a report can never quietly disagree with the evidence it claims to
6
+ summarise.
7
+
8
+ ## What a run is
9
+
10
+ ```text
11
+ .supercov/runs/2026-08-24T01-25-11Z/
12
+ evidence.raw.gz exact denominator manifest + raw per-worker and background evidence
13
+ run.json fingerprints, phase timings, schema version, integrity state
14
+ ```
15
+
16
+ Two files. No HTML, no derived report, no query cache. Loose evidence written
17
+ during the run is removed only after the whole run directory is atomically
18
+ visible, so a run is either complete or absent.
19
+
20
+ Run ids are UTC timestamps, which makes them sort chronologically and makes
21
+ retention deterministic.
22
+
23
+ ## Derived, never stored
24
+
25
+ Every coverage view — the summary, per-file rankings, gap lists, decision
26
+ detail, per-test contribution, the minimizer, and the passed and failed filters
27
+ — is reconstructed from the archive when you ask for it. Nothing is written back.
28
+
29
+ This is why `--filter passed` and `--filter all` can never contradict each
30
+ other, and why a query added in a future version can answer questions about a
31
+ run recorded today: the stored schema is the raw evidence, not a rendering of it.
32
+
33
+ Fresh-process summary, files and gaps queries take roughly two tenths of a
34
+ second on the reference run described in [Performance](/docs/performance).
35
+
36
+ ## Integrity and staleness
37
+
38
+ Each run stores SHA-256 fingerprints for:
39
+
40
+ - first-party source
41
+ - test files
42
+ - dependency lockfiles
43
+ - test and build configuration
44
+ - the instrumenter itself
45
+
46
+ plus the evidence schema version and the Git revision and dirty state at the
47
+ time of the run.
48
+
49
+ Queries compare the stored fingerprint against the current workspace and
50
+ visibly mark a stale run. Evidence carrying a different run scope is rejected
51
+ outright rather than merged in.
52
+
53
+ ## Comparing two runs
54
+
55
+ ```sh
56
+ npx supercov diff <older-run> <newer-run>
57
+ npx supercov diff <older-run> <newer-run> --json
58
+ ```
59
+
60
+ `diff` reports what the newer run covers that the older one did not, and what
61
+ it lost. Both inputs are immutable and untouched, which is what makes the
62
+ comparison meaningful: neither side can have been rewritten by the act of
63
+ comparing them.
64
+
65
+ ## Merging shards
66
+
67
+ ```sh
68
+ npx supercov merge <first-run-id> <second-run-id> [...]
69
+ ```
70
+
71
+ `merge` accepts only runs whose source, test, dependency, configuration,
72
+ instrumenter, schema and denominator fingerprints are identical. It rewrites
73
+ the run scope inside every evidence record, namespaces shard paths, and
74
+ publishes a new immutable run atomically. Input runs are never modified or
75
+ deleted.
76
+
77
+ This is the distributed and multi-host primitive. Incompatible shards fail with
78
+ the exact differing fingerprint domains rather than producing a plausible but
79
+ invalid aggregate — two shards built from different source trees do not have a
80
+ common denominator, and no amount of arithmetic creates one.
81
+
82
+ ## Durability
83
+
84
+ Everything that can be interrupted is written to survive it.
85
+
86
+ - Evidence archive, metadata and state writes use sibling temporary files,
87
+ `fsync`, and atomic rename.
88
+ - Lock acquisition uses exclusive creation followed by `fsync`.
89
+ - Run state is written durably through the preparing, building, testing and
90
+ publishing phases.
91
+ - `SIGINT`, `SIGTERM` and `SIGHUP` are forwarded to the entire child process
92
+ group.
93
+ - If the process is killed without a cleanup opportunity, the next invocation
94
+ marks the dead PID's run abandoned and refreshes the isolated namespace
95
+ before reusing it.
96
+
97
+ The published `run.json` is the durable terminal record, so terminal work state
98
+ is not retained after publication.
99
+
100
+ ## Retention
101
+
102
+ ```sh
103
+ npx supercov clean
104
+ npx supercov clean --keep 20 --dry-run
105
+ npx supercov clean --keep 20
106
+ ```
107
+
108
+ Cleanup never runs automatically. `clean` removes explicit history, orphaned
109
+ and terminal transient data, and the marker-owned build workspace; `--keep N`
110
+ preserves the N newest runs. It acquires the same lock as a coverage run,
111
+ refuses to race an active run, and never touches unowned paths.
112
+
113
+ ## Phase timings
114
+
115
+ Every run records monotonic durations for initialization, workspace
116
+ preparation, adapter setup, the instrumented build, your unchanged test command,
117
+ and evidence publication. They are stored in `run.json` and returned by
118
+ `supercov runs --json`.
119
+
120
+ These are timings, not an overhead claim. A test script that performs its own
121
+ build may overlap work with the instrumented-build phase, and true end-to-end
122
+ overhead requires an explicit control run — which Supercov never performs
123
+ automatically, because an arbitrary test command can write data or cost money.
124
+ [Performance](/docs/performance) documents the comparison methodology.