You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
verify:typecheck-coverage cannot run on Windows — execFileSync('npx', …) is ENOENT, so validate/ci fail at step one #1939
On Windows, npm run validate (and therefore the mandatory npm run ci pre-push gate) fails at its first step. Every project harvest prints:
verify:typecheck-coverage — `tsc -p tsconfig.json` (in clients/cli) exited non-zero:
(no diagnostic captured)
…for every client/project, followed by 905 tracked source file(s) get no tsc pass — every TS file in the repo listed as uncovered.
Root cause
rawProjectFiles() (and projectDisablesChecking()) call execFileSync("npx", ["--no-install", "tsc", …]). On Windows there is no npx executable — only npx.cmd — and Node cannot spawn a .cmd without a shell (and refuses shell-less .cmd spawns since the CVE-2024-27980 hardening). The spawn throws ENOENT, the catch treats it as a tsc config error with empty stdout, every project's file set comes back empty, and the guard reports the whole repo as unclaimed.
GitHub CI never sees this because its runners are Linux, so the gate is green there while being unrunnable for any Windows contributor.
Suggested fix
Resolve the real tsc entry instead of shelling through npx: spawn process.execPath with the client-local node_modules/typescript/lib/tsc.js (falling back up the tree), which is cross-platform, faster (no npx resolution), and keeps execFileSync shell-free. Alternatively spawn npx.cmd with shell: true on win32 and quote args — the approach scripts/pack-and-verify.mjs needs for the same reason — but the direct-node form avoids the shell-quoting hazards entirely. scripts/verify-typecheck-coverage.test.mjs should pin whichever contract is chosen with a Windows-shaped regression test (a spawner that throws ENOENT must surface as a hard "cannot measure" error, not as an empty file set silently reported as 905 uncovered files).
Context
Found while working #1852 on a Windows machine — the gate could not be run to validate that fix. Note the failure mode is doubly silent: the ENOENT is reported as "(no diagnostic captured)" and then converted into a misleading coverage report instead of an environment error.
Same defect in scripts/verify-build-gate.mjs: on Windows npm run verify:build-gate fails immediately with
verify:build-gate FAILED — could not run `vite build` (spawnSync npx ENOENT)
Whatever spawn helper fixes this should be shared by both verifier scripts (and any other script spawning npx/npm via execFileSync/spawnSync without shell on win32 — scripts/pack-and-verify.mjs had the same class of problem).
Investigated on Windows against current v2/main (57d6e0d). Confirmed, with two additions to the report:
Root cause:npx/npm are .cmd shims on Windows, and Node refuses shell-less .cmd spawns (the CVE-2024-27980 hardening) → ENOENT. CI runners are ubuntu-only, so CI never sees it. The failure is doubly silent in verify:typecheck-coverage: on a spawn failure err.stdout is undefined, the catch turns that into an empty file set, and the guard reports "918 tracked source file(s) get no tsc pass" instead of "cannot run tsc".
Scope is wider than this script — seven call sites spawn npx/npm without a Windows-safe shell: both tsc spawns in verify-typecheck-coverage.mjs, the vite spawn in verify-build-gate.mjs, the npx tsc bootstrap in smoke-cli.mjs / smoke-tui.mjs / smoke-web-app.mjs, and the npm pack in pack-and-verify.mjs (whose other spawns already pass shell: process.platform === "win32"). verify-format-coverage and verify-dep-lockstep are unaffected (they only spawn git).
Planned fix: a shared scripts/lib/resolve-node-bin.mjs that resolves a package's bin entry via its package.json (Vite 8's exports map blocks deep require.resolve) and spawns it with process.execPath — shell-free, cross-platform, and a resolution failure becomes a hard "cannot resolve — run npm install" error instead of a bogus coverage report. Prototyped: verify:typecheck-coverage goes green on Windows (all 918 files, ~28s) and verify:build-gate passes.
Fixed by #1997, merged to v2/main as 7bda382. (Closing manually — Closes #N doesn't auto-fire for PRs targeting the non-default v2/main.)
Beyond the original ENOENT fix, three rounds of review hardened the resolver against failures in the same family — a globally-installed TypeScript silently standing in for a missing repo install, a partial install whose declared bin file is gone, and a package whose exports map hides its own package.json. Each of those would have been green and wrong rather than loudly broken, which is the failure mode this issue was reported for.
Activity
olaservo commentedon Aug 6, 2026
Same defect in
scripts/verify-build-gate.mjs: on Windowsnpm run verify:build-gatefails immediately withWhatever spawn helper fixes this should be shared by both verifier scripts (and any other script spawning
npx/npmviaexecFileSync/spawnSyncwithoutshellon win32 —scripts/pack-and-verify.mjshad the same class of problem).olaservo commentedon Aug 12, 2026
Investigated on Windows against current
v2/main(57d6e0d). Confirmed, with two additions to the report:Root cause:
npx/npmare.cmdshims on Windows, and Node refuses shell-less.cmdspawns (the CVE-2024-27980 hardening) →ENOENT. CI runners are ubuntu-only, so CI never sees it. The failure is doubly silent inverify:typecheck-coverage: on a spawn failureerr.stdoutisundefined, the catch turns that into an empty file set, and the guard reports "918 tracked source file(s) get no tsc pass" instead of "cannot run tsc".Scope is wider than this script — seven call sites spawn
npx/npmwithout a Windows-safe shell: both tsc spawns inverify-typecheck-coverage.mjs, the vite spawn inverify-build-gate.mjs, thenpx tscbootstrap insmoke-cli.mjs/smoke-tui.mjs/smoke-web-app.mjs, and thenpm packinpack-and-verify.mjs(whose other spawns already passshell: process.platform === "win32").verify-format-coverageandverify-dep-lockstepare unaffected (they only spawngit).Planned fix: a shared
scripts/lib/resolve-node-bin.mjsthat resolves a package'sbinentry via itspackage.json(Vite 8'sexportsmap blocks deeprequire.resolve) and spawns it withprocess.execPath— shell-free, cross-platform, and a resolution failure becomes a hard "cannot resolve — run npm install" error instead of a bogus coverage report. Prototyped:verify:typecheck-coveragegoes green on Windows (all 918 files, ~28s) andverify:build-gatepasses.Merge origin/v2/main: move the #1939 npx fix into the shared tsc-prog…
cliffhall commentedon Aug 17, 2026
Fixed by #1997, merged to
v2/mainas 7bda382. (Closing manually —Closes #Ndoesn't auto-fire for PRs targeting the non-defaultv2/main.)Beyond the original ENOENT fix, three rounds of review hardened the resolver against failures in the same family — a globally-installed TypeScript silently standing in for a missing repo install, a partial install whose declared bin file is gone, and a package whose
exportsmap hides its ownpackage.json. Each of those would have been green and wrong rather than loudly broken, which is the failure mode this issue was reported for.fix: resolve node bins via package.json instead of spawning npx .cmd …
test(smoke): run the headless web smokes in Firefox and WebKit, not j…
chore(scripts): build test-servers unconditionally from one shared he…
chore: address Copilot review round 7 (Windows .cmd shims)