Summary
hooks coverage-gaps (and coverage-route / coverage-suggest) report files that contain no executable lines — e.g. TypeScript files with only type/interface exports — as 0% line coverage, gap type critical, priority 10.
Reproduce
- In a Vitest (v8 provider) project, include a type-only file in coverage and emit lcov:
npx vitest run --coverage --coverage.reporter=lcov
- The lcov record for that file is
LF:0 / LH:0 / BRF:0 / BRH:0.
npx @claude-flow/cli@latest hooks coverage-gaps --format table
Actual: the type-only file is listed as 0.0% | critical | 10.
Expected: a file with nothing to cover is skipped (or treated as 100%), never a critical gap.
In a real run, 2 of 6 reported gaps were type-only files at the top of the list.
Cause
dist/src/commands/hooks.js, parseLcovInfo → flushRecord:
lines: linesFound > 0 ? (linesHit / linesFound) * 100 : 0,
branches: branchesFound > 0 ? (branchesHit / branchesFound) * 100 : 100,
lines falls back to 0 while branches falls back to 100. The MCP-side parser (dist/src/ruvector/coverage-router.js, finalizeFileCoverage) uses 100 for the same case, so CLI and MCP disagree.
parseCoverageSummaryJson has the same outcome via an operator-precedence slip:
const linePct = m.lines?.pct ?? m.lines?.covered != null ? (...) / Math.max(m.lines?.total ?? 1, 1) * 100 : 0;
parses as (pct ?? covered) != null ? ... : 0, so total: 0 yields 0 / 1 = 0%, and a present pct is ignored.
Suggested fix
Skip entries with linesFound === 0 (or use 100, matching coverage-router.js), and parenthesize linePct.
Verified on @claude-flow/cli 3.56.5 and 3.56.7.
🤖 Generated with claude-flow
Summary
hooks coverage-gaps(andcoverage-route/coverage-suggest) report files that contain no executable lines — e.g. TypeScript files with onlytype/interfaceexports — as 0% line coverage, gap typecritical, priority 10.Reproduce
npx vitest run --coverage --coverage.reporter=lcovLF:0 / LH:0 / BRF:0 / BRH:0.npx @claude-flow/cli@latest hooks coverage-gaps --format tableActual: the type-only file is listed as
0.0% | critical | 10.Expected: a file with nothing to cover is skipped (or treated as 100%), never a critical gap.
In a real run, 2 of 6 reported gaps were type-only files at the top of the list.
Cause
dist/src/commands/hooks.js,parseLcovInfo→flushRecord:linesfalls back to0whilebranchesfalls back to100. The MCP-side parser (dist/src/ruvector/coverage-router.js,finalizeFileCoverage) uses100for the same case, so CLI and MCP disagree.parseCoverageSummaryJsonhas the same outcome via an operator-precedence slip:parses as
(pct ?? covered) != null ? ... : 0, sototal: 0yields0 / 1 = 0%, and a presentpctis ignored.Suggested fix
Skip entries with
linesFound === 0(or use 100, matchingcoverage-router.js), and parenthesizelinePct.Verified on
@claude-flow/cli3.56.5 and 3.56.7.🤖 Generated with claude-flow