Skip to content

test(coverage): gate --require-source-files on unmappable source files - #1227

Merged
mrbobbytables merged 1 commit into
mainfrom
quality/require-source-files-unmapped
Oct 9, 2026
Merged

mrbobbytables merged 1 commit into
mainfrom
quality/require-source-files-unmapped

Conversation

@hivecommons-hive

Copy link
Copy Markdown
Contributor

Test Improvement

--require-source-files is the repository's file-set floor, and it caught only one of
the two ways a source file can leave every coverage ratio.

A file nothing imports is recorded nowhere, so missingSourceFiles names it and the gate
fails. A file that was executed but whose V8 record could not be attributed back to
the text on disk is dropped by collect() into unmapped, and report() is built from
the merged map alone — so it leaves the src files numerator and denominator together.
main() folded unmapped into the measured set, so the gate passed over it as well. The
only trace left was the informational "Not reported" notice, which nothing scores.

That fold is deliberate and right for the question missingSourceFiles asks — the
rationale is argued at tests/coverage-report-source-files.test.mjs:139-151, and this
change leaves it and its test untouched. The fix is a second, separate condition under
the same flag.

Measured

At 593b9fe, node v26.10.0, TZ=UTC, locally. tryRemapJsx was made to decline one
component, simulating the drift this guards against, and the full
npm run test:unit:coverage:check was run:

before after
exit code 0 1
src files 8999/8999 lines, 2560/2561 regions (was 9157/9157, 2595/2596) names the file and stops

158 lines and 35 regions of src/components/CaseStudies/index.js had left the
denominator with every gate still green.

The drift is reachable rather than hypothetical: tryRemapJsx compares the rebuilt
loader output's length against the recorded text before trusting the offsets, so an
swc bump or a change to tests/tools/jsx-hooks.mjs is enough to unmap any of the 19
JSX-bearing modules under src/.

Unmodified, the gate still exits 0 at src files 100.00 | 99.96 | 9157/9157 lines | 2595/2596 regions, and the nine data/*.json modules are still merely listed — only the
trees --require-source-files walks are gated.

What this changes

  • unmappedSourceFiles() in tests/tools/coverage-report.mjs
  • main() fails under --require-source-files when that list is non-empty, and reports
    both file-set diagnoses together instead of the first short-circuiting the second
  • tests/coverage-report-unmapped-sources.test.mjs — what is claimed, that the two
    diagnoses stay disjoint, and an end-to-end run that fails on a module whose text
    drifted after it was recorded

Verified non-vacuous: with the new condition disabled, the end-to-end test fails on
/could not be mapped onto the text on disk/.

No new flag and no package.json change.

Overlap with open PRs

tests/tools/coverage-report.mjs is also edited by open PR #1207, which adds the
--check-harness* threshold family (parseArgs, report(), and main() at the
report(merged) destructure and the threshold block below it). This change sits in the
--require-source-files failure block between those hunks and touches no line either of
them does; the new test file is its own. They are disjoint at hunk level and neither
needs to land first.

Related Issue

Closes #1226


Filed by quality agent (hold-gated mode). Human review required.

— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88

`--require-source-files` walks scripts/, src/ and tests/tools/ and fails
when a file there was never measured. It caught only one of the two ways a
source file can leave every ratio.

A file nothing imports is recorded nowhere and is named. A file that was
executed but whose V8 record could not be attributed back to the text on
disk is dropped by collect() into `unmapped`, and report() is built from the
merged map alone, so it leaves the `src files` numerator and denominator
together. The gate passed over it too, because main() folded `unmapped` into
the measured set. That fold is right for the question missingSourceFiles
asks -- counting an unmappable component as missing would make every one of
them a permanent failure -- but nothing else asked the other question, and
the only trace left was an informational notice nothing scores.

Making tryRemapJsx decline one component left the full gate exiting 0 with
src/components/CaseStudies/index.js (158 lines, 35 regions) gone from the
denominator. The drift is reachable: tryRemapJsx compares the rebuilt
loader output's length against the recorded text, so an swc bump or a
change to tests/tools/jsx-hooks.mjs is enough.

Adds unmappedSourceFiles(), claiming only paths inside SOURCE_ROOTS so the
nine data/*.json modules go on being listed without failing anything, and a
second condition under the same flag. The two diagnoses are now reported
together rather than the first short-circuiting the second.

Signed-off-by: quality <quality@hive.kubestellar.io>
@hivecommons-hive hivecommons-hive Bot added the hold label Oct 9, 2026
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Important

Held for human review by the hive's ACMM level gate.

This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the outreach agent is always held because it publishes project-facing communication.

Hive will keep the hold label until a human removes it. Operators can make a deliberate one-off release during an ACMM level change with release_level_holds=true, but level changes never release this hold automatically.

@mrbobbytables
mrbobbytables added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 59ba83c Oct 9, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[quality] --require-source-files passes over a source file whose coverage record could not be mapped

1 participant