supercov 0.0.16 → 0.0.18

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,157 @@
1
+ # Agent loop
2
+
3
+ Use Supercov in a repeatable agent loop: run the suite, query uncovered
4
+ obligations, add a focused test, rerun, and compare results. This page covers
5
+ the loop, the recommended prompt, and failure handling.
6
+
7
+ ## The shape of the loop
8
+
9
+ ```text
10
+ run the suite -> ask what is open -> write one test -> re-run -> diff
11
+ ^ |
12
+ +-------------------------------------------------------------------+
13
+ ```
14
+
15
+ Each pass should close a small number of related obligations and end with
16
+ evidence that it did. An agent that writes ten tests before re-running has no
17
+ way to attribute the outcome; an agent that re-runs after every trivial edit
18
+ spends its budget on test execution instead of thinking.
19
+
20
+ ## One pass, in commands
21
+
22
+ ```sh
23
+ # 1. Establish a baseline. Only needed once per session.
24
+ npx supercov -- npm test
25
+
26
+ # 2. Orient without loading a report into context.
27
+ npx supercov runs latest --json
28
+ npx supercov runs latest gaps --limit 5 --json
29
+
30
+ # 3. Understand one target.
31
+ npx supercov runs latest file app/checkout/session.ts --json
32
+ npx supercov runs latest decision app/checkout/session.ts:64 --json
33
+
34
+ # 4. Check what already exercises that line, to avoid writing a duplicate.
35
+ npx supercov runs latest line app/checkout/session.ts:64 --json
36
+
37
+ # 5. Write one test. Then re-run and prove the gain.
38
+ npx supercov -- npm test
39
+ npx supercov diff <previous-run-id> latest --json
40
+ ```
41
+
42
+ Step 4 is the one agents skip and should not. `line` answers "what already
43
+ executes this line", which usually reveals either an existing test to extend or
44
+ the exact reason nothing reaches it.
45
+
46
+ ## A prompt you can paste
47
+
48
+ ```text
49
+ You are improving test coverage for this repository using Supercov.
50
+
51
+ Baseline:
52
+ npx supercov -- npm test
53
+
54
+ Then repeat this loop until coverage completeness stops improving, the target
55
+ is met, or you run out of time:
56
+
57
+ 1. npx supercov runs latest gaps --limit 5 --json
58
+ 2. Pick the file with the highest-value open obligations.
59
+ 3. npx supercov runs latest file <path> --json
60
+ npx supercov runs latest decision <path>:<line> --json
61
+ npx supercov runs latest line <path>:<line> --json
62
+ 4. Write ONE focused test that closes the specific obligations you just read.
63
+ The assertion must be meaningful on its own; never assert something trivial
64
+ just to execute a line.
65
+ 5. npx supercov -- npm test
66
+ 6. npx supercov diff <previous-run-id> latest --json
67
+ If the diff shows no gain, revert the test rather than keeping it.
68
+
69
+ Rules:
70
+ - Never modify application source to make coverage easier.
71
+ - Never weaken or delete an existing assertion.
72
+ - If a decision cannot be reached from any public entry point, say so and move
73
+ on instead of exporting internals to reach it.
74
+ - Report the run ids you compared and the obligations you closed.
75
+ ```
76
+
77
+ The last rule matters more than it looks. An unattended agent that cannot reach
78
+ a branch will otherwise start reshaping the code so it can, which is exactly
79
+ the failure mode that gives coverage targets a bad name.
80
+
81
+ ## Budgeting an overnight session
82
+
83
+ Test execution dominates the wall clock, so the number of passes is roughly the
84
+ time budget divided by suite duration. Two adjustments help:
85
+
86
+ - Narrow the command while iterating. `npx supercov -- npx vitest run
87
+ app/checkout` produces a valid run over a smaller denominator; use the full
88
+ suite for the baseline and the final verification.
89
+ - Let the build cache work. When the source, configuration and toolchain
90
+ fingerprint is unchanged, the instrumented build is reused and that phase
91
+ costs approximately nothing. Changing a dependency or a build config in the
92
+ middle of a session throws that away.
93
+
94
+ ## Choosing what to attack
95
+
96
+ `gaps` is ordered to be useful, but not every open obligation deserves a test.
97
+ For a project that prefers end-to-end evidence, start with the existing
98
+ projection rather than inventing a new test taxonomy:
99
+
100
+ ```sh
101
+ npx supercov runs latest gaps --kind e2e
102
+ ```
103
+
104
+ Each file distinguishes obligations covered by another test kind from those
105
+ uncovered everywhere. The former are candidates for stronger E2E coverage;
106
+ the latter are gaps in the combined suite. When an error path cannot be reached
107
+ through E2E, first check whether the test double can express that failure before
108
+ falling back to a narrower unit test.
109
+
110
+ Two queries help an agent argue about value rather than count:
111
+
112
+ ```sh
113
+ # What does the suite prove today, minus redundancy?
114
+ npx supercov runs latest minimize --filter passed
115
+
116
+ # Reach a target with the smallest possible subset.
117
+ npx supercov runs latest minimize --filter passed --metric mcdc --target 80
118
+ ```
119
+
120
+ `minimize` is an exact branch-and-bound solver, not a greedy approximation: the
121
+ subset it returns is a proved minimum. It refuses to answer for a view that
122
+ contains background or unattributed evidence, because there is no honest way to
123
+ name an exact subset of tests when the runner never exposed test boundaries.
124
+
125
+ ## Reading a run that is not the newest
126
+
127
+ `latest` is a convenience for interactive use. An agent that resumes work later,
128
+ or that compares across a session, should use the immutable run id:
129
+
130
+ ```sh
131
+ npx supercov runs --limit 10 --json
132
+ npx supercov runs run_0123456789abcdef gaps --json
133
+ ```
134
+
135
+ Queries compare the stored fingerprint with the current workspace and mark a run
136
+ stale when the code has moved on. Treat a stale run as history, not as a
137
+ description of the working tree.
138
+
139
+ ## What to do about honest gaps
140
+
141
+ Some obligations are open because the tooling says so, not because a test is
142
+ missing:
143
+
144
+ - **Background or unattributed evidence.** An unsupported runner, or work that
145
+ arrived without a carrier, is recorded under a first-class background scope.
146
+ It appears in the default all-attempt view and is excluded from per-test
147
+ passed-only coverage. Writing more tests will not move it; adding runner
148
+ support will.
149
+ - **Ambiguous source scope.** A candidate file that Supercov could not
150
+ confidently classify as first-party blocks a complete verdict. Inspect with
151
+ `coverage scope` and set `SUPERCOV_SOURCE_ROOTS` to declare the authoritative
152
+ scope.
153
+ - **Semantic-safety blockers.** A function whose source is coerced or reflected
154
+ on at runtime is left uninstrumented on purpose, and direct `eval` cannot have
155
+ a stable denominator at all. Both are recorded with their exact location.
156
+
157
+ 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,134 @@
1
+ # Coverage model
2
+
3
+ Supercov derives coverage obligations from code structure. It reports which
4
+ obligations ran and the quality of the available evidence, including line,
5
+ branch, value-path, control-flow, and MC/DC coverage.
6
+
7
+ ## Obligations
8
+
9
+ An obligation is one thing the code structure requires a test to demonstrate.
10
+ The denominator is fixed before the run from the source itself, so a percentage
11
+ cannot drift when tests are added or removed.
12
+
13
+ | Family | Obligation |
14
+ | --- | --- |
15
+ | Lines | Each executable line executes |
16
+ | Statements | Each statement executes |
17
+ | Functions | Each function is entered |
18
+ | Branches | Each alternative is taken: `true`, `false`, switch fallthrough, and the implicit no-match arm |
19
+ | MC/DC | Each atomic condition is shown to independently determine its decision |
20
+ | Value selection | Optional-chain short-circuits, logical assignments, and parameter or destructuring defaults each resolve both ways |
21
+ | Control flow | `try` versus `catch`, and zero-iteration versus entered `for-in` / `for-of` |
22
+
23
+ The value-selection and control-flow families are the ones most tools omit.
24
+ `a?.b`, `x ??= y` and `function f(a = 1)` each hide a decision that never
25
+ appears as a branch in a conventional report, and a `for-of` that never runs
26
+ with an empty collection is an untested path even though every line inside it
27
+ is green.
28
+
29
+ ## MC/DC in one example
30
+
31
+ Modified condition/decision coverage asks more than "was this condition true and
32
+ false at some point". It asks whether each condition was shown to *independently
33
+ change the outcome*, which requires a pair of executions differing in that one
34
+ condition and producing different decisions.
35
+
36
+ For `isAdmin || (total > limit && !locked)`:
37
+
38
+ | Vector | `isAdmin` | `total > limit` | `!locked` | Decision |
39
+ | --- | --- | --- | --- | --- |
40
+ | v1 | F | T | T | true |
41
+ | v2 | F | F | T | false |
42
+ | v3 | F | T | F | false |
43
+ | v4 | T | F | T | true |
44
+
45
+ - v1 and v2 differ only in `total > limit` and disagree, so that condition is
46
+ proven.
47
+ - v1 and v3 do the same for `!locked`.
48
+ - v4 and v2 do the same for `isAdmin`.
49
+
50
+ Remove v2 and two of the three proofs collapse, even though every condition has
51
+ still been observed both true and false, and every line is still green. That is
52
+ the gap MC/DC exists to catch, and it is why the criterion is required for the
53
+ highest software assurance levels in avionics.
54
+
55
+ Supercov stores **vector-level provenance**: which test produced each observed
56
+ vector, not just which tests touched the decision. A filtered query therefore
57
+ recomputes valid witness pairs for the tests it selected, rather than filtering
58
+ a percentage computed for a different set. A witness assembled from one unit
59
+ vector and one end-to-end vector counts for the combined suite and for neither
60
+ level alone — and Supercov reports it that way.
61
+
62
+ ## Quality of evidence
63
+
64
+ Not all coverage is equally convincing. Each line, branch alternative, vector
65
+ and condition records how it was reached:
66
+
67
+ | Level | Meaning |
68
+ | --- | --- |
69
+ | Unexecuted | No evidence |
70
+ | Executed | Reached during a test, with no explicit causal link |
71
+ | Action-linked | Reached inside a recognised browser action such as `locator.click()` |
72
+ | Assertion-linked | Reached inside an `expect()` matcher, or in the code path an assertion depends on |
73
+
74
+ Only an explicit browser or server event can raise confidence to
75
+ assertion-linked. Where Supercov has to fall back on timing correlation — an
76
+ early cross-origin iframe probe, for example — the evidence stays
77
+ execution-only and is labelled as such. Code reached outside a recognised
78
+ action, such as setup work or a helper making its own HTTP requests, still has
79
+ exact test attribution but may carry no action phase at all.
80
+
81
+ In Playwright, the phase travels with the request: an action opened in the
82
+ browser is still the active phase inside the server route it triggers, so a
83
+ chain of `click → application decision → visible assertion` is queryable.
84
+
85
+ ## Provenance
86
+
87
+ Every test carries two independent labels.
88
+
89
+ **Runner** is the process that executed it — `playwright`, `vitest`, `jest`,
90
+ `node`.
91
+
92
+ **Kind** is its semantic level — `e2e`, `integration`, `component`, `unit`.
93
+ Kind is resolved in descending confidence from an explicit `SUPERCOV_TEST_KIND`,
94
+ then the Playwright project name, then the test path, then the runner default
95
+ (Playwright is end-to-end, Vitest is unit). Queries preserve how the label was
96
+ established, so an inferred kind is never presented as a declared one.
97
+
98
+ Vitest module-import and setup execution is retained as a separate setup scope
99
+ rather than being attributed to whichever test happened to run first.
100
+
101
+ ## Attempts and filters
102
+
103
+ Evidence records attempt status, so a test is classified as passed, failed,
104
+ flaky, skipped, timed out, interrupted, unknown, or selected but unstarted
105
+ after fail-fast. `--filter` selects which attempts contribute to a view:
106
+
107
+ - `all` — every executed attempt, including attempts that later failed. This is
108
+ the default and matches conventional coverage tools.
109
+ - `passed` — successful attempts of tests that ultimately passed.
110
+ - `failed` — failed attempts only, including failed retries of a flaky test.
111
+
112
+ Passed and failed views are derived from the same immutable archive rather than
113
+ duplicated into separate report files, so they cannot disagree.
114
+
115
+ ## When completeness is blocked
116
+
117
+ A verdict is only useful if it refuses to be complete when it cannot be:
118
+
119
+ - **Ambiguous scope.** Every candidate source file is retained as included,
120
+ excluded, or ambiguous. Ambiguity blocks a complete verdict and is
121
+ inspectable with `coverage scope`. Set `SUPERCOV_SOURCE_ROOTS` to declare the
122
+ authoritative scope.
123
+ - **Semantic-safety blockers.** When application code coerces or observes a
124
+ function's own source, Supercov leaves that body uninstrumented and records
125
+ the blocker rather than transforming code whose text is being read.
126
+ - **Unknowable denominators.** Direct `eval` and `Function` source cannot
127
+ receive a stable pre-run denominator. Their exact locations are recorded as
128
+ completeness blockers instead of being silently excluded.
129
+ - **Unattributed evidence.** Execution that arrives without a carrier is stored
130
+ under a first-class background scope, visible in the all-attempt view and
131
+ excluded from per-test passed-only coverage.
132
+
133
+ None of these are rounded away. A blocked verdict is more useful than a
134
+ comfortable 100%.
@@ -0,0 +1,122 @@
1
+ # Evidence and runs
2
+
3
+ Each run stores one evidence artifact and its metadata. Reports, queries, and
4
+ comparisons are derived from those files on demand.
5
+
6
+ ## What a run is
7
+
8
+ ```text
9
+ .supercov/runs/2026-08-24T01-25-11Z/
10
+ evidence.raw.gz exact denominator manifest + raw per-worker and background evidence
11
+ run.json fingerprints, phase timings, schema version, integrity state
12
+ ```
13
+
14
+ Two files. No HTML, no derived report, no query cache. Loose evidence written
15
+ during the run is removed only after the whole run directory is atomically
16
+ visible, so a run is either complete or absent.
17
+
18
+ Run ids are UTC timestamps, which makes them sort chronologically and makes
19
+ retention deterministic.
20
+
21
+ ## Derived, never stored
22
+
23
+ Every coverage view — the summary, per-file rankings, gap lists, decision
24
+ detail, per-test contribution, the minimizer, and the passed and failed filters
25
+ — is reconstructed from the archive when you ask for it. Nothing is written back.
26
+
27
+ This is why `--filter passed` and `--filter all` can never contradict each
28
+ other, and why a query added in a future version can answer questions about a
29
+ run recorded today: the stored schema is the raw evidence, not a rendering of it.
30
+
31
+ Fresh-process summary, files and gaps queries take roughly two tenths of a
32
+ second on the reference run described in [Performance](/docs/performance).
33
+
34
+ ## Integrity and staleness
35
+
36
+ Each run stores SHA-256 fingerprints for:
37
+
38
+ - first-party source
39
+ - test files
40
+ - dependency lockfiles
41
+ - test and build configuration
42
+ - the instrumenter itself
43
+
44
+ plus the evidence schema version and the Git revision and dirty state at the
45
+ time of the run.
46
+
47
+ Queries compare the stored fingerprint against the current workspace and
48
+ visibly mark a stale run. Evidence carrying a different run scope is rejected
49
+ outright rather than merged in.
50
+
51
+ ## Comparing two runs
52
+
53
+ ```sh
54
+ npx supercov diff <older-run> <newer-run>
55
+ npx supercov diff <older-run> <newer-run> --json
56
+ ```
57
+
58
+ `diff` reports what the newer run covers that the older one did not, and what
59
+ it lost. Both inputs are immutable and untouched, which is what makes the
60
+ comparison meaningful: neither side can have been rewritten by the act of
61
+ comparing them.
62
+
63
+ ## Merging shards
64
+
65
+ ```sh
66
+ npx supercov merge <first-run-id> <second-run-id> [...]
67
+ ```
68
+
69
+ `merge` accepts only runs whose source, test, dependency, configuration,
70
+ instrumenter, schema and denominator fingerprints are identical. It rewrites
71
+ the run scope inside every evidence record, namespaces shard paths, and
72
+ publishes a new immutable run atomically. Input runs are never modified or
73
+ deleted.
74
+
75
+ This is the distributed and multi-host primitive. Incompatible shards fail with
76
+ the exact differing fingerprint domains rather than producing a plausible but
77
+ invalid aggregate — two shards built from different source trees do not have a
78
+ common denominator, and no amount of arithmetic creates one.
79
+
80
+ ## Durability
81
+
82
+ Everything that can be interrupted is written to survive it.
83
+
84
+ - Evidence archive, metadata and state writes use sibling temporary files,
85
+ `fsync`, and atomic rename.
86
+ - Lock acquisition uses exclusive creation followed by `fsync`.
87
+ - Run state is written durably through the preparing, building, testing and
88
+ publishing phases.
89
+ - `SIGINT`, `SIGTERM` and `SIGHUP` are forwarded to the entire child process
90
+ group.
91
+ - If the process is killed without a cleanup opportunity, the next invocation
92
+ marks the dead PID's run abandoned and refreshes the isolated namespace
93
+ before reusing it.
94
+
95
+ The published `run.json` is the durable terminal record, so terminal work state
96
+ is not retained after publication.
97
+
98
+ ## Retention
99
+
100
+ ```sh
101
+ npx supercov clean
102
+ npx supercov clean --keep 20 --dry-run
103
+ npx supercov clean --keep 20
104
+ ```
105
+
106
+ Cleanup never runs automatically. `clean` removes explicit history, orphaned
107
+ and terminal transient data, and the marker-owned build workspace; `--keep N`
108
+ preserves the N newest runs. It acquires the same lock as a coverage run,
109
+ refuses to race an active run, and never touches unowned paths.
110
+
111
+ ## Phase timings
112
+
113
+ Every run records monotonic durations for initialization, workspace
114
+ preparation, adapter setup, the instrumented build, your unchanged test command,
115
+ and evidence publication. They are stored in `run.json` and returned by
116
+ `supercov runs --json`.
117
+
118
+ These are timings, not an overhead claim. A test script that performs its own
119
+ build may overlap work with the instrumented-build phase, and true end-to-end
120
+ overhead requires an explicit control run — which Supercov never performs
121
+ automatically, because an arbitrary test command can write data or cost money.
122
+ [Performance](/docs/performance) documents the comparison methodology.