openvibers/openvibe.bench Commits

Bench B2: the results pages (benchmarks, runs, compare, models, value chart) (#27)

OP OpenVibers committed 14 days ago · 71140fcf15 · 11 files, +894 −24

The pages people read:
- /benchmarks: search and family filters;
- /benchmarks/:id: rankings by score, value, speed or steadiness, a value chart with the frontier marked, the tasks
  (holdout hidden), the latest runs;
- /runs/:id: Geekbench style, with the environment, the score, where it sits among the model's runs, each task and
  its live outputs from the render origin;
- /compare: up to four runs, the best in each row marked, outputs side by side;
- /models/:model: medians, spread, cost and time per public benchmark.
The home now leads with what models make. The showcase is restyled. All of it is server-rendered and escaped, and
works without JavaScript.

Fixes found on the way:
- Lists showed unlisted benchmarks to everyone. Lists are now public-only (?mine=1 adds your own). A private
  benchmark, once that visibility exists, answers only its owner and the executor. A model's page shows public
  benchmarks' runs only.
- rankings(): `since` without `harness` used $4 with three parameters (an SQL error), and the harness filter
  compared the name with a JSON array, so it never matched.
- The showcase stats read c.medemon_ms, so the median time was always null.
- Render responses send no-transform, so Cloudflare stops injecting its beacon into the frames (their CSP refused it:
  dozens of console errors per showcase page).
- "Run it yourself" shows when CODES_BENCH_RUN_URL is set (stage B3); until then the page points at the documented
  publish API.

Changes

ddiff --git a/public/css/bench.css b/public/css/bench.css
iindex 4ed2fdc..33dde8c 100644
---- a/public/css/bench.css
++++ b/public/css/bench.css
@@@ -61,3 +61,193 @@ html, body { margin: 0; background: var(--bg-primary, #0a0f1c); color: var(--tex
.bm-kind-link:hover { text-decoration: underline; }
.bm-kind-link:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; border-radius: 4px; }
.bm-kind-meta { font-size: .82rem; color: var(--text-muted, #7386a3); }
++
++/* ---- Results pages (B2): benchmarks, a benchmark, a run, compare, a model. Classes start with br-. ---------- */
++.br-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
++.br-mono, .br-mono-h { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
++.br-mono-h { font-size: clamp(1.5rem, 4.4vw, 2.4rem) !important; letter-spacing: -.01em !important; }
++.br-fine { color: var(--text-muted, #7386a3); font-size: .86rem; line-height: 1.5; margin: 0; }
++.br-chip { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px; font-size: .76rem; font-weight: 700; background: color-mix(in srgb, var(--room-accent) 14%, transparent); color: var(--room-strong); white-space: nowrap; }
++.br-crumbs { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding-top: clamp(16px, 3vw, 28px); font-size: .86rem; color: var(--text-muted, #7386a3); min-width: 0; }
++.br-crumbs a { color: var(--text-secondary, #96a7c2); text-decoration: none; }
++.br-crumbs a:hover { color: var(--room-strong); }
++.br-crumbs > span:last-child { color: var(--text-primary, #e6edf7); overflow-wrap: anywhere; }
++
++/* The page head: the title on the left, the action (or the score) on the right, stacked on phones */
++.br-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 18px 32px; padding: 14px 0 4px; }
++.br-head-main { flex: 1 1 420px; min-width: 0; }
++.br-head-main h1 { margin: 6px 0 0; font-size: clamp(1.8rem, 5vw, 2.9rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.06; overflow-wrap: anywhere; text-wrap: balance; }
++.br-head-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; margin: 0; color: var(--text-muted, #7386a3); font-size: .86rem; }
++.br-head-meta a { color: var(--room-strong); }
++.br-head-act { flex: 0 1 300px; display: grid; gap: 8px; justify-items: start; }
++
++/* Facts, environment and totals: label over value, in a wrapping grid */
++.br-facts, .br-env, .br-totals { display: grid; gap: 10px; margin: 22px 0 0; }
++.br-facts { grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); }
++.br-env { grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); margin-top: 12px; }
++.br-facts > div, .br-env > div { padding: 12px 14px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); min-width: 0; }
++.br-facts dt, .br-env dt, .br-totals dt { font-size: .7rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted, #7386a3); }
++.br-facts dd, .br-env dd, .br-totals dd { margin: 4px 0 0; font-weight: 650; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
++.br-env dd a { color: var(--room-strong); }
++.br-totals { grid-template-columns: repeat(3, auto); justify-content: start; gap: 4px 22px; margin-top: 12px; }
++
++/* A score out of 100, its thin bar under it */
++.br-score { display: inline-flex; flex-wrap: wrap; align-items: baseline; column-gap: 2px; min-width: 92px; font-variant-numeric: tabular-nums; }
++.br-score-n { font-weight: 800; font-size: 1rem; }
++.br-score-of { font-size: .74rem; color: var(--text-muted, #7386a3); }
++.br-bar { flex: 0 0 100%; display: block; height: 4px; margin-top: 4px; border-radius: 999px; background: color-mix(in srgb, var(--text-muted, #7386a3) 22%, transparent); overflow: hidden; }
++.br-bar > span { display: block; height: 100%; border-radius: inherit; background: var(--room-accent); }
++.br-score.is-big { min-width: 200px; }
++.br-score.is-big .br-score-n { font-size: clamp(3rem, 9vw, 4.4rem); line-height: .95; letter-spacing: -.04em; }
++.br-score.is-big .br-score-of { font-size: 1rem; }
++.br-score.is-big .br-bar { height: 6px; margin-top: 10px; }
++.br-head-score { flex: 0 1 auto; }
++
++/* Verified / submitted */
++.br-prov { display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border-radius: 999px; font-size: .74rem; font-weight: 750; white-space: nowrap; }
++.br-prov.is-verified { background: color-mix(in srgb, var(--success, #22c55e) 15%, transparent); color: var(--success, #22c55e); }
++.br-prov.is-verified::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
++.br-prov.is-submitted { border: 1px solid var(--border, #1f2d47); color: var(--text-muted, #7386a3); }
++
++/* Rank-by tabs: links, so every metric has its own URL */
++.br-tabs { display: flex; flex-wrap: wrap; gap: 4px; padding: 3px; border-radius: 999px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
++.br-tab { display: inline-flex; align-items: center; min-height: 34px; padding: 0 13px; border-radius: 999px; font-size: .86rem; font-weight: 700; color: var(--text-secondary, #96a7c2) !important; text-decoration: none; }
++.br-tab:hover { color: var(--text-primary, #e6edf7) !important; }
++.br-tab[aria-current="page"] { background: var(--room-fill); color: var(--room-on) !important; }
++.br-tab:focus-visible, .br-filter:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; }
++
++/* The value chart */
++.br-chart { margin: 4px 0 16px; padding: 14px 14px 10px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
++.br-chart svg { display: block; width: 100%; height: auto; overflow: visible; }
++.br-chart figcaption { margin-top: 6px; font-size: .82rem; color: var(--text-muted, #7386a3); }
++.br-grid { stroke: var(--border, #1f2d47); stroke-width: 1; }
++.br-axis { fill: var(--text-muted, #7386a3); font-size: 11px; font-variant-numeric: tabular-nums; }
++.br-chart-empty { fill: var(--text-secondary, #96a7c2); font-size: 14px; font-weight: 600; }
++.br-front { fill: none; stroke: var(--room-accent); stroke-width: 2; stroke-dasharray: 5 4; opacity: .8; }
++.br-dot circle { fill: var(--bg-card, #0f1729); stroke: var(--text-secondary, #96a7c2); stroke-width: 2; }
++.br-dot text { fill: var(--text-secondary, #96a7c2); font-size: 12px; font-weight: 600; paint-order: stroke; stroke: var(--bg-card, #0f1729); stroke-width: 4px; stroke-linejoin: round; }
++.br-dot.is-front circle { fill: var(--room-accent); stroke: var(--room-accent); }
++.br-dot.is-front text { fill: var(--text-primary, #e6edf7); font-weight: 750; }
++
++/* Tables: scroll inside their own box on phones, never the page */
++.br-table-wrap { overflow-x: auto; border: 1px solid var(--border, #1f2d47); border-radius: 16px; background: var(--bg-card, #0f1729); }
++.br-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
++.br-table th, .br-table td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--border, #1f2d47); vertical-align: middle; }
++.br-table tbody tr:last-child > * { border-bottom: 0; }
++.br-table thead th { font-size: .7rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--text-muted, #7386a3); white-space: nowrap; }
++.br-table tbody th { font-weight: 650; color: var(--text-secondary, #96a7c2); white-space: nowrap; }
++.br-num { text-align: right !important; font-variant-numeric: tabular-nums; white-space: nowrap; }
++.br-rank { width: 2.5em; color: var(--text-muted, #7386a3); font-variant-numeric: tabular-nums; }
++.br-model { font-weight: 750; color: var(--text-primary, #e6edf7) !important; text-decoration: none; overflow-wrap: anywhere; }
++.br-model:hover { color: var(--room-strong) !important; }
++.br-table td.is-best { color: var(--room-strong); font-weight: 800; }
++.br-table a { color: var(--room-strong); }
++
++/* Run lists */
++.br-runs { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
++.br-run { display: flex; align-items: center; gap: 10px; padding: 4px 12px 4px 4px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); min-width: 0; }
++.br-run:hover { border-color: color-mix(in srgb, var(--room-accent) 55%, var(--border, #1f2d47)); }
++.br-run-link { flex: 1; display: flex; align-items: center; justify-content: space-between; gap: 14px; min-height: 52px; padding: 6px 10px; color: inherit !important; text-decoration: none; min-width: 0; }
++.br-run-main { display: grid; gap: 2px; min-width: 0; }
++.br-run-model { font-weight: 750; overflow-wrap: anywhere; }
++.br-run-sub { font-size: .82rem; color: var(--text-muted, #7386a3); overflow-wrap: anywhere; }
++
++/* Tasks */
++.br-tasks, .br-ptasks { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
++.br-task { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: start; padding: 14px; border-radius: 14px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); }
++.br-task p { margin: 0; line-height: 1.55; color: var(--text-secondary, #96a7c2); }
++.br-task.is-hidden { border-style: dashed; background: transparent; }
++.br-task-id { display: inline-flex; padding: 2px 8px; border-radius: 8px; background: var(--bg-hover, rgba(255,255,255,.05)); font-size: .78rem; font-weight: 700; color: var(--text-primary, #e6edf7); white-space: nowrap; margin: 0; }
++.br-ptask { display: grid; gap: 10px; padding: 14px; border-radius: 16px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); min-width: 0; }
++.br-ptask-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
++.br-ptask-meta { margin-left: auto; font-size: .84rem; color: var(--text-muted, #7386a3); font-variant-numeric: tabular-nums; }
++.br-ptask-prompt { margin: 0; color: var(--text-secondary, #96a7c2); line-height: 1.55; }
++
++/* Outputs: live frames from the render origin */
++.br-outs { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
++.br-out { margin: 0; display: grid; gap: 6px; min-width: 0; }
++.br-out-frame, .br-out-img { width: 100%; max-width: 100%; aspect-ratio: 4 / 3; border: 1px solid var(--border, #1f2d47); border-radius: 12px; background: #fff; display: block; }
++.br-out-img { object-fit: contain; background: var(--bg-primary, #0a0f1c); }
++.br-out figcaption { font-size: .76rem; color: var(--text-muted, #7386a3); }
++.br-out-link { font-size: .86rem; overflow-wrap: anywhere; color: var(--room-strong); }
++
++/* Where a run sits among its model's runs */
++.br-pct { display: grid; gap: 8px; margin-top: 18px; padding: 14px 16px; border-radius: 16px; background: color-mix(in srgb, var(--room-accent) 8%, var(--bg-card, #0f1729)); border: 1px solid color-mix(in srgb, var(--room-accent) 24%, transparent); }
++.br-pct p { margin: 0; line-height: 1.5; }
++.br-pct-strip { position: relative; height: 8px; border-radius: 999px; background: linear-gradient(90deg, color-mix(in srgb, var(--text-muted, #7386a3) 25%, transparent), color-mix(in srgb, var(--room-accent) 45%, transparent)); }
++.br-pct-strip > span { position: absolute; top: 50%; width: 16px; height: 16px; margin: -8px 0 0 -8px; border-radius: 50%; background: var(--room-accent); border: 3px solid var(--bg-card, #0f1729); }
++
++/* Benchmark cards */
++.br-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
++.br-card { min-width: 0; }
++.br-card-link { height: 100%; display: grid; align-content: start; gap: 8px; padding: 16px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); color: inherit !important; text-decoration: none; transition: border-color .18s ease, transform .18s ease; }
++.br-card-link:hover { border-color: color-mix(in srgb, var(--room-accent) 60%, var(--border, #1f2d47)); transform: translateY(-1px); }
++.br-card-link:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 2px; }
++.br-card-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
++.br-card-v { font-size: .76rem; color: var(--text-muted, #7386a3); font-variant-numeric: tabular-nums; }
++.br-card-title { font-size: 1.08rem; font-weight: 800; letter-spacing: -.01em; }
++.br-card-desc { color: var(--text-secondary, #96a7c2); font-size: .9rem; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
++.br-card-foot { font-size: .8rem; color: var(--text-muted, #7386a3); }
++@media (prefers-reduced-motion: reduce) { .br-card-link { transition: none; } .br-card-link:hover { transform: none; } }
++
++/* Search and filters */
++.br-search { display: flex; gap: 8px; max-width: 560px; }
++.br-search input { flex: 1; min-width: 0; min-height: 44px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); background: var(--bg-input, var(--bg-card, #0f1729)); color: var(--text-primary, #e6edf7); font: inherit; }
++.br-search input:focus-visible { outline: 2px solid var(--room-accent); outline-offset: 1px; }
++.br-search .bm-btn { min-height: 44px; }
++.br-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 14px 0 18px; }
++.br-filter { display: inline-flex; align-items: center; min-height: 36px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2) !important; text-decoration: none; font-size: .88rem; font-weight: 650; text-transform: capitalize; }
++.br-filter[aria-current="page"] { background: var(--room-fill); border-color: transparent; color: var(--room-on) !important; }
++
++/* Empty states: one line of what, one line of why, one way forward */
++.br-empty { display: grid; justify-items: start; gap: 6px; padding: 18px; border-radius: 16px; border: 1px dashed var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); }
++.br-empty p { margin: 0; line-height: 1.5; }
++.br-empty-t { font-weight: 750; color: var(--text-primary, #e6edf7); }
++.br-api { margin-top: 28px; }
++.br-again .bm-actions { margin-top: 10px; }
++
++/* Compare */
++.br-cmp thead th { text-transform: none; letter-spacing: 0; font-size: .92rem; color: var(--text-primary, #e6edf7); vertical-align: top; }
++.br-cmp thead th a { font-weight: 800; }
++.br-cmp-sub { display: block; margin: 2px 0 6px; font-size: .78rem; font-weight: 500; color: var(--text-muted, #7386a3); }
++.br-cmp-row { display: grid; gap: 8px; margin-top: 14px; }
++.br-cmp-outs { display: grid; gap: 10px; grid-template-columns: repeat(var(--n, 2), minmax(0, 1fr)); }
++.br-cmp-out { min-width: 0; }
++@media (max-width: 720px) { .br-cmp-outs { grid-template-columns: minmax(0, 1fr); } }
++
++/* ---- The showcase: two models, one prompt, live frames side by side. Classes start with sc-. ---------------- */
++.sc-showcase { max-width: 1180px; margin: 0 auto; padding: clamp(20px, 4vw, 40px) 16px 64px; }
++.sc-showcase > h1 { margin: 0; font-size: clamp(1.8rem, 5vw, 2.8rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.08; text-wrap: balance; }
++.sc-lede { margin: 10px 0 24px; max-width: 680px; color: var(--text-secondary, #96a7c2); line-height: 1.55; }
++.sc-pairs { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 520px), 1fr)); }
++.sc-pair { display: grid; gap: 12px; padding: 14px; border-radius: 20px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); min-width: 0; }
++.sc-pair-prompt { margin: 0; font-size: .98rem; font-weight: 650; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
++.sc-pair-slots, .sc-round-panes { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
++.sc-round-panes { gap: 14px; margin-top: 16px; }
++@media (max-width: 560px) { .sc-pair-slots, .sc-round-panes { grid-template-columns: minmax(0, 1fr); } }
++.sc-pane { margin: 0; display: grid; gap: 6px; min-width: 0; }
++.sc-pane figcaption { display: flex; justify-content: space-between; align-items: center; font-size: .78rem; font-weight: 700; color: var(--text-muted, #7386a3); }
++.sc-pane-label { letter-spacing: .04em; text-transform: uppercase; }
++.sc-pane-clock { font-variant-numeric: tabular-nums; }
++.sc-pane-frame { width: 100%; max-width: 100%; aspect-ratio: 4 / 3; border: 1px solid var(--border, #1f2d47); border-radius: 12px; background: #fff; display: block; }
++.sc-round .sc-pane-frame { aspect-ratio: 16 / 11; }
++.sc-pane-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 6px; margin: 4px 0 0; font-size: .82rem; }
++.sc-pane-facts dt { color: var(--text-muted, #7386a3); font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 800; }
++.sc-pane-facts dd { margin: 2px 0 0; font-weight: 650; overflow-wrap: anywhere; }
++.sc-pair-meta, .sc-round-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 0; font-size: .86rem; color: var(--text-muted, #7386a3); }
++.sc-round-meta { margin-top: 10px; }
++.sc-kind { padding: 2px 10px; border-radius: 999px; background: color-mix(in srgb, var(--room-accent) 14%, transparent); color: var(--room-strong); font-weight: 700; }
++.sc-votes { font-variant-numeric: tabular-nums; }
++.sc-pair-link { margin-left: auto; font-weight: 750; color: var(--room-strong) !important; text-decoration: none; }
++.sc-pair-link:hover { text-decoration: underline; }
++.sc-round-prompt { font-size: clamp(1.3rem, 3.6vw, 2rem) !important; line-height: 1.25 !important; letter-spacing: -.015em !important; }
++.sc-vote { margin-top: 18px; padding: 16px; border-radius: 18px; background: var(--bg-card, #0f1729); border: 1px solid var(--border, #1f2d47); display: grid; gap: 12px; }
++.sc-vote p { margin: 0; }
++.sc-vote fieldset { margin: 0; padding: 0; border: 0; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
++.sc-vote legend { width: 100%; margin-bottom: 8px; font-weight: 800; }
++.sc-vote label { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); cursor: pointer; font-weight: 650; }
++.sc-vote label:has(input:checked) { border-color: var(--room-accent); background: color-mix(in srgb, var(--room-accent) 14%, transparent); }
++.sc-vote input[type="radio"], .sc-vote input[type="checkbox"] { accent-color: var(--room-accent); }
++.sc-vote button { justify-self: start; min-height: 44px; padding: 0 20px; border: 0; border-radius: 999px; background: var(--room-fill); color: var(--room-on); font: inherit; font-weight: 750; cursor: pointer; }
++.sc-vote a { color: var(--room-strong); font-weight: 700; }
++.sc-empty { color: var(--text-muted, #7386a3); }
ddiff --git a/server/bench/registry.js b/server/bench/registry.js
iindex 51ef519..eedb338 100644
---- a/server/bench/registry.js
++++ b/server/bench/registry.js
@@@ -320,9 +320,16 @@ function createRegistry(s, { log = console } = {}) {
}
/** The registry list: by kind, by tag, or by a search over the title and the description. */
-- async function listBenchmarks({ kind = null, tag = null, q = null, limit = 50 } = {}) {
++ /**
++ * Benchmarks for a listing: the public ones, plus every one the `owner` owns when one is named. Unlisted and
++ * private benchmarks are never listed to anyone else (an unlisted one is reachable by its id; a private one only
++ * by its owner and the executor).
++ */
++ async function listBenchmarks({ kind = null, tag = null, q = null, limit = 50, owner = null } = {}) {
const where = [];
const params = [];
++ if (owner) { params.push(String(owner)); where.push(`(visibility = 'public' OR owner = $${params.length})`); }
++ else where.push("visibility = 'public'");
if (kind) { params.push(String(kind)); where.push(`kind = $${params.length}`); }
if (tag) { params.push(JSON.stringify([String(tag)])); where.push(`tags @> $${params.length}::jsonb`); }
if (q) {
@@@ -456,8 +463,9 @@ function createRegistry(s, { log = console } = {}) {
const params = [ref.id, wanted];
let sql = `SELECT * FROM bench_runs WHERE benchmark_id = $1 AND benchmark_version = ANY($2::int[])`;
if (!includeSubmitted) sql += " AND provenance = 'verified'";
-- if (harness) { params.push(JSON.stringify([String(harness)])); sql += ` AND harness->>'name' = $3`; }
-- if (since) { params.push(String(since)); sql += ' AND created_at >= $4::timestamptz'; }
++ // Each filter takes the next placeholder: `since` alone is $3, not $4.
++ if (harness) { params.push(String(harness)); sql += ` AND harness->>'name' = $${params.length}`; }
++ if (since) { params.push(String(since)); sql += ` AND created_at >= $${params.length}::timestamptz`; }
const rows = await db.many(sql, params);
const byModel = new Map();
for (const row of rows) {
@@@ -534,9 +542,76 @@ function createRegistry(s, { log = console } = {}) {
};
}
++ /**
++ * Runs, newest first, for the pages: a benchmark's latest runs, a model's, or one person's. Without tasks: a list
++ * row needs the totals only. Submitted runs are listed (and labelled) unless `verifiedOnly`.
++ */
++ /** How many runs each benchmark has, verified and submitted: the listing's counts. */
++ async function runCounts() {
++ const rows = await db.many('SELECT benchmark_id, COUNT(*)::int AS n FROM bench_runs GROUP BY benchmark_id');
++ return new Map(rows.map((r) => [r.benchmark_id, num(r.n)]));
++ }
++
++ async function listRuns({ benchmark = null, model = null, createdBy = null, verifiedOnly = false, publicOnly = false, limit = 20 } = {}) {
++ const where = [];
++ const params = [];
++ const add = (clause, value) => { params.push(value); where.push(clause.replace('?', `$${params.length}`)); };
++ if (benchmark) {
++ const ref = parseRef(benchmark);
++ if (!ref) return [];
++ add('benchmark_id = ?', ref.id);
++ if (ref.version != null) add('benchmark_version = ?', ref.version);
++ }
++ if (model) add('model = ?', String(model));
++ if (createdBy) add('created_by = ?', String(createdBy));
++ if (verifiedOnly) where.push("provenance = 'verified'");
++ // A listing across benchmarks (a model's page) shows the public benchmarks' runs only.
++ if (publicOnly) where.push("benchmark_id IN (SELECT id FROM bench_benchmarks WHERE visibility = 'public')");
++ params.push(Math.min(100, Math.max(1, Number(limit) || 20)));
++ const rows = await db.many(
++ `SELECT * FROM bench_runs ${where.length ? `WHERE ${where.join(' AND ')}` : ''} ORDER BY created_at DESC, id DESC LIMIT $${params.length}`,
++ params);
++ return rows.map((row) => wireRun(row));
++ }
++
++ /**
++ * One model across every public benchmark it ran: per benchmark, its verified runs, the median score, cost and time, and
++ * the spread between its best and worst run. The model page reads this.
++ */
++ async function modelSummary(model) {
++ const rows = await db.many(
++ "SELECT benchmark_id, score, cost_usd, ms FROM bench_runs WHERE model = $1 AND provenance = 'verified'"
++ + " AND benchmark_id IN (SELECT id FROM bench_benchmarks WHERE visibility = 'public')",
++ [String(model || '')]);
++ const byBenchmark = new Map();
++ for (const row of rows) {
++ if (!byBenchmark.has(row.benchmark_id)) byBenchmark.set(row.benchmark_id, []);
++ byBenchmark.get(row.benchmark_id).push(row);
++ }
++ const round = (v, p) => (v == null ? null : Math.round(v * p) / p);
++ const out = [];
++ for (const [benchmarkId, list] of byBenchmark) {
++ const scores = list.map((r) => num(r.score));
++ const b = await getBenchmark(benchmarkId);
++ out.push({
++ benchmark_id: benchmarkId,
++ title: b ? b.title : benchmarkId,
++ kind: b ? b.kind : null,
++ runs: list.length,
++ score: round(median(scores), 1000),
++ best: Math.max(...scores),
++ worst: Math.min(...scores),
++ cost_usd: round(median(list.map((r) => num(r.cost_usd))), 1e6),
++ ms: round(median(list.map((r) => num(r.ms))), 1),
++ });
++ }
++ out.sort((a, b) => b.runs - a.runs || a.title.localeCompare(b.title));
++ return { model: String(model || ''), runs: rows.length, benchmarks: out };
++ }
++
return {
createBenchmark, getBenchmark, getVersion, latestVersion, getDefinition, saveDraft, freezeVersion,
-- forkBenchmark, listBenchmarks, publishRun, getRun, compareRuns, rankings, percentile,
++ forkBenchmark, listBenchmarks, publishRun, getRun, compareRuns, rankings, percentile, listRuns, modelSummary, runCounts,
};
}
ddiff --git a/server/config.js b/server/config.js
iindex 13e9801..edf78d5 100644
---- a/server/config.js
++++ b/server/config.js
@@@ -25,6 +25,8 @@ function load(env = process.env) {
nodeEnv,
isProduction,
baseUrl,
++ // "Run it yourself" on a benchmark page opens this openvibe.codes page (stage B3); unset, the page offers the API.
++ codesRunUrl: env.CODES_BENCH_RUN_URL ? trim(env.CODES_BENCH_RUN_URL) : null,
trustProxy: env.TRUST_PROXY != null ? Number(env.TRUST_PROXY) : 2,
drill: env.BENCH_DRILL === '1' || env.BENCH_DRILL === 'true',
limits: {
ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex f938b07..6a8629b 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -11,6 +11,7 @@ const { createDiscoveryRoutes, homeJsonLd } = require('./discovery');
const { raw } = require('../render/html');
const { send } = require('../render/layout');
const views = require('../views');
++const results = require('../views/results');
const PUBLIC_CACHE = 'public, max-age=60';
@@@ -43,22 +44,129 @@ function oneGraph(nodes) {
}
function createPageRoutes(ctx) {
-- const { config, bench } = ctx;
++ const { config, bench, registry } = ctx;
const r = asyncRouter();
const base = () => String(config.baseUrl).replace(/\/+$/, '');
const page = (req, res, o, status = 200) => send(res, status, { viewer: req.viewer, config, path: req.originalUrl, ...o });
r.get('/', async (req, res) => {
const kinds = await bench.listKinds();
++ const benchmarks = await registry.listBenchmarks({ limit: 6 });
++ const counts = await registry.runCounts();
++ for (const b of benchmarks) b.runs = counts.get(b.id) || 0;
return page(req, res, {
index: true, cache: PUBLIC_CACHE,
title: null,
-- description: 'OpenVibe.Bench is one AI benchmark and comparison engine for every room: blind votes, rubrics, graders, test suites and game outcomes, with reproducible pinned rounds and an open dataset per kind.',
++ description: 'OpenVibe.Bench shows what AI models really make: the same tasks on every model, outputs live side by side, every run published with its cost and time, free and open.',
jsonLd: oneGraph([...homeJsonLd(config), ...kinds.map((k) => datasetJsonLd(k, base())), seo.jsonLd.organization()]),
-- body: raw(views.home({ kinds, counts: { kinds: kinds.length } })),
++ body: raw(views.home({ kinds, counts: { kinds: kinds.length }, benchmarks })),
});
});
++ // ---- the results pages (B2) ----------------------------------------------------------------------------
++ const subjectOf = (req) => (req.viewer && req.viewer.kind === 'user' ? (req.viewer.subject || req.viewer.id || null) : null);
++ const notFound = (req, res, what) => page(req, res, { title: `No such ${what}`, body: raw(views.notFound()) }, 404);
++ const METRIC_IDS = results.METRICS.map((m) => m.id);
++
++ r.get('/benchmarks', async (req, res) => {
++ const q = String(req.query.q || '').slice(0, 80);
++ const family = String(req.query.kind || '').replace(/[^a-z0-9._-]/gi, '').slice(0, 40) || null;
++ const all = await registry.listBenchmarks({ q: q || null, limit: 200 });
++ const kinds = [...new Set(all.map((b) => b.kind).filter(Boolean))];
++ const list = family ? all.filter((b) => b.kind === family || String(b.kind).startsWith(`${family}.`)) : all;
++ const counts = await registry.runCounts();
++ for (const b of list) b.runs = counts.get(b.id) || 0;
++ return page(req, res, {
++ index: !q && !family, cache: PUBLIC_CACHE,
++ title: 'Benchmarks',
++ description: 'Versioned AI benchmarks anyone can run again: tasks, judges and limits, with every run published.',
++ jsonLd: [seo.jsonLd.webPage({ name: 'Benchmarks', url: `${base()}/benchmarks`, description: 'Versioned AI benchmarks anyone can run again.', siteUrl: base() })],
++ body: raw(String(results.benchmarksIndex({ benchmarks: list, kinds, kind: family, q }))),
++ });
++ });
++
++ r.get('/benchmarks/:ref', async (req, res) => {
++ const ref = String(req.params.ref || '');
++ const out = await registry.getDefinition(ref, { reveal: false });
++ const subject = subjectOf(req);
++ if (!out || (out.benchmark.visibility === 'private' && out.benchmark.owner !== subject)) return notFound(req, res, 'benchmark');
++ const metric = METRIC_IDS.includes(String(req.query.metric)) ? String(req.query.metric) : 'score';
++ const id = out.benchmark.id;
++ const [rankings, runs] = await Promise.all([
++ registry.rankings({ benchmark: id, metric }),
++ registry.listRuns({ benchmark: id, limit: 8 }),
++ ]);
++ const isPublic = out.benchmark.visibility === 'public';
++ return page(req, res, {
++ index: isPublic && metric === 'score', cache: isPublic ? PUBLIC_CACHE : null,
++ title: out.benchmark.title,
++ description: out.benchmark.description || `${out.benchmark.title}: rankings, value and every run.`,
++ jsonLd: [seo.jsonLd.webPage({ name: out.benchmark.title, url: `${base()}/benchmarks/${encodeURIComponent(id)}`, description: out.benchmark.description || out.benchmark.title, siteUrl: base() })],
++ body: raw(String(results.benchmarkPage({
++ benchmark: out.benchmark, definition: out.definition, version: out.version && out.version.version,
++ rankings, metric, runs, codesRunUrl: config.codesRunUrl || null,
++ }))),
++ });
++ });
++
++ r.get('/runs/:id', async (req, res) => {
++ const run = await registry.getRun(String(req.params.id || ''));
++ if (!run) return notFound(req, res, 'run');
++ const def = await registry.getDefinition(`${run.benchmark_id}@${run.benchmark_version}`, { reveal: false });
++ const benchmark = def ? def.benchmark : null;
++ if (benchmark && benchmark.visibility === 'private' && benchmark.owner !== subjectOf(req)) return notFound(req, res, 'run');
++ const pct = run.score != null ? await registry.percentile({ benchmark: run.benchmark_id, model: run.model, score: run.score }) : null;
++ const isPublic = !benchmark || benchmark.visibility === 'public';
++ return page(req, res, {
++ index: isPublic && run.provenance === 'verified', cache: isPublic ? PUBLIC_CACHE : null,
++ title: `${run.model} on ${benchmark ? benchmark.title : run.benchmark_id}`,
++ description: `${run.model} scored ${results.fmtScore(run.score)} out of 100 on ${benchmark ? benchmark.title : 'this benchmark'}, for ${results.fmtUsd(run.cost_usd)}.`,
++ body: raw(String(results.runPage({ run, benchmark, definition: def && def.definition, pct }))),
++ });
++ });
++
++ r.get('/compare', async (req, res) => {
++ const refs = String(req.query.runs || '').split(',').map((x) => x.trim()).filter(Boolean).slice(0, 4);
++ let cmp = refs.length ? await registry.compareRuns(refs) : null;
++ const titles = {};
++ if (cmp) {
++ const subject = subjectOf(req);
++ const keep = [];
++ for (const run of cmp.runs) {
++ const b = await registry.getBenchmark(run.benchmark_id);
++ if (b && b.visibility === 'private' && b.owner !== subject) continue;
++ if (b) titles[b.id] = b.title;
++ keep.push(run);
++ }
++ cmp = keep.length ? { ...cmp, runs: keep, rows: cmp.rows.map((row) => ({ ...row, scores: keep.map((run) => { const t = run.tasks.find((x) => x.task_id === row.task_id); return t ? t.score : null; }) })) } : null;
++ }
++ return page(req, res, {
++ index: false, cache: PUBLIC_CACHE,
++ title: cmp ? `Compare: ${cmp.runs.map((x) => x.model).join(' vs ')}` : 'Compare runs',
++ description: 'Runs side by side: the same tasks, their scores, cost, time and outputs.',
++ body: raw(String(results.comparePage({ cmp, titles }))),
++ });
++ });
++
++ r.get('/models/:model', async (req, res) => {
++ const model = String(req.params.model || '').slice(0, 160);
++ const [summary, runs] = await Promise.all([
++ registry.modelSummary(model),
++ registry.listRuns({ model, publicOnly: true, limit: 12 }),
++ ]);
++ const titles = {};
++ for (const b of summary.benchmarks) titles[b.benchmark_id] = b.title;
++ for (const run of runs) {
++ if (!titles[run.benchmark_id]) { const b = await registry.getBenchmark(run.benchmark_id); if (b) titles[b.id] = b.title; }
++ }
++ return page(req, res, {
++ index: summary.runs > 0, cache: PUBLIC_CACHE,
++ title: model,
++ description: `${model} across OpenVibe.Bench: its median score, spread, cost and time on every benchmark it ran.`,
++ body: raw(String(results.modelPage({ model, summary, runs, titles }))),
++ }, summary.runs > 0 || runs.length ? 200 : 404);
++ });
++
r.get('/docs', (req, res) => page(req, res, {
index: true, cache: PUBLIC_CACHE,
title: 'The Bench API',
ddiff --git a/server/http/registry.js b/server/http/registry.js
iindex ec150bd..d0cb695 100644
---- a/server/http/registry.js
++++ b/server/http/registry.js
@@@ -129,7 +129,10 @@ function createRegistryApi(ctx) {
r.get('/benchmarks', async (req, res) => {
try {
++ // ?mine=1 adds the caller's own unlisted and private benchmarks; nobody else ever sees those in a list.
++ const mine = req.query.mine && req.principal && req.principal.kind === 'user' ? req.principal.subject : null;
const list = await registry.listBenchmarks({
++ owner: mine,
kind: req.query.kind ? String(req.query.kind) : null,
tag: req.query.tag ? String(req.query.tag) : null,
q: req.query.q ? String(req.query.q) : null,
@@@ -151,7 +154,9 @@ function createRegistryApi(ctx) {
}
const out = await registry.getDefinition(ref, { reveal });
if (!out) return problem(res, 404, 'bench.benchmark_not_found', 'No such benchmark.');
-- res.set('Cache-Control', 'public, max-age=60');
++ // A private benchmark answers only its owner and the executor; to everyone else it does not exist.
++ if (out.benchmark.visibility === 'private' && !reveal) return problem(res, 404, 'bench.benchmark_not_found', 'No such benchmark.');
++ res.set('Cache-Control', out.benchmark.visibility === 'public' ? 'public, max-age=60' : 'private, no-store');
return res.json({
benchmark: out.benchmark,
version: { ...out.version, definition: out.definition },
ddiff --git a/server/http/render.js b/server/http/render.js
iindex cee3dfd..a66bab6 100644
---- a/server/http/render.js
++++ b/server/http/render.js
@@@ -28,7 +28,7 @@ function createRenderRoutes(ctx) {
function bare(res) {
res.removeHeader('Set-Cookie');
res.removeHeader('X-Frame-Options'); // helmet's DENY; frame-ancestors in each response's CSP names who may frame it
-- res.set('Cache-Control', 'no-store');
++ res.set('Cache-Control', 'no-store, no-transform'); // no-transform: Cloudflare injects no analytics beacon (the frame's CSP refuses it)
res.set('X-Content-Type-Options', 'nosniff');
res.set('Referrer-Policy', 'no-referrer');
return res;
ddiff --git a/server/http/showcase.js b/server/http/showcase.js
iindex ceb648e..7902eda 100644
---- a/server/http/showcase.js
++++ b/server/http/showcase.js
@@@ -129,7 +129,7 @@ async function loadStats(ctx) {
total_cost_usd: Math.round(totalCost * 1e6) / 1e6,
cost_per_entry_usd: entries ? Math.round((totalCost / entries) * 1e6) / 1e6 : null,
median_cost_usd: c.median_cost == null ? null : num(c.median_cost),
-- median_ms: c.medemon_ms == null ? null : num(c.median_ms),
++ median_ms: c.median_ms == null ? null : num(c.median_ms),
p95_ms: c.p95_ms == null ? null : num(c.p95_ms),
tokens_per_entry: entries ? Math.round(num(c.tokens) / entries) : 0,
error_rate: entries ? num(c.errors) / entries : 0,
ddiff --git a/server/render/layout.js b/server/render/layout.js
iindex 8e327d7..5518179 100644
---- a/server/render/layout.js
++++ b/server/render/layout.js
@@@ -19,6 +19,8 @@ const TAGLINE = 'One AI benchmark and comparison engine for every room.';
const PUBLIC_DIR = path.join(__dirname, '..', '..', 'public');
const NAV = [
{ label: 'Home', href: '/' },
++ { label: 'Benchmarks', href: '/benchmarks' },
++ { label: 'Showcase', href: '/showcase' },
{ label: 'Docs', href: '/docs' },
];
@@@ -44,7 +46,7 @@ function renderPage(o) {
const nav = {
service: 'bench',
apiBase: NETWORK_URL,
-- links: NAV.map((l) => ({ label: l.label, href: l.href, active: pathNow === l.href || pathNow.startsWith(`${l.href}/`) })),
++ links: NAV.map((l) => ({ label: l.label, href: l.href, active: l.href === '/' ? pathNow === '/' : (pathNow.split('?')[0] === l.href || pathNow.startsWith(`${l.href}/`) || (l.href === '/benchmarks' && /^\/(runs|models|compare)(\/|\?|$)/.test(pathNow))) })),
history: { type: 'page', title: o.title || SITE_NAME },
silentLogin: `${o.config.baseUrl}/auth/login?silent=1&next={url}`,
sessionUrl: '/auth/me',
ddiff --git a/server/views/index.js b/server/views/index.js
iindex f5ae073..b0f7abd 100644
---- a/server/views/index.js
++++ b/server/views/index.js
@@@ -1,8 +1,8 @@
'use strict';
/**
-- * Bench's own origin pages (the main session designs them later): a plain home that says what Bench is, lists the
-- * every kind and links to boards and docs, and /docs for the API. Ids and classes start with `bm-`.
++ * Bench's own origin pages: the home (what Bench shows, the newest benchmarks, the arena kinds for developers) and
++ * /docs for the API. The results pages are in ./results.js. Ids and classes start with `bm-`.
*/
const { esc } = require('../render/html');
@@@ -10,21 +10,24 @@ function kindRow(k) {
return `<li class="bm-kind"><a class="bm-kind-link" href="/api/v1/boards/${esc(k.id)}?type=elo">${esc(k.id)}</a><span class="bm-kind-meta">${esc(k.room)} · ${esc(k.judge)} · ${esc(k.name)}</span></li>`;
}
--function home({ kinds = [], counts = { kinds: 0 } } = {}) {
++function home({ kinds = [], counts = { kinds: 0 }, benchmarks = [] } = {}) {
++ const { benchmarkCard } = require('./results');
++ const cards = benchmarks.slice(0, 6).map((b) => String(benchmarkCard(b))).join('');
return `<div class="bm-wrap bm-home" id="bm-home">
--<header class="bm-hero"><p class="bm-kicker">OpenVibe.Bench</p><h1>One benchmark engine for every room</h1>
--<p class="bm-lede">Put AI models side by side on real work: a page design, a game, a meme, a code fix, a web task. People vote blind, graders and tests score the rest, and every round is pinned to the exact model it ran, so anyone can run it again.</p>
--<div class="bm-actions"><a class="bm-btn" href="/docs">Read the API</a><a class="bm-btn bm-btn-quiet" href="#bm-kinds">The kinds</a></div></header>
++<header class="bm-hero"><p class="bm-kicker">OpenVibe.Bench</p><h1>See what AI really makes</h1>
++<p class="bm-lede">Every model on the same tasks: real pages, games, scenes and fixes, shown live side by side. Every run is published with what it cost and how long it took, and anyone can run it again.</p>
++<div class="bm-actions"><a class="bm-btn" href="/benchmarks">Browse benchmarks</a><a class="bm-btn bm-btn-quiet" href="/showcase">Vote in the showcase</a></div></header>
<ul class="bm-why" aria-label="What Bench promises">
--<li><b>Free and open</b><span>The public arena costs nothing, and every kind's votes and results download as JSONL, without voter names.</span></li>
--<li><b>Cost per solved task</b><span>Boards rank quality against what a solved task really cost, with confidence intervals, not a single score.</span></li>
--<li><b>Every room, one engine</b><span>Design, Games, Meme, Vision, Codes, Surf, Chat and speech share one API, SDK and MCP tools.</span></li>
--<li><b>Your own evals</b><span>Private task suites on your models or keys, free up to a daily cap, never published.</span></li>
++<li><b>Outputs, not just scores</b><span>The pages, games and drawings themselves, live in sandboxed frames, next to the numbers.</span></li>
++<li><b>Every run published</b><span>Whatever it scored. No private variants, no best of ten: the service that ran it publishes it.</span></li>
++<li><b>Value you can read</b><span>Score against cost per run, with the frontier marked, so the cheapest good model is obvious.</span></li>
++<li><b>Free and open</b><span>Votes, runs and outputs download as JSONL. Versions are frozen, so every result can be repeated.</span></li>
</ul>
--<section class="bm-section" id="bm-kinds" aria-labelledby="bm-kinds-h"><div class="bm-head-row"><h2 id="bm-kinds-h">Kinds</h2><span class="bm-count">${Number(counts.kinds) || kinds.length}</span></div>
++<section class="bm-section" aria-labelledby="bm-bench-h"><div class="bm-head-row"><h2 id="bm-bench-h">Benchmarks</h2><a class="bm-more" href="/benchmarks">All of them</a></div>
++${cards ? `<ul class="br-cards">${cards}</ul>` : '<p class="bm-muted">No benchmark is published yet.</p>'}</section>
++<section class="bm-section" id="bm-kinds" aria-labelledby="bm-kinds-h"><div class="bm-head-row"><h2 id="bm-kinds-h">For developers: the arena kinds</h2><span class="bm-count">${Number(counts.kinds) || kinds.length}</span></div>
++<p class="bm-fine">Each kind is a room's arena with its judge and entry type. Its board is JSON at <code>/api/v1/boards/:kind</code>, and the <a href="/docs">API</a> covers the rest.</p>
${kinds.length ? `<ul class="bm-kinds">${kinds.map(kindRow).join('')}</ul>` : '<p class="bm-muted">No kinds are seeded yet.</p>'}</section>
--<section class="bm-section" id="bm-boards"><div class="bm-head-row"><h2>Boards</h2></div>
--<p class="bm-fine">Boards are JSON at <code>/api/v1/boards/:kind?type=elo|pareto|cost</code>. Public HTML boards render in OpenVibe.Report; rooms show their own arenas through Bench.</p></section>
</div>`;
}
@@@ -52,6 +55,15 @@ ${ep('POST', '/api/v1/evals', 'Create a private task suite (never in the public
${ep('POST', '/api/v1/evals/:id/run', 'Run a private eval against chosen offers, under the free daily cap.')}
${ep('GET', '/api/v1/evals/:id/results', 'A private eval and its runs.')}
</dl></section>
++<section id="br-publish"><h2>Benchmarks and runs</h2><p class="bm-fine">A benchmark is a versioned set of tasks, judges and limits; a frozen version never changes once a run names it. A run published by a service (openvibe.codes, Run, a Node worker) is <b>verified</b>; one published with a person's token is <b>submitted</b>, labelled, and never ranked by default.</p><dl class="bm-eps">
++${ep('GET', '/api/v1/benchmarks?kind=&tag=&q=&mine=1', 'The public benchmarks; <code>mine=1</code> adds your own unlisted ones.')}
++${ep('GET', '/api/v1/benchmarks/:id[@version]', 'A definition. Held-out tasks keep their prompt for the executor and the owner.')}
++${ep('POST', '/api/v1/benchmarks', 'Create one (<code>bench.benchmark.write</code>); <code>PUT</code> saves a draft, <code>POST …/versions</code> freezes it, <code>POST …/fork</code> forks it.')}
++${ep('POST', '/api/v1/runs', 'Publish a run: <code>{ benchmark: "bmk_…@N", model, provider, harness: { name, version }, executor, tasks: [{ task_id, score, cost_usd, ms, tokens, artifacts }], score, cost_usd, ms, tokens }</code>.')}
++${ep('GET', '/api/v1/runs/:id', 'A run with its tasks; <code>/api/v1/compare?runs=a,b</code> puts up to four side by side.')}
++${ep('GET', '/api/v1/rankings?benchmark=&metric=score|value|speed|consistency&harness=&since=', 'Medians per model over verified runs; value is score per dollar, speed is score per minute.')}
++${ep('GET', '/api/v1/percentile?benchmark=&model=&score=', 'Where a score sits among a model’s verified runs.')}
++</dl></section>
<section><h2>MCP</h2><p class="bm-fine"><code>POST /mcp</code> exposes <code>bench.list_kinds</code>, <code>bench.start_round</code>, <code>bench.next_pair</code>, <code>bench.cast_judgement</code>, <code>bench.get_board</code> and <code>bench.run_eval</code>.</p></section>
</div>`;
}
ddiff --git a/server/views/results.js b/server/views/results.js
nnew file mode 100644
iindex 0000000..4f38cf1
---- /dev/null
++++ b/server/views/results.js
@@@ -0,0 +1,301 @@
++'use strict';
++
++/**
++ * The results pages (stage B2): the benchmark list, a benchmark with its rankings and value chart, a run (Geekbench
++ * style: the environment, the score, where it sits, each task and its outputs), compare, and a model across
++ * benchmarks. Server-rendered and complete without JavaScript. Everything dynamic goes through html`` (escaped).
++ * Ids and classes start with `br-`.
++ */
++const { html, raw, render } = require('../render/html');
++const renderLib = require('../bench/render');
++
++const MEDIA_ORIGIN = 'https://openvibe.media';
++
++// ---- numbers -------------------------------------------------------------------------------------------------
++
++const fmtScore = (v) => (v == null ? '—' : (Math.round(Number(v) * 10) / 10).toFixed(1).replace(/\.0$/, ''));
++function fmtUsd(v) {
++ if (v == null) return '—';
++ const n = Number(v);
++ if (n === 0) return '$0';
++ if (n < 0.01) return `$${n.toPrecision(2)}`;
++ return `$${n.toFixed(2)}`;
++}
++function fmtMs(v) {
++ if (v == null) return '—';
++ const s = Number(v) / 1000;
++ if (s < 1) return `${Math.round(Number(v))} ms`;
++ if (s < 90) return `${Math.round(s * 10) / 10} s`;
++ return `${Math.round(s / 6) / 10} min`;
++}
++function fmtTokens(v) {
++ if (v == null) return '—';
++ const n = Number(v);
++ if (n >= 1e6) return `${Math.round(n / 1e5) / 10}M`;
++ if (n >= 1e3) return `${Math.round(n / 100) / 10}K`;
++ return String(n);
++}
++const fmtDate = (iso) => (iso ? html`<time datetime="${iso}">${String(iso).slice(0, 10)}</time>` : '—');
++function ordinal(n) {
++ const v = Math.round(Number(n));
++ const t = v % 100;
++ if (t >= 11 && t <= 13) return `${v}th`;
++ return `${v}${({ 1: 'st', 2: 'nd', 3: 'rd' })[v % 10] || 'th'}`;
++}
++const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
++
++// ---- links ---------------------------------------------------------------------------------------------------
++
++const benchmarkHref = (id) => `/benchmarks/${encodeURIComponent(id)}`;
++const runHref = (id) => `/runs/${encodeURIComponent(id)}`;
++const modelHref = (m) => `/models/${encodeURIComponent(m)}`;
++const compareHref = (ids) => `/compare?runs=${ids.map(encodeURIComponent).join(',')}`;
++
++// ---- small pieces --------------------------------------------------------------------------------------------
++
++const kindChip = (kind) => (kind ? html`<span class="br-chip br-mono">${kind}</span>` : '');
++
++function provenance(p) {
++ return p === 'verified'
++ ? html`<span class="br-prov is-verified" title="Run by openvibe.codes, Run or a Node worker, and published by that service">Verified</span>`
++ : html`<span class="br-prov is-submitted" title="Published by a person; nobody re-ran it">Submitted</span>`;
++}
++
++/** A score out of 100 with a thin bar under it. */
++function scoreBar(score, { big = false } = {}) {
++ const pct = score == null ? 0 : Math.max(0, Math.min(100, Number(score)));
++ return html`<span class="br-score${big ? ' is-big' : ''}"><span class="br-score-n">${fmtScore(score)}</span><span class="br-score-of">/100</span><span class="br-bar" aria-hidden="true"><span style="width:${pct.toFixed(1)}%"></span></span></span>`;
++}
++
++function empty(title, text, action = '') {
++ return html`<div class="br-empty"><p class="br-empty-t">${title}</p><p>${text}</p>${action}</div>`;
++}
++
++function harnessLabel(h) {
++ if (!h || !h.name) return '—';
++ return h.version ? `${h.name} ${h.version}` : h.name;
++}
++
++// ---- the value chart -----------------------------------------------------------------------------------------
++
++/**
++ * Score against cost per run, one dot per model, cost on a log scale. The frontier joins the models nobody beats on
++ * both at once (no other model is cheaper and better). Drawn as SVG on the server; the table under it carries the
++ * same numbers for screen readers.
++ */
++function valueChart(entries) {
++ const W = 880; const H = 320; const L = 44; const R = 16; const T = 14; const B = 40;
++ const pts = entries.filter((e) => e.score != null && e.cost_usd != null && e.cost_usd > 0);
++ const grid = [0, 25, 50, 75, 100].map((y) => {
++ const py = T + (H - T - B) * (1 - y / 100);
++ return html`<line class="br-grid" x1="${L}" x2="${W - R}" y1="${py}" y2="${py}"/><text class="br-axis" x="${L - 8}" y="${py + 4}" text-anchor="end">${y}</text>`;
++ });
++ if (!pts.length) {
++ return html`<figure class="br-chart" aria-label="Score against cost: no verified runs yet"><svg viewBox="0 0 ${W} ${H}" role="img" preserveAspectRatio="xMidYMid meet">${grid}<text class="br-axis" x="${(L + W - R) / 2}" y="${H - 8}" text-anchor="middle">Cost per run (log scale)</text><text class="br-chart-empty" x="${(L + W - R) / 2}" y="${(T + H - B) / 2}" text-anchor="middle">The chart fills in with the first verified runs</text></svg></figure>`;
++ }
++ const costs = pts.map((p) => Math.log10(p.cost_usd));
++ let lo = Math.floor(Math.min(...costs)); let hi = Math.ceil(Math.max(...costs));
++ if (hi === lo) { lo -= 1; hi += 1; }
++ const x = (c) => L + (W - L - R) * ((Math.log10(c) - lo) / (hi - lo));
++ const y = (s) => T + (H - T - B) * (1 - s / 100);
++ const ticks = [];
++ for (let e = lo; e <= hi; e++) {
++ const px = L + (W - L - R) * ((e - lo) / (hi - lo));
++ ticks.push(html`<text class="br-axis" x="${px}" y="${H - B + 18}" text-anchor="middle">${fmtUsd(10 ** e)}</text>`);
++ }
++ // The frontier: walk from the cheapest up, keeping each model that scores higher than every cheaper one.
++ const sorted = [...pts].sort((a, b) => a.cost_usd - b.cost_usd || b.score - a.score);
++ const frontier = [];
++ let best = -Infinity;
++ for (const p of sorted) if (p.score > best) { frontier.push(p); best = p.score; }
++ const onFrontier = new Set(frontier.map((p) => p.model));
++ let path = '';
++ frontier.forEach((p, i) => {
++ const px = x(p.cost_usd).toFixed(1); const py = y(p.score).toFixed(1);
++ if (i === 0) path += `M${px} ${py}`;
++ else path += ` H${px} V${py}`;
++ });
++ // Labels sit beside their dot; one that would overlap a label already placed moves down (or up) by a line.
++ const placed = [];
++ const labelY = (px, py, anchor) => {
++ const w = 7 * 22; const x0 = anchor === 'end' ? px - w : px; let ly = py + 4;
++ for (let tries = 0; tries < 6; tries++) {
++ const hit = placed.find((b) => Math.abs(b.y - ly) < 13 && x0 < b.x0 + b.w && b.x0 < x0 + w);
++ if (!hit) break;
++ ly = hit.y + (ly >= hit.y ? 13 : -13);
++ }
++ placed.push({ x0, w, y: ly });
++ return ly;
++ };
++ const dots = [...pts].sort((a, b) => b.score - a.score).map((p) => {
++ const px = x(p.cost_usd); const py = y(p.score);
++ const anchor = px > W - 180 ? 'end' : 'start';
++ const dx = anchor === 'end' ? -9 : 9;
++ const ly = labelY(px + dx, py, anchor);
++ return html`<g class="br-dot${onFrontier.has(p.model) ? ' is-front' : ''}"><circle cx="${px.toFixed(1)}" cy="${py.toFixed(1)}" r="5"><title>${p.model}: ${fmtScore(p.score)} for ${fmtUsd(p.cost_usd)} a run</title></circle><text x="${(px + dx).toFixed(1)}" y="${ly.toFixed(1)}" text-anchor="${anchor}">${p.model}</text></g>`;
++ });
++ return html`<figure class="br-chart" aria-label="Score against cost per run; the line is the frontier"><svg viewBox="0 0 ${W} ${H}" role="img" preserveAspectRatio="xMidYMid meet">${grid}${ticks}<text class="br-axis" x="${(L + W - R) / 2}" y="${H - 4}" text-anchor="middle">Cost per run (log scale)</text>${path ? raw(`<path class="br-front" d="${path}"/>`) : ''}${dots}</svg><figcaption>Up is better, left is cheaper. The line joins the models no other model beats on both.</figcaption></figure>`;
++}
++
++// ---- the rankings table --------------------------------------------------------------------------------------
++
++const METRICS = [
++ { id: 'score', label: 'Score' },
++ { id: 'value', label: 'Value', hint: 'score per dollar' },
++ { id: 'speed', label: 'Speed', hint: 'score per minute' },
++ { id: 'consistency', label: 'Steadiness', hint: 'spread inside a session, lower is steadier' },
++];
++
++function metricTabs(base, current) {
++ return html`<nav class="br-tabs" aria-label="Rank by">${METRICS.map((m) => html`<a class="br-tab" href="${base}?metric=${m.id}#br-rank"${m.id === current ? raw(' aria-current="page"') : ''}${m.hint ? raw(` title="${m.hint}"`) : ''}>${m.label}</a>`)}</nav>`;
++}
++
++function rankingsTable(entries, metric) {
++ if (!entries.length) return '';
++ const col = { value: (e) => (e.value == null ? '—' : `${fmtScore(e.value)} / $`), speed: (e) => (e.speed == null ? '—' : `${fmtScore(e.speed)} / min`), consistency: (e) => (e.consistency == null ? '—' : `±${fmtScore(e.consistency)}`) }[metric];
++ return html`<div class="br-table-wrap"><table class="br-table"><thead><tr><th scope="col">#</th><th scope="col">Model</th><th scope="col">Score</th>${col ? html`<th scope="col">${METRICS.find((m) => m.id === metric).label}</th>` : ''}<th scope="col" class="br-num">Cost / run</th><th scope="col" class="br-num">Time</th><th scope="col" class="br-num">Runs</th></tr></thead><tbody>${entries.map((e, i) => html`<tr><td class="br-rank">${i + 1}</td><td><a class="br-model" href="${modelHref(e.model)}">${e.model}</a></td><td>${scoreBar(e.score)}</td>${col ? html`<td class="br-num">${col(e)}</td>` : ''}<td class="br-num">${fmtUsd(e.cost_usd)}</td><td class="br-num">${fmtMs(e.ms)}</td><td class="br-num">${e.runs}</td></tr>`)}</tbody></table></div>`;
++}
++
++// ---- runs list -----------------------------------------------------------------------------------------------
++
++function runsList(runs, { showBenchmark = false, titles = {} } = {}) {
++ if (!runs.length) return '';
++ return html`<ul class="br-runs">${runs.map((r) => html`<li class="br-run"><a class="br-run-link" href="${runHref(r.id)}"><span class="br-run-main"><span class="br-run-model">${r.model}</span><span class="br-run-sub">${showBenchmark ? html`${titles[r.benchmark_id] || r.benchmark_id} · ` : ''}${harnessLabel(r.harness)} · ${fmtDate(r.created_at)}</span></span>${scoreBar(r.score)}</a>${provenance(r.provenance)}</li>`)}</ul>`;
++}
++
++// ---- outputs -------------------------------------------------------------------------------------------------
++
++/** An output: a live frame when it is ours to frame (the render origin), a picture from Media, else a plain link. */
++function artifact(a, label) {
++ const url = String(a.url || '');
++ if (url.startsWith(`${renderLib.RENDER_ORIGIN}/render/`)) {
++ return html`<figure class="br-out"><iframe class="br-out-frame" src="${url}" title="${label}" sandbox="${renderLib.WRAPPER_SANDBOX}" referrerpolicy="no-referrer" loading="lazy"></iframe><figcaption>${a.kind}</figcaption></figure>`;
++ }
++ if (url.startsWith(`${MEDIA_ORIGIN}/`) && /^(image|screenshot|png|jpeg|webp)$/i.test(String(a.kind))) {
++ return html`<figure class="br-out"><img class="br-out-img" src="${url}" alt="${label}" loading="lazy"><figcaption>${a.kind}</figcaption></figure>`;
++ }
++ if (/^https:\/\//.test(url)) return html`<a class="br-out-link" href="${url}" rel="noopener nofollow ugc">${a.kind}: ${url.replace(/^https:\/\//, '').slice(0, 60)}</a>`;
++ return html`<span class="br-out-link">${a.kind}</span>`;
++}
++
++// ---- pages ---------------------------------------------------------------------------------------------------
++
++function benchmarksIndex({ benchmarks = [], kinds = [], kind = null, q = '' } = {}) {
++ const families = [...new Set(kinds.map((k) => String(k).split('.')[0]))].sort();
++ const chip = (label, value) => {
++ const on = (value || null) === (kind || null);
++ const href = value ? `/benchmarks?kind=${encodeURIComponent(value)}` : '/benchmarks';
++ return html`<a class="br-filter" href="${href}"${on ? raw(' aria-current="page"') : ''}>${label}</a>`;
++ };
++ return html`<div class="bm-wrap br" id="br-benchmarks">
++<header class="bm-page-h"><p class="bm-kicker">Benchmarks</p><h1>Benchmarks anyone can run again</h1>
++<p class="bm-lede">Each one is a versioned set of tasks with its judges and limits. A version never changes once a run names it, so every score here can be repeated.</p></header>
++<form class="br-search" method="get" action="/benchmarks" role="search"><label class="br-sr" for="br-q">Search benchmarks</label><input id="br-q" name="q" type="search" value="${q}" placeholder="Search by title, tag or kind" autocomplete="off">${kind ? html`<input type="hidden" name="kind" value="${kind}">` : ''}<button class="bm-btn bm-btn-small" type="submit">Search</button></form>
++<nav class="br-filters" aria-label="Filter by family">${chip('All', null)}${families.map((f) => chip(f, f))}</nav>
++${benchmarks.length ? html`<ul class="br-cards">${benchmarks.map(benchmarkCard)}</ul>` : empty('No benchmark matches', q ? 'Try a shorter search, or clear the filter.' : 'None are published yet.', html`<a class="bm-btn bm-btn-quiet bm-btn-small" href="/benchmarks">Show all</a>`)}
++</div>`;
++}
++
++function benchmarkCard(b) {
++ const tasks = ((b.draft && b.draft.tasks) || []).length;
++ return html`<li class="br-card"><a class="br-card-link" href="${benchmarkHref(b.id)}"><span class="br-card-top">${kindChip(b.kind)}<span class="br-card-v">v${b.latest_version || 0}</span></span><span class="br-card-title">${b.title}</span><span class="br-card-desc">${b.description}</span><span class="br-card-foot">${plural(tasks, 'task')}${b.runs != null ? html` · ${plural(b.runs, 'run')}` : ''}${b.parent ? ' · a fork' : ''}</span></a></li>`;
++}
++
++function benchmarkPage({ benchmark, definition, version, rankings, metric = 'score', runs = [], codesRunUrl = null }) {
++ const d = definition || {};
++ const tasks = Array.isArray(d.tasks) ? d.tasks : [];
++ const shown = tasks.filter((t) => !t.holdout && t.prompt);
++ const hidden = tasks.length - shown.length;
++ const judges = Array.isArray(d.judges) ? d.judges : [];
++ const limits = d.limits || {};
++ const entries = (rankings && rankings.entries) || [];
++ const base = benchmarkHref(benchmark.id);
++ const runYourself = codesRunUrl
++ ? html`<a class="bm-btn" href="${codesRunUrl}?benchmark=${encodeURIComponent(`${benchmark.id}@${version || benchmark.latest_version}`)}">Run it yourself</a>`
++ : html`<a class="bm-btn bm-btn-quiet" href="/docs#br-publish">Publish a run</a>`;
++ return html`<div class="bm-wrap br" id="br-benchmark">
++<nav class="br-crumbs" aria-label="Breadcrumb"><a href="/benchmarks">Benchmarks</a><span aria-hidden="true">/</span><span>${benchmark.title}</span></nav>
++<header class="br-head"><div class="br-head-main"><p class="br-head-meta">${kindChip(benchmark.kind)}<span>v${version || benchmark.latest_version || 0}</span><span>by ${benchmark.owner}</span>${benchmark.parent ? html`<a href="${benchmarkHref(benchmark.parent)}">forked</a>` : ''}</p>
++<h1>${benchmark.title}</h1>${benchmark.description ? html`<p class="bm-lede">${benchmark.description}</p>` : ''}</div>
++<div class="br-head-act">${runYourself}<p class="br-fine">${codesRunUrl ? 'On openvibe.codes, with your own keys. The result is published here.' : 'Running it on openvibe.codes with your own keys is coming. Today a run is published through the API.'}</p></div></header>
++<dl class="br-facts"><div><dt>Tasks</dt><dd>${tasks.length}${hidden ? html` <span class="br-fine">(${hidden} hidden)</span>` : ''}</dd></div><div><dt>Judged by</dt><dd>${judges.length ? judges.map((j) => String(j.type).replace(/_/g, ' ')).join(', ') : '—'}</dd></div><div><dt>Limits</dt><dd>${[limits.ms ? fmtMs(limits.ms) : null, limits.tokens ? `${fmtTokens(limits.tokens)} tokens` : null, limits.usd ? fmtUsd(limits.usd) : null].filter(Boolean).join(' · ') || '—'}</dd></div><div><dt>Verified runs</dt><dd>${entries.reduce((n, e) => n + e.runs, 0)}</dd></div></dl>
++<section class="bm-section" id="br-rank" aria-labelledby="br-rank-h"><div class="bm-head-row"><h2 id="br-rank-h">Rankings</h2>${metricTabs(base, metric)}</div>
++${valueChart(entries)}
++${entries.length ? rankingsTable(entries, metric) : empty('No verified runs yet', 'A run counts here once openvibe.codes, Run or a Node worker ran it and published it. Submitted runs are listed below but never ranked.')}
++</section>
++<section class="bm-section" aria-labelledby="br-tasks-h"><div class="bm-head-row"><h2 id="br-tasks-h">Tasks</h2><span class="bm-count">${tasks.length}</span></div>
++<ol class="br-tasks">${shown.map((t) => html`<li class="br-task"><span class="br-task-id br-mono">${t.id}</span><p>${t.prompt}</p></li>`)}${hidden ? html`<li class="br-task is-hidden"><span class="br-task-id br-mono">${plural(hidden, 'task')}</span><p>Held out: only the executor sees ${hidden === 1 ? 'it' : 'them'}, so nobody can tune for the answer.</p></li>` : ''}</ol></section>
++<section class="bm-section" aria-labelledby="br-latest-h"><div class="bm-head-row"><h2 id="br-latest-h">Latest runs</h2>${runs.length > 1 ? html`<a class="bm-more" href="${compareHref(runs.slice(0, 2).map((r) => r.id))}">Compare the newest two</a>` : ''}</div>
++${runs.length ? runsList(runs) : empty('No runs yet', 'The first run shows up here, verified or submitted.')}</section>
++<p class="br-fine br-api">The same data as JSON: <a href="/api/v1/benchmarks/${encodeURIComponent(benchmark.id)}">definition</a> · <a href="/api/v1/rankings?benchmark=${encodeURIComponent(benchmark.id)}&amp;metric=${metric}">rankings</a></p>
++</div>`;
++}
++
++function runPage({ run, benchmark, definition, pct }) {
++ const tasksDef = new Map(((definition && definition.tasks) || []).map((t) => [t.id, t]));
++ const env = [
++ ['Model', html`<a href="${modelHref(run.model)}">${run.model}</a>`],
++ ['Provider', run.provider || '—'],
++ ['Route', run.route && Object.keys(run.route).length ? Object.entries(run.route).map(([k, v]) => `${k} ${v}`).join(' · ') : '—'],
++ ['Harness', harnessLabel(run.harness)],
++ ['Settings', [run.settings && run.settings.effort ? `effort ${run.settings.effort}` : null, run.settings && run.settings.temperature != null ? `temperature ${run.settings.temperature}` : null].filter(Boolean).join(' · ') || 'defaults'],
++ ['Ran on', run.executor ? ({ codes: 'openvibe.codes', run: 'OpenVibe.Run', node: 'a Node worker', submitted: 'not re-run' })[run.executor.kind] || run.executor.kind : '—'],
++ ['Hardware', run.hardware ? Object.entries(run.hardware).map(([k, v]) => `${k} ${v}`).join(' · ') : '—'],
++ ['Benchmark', html`<a href="${benchmarkHref(run.benchmark_id)}">${benchmark ? benchmark.title : run.benchmark_id}</a> <span class="br-fine">v${run.benchmark_version}</span>`],
++ ];
++ const pctLine = pct && pct.runs > 1 && pct.percentile != null
++ ? html`<div class="br-pct"><div class="br-pct-strip" aria-hidden="true"><span style="left:${Math.max(0, Math.min(100, pct.percentile)).toFixed(1)}%"></span></div><p><b>${ordinal(pct.percentile)} percentile</b> of ${pct.runs} verified runs of ${run.model} on this benchmark. Median ${fmtScore(pct.median)}, best ${fmtScore(pct.best)}.</p></div>`
++ : html`<p class="br-fine">Where it sits shows once ${run.model} has more verified runs here.</p>`;
++ return html`<div class="bm-wrap br" id="br-run">
++<nav class="br-crumbs" aria-label="Breadcrumb"><a href="/benchmarks">Benchmarks</a><span aria-hidden="true">/</span><a href="${benchmarkHref(run.benchmark_id)}">${benchmark ? benchmark.title : run.benchmark_id}</a><span aria-hidden="true">/</span><span class="br-mono">${run.id}</span></nav>
++<header class="br-head br-run-head"><div class="br-head-main"><p class="br-head-meta">${provenance(run.provenance)}<span>${fmtDate(run.created_at)}</span></p><h1>${run.model}</h1><p class="bm-lede">on ${benchmark ? benchmark.title : run.benchmark_id}, through ${harnessLabel(run.harness)}</p></div>
++<div class="br-head-score">${scoreBar(run.score, { big: true })}<dl class="br-totals"><div><dt>Cost</dt><dd>${fmtUsd(run.cost_usd)}</dd></div><div><dt>Time</dt><dd>${fmtMs(run.ms)}</dd></div><div><dt>Tokens</dt><dd>${fmtTokens(run.tokens)}</dd></div></dl></div></header>
++${pctLine}
++<section class="bm-section" aria-labelledby="br-env-h"><h2 id="br-env-h">Environment</h2><dl class="br-env">${env.map(([k, v]) => html`<div><dt>${k}</dt><dd>${v}</dd></div>`)}</dl></section>
++<section class="bm-section" aria-labelledby="br-pt-h"><div class="bm-head-row"><h2 id="br-pt-h">Tasks</h2><span class="bm-count">${(run.tasks || []).length}</span></div>
++${(run.tasks || []).length ? html`<ol class="br-ptasks">${run.tasks.map((t) => {
++ const def = tasksDef.get(t.task_id);
++ const prompt = def && def.prompt && !def.holdout ? def.prompt : 'A held-out task: its prompt stays with the executor.';
++ return html`<li class="br-ptask"><div class="br-ptask-head"><span class="br-task-id br-mono">${t.task_id}</span>${scoreBar(t.score)}<span class="br-ptask-meta">${fmtUsd(t.cost_usd)} · ${fmtMs(t.ms)} · ${fmtTokens(t.tokens)} tokens</span></div><p class="br-ptask-prompt">${prompt}</p>${t.artifacts && t.artifacts.length ? html`<div class="br-outs">${t.artifacts.map((a, i) => artifact(a, `${t.task_id} output ${i + 1}`))}</div>` : ''}${t.transcript_url && /^https:\/\//.test(t.transcript_url) ? html`<a class="bm-more" href="${t.transcript_url}" rel="noopener nofollow">Transcript</a>` : ''}</li>`;
++ })}</ol>` : empty('No task results', 'This run published its totals only.')}</section>
++<section class="bm-section br-again" aria-labelledby="br-again-h"><h2 id="br-again-h">Again</h2><p class="br-fine">The benchmark version is frozen, so this run can be repeated exactly: the same tasks, the same judges.</p><div class="bm-actions"><a class="bm-btn bm-btn-quiet" href="${benchmarkHref(run.benchmark_id)}">See every model on it</a><a class="bm-btn bm-btn-quiet" href="/api/v1/runs/${encodeURIComponent(run.id)}">The run as JSON</a></div></section>
++</div>`;
++}
++
++function comparePage({ cmp, titles = {} }) {
++ if (!cmp) {
++ return html`<div class="bm-wrap br" id="br-compare"><header class="bm-page-h"><p class="bm-kicker">Compare</p><h1>Compare runs</h1><p class="bm-lede">Open a benchmark, then compare two to four of its runs: the same tasks side by side, with their scores, cost, time and outputs.</p></header>${empty('Nothing to compare', 'Add runs with ?runs=run_a,run_b, or pick them from a benchmark.', html`<a class="bm-btn bm-btn-small" href="/benchmarks">Benchmarks</a>`)}</div>`;
++ }
++ const { runs, rows } = cmp;
++ const best = (vals, hi = true) => { const v = vals.filter((x) => x != null); return v.length ? (hi ? Math.max(...v) : Math.min(...v)) : null; };
++ const cell = (v, b, f) => html`<td class="br-num${v != null && v === b ? ' is-best' : ''}">${f(v)}</td>`;
++ const line = (label, vals, f, hi = true) => { const b = best(vals, hi); return html`<tr><th scope="row">${label}</th>${vals.map((v) => cell(v, b, f))}</tr>`; };
++ return html`<div class="bm-wrap br" id="br-compare">
++<header class="bm-page-h"><p class="bm-kicker">Compare</p><h1>${runs.map((r) => r.model).join(' vs ')}</h1><p class="bm-lede">${[...new Set(runs.map((r) => titles[r.benchmark_id] || r.benchmark_id))].join(', ')}. The best value in each row is marked.</p></header>
++<div class="br-table-wrap"><table class="br-table br-cmp"><thead><tr><th scope="col"><span class="br-sr">Measure</span></th>${runs.map((r) => html`<th scope="col"><a href="${runHref(r.id)}">${r.model}</a><span class="br-cmp-sub">${harnessLabel(r.harness)}</span>${provenance(r.provenance)}</th>`)}</tr></thead><tbody>
++${line('Score', runs.map((r) => r.score), fmtScore)}${line('Cost', runs.map((r) => r.cost_usd), fmtUsd, false)}${line('Time', runs.map((r) => r.ms), fmtMs, false)}${line('Tokens', runs.map((r) => r.tokens), fmtTokens, false)}
++${rows.map((row) => line(html`<span class="br-mono">${row.task_id}</span>`, row.scores, fmtScore))}
++</tbody></table></div>
++${rows.some((row) => runs.some((r) => ((r.tasks.find((t) => t.task_id === row.task_id) || {}).artifacts || []).length)) ? html`<section class="bm-section" aria-labelledby="br-cmp-out-h"><h2 id="br-cmp-out-h">Outputs side by side</h2>${rows.map((row) => html`<div class="br-cmp-row"><p class="br-task-id br-mono">${row.task_id}</p><div class="br-cmp-outs" style="--n:${runs.length}">${runs.map((r) => {
++ const t = r.tasks.find((x) => x.task_id === row.task_id);
++ const a = t && t.artifacts && t.artifacts[0];
++ return html`<div class="br-cmp-out"><p class="br-cmp-sub">${r.model}</p>${a ? artifact(a, `${r.model} ${row.task_id}`) : html`<p class="br-fine">No output</p>`}</div>`;
++ })}</div></div>`)}</section>` : ''}
++</div>`;
++}
++
++function modelPage({ model, summary, runs = [], titles = {} }) {
++ const rows = (summary && summary.benchmarks) || [];
++ return html`<div class="bm-wrap br" id="br-model">
++<nav class="br-crumbs" aria-label="Breadcrumb"><a href="/benchmarks">Benchmarks</a><span aria-hidden="true">/</span><span>Models</span></nav>
++<header class="bm-page-h"><p class="bm-kicker">Model</p><h1 class="br-mono-h">${model}</h1><p class="bm-lede">${summary && summary.runs ? `${plural(summary.runs, 'verified run')} across ${plural(rows.length, 'benchmark')}.` : 'No verified runs yet.'}</p></header>
++<section class="bm-section" aria-labelledby="br-mb-h"><h2 id="br-mb-h">By benchmark</h2>
++${rows.length ? html`<div class="br-table-wrap"><table class="br-table"><thead><tr><th scope="col">Benchmark</th><th scope="col">Median</th><th scope="col" class="br-num">Spread</th><th scope="col" class="br-num">Cost / run</th><th scope="col" class="br-num">Time</th><th scope="col" class="br-num">Runs</th></tr></thead><tbody>${rows.map((b) => html`<tr><td><a href="${benchmarkHref(b.benchmark_id)}">${b.title}</a> ${kindChip(b.kind)}</td><td>${scoreBar(b.score)}</td><td class="br-num">${b.runs > 1 ? `${fmtScore(b.worst)}–${fmtScore(b.best)}` : '—'}</td><td class="br-num">${fmtUsd(b.cost_usd)}</td><td class="br-num">${fmtMs(b.ms)}</td><td class="br-num">${b.runs}</td></tr>`)}</tbody></table></div>` : empty('Nothing measured yet', 'This model shows up here after its first verified run.', html`<a class="bm-btn bm-btn-small" href="/benchmarks">Pick a benchmark</a>`)}</section>
++${runs.length ? html`<section class="bm-section" aria-labelledby="br-mr-h"><h2 id="br-mr-h">Latest runs</h2>${runsList(runs, { showBenchmark: true, titles })}</section>` : ''}
++</div>`;
++}
++
++module.exports = {
++ benchmarksIndex, benchmarkPage, runPage, comparePage, modelPage, valueChart, METRICS,
++ fmtScore, fmtUsd, fmtMs, fmtTokens, ordinal, benchmarkCard, render,
++};
ddiff --git a/test/results-pages.test.js b/test/results-pages.test.js
nnew file mode 100644
iindex 0000000..4148855
---- /dev/null
++++ b/test/results-pages.test.js
@@@ -0,0 +1,175 @@
++'use strict';
++
++/**
++ * The results pages (stage B2) and the rules they rest on.
++ *
++ * Under test: every page renders from the registry's own data (benchmarks, a benchmark with its rankings and value
++ * chart, a run, compare, a model); dynamic text is escaped; an unlisted benchmark is reachable by its id but listed
++ * to nobody but its owner, and its runs stay off a model's page; the rankings'
++ * `since` and `harness` filters work on their own; the value chart marks the frontier and only the frontier.
++ */
++const assert = require('assert');
++const { boot, check, done } = require('./helpers/boot');
++const results = require('../server/views/results');
++
++const TASKS = [
++ { id: 't1', prompt: 'Build a one-screen arcade game with a paddle, bricks, a ball and a score.', files: [], expected: null, holdout: false },
++ { id: 't2', prompt: 'A held-out snake game.', files: [], expected: null, holdout: true },
++];
++
++function definition(over = {}) {
++ return {
++ title: 'Pages test arena',
++ description: 'A benchmark for the page tests.',
++ kind: 'games.play',
++ tags: ['games'],
++ visibility: 'public',
++ tasks: TASKS,
++ judges: [{ type: 'game_outcome', version: '1.0', config: {} }],
++ scoring: { method: 'mean', scale: { min: 0, max: 100 } },
++ limits: { ms: 120000, tokens: 200000, usd: 0.5 },
++ matrix: { models: ['model-a'], harnesses: ['claude-code'] },
++ ...over,
++ };
++}
++
++function runDoc(benchmark, over = {}) {
++ return {
++ benchmark,
++ model: 'model-a',
++ route: { provider: 'acme' },
++ provider: 'acme',
++ harness: { name: 'claude-code', version: '3.1.0' },
++ settings: { temperature: 1, effort: 'high' },
++ executor: { kind: 'codes', id: 'tsk_pages' },
++ tasks: [
++ { task_id: 't1', score: 80, cost_usd: 0.01, ms: 30000, tokens: 5000, artifacts: [{ kind: 'html', url: 'https://bench-render.openvibe.services/render/w/ent_pages' }] },
++ { task_id: 't2', score: 60, cost_usd: 0.01, ms: 30000, tokens: 5000, artifacts: [] },
++ ],
++ score: 70,
++ cost_usd: 0.02,
++ ms: 60000,
++ tokens: 10000,
++ ...over,
++ };
++}
++
++(async () => {
++ const t = await boot();
++ const alice = t.network.addUser('alice');
++ const bob = t.network.addUser('bob');
++ const publisher = t.network.serviceToken({ sub: 'svc:codes', audience: 'openvibe.bench', cap: ['bench.run.publish', 'bench.benchmark.write'] });
++
++ const create = async (over, as) => {
++ const res = await t.get('/api/v1/benchmarks', as ? { as, json: definition(over) } : { bearer: publisher, json: definition(over) });
++ assert.strictEqual(res.status, 201, res.text);
++ const id = res.json().benchmark.id;
++ const frozen = await t.get(`/api/v1/benchmarks/${id}/versions`, as ? { as, json: {} } : { bearer: publisher, json: {} });
++ assert.strictEqual(frozen.status, 201, frozen.text);
++ return { id, version: frozen.json().version.version };
++ };
++ const publish = async (ref, over) => {
++ const res = await t.get('/api/v1/runs', { bearer: publisher, json: runDoc(ref, over) });
++ assert.strictEqual(res.status, 201, res.text);
++ return res.json().run.id;
++ };
++
++ const pub = await create({ title: 'Pages <script>alert(1)</script> arena' });
++ const cheap = await publish(`${pub.id}@${pub.version}`, { model: 'model-cheap', score: 60, cost_usd: 0.002 });
++ const best = await publish(`${pub.id}@${pub.version}`, { model: 'model-best', score: 90, cost_usd: 0.05 });
++ await publish(`${pub.id}@${pub.version}`, { model: 'model-worse', score: 55, cost_usd: 0.04, harness: { name: 'opencode', version: '1.4.2' } });
++ const priv = await create({ title: 'Alice unlisted prompts', visibility: 'unlisted' }, alice);
++ await publish(`${priv.id}@${priv.version}`, { model: 'model-best', score: 12, cost_usd: 0.01 });
++
++ await check('the benchmarks page lists the public benchmarks and escapes their titles', async () => {
++ const res = await t.get('/benchmarks');
++ assert.strictEqual(res.status, 200, res.text.slice(0, 300));
++ assert.ok(res.text.includes('Pages &lt;script&gt;alert(1)&lt;/script&gt; arena'), 'the title is escaped');
++ assert.ok(!res.text.includes('<script>alert(1)</script>'), 'never raw');
++ assert.ok(!res.text.includes('Alice unlisted prompts'), 'an unlisted benchmark is not listed');
++ });
++
++ await check('a benchmark page shows its rankings, its value chart and its runs', async () => {
++ const res = await t.get(`/benchmarks/${pub.id}`);
++ assert.strictEqual(res.status, 200, res.text.slice(0, 300));
++ assert.ok(res.text.includes('id="br-rank"'), 'the rankings section');
++ assert.ok(res.text.includes('class="br-chart"'), 'the value chart');
++ for (const m of ['model-best', 'model-cheap', 'model-worse']) assert.ok(res.text.includes(m), `${m} is ranked`);
++ assert.ok(res.text.includes('Held out'), 'the holdout task is counted, its prompt is not shown');
++ assert.ok(!res.text.includes('A held-out snake game.'), 'the holdout prompt never reaches the page');
++ const value = await t.get(`/benchmarks/${pub.id}?metric=value`);
++ assert.ok(value.text.includes('aria-current="page"') && value.text.includes('/ $'), 'the value tab is current and shows score per dollar');
++ });
++
++ await check('a run page shows the environment, the score, the provenance and the outputs', async () => {
++ const res = await t.get(`/runs/${best}`);
++ assert.strictEqual(res.status, 200, res.text.slice(0, 300));
++ assert.ok(res.text.includes('model-best'));
++ assert.ok(res.text.includes('Verified'), 'the provenance badge');
++ assert.ok(res.text.includes('claude-code 3.1.0'), 'the harness and its version');
++ assert.ok(res.text.includes('src="https://bench-render.openvibe.services/render/w/ent_pages"'), 'an output from the render origin is framed');
++ assert.ok(res.text.includes('sandbox="'), 'and sandboxed');
++ assert.ok(res.text.includes('A held-out task'), 'a holdout task keeps its prompt hidden on the run page too');
++ assert.strictEqual((await t.get('/runs/brn_nope')).status, 404);
++ });
++
++ await check('compare puts runs side by side and marks the best in each row', async () => {
++ const res = await t.get(`/compare?runs=${best},${cheap}`);
++ assert.strictEqual(res.status, 200, res.text.slice(0, 300));
++ assert.ok(res.text.includes('model-best vs model-cheap'));
++ assert.ok(res.text.includes('is-best'), 'a best value is marked');
++ assert.strictEqual((await t.get('/compare')).status, 200, 'an empty compare explains itself');
++ });
++
++ await check('a model page shows the public benchmarks only', async () => {
++ const res = await t.get('/models/model-best');
++ assert.strictEqual(res.status, 200, res.text.slice(0, 300));
++ assert.ok(res.text.includes('Pages &lt;script&gt;'), 'its public benchmark');
++ assert.ok(!res.text.includes('Alice unlisted prompts'), 'never an unlisted benchmark, not even its title');
++ assert.strictEqual((await t.get('/models/nobody-ran-this')).status, 404);
++ });
++
++ await check('an unlisted benchmark is reachable by its id and listed to nobody but its owner', async () => {
++ assert.strictEqual((await t.get(`/api/v1/benchmarks/${priv.id}`)).status, 200, 'reachable by id');
++ const page = await t.get(`/benchmarks/${priv.id}`);
++ assert.strictEqual(page.status, 200, 'its page too');
++ assert.ok(page.text.includes('noindex'), 'but kept out of search engines');
++ const list = await t.get('/api/v1/benchmarks');
++ assert.ok(!list.json().benchmarks.some((b) => b.id === priv.id), 'never in the public list');
++ const mine = await t.get('/api/v1/benchmarks?mine=1', { as: alice });
++ assert.ok(mine.json().benchmarks.some((b) => b.id === priv.id), 'the owner lists it with ?mine=1');
++ const theirs = await t.get('/api/v1/benchmarks?mine=1', { as: bob });
++ assert.ok(!theirs.json().benchmarks.some((b) => b.id === priv.id), 'nobody else does');
++ });
++
++ await check('the rankings filter by since alone and by harness name', async () => {
++ const since = await t.get(`/api/v1/rankings?benchmark=${pub.id}&metric=score&since=2000-01-01T00:00:00Z`);
++ assert.strictEqual(since.status, 200, since.text);
++ assert.strictEqual(since.json().rankings.entries.length, 3, '`since` without `harness` is its own placeholder');
++ const harness = await t.get(`/api/v1/rankings?benchmark=${pub.id}&metric=score&harness=opencode`);
++ assert.strictEqual(harness.status, 200, harness.text);
++ assert.deepStrictEqual(harness.json().rankings.entries.map((e) => e.model), ['model-worse'], 'the harness name matches');
++ });
++
++ await check('the value chart marks the frontier and only the frontier', () => {
++ const svg = String(results.valueChart([
++ { model: 'cheap', score: 60, cost_usd: 0.002 },
++ { model: 'best', score: 90, cost_usd: 0.05 },
++ { model: 'dominated', score: 55, cost_usd: 0.04 },
++ ]));
++ const front = [...svg.matchAll(/<g class="br-dot( is-front)?">.*?<text[^>]*>([^<]+)<\/text>/g)].map((m) => [m[2], Boolean(m[1])]);
++ const by = Object.fromEntries(front);
++ assert.strictEqual(by.cheap, true);
++ assert.strictEqual(by.best, true);
++ assert.strictEqual(by.dominated, false, 'a model both dearer and worse than another is off the frontier');
++ assert.ok(String(results.valueChart([])).includes('fills in with the first verified runs'), 'the empty chart says so');
++ });
++
++ await check('the render origin asks Cloudflare not to rewrite its frames', async () => {
++ const res = await t.get('/render/w/ent_nope');
++ assert.match(String(res.headers.get('cache-control')), /no-transform/);
++ });
++
++ await check('stop', async () => { await t.close(); });
++ done();
++})();