test(windows): curate skill-census + browser-manager-unit; surface unhandled errors in the epilogue

Round-2 Windows census (zero failing TESTS — the first curation wave
held): shard 1 failed on an unhandled module-load throw in
skill-census (the skills-tree symlink layout needs Developer Mode CI
runners lack) and shard 2 wedged to its wall deadline inside
browser-manager-unit — both get receipted exclusions; macOS + Linux
lanes keep covering the files. The unhandled-error class also exposed
an epilogue gap: it fails the shard via the strict classifier but
produces no (fail) lines, so the epilogue read 'FAIL — 0 failing
test(s)' with no culprit. The reporter now attributes each
'# Unhandled error between tests' marker to its chunk and the FAIL
line carries the count.
This commit is contained in:
Garry Tan 2026-08-15 18:33:40 -07:00
parent f7f402106d
commit e3cb0416ee
No known key found for this signature in database
GPG Key ID: C1F69E85C74EFE1D
1 changed files with 27 additions and 1 deletions

View File

@ -197,6 +197,15 @@ export const KNOWN_WINDOWS_INCOMPATIBLE: Array<{ file: string; reason: string }>
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',
},
// Round-2 census (PR #2593 run 31919227507) after the first seven:
{
file: 'test/skill-census.test.ts',
reason: 'census walk throws at module load on Windows (skill-census.ts:63) — the skills-tree symlink layout needs Developer Mode that CI runners lack',
},
{
file: 'browse/test/browser-manager-unit.test.ts',
reason: 'wedges the shard to its wall deadline on windows-latest (in-flight at kill); needs a Windows repro to diagnose — macOS + Linux lanes cover the file',
},
];
// Force-include overrides: files a WINDOWS_FRAGILE_PATTERNS regex excludes for
@ -574,6 +583,14 @@ export interface FreeRunReport {
failures: FreeRunFailure[];
/** Files that crashed a worker (bun retries once; a second crash is final). Deduped. */
crashedFiles: string[];
/**
* "# Unhandled error between tests" markers, attributed to the chunk they
* appeared in. These fail the shard via the strict classifier but produce
* NO (fail) lines without surfacing them here, the epilogue reads
* "FAIL — 0 failing test(s)" and the culprit is undiscoverable from CI
* output (first Windows lane run: a module-load throw in skill-census).
*/
unhandledErrors: Array<{ file: string | null }>;
/**
* Wedge-suspect heuristic for a wall-timeout kill: files whose header was
* seen but whose chunk never ENDED (chunk end = the next file's header, or
@ -620,6 +637,7 @@ export class FreeRunReporter {
private readonly crashed = new Set<string>();
private currentFile: string | null = null;
private inRecap = false;
private readonly unhandled: Array<{ file: string | null }> = [];
private testsRan: number | null = null;
private filesRan: number | null = null;
private sawSummary = false;
@ -665,6 +683,7 @@ export class FreeRunReporter {
sawTerminalSummary: this.sawSummary,
failures: [...this.failures],
crashedFiles: [...this.crashed].sort(),
unhandledErrors: [...this.unhandled],
inFlight,
filesWithNoOutput: this.plannedFiles.filter((f) => !this.progress.has(normalizeRelativePath(f))).length,
};
@ -689,6 +708,10 @@ export class FreeRunReporter {
this.currentFile = null;
}
if (line === '# Unhandled error between tests') {
this.unhandled.push({ file: this.currentFile });
}
const header = FILE_HEADER_RE.exec(line);
if (header) {
const file = this.canonicalize(header[1]);
@ -788,7 +811,7 @@ export function buildRunEpilogue(
const failingFiles = new Set(report.failures.map((f) => f.file ?? '(unattributed)'));
const lines = [
`[test:free] FAIL — ${report.failures.length} failing test(s) in ${failingFiles.size} file(s), `
+ `${report.crashedFiles.length} crashed worker(s). Full log: ${logPath}`,
+ `${report.crashedFiles.length} crashed worker(s)${report.unhandledErrors.length > 0 ? `, ${report.unhandledErrors.length} unhandled error(s) between tests` : ''}. Full log: ${logPath}`,
];
for (const failure of report.failures) {
lines.push(`${failure.file ?? '(unattributed)'}${failure.testName}`);
@ -796,6 +819,9 @@ export function buildRunEpilogue(
for (const file of report.crashedFiles) {
lines.push(` ⚠ crashed+retried: ${file}`);
}
for (const u of report.unhandledErrors) {
lines.push(` ⚠ unhandled error between tests (around ${u.file ?? 'unknown file'})`);
}
if (status === 'timed-out') {
if (report.inFlight.length > 0) {
lines.push(` ⏱ in flight at kill: ${report.inFlight.join(', ')}`);