← back to Model Arena
model-arena: bulk re-run a SUBSET of a battle's models with checkboxes
fcff4496b4940cd96db5be0e75248c61fa2e1819 · 2026-09-23 17:48:34 -0700 · Steve Abrams
Adds a "Re-run selected" checkbox bar to the battle detail view — check any number
of the battle's models (including errored ones) and re-run ONLY those in one click,
the missing middle ground between the per-row ↻ (one at a time) and "Re-run entire
challenge" (all of them). Reuses the existing guarded /api/challenges/:id/retry/:model
endpoint. Default = none checked (re-running re-bills metered models, so opt-in is the
safe default) and the checked set is preserved across the 4s detail poll, same pattern
as the build-picker fix. Confirms a metered-model re-bill before firing.
Steve-clarified scope: he wanted the build launcher (done), THIS bulk-rerun subset, and
the new-battle checkboxes (verified already-correct) — all three.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer
Files touched
Diff
commit fcff4496b4940cd96db5be0e75248c61fa2e1819
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 23 17:48:34 2026 -0700
model-arena: bulk re-run a SUBSET of a battle's models with checkboxes
Adds a "Re-run selected" checkbox bar to the battle detail view — check any number
of the battle's models (including errored ones) and re-run ONLY those in one click,
the missing middle ground between the per-row ↻ (one at a time) and "Re-run entire
challenge" (all of them). Reuses the existing guarded /api/challenges/:id/retry/:model
endpoint. Default = none checked (re-running re-bills metered models, so opt-in is the
safe default) and the checked set is preserved across the 4s detail poll, same pattern
as the build-picker fix. Confirms a metered-model re-bill before firing.
Steve-clarified scope: he wanted the build launcher (done), THIS bulk-rerun subset, and
the new-battle checkboxes (verified already-correct) — all three.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer
---
public/index.html | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/public/index.html b/public/index.html
index 90ccece..ad89ff1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -337,6 +337,10 @@ table{border-collapse:separate;border-spacing:0;border:1px solid var(--line);bor
<span class="back" id="back">← back to challenges</span>
<a class="back" id="export" style="margin-left:16px" download>⬇ export battle as standalone HTML</a>
<button class="btn pink" id="rerun-all" style="margin-left:16px" title="Re-run EVERY model in this battle from scratch (re-spends any metered models)">↻ Re-run entire challenge</button>
+ <span id="d-rerunbar" hidden style="margin-left:16px">
+ <span id="d-rerun-picks" class="build-picks" title="Check every model you want to re-run — regenerates ONLY those models' artifacts (the middle ground between the per-row ↻ and 'Re-run entire challenge'). Metered models re-bill."></span>
+ <button class="btn pink" id="d-rerun-go" title="Re-run ONLY the checked models">↻ Re-run selected</button>
+ </span>
<span id="d-buildbar" hidden>
<span id="d-build-picks" class="build-picks" title="Check every model whose artifact you want built — EACH checked model gets its OWN independent iTerm2 Claude window + its own landing page"></span>
<button class="btn pink" id="d-build-go" title="Launch one independent iTerm2 Claude session + landing page for EACH checked model">⚒ Build in iTerm2</button>
@@ -627,6 +631,43 @@ async function renderDetail(id, statusOnly){
};
} else { bbar.hidden=true; }
}
+ // Bulk re-run a SUBSET — check any number of the battle's models, hit one button, and
+ // re-run ONLY those (the middle ground between the per-row ↻ and 'Re-run entire challenge').
+ // Lists ALL runs so a failed/errored model can be re-run too. Same preserve-across-poll
+ // pattern as the build-picker: default = none checked (re-running re-bills metered models,
+ // so an opt-in default is the safe/reversible choice), then keep the user's set on rebuild.
+ const rbar=$('#d-rerunbar'), rpicks=$('#d-rerun-picks'), rgo=$('#d-rerun-go');
+ if (rbar){
+ if (c.runs.length){
+ rbar.hidden=false;
+ const firstRerunRender = !rpicks.querySelector('input');
+ const prevRerun = new Set([...rpicks.querySelectorAll('input:checked')].map(i=>i.value));
+ rpicks.innerHTML = c.runs.map(r=>{
+ const on = !firstRerunRender && prevRerun.has(r.model); // default: none checked
+ const badge = r.status==='error' ? ' ✗' : (r.status==='done' ? '' : ' ⏳');
+ return `<label title="re-run ${mLabel(r.model)}${r.status==='error'?' (currently errored)':''}"><input type="checkbox" value="${r.model}"${on?' checked':''}> ${mLabel(r.model)}${badge}</label>`;
+ }).join('');
+ const meteredIds = new Set(c.runs.filter(r=>{ const m=(models||[]).find(x=>x.id===r.model); return m && m.kind!=='local' && m.kind!=='cli'; }).map(r=>r.model));
+ const updateRerunGo = ()=>{
+ const checked = [...rpicks.querySelectorAll('input:checked')].map(i=>i.value);
+ const nm = checked.filter(id=>meteredIds.has(id)).length;
+ rgo.textContent = checked.length ? `↻ Re-run selected (${checked.length})` : '↻ Re-run selected';
+ rgo.disabled = checked.length===0;
+ rgo.title = checked.length ? ('Re-run '+checked.length+' model(s)'+(nm?' — '+nm+' metered, will re-bill':'')) : 'Check at least one model';
+ };
+ rpicks.onchange = updateRerunGo;
+ updateRerunGo();
+ rgo.onclick = async ()=>{
+ const ids = [...rpicks.querySelectorAll('input:checked')].map(i=>i.value);
+ if (!ids.length) return;
+ const nm = ids.filter(id=>meteredIds.has(id)).length;
+ if (!confirm('Re-run '+ids.length+' selected model'+(ids.length===1?'':'s')+' in "'+c.title+'"?'+(nm?'\n\n'+nm+' metered model(s) will be re-billed.':''))) return;
+ rgo.disabled = true; rgo.textContent = '↻ re-running…';
+ for (const id of ids) { try { await fetch(API+'/api/challenges/'+c.id+'/retry/'+id, {method:'POST'}); } catch {} }
+ openDetail(c.id); // reloads + restarts polling; the re-queued models animate back to running
+ };
+ } else { rbar.hidden=true; }
+ }
// Continue with battles — jump to the New Battle form (mirrors the + New Battle nav)
{ const cont=$('#d-continue'); if (cont) cont.onclick = ()=> show('new'); }
const arena = $('#d-arena');
← b94f38e Fix PT-06 stamp/label overlap and PT-03 ticket/face collisio
·
back to Model Arena
·
auto-data-snapshot: 2026-09-24T02:22:01 (1 data files) — dat 00f276b →