diff --git a/scripts/test-free-shards.ts b/scripts/test-free-shards.ts index 41106336a..3e921bc7b 100755 --- a/scripts/test-free-shards.ts +++ b/scripts/test-free-shards.ts @@ -156,7 +156,7 @@ const WINDOWS_FRAGILE_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [ // pattern. Listed here with the precise reason. Prefer adding a pattern above // when possible; this list is for environment-/runtime-specific tests where // the failure mode is structural rather than detectable via source-file scan. -const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }> = [ +export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }> = [ { file: 'test/host-config.test.ts', reason: 'asserts "claude" binary on PATH (only true when running inside Claude Code, not on bare CI runner)', @@ -165,6 +165,38 @@ const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }> = [ file: 'browse/test/findport.test.ts', reason: 'asserts Bun.serve.stop() is fire-and-forget — Bun behavior differs on Windows for this polyfill', }, + // First full run of the expanded lane (v1.66, 13 → ~258 files) surfaced + // seven POSIX-bound files the content patterns cannot see (their + // POSIX-ness is what they TEST, or arrives via a variable). Receipts: + // PR #2593 windows-free-tests run 31918591602. + { + file: 'test/regression-pr1169-build-app-sed.test.ts', + reason: 'tests sed escape sequences in build-app.sh — sed/bash are the subject under test', + }, + { + file: 'test/setup-conductor-worktree.test.ts', + reason: 'tests ln -snf symlink semantics in the setup script — POSIX ln is the subject under test', + }, + { + file: 'test/artifacts-init-migration.test.ts', + reason: 'runs a bash migration script + jq against a scaffolded git state — POSIX toolchain paths break under cmd spawn', + }, + { + file: 'test/gstack-decision-semantic.test.ts', + reason: 'installs a fake gbrain SHEBANG SHIM on PATH; Windows spawn cannot exec shebang scripts', + }, + { + file: 'test/question-log-hook.test.ts', + reason: 'spawns the PostToolUse hook script (bash shebang) directly; Windows spawn cannot exec it', + }, + { + file: 'browse/test/browser-skills-e2e.test.ts', + reason: 'asserts forward-slash tier paths (/browser-skills/) that resolve with backslashes on Windows', + }, + { + file: 'design/test/variants-retry-after.test.ts', + reason: 'wall-clock retry-timing assertions — flaky on the slow windows-latest runner even with widened bounds', + }, ]; // Force-include overrides: files a WINDOWS_FRAGILE_PATTERNS regex excludes for diff --git a/test/design-flag-utils.test.ts b/test/design-flag-utils.test.ts index 9e3734081..c891bf039 100644 --- a/test/design-flag-utils.test.ts +++ b/test/design-flag-utils.test.ts @@ -90,8 +90,12 @@ describe("parseIntFlag contract (#2032, codex 17a-c)", () => { describe("normalizeIntFlag CLI wrapper (exit-1 semantics)", () => { function runWrapper(rawExpr: string, specExpr: string): { status: number; stderr: string } { + // Forward slashes: a raw Windows ROOT embeds backslashes into the eval + // string where they act as ESCAPES ("D:\\a\\gstack" imports as + // "D:agstack" — first Windows lane run). Import specifiers accept + // forward slashes on every platform. const script = ` - import { normalizeIntFlag } from "${ROOT}/design/src/flag-utils"; + import { normalizeIntFlag } from "${ROOT.replaceAll('\\', '/')}/design/src/flag-utils"; const v = normalizeIntFlag(${rawExpr}, ${specExpr}); console.log("VALUE:" + v); `; diff --git a/test/test-free-shards.test.ts b/test/test-free-shards.test.ts index f42475378..a3a2a669d 100644 --- a/test/test-free-shards.test.ts +++ b/test/test-free-shards.test.ts @@ -18,6 +18,7 @@ import { DEFAULT_WALL_TIMEOUT_MS, PER_FILE_WALL_MS, wallTimeoutForShard, + KNOWN_WINDOWS_INCOMPATIBLE, TEST_ROOTS, TREE_MUTATING, WORKER_HOSTILE, @@ -535,6 +536,12 @@ describe('test-free-shards: curated-list census pins', () => { expect(stale).toEqual([]); }); + test('every KNOWN_WINDOWS_INCOMPATIBLE entry names a real free test file', () => { + const census = new Set(collectFreeTestFiles(ROOT)); + const stale = KNOWN_WINDOWS_INCOMPATIBLE.map((e) => e.file).filter((f) => !census.has(f)); + expect(stale).toEqual([]); + }); + test('every TEST_ROOTS entry exists on disk and contributes at least one test file', () => { const files = collectFreeTestFiles(ROOT); for (const root of TEST_ROOTS) {