Skip to content

Commit 77b8c30

Browse files
committed
fix(evals): make completion-harness withTimeout actually race the deadline
The sync finally cleared the timer before Promise.race settled, so RunStatus timeout was unreachable and --timeout-ms was a no-op. Await the race so the timer clears on settle, export withTimeout for testing, and guard main() with import.meta.main. Adds scripts/eval-completion.test.ts proving a hung run rejects after the timeout. Baseline re-run still 50.0% (4/8).
1 parent 9bfa3a7 commit 77b8c30

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

scripts/eval-completion.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { withTimeout } from "./eval-completion.js";
3+
4+
describe("withTimeout", () => {
5+
test("rejects a hung run after the timeout", async () => {
6+
const hung = new Promise<never>(() => undefined);
7+
const start = Date.now();
8+
const error = await withTimeout(hung, 50, "task stall-read").catch(
9+
(err: unknown) => err,
10+
);
11+
expect(error).toBeInstanceOf(Error);
12+
expect((error as Error).message).toBe(
13+
"task stall-read timed out after 50ms",
14+
);
15+
// Fires near the deadline instead of hanging (bun test's own 5s
16+
// timeout would fail this test if withTimeout never settled).
17+
expect(Date.now() - start).toBeLessThan(2000);
18+
});
19+
20+
test("resolves the inner value before the timeout", async () => {
21+
await expect(
22+
withTimeout(Promise.resolve("done"), 50, "task"),
23+
).resolves.toBe("done");
24+
});
25+
26+
test("propagates an inner rejection that wins the race", async () => {
27+
await expect(
28+
withTimeout(Promise.reject(new Error("boom")), 1000, "task"),
29+
).rejects.toThrow("boom");
30+
});
31+
});

scripts/eval-completion.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,17 @@ function commitSha(): string {
113113
return result.stdout.trim();
114114
}
115115

116-
function withTimeout<T>(
116+
export async function withTimeout<T>(
117117
promise: Promise<T>,
118118
ms: number,
119119
label: string,
120120
): Promise<T> {
121121
let timer: ReturnType<typeof setTimeout> | undefined;
122122
try {
123-
return Promise.race([
123+
// Await the race so the finally below runs on settle, not synchronously
124+
// on return: clearing the timer before Promise.race settles would make
125+
// the timeout unreachable and a hung run would hang the harness.
126+
return await Promise.race([
124127
promise,
125128
new Promise<never>((_, reject) => {
126129
timer = setTimeout(
@@ -370,4 +373,6 @@ async function main(): Promise<void> {
370373
}
371374
}
372375

373-
await main();
376+
if (import.meta.main) {
377+
await main();
378+
}

0 commit comments

Comments
 (0)