.jscpd.json set pattern to an array, but jscpd's --pattern takes a single
glob string. An array matches nothing, so the gate had reported success
without reading a file since 2793a66 introduced it — its 0.074ms
"detection time" and absent stats table were the tell. Bisecting the
config confirmed that key alone reduces the run to zero files.
The ignore entries were also bare names rather than globs, so once the
pattern worked it swept apps/web/dist and .pnpm-store — 948 files.
Excludes __tests__, matching what .jsinspectrc already does. Duplication
between test cases is usually deliberate: parallel arrange/act/assert
blocks read better than shared setup, and the factories under
apps/web/src/__tests__/factories cover the deduplication worth having.
Real duplication is 1.72% across 209 source files, so the threshold drops
from 5% to 3%. jscpd measures a ratio rather than blocking each new clone,
and a 5% budget left roughly 3x headroom before it would ever fire.
Gives jscpd explicit paths behind `pnpm jscpd`, so lefthook and the check
script share one invocation, as jsinspect already does.
Also guards the pattern key in check-gates.mjs, since an array there fails
silently rather than erroring.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vitest.config.ts set passWithNoTests: true, added in the 7dd4abb
scaffolding commit when the repo genuinely had no tests yet. Once tests
existed it became the same hazard as the oxlint bugs fixed in 91f46ff:
if the include globs ever stopped matching, the suite would exit 0
having run nothing.
Vitest already defaults to failing in that case, so removing the line is
the whole fix — a broken glob now reports "No test files found, exiting
with code 1". Verified by pointing the globs at a nonexistent directory.
Extends the gate check to flag the option coming back, and renames it
from check-lint-gate to check-gates now that it covers more than oxlint.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>