Skip to content

refactor(test): scope coverage with include instead of exclude - #211

Merged
7nohe merged 1 commit into
mainfrom
claude/recursing-liskov-184870
Aug 11, 2026
Merged

refactor(test): scope coverage with include instead of exclude#211
7nohe merged 1 commit into
mainfrom
claude/recursing-liskov-184870

Conversation

@7nohe

@7nohe 7nohe commented Aug 11, 2026

Copy link
Copy Markdown
Owner

What

Scope v8 coverage with coverage.include: ["src/**"] instead of enumerating coverage.exclude entries. Removes the derived project root, the node:url import and five exclude entries.

Why

coverage.exclude patterns are matched against absolute paths with picomatch's contains option. A .claude ignore therefore also matches the project's own root whenever the repo is checked out under a .claude/ path — which is exactly where Claude Code agent worktrees live (.claude/worktrees/). Every source file got excluded, and the 95%/90% thresholds passed against 0/0 while still exiting 0.

#206 fixed this by building an absolute pattern from import.meta.url. That works, but it works around the sharp edge rather than removing it. coverage.include is anchored to the project root, so scoping by include has no such failure mode.

I measured all four relative spellings inside a .claude/worktrees/ checkout — every one of them fails open:

pattern result
.claude/** (pre-#206) Statements : Unknown% ( 0/0 ), exit 0
./.claude/** 0/0, exit 0
/.claude/** 0/0, exit 0
**/.claude/** 0/0, exit 0
absolute (#206) correct
include: ["src/**"] correct

What is deliberately left alone

  • test.exclude keeps .claude/**. It is globbed by tinyglobby relative to the project root and is load-bearing at the repo root: dropping it collects the nested agent worktrees too, 75 test files instead of 15. I initially removed it based on a bad experiment — a CLI --exclude appends to the config value rather than replacing it, so both arms of the A/B still had the config entry active. The comment now records this so nobody repeats it.
  • biome.json is unchanged. "!.claude" is anchored to the config's directory and is load-bearing (removing it takes the check from 107 to 109 files and picks up .claude/). Worth noting the biome half never failed open: with the old "!**/.claude" it reported Checked 0 files / No files were processed in the specified paths and exited 1. Only vitest was silently green.

Reviewer note — this is a semantic change, not only a cleanup

Setting include means coverage now counts every file under src/, not just the ones a test imported. Numbers are byte-identical today because all src files are currently reached by tests, but from here on a new untested src/*.mts will pull the percentages down and can fail the 95% gate on an unrelated PR. That is stricter and seemed right for a repo that gates on coverage — happy to revert to the absolute-pattern form from #206 if you would rather not have that.

Verification

Config file physically placed in each location and run, not simulated with CLI overrides.

repo root worktree under .claude/worktrees/
tests 187/187, exit 0 187/187, exit 0
coverage 529/537 (98.51%) 529/537 (98.51%)
test files collected 15 15
.claude paths in report 0 0
npm run lint 108 files 107 files

Negative controls: .claude/src/probe.mts and .claude/src/probe.test.ts (paths that contain src/, so they would leak if include were contains-matched) stay out of both collection and the coverage report. No .d.ts rows entered the report.

Unrelated, not addressed here: plain npm test is flaky on a loaded machine (default testTimeout 5s / hookTimeout 10s vs. generate.test.ts running @hey-api/openapi-ts in beforeAll); the failing test names differ per run. --fileParallelism=false is reliably green.

🤖 Generated with Claude Code

`coverage.exclude` patterns are matched against absolute paths with
picomatch's `contains` option, so every relative spelling of a `.claude`
ignore also matches the project's own root when the repo is checked out
under a `.claude/` path, as agent git worktrees in `.claude/worktrees/`
are. That excluded every source file and let the thresholds pass against
0/0. All four relative forms were measured to fail open:

  .claude/**  ./.claude/**  /.claude/**  **/.claude/**

#206 fixed this by building an absolute pattern from `import.meta.url`.
`coverage.include` is anchored to the project root instead, so scoping by
include removes the failure mode structurally rather than working around
it, and drops the `node:url` import, the derived root and five exclude
entries along with it.

`test.exclude` keeps its `.claude/**` entry: it is globbed relative to the
project root and is load-bearing at the repo root, where dropping it
collects the nested worktrees too (75 test files instead of 15).

Coverage output is unchanged in both locations: 187/187 tests, 529/537
statements, no `.claude` paths in the report.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openapi-react-query-codegen Building Building Preview Aug 11, 2026 11:58am

@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 99.22% (🎯 95%) 514 / 518
🟢 Statements 98.51% (🎯 95%) 529 / 537
🟢 Functions 99.25% (🎯 95%) 133 / 134
🟢 Branches 91.73% (🎯 90%) 222 / 242
File CoverageNo changed files found.
Generated in workflow #451 for commit cfa2fa8 by the Vitest Coverage Report Action

@7nohe
7nohe merged commit f3e374b into main Aug 11, 2026
5 of 8 checks passed
@7nohe
7nohe deleted the claude/recursing-liskov-184870 branch August 11, 2026 12:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant