| 1 | package main |
| 2 | |
| 3 | // The dashboard page: dark flight-recorder instrument panel, all-monospace |
| 4 | // type, recessed trace screens. The renderer is incremental (keyed DOM, |
| 5 | // in-place updates) so cells animate, numbers tween, and hover survives |
| 6 | // polling; a replay engine scrubs the run by wall-clock timestamp. Win/break |
| 7 | // polarity is carried by geometry, never by the green/red pair alone. |
| 8 | const serveHTML = `<!doctype html> |
| 9 | <html lang="en"><head><meta charset="utf-8"> |
| 10 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 11 | <meta name="theme-color" content="#0d0d0d"> |
| 12 | <title>e2ebench · live</title> |
| 13 | <style> |
| 14 | :root { |
| 15 | color-scheme: dark; |
| 16 | --page: #0d0d0d; --panel: #1a1a19; --screen: #141413; |
| 17 | --ink: #f2f1ec; --ink-2: #c3c2b7; --muted: #898781; --faint: #55544f; |
| 18 | --hairline: rgba(255,255,255,0.08); --tick: rgba(255,255,255,0.05); |
| 19 | --grid: #2c2c2a; |
| 20 | --explore: #3987e5; --attempt: #d95926; |
| 21 | --win: #0ca30c; --break: #d03b3b; |
| 22 | --accent: #86b6ef; |
| 23 | } |
| 24 | * { box-sizing: border-box; margin: 0; } |
| 25 | html { background: var(--page); scroll-behavior: smooth; } |
| 26 | body { color: var(--ink); |
| 27 | font: 13px/1.55 ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace; |
| 28 | padding: 34px clamp(18px, 4.5vw, 56px) 80px; max-width: 1280px; margin: 0 auto; |
| 29 | font-variant-numeric: tabular-nums; } |
| 30 | section, .rule, header, #runseg, #deck, #cluster { animation: rise .5s ease-out backwards; } |
| 31 | #runseg { animation-delay: .05s; } #deck { animation-delay: .1s; } |
| 32 | #cluster { animation-delay: .15s; } |
| 33 | @keyframes rise { from { opacity: 0; transform: translateY(8px); } } |
| 34 | |
| 35 | .rule { border: 0; border-top: 1px solid var(--hairline); margin: 40px 0 0; } |
| 36 | .eyebrow { font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; |
| 37 | color: var(--muted); margin: 10px 0 14px; } |
| 38 | .eyebrow b { color: var(--ink-2); font-weight: 600; } |
| 39 | .caption { color: var(--muted); font-size: 12px; max-width: 72ch; margin: -6px 0 18px; } |
| 40 | .caption b { color: var(--ink-2); font-weight: 600; } |
| 41 | |
| 42 | header { display: flex; align-items: baseline; } |
| 43 | .wordmark { font-size: 13px; font-weight: 700; letter-spacing: 0.08em; } |
| 44 | .wordmark em { font-style: normal; color: var(--accent); } |
| 45 | .wordmark span { color: var(--muted); font-weight: 400; } |
| 46 | #clock { margin-left: auto; color: var(--muted); font-size: 12px; } |
| 47 | |
| 48 | #runseg { display: flex; gap: 3px; margin-top: 22px; } |
| 49 | #runseg i { flex: 1 1 0; max-width: 22px; height: 14px; border-radius: 2px; |
| 50 | background: var(--grid); cursor: pointer; |
| 51 | transition: background .3s, box-shadow .3s, transform .12s; } |
| 52 | #runseg i:hover { transform: translateY(-2px); } |
| 53 | #runseg i.done { background: var(--accent); opacity: 0.85; } |
| 54 | #runseg i.live { background: var(--attempt); |
| 55 | box-shadow: 0 0 10px color-mix(in srgb, var(--attempt) 55%, transparent); |
| 56 | animation: pulse 1.1s ease-in-out infinite; } |
| 57 | |
| 58 | #deck { display: flex; align-items: center; gap: 14px; margin-top: 16px; |
| 59 | padding: 10px 14px; background: var(--panel); border: 1px solid var(--hairline); |
| 60 | border-radius: 10px; } |
| 61 | #deck button, #deck select { font: inherit; color: var(--ink-2); background: transparent; |
| 62 | border: 1px solid var(--hairline); border-radius: 6px; padding: 4px 12px; |
| 63 | cursor: pointer; transition: color .15s, border-color .15s, background .15s; } |
| 64 | #deck button:hover { color: var(--ink); border-color: var(--muted); } |
| 65 | #deck button.hot { color: var(--attempt); border-color: var(--attempt); } |
| 66 | #ptime { color: var(--muted); font-size: 12px; min-width: 15ch; } |
| 67 | #scrub { flex: 1 1 auto; -webkit-appearance: none; appearance: none; height: 4px; |
| 68 | background: var(--grid); border-radius: 2px; cursor: pointer; } |
| 69 | #scrub::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; |
| 70 | border-radius: 50%; background: var(--ink-2); border: 2px solid var(--page); |
| 71 | transition: transform .12s; } |
| 72 | #scrub::-webkit-slider-thumb:hover { transform: scale(1.25); } |
| 73 | |
| 74 | #cluster { display: flex; gap: 48px; margin-top: 20px; flex-wrap: wrap; } |
| 75 | .gauge b { display: block; font-size: 30px; font-weight: 300; letter-spacing: -0.01em; |
| 76 | line-height: 1.1; } |
| 77 | .gauge b small { font-size: 15px; color: var(--muted); font-weight: 300; } |
| 78 | .gauge span { display: block; font-size: 10px; letter-spacing: 0.2em; |
| 79 | text-transform: uppercase; color: var(--muted); margin-top: 5px; } |
| 80 | |
| 81 | #now { display: flex; gap: 36px; align-items: stretch; min-height: 118px; } |
| 82 | #now .main { flex: 1 1 auto; min-width: 0; } |
| 83 | #now .head { display: flex; align-items: baseline; gap: 16px; } |
| 84 | #now .name { font-size: 17px; font-weight: 700; letter-spacing: 0.02em; cursor: pointer; } |
| 85 | #now .name:hover { color: var(--accent); } |
| 86 | #now .rec { color: var(--attempt); font-size: 11px; letter-spacing: 0.2em; |
| 87 | animation: pulse 1.1s ease-in-out infinite; } |
| 88 | #now .doing { color: var(--ink-2); font-size: 12px; transition: color .3s; } |
| 89 | #now .side { display: flex; flex-direction: column; gap: 14px; padding-left: 32px; |
| 90 | border-left: 1px solid var(--hairline); } |
| 91 | #now .side .gauge b { font-size: 22px; } |
| 92 | #now.idle { color: var(--muted); font-size: 12px; display: block; min-height: 0; } |
| 93 | |
| 94 | .screen { background: var(--screen); border: 1px solid var(--hairline); |
| 95 | border-radius: 8px; padding: 0 12px; margin-top: 14px; position: relative; |
| 96 | background-image: repeating-linear-gradient(90deg, var(--tick) 0 1px, transparent 1px 75px); |
| 97 | background-origin: content-box; transition: border-color .3s, box-shadow .3s; } |
| 98 | .trace { display: flex; align-items: center; height: 56px; position: relative; } |
| 99 | .trace::before { content: ""; position: absolute; left: 0; right: 0; top: 50%; |
| 100 | border-top: 1px solid var(--grid); } |
| 101 | .cells { display: flex; align-items: center; height: 100%; overflow-x: auto; flex: 1; } |
| 102 | .cell { flex: 0 0 12px; height: 12px; border-radius: 2px; margin-right: 3px; |
| 103 | background: var(--grid); align-self: center; position: relative; |
| 104 | transition: transform .12s; } |
| 105 | .cell:hover { transform: scale(1.4); z-index: 2; } |
| 106 | .cell.e { background: var(--explore); } |
| 107 | .cell.a { background: var(--attempt); } |
| 108 | .cell.o { background: var(--win); height: 44px; align-self: flex-start; |
| 109 | border-radius: 3px 3px 0 0; |
| 110 | box-shadow: 0 0 12px color-mix(in srgb, var(--win) 50%, transparent); } |
| 111 | .cell.r { background: var(--break); height: 44px; align-self: flex-end; |
| 112 | border-radius: 0 0 3px 3px; |
| 113 | box-shadow: 0 0 12px color-mix(in srgb, var(--break) 50%, transparent); } |
| 114 | .cell.new { animation: pop .3s cubic-bezier(.2,.9,.3,1.35); } |
| 115 | .cell.o.new, .cell.r.new { animation: pop .3s cubic-bezier(.2,.9,.3,1.35), |
| 116 | flare .9s ease-out; } |
| 117 | @keyframes pop { from { transform: scale(.2); opacity: 0; } } |
| 118 | @keyframes flare { 0% { box-shadow: 0 0 2px 8px color-mix(in srgb, currentColor 40%, transparent); } |
| 119 | 100% { } } |
| 120 | .cell.cursor { background: transparent; border: 1px dashed var(--muted); |
| 121 | animation: pulse 1.1s ease-in-out infinite; flex: none; } |
| 122 | .mini .cell { flex-basis: 7px; height: 7px; margin-right: 2px; border-radius: 1.5px; } |
| 123 | .mini .cell.o, .mini .cell.r { height: 21px; box-shadow: none; } |
| 124 | .mini.trace { height: 30px; } |
| 125 | |
| 126 | #kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); } |
| 127 | .kpi { padding: 4px 28px 6px 0; } |
| 128 | .kpi + .kpi { border-left: 1px solid var(--hairline); padding-left: 28px; } |
| 129 | .kpi b { font-size: 38px; font-weight: 300; line-height: 1.05; letter-spacing: -0.01em; |
| 130 | transition: color .3s; } |
| 131 | .kpi b small { font-size: 14px; color: var(--muted); font-weight: 400; } |
| 132 | .kpi.win b { color: var(--win); text-shadow: 0 0 18px color-mix(in srgb, var(--win) 35%, transparent); } |
| 133 | .kpi.break b { color: var(--break); } |
| 134 | .kpi.zero b { color: var(--faint); text-shadow: none; } |
| 135 | .kpi .label { font-size: 10px; letter-spacing: 0.2em; text-transform: uppercase; |
| 136 | color: var(--ink-2); margin-top: 8px; } |
| 137 | .kpi .why { color: var(--muted); font-size: 11.5px; margin-top: 6px; line-height: 1.5; |
| 138 | font-family: system-ui, sans-serif; } |
| 139 | .kpi .bar { height: 3px; background: var(--grid); margin-top: 12px; border-radius: 2px; |
| 140 | overflow: hidden; } |
| 141 | .kpi .bar i { display: block; height: 100%; background: var(--attempt); |
| 142 | transition: width .6s ease; } |
| 143 | |
| 144 | #legend { display: flex; flex-wrap: wrap; gap: 10px 30px; } |
| 145 | #legend span { display: inline-flex; align-items: center; gap: 9px; font-size: 12px; |
| 146 | color: var(--ink-2); } |
| 147 | #legend i { width: 10px; height: 10px; border-radius: 2px; flex: none; } |
| 148 | #legend .up, #legend .down { height: 26px; } |
| 149 | #legend .up i { height: 20px; border-radius: 3px 3px 0 0; align-self: flex-start; } |
| 150 | #legend .down i { height: 20px; border-radius: 0 0 3px 3px; align-self: flex-end; } |
| 151 | |
| 152 | #wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); |
| 153 | gap: 26px 30px; } |
| 154 | .task { transition: opacity .4s; } |
| 155 | .task .top { display: flex; align-items: baseline; gap: 8px; } |
| 156 | .task .name { font-size: 12px; font-weight: 700; letter-spacing: 0.02em; |
| 157 | overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
| 158 | .task .badges { margin-left: auto; display: flex; gap: 8px; font-size: 11px; flex: none; } |
| 159 | .badge.win { color: var(--win); } .badge.break { color: var(--break); } |
| 160 | .badge.guard { color: var(--attempt); } |
| 161 | .task .screen { margin-top: 8px; padding: 0 8px; |
| 162 | background-image: repeating-linear-gradient(90deg, var(--tick) 0 1px, transparent 1px 45px); } |
| 163 | .task:hover .screen { border-color: var(--muted); } |
| 164 | .task.live .screen { border-color: var(--attempt); |
| 165 | box-shadow: 0 0 14px color-mix(in srgb, var(--attempt) 25%, transparent); } |
| 166 | .task.flash .screen { animation: flashring 1.2s ease-out; } |
| 167 | @keyframes flashring { 0% { border-color: var(--accent); |
| 168 | box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 45%, transparent); } } |
| 169 | .task .meta { color: var(--muted); font-size: 11px; margin-top: 7px; } |
| 170 | .task .meta b { color: var(--ink-2); font-weight: 600; } |
| 171 | .task.queued { opacity: .55; } |
| 172 | .task.queued .name { color: var(--faint); font-weight: 400; } |
| 173 | .task.queued .screen { border-style: dashed; background-image: none; } |
| 174 | .qnote { color: var(--faint); font-size: 11px; letter-spacing: 0.18em; |
| 175 | text-transform: uppercase; margin: auto; } |
| 176 | |
| 177 | #tip { position: fixed; z-index: 10; pointer-events: none; background: var(--panel); |
| 178 | border: 1px solid var(--hairline); border-radius: 8px; padding: 8px 12px; |
| 179 | font-size: 12px; color: var(--ink-2); max-width: 320px; line-height: 1.5; |
| 180 | box-shadow: 0 8px 30px rgba(0,0,0,.5); opacity: 0; transform: translateY(4px) scale(.97); |
| 181 | transition: opacity .15s, transform .15s; } |
| 182 | #tip.on { opacity: 1; transform: none; } |
| 183 | #tip b { color: var(--ink); } |
| 184 | |
| 185 | @keyframes pulse { 50% { opacity: 0.3; } } |
| 186 | @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } |
| 187 | html { scroll-behavior: auto; } } |
| 188 | </style></head> |
| 189 | <body> |
| 190 | <header> |
| 191 | <div class="wordmark"><em>reasonix</em> e2ebench <span>· flight recorder</span></div> |
| 192 | <span id="clock"></span> |
| 193 | </header> |
| 194 | <div id="runseg"></div> |
| 195 | <div id="deck"> |
| 196 | <button id="play" title="replay the run from its trajectories">▶ replay</button> |
| 197 | <input id="scrub" type="range" min="0" max="1000" value="1000"> |
| 198 | <span id="ptime"></span> |
| 199 | <select id="speed"><option value="30">30×</option><option value="120" selected>120×</option><option value="600">600×</option></select> |
| 200 | <button id="golive">● live</button> |
| 201 | </div> |
| 202 | <div id="cluster"></div> |
| 203 | |
| 204 | <hr class="rule"><div class="eyebrow">now running</div> |
| 205 | <div id="now" class="idle">waiting for the first task…</div> |
| 206 | |
| 207 | <hr class="rule"><div class="eyebrow">shadow scorer verdicts</div> |
| 208 | <p class="caption">A second scorer watches every tool round and only credits <b>verified</b> progress — a recognized check going from fail to pass. Everything else is motion.</p> |
| 209 | <section id="kpis"></section> |
| 210 | |
| 211 | <hr class="rule"><div class="eyebrow">how to read a trace</div> |
| 212 | <div id="legend"> |
| 213 | <span><i style="background:var(--grid)"></i>quiet — nothing new</span> |
| 214 | <span><i style="background:var(--explore)"></i>exploring — new files or commands</span> |
| 215 | <span><i style="background:var(--attempt)"></i>working — edits or checks, unverified</span> |
| 216 | <span class="up"><i style="background:var(--win)"></i>verified win — failing check turned green</span> |
| 217 | <span class="down"><i style="background:var(--break)"></i>broke something — passing check failed</span> |
| 218 | </div> |
| 219 | |
| 220 | <hr class="rule"><div class="eyebrow" id="walllabel">all tasks</div> |
| 221 | <section id="wall"></section> |
| 222 | <div id="tip"></div> |
| 223 | |
| 224 | <script> |
| 225 | "use strict"; |
| 226 | var $ = function (id) { return document.getElementById(id); }; |
| 227 | var LIVE_MS = 15000; |
| 228 | var state = null, fetchedAt = 0, suite = [], byID = {}; |
| 229 | var segs = {}, cards = {}; |
| 230 | var R = { on: false, playing: false, t: 0, speed: 120, t0: 0, t1: 0, last: 0 }; |
| 231 | var nowShown = null; |
| 232 | |
| 233 | function fmtDur(ms) { |
| 234 | if (!ms || ms < 0) return "0s"; |
| 235 | if (ms < 90000) return (ms / 1000).toFixed(0) + "s"; |
| 236 | return Math.floor(ms / 60000) + "m" + String(Math.round((ms % 60000) / 1000)).padStart(2, "0") + "s"; |
| 237 | } |
| 238 | function fmtOff(ms) { |
| 239 | var s = Math.max(0, Math.round(ms / 1000)); |
| 240 | return "T+" + String(Math.floor(s / 60)).padStart(2, "0") + ":" + String(s % 60).padStart(2, "0"); |
| 241 | } |
| 242 | function catOf(r) { return r.r ? "r" : r.o ? "o" : (r.c || r.v) ? "a" : r.e ? "e" : ""; } |
| 243 | function nameOf(r) { |
| 244 | return r.r ? "broke something" : r.o ? "verified win" : |
| 245 | (r.c || r.v) ? "working" : r.e ? "exploring" : "quiet"; |
| 246 | } |
| 247 | function tipOf(i, r) { |
| 248 | var p = []; |
| 249 | if (r.o) p.push(r.o + " check(s) turned green"); |
| 250 | if (r.r) p.push(r.r + " check(s) broke"); |
| 251 | if (r.v) p.push(r.v + " check runs"); |
| 252 | if (r.c) p.push(r.c + " edits"); |
| 253 | if (r.e) p.push(r.e + " new files/commands"); |
| 254 | if (!p.length) p.push("repeat of earlier work"); |
| 255 | return "<b>round " + (i + 1) + " — " + nameOf(r) + "</b><br>" + p.join(", "); |
| 256 | } |
| 257 | function cellNode(i, r, animate) { |
| 258 | var d = document.createElement("div"); |
| 259 | d.className = "cell " + catOf(r) + (animate ? " new" : ""); |
| 260 | d.dataset.tip = tipOf(i, r); |
| 261 | return d; |
| 262 | } |
| 263 | function countVis(rounds, cut) { |
| 264 | var n = 0; |
| 265 | while (n < rounds.length && (rounds[n].t || 0) <= cut) n++; |
| 266 | return n; |
| 267 | } |
| 268 | function goTo(id) { |
| 269 | var c = cards[id]; |
| 270 | if (!c) return; |
| 271 | c.el.scrollIntoView({ behavior: "smooth", block: "center" }); |
| 272 | c.el.classList.remove("flash"); |
| 273 | void c.el.offsetWidth; |
| 274 | c.el.classList.add("flash"); |
| 275 | } |
| 276 | |
| 277 | function ensure() { |
| 278 | suite.forEach(function (id) { |
| 279 | if (!segs[id]) { |
| 280 | var i = document.createElement("i"); |
| 281 | i.dataset.tip = "<b>" + id + "</b><br>click to jump to its card"; |
| 282 | i.addEventListener("click", function () { goTo(id); }); |
| 283 | $("runseg").appendChild(i); |
| 284 | segs[id] = i; |
| 285 | } |
| 286 | if (!cards[id]) { |
| 287 | var el = document.createElement("div"); |
| 288 | el.className = "task queued"; |
| 289 | el.innerHTML = '<div class="top"><span class="name">' + id + |
| 290 | '</span><div class="badges"></div></div>' + |
| 291 | '<div class="screen"><div class="trace mini"><div class="cells"></div>' + |
| 292 | '<span class="qnote">queued</span></div></div><div class="meta"></div>'; |
| 293 | $("wall").appendChild(el); |
| 294 | cards[id] = { el: el, cells: el.querySelector(".cells"), badges: el.querySelector(".badges"), |
| 295 | meta: el.querySelector(".meta"), qnote: el.querySelector(".qnote"), built: 0, queued: true }; |
| 296 | } |
| 297 | }); |
| 298 | } |
| 299 | |
| 300 | function updateCard(id, t, cut, liveNow) { |
| 301 | var c = cards[id]; |
| 302 | var rounds = t ? t.rounds : []; |
| 303 | var vis = t ? (cut === Infinity ? rounds.length : countVis(rounds, cut)) : 0; |
| 304 | if (!t || vis === 0) { |
| 305 | c.el.classList.add("queued"); c.el.classList.remove("live"); |
| 306 | c.qnote.style.display = ""; c.cells.innerHTML = ""; c.built = 0; |
| 307 | c.badges.innerHTML = ""; c.meta.textContent = ""; |
| 308 | return { o: 0, r: 0 }; |
| 309 | } |
| 310 | c.el.classList.remove("queued"); |
| 311 | c.qnote.style.display = "none"; |
| 312 | if (vis < c.built) { c.cells.innerHTML = ""; c.built = 0; } |
| 313 | var animate = vis - c.built <= 12; |
| 314 | for (var i = c.built; i < vis; i++) c.cells.appendChild(cellNode(i, rounds[i], animate)); |
| 315 | c.built = vis; |
| 316 | var o = 0, rg = 0; |
| 317 | for (var j = 0; j < vis; j++) { o += rounds[j].o || 0; rg += rounds[j].r || 0; } |
| 318 | c.badges.innerHTML = |
| 319 | (o ? '<span class="badge win" data-tip="verified wins">↑' + o + "</span>" : "") + |
| 320 | (rg ? '<span class="badge break" data-tip="regressions">↓' + rg + "</span>" : "") + |
| 321 | (t.no_progress ? '<span class="badge guard" data-tip="progress guard interventions">g' + t.no_progress + "</span>" : ""); |
| 322 | var span = cut === Infinity ? t.span_ms |
| 323 | : Math.max(0, (rounds[vis - 1].t || 0) - ((rounds[0].t || 0))); |
| 324 | c.meta.innerHTML = "<b>" + vis + "</b> rounds · " + fmtDur(span) + |
| 325 | (liveNow ? ' · <span class="badge guard">running</span>' : ""); |
| 326 | c.el.classList.toggle("live", liveNow); |
| 327 | return { o: o, r: rg }; |
| 328 | } |
| 329 | |
| 330 | function updateNow(liveIDs, cut) { |
| 331 | var el = $("now"); |
| 332 | if (!liveIDs.length) { |
| 333 | nowShown = null; |
| 334 | el.className = "idle"; |
| 335 | var complete = suite.length > 0 && suite.every(function (id) { return byID[id]; }); |
| 336 | el.textContent = R.on ? "…" : |
| 337 | complete ? "run complete — press ▶ replay to watch it back" : |
| 338 | "between tasks — grading the last one or starting the next…"; |
| 339 | return; |
| 340 | } |
| 341 | var id = liveIDs[liveIDs.length - 1]; |
| 342 | var t = byID[id]; |
| 343 | var rounds = t.rounds; |
| 344 | var vis = cut === Infinity ? rounds.length : countVis(rounds, cut); |
| 345 | if (nowShown !== id) { |
| 346 | nowShown = id; |
| 347 | el.className = ""; |
| 348 | el.innerHTML = '<div class="main"><div class="head"><span class="rec">● rec</span>' + |
| 349 | '<span class="name" data-goto="' + id + '">' + id + "</span>" + |
| 350 | '<span class="doing"></span></div>' + |
| 351 | '<div class="screen"><div class="trace"><div class="cells"></div>' + |
| 352 | '<div class="cell cursor" data-tip="next round"></div></div></div></div>' + |
| 353 | '<div class="side"><div class="gauge"><b class="g-round"></b><span>round</span></div>' + |
| 354 | '<div class="gauge"><b class="g-elapsed"></b><span>elapsed</span></div>' + |
| 355 | '<div class="gauge"><b class="g-tools"></b><span>in tools</span></div></div>'; |
| 356 | el.querySelector(".name").addEventListener("click", function () { goTo(id); }); |
| 357 | el._built = 0; |
| 358 | } |
| 359 | var cells = el.querySelector(".cells"); |
| 360 | if (vis < el._built) { cells.innerHTML = ""; el._built = 0; } |
| 361 | var animate = vis - el._built <= 12; |
| 362 | for (var i = el._built; i < vis; i++) cells.appendChild(cellNode(i, rounds[i], animate)); |
| 363 | el._built = vis; |
| 364 | var last = rounds[vis - 1] || {}; |
| 365 | el.querySelector(".doing").textContent = nameOf(last) + "…"; |
| 366 | el.querySelector(".g-round").textContent = vis; |
| 367 | var elapsed = cut === Infinity |
| 368 | ? t.span_ms + (state ? Date.now() - fetchedAt : 0) |
| 369 | : Math.max(0, (last.t || 0) - (rounds[0] ? rounds[0].t || 0 : 0)); |
| 370 | el.querySelector(".g-elapsed").textContent = fmtDur(elapsed); |
| 371 | el.querySelector(".g-tools").textContent = fmtDur(t.tool_ms); |
| 372 | } |
| 373 | |
| 374 | function gauge(v, label) { |
| 375 | return '<div class="gauge"><b>' + v + "</b><span>" + label + "</span></div>"; |
| 376 | } |
| 377 | function kpiHTML(cls, value, unit, label, why, ratio) { |
| 378 | return '<div class="kpi ' + cls + '"><b>' + value + (unit ? "<small> " + unit + "</small>" : "") + |
| 379 | '</b><div class="label">' + label + '</div><div class="why">' + why + "</div>" + |
| 380 | (ratio !== undefined ? '<div class="bar"><i style="width:' + Math.min(100, ratio) + '%"></i></div>' : "") + |
| 381 | "</div>"; |
| 382 | } |
| 383 | |
| 384 | function applyAll() { |
| 385 | if (!state) return; |
| 386 | var cut = R.on ? R.t : Infinity; |
| 387 | ensure(); |
| 388 | var doneN = 0, liveIDs = [], obj = 0, reg = 0; |
| 389 | suite.forEach(function (id) { |
| 390 | var t = byID[id]; |
| 391 | var liveNow = false, isDone = false; |
| 392 | if (t) { |
| 393 | if (R.on) { |
| 394 | var vis = countVis(t.rounds, cut); |
| 395 | liveNow = vis > 0 && vis < t.rounds.length; |
| 396 | isDone = t.rounds.length > 0 && vis >= t.rounds.length; |
| 397 | } else { |
| 398 | liveNow = t.ago_ms >= 0 && t.ago_ms < LIVE_MS; |
| 399 | isDone = !liveNow; |
| 400 | } |
| 401 | } |
| 402 | if (isDone) doneN++; |
| 403 | if (liveNow) liveIDs.push(id); |
| 404 | segs[id].className = liveNow ? "live" : isDone ? "done" : ""; |
| 405 | var c = updateCard(id, t, cut, liveNow); |
| 406 | obj += c.o; reg += c.r; |
| 407 | }); |
| 408 | |
| 409 | var spans = []; |
| 410 | suite.forEach(function (id) { |
| 411 | var t = byID[id]; |
| 412 | if (t && t.span_ms && (!R.on || countVis(t.rounds, cut) >= t.rounds.length)) spans.push(t.span_ms); |
| 413 | }); |
| 414 | var avg = spans.length ? spans.reduce(function (a, b) { return a + b; }, 0) / spans.length : 0; |
| 415 | var left = suite.length - doneN - liveIDs.length; |
| 416 | $("cluster").innerHTML = |
| 417 | gauge(doneN + "<small> / " + suite.length + "</small>", "tasks recorded") + |
| 418 | (avg ? gauge(fmtDur(avg), "per task") : "") + |
| 419 | (R.on ? gauge(fmtOff(R.t - R.t0), "playhead") : |
| 420 | left + liveIDs.length > 0 && avg |
| 421 | ? gauge("~" + fmtDur(avg * (left + liveIDs.length * 0.5)), "time left") |
| 422 | : gauge("done", "status")) + |
| 423 | (liveIDs.length ? gauge(liveIDs.length, R.on ? "on screen" : "running now") : "") + |
| 424 | (left > 0 ? gauge(left, "queued") : ""); |
| 425 | |
| 426 | updateNow(liveIDs, cut); |
| 427 | |
| 428 | var atEnd = !R.on || R.t >= R.t1; |
| 429 | var fp = 0, pr = 0, stall = 0, regressed = 0; |
| 430 | state.tasks.forEach(function (t) { |
| 431 | var o = t.outcome; if (!o) return; |
| 432 | fp += o.false_progress_rounds || 0; pr += o.progress_rounds || 0; |
| 433 | stall = Math.max(stall, o.solution_stall_max || 0); |
| 434 | if (o.regressed_from_best) regressed++; |
| 435 | }); |
| 436 | $("kpis").innerHTML = |
| 437 | kpiHTML(obj ? "win" : "zero", obj, "", "verified wins", |
| 438 | "Rounds where a failing check turned green — the only progress the shadow scorer trusts.") + |
| 439 | kpiHTML(reg ? "break" : "zero", reg + (regressed && atEnd ? " / " + regressed : ""), "", |
| 440 | "regressions" + (regressed && atEnd ? " / peaked early" : ""), |
| 441 | "A previously passing check broke" + (regressed && atEnd ? "; some runs ended below their best state." : ".")) + |
| 442 | kpiHTML("", atEnd && pr ? Math.round(100 * fp / pr) + "%" : "–", atEnd && pr ? fp + " of " + pr : "", |
| 443 | "false progress", |
| 444 | "Rounds the current scorer counted as progress that never turned into a verified win.", |
| 445 | atEnd && pr ? 100 * fp / pr : 0) + |
| 446 | kpiHTML("", atEnd && stall ? stall : "–", atEnd && stall ? "rounds" : "", "longest stall", |
| 447 | "Most consecutive rounds of work with no verified win — where an agent burns time."); |
| 448 | |
| 449 | $("walllabel").innerHTML = "all tasks · <b>" + suite.length + "</b>"; |
| 450 | $("scrub").value = R.on && R.t1 > R.t0 ? Math.round(1000 * (R.t - R.t0) / (R.t1 - R.t0)) : 1000; |
| 451 | $("ptime").textContent = R.on |
| 452 | ? fmtOff(R.t - R.t0) + " / " + fmtOff(R.t1 - R.t0) |
| 453 | : "live tail"; |
| 454 | $("play").textContent = R.playing ? "⏸ pause" : "▶ replay"; |
| 455 | $("play").classList.toggle("hot", R.playing); |
| 456 | } |
| 457 | |
| 458 | function bounds() { |
| 459 | var t0 = Infinity, t1 = 0; |
| 460 | state.tasks.forEach(function (t) { |
| 461 | if (t.rounds.length) { |
| 462 | t0 = Math.min(t0, t.rounds[0].t || Infinity); |
| 463 | t1 = Math.max(t1, t.rounds[t.rounds.length - 1].t || 0); |
| 464 | } |
| 465 | }); |
| 466 | R.t0 = t0 === Infinity ? 0 : t0; |
| 467 | R.t1 = t1; |
| 468 | } |
| 469 | |
| 470 | function frame(now) { |
| 471 | if (!R.playing) return; |
| 472 | var dt = R.last ? now - R.last : 16; |
| 473 | R.last = now; |
| 474 | R.t += dt * R.speed; |
| 475 | if (R.t >= R.t1) { R.t = R.t1; R.playing = false; } |
| 476 | applyAll(); |
| 477 | if (R.playing) requestAnimationFrame(frame); |
| 478 | } |
| 479 | $("play").addEventListener("click", function () { |
| 480 | if (!state) return; |
| 481 | bounds(); |
| 482 | if (R.playing) { R.playing = false; applyAll(); return; } |
| 483 | if (!R.on || R.t >= R.t1) { R.on = true; R.t = R.t0; resetBuilt(); } |
| 484 | R.playing = true; R.last = 0; |
| 485 | requestAnimationFrame(frame); |
| 486 | }); |
| 487 | $("golive").addEventListener("click", function () { |
| 488 | R.on = false; R.playing = false; resetBuilt(); applyAll(); |
| 489 | }); |
| 490 | $("scrub").addEventListener("input", function () { |
| 491 | if (!state) return; |
| 492 | bounds(); |
| 493 | R.on = true; |
| 494 | R.t = R.t0 + (R.t1 - R.t0) * (this.value / 1000); |
| 495 | applyAll(); |
| 496 | }); |
| 497 | $("speed").addEventListener("change", function () { R.speed = +this.value; }); |
| 498 | function resetBuilt() { |
| 499 | Object.keys(cards).forEach(function (id) { cards[id].cells.innerHTML = ""; cards[id].built = 0; }); |
| 500 | nowShown = null; |
| 501 | } |
| 502 | |
| 503 | var tip = $("tip"); |
| 504 | document.addEventListener("mouseover", function (e) { |
| 505 | var n = e.target.closest ? e.target.closest("[data-tip]") : null; |
| 506 | if (n) { tip.innerHTML = n.dataset.tip; tip.classList.add("on"); } |
| 507 | else tip.classList.remove("on"); |
| 508 | }); |
| 509 | document.addEventListener("mousemove", function (e) { |
| 510 | if (!tip.classList.contains("on")) return; |
| 511 | var x = Math.min(e.clientX + 14, window.innerWidth - tip.offsetWidth - 10); |
| 512 | var y = e.clientY + 16; |
| 513 | if (y + tip.offsetHeight > window.innerHeight - 8) y = e.clientY - tip.offsetHeight - 10; |
| 514 | tip.style.left = x + "px"; tip.style.top = y + "px"; |
| 515 | }); |
| 516 | |
| 517 | setInterval(function () { |
| 518 | if (state && !R.on) applyAll(); |
| 519 | }, 1000); |
| 520 | |
| 521 | function tick() { |
| 522 | fetch("/api/state").then(function (r) { return r.json(); }).then(function (st) { |
| 523 | state = st; fetchedAt = Date.now(); |
| 524 | byID = {}; |
| 525 | st.tasks.forEach(function (t) { byID[t.id] = t; }); |
| 526 | suite = st.suite && st.suite.length ? st.suite : st.tasks.map(function (t) { return t.id; }); |
| 527 | $("clock").textContent = new Date(st.now).toLocaleTimeString(); |
| 528 | if (!R.on) applyAll(); |
| 529 | if (location.hash === "#replay" && !R.on) { location.hash = ""; $("play").click(); } |
| 530 | }).catch(function () { |
| 531 | $("clock").textContent = "poll failed — is the server still up?"; |
| 532 | }); |
| 533 | } |
| 534 | tick(); |
| 535 | setInterval(tick, 2000); |
| 536 | </script> |
| 537 | </body></html> |
| 538 | ` |
| 539 |