ci(windows): curate the fix-wave regression tests into the windows-latest run

The windows-free-tests curated set is derived (POSIX-fragility regex scan
+ explicit deny list), and two of this wave's Windows regression files
were auto-excluded on false-positive pattern hits:

- browse/test/file-permissions.test.ts tripped the POSIX-mode-bitmask
  pattern, but every `mode & 0o777` assertion is platform-guarded — and
  the file carries the win32-only icacls-by-SID regression tests, which
  can only ever execute on windows-latest.
- browse/test/terminal-agent-owner-watchdog.test.ts tripped the
  spawn(['bun','run',...]) pattern whose reason is the Playwright-bound
  browse server; it actually spawns terminal-agent.ts (fs/path/crypto +
  local helpers only, no Playwright at module scope), and the owner-PID
  orphan leak it pins was reported on Windows (#2019).

Adds a KNOWN_WINDOWS_SAFE force-include list (mirror of
KNOWN_WINDOWS_INCOMPATIBLE, each entry carrying its false-positive
rationale) consulted before the pattern scan, and makes the
owner-watchdog test's throwaway owner process Windows-portable
(process.execPath instead of `sleep`, which a bare runner may not have).

The wave's other new files need no wiring: process-liveness-windows and
the bun-polyfill windowsHide/exited tests pass curation automatically;
setup-runtime-lib-command self-skips on win32 by design (its Windows
branch is exercised by simulating IS_WINDOWS=1 under bash), so
force-including it would add a permanently-skipped file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan 2026-08-14 19:23:30 -07:00
parent d10aa39408
commit 2e25bfdefb
No known key found for this signature in database
GPG Key ID: C1F69E85C74EFE1D
2 changed files with 38 additions and 1 deletions

View File

@ -41,7 +41,13 @@ describe('terminal-agent owner lifecycle', () => {
const stateFile = path.join(stateDir, 'browse.json');
fs.writeFileSync(stateFile, JSON.stringify({ token: 'test-token' }));
const owner = Bun.spawn(['sleep', '30'], { stdio: ['ignore', 'ignore', 'ignore'] });
// process.execPath (the running bun) instead of `sleep`: coreutils are
// not guaranteed on a bare windows-latest runner, and this test is on the
// Windows CI curated list — the owner-orphan leak it pins is a Windows bug.
const owner = Bun.spawn(
[process.execPath, '-e', 'await Bun.sleep(30000)'],
{ stdio: ['ignore', 'ignore', 'ignore'] },
);
spawned.push(owner);
const agent = Bun.spawn(['bun', 'run', AGENT_SCRIPT], {
env: {

View File

@ -101,6 +101,32 @@ const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }> = [
},
];
// Force-include overrides: files a WINDOWS_FRAGILE_PATTERNS regex excludes for
// a reason that does not actually apply to them. Each entry documents WHY the
// pattern hit is a false positive — the point of these files is Windows
// coverage, so auto-excluding them defeats the regression tests they carry.
const KNOWN_WINDOWS_SAFE: Array<{ file: string; reason: string }> = [
{
file: 'browse/test/file-permissions.test.ts',
// Trips the POSIX-mode-bitmask pattern, but every `mode & 0o777` assertion
// is platform-guarded (win32 returns early / takes the icacls branch).
// This file carries the win32-only icacls-by-SID regression tests, which
// can ONLY execute on windows-latest — excluding it here means the
// machine-account ACL lockout regression is never exercised on the one
// platform it bricks.
reason: 'mode-bitmask hits are POSIX-branch only; win32-only ACL regression tests must run on windows-latest',
},
{
file: 'browse/test/terminal-agent-owner-watchdog.test.ts',
// Trips the spawn(['bun','run',...]) pattern, whose reason is the
// Playwright-bound browse server. This test spawns terminal-agent.ts,
// which imports only fs/path/crypto + local helpers (no Playwright, no
// PTY at module scope) and boots under Bun on Windows — the owner-PID
// orphan leak it pins was reported on Windows (#2019).
reason: 'spawns terminal-agent (no Playwright), not the browse server; owner-orphan leak is a Windows defect',
},
];
export const DEFAULT_SHARD_COUNT = 20;
export const FREE_TEST_TIMEOUT_MS = 10_000;
@ -170,12 +196,17 @@ export function curateWindowsSafe(files: string[], rootDir = ROOT): CurationResu
const safe: string[] = [];
const excluded: Array<{ file: string; reason: string }> = [];
const knownBad = new Map(KNOWN_WINDOWS_INCOMPATIBLE.map((e) => [e.file, e.reason]));
const knownSafe = new Set(KNOWN_WINDOWS_SAFE.map((e) => e.file));
for (const relativePath of files) {
const knownReason = knownBad.get(relativePath);
if (knownReason) {
excluded.push({ file: relativePath, reason: knownReason });
continue;
}
if (knownSafe.has(relativePath)) {
safe.push(relativePath);
continue;
}
const absolute = path.join(rootDir, relativePath);
const fragility = detectWindowsFragility(absolute);
if (fragility) {