← back to Model Arena
fix(TK-12121): count thumbnail by fresh PNG not Chrome exit code; wall-clock deadline in daily runner
69020dbae15eaf4e68a32df7c38586a5caab8769 · 2026-09-26 08:41:59 -0700 · Steve Abrams
Chrome updated 2026-09-22 writes the screenshot then exits 2 (Teardown watchdog), so
run.thumb was never set, auto-judge never fired, and the daily battle timed out (exit=2)
on 09-22/24/25/26. The runner's 340-iteration cap also shrank to ~16min after settle,
pre-empting the 35min judge patience.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BAadfW5L9VDTcwyeVxhfB2
Files touched
M scripts/daily-challenge.jsM server.js
Diff
commit 69020dbae15eaf4e68a32df7c38586a5caab8769
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Sep 26 08:41:59 2026 -0700
fix(TK-12121): count thumbnail by fresh PNG not Chrome exit code; wall-clock deadline in daily runner
Chrome updated 2026-09-22 writes the screenshot then exits 2 (Teardown watchdog), so
run.thumb was never set, auto-judge never fired, and the daily battle timed out (exit=2)
on 09-22/24/25/26. The runner's 340-iteration cap also shrank to ~16min after settle,
pre-empting the 35min judge patience.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BAadfW5L9VDTcwyeVxhfB2
---
scripts/daily-challenge.js | 9 +++++++--
server.js | 9 ++++++++-
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/scripts/daily-challenge.js b/scripts/daily-challenge.js
index 3c78664..5913f52 100644
--- a/scripts/daily-challenge.js
+++ b/scripts/daily-challenge.js
@@ -88,8 +88,13 @@ if (require.main === module) (async () => {
// judgeTimeout: observed max judge duration was 32min (Aug-04, 3/4 models errored on Mac1 down)
// → wait 35min post-settle before giving up on judging (contrarian fix: was 8min, too short)
const JUDGE_PATIENCE_MS = 35 * 60 * 1000;
+ // TK-12121: bound the wait by WALL CLOCK, not iterations. The old 340-iteration cap shrank to
+ // ~16min once fast-poll (3s) kicked in after settle, so it gave up before JUDGE_PATIENCE (35min)
+ // could ever apply and misreported a settled-but-unjudged battle as "not settled".
+ const SETTLE_DEADLINE_MS = 45 * 60 * 1000;
+ const startedAt = Date.now();
let settledAt = null;
- for (let i = 0; i < 340; i++) {
+ while (settledAt ? true : (Date.now() - startedAt) < SETTLE_DEADLINE_MS) {
await sleep(settledAt ? 3000 : 8000);
const cur = await req('GET', '/api/challenges/' + c.id);
const settled = Array.isArray(cur.runs) && cur.runs.length > 0 &&
@@ -110,7 +115,7 @@ if (require.main === module) (async () => {
return;
}
}
- console.error('timed out waiting for battle to settle'); process.exit(2);
+ console.error('timed out waiting for battle to settle (' + SETTLE_DEADLINE_MS/60000 + 'min)'); process.exit(2);
})().catch(e => { console.error(e); process.exit(1); });
module.exports = { POOL, TWISTS, recentIds, choosePick, buildBrief };
diff --git a/server.js b/server.js
index a8968ea..5bb5752 100644
--- a/server.js
+++ b/server.js
@@ -29,9 +29,16 @@ const CHROME_BIN = process.env.CHROME_BIN || '/Applications/Google Chrome.app/Co
// render an artifact HTML file to a PNG thumbnail (best-effort, never throws)
function shootThumb(htmlPath, outPng, cb) {
if (!fs.existsSync(CHROME_BIN)) return cb && cb(new Error('no chrome'));
+ // TK-12121: Chrome (updated 2026-09-22) writes the PNG then exits 2 on "Teardown watchdog
+ // expired". Judge success by a FRESH non-empty PNG, not the exit code — the old err-first check
+ // left run.thumb unset, so auto-judge never fired and the daily runner timed out (exit=2).
+ try { fs.unlinkSync(outPng); } catch {} // a stale png must never count as a fresh shot
execFile(CHROME_BIN, ['--headless', '--disable-gpu', '--no-sandbox', '--hide-scrollbars',
'--window-size=800,600', '--virtual-time-budget=2800', '--screenshot=' + outPng,
- 'file://' + htmlPath], { timeout: 20000 }, (err) => cb && cb(err || (fs.existsSync(outPng) ? null : new Error('no png'))));
+ 'file://' + htmlPath], { timeout: 20000 }, (err) => {
+ let ok = false; try { ok = fs.statSync(outPng).size > 0; } catch {}
+ cb && cb(ok ? null : (err || new Error('no png')));
+ });
}
const PORT = parseInt(process.env.PORT || '9758', 10);
← 76e878b auto-data-snapshot: 2026-09-26T08:19:11 (1 data files) — dat
·
back to Model Arena
·
auto-data-snapshot: 2026-09-26T08:50:16 (3 data files) — dat b0b30ad →