Reading a report
What each line of `spirolo check` means, on a real run.
This is the unedited output of spirolo check on
react-day-picker at commit 19033b4:
$ spirolo check
904 files · 223 test files · 938 tests (938 jest) · 1268 expects · 0 handlers · 4 configs · 82ms
Health C (61)
── Weak tests (audit) ────────────────────────────────────────
● absence-anchor (2 files) · advisory: 16/20 (UNADJUDICATED)
examples/Disabled.test.tsx:21 test "should not display the previous button" asserts only that DOM is absent with no anchor (not.toBeInTheDocument)
examples/ModifiersHidden.test.tsx:16 test "the day %s should be hidden" asserts only that DOM is absent with no anchor (not.toBeInTheDocument)
A render test asserts only that things are absent from the DOM, with no positive assertion proving the render happened at all. · spirolo explain absence-anchor
To gate it: .spirolorc.jsonc { "rules": { "absence-anchor": { "severity": "warn" } } } · to accept today's rows: spirolo ratchet
── Merge gate (gate) ────────────────────────────────────────
● fail-closed-setup (1 file, 2 findings) · advisory: 19/19 (UNADJUDICATED)
jest.config.ts (2)
:0 setup does not guard the console (no throwing console.error/warn spy or fail-on-console package)
:0 setup does not restore mocks between tests (no restoreMocks or afterEach restoreAllMocks)
The runner's setup misses an invariant (unhandled requests error, a console guard, mock reset, a global server) that makes a broken test fail. · spirolo explain fail-closed-setup
To gate it: .spirolorc.jsonc { "rules": { "fail-closed-setup": { "severity": "warn" } } } · to accept today's rows: spirolo ratchet
● dead-skip (1 file, 2 findings) · 1 info · 1 listed · advisory: 18/18 (INSUFFICIENT)
packages/ethiopic/src/ethiopic/lib/endOfWeek.test.ts (2)
:1 todo "should return the correct end of the week date for a given date": unconditional · listed
:1 todo "should return the correct end of the week date for a given date" has no linked issue
A skipped or focused test disagrees with the suite that is believed to run, with no linked issue or visible condition watching it. · spirolo explain dead-skip
To gate it: .spirolorc.jsonc { "rules": { "dead-skip": { "severity": "warn" } } } · to accept today's rows: spirolo ratchet
── Unused tests (hygiene) ────────────────────────────────────────
● duplicate-test (2 files) · advisory: 15/18 (UNADJUDICATED)
examples/Input.test.tsx:26 updates the calendar when a date is typed in duplicates 1 other test
packages/react-day-picker/src/helpers/getNavMonth.test.ts:95 when "captionLayout" is "dropdown-months" › "startMonth" should be undefined duplicates 1 other test
Two tests with different names have bodies that normalise to the same code, so one of them adds nothing. · spirolo explain duplicate-test
To gate it: .spirolorc.jsonc { "rules": { "duplicate-test": { "severity": "warn" } } } · to accept today's rows: spirolo ratchet
✓ 8 rules found nothing: handler-drift, generator-walk, data-layer-mock, redundant-test, unused-mock, orphan-test, stale-snapshot, flake-observed
7 findings in 4 of 16 rules · 0 warn · 0 error · 7 info · 1 listed · exit 0
hidden 227 findings: 222 experimental (gate-truth 1, flake-candidate 1, untested-hotspot 220), 5 from DROPPED or FAIL rules (weak-assertion 5); spirolo check --all shows them
note no churn input: untested-hotspot ranks by complexity × weakness alone. Save `git log --numstat --format= --since=180.days` to a file and pass it with `--churn <file>` or name it in `inputs.churn`
The header
The first line counts what the graph holds: files, test files, tests per runner, expect calls,
MSW or nock handlers, runner configs, and how long the pass took.
Health C (61) is a provisional score out of 100. Each family takes off points for the share of
its subjects that have findings, up to a cap per family. Rules outside the default view count half. The score is a
summary to watch over time: nothing gates on it, and machine formats do not carry it.
Families and rules
Findings are grouped under one banner per family: Weak tests (audit), Merge gate
(gate), Unused tests (hygiene) and Flaky (flake). Under each banner, each rule
appears once:
● dead-skip (1 file, 2 findings) · 1 info · 1 listed · advisory: 18/18 (INSUFFICIENT)- the rule and how many files and findings it has;
- the severity counts.
listedrows are context that never counts as a finding; - the trust label:
advisoryis the tier,18/18means 18 of its 18 decided corpus findings were true, andINSUFFICIENTis its verdict (not enough decided to call it proven). How far to trust a finding explains each part.
Then the rows, file by file, with line numbers. A :0 row is about the whole file or config. The
last two lines of each rule state what it claims and how to make it gate.
The closing lines
7 findings in 4 of 16 rules · 0 warn · 0 error · 7 info · 1 listed · exit 0
hidden 227 findings: 222 experimental (...), 5 from DROPPED or FAIL rules (...); spirolo check --all shows themThe summary counts what was shown and gives the exit code. The hidden line counts the findings
of rules the default view leaves out: experimental rules, and rules whose measurement failed or
that were dropped. spirolo check --all shows them, each labelled.
A note line flags missing input. Here, without a churn file, untested-hotspot ranks modules by
complexity and test weakness alone.
Caps
Long sections are capped by files and rows, and end with … and N more files and the command that
lifts the cap: spirolo check --rule <rule> --all.