commitarena 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,394 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>commitarena report</title>
7
+ <meta name="description" content="commitarena: real bug fixes from git history replayed against LLMs in a sandbox with no network.">
8
+ <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='1' y='1' width='22' height='22' rx='6' fill='%233b5bd4'/%3E%3Ccircle cx='12' cy='12' r='3.4' fill='none' stroke='%23fff' stroke-width='2.2'/%3E%3Cpath d='M4.8 12h3.6M15.6 12h3.6' fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E">
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
12
+ <style>
13
+ :root {
14
+ --bg: #ffffff; --surface: #ffffff; --surface-2: #f7f7f9; --border: #e4e4e9;
15
+ --text: #111116; --muted: #5c5d66;
16
+ --accent: #3b5bd4; --accent-soft: rgba(59, 91, 212, .1); --tile: #3b5bd4; --tile-fg: #ffffff;
17
+ --ok: #11813c; --ok-soft: rgba(17, 129, 60, .1); --warn: #a45e00; --err: #c22826; --err-soft: rgba(194, 40, 38, .09);
18
+ --s-before: #3b5bd4; --s-after: #c2549a;
19
+ --add: rgba(17, 129, 60, .1); --del: rgba(194, 40, 38, .09);
20
+ --shadow: 0 1px 2px rgba(17, 17, 22, .04), 0 4px 16px rgba(17, 17, 22, .05);
21
+ --sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
22
+ --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
23
+ color-scheme: light;
24
+ }
25
+ @media (prefers-color-scheme: dark) {
26
+ :root:not([data-theme="light"]) {
27
+ --bg: #000000; --surface: #0d0d10; --surface-2: #141418; --border: #28282e;
28
+ --text: #f4f5f8; --muted: #a3a4ae;
29
+ --accent: #88a9ff; --accent-soft: rgba(136, 169, 255, .14);
30
+ --ok: #5fd37f; --ok-soft: rgba(95, 211, 127, .13); --warn: #f7b83d; --err: #f66d67; --err-soft: rgba(246, 109, 103, .13);
31
+ --s-before: #6684ec; --s-after: #cc5ea6;
32
+ --add: rgba(95, 211, 127, .12); --del: rgba(246, 109, 103, .12);
33
+ --shadow: none; color-scheme: dark;
34
+ }
35
+ }
36
+ :root[data-theme="dark"] {
37
+ --bg: #000000; --surface: #0d0d10; --surface-2: #141418; --border: #28282e;
38
+ --text: #f4f5f8; --muted: #a3a4ae;
39
+ --accent: #88a9ff; --accent-soft: rgba(136, 169, 255, .14);
40
+ --ok: #5fd37f; --ok-soft: rgba(95, 211, 127, .13); --warn: #f7b83d; --err: #f66d67; --err-soft: rgba(246, 109, 103, .13);
41
+ --s-before: #6684ec; --s-after: #cc5ea6;
42
+ --add: rgba(95, 211, 127, .12); --del: rgba(246, 109, 103, .12);
43
+ --shadow: none; color-scheme: dark;
44
+ }
45
+ * { box-sizing: border-box; }
46
+ html, body { margin: 0; }
47
+ body { background: var(--bg); color: var(--text); font: 15px/1.5 var(--sans); -webkit-font-smoothing: antialiased; }
48
+ .wrap { max-width: 1120px; margin: 0 auto; padding: 24px 16px 64px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 28px; min-width: 0; }
49
+ header.top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
50
+ .brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.15rem; letter-spacing: -.01em; }
51
+ .brand svg { width: 32px; height: 32px; }
52
+ .spacer { flex: 1; }
53
+ .controls { display: flex; gap: 6px; }
54
+ button.tog { font: 500 .8rem var(--sans); color: var(--text); background: var(--surface); border: 1px solid var(--border);
55
+ border-radius: 8px; padding: 6px 10px; cursor: pointer; }
56
+ button.tog:hover { border-color: var(--accent); }
57
+ button.tog:focus-visible, .cell:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
58
+ h1 { font-size: 1.6rem; line-height: 1.2; letter-spacing: -.02em; margin: 0 0 6px; }
59
+ h2 { font-size: 1.05rem; margin: 0 0 4px; letter-spacing: -.01em; }
60
+ .sub { color: var(--muted); margin: 0; }
61
+ .badge { display: inline-block; font: 600 .7rem var(--sans); text-transform: uppercase; letter-spacing: .06em;
62
+ color: var(--accent); background: var(--accent-soft); border-radius: 6px; padding: 2px 8px; vertical-align: middle; }
63
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 18px; box-shadow: var(--shadow); min-width: 0; }
64
+ .card > p.sub { margin-bottom: 14px; font-size: .88rem; }
65
+ .kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
66
+ .kpi { padding: 14px 16px; }
67
+ .kpi .v { font: 600 1.5rem var(--mono); letter-spacing: -.02em; }
68
+ .kpi .l { color: var(--muted); font-size: .8rem; }
69
+ .scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
70
+ table { border-collapse: collapse; width: 100%; font-size: .88rem; }
71
+ th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
72
+ th { color: var(--muted); font-weight: 500; font-size: .78rem; }
73
+ td.num, th.num { text-align: right; }
74
+ td.num { font-family: var(--mono); font-size: .82rem; }
75
+ tr:last-child td { border-bottom: 0; }
76
+ .rank { color: var(--muted); font-family: var(--mono); }
77
+ td.stops { color: var(--muted); font-size: .8rem; white-space: normal; min-width: 130px; max-width: 190px; }
78
+ .model { font-family: var(--mono); font-size: .82rem; }
79
+ .rate { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
80
+ .meter { width: 70px; height: 6px; border-radius: 3px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
81
+ .meter i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
82
+ /* cutoff chart */
83
+ .legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: .8rem; color: var(--muted); margin-bottom: 12px; }
84
+ .legend span { display: inline-flex; align-items: center; gap: 6px; }
85
+ .sw { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
86
+ .cgroup { display: grid; grid-template-columns: minmax(0, 220px) minmax(0, 1fr); gap: 4px 14px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--border); }
87
+ .cgroup:last-child { border-bottom: 0; }
88
+ .cgroup .name { font-family: var(--mono); font-size: .82rem; overflow: hidden; text-overflow: ellipsis; }
89
+ .cgroup .name small { display: block; font-family: var(--sans); color: var(--muted); font-size: .74rem; }
90
+ .bars { display: grid; gap: 6px; }
91
+ .bar { display: grid; grid-template-columns: minmax(0, 1fr) 92px; gap: 8px; align-items: center; }
92
+ .track { height: 12px; position: relative; border-left: 1px solid var(--border); }
93
+ .fill { height: 100%; border-radius: 0 4px 4px 0; min-width: 2px; }
94
+ .bar .lab { font: .78rem var(--mono); color: var(--text); }
95
+ .bar .lab em { color: var(--muted); font-style: normal; }
96
+ .bar[data-tip] { cursor: default; }
97
+ /* matrix */
98
+ .matrix td.task { font-family: var(--mono); font-size: .8rem; }
99
+ .matrix td.task small { display: block; font-family: var(--sans); color: var(--muted); white-space: normal; max-width: 260px; overflow-wrap: anywhere; }
100
+ .cell { display: inline-flex; align-items: center; justify-content: center; gap: 4px; min-width: 64px; padding: 4px 8px;
101
+ border-radius: 7px; font: 600 .74rem var(--sans); border: 1px solid transparent; cursor: pointer; background: none; color: inherit; }
102
+ .cell.pass { color: var(--ok); background: var(--ok-soft); }
103
+ .cell.fail { color: var(--err); background: var(--err-soft); }
104
+ .cell.sel { border-color: var(--accent); }
105
+ .cell svg { width: 12px; height: 12px; }
106
+ .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; }
107
+ /* diff */
108
+ .diffhead { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
109
+ select { font: .85rem var(--sans); color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 6px 8px; max-width: 100%; }
110
+ .diffs { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
111
+ .pane { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; min-width: 0; }
112
+ .pane h3 { margin: 0; font-size: .8rem; font-weight: 600; padding: 8px 12px; border-bottom: 1px solid var(--border); background: var(--surface-2); display: flex; gap: 8px; align-items: center; }
113
+ .pane pre { margin: 0; padding: 10px 0; font: .74rem/1.55 var(--mono); overflow: auto; max-height: 520px; }
114
+ .pane pre span { display: block; padding: 0 12px; white-space: pre; }
115
+ .pane pre .a { background: var(--add); }
116
+ .pane pre .d { background: var(--del); }
117
+ .pane pre .h { color: var(--accent); }
118
+ .pane pre .f { color: var(--muted); font-weight: 600; }
119
+ .empty { color: var(--muted); padding: 12px; font-size: .85rem; }
120
+ .note { margin: 12px 0 0; padding: 10px 12px; border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 8px; font-size: .85rem; color: var(--muted); background: var(--surface); }
121
+ .meta { color: var(--muted); font-size: .8rem; display: flex; flex-wrap: wrap; gap: 4px 14px; }
122
+ a { color: var(--accent); }
123
+ footer { color: var(--muted); font-size: .78rem; border-top: 1px solid var(--border); padding-top: 14px; }
124
+ #tip { position: fixed; pointer-events: none; z-index: 10; background: var(--surface); color: var(--text); border: 1px solid var(--border);
125
+ border-radius: 8px; padding: 6px 9px; font-size: .78rem; box-shadow: 0 4px 16px rgba(0,0,0,.12); display: none; max-width: 260px; }
126
+ @media (max-width: 760px) {
127
+ .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
128
+ .diffs { grid-template-columns: minmax(0, 1fr); }
129
+ .cgroup { grid-template-columns: minmax(0, 1fr); }
130
+ .matrix .mdate { display: none; }
131
+ .matrix td.task small { display: none; }
132
+ .matrix th, .matrix td { padding: 8px 6px; }
133
+ .cell { min-width: 0; padding: 4px 6px; }
134
+ .cell .ct { display: none; }
135
+ h1 { font-size: 1.3rem; }
136
+ .card { padding: 14px; }
137
+ }
138
+ </style>
139
+ </head>
140
+ <body>
141
+ <svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
142
+ <defs>
143
+ <symbol id="g-commit" viewBox="0 0 24 24">
144
+ <rect x="1" y="1" width="22" height="22" rx="6" fill="var(--tile)"/>
145
+ <circle cx="12" cy="12" r="3.4" fill="none" stroke="var(--tile-fg)" stroke-width="2.2"/>
146
+ <path d="M4.8 12h3.6M15.6 12h3.6" fill="none" stroke="var(--tile-fg)" stroke-width="2.2" stroke-linecap="round"/>
147
+ </symbol>
148
+ <symbol id="i-ok" viewBox="0 0 12 12"><path d="M2.5 6.2l2.3 2.3 4.7-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></symbol>
149
+ <symbol id="i-x" viewBox="0 0 12 12"><path d="M3 3l6 6M9 3l-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></symbol>
150
+ </defs>
151
+ </svg>
152
+ <div class="wrap">
153
+ <header class="top">
154
+ <div class="brand"><svg aria-hidden="true"><use href="#g-commit"/></svg>commitarena</div>
155
+ <div class="spacer"></div>
156
+ <div class="controls">
157
+ <button class="tog" id="lang" type="button" aria-label="Language">ES</button>
158
+ <button class="tog" id="theme" type="button" aria-label="Theme"></button>
159
+ </div>
160
+ </header>
161
+
162
+ <section>
163
+ <h1 data-i="title"></h1>
164
+ <p class="sub" data-i="lead"></p>
165
+ <div class="meta" id="meta" style="margin-top:10px"></div>
166
+ <p class="note" id="note" style="display:none"></p>
167
+ </section>
168
+
169
+ <section class="kpis" id="kpis"></section>
170
+
171
+ <section class="card">
172
+ <h2 data-i="board"></h2>
173
+ <p class="sub" data-i="boardSub"></p>
174
+ <div class="scroll"><table id="board"></table></div>
175
+ </section>
176
+
177
+ <section class="card">
178
+ <h2 data-i="cut"></h2>
179
+ <p class="sub" data-i="cutSub"></p>
180
+ <div class="legend">
181
+ <span><i class="sw" style="background:var(--s-before)"></i><b data-i="before"></b></span>
182
+ <span><i class="sw" style="background:var(--s-after)"></i><b data-i="after"></b></span>
183
+ </div>
184
+ <div id="cut"></div>
185
+ </section>
186
+
187
+ <section class="card">
188
+ <h2 data-i="matrix"></h2>
189
+ <p class="sub" data-i="matrixSub"></p>
190
+ <div class="scroll"><table class="matrix" id="matrix"></table></div>
191
+ </section>
192
+
193
+ <section class="card" id="diffcard">
194
+ <h2 data-i="diff"></h2>
195
+ <p class="sub" data-i="diffSub"></p>
196
+ <div class="diffhead">
197
+ <select id="selTask" aria-label="Task"></select>
198
+ <select id="selModel" aria-label="Model"></select>
199
+ <span class="meta" id="diffmeta"></span>
200
+ </div>
201
+ <div class="diffs">
202
+ <div class="pane"><h3><span data-i="modelPatch"></span><span id="mname" class="model"></span></h3><pre id="pModel"></pre></div>
203
+ <div class="pane"><h3><span data-i="humanPatch"></span><a id="clink" target="_blank" rel="noopener"></a></h3><pre id="pGold"></pre></div>
204
+ </div>
205
+ </section>
206
+
207
+ <footer id="foot"></footer>
208
+ </div>
209
+ <div id="tip" role="tooltip"></div>
210
+ <script type="application/json" id="data">/*__DATA__*/{}</script>
211
+ <script>
212
+ (function () {
213
+ "use strict";
214
+ var D = JSON.parse(document.getElementById("data").textContent);
215
+ var T = {
216
+ en: {
217
+ title: "Which model fixes your real bugs?",
218
+ lead: "Every task is a real bug fix from this repository's git history. Each model gets the failing tests, the same minimal agent loop with one bash tool, and a container with no network. The original tests are restored before scoring.",
219
+ board: "Leaderboard", boardSub: "Pass = every fail-to-pass test passes and no pass-to-pass test breaks. Cost is what the provider reports or tokens times list price; local models cost 0.",
220
+ cut: "Solved or remembered?", cutSub: "Pass rate on fixes committed before the model's training cutoff (could be memorized) versus after it (cannot be). Cutoff dates are configurable; by default the model's release date is used as an upper bound.",
221
+ before: "Before cutoff", after: "After cutoff",
222
+ matrix: "Task x model", matrixSub: "Tasks sorted by commit date. Select a cell to compare the model's diff with the human fix.",
223
+ diff: "Model diff next to the human fix", diffSub: "Only changes to non-test files are scored; edits to tests are discarded.",
224
+ modelPatch: "Model", humanPatch: "Human fix",
225
+ m: "Model", pass: "Pass", rate: "Rate", cost: "Cost", tokens: "Tokens", time: "Avg time", steps: "Avg steps", cutoff: "Cutoff",
226
+ task: "Task", date: "Date", tasks: "tasks", models: "models", best: "best pass rate", spent: "total cost (USD)",
227
+ none: "No changes to non-test files.", nores: "This pair was not run.", recorded: "Recorded run",
228
+ ok: "pass", fail: "fail", files: "files", n: "n",
229
+ genBy: "Generated by commitarena", hw: "Hardware", ver: "Version", ran: "Run date", sandbox: "Sandbox",
230
+ stop: "stop", broken: "broken", f2p: "fail to pass", unknown: "no cutoff set", ended: "Ended by",
231
+ s_finished: "finished", s_max_steps: "step limit", s_time_limit: "time limit", s_no_action: "no tool call",
232
+ s_budget_exceeded: "budget", s_model_error: "model error", s_unknown: "unknown"
233
+ },
234
+ es: {
235
+ title: "¿Qué modelo arregla tus bugs reales?",
236
+ lead: "Cada tarea es un fix real del historial de git de este repositorio. Cada modelo recibe los tests que fallan, el mismo bucle de agente mínimo con una sola herramienta bash y un contenedor sin red. Los tests originales se restauran antes de puntuar.",
237
+ board: "Clasificación", boardSub: "Acierto = pasan todos los tests fail-to-pass y no se rompe ningún pass-to-pass. El coste es el que informa el proveedor o tokens por precio de lista; los modelos locales cuestan 0.",
238
+ cut: "¿Lo resuelve o lo recuerda?", cutSub: "Acierto en fixes anteriores a la fecha de corte del modelo (podría haberlos memorizado) frente a posteriores (no puede). Las fechas se configuran; por defecto se usa la fecha de lanzamiento como cota superior.",
239
+ before: "Antes del corte", after: "Después del corte",
240
+ matrix: "Tarea x modelo", matrixSub: "Tareas ordenadas por fecha del commit. Elige una celda para comparar el diff del modelo con el fix humano.",
241
+ diff: "Diff del modelo junto al fix humano", diffSub: "Solo se puntúan los cambios en ficheros que no son de test; lo que toque en los tests se descarta.",
242
+ modelPatch: "Modelo", humanPatch: "Fix humano",
243
+ m: "Modelo", pass: "Aciertos", rate: "Tasa", cost: "Coste", tokens: "Tokens", time: "Tiempo medio", steps: "Pasos medios", cutoff: "Corte",
244
+ task: "Tarea", date: "Fecha", tasks: "tareas", models: "modelos", best: "mejor tasa de acierto", spent: "coste total (USD)",
245
+ none: "Sin cambios en ficheros que no son de test.", nores: "Esta pareja no se ejecutó.", recorded: "Ejecución grabada",
246
+ ok: "pasa", fail: "falla", files: "ficheros", n: "n",
247
+ genBy: "Generado con commitarena", hw: "Hardware", ver: "Versión", ran: "Fecha", sandbox: "Sandbox",
248
+ stop: "parada", broken: "rotos", f2p: "fail to pass", unknown: "sin fecha de corte", ended: "Terminó por",
249
+ s_finished: "terminado", s_max_steps: "límite de pasos", s_time_limit: "límite de tiempo", s_no_action: "sin llamar a la herramienta",
250
+ s_budget_exceeded: "presupuesto", s_model_error: "error del modelo", s_unknown: "desconocido"
251
+ }
252
+ };
253
+ var lang = "en";
254
+ try { lang = localStorage.getItem("ca-lang") || ((navigator.language || "").slice(0, 2) === "es" ? "es" : "en"); } catch (e) {}
255
+ var qs = new URLSearchParams(location.search);
256
+ if (qs.get("lang")) lang = qs.get("lang");
257
+ if (!T[lang]) lang = "en";
258
+ function t(k) { return T[lang][k] || k; }
259
+ function esc(s) { return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { return {"&":"&amp;","<":"&lt;",">":"&gt;","\"":"&quot;","'":"&#39;"}[c]; }); }
260
+ function pct(x) { return Math.round(x * 100) + "%"; }
261
+ function fmtNum(n) { return Number(n || 0).toLocaleString(lang === "es" ? "es-ES" : "en-US"); }
262
+ function money(x) { return "$" + Number(x || 0).toFixed(x < 0.01 && x > 0 ? 4 : 2); }
263
+ function short(id) { var p = id.split("-"); return p[p.length - 1]; }
264
+ var R = {};
265
+ D.results.forEach(function (r) { R[r.task_id + "|" + r.model] = r; });
266
+ var sel = { task: null, model: null };
267
+ var firstPass = D.results.filter(function (r) { return r.passed; })[0] || D.results[0];
268
+ if (firstPass) { sel.task = firstPass.task_id; sel.model = firstPass.model; }
269
+
270
+ // theme
271
+ var root = document.documentElement;
272
+ function curTheme() { var a = root.getAttribute("data-theme"); if (a) return a; return matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; }
273
+ try { var st = localStorage.getItem("ca-theme"); if (st) root.setAttribute("data-theme", st); } catch (e) {}
274
+ if (qs.get("theme") === "dark" || qs.get("theme") === "light") root.setAttribute("data-theme", qs.get("theme"));
275
+ var themeBtn = document.getElementById("theme");
276
+ function themeLabel() { themeBtn.textContent = curTheme() === "dark" ? (lang === "es" ? "Claro" : "Light") : (lang === "es" ? "Oscuro" : "Dark"); }
277
+ themeBtn.onclick = function () { var n = curTheme() === "dark" ? "light" : "dark"; root.setAttribute("data-theme", n); try { localStorage.setItem("ca-theme", n); } catch (e) {} themeLabel(); };
278
+ document.getElementById("lang").onclick = function () { lang = lang === "en" ? "es" : "en"; try { localStorage.setItem("ca-lang", lang); } catch (e) {} render(); };
279
+
280
+ var tip = document.getElementById("tip");
281
+ document.addEventListener("mousemove", function (e) {
282
+ var el = e.target.closest ? e.target.closest("[data-tip]") : null;
283
+ if (!el) { tip.style.display = "none"; return; }
284
+ tip.innerHTML = el.getAttribute("data-tip"); tip.style.display = "block";
285
+ var x = Math.min(e.clientX + 14, innerWidth - tip.offsetWidth - 8), y = e.clientY + 16;
286
+ if (y + tip.offsetHeight > innerHeight) y = e.clientY - tip.offsetHeight - 10;
287
+ tip.style.left = x + "px"; tip.style.top = y + "px";
288
+ });
289
+
290
+ function render() {
291
+ root.lang = lang;
292
+ document.getElementById("lang").textContent = lang === "en" ? "ES" : "EN";
293
+ themeLabel();
294
+ document.querySelectorAll("[data-i]").forEach(function (el) { el.textContent = t(el.getAttribute("data-i")); });
295
+ var m = D.meta || {};
296
+ var sb = m.sandbox || {};
297
+ var metaBits = [];
298
+ if (m.recorded) metaBits.push('<span class="badge">' + esc(t("recorded")) + "</span>");
299
+ if (m.repo) metaBits.push("<span>" + esc(m.repo) + (m.repo_license ? " (" + esc(m.repo_license) + ")" : "") + "</span>");
300
+ if (m.updated_at) metaBits.push("<span>" + esc(t("ran")) + ": " + esc(m.updated_at.slice(0, 10)) + "</span>");
301
+ if (m.version) metaBits.push("<span>commitarena " + esc(m.version) + "</span>");
302
+ if (m.hardware) metaBits.push("<span>" + esc(t("hw")) + ": " + esc(m.hardware) + "</span>");
303
+ if (sb.network) metaBits.push("<span>" + esc(t("sandbox")) + ": --network " + esc(sb.network) + ", " + esc(sb.cpus) + " CPU, " + esc(sb.memory) + "</span>");
304
+ document.getElementById("meta").innerHTML = metaBits.join("");
305
+ var note = m["note_" + lang] || m.note || "";
306
+ var noteEl = document.getElementById("note");
307
+ noteEl.textContent = note; noteEl.style.display = note ? "block" : "none";
308
+
309
+ var best = D.board.length ? D.board[0] : null;
310
+ var total = D.board.reduce(function (a, b) { return a + b.cost; }, 0);
311
+ document.getElementById("kpis").innerHTML = [
312
+ [D.tasks.length, t("tasks")], [D.models.length, t("models")],
313
+ [best ? pct(best.rate) : "-", t("best") + (best ? " · " + best.model : "")], [money(total), t("spent")]
314
+ ].map(function (k) { return '<div class="card kpi"><div class="v">' + esc(k[0]) + '</div><div class="l">' + esc(k[1]) + "</div></div>"; }).join("");
315
+
316
+ var h = "<thead><tr><th>#</th><th>" + t("m") + '</th><th class="num">' + t("pass") + '</th><th class="num">' + t("rate") + '</th><th class="num">' + t("cost") + '</th><th class="num">' + t("tokens") + '</th><th class="num">' + t("time") + '</th><th class="num">' + t("steps") + "</th><th>" + t("ended") + "</th><th>" + t("cutoff") + "</th></tr></thead><tbody>";
317
+ D.board.forEach(function (r, i) {
318
+ h += '<tr><td class="rank">' + (i + 1) + '</td><td class="model">' + esc(r.model) + '</td><td class="num">' + r.passed + "/" + r.total +
319
+ '</td><td class="num"><div class="rate"><span>' + pct(r.rate) + '</span><span class="meter"><i style="width:' + (r.rate * 100) + '%"></i></span></div></td><td class="num">' + money(r.cost) +
320
+ '</td><td class="num">' + fmtNum(r.tokens) + '</td><td class="num">' + Math.round(r.avg_time) + ' s</td><td class="num">' + r.avg_steps.toFixed(1) + '</td><td class="stops">' + Object.keys(r.stops || {}).sort(function (a, b) { return r.stops[b] - r.stops[a]; }).map(function (k) { return esc(t("s_" + k)) + " " + r.stops[k]; }).join(", ") + "</td><td>" + esc(r.cutoff || t("unknown")) + "</td></tr>";
321
+ });
322
+ document.getElementById("board").innerHTML = h + "</tbody>";
323
+
324
+ var c = "";
325
+ D.board.forEach(function (r) {
326
+ c += '<div class="cgroup"><div class="name">' + esc(r.model) + "<small>" + t("cutoff") + ": " + esc(r.cutoff || t("unknown")) + '</small></div><div class="bars">';
327
+ [["before", "--s-before"], ["after", "--s-after"]].forEach(function (s) {
328
+ var v = r[s[0]], rate = v.total ? v.passed / v.total : 0;
329
+ var tipTxt = "<b>" + esc(r.model) + "</b><br>" + esc(t(s[0])) + ": " + v.passed + "/" + v.total + (v.total ? " (" + pct(rate) + ")" : "");
330
+ c += '<div class="bar" data-tip="' + esc(tipTxt) + '"><div class="track"><div class="fill" style="width:' + (v.total ? Math.max(rate * 100, 0.6) : 0) + "%;background:var(" + s[1] + ')"></div></div><div class="lab">' +
331
+ (v.total ? pct(rate) + " <em>" + v.passed + "/" + v.total + "</em>" : "<em>n = 0</em>") + "</div></div>";
332
+ });
333
+ c += "</div></div>";
334
+ });
335
+ document.getElementById("cut").innerHTML = c;
336
+
337
+ var mx = "<thead><tr><th>" + t("task") + '</th><th class="mdate">' + t("date") + "</th>";
338
+ D.models.forEach(function (mm) { mx += '<th title="' + esc(mm) + '">' + esc(mm.split("/").slice(1).join("/") || mm) + "</th>"; });
339
+ mx += "</tr></thead><tbody>";
340
+ D.tasks.forEach(function (tk) {
341
+ mx += '<tr><td class="task">' + esc(short(tk.id)) + "<small>" + esc((tk.files || []).join(", ")) + "</small></td><td class=\"mdate\">" + esc(tk.date.slice(0, 10)) + "</td>";
342
+ D.models.forEach(function (mm) {
343
+ var r = R[tk.id + "|" + mm];
344
+ var cut = D.cutoffs[mm];
345
+ var side = cut ? (tk.date.slice(0, 10) < cut ? "before" : "after") : null;
346
+ var dot = side ? '<i class="dot" style="background:var(--s-' + side + ')" title="' + esc(t(side)) + '"></i>' : "";
347
+ if (!r) { mx += "<td>" + dot + '<span class="empty">-</span></td>'; return; }
348
+ var tipTxt = "<b>" + esc(r.passed ? t("ok") : t("fail")) + "</b> · " + r.fail_to_pass_passed + "/" + r.fail_to_pass_total + " " + t("f2p") + ", " + r.pass_to_pass_failed + " " + t("broken") + "<br>" + r.steps + " " + t("steps").toLowerCase() + ", " + Math.round(r.wall_time_s) + " s, " + money(r.cost_usd) + "<br>" + t("stop") + ": " + esc(r.stop_reason);
349
+ var isSel = sel.task === tk.id && sel.model === mm;
350
+ mx += "<td>" + dot + '<button type="button" class="cell ' + (r.passed ? "pass" : "fail") + (isSel ? " sel" : "") + '" data-t="' + esc(tk.id) + '" data-m="' + esc(mm) + '" data-tip="' + esc(tipTxt) + '"><svg><use href="#' + (r.passed ? "i-ok" : "i-x") + '"/></svg><span class="ct">' + esc(r.passed ? t("ok") : t("fail")) + "</span></button></td>";
351
+ });
352
+ mx += "</tr>";
353
+ });
354
+ document.getElementById("matrix").innerHTML = mx + "</tbody>";
355
+ document.querySelectorAll(".cell").forEach(function (b) {
356
+ b.onclick = function () { sel.task = b.getAttribute("data-t"); sel.model = b.getAttribute("data-m"); render(); document.getElementById("diffcard").scrollIntoView({ behavior: "smooth", block: "start" }); };
357
+ });
358
+
359
+ var sT = document.getElementById("selTask"), sM = document.getElementById("selModel");
360
+ sT.innerHTML = D.tasks.map(function (tk) { return '<option value="' + esc(tk.id) + '"' + (tk.id === sel.task ? " selected" : "") + ">" + esc(short(tk.id) + " · " + tk.date.slice(0, 10)) + "</option>"; }).join("");
361
+ sM.innerHTML = D.models.map(function (mm) { return '<option value="' + esc(mm) + '"' + (mm === sel.model ? " selected" : "") + ">" + esc(mm) + "</option>"; }).join("");
362
+ sT.onchange = function () { sel.task = sT.value; render(); };
363
+ sM.onchange = function () { sel.model = sM.value; render(); };
364
+ renderDiff();
365
+
366
+ document.getElementById("foot").innerHTML = esc(t("genBy")) + " " + esc(D.meta.report_version || "") + ' · <a href="https://github.com/diegoperezg7/commitarena">github.com/diegoperezg7/commitarena</a>';
367
+ }
368
+
369
+ function diffHtml(text) {
370
+ if (!text) return '<div class="empty">' + esc(t("none")) + "</div>";
371
+ return text.split("\n").map(function (l) {
372
+ var c = "";
373
+ if (l.indexOf("diff --git") === 0 || l.indexOf("+++") === 0 || l.indexOf("---") === 0) c = "f";
374
+ else if (l.indexOf("@@") === 0) c = "h";
375
+ else if (l[0] === "+") c = "a";
376
+ else if (l[0] === "-") c = "d";
377
+ return '<span class="' + c + '">' + (esc(l) || " ") + "</span>";
378
+ }).join("");
379
+ }
380
+ function renderDiff() {
381
+ var tk = D.tasks.filter(function (x) { return x.id === sel.task; })[0];
382
+ var r = R[sel.task + "|" + sel.model];
383
+ document.getElementById("mname").textContent = sel.model || "";
384
+ document.getElementById("pModel").innerHTML = r ? diffHtml(r.patch) : '<div class="empty">' + esc(t("nores")) + "</div>";
385
+ document.getElementById("pGold").innerHTML = tk ? diffHtml(tk.gold) : "";
386
+ var cl = document.getElementById("clink");
387
+ if (tk && tk.url) { cl.href = tk.url; cl.textContent = tk.commit; } else { cl.removeAttribute("href"); cl.textContent = tk ? tk.commit : ""; }
388
+ document.getElementById("diffmeta").innerHTML = r ? ('<span class="cell ' + (r.passed ? "pass" : "fail") + '" style="cursor:default"><svg><use href="#' + (r.passed ? "i-ok" : "i-x") + '"/></svg>' + esc(r.passed ? t("ok") : t("fail")) + "</span>") : "";
389
+ }
390
+ render();
391
+ })();
392
+ </script>
393
+ </body>
394
+ </html>