supercov 0.0.45 → 0.0.47

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.
@@ -1,82 +1,111 @@
1
- # Understanding assertion coverage
1
+ # Assertion coverage
2
2
 
3
- After a normal test run, a coding agent can map what each JavaScript or
4
- TypeScript assertion checks into the run's `assertions.json`. Supercov reads
5
- that map alongside the recorded execution evidence and displays an assertion
6
- percentage in the regular coverage report.
3
+ Line coverage shows which code ran. Assertion coverage helps you see what the
4
+ tests checked. Your coding agent traces assertions back to the source, and
5
+ Supercov checks those links against recorded execution from the same test.
7
6
 
8
- ```sh supercov
9
- npx supercov -- npm test
10
- npx supercov runs latest assertions --json
7
+ Assertion coverage measures JavaScript, TypeScript, Python, Ruby and Rust. The
8
+ map format is the same for every one of them: an assertion is identified by its
9
+ file, line and column, so a project written in more than one language keeps a
10
+ single map.
11
+
12
+ Python and Ruby report an assertion's line but not its column, so two
13
+ assertions written on one line cannot be told apart and neither is credited.
14
+ Put them on separate lines.
15
+
16
+ ## A passing test can miss a wrong result
17
+
18
+ This function confirms an order and calculates its total:
19
+
20
+ ```js
21
+ export function checkout(price, quantity) {
22
+ const total = price * quantity;
23
+ return { status: 'confirmed', total };
24
+ }
11
25
  ```
12
26
 
13
- The run creates the map automatically, reusing the newest available map for the
14
- same test command and language. Pin the returned run ID and edit `data.map`.
15
- Ask your agent to follow
16
- `supercov docs assertion-agent`, read the current project source, and complete the map.
17
- Then validate and save the acknowledgement tokens for examined flows:
18
-
19
- ```sh supercov-example
20
- npx supercov runs <run> assertions validate --json
21
- # Copy examined expectedBasis tokens into assertions.json; save again.
22
- npx supercov runs <run> assertions check --require-mappings --require-observed --json
23
- npx supercov runs <run>
27
+ The test checks the status, but not the total:
28
+
29
+ ```js
30
+ import assert from 'node:assert/strict';
31
+ import test from 'node:test';
32
+ import { checkout } from './checkout.js';
33
+
34
+ test('confirms an order', () => {
35
+ const order = checkout(25, 2);
36
+ assert.equal(order.status, 'confirmed');
37
+ });
24
38
  ```
25
39
 
26
- The **Assertions** row appears beside Lines, Branches and MC/DC. It is the
27
- percentage of measured statements credited by current agent-authored flows,
28
- with exact passing assertion identity and execution in the same test. It uses
29
- the whole run with matching current source even when structural coverage is filtered. Incomplete
30
- maps remain useful; unmapped or dirty flows earn no credit.
40
+ Every line in the function runs. The test passes. But it would also pass if the
41
+ total were `49` instead of `50`.
42
+
43
+ ## How the agent finds the gap
44
+
45
+ 1. **Supercov records execution:** which statements ran in each test and which
46
+ assertions passed.
47
+ 2. **Your agent traces each assertion** through the test and source to explain
48
+ what it checks. It saves those links in the run's `assertions.json` map.
49
+ 3. **Supercov checks the map against the run.** A statement needs a current
50
+ explanation, execution, and a passing assertion in the same test to count.
31
51
 
32
- ## Check what the test asserts
52
+ In this example, the agent follows the status assertion back to the returned
53
+ order. It finds no check that reads the total:
33
54
 
34
- Suppose `src/shipping.js` contains:
55
+ - `order.status` → must equal `'confirmed'`.
56
+ - `order.total` → no assertion checks the calculated total.
57
+
58
+ The agent adds the missing assertion to the existing test:
35
59
 
36
60
  ```js
37
- export function shippingCost() {
38
- return 4;
39
- }
61
+ assert.equal(order.total, 50);
40
62
  ```
41
63
 
42
- These checks all call the function, but they test different things. Here,
43
- `assert` comes from `node:assert/strict`:
64
+ It reruns the full suite and updates the map. A total of `49` now fails the
65
+ test: two items at $25 must total $50. Line coverage has not changed, but the
66
+ test now checks the calculation.
67
+
68
+ ## Try it in your project
44
69
 
45
- | Assertion | What it checks |
46
- | --- | --- |
47
- | `assert.equal(shippingCost(), 4)` | The cost is exactly `4`. Returning `5` would fail. |
48
- | `assert.ok(shippingCost())` | The cost is truthy. Returning `5` would still pass. |
49
- | `shippingCost(); assert.equal(7, 7)` | Nothing about the returned cost. The assertion compares two constants. |
70
+ Open your project in your usual coding agent and paste this prompt. The agent
71
+ can install Supercov and run the commands for you.
72
+
73
+ ```text supercov-prompt
74
+ Read npx supercov docs assertion-agent. Run the full test suite through
75
+ Supercov, then build and validate its assertion map. Find one useful
76
+ missing check and add an assertion for the expected behavior.
77
+ Only change tests; do not weaken existing checks. Rerun the same suite
78
+ and update the map. Show the test change, before-and-after assertion
79
+ coverage, and any uncertainty or missing evidence.
80
+ ```
50
81
 
51
- Line coverage can be the same in all three cases. The useful test is the one
52
- that checks the behavior you need to preserve.
82
+ The agent uses your actual test command after `--`, for example
83
+ `npx supercov -- npm test`. On later runs of the same command, Supercov reuses
84
+ compatible mappings and flags explanations that need another look.
53
85
 
54
- The same applies to other results: checking the number of log calls does not
55
- check their messages, and checking one substring does not check the whole response.
86
+ ## Read the result
56
87
 
57
- ## Inspect and improve the map
88
+ The agent can show the summary and the statement-level report for a file:
58
89
 
59
- ```sh supercov-example
60
- npx supercov runs <run> assertions --limit 5
61
- npx supercov runs <run> assertion <assertion-id>
62
- npx supercov runs <run> source src/shipping.js
63
- npx supercov runs <run> assertions --view statements --file src/shipping.js --limit 20
90
+ ```sh
91
+ npx supercov runs <run-id>
92
+ npx supercov runs <run-id> assertions report --view statements --file checkout.js
64
93
  ```
65
94
 
66
- Each assertion keeps its exact test source anchor, the behavior it observes,
67
- and flows to explicit production statements. Follow those links when deciding
68
- whether a test needs another scenario or a stronger assertion. The model owns
69
- the semantic assessment; Supercov validates syntax, source references, evidence
70
- and freshness without reconstructing the reasoning.
71
-
72
- After changing code or tests, run the same suite command again. Its new map
73
- automatically carries forward unchanged entries;
74
- changed or ambiguous references stay dirty until reviewed. Source files are
75
- read from the project; each run retains file hashes and the map, without storing
76
- complete source copies. A stale checkout makes assertion coverage unavailable
77
- until tests are rerun. See the
78
- [map reference](assertion-maps.md) for the format, commands and limitations.
79
-
80
- MC/DC measures independent condition effects. Assertion coverage measures the
81
- statements linked to checks by the reviewed map. Maximizing either number does
82
- not prove every edit will fail a test: the exact property in `observes` matters.
95
+ **Assertions** is the percentage of measured source statements linked to
96
+ passing assertions by the agent's map. It is not a count of assertions. Each
97
+ statement counts once; statements that never ran remain in the total.
98
+
99
+ An unmapped statement is a place to investigate, not proof of a missing test:
100
+ the agent may not have mapped its existing check yet.
101
+
102
+ The strength of the check still matters. `assert.ok(order.total)` accepts both
103
+ `49` and `50`; `assert.equal(order.total, 50)` distinguishes them. Review the
104
+ expected behavior, not just the percentage.
105
+
106
+ Supercov does not generate or execute mutated code for this assessment. Unlike
107
+ mutation testing, it does not test whether deliberately introduced bugs are
108
+ caught. The agent's explanations still need review.
109
+
110
+ For the detailed workflow, see [Mapping assertions with an agent](assertion-agent.md).
111
+ For a result you cannot explain, see [Investigating assertion evidence](assertion-evidence.md).
package/docs/cli.md CHANGED
@@ -100,20 +100,37 @@ npx supercov runs latest assertions --help
100
100
  Assertion queries read the run-owned map. `source <path>` reads the matching current
101
101
  file directly. It prints source code with line numbers, preserving indentation;
102
102
  add `--json` only when you want structured `{line, text}` items. `--offset` is
103
- zero-based and `--limit` controls the number of source lines. Source and assertion analysis require the current
104
- checkout to match the run. Changed source makes assertion coverage unavailable;
105
- rerun tests to inherit mappings. The old `assertions inventory` and nested
106
- `assertions source --file` commands are replaced by these resource queries.
107
- Use read-only `assertions validate --json` to obtain expected acknowledgement tokens.
108
- After investigating, copy those tokens into `assertions.json`, save, and run
109
- `assertions check`. The `review` command is removed. `--require-mappings` requires
110
- a current explanation per recognized passing site, without implying completeness.
111
- Use `assertions --needs-attention` and `assertions report --view changes` to resume work.
112
- The regular run summary automatically includes the assertion percentage when
113
- that map exists, in text and `data.assertionCoverage` JSON. Read
114
- [Understanding assertion coverage](assertions.md) for the workflow or run
115
- `npx supercov docs assertion-agent` for instructions to give a coding agent.
116
- The [map reference](assertion-maps.md) covers syntax and verification gates.
103
+ zero-based and `--limit` controls the number of source lines. Source and assertion
104
+ investigation require current files that match the run. Rerun the suite after
105
+ source changes to inherit the map into a new run.
106
+
107
+ ### Assertion coverage
108
+
109
+ The regular run summary includes assertion coverage when a map has been
110
+ assessed. JSON reports expose it under `data.assertionCoverage`. Start with
111
+ [Understanding assertion coverage](assertions.md), or use these commands to
112
+ inspect and check a map:
113
+
114
+ ```sh supercov-example
115
+ npx supercov runs <run-id> assertions --needs-attention
116
+ npx supercov runs <run-id> assertion <assertion-id>
117
+ npx supercov runs <run-id> assertions report --view statements --file src/shipping.js
118
+ npx supercov runs <run-id> assertions report --view excludedStatements
119
+ npx supercov runs <run-id> assertions validate --json
120
+ npx supercov runs <run-id> assertions check --require-mappings
121
+ ```
122
+
123
+ Edit the file shown by `assertions`. Validation returns `expectedBasis` tokens;
124
+ after examining a flow, save its token in the map before running `check`.
125
+ `--require-mappings` requires explanations for recognized assertions observed
126
+ passing. Add `--require-observed` when every mapped site and selector should have
127
+ passing evidence, or `--min <percentage>` for a chosen target.
128
+
129
+ To inspect one large flow, add `--flow <flow-id> --view nodes` or `--view edges`
130
+ to the assertion detail command. `--compact` omits repeated source text from the
131
+ report. Follow the printed next-page command or JSON `pagination.nextOffset`.
132
+ Validation supports `--view flows`, `--view changes` and `--view errors` for large
133
+ maps. The [map reference](assertion-maps.md) describes all fields and gates.
117
134
 
118
135
  ## Narrow a view
119
136
 
@@ -33,6 +33,7 @@ npx supercov runs latest scope
33
33
  | Branch | Did each alternative execute? |
34
34
  | Decision vector | Which combinations of boolean conditions occurred? |
35
35
  | MC/DC witness | Was each condition shown to affect the decision independently? |
36
+ | Assertion coverage | Which measured statements are linked to passing checks by the agent-authored map? |
36
37
  | Value path | Did defaults, optional chains, logical assignments, and similar constructs take each meaningful path? |
37
38
 
38
39
  The exact obligations depend on the language and source construct. You do not
@@ -71,6 +72,11 @@ does not mean the product has no bugs, the assertions are meaningful, or every
71
72
  possible input was tested. Review test quality and user-visible behavior, not
72
73
  only the percentage.
73
74
 
75
+ TypeScript imports known to disappear during compilation do not add runtime
76
+ statement obligations. The assertion report's `excludedStatements` view lists
77
+ these locations. Imports that still execute, including side-effect imports,
78
+ remain in the denominator.
79
+
74
80
  To review what your JavaScript and TypeScript tests actually check, see
75
81
  [Understanding assertions](assertions.md).
76
82
 
package/docs/evidence.md CHANGED
@@ -109,3 +109,31 @@ npx supercov clean
109
109
  Preview cleanup first. The final command removes all runs and the isolated build
110
110
  cache; `--keep 20` preserves the 20 newest runs. Cleanup removes only
111
111
  marker-owned Supercov data.
112
+
113
+ ## Understand test kinds
114
+
115
+ Reports group tests by kind, such as unit, integration or E2E. Supercov uses
116
+ recognized file names and runner information; a kind is a classification, not
117
+ proof of what the test checks. A name such as `gatewayE2e.test.ts` identifies an
118
+ E2E test. Ambiguous names keep the runner's default, and the report tells you
119
+ how many tests use that default.
120
+
121
+ If your suite has a known kind, set it when running the command:
122
+
123
+ ```sh supercov-example
124
+ SUPERCOV_TEST_KIND=integration npx supercov -- npm test
125
+ ```
126
+
127
+ Supported values are `unit`, `component`, `integration` and `e2e`. Use separate
128
+ runs when different suites need different classifications.
129
+
130
+ ## When assertion evidence is missing
131
+
132
+ A test can make assertions without executing measured application code. It
133
+ might check static data or a dependency, or use work performed in shared setup.
134
+ Missing attribution across an asynchronous boundary can also leave a gap.
135
+
136
+ The assertion detail explains why a mapped node did or did not receive credit.
137
+ Use [Investigating assertion evidence](assertion-evidence.md) to distinguish
138
+ these cases. The report's runtime action-phase counts are separate from the
139
+ assertion map; zero action-phase lines does not mean zero asserted statements.
@@ -82,3 +82,19 @@ npx supercov clean
82
82
  Use `--dry-run` to preview cleanup. Keep enough run history for active reviews
83
83
  and automation; remove the cache only when reclaiming space matters more than a
84
84
  faster next run.
85
+
86
+ ## Keep assertion investigation fast
87
+
88
+ Save `assertions.json` and query the run to see the updated score. Supercov does
89
+ not rerun tests or call a model to calculate the report. Repeated queries reuse
90
+ the previous assessment while checking that your source still matches the run.
91
+ Editing the map automatically refreshes that assessment.
92
+
93
+ For large maps, ask for a short page instead of the whole graph. Within one
94
+ flow, `--view nodes` or `--view edges` pages the details; `--compact` omits repeated
95
+ source text while keeping locations and credit reasons. See
96
+ [Investigating assertion evidence](assertion-evidence.md#read-a-large-flow).
97
+
98
+ The disposable `assertions.report.cache.json` file lives beside the map. A missing
99
+ or damaged cache is rebuilt. Removing it affects the next query's speed, not
100
+ your saved explanations.
@@ -45,7 +45,7 @@ Supercov reports the level it actually observed. It does not guess.
45
45
  | --- | --- |
46
46
  | Playwright | Exact per test, worker, retry, outcome, action, and assertion phase |
47
47
  | Vitest | Exact per test, with setup execution kept separate |
48
- | Jest | Exact per test, including parameterized tests, with the user's own configuration, setup files and reporters kept; `expect` assertions link the evidence they check |
48
+ | Jest | Exact per test, including parameterized tests, with the user's own configuration, setup files and reporters kept; passing `expect` occurrences are identified for assertion maps |
49
49
  | `node:test` | Exact per test |
50
50
  | AVA and Mocha | Aggregate structural coverage |
51
51
  | Other Node-based runners | Aggregate when their processes remain visible to Supercov |
@@ -63,19 +63,15 @@ TypeScript, and TSX are supported.
63
63
  Supercov instruments an isolated copy. It does not ask you to add an import,
64
64
  reporter, plugin, or alternate build output.
65
65
 
66
- A suite that runs its own compiled output is built before the tests start.
67
- Supercov reads the tests: one that imports from `dist/`, or launches a package
68
- script that does—`spawn("npm", ["run", "start"])`, `execSync("npm run
69
- start")`, or any launch handed to the shell as one string—means the build is
70
- part of the run, and a `build` script is run inside the isolated copy first.
71
- Without it the server under test would never exist. A launch that only names a
72
- build subcommand, such as `vite build`, is not taken as consuming a build.
66
+ If tests import compiled output such as `dist/` or launch a script that uses it,
67
+ Supercov runs the project's build inside the isolated copy before testing.
68
+ Keep using your normal test and build commands. Instrumentation does not
69
+ require changing the project's TypeScript settings.
73
70
 
74
- Instrumented TypeScript is exempt from the host's type policy whether the
75
- compile is Supercov's own build step or the test command's own `tsc`.
76
- Instrumentation necessarily rewrites control-flow expressions in ways a type
77
- checker cannot narrow through, so a project that compiles inside its test
78
- command builds under measurement exactly as it does without it.
71
+ For assertion maps, Node, Vitest, Jest and Playwright's Node-side assertions
72
+ supply supported passing-occurrence evidence. Custom assertion wrappers and
73
+ browser-side checks can have additional observation limits. See
74
+ [Assertion evidence](assertion-evidence.md) before interpreting a missing occurrence.
79
75
 
80
76
  ### Browsers, servers, and child processes
81
77
 
@@ -114,22 +110,16 @@ whatever was still buffered.
114
110
  | cargo-nextest | Exact test, attempt, retry, and binary identity | cargo-nextest 0.9.138 or 0.9.140 |
115
111
 
116
112
  Supercov preserves Cargo's test selection, scheduling, fail-fast behavior,
117
- environment, and exit status. Doctests are measured like any other test:
118
- Supercov stands in for rustdoc during `cargo test --doc`, runs each doctest
119
- in its own process, and attributes what it executed to that doctest by name.
120
- With nextest, Supercov is nextest's target runner, so nextest's own
121
- scheduling, retries and output stay as they are and each attempt is recorded
122
- separately. The measured source is what rustc compiles: every crate root and
123
- the modules it reaches through `mod` declarations, `#[path]` attributes and
124
- literal `include!` calls; a `.rs` file nothing declares as a module, such as
125
- one embedded with `include_str!`, is left untouched. Statements, functions,
126
- `if`/`while`/match-guard decisions with their conditions (let chains
127
- included), match arms, `&&` and `||`, `for` and `while` loops and the `?`
128
- operator each take a probe; what a thread recorded before it passed an
129
- `assert!`, `assert_eq!` or `assert_ne!` is linked to that assertion, so the
130
- report can separate code a test checked from code it merely ran; const contexts and macro expansions stay in the
131
- denominator behind an explicit limitation. Use the repository's normal flags after the
132
- wrapped command:
113
+ environment and exit status. Doctests run with their own identities; nextest
114
+ retries remain separate attempts.
115
+
116
+ Measured source follows the modules rustc compiles, including `#[path]` and
117
+ literal `include!` calls. Undeclared `.rs` files are not treated as application
118
+ modules. Statements, functions, branches, boolean decisions, loops and error
119
+ propagation are measured. Const contexts and macro expansions remain visible
120
+ with explicit measurement limitations.
121
+
122
+ Use the repository's normal flags after the wrapped command:
133
123
 
134
124
  ```sh
135
125
  npx supercov -- cargo test --workspace
@@ -149,35 +139,22 @@ attribution.
149
139
  | pytest-rerunfailures | Exact per attempt; flaky tests are reported as such | |
150
140
  | `python -m unittest` | Exact test and setUp/test/tearDown phase identity | Serial in-process; skips and expected failures are recorded; subtest failures roll up to the parent test |
151
141
 
152
- Evidence a test records before its first assertion is linked to that assertion
153
- when the test passes, so a line reads "linked to a passing assertion" rather
154
- than "execution only". Under pytest this covers plain `assert` statements,
155
- through pytest's assertion-pass hook, which Supercov turns on for a rewrite
156
- cache of its own so plain runs keep theirs, and `pytest.raises` and
157
- `pytest.warns` blocks; every `unittest` `assert*` method counts under both
158
- runners. What a test runs after its first assertion is execution only.
159
-
160
- Supercov measures Python through CPython's own monitoring interface. Nothing is
161
- copied, rewritten, or compiled differently: the project runs in place with its
162
- own interpreter and virtual environment, and Supercov only adds a start-up hook
163
- through `PYTHONPATH`, a pytest plugin through `PYTEST_PLUGINS`, one pytest
164
- option (`enable_assertion_pass_hook`) through `PYTEST_ADDOPTS`, and a few
165
- `SUPERCOV_*` variables. Child interpreters started with `subprocess` or
166
- `multiprocessing` inherit the exact test identity; threads and thread pools
167
- carry it through `contextvars`.
168
-
169
- Each interpreter writes commit-framed evidence to a process-owned mmap. A hard
170
- kill preserves completed observations and an incomplete tail is ignored; an
171
- exhausted transport or corrupt committed frame fails the run closed.
172
-
173
- Measured obligations are statements (including several on one line), function
174
- entry, boolean decisions with MC/DC vectors, `for` and comprehension iteration,
175
- `and`/`or` short-circuiting, `match` case selection, and `try` completion,
176
- handler selection and exception propagation, all derived from CPython's own
177
- instruction positions rather than from exception hooks.
178
-
179
- Interpreters launched with `-I`, `-E`, or `-S` ignore `PYTHONPATH` and are not
180
- measured. Code compiled from strings at runtime has no source obligations.
142
+ Your project runs in place with its own interpreter and virtual environment.
143
+ Supercov adds its monitoring and runner hooks through the process environment;
144
+ you do not need to rewrite tests or configure a different build.
145
+
146
+ Coverage includes statements, functions, boolean decisions, loops,
147
+ comprehensions, short-circuit operators, `match` cases and exception paths.
148
+ Child interpreters, threads and thread pools can retain the calling test's
149
+ identity. The report also distinguishes execution before a passing assertion
150
+ from later execution. These phase records alone do not prove which values the
151
+ assertion checks.
152
+
153
+ Interpreters launched with `-I`, `-E` or `-S` ignore the required startup hook
154
+ and are not measured. Code compiled from strings at runtime has no source
155
+ obligations. Completed observations can survive a hard kill, but a corrupt or
156
+ exhausted evidence channel fails the run rather than reporting partial data as
157
+ complete.
181
158
 
182
159
  ```sh
183
160
  npx supercov -- pytest
@@ -197,95 +174,35 @@ npx supercov -- python -m unittest
197
174
  | Thread-parallel Minitest (`parallelize_me!`, `parallelize(with: :threads)`) | Probe observations exact per test; line, method and simple-branch observations made while phases overlapped go to the run, declared | |
198
175
  | Cucumber | Exact scenario identity (`features/x.feature:LINE`), hook steps as setup/teardown | `cucumber`, `bundle exec cucumber` |
199
176
 
200
- Evidence a test records before its first assertion is linked to that assertion
201
- when the test passes: Minitest's `assert`/`refute` family, RSpec's
202
- `expect(...).to` and `not_to` (in Cucumber steps too) and test-unit's
203
- assertions all count. What a test runs after its first assertion is execution
204
- only.
205
-
206
- Supercov measures Ruby with Ruby's own `Coverage` module plus probe calls it
207
- splices into application files in memory as they load. Nothing on disk is
208
- rewritten or copied; the project runs with its own interpreter and bundle, and
209
- Supercov only adds a `-r` entry to `RUBYOPT` and a few `SUPERCOV_*` variables.
210
- No insertion adds a line, so backtraces keep their line numbers.
211
-
212
- Measured obligations are statements, method definitions, `if`/`unless`/
213
- ternary/`while`/`until` decisions with MC/DC vectors over `&&`/`||` operands,
214
- `while`/`until`/`for` iteration and the idiomatic iterator blocks (`each`,
215
- `map`, `times`, `select`, ...), `&&`/`||`/`||=`/`&&=` short-circuiting,
216
- `case`/`when`, `case`/`in` and `&.` selection, and `begin`/`rescue` completion,
217
- handler selection and propagation. `||=` and `&&=` on method-call, index and
218
- constant targets are exact too: an arrival probe and a right-side probe count
219
- the skipped side without re-reading the target. Blocks and lambdas are
220
- statements inside their methods, not function entry points, and that includes
221
- a `define_method` block: Ruby's own method coverage reports one entry per
222
- method it defines, but the block's body is measured statement by statement
223
- instead of as a definition.
224
-
225
- A statement on a line Ruby's own line table never counts (`x = case`, a
226
- multi-line literal assignment, a bare `begin`, `if false`) gets a probe at load
227
- time instead.
228
-
229
- On Ruby 3.4 and newer, Supercov asks the `Coverage` module for line events
230
- alone. Each test phase is sampled from `Coverage`, and asking for its branch
231
- and method tables too made every sample rebuild both for every loaded file,
232
- gems included, which was most of what a Ruby test suite paid under Supercov.
233
- Instead, the statement that starts a branch body or a method body proves the
234
- branch, the method and the decision outcome it witnesses, and what has no such
235
- statement is probed: an `if` without `else`, a modifier `if`, a ternary, `&.`,
236
- a `case` without `else`, an empty body. Ruby 3.3 cannot apply probes and keeps
237
- reading `Coverage`'s branch and method keys.
238
- `if true`/`if false`/`if nil` and other literal predicates are folded the way
239
- Ruby folds them: no branch, and the dead arm is not an obligation. Code inside
240
- a `Ractor.new` block gets no probes: a non-main Ractor cannot read the probe
241
- receiver, so a probe there would raise where the untouched program ran. Its
242
- lines are still counted; what only a probe could have proven inside it is
243
- declared unmeasured at the block. A Spring
244
- preloader started before the run has no hook and fails closed; JRuby and
245
- TruffleRuby are not supported.
246
-
247
- The runtime loads through `RUBYOPT` before Bundler and requires only
248
- `coverage`, so it never activates a gem an application's Gemfile pins
249
- differently. Insertions are checked against Ruby itself by a sweep
250
- (`scripts/ruby-corpus-sweep.sh`, which drives `scripts/ruby-position-sweep.rb`)
251
- over Ruby's whole standard library and the Rails, Rack, RSpec, Minitest,
252
- test-unit and Cucumber gems, about 4,300 files installed once into a stable
253
- corpus directory: every file is transformed and compiled with its line count
254
- intact, every branch key Ruby 3.3 reads is compared with what Ruby reports for
255
- the untouched source, and each file is loaded twice, untouched and
256
- transformed, so the probes are proven to preserve behaviour and define the
257
- same methods.
258
-
259
- A `begin` whose body ends in an expression that can `return` from inside
260
- itself has its handlers and propagation measured as usual, but its normal
261
- completion is declared instead of probed unless every branch of that
262
- expression can carry the probe: Ruby cannot pass such an expression as an
263
- argument, which is what a probe wrapper does.
264
-
265
- Measuring never breaks the program being measured. If a file cannot be
266
- compiled with its probes, it loads unmodified: Ruby's `Coverage` still
267
- measures its lines, methods and own branches, and only the obligations a
268
- probe would have proven are declared for that file. Setting
269
- `SUPERCOV_RUBY_SKIP_PROBES` to a comma-separated list of path fragments puts
270
- chosen files on that same path deliberately, which is the escape hatch if
271
- instrumentation ever disagrees with one of yours.
272
-
273
- Ruby 3.3 does not apply its `Coverage` module to code compiled by a load hook,
274
- so on 3.3 Supercov measures through `Coverage` alone: lines, methods and the
275
- branches Ruby reports itself. Everything that needs a probe (multi-condition
276
- decisions, `||=`, loops, `rescue` flow, a second statement on a line) is
277
- declared unmeasured on that interpreter rather than shown as a gap. Ruby 3.4
278
- and newer measure everything.
279
-
280
- Ruby reads its own coverage as the interpreter exits, and that shapes what a
281
- stopped process keeps. A process ended by a signal it can catch—`SIGTERM`
282
- from a test's teardown, `SIGINT`—unwinds through that exit and reports
283
- everything it measured. A process killed with `SIGKILL`, or one that leaves
284
- through `exit!`, never gets there and takes with it whatever it observed since
285
- its last test boundary. Supercov cannot recover that or say which lines it
286
- would have been, so the run declares that a process did not report, which
287
- blocks completeness, rather than counting those lines against the code. Stop a
288
- Ruby server with `SIGTERM`, or wait for it to exit, and it reports.
177
+ Your project runs in place with its own interpreter and bundle. Supercov loads
178
+ through `RUBYOPT`; application files on disk and their backtrace line numbers
179
+ stay unchanged. RSpec, Minitest and test-unit assertions can identify execution
180
+ before a passing assertion. That timing evidence alone does not show which
181
+ values the assertion checks.
182
+
183
+ Ruby 3.4 and newer support statement, method, branch and MC/DC measurement,
184
+ including loops, iterator blocks, short-circuit operators, pattern matching,
185
+ optional calls and exception paths. Ruby 3.3 supplies Ruby's own line, method
186
+ and branch coverage; obligations requiring additional instrumentation are
187
+ reported as measurement limits.
188
+
189
+ Some constructs have narrower coverage. Code in a non-main Ractor keeps line
190
+ coverage but may lack other observations. Certain nested-return expressions
191
+ limit normal-completion measurement. Constant predicates are folded as Ruby
192
+ folds them, so unreachable alternatives do not become obligations.
193
+
194
+ If a file cannot be instrumented safely, Supercov loads it unchanged and reports
195
+ the remaining limits. To apply that fallback to a known incompatible file, set
196
+ `SUPERCOV_RUBY_SKIP_PROBES` to a comma-separated list of path fragments. This
197
+ reduces measurement; it is not a way to claim that skipped obligations are covered.
198
+
199
+ A Spring preloader started before the run has no coverage hook. Restart it
200
+ within the measured command. JRuby and TruffleRuby are not supported.
201
+
202
+ Stop test-owned Ruby servers with `SIGTERM` or wait for normal exit so they can
203
+ report their evidence. `SIGKILL` and `exit!` can lose observations since the last
204
+ test boundary. A process that does not report leaves a measurement limit; its
205
+ missing evidence is not counted as uncovered application code.
289
206
 
290
207
  ```sh
291
208
  npx supercov -- rspec
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supercov",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
4
4
  "description": "Coverage for coding agents and software factories \ud83c\udf19",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -87,19 +87,21 @@
87
87
  "test:python-monitoring": "cargo build -p supercov && node scripts/python-monitoring-integration.mjs",
88
88
  "test:ruby-coverage": "cargo build -p supercov && node scripts/ruby-coverage-integration.mjs",
89
89
  "test:rust-public-cargo": "cargo build -p supercov && node scripts/rust-public-cargo-integration.mjs",
90
- "test:assertion-maps": "cargo build -p supercov && node scripts/assertion-map-schema.mjs --check && node scripts/assertion-map-integration.mjs && node scripts/assertion-map-js-integration.mjs",
91
- "test:assertion-maps:js": "cargo build -p supercov && node scripts/assertion-map-schema.mjs --check && node scripts/assertion-map-js-integration.mjs",
92
- "sync:assertion-schema": "cargo build -p supercov && node scripts/assertion-map-schema.mjs"
90
+ "test:assertion-maps": "cargo build -p supercov && node scripts/assertion-map-schema.mjs --check && node scripts/assertion-map-integration.mjs && node scripts/assertion-map-js-integration.mjs && node scripts/assertion-dogfood-integration.mjs",
91
+ "test:assertion-maps:js": "cargo build -p supercov && node scripts/assertion-map-schema.mjs --check && node scripts/assertion-map-js-integration.mjs && node scripts/assertion-dogfood-integration.mjs",
92
+ "sync:assertion-schema": "cargo build -p supercov && node scripts/assertion-map-schema.mjs",
93
+ "docs:sync": "node scripts/sync-docs.mjs",
94
+ "docs:check": "node scripts/sync-docs.mjs --check"
93
95
  },
94
96
  "optionalDependencies": {
95
- "@supercov/cli-darwin-arm64": "0.0.45",
96
- "@supercov/cli-darwin-x64": "0.0.45",
97
- "@supercov/cli-linux-arm64-gnu": "0.0.45",
98
- "@supercov/cli-linux-arm64-musl": "0.0.45",
99
- "@supercov/cli-linux-x64-gnu": "0.0.45",
100
- "@supercov/cli-linux-x64-musl": "0.0.45",
101
- "@supercov/cli-win32-arm64": "0.0.45",
102
- "@supercov/cli-win32-x64": "0.0.45"
97
+ "@supercov/cli-darwin-arm64": "0.0.47",
98
+ "@supercov/cli-darwin-x64": "0.0.47",
99
+ "@supercov/cli-linux-arm64-gnu": "0.0.47",
100
+ "@supercov/cli-linux-arm64-musl": "0.0.47",
101
+ "@supercov/cli-linux-x64-gnu": "0.0.47",
102
+ "@supercov/cli-linux-x64-musl": "0.0.47",
103
+ "@supercov/cli-win32-arm64": "0.0.47",
104
+ "@supercov/cli-win32-x64": "0.0.47"
103
105
  },
104
106
  "peerDependencies": {
105
107
  "@playwright/test": ">=1.55.0",
@@ -578,6 +578,11 @@ function injectChildEnvironment(method, args) {
578
578
  const environment = existingEnvironment
579
579
  ? { ...existingEnvironment, ...inherited }
580
580
  : { ...process.env, ...inherited };
581
+ // The runtime child wrapper has captured the current async test scope in
582
+ // these options. An inherited setup/unscoped carrier from the launcher
583
+ // must not overwrite it when we restore the other coverage variables.
584
+ if (existingEnvironment?.SUPERCOV_CONTEXT !== undefined)
585
+ environment.SUPERCOV_CONTEXT = existingEnvironment.SUPERCOV_CONTEXT;
581
586
  // The register path must come from THIS module's own location, never from
582
587
  // the environment or the working directory. Monorepo runners both defeat
583
588
  // the old derivation at once: turbo's strict env strips SUPERCOV_PROJECT_ROOT