[object Object]

← back to Model Arena

Add ARENA_PAUSED gate: serve UI + results with zero model runs / zero spend

75c9bd6627edc96ff31cdac5a811c0d2703210ae · 2026-09-23 08:44:59 -0700 · Steve Abrams

Gates runModel() (the sole dispatch chokepoint for new/rerun/boot-resume runs)
and skips the boot resume-collection so paused restarts don't burn the resume
budget. Lets the arena come back up read-only without spending on metered LLMs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 75c9bd6627edc96ff31cdac5a811c0d2703210ae
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 23 08:44:59 2026 -0700

    Add ARENA_PAUSED gate: serve UI + results with zero model runs / zero spend
    
    Gates runModel() (the sole dispatch chokepoint for new/rerun/boot-resume runs)
    and skips the boot resume-collection so paused restarts don't burn the resume
    budget. Lets the arena come back up read-only without spending on metered LLMs.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 server.js | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/server.js b/server.js
index b023560..1d4b81a 100644
--- a/server.js
+++ b/server.js
@@ -36,6 +36,9 @@ function shootThumb(htmlPath, outPng, cb) {
 
 const PORT = parseInt(process.env.PORT || '9758', 10);
 const BASIC_AUTH = process.env.BASIC_AUTH !== undefined ? process.env.BASIC_AUTH : 'admin:DW2024!';
+// ARENA_PAUSED=1 serves the UI + existing results read-only but runs NO model challenges
+// (no boot-resume of interrupted runs, no new/rerun dispatch) — zero metered spend while up.
+const ARENA_PAUSED = /^(1|true|yes|on)$/i.test(process.env.ARENA_PAUSED || '');
 const DIR = __dirname;
 // DATA_DIR keeps verification and canary servers from touching the live arena ledger.
 const DATA = process.env.DATA_DIR ? path.resolve(process.env.DATA_DIR) : path.join(DIR, 'data');
@@ -141,6 +144,7 @@ const MAX_RESUMES = 4;
 const resumeOnBoot = [];
 for (const c of challenges) {
   if (c.judging) c.judging = false; // a restart mid-judge would otherwise block judging forever
+  if (ARENA_PAUSED) continue; // paused: don't burn the resume budget — leave interrupted runs for a later unpaused boot
   for (const r of c.runs) {
     const corrupt = r.started_at && r.finished_at && r.finished_at < r.started_at;
     const interrupted = r.status === 'running' || r.status === 'queued' ||
@@ -580,6 +584,7 @@ function maybeAutoJudge(challenge) {
 }
 
 function runModel(challenge, modelId) {
+  if (ARENA_PAUSED) return; // paused: never dispatch a model run (no metered spend)
   const m = MODELS.find(x => x.id === modelId);
   const run = challenge.runs.find(r => r.model === modelId);
   if (!m || !run) return;
@@ -1072,8 +1077,8 @@ iframe{width:100%;height:460px;border:0;background:#000}.c.win iframe{height:560
 });
 
 server.listen(PORT, function () {
-  console.log('[model-arena] http://localhost:' + this.address().port + (BASIC_AUTH ? '  (basic auth on)' : ''));
-  if (resumeOnBoot.length) {
+  console.log('[model-arena] http://localhost:' + this.address().port + (BASIC_AUTH ? '  (basic auth on)' : '') + (ARENA_PAUSED ? '  [CHALLENGES PAUSED — no model runs, no spend]' : ''));
+  if (!ARENA_PAUSED && resumeOnBoot.length) {
     console.log('[model-arena] resuming ' + resumeOnBoot.length + ' run(s) interrupted by restart');
     for (const j of resumeOnBoot) runModel(j.c, j.model);
   }

← 8fb3efb auto-data-snapshot: 2026-09-22T09:54:34 (3 data files) — dat  ·  back to Model Arena  ·  auto-data-snapshot: 2026-09-23T08:59:35 (1 data files) — dat 969f6b3 →