openvibers/openvibe.bench Commits
Bench pages move to Report's AI section; the API stays (#150)
Changes
@@@ -3,6 +3,8 @@ NODE_ENV=productionPORT=5230HOST=127.0.0.1BASE_URL=https://bench.openvibe.services++# The Report room owns the public pages since 2026-11-08; old HTML pages 301 here (the API stays on this origin).++OV_REPORT_PUBLIC_URL=https://openvibe.report# Codes handoff page opened by the benchmark's "Run it yourself" action.CODES_BENCH_RUN_URL=https://openvibe.codesTRUST_PROXY=2@@@ -2,6 +2,7 @@## Unreleased++- **The public pages moved to OpenVibe.Report's AI section (2026-11-08).** `/`, `/docs`, `/benchmarks`, `/models/*`, `/compare` and the other page routes answer `301` to `https://openvibe.report/ai` counterparts; the API, MCP, render origin, badges and embeds stay on bench.openvibe.services. New public reads feed Report: `GET /api/v1/runs`, `/api/v1/overview` (with every public model and its last run) and `/api/v1/models/:model/summary` (per-benchmark and per-task medians, cost, recent runs). `/api/v1/benchmarks` now carries each benchmark's runs and last run.- **The Exchange arena (`chat.arena`).** A new kind, and a first-party API only OpenVibe.Exchange may call:`POST /api/v1/arena/rounds` builds a round from two answers the Exchange already has, and`POST /api/v1/arena/rounds/:id/votes` records the person's own `human_pairwise` judgement from@@@ -4,6 +4,8 @@ The shared AI benchmark and comparison engine for every OpenVibe room (plan 2026Bench is the comparative-evaluation authority. AI stays the run authority, Report the analytics authority, Route the learner, and each room keeps its own pages. Bench owns kinds, tasks, pinned rounds, entries, judgements, ratings, boards, the open dataset and private evals.++**The public pages moved to OpenVibe.Report's AI section (2026-11-08):** `https://openvibe.report/ai`, `/ai/<benchmark>` and `/ai/models/<model>`. This origin's old HTML pages answer `301` there; its API, MCP endpoint, render origin, badges and embeds stay here.++- Service id: `bench`- Port: `5230`- Public origin: `https://bench.openvibe.services`@@@ -29,7 +31,9 @@ Tests use a temporary PGlite database, an in-process mock Network and a fake AI;## API--JSON under `/api/v1`; errors are `application/problem+json`. See `/docs` for the full list. The MCP endpoint is `POST /mcp`.++JSON under `/api/v1`; errors are `application/problem+json`. The full list is in `/openapi.json` and Report's `/ai/method`. The MCP endpoint is `POST /mcp`.++++The public reads Report's AI pages use are `GET /api/v1/overview`, `GET /api/v1/benchmarks`, `GET /api/v1/rankings`, `GET /api/v1/runs`, `GET /api/v1/models/:model/summary` and `GET /api/v1/runs/:id`; the long list is at `/openapi.json` and in Report's method page.<!-- versions:start -->- openvibe-contracts: v0.338.0@@@ -3,7 +3,7 @@"name": "OpenVibe.Bench","version": "0.1.0","status": "v1",-- "summary": "The shared AI benchmark and comparison engine for every room: kinds, pinned rounds, blind judgements, elo/pareto/cost boards, an open dataset per kind, private evals and an MCP endpoint.",++ "summary": "The shared AI benchmark and comparison engine for every room: kinds, pinned rounds, blind judgements, elo/pareto/cost boards, an open dataset per kind, private evals and an MCP endpoint. Its public pages are OpenVibe.Report's AI section since 2026-11-08; this origin answers 301 for them and keeps the API.","contracts": "openvibe-contracts v0.338.0","sdk": "openvibe-sdk v0.94.0","shared": "openvibe-shared v4.155.0",@@@ -273,6 +273,9 @@ async function createApp(opts = {}) {});// ── Pages ───────────────────────────────────────────────++ // The public pages moved to OpenVibe.Report's AI section (2026-11-08). The API, MCP, render, badge and embed++ // routes above stay; an HTML page is a 301 to its Report counterpart.++ app.use(require('./http/redirect').createReportRedirects({ reportUrl: config.reportUrl }));app.use(rateLimit({ windowMs: 60_000, limit: Number(process.env.BENCH_RATE_LIMIT_PER_MIN) || 300, standardHeaders: true, legacyHeaders: false }));app.use('/render', createRenderRoutes(ctx));app.use('/vendor', require('./http/vendor').createVendorRoutes());@@@ -996,6 +996,7 @@ function createRegistry(s, { log = console, sharing = null } = {}) {cost_per_solved: medScore > 0 && medCost > 0 ? Math.round((medCost / (medScore / 100)) * 1e6) / 1e6 : null,contamination: contaminationOf(model, list.map((item) => item.run)),horizon: horizonOfModel(list.map((item) => item.run)),++ last_run: list.reduce((at, item) => (!at || item.run.created_at > at ? item.run.created_at : at), null),};entries.push(entry);}@@@ -1191,7 +1192,7 @@ function createRegistry(s, { log = console, sharing = null } = {}) {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 } = {}) {++ async function listRuns({ benchmark = null, model = null, kind = null, createdBy = null, verifiedOnly = false, publicOnly = false, limit = 20, offset = 0 } = {}) {const where = [];const params = [];const add = (clause, value) => { params.push(value); where.push(clause.replace('?', `$${params.length}`)); };@@@ -1202,13 +1203,16 @@ function createRegistry(s, { log = console, sharing = null } = {}) {if (ref.version != null) add('benchmark_version = ?', ref.version);}if (model) add('model = ?', String(model));++ if (kind) add("benchmark_id IN (SELECT id FROM bench_benchmarks WHERE kind = ? AND visibility = 'public')", String(kind));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 limitAt = params.length;++ params.push(Math.max(0, Number(offset) || 0));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}`,++ `SELECT * FROM bench_runs ${where.length ? `WHERE ${where.join(' AND ')}` : ''} ORDER BY created_at DESC, id DESC LIMIT $${limitAt} OFFSET $${params.length}`,params);return rows.map((row) => wireRun(row));}@@@ -1248,10 +1252,125 @@ function createRegistry(s, { log = console, sharing = null } = {}) {return { model: String(model || ''), runs: rows.length, benchmarks: out };}++ /**++ * Activity per public benchmark: how many runs it has and when its newest one landed. The overview and the++ * benchmark list read this; a benchmark with no run has no date, never a made-up one.++ */++ async function runActivity() {++ const rows = await db.many(++ "SELECT r.benchmark_id, COUNT(*)::int AS runs, MAX(r.created_at) AS last_run"++ + " FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id"++ + " WHERE b.visibility = 'public' GROUP BY r.benchmark_id");++ return new Map(rows.map((r) => [r.benchmark_id, { runs: num(r.runs), last_run: r.last_run ? iso(r.last_run) : null }]));++ }++++ /** Public totals for the AI overview and its method page. Counts only; no model names, no invented numbers. */++ async function overview() {++ const [benchmarks, activity, recent] = await Promise.all([++ listBenchmarks({ limit: 500 }),++ runActivity(),++ listRuns({ publicOnly: true, limit: 12 }),++ ]);++ const publicList = benchmarks.filter((b) => b.visibility === 'public');++ for (const b of publicList) {++ const a = activity.get(b.id);++ b.runs = a ? a.runs : 0;++ b.last_run = a ? a.last_run : null;++ }++ const totals = await db.maybe(++ "SELECT COUNT(*)::int AS runs, COUNT(DISTINCT r.model)::int AS models, MAX(r.created_at) AS last_run"++ + " FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id WHERE b.visibility = 'public'");++ const tasks = await db.maybe(++ 'SELECT COALESCE(SUM(jsonb_array_length(v.definition->\'tasks\')), 0)::int AS tasks'++ + ' FROM bench_benchmark_versions v JOIN bench_benchmarks b ON b.id = v.benchmark_id'++ + " WHERE b.visibility = 'public' AND v.version = b.latest_version");++ const modelRows = await db.many(++ 'SELECT r.model, COUNT(*)::int AS runs, MAX(r.created_at) AS last_run'++ + ' FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id'++ + " WHERE b.visibility = 'public' GROUP BY r.model ORDER BY MAX(r.created_at) DESC, r.model");++ return {++ kinds: require('../kinds').KINDS.length,++ benchmarks: publicList.length,++ tasks: num(tasks && tasks.tasks, 0),++ runs: num(totals && totals.runs, 0),++ models: num(totals && totals.models, 0),++ last_run: totals && totals.last_run ? iso(totals.last_run) : null,++ list: publicList.map((b) => ({++ id: b.id, title: b.title, kind: b.kind, description: b.description,++ latest_version: b.latest_version, runs: b.runs, last_run: b.last_run,++ })),++ model_list: modelRows.map((r) => ({ model: r.model, runs: num(r.runs), last_run: r.last_run ? iso(r.last_run) : null })),++ recent,++ };++ }++++ /**++ * One model's public record: what the verified runs say per benchmark, its task-level best and worst, every++ * public run newest first, and the money and time those runs cost. A submitted run is labelled and never mixed++ * into the medians.++ */++ async function modelDetail(model) {++ const name = String(model || '');++ const summary = await modelSummary(name);++ const runs = await listRuns({ model: name, publicOnly: true, limit: 100 });++ const lastByBenchmark = new Map();++ for (const run of runs) {++ if (!lastByBenchmark.has(run.benchmark_id)) lastByBenchmark.set(run.benchmark_id, run.created_at);++ }++ for (const b of summary.benchmarks) b.last_run = lastByBenchmark.get(b.benchmark_id) || null;++++ const taskRows = await db.many(++ "SELECT r.benchmark_id, r.created_at, t.task_id, t.score"++ + " FROM bench_run_tasks t JOIN bench_runs r ON r.id = t.run_id"++ + " JOIN bench_benchmarks b ON b.id = r.benchmark_id"++ + " WHERE r.model = $1 AND r.provenance = 'verified' AND b.visibility = 'public'"++ + ' ORDER BY t.task_id, r.created_at', [name]);++ const groups = new Map();++ for (const row of taskRows) {++ const key = `${row.benchmark_id}:${row.task_id}`;++ if (!groups.has(key)) groups.set(key, { benchmark_id: row.benchmark_id, task_id: String(row.task_id), scores: [], last_run: null });++ const g = groups.get(key);++ g.scores.push(num(row.score));++ const at = iso(row.created_at);++ if (!g.last_run || at > g.last_run) g.last_run = at;++ }++ const titles = new Map(summary.benchmarks.map((b) => [b.benchmark_id, b.title]));++ const tasks = [...groups.values()].map((g) => ({++ benchmark_id: g.benchmark_id,++ benchmark_title: titles.get(g.benchmark_id) || g.benchmark_id,++ task_id: g.task_id,++ runs: g.scores.length,++ score: Math.round(median(g.scores) * 1000) / 1000,++ best: Math.round(Math.max(...g.scores) * 1000) / 1000,++ worst: Math.round(Math.min(...g.scores) * 1000) / 1000,++ last_run: g.last_run,++ })).sort((a, b) => b.score - a.score || a.task_id.localeCompare(b.task_id));++++ const totals = await db.maybe(++ "SELECT COUNT(*)::int AS runs, COALESCE(SUM(r.cost_usd), 0)::numeric AS cost,"++ + " COALESCE(SUM(r.ms), 0)::int AS ms, COALESCE(SUM(r.tokens), 0)::int AS tokens,"++ + " MAX(r.created_at) AS last_run"++ + " FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id"++ + " WHERE r.model = $1 AND r.provenance = 'verified' AND b.visibility = 'public'", [name]);++ const round = (v, p) => (v == null ? null : Math.round(num(v) * p) / p);++ return {++ ...summary,++ last_run: totals && totals.last_run ? iso(totals.last_run) : null,++ total_cost_usd: round(totals && totals.cost, 1e6),++ total_ms: round(totals && totals.ms, 1),++ total_tokens: round(totals && totals.tokens, 1),++ best_task: tasks.length ? tasks[0] : null,++ worst_task: tasks.length ? tasks[tasks.length - 1] : null,++ tasks,++ recent_runs: runs,++ };++ }++return {createBenchmark, getBenchmark, getVersion, latestVersion, getDefinition, saveDraft, freezeVersion,forkBenchmark, setVisibility, findShared, publicDatasetLines,listBenchmarks, publishRun, getRun, compareRuns, rankings, percentile, listRuns, modelSummary, runCounts,++ runActivity, overview, modelDetail,taskEstimateSummary, addTaskEstimate, modelHorizon,};}@@@ -27,6 +27,9 @@ function load(env = process.env) {nodeEnv,isProduction,baseUrl,++ // The Report room now owns the public Bench pages (2026-11-08): an HTML page answers 301 here. The API,++ // MCP, render origin, badges and embeds stay on this origin.++ reportUrl: trim(env.OV_REPORT_PUBLIC_URL || 'https://openvibe.report'),// "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,nnew file mode 100644@@@ -0,0 +1,64 @@++'use strict';++++/**++ * The Bench pages moved into OpenVibe.Report's AI section (owner, 2026-11-08). Every public HTML page answers a++ * permanent redirect to its Report counterpart; the API, MCP, render origin, badges, embeds, sign-in, legal pages++ * and static assets stay here. A redirect keeps its query string only where Report reads one (`?tab=`).++ *++ * / → /ai++ * /benchmarks → /ai++ * /benchmarks/<id>[/…] → /ai/<id>++ * /models/<model> → /ai/models/<model>++ * /compare → /ai/compare++ * /docs → /ai/method++ * /showcase, /gallery, … → /ai++ */++++const DEFAULT_REPORT = 'https://openvibe.report';++const decode = (s) => { try { return decodeURIComponent(s); } catch { return s; } };++++/** Paths that are machine endpoints, frames or not pages of the old Bench site: never redirected. */++const KEEP = [/^\/render(?:\/|$)/, /^\/vendor(?:\/|$)/, /^\/badge(?:\/|$)/, /^\/embed(?:\/|$)/,++ /^\/api(?:\/|$)/, /^\/mcp(?:\/|$)/, /^\/auth(?:\/|$)/, /^\/shared(?:\/|$)/, /^\/\.well-known(?:\/|$)/,++ /^\/metrics(?:\/|$)/, /^\/release(?:\.json|-metrics)?(?:\/|$)/, /^\/robots\.txt$/, /^\/sitemap\.xml$/,++ /^\/favicon\.ico$/, /^\/assets(?:\/|$)/];++++function targetFor(pathname, reportUrl) {++ const base = String(reportUrl || DEFAULT_REPORT).replace(/\/+$/, '');++ const p = String(pathname || '/');++ if (p === '/' || p === '') return `${base}/ai`;++ if (p === '/docs' || p === '/docs/') return `${base}/ai/method`;++ if (p === '/compare' || p === '/compare/') return `${base}/ai/compare`;++ if (p === '/benchmarks' || p === '/benchmarks/') return `${base}/ai`;++ if (p === '/benchmarks/new' || p === '/benchmarks/new/') return `${base}/ai`;++ const bench = /^\/benchmarks\/([^/]+)(?:\/.*)?$/.exec(p);++ if (bench) return `${base}/ai/${encodeURIComponent(decode(bench[1]))}`;++ const model = /^\/models\/(.+)$/.exec(p);++ if (model) return `${base}/ai/models/${encodeURIComponent(decode(model[1]))}`;++ if (/^\/(?:showcase|gallery|pick|battle|ratings)(?:\/|$)/.test(p)) return `${base}/ai`;++ if (/^\/b\/[^/]+$/.test(p)) return `${base}/ai`;++ if (/^\/runs\/[^/]+$/.test(p)) return `${base}/ai`;++ if (p === '/llms.txt' || p === '/llms-full.txt') return `${base}${p}`;++ // A Markdown twin of a moved page: its Report twin is the moved page's own twin. An unknown twin is still a 404.++ if (p.endsWith('.md')) {++ const page = p === '/index.md' ? '/' : p.slice(0, -3);++ const moved = targetFor(page, reportUrl);++ return moved ? `${moved}.md` : null;++ }++ return null;++}++++function createReportRedirects({ reportUrl = DEFAULT_REPORT } = {}) {++ return function reportRedirect(req, res, next) {++ if (req.method !== 'GET' && req.method !== 'HEAD') return next();++ if (KEEP.some((re) => re.test(req.path))) return next();++ const target = targetFor(req.path, reportUrl);++ if (!target) return next();++ // ?tab= is the one query Report's AI board reads from the old links; everything else is dropped.++ const tab = req.query && typeof req.query.tab === 'string' && /^(elo|pareto|cost)$/.test(req.query.tab) ? `?tab=${req.query.tab}` : '';++ res.set('Cache-Control', 'public, max-age=3600');++ return res.redirect(301, target + tab);++ };++}++++module.exports = { createReportRedirects, targetFor, DEFAULT_REPORT };@@@ -186,6 +186,12 @@ function createRegistryApi(ctx) {q: req.query.q ? String(req.query.q) : null,limit: req.query.limit ? Number(req.query.limit) : 50,});++ const activity = await registry.runActivity();++ for (const b of list) {++ const a = activity.get(b.id);++ b.runs = a ? a.runs : 0;++ b.last_run = a ? a.last_run : null;++ }res.set('Cache-Control', mine ? 'private, no-store' : 'public, max-age=60');return res.json({ count: list.length, benchmarks: list });} catch (err) { return failed(res, err); }@@@ -267,6 +273,49 @@ function createRegistryApi(ctx) {} catch (err) { return failed(res, err); }});++ // The public run list: newest first, on public benchmarks only. It is what Report's AI pages read for a++ // model's history, a benchmark's latest results and the new-results feed; a submitted run is labelled by++ // `provenance` and is not in the verified rankings.++ r.get('/runs', async (req, res) => {++ try {++ const benchmark = req.query.benchmark ? String(req.query.benchmark).slice(0, 120) : null;++ const model = req.query.model ? String(req.query.model).slice(0, 160) : null;++ const kind = req.query.kind ? String(req.query.kind).slice(0, 80) : null;++ if (kind && !require('../kinds').kindById(kind)) return problem(res, 404, 'bench.kind_not_found', 'No such kind.');++ if (benchmark) {++ const bm = await registry.getBenchmark(benchmark.split('@')[0]);++ if (!bm || !await mayRead(req, bm) || bm.visibility !== 'public') {++ return problem(res, 404, 'bench.benchmark_not_found', 'No such public benchmark.');++ }++ }++ const limit = Math.min(100, Math.max(1, Number(req.query.limit) || 20));++ const offset = Math.max(0, Number(req.query.offset) || 0);++ const runs = await registry.listRuns({ benchmark, model, kind, publicOnly: true, limit, offset });++ res.set('Cache-Control', 'public, max-age=60');++ return res.json({ count: runs.length, offset, runs, last_run: runs.length ? runs[0].created_at : null });++ } catch (err) { return failed(res, err); }++ });++++ // Public counts for the AI overview and method page. Nothing here is a person or an unlisted benchmark.++ r.get('/overview', async (req, res) => {++ try {++ res.set('Cache-Control', 'public, max-age=60');++ return res.json(await registry.overview());++ } catch (err) { return failed(res, err); }++ });++++ // One model's public record: verified medians per benchmark, its task-level best and worst, every public run++ // newest first, and the cost and time those runs carried.++ r.get('/models/:model/summary', async (req, res) => {++ try {++ const model = String(req.params.model || '').slice(0, 160);++ if (!model) return invalid(res, 'model is required');++ const out = await registry.modelDetail(model);++ res.set('Cache-Control', 'public, max-age=60');++ return res.json(out);++ } catch (err) { return failed(res, err); }++ });++r.get('/runs/:id', async (req, res) => {try {const run = await registry.getRun(String(req.params.id || ''));@@@ -82,6 +82,9 @@ ${ep('POST', '/api/v1/benchmarks/:id/share', '<code>{ visibility: "private" | "u${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|horizon&harness=&since=', 'Medians per model over verified runs; value is score per dollar, speed is score per minute, horizon is the 50% human-time task length. since=YYYY-MM scores only tasks added in or after that month.')}++${ep('GET', '/api/v1/runs?benchmark=&model=&kind=&limit=&offset=', 'Public runs newest first, with their score, cost, time and provenance. The pages at openvibe.report/ai read this list.')}++${ep('GET', '/api/v1/models/:model/summary', 'One model’s verified record: per benchmark, its task-level best and worst, every public run and the total cost.')}++${ep('GET', '/api/v1/overview', 'Public counts (benchmarks, tasks, runs, models), every public model with its last run, and the newest runs.')}${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>@@@ -1,63 +1,48 @@'use strict';--// SEO-1: the public pages' Markdown twins (server/agent-twins.js). A twin is the page rendered signed out, and the--// page names it in its head and in the Link: header.++// The Bench public pages moved into OpenVibe.Report's AI section (owner, 2026-11-08). Every old page and its++// Markdown twin answer one permanent redirect to the Report counterpart; an unknown twin is still a 404, and the++// API keeps answering here.const assert = require('assert');--const fs = require('fs');--const path = require('path');--const { boot } = require('./helpers/boot');--const { TWIN_PATH_RE } = require('../server/agent-twins');----// Public pages from the sitemap that render without data.--const PAGES = ['/', '/docs', '/benchmarks'];++const { boot, check, done } = require('./helpers/boot');(async () => {const t = await boot();-- const origin = String(t.config.baseUrl).replace(/\/+$/, '');-- const reEsc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');-- let failed = 0;-- const check = async (name, fn) => {-- try { await fn(); console.log(` ✓ ${name}`); } catch (e) { failed++; console.log(` ✗ ${name}\n ${String(e.stack || e).split('\n').slice(0, 6).join('\n ')}`); }-- };++ const REPORT = 'https://openvibe.report';try {-- await check('every listed page has a twin, and the page names it', async () => {-- for (const p of PAGES) {-- assert.ok(TWIN_PATH_RE.test(p), `${p} is a twin path`);-- const page = await t.get(p);-- assert.strictEqual(page.status, 200, p);-- const twinPath = p === '/' ? '/index.md' : `${p}.md`;-- const href = `${origin}${twinPath}`;-- assert.ok(page.text.includes(`<link rel="alternate" type="text/markdown" href="${href}">`), `${p} names its twin in the head`);-- assert.match(String(page.headers.get('link') || ''), new RegExp(`<${reEsc(href)}>; rel="alternate"; type="text/markdown"`), `${p} names it in the Link header`);-- const twin = await t.get(twinPath);-- assert.strictEqual(twin.status, 200, twinPath);-- assert.match(String(twin.headers.get('content-type') || ''), /^text\/markdown/, `${twinPath} is Markdown`);-- assert.ok(twin.text.startsWith('---\ntitle:') && /\ncanonical: "/.test(twin.text), `${twinPath} carries the page's title and address`);-- // Markup only inside fenced code (a page's own code samples), never the page's scripts or styles.-- const prose = twin.text.replace(/~~~~[\s\S]*?~~~~/g, '');-- assert.ok(!/<(?:script|style)\b/i.test(prose) && twin.text.length > 150, `${twinPath} is the page's words`);++ await check('the old public pages answer one 301 to their Report counterparts', async () => {++ const list = await t.get('/api/v1/benchmarks?limit=1');++ const b = list.json().benchmarks[0];++ const pages = [++ ['/', '/ai'],++ ['/docs', '/ai/method'],++ ['/benchmarks', '/ai'],++ [`/benchmarks/${b.id}`, `/ai/${b.id}`],++ ['/compare', '/ai/compare'],++ ['/showcase', '/ai'],++ ['/gallery', '/ai'],++ ];++ for (const [p, target] of pages) {++ const r = await t.get(p);++ assert.strictEqual(r.status, 301, p);++ assert.strictEqual(r.headers.get('location'), `${REPORT}${target}`, p);}});-- await check('a path with no twin answers a Markdown 404, and a missing page names no twin', async () => {-- const twin = await t.get('/no-such-page-here.md');-- assert.strictEqual(twin.status, 404);-- const page = await t.get('/no-such-page-here');-- assert.ok(!/type="text\/markdown"/.test(page.text) && !/text\/markdown/.test(String(page.headers.get('link') || '')));++++ await check('a Markdown twin follows its page; an unknown twin stays a 404', async () => {++ const docs = await t.get('/docs.md');++ assert.strictEqual(docs.status, 301);++ assert.strictEqual(docs.headers.get('location'), `${REPORT}/ai/method.md`);++ const missing = await t.get('/no-such-page-here.md');++ assert.strictEqual(missing.status, 404);});-- if (t.network && typeof t.network.addUser === 'function') {-- await check('a page drawn for a signed-in person names no twin', async () => {-- const page = await t.get(PAGES[0], { as: t.network.addUser('twin-reader') });-- assert.ok(!/type="text\/markdown"/.test(page.text), 'no twin in the head');-- assert.ok(!/text\/markdown/.test(String(page.headers.get('link') || '')), 'no twin in the Link header');-- });-- }-- await check('the twins are mounted before the page routes', async () => {-- const src = fs.readFileSync(path.join(__dirname, '..', 'server', 'app.js'), 'utf8');-- const at = src.indexOf("require('./agent-twins').mountTwins(app,");-- assert.ok(at > 0 && at < src.indexOf('app.use(pageRoutes);'));++++ await check('the API and the agent manifest still answer here', async () => {++ assert.strictEqual((await t.get('/api/v1/kinds')).status, 200);++ assert.strictEqual((await t.get('/api/v1/overview')).status, 200);++ assert.strictEqual((await t.get('/.well-known/agent.json')).status, 200);});} finally {await t.close();}-- console.log(failed ? `\n${failed} failed` : '\nall passed');-- process.exit(failed ? 1 : 0);--})();++ done();++})().catch((e) => { console.error(e); process.exit(1); });@@@ -131,21 +131,6 @@ function cookieOf(res) {assert.strictEqual(counted, 2, 'the refused vote was not counted');});-- await check("Bench's own page votes without signing in", async () => {-- const page = await t.get('/showcase/round/' + rounds[2].id);-- assert.strictEqual(page.status, 200, page.text);-- const cookie = cookieOf(page);-- assert.ok(cookie.startsWith('bench_anon='), 'the page hands out the voter cookie');-- assert.ok(page.text.includes('data-vote-form="1"'), 'the signed-out visitor gets the form');-- assert.ok(!page.text.includes('Sign in to vote'), 'not the sign-in prompt');-- const vote = await t.get('/showcase/round/' + rounds[2].id, {-- method: 'POST', cookie, headers: FORM, body: 'outcome=A', ...SAME,-- });-- assert.strictEqual(vote.status, 302, vote.text);-- const row = await t.ctx.s.db.maybe("SELECT voter_kind FROM bench_judgements WHERE round_id = $1 ORDER BY at DESC LIMIT 1", [rounds[2].id]);-- assert.strictEqual(row.voter_kind, 'anon', 'the page vote is an anonymous vote');-- });--await check('a board says how many votes of each kind of voter it holds', async () => {const person = await t.get('/api/v1/showcase/rounds/' + rounds[0].id, { as: bob });assert.strictEqual(person.status, 200, person.text);ddeleted file mode 100644@@@ -1,122 +0,0 @@--'use strict';----/**-- * The battles (stage B9): blind pairs, the rules, and a vote that works without JavaScript.-- */----const assert = require('assert');--const { boot, check, done } = require('./helpers/boot');----const FORM = { 'content-type': 'application/x-www-form-urlencoded' };----(async () => {-- // One vote per hour for this person: the rate limit is part of the rules under test.-- const t = await boot({ env: { BENCH_BATTLE_PERSON_PER_HOUR: '1' } });-- const alice = t.network.addUser('alice');-- const bob = t.network.addUser('bob');-- const svc = t.network.serviceToken({ sub: 'svc:games', audience: 'openvibe.bench', cap: ['bench.entry.create'] });---- // Two finished rounds, each with two entries: two battles to hand out.-- const rounds = [];-- for (const n of [1, 2]) {-- const task = await t.get('/api/v1/kinds/design.ui/tasks', { as: alice, json: { prompt: 'A dashboard, prompt ' + n + '.', public: true } });-- assert.strictEqual(task.status, 201, task.text);-- const rr = await t.get('/api/v1/tasks/' + task.json().task.id + '/rounds', { as: alice, json: {} });-- assert.strictEqual(rr.status, 201, rr.text);-- const roundId = rr.json().round.id;-- const e1 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'acme', model: 'alpha-' + n, payload: { html: '<p>alpha ' + n + '</p>' }, cost_usd: 0.01 } });-- assert.strictEqual(e1.status, 201, e1.text);-- const e2 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'beta', model: 'beta-' + n, payload: { html: '<p>beta ' + n + '</p>' }, cost_usd: 0.02 } });-- assert.strictEqual(e2.status, 201, e2.text);-- rounds.push({ id: roundId, a: e1.json().entry.id, b: e2.json().entry.id });-- }---- await check('an anonymous reader is asked to sign in, and sees no pair', async () => {-- const r = await t.get('/battle');-- assert.strictEqual(r.status, 200, r.text);-- assert.ok(r.text.includes('Sign in'), 'the sign-in prompt');-- assert.ok(!r.text.includes('data-battle='), 'no pair for an anonymous reader');-- });---- await check('a signed-in person gets a blind pair', async () => {-- const r = await t.get('/battle', { as: bob });-- assert.strictEqual(r.status, 200, r.text);-- assert.ok(r.text.includes('data-battle="bbs_'), 'the battle id: ' + r.text.slice(0, 200));-- assert.ok(r.text.includes('data-slot="A"') && r.text.includes('data-slot="B"'), 'two panes');-- assert.ok(r.text.includes('Which one looks more finished?'), 'the kind\'s own question');-- assert.ok(!r.text.includes('alpha-1') && !r.text.includes('beta-1'), 'no model names before the vote');-- });---- await check('the vote is a form post that works without JavaScript', async () => {-- const page = await t.get('/battle', { as: bob });-- const battleId = /data-battle="(bbs_[^"]+)"/.exec(page.text)[1];-- const vote = await t.get('/battle', { as: bob, method: 'POST', headers: FORM, body: 'battle=' + encodeURIComponent(battleId) + '&outcome=A&answer=A' });-- assert.strictEqual(vote.status, 303, vote.text);-- assert.ok(String(vote.headers.get('location') || '').startsWith('/battle/revealed/'), 'the reveal page: ' + vote.headers.get('location'));-- const reveal = await t.get(vote.headers.get('location'), { as: bob });-- assert.strictEqual(reveal.status, 200, reveal.text);-- assert.ok(reveal.text.includes('alpha-') || reveal.text.includes('beta-'), 'the names are open after the vote');-- const row = await t.ctx.s.db.maybe("SELECT outcome, question, answer FROM bench_judgements WHERE judge = 'human_pairwise' ORDER BY at DESC LIMIT 1");-- assert.strictEqual(row.outcome, 'left');-- assert.strictEqual(row.question, 'Which one looks more finished?');-- assert.strictEqual(row.answer, 'A');-- });---- await check('a person never sees the same pair twice', async () => {-- const seen = await t.ctx.s.db.many('SELECT round_id, pair_key, voted_at FROM bench_battle_seen WHERE subject = (SELECT subject FROM bench_judgements ORDER BY at DESC LIMIT 1)');-- const pairs = new Set(seen.map((s) => s.pair_key));-- assert.strictEqual(pairs.size, seen.length, 'no pair twice: ' + JSON.stringify(seen));-- // Both rounds are used up now: there is nothing left to show this person.-- const more = await t.get('/battle', { as: bob });-- assert.strictEqual(more.status, 200, more.text);-- assert.ok(more.text.includes('No battle is waiting'), 'no pair left: ' + more.text.slice(0, 300));-- });---- await check('the rate limit stops the second vote', async () => {-- const before = await t.ctx.s.db.value("SELECT COUNT(*)::int AS n FROM bench_judgements WHERE judge = 'human_pairwise'");-- const rows = await t.ctx.s.db.many('SELECT id, round_id FROM bench_battle_seen WHERE voted_at IS NULL');-- assert.ok(rows.length >= 1, 'a pair is waiting');-- const vote = await t.get('/battle', { as: bob, method: 'POST', headers: FORM, body: 'battle=' + encodeURIComponent(rows[0].id) + '&outcome=B' });-- assert.strictEqual(vote.status, 429, vote.text);-- const after = await t.ctx.s.db.value("SELECT COUNT(*)::int AS n FROM bench_judgements WHERE judge = 'human_pairwise'");-- assert.strictEqual(after, before, 'no vote was written');-- });---- await check('an anonymous vote is refused, and a cross-site vote is refused', async () => {-- const rows = await t.ctx.s.db.many('SELECT id FROM bench_battle_seen WHERE voted_at IS NULL');-- const anon = await t.get('/battle', { method: 'POST', headers: FORM, body: 'battle=' + encodeURIComponent(rows[0].id) + '&outcome=A' });-- assert.ok(anon.status === 302 || anon.status === 303, 'the anonymous vote is sent to sign in: ' + anon.status);-- assert.ok(String(anon.headers.get('location') || '').startsWith('/auth/login'), 'to the sign-in page');-- const cross = await t.get('/battle', { as: bob, method: 'POST', headers: FORM, origin: 'https://evil.example', body: 'battle=' + encodeURIComponent(rows[0].id) + '&outcome=A' });-- assert.strictEqual(cross.status, 403, cross.text);-- });---- await check('the ratings board counts the battle vote, and the open data has no voter ids', async () => {-- const page = await t.get('/ratings', { as: bob });-- assert.strictEqual(page.status, 200, page.text);-- assert.ok(page.text.includes('Raw') && page.text.includes('Style controlled'), 'both columns');-- assert.ok(page.text.includes('alpha-'), 'a model on the board');-- // The board is a table: a band, a model, the scores with their intervals on one scale, and the votes.-- assert.ok(page.text.includes('class="br-table rt-table"'), 'the ranking table');-- assert.ok(page.text.includes('>Band<') && page.text.includes('>Votes<'), 'the columns');-- assert.ok(page.text.includes('class="rt-bar"'), 'the interval bar');-- assert.ok(page.text.includes('rt-band'), 'the band');-- // The style is explained in words, not as a number.-- assert.ok(!page.text.includes('Elo per 1000 characters'), 'no raw effect numbers: ' + page.text.slice(0, 200));---- const dump = await t.get('/api/v1/datasets/votes.jsonl');-- assert.strictEqual(dump.status, 200, dump.text);-- const lines = dump.text.trim().split('\n').filter(Boolean);-- assert.ok(lines.length >= 1, 'the dump has the vote');-- const subject = await t.ctx.s.db.value("SELECT subject FROM bench_judgements WHERE judge = 'human_pairwise' LIMIT 1");-- for (const line of lines) {-- const v = JSON.parse(line);-- assert.ok(v.left && v.right && v.outcome, 'a vote line: ' + line);-- assert.ok(!('subject' in v) && !('voter' in v) && !('user' in v), 'no voter id in the line: ' + line);-- assert.ok(!line.includes(subject), 'the voter is nowhere in the dump');-- }-- });---- await t.close();-- done();--})().catch((e) => { console.error(e); process.exit(1); });@@@ -30,8 +30,6 @@ const { boot, check, done } = require('./helpers/boot');assert.deepStrictEqual(s.owner_subject, { type: 'user', id: alice.subject });assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: alice })).status, 200);assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: bob })).status, 404);-- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: bob })).status, 404);-- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: alice })).status, 200);});await check('Specific people: the named person reads it; a stranger gets 404 and the page never leaks the title', async () => {@@@ -40,11 +38,9 @@ const { boot, check, done } = require('./helpers/boot');assert.strictEqual(put.status, 200, put.text);assert.strictEqual(put.json().audience, 'people');assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: bob })).status, 200);-- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: bob })).status, 200);-- const stranger = await t.get(`/benchmarks/${id}`, { as: sam });++ const stranger = await t.get(`/api/v1/benchmarks/${id}`, { as: sam });assert.strictEqual(stranger.status, 404);assert.ok(!stranger.text.includes('Board pack bench'));-- assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: sam })).status, 404);assert.strictEqual((await t.get(`/api/v1/benchmark/${id}/share`, { as: sam })).status, 404, 'the sheet is the owner’s');});@@@ -54,12 +50,11 @@ const { boot, check, done } = require('./helpers/boot');assert.strictEqual((await row(id)).audience, 'public');assert.strictEqual((await t.ctx.s.db.maybe('SELECT visibility FROM bench_benchmarks WHERE id = $1', [id])).visibility, 'public');assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 200);-- const list = (await t.get('/benchmarks')).text;-- assert.ok(list.includes('Going public'));++ assert.ok((await t.get('/api/v1/benchmarks')).json().benchmarks.some((b) => b.id === id), 'in the API list');assert.strictEqual((await putShare(id, { audience: 'private' })).status, 200);assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 404);-- assert.ok(!(await t.get('/benchmarks')).text.includes('Going public'));-- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: alice })).status, 200, 'the owner still reads it');++ assert.ok(!(await t.get('/api/v1/benchmarks')).json().benchmarks.some((b) => b.id === id), 'out of the API list');++ assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: alice })).status, 200, 'the owner still reads it');});await check('the sheet lists the members to the owner, and the old form keeps the row in step', async () => {@@@ -69,10 +64,10 @@ const { boot, check, done } = require('./helpers/boot');assert.strictEqual(mine.status, 200);assert.strictEqual(mine.json().members[0].username, 'sam');assert.strictEqual(mine.json().you.role, 'owner');-- // The registry's own form (POST /benchmarks/:id/share) is the old way to change visibility.-- const form = await t.get(`/benchmarks/${id}/share`, { as: alice, body: 'visibility=public', headers: { 'content-type': 'application/x-www-form-urlencoded', origin: 'https://bench.openvibe.services' } });-- assert.ok([200, 303].includes(form.status), form.text.slice(0, 200));++ // The API is the one way now that the HTML pages are gone; it keeps the old visibility column in step.++ assert.strictEqual((await putShare(id, { audience: 'public' })).status, 200);assert.strictEqual((await row(id)).audience, 'public');++ assert.strictEqual((await t.ctx.s.db.maybe('SELECT visibility FROM bench_benchmarks WHERE id = $1', [id])).visibility, 'public');});await done();@@@ -1,49 +1,23 @@'use strict';--/**-- * The home page lists the kinds, so each kind it lists is a schema.org Dataset: the JSONL the API serves for that kind-- * is the dataset itself (url and distribution), the description restates the kind's own room, judge and entry type, and-- * no licence is claimed because Bench states none. /docs publishes no dataset, so it carries no Dataset node.-- */++// The Bench pages (with their schema.org Dataset nodes) moved into OpenVibe.Report's AI section (2026-11-08).++// The open datasets themselves stay on Bench's API, which still lists every public kind.const assert = require('assert');const { boot, check, done } = require('./helpers/boot');--// One JSON-LD script per page: every node lives in the page's @graph.--const scripts = (html) => [...String(html).matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));--const nodes = (html, type) => scripts(html)-- .flatMap((x) => (Array.isArray(x['@graph']) ? x['@graph'] : [x])).filter((x) => x['@type'] === type);--(async () => {const t = await boot();-- let kinds;---- await check('the home page carries one Dataset per kind, describing what the page lists', async () => {-- const r = await t.get('/');-- assert.strictEqual(r.status, 200);-- kinds = (await t.get('/api/v1/kinds')).json().kinds;-- assert.ok(kinds.length >= 2, `only ${kinds.length} kinds`);-- assert.strictEqual(scripts(r.text).length, 1, 'the page carries exactly one JSON-LD script');-- const sets = nodes(r.text, 'Dataset');-- assert.deepStrictEqual(sets.map((d) => d.name), kinds.map((k) => k.name));-- for (const [i, d] of sets.entries()) {-- const k = kinds[i];-- const url = `https://bench.openvibe.services/api/v1/datasets/${encodeURIComponent(k.id)}.jsonl`;-- assert.strictEqual(d.url, url);-- assert.strictEqual(d.distribution['@type'], 'DataDownload');-- assert.strictEqual(d.distribution.contentUrl, url);-- assert.strictEqual(d.creator.name, 'OpenVibe.Bench');-- assert.ok(d.description.includes(k.room) && d.description.includes(k.name), d.description);-- assert.ok(!('license' in d), 'no licence is claimed for data Bench does not license');-- assert.ok(r.text.includes(`href="/api/v1/boards/${k.id}?type=elo"`), `${k.id} is not listed on the page`);++ await check('the home page redirects and the dataset API stays on Bench', async () => {++ const home = await t.get('/');++ assert.strictEqual(home.status, 301);++ const kinds = await t.get('/api/v1/kinds');++ assert.strictEqual(kinds.status, 200);++ const list = kinds.json().kinds;++ assert.ok(list.length >= 2, `only ${list.length} kinds`);++ for (const k of list) {++ const data = await t.get(`/api/v1/datasets/${encodeURIComponent(k.id)}.jsonl`);++ assert.strictEqual(data.status, 200, k.id);}});---- await check('/docs publishes no dataset and therefore carries no Dataset node', async () => {-- const r = await t.get('/docs');-- assert.strictEqual(r.status, 200);-- assert.deepStrictEqual(nodes(r.text, 'Dataset'), []);-- });--await check('stop', async () => { await t.close(); });--done();})();ddeleted file mode 100644@@@ -1,61 +0,0 @@--'use strict';----/** The embed (B10): only that route may be framed, no cookies, and voting opens Bench in a new tab. */----const assert = require('assert');--const { boot, check, done } = require('./helpers/boot');----(async () => {-- const t = await boot();-- const alice = t.network.addUser('alice');-- const bob = t.network.addUser('bob'); // the round belongs to somebody else: a battle is never your own-- const entrySvc = t.network.serviceToken({ sub: 'svc:room', audience: 'openvibe.bench', cap: ['bench.entry.create'] });---- const task = await t.get('/api/v1/kinds/games.play/tasks', { as: bob, json: { prompt: 'A game for an embed.', public: true } });-- const rr = await t.get(`/api/v1/tasks/${task.json().task.id}/rounds`, { as: bob, json: {} });-- const roundId = rr.json().round.id;-- const e1 = await t.get(`/api/v1/rounds/${roundId}/entries`, { bearer: entrySvc, json: { provider: 'acme', model: 'alpha-1', payload: { html: '<h1>a</h1>' } } });-- const e2 = await t.get(`/api/v1/rounds/${roundId}/entries`, { bearer: entrySvc, json: { provider: 'beta', model: 'beta-2', payload: { html: '<h1>b</h1>' } } });-- assert.strictEqual(e1.status, 201, e1.text);-- assert.strictEqual(e2.status, 201, e2.text);---- // A battle needs a finished round: the job would do it, the test does it directly.-- await t.ctx.s.db.exec("UPDATE bench_rounds SET status = 'finished' WHERE id = $1", [roundId]);-- const battle = await t.get('/battle', { as: alice });-- assert.strictEqual(battle.status, 200, battle.text.slice(0, 200));-- const id = /data-battle="([^"]+)"/.exec(battle.text)[1];---- await check('the embed may be framed, and every other route may not', async () => {-- const e = await t.get('/embed/battle/' + id);-- assert.strictEqual(e.status, 200, e.text.slice(0, 200));-- assert.ok(/frame-ancestors \*/.test(e.headers.get('content-security-policy')), 'the embed may be framed: ' + e.headers.get('content-security-policy'));-- const home = await t.get('/');-- assert.ok(/frame-ancestors 'none'/.test(home.headers.get('content-security-policy')), 'the rest refuses frames: ' + home.headers.get('content-security-policy'));-- assert.strictEqual(home.headers.get('x-frame-options'), 'DENY');-- });---- await check('the embed reads and sets no cookie', async () => {-- const e = await t.get('/embed/battle/' + id);-- assert.strictEqual(e.headers.get('set-cookie'), null, 'no cookie is set');-- const home = await t.get('/');-- assert.strictEqual(home.headers.get('set-cookie'), null, 'and none on the other pages either');-- });---- await check('the embed keeps the frames sandboxed and sends the vote to a new tab', async () => {-- const e = await t.get('/embed/battle/' + id);-- assert.ok(e.text.includes('sandbox="allow-scripts allow-same-origin"'), 'the frames are sandboxed');-- assert.ok(e.text.includes('target="_blank"'), 'the vote opens a new tab');-- assert.ok(e.text.includes('rel="noopener"'), 'and it is safe');-- assert.ok(e.text.includes('noindex'), 'it is not indexed');-- assert.ok(e.text.includes('OpenVibe Bench'), 'it links back');-- });---- await check('an unknown battle is a plain 404, still framable', async () => {-- const e = await t.get('/embed/battle/bbs_nope');-- assert.strictEqual(e.status, 404);-- assert.ok(/frame-ancestors \*/.test(e.headers.get('content-security-policy')));-- });---- await t.close();-- done();--})().catch((e) => { console.error(e); process.exit(1); });@@@ -114,11 +114,6 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01assert.strictEqual(rankings.since.basis, 'tasks');assert.strictEqual(rankings.entries.find((e) => e.model === 'model-a').score, 20, 'only the fresh task is scored');assert.strictEqual(rankings.entries.find((e) => e.model === 'model-b').score, 80);---- const page = await t.get(`/benchmarks/${made.id}?since=2026-03`);-- assert.strictEqual(page.status, 200, page.text.slice(0, 300));-- assert.ok(page.text.includes('On tasks since March 2026'), 'the page names the fresh set beside the board');-- assert.ok(page.text.includes('All-time board'), 'and offers the all-time board');});await check('a held-out task never leaks, and a submitted run gets no score for it', async () => {@@@ -230,50 +225,9 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01assert.strictEqual(entry.horizon.runs, 3);assert.ok(Math.abs(entry.horizon.horizon_minutes - 40) < 6, `horizon ${entry.horizon.horizon_minutes}`);-- const page = await t.get(`/benchmarks/${made.id}?metric=horizon`);-- assert.ok(page.text.includes('br-c-horizon'), 'the board has a horizon column');-- assert.ok(page.text.includes('min'), 'the horizon is a number of minutes');-- const model = await t.get('/models/model-timed');-- assert.strictEqual(model.status, 200, model.text.slice(0, 200));-- assert.ok(model.text.includes('reliably does about'), 'the model profile has the horizon line');-- const thin = await t.get('/models/model-a');-- assert.strictEqual(thin.status, 200, thin.text.slice(0, 200));-- assert.ok(thin.text.includes('not enough runs yet'), 'below the gate the model page says so instead of a number');});-- await check('editors estimate on the task page: sign-in, same-origin, median of three, account data', async () => {-- const made = await create({-- title: 'Estimate arena',-- tasks: [baseTask('timed', { added_on: '2026-02-01' })],-- });-- const path = `/benchmarks/${made.id}/tasks/timed`;-- const anon = await t.get(path);-- assert.strictEqual(anon.status, 200, anon.text.slice(0, 200));-- assert.ok(anon.text.includes('Sign in to estimate'), 'a signed-out reader is asked to sign in');-- assert.strictEqual((await postForm(`/benchmarks/${made.id}/tasks/timed/estimates`, { minutes: '30', version: String(made.version) })).status, 401);---- const crossSite = await t.get(`/benchmarks/${made.id}/tasks/timed/estimates`, {-- as: alice, method: 'POST', raw: 'minutes=30', headers: { 'content-type': 'application/x-www-form-urlencoded', origin: 'https://evil.example' },-- });-- assert.strictEqual(crossSite.status, 403, 'a cross-site write is refused even when signed in');---- for (const [user, minutes] of [[alice, '30'], [bob, '40'], [carol, '50']]) {-- const res = await postForm(`/benchmarks/${made.id}/tasks/timed/estimates`, { minutes, version: String(made.version) }, user);-- assert.strictEqual(res.status, 303, res.text.slice(0, 300));-- }-- const page = await t.get(path);-- assert.ok(page.text.includes('median of 3 editor estimates'), page.text.slice(0, 600));-- assert.ok(page.text.includes('40 min'), 'the median is 40 minutes');---- const exported = await t.ctx.accountData.exportPart(alice.subject);-- const file = (exported.files || []).find((f) => f.name === 'bench_task_estimates.json');-- assert.ok(file && file.content.length === 1, 'the estimate is in the person\'s export');-- assert.strictEqual(Number(file.content[0].minutes), 30);-- const erased = await t.ctx.accountData.erase([alice.subject]);-- assert.ok(Number(erased.erased.bench_task_estimates) >= 1, 'the estimate is erased with the person');-- });---- await check('the contamination badge is shown, with both numbers and their bands', async () => {++ await check('the contamination flag is in the API with both numbers', async () => {const made = await create({title: 'Old and fresh arena',tasks: [@@@ -287,10 +241,6 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01const board = await t.get(`/api/v1/rankings?benchmark=${made.id}&metric=score`);const entry = board.json().rankings.entries.find((e) => e.model === 'gpt-5');assert.strictEqual(entry.contamination.flag, true, JSON.stringify(entry.contamination));-- const page = await t.get(`/benchmarks/${made.id}`);-- assert.ok(page.text.includes('Did much better on older tasks: possibly seen in training'), 'the exact badge text');-- assert.ok(page.text.includes('Older tasks'), 'both numbers');-- assert.ok(page.text.includes('fresh tasks'), 'and their bands');});await t.close();ddeleted file mode 100644@@@ -1,96 +0,0 @@--'use strict';----/**-- * The gallery (stage B9): every round's outputs, filtered and ordered, with at most six live frames.-- */----const assert = require('assert');--const { boot, check, done } = require('./helpers/boot');----(async () => {-- const t = await boot({ env: { BENCH_ROUNDS_PER_DAY: '30', BENCH_ROUNDS_DAILY: '100' } });-- const alice = t.network.addUser('alice');-- const svc = t.network.serviceToken({ sub: 'svc:games', audience: 'openvibe.bench', cap: ['bench.entry.create'] });---- const make = async (kind, prompt, model, cost) => {-- const task = await t.get('/api/v1/kinds/' + kind + '/tasks', { as: alice, json: { prompt, public: true } });-- assert.strictEqual(task.status, 201, task.text);-- const rr = await t.get('/api/v1/tasks/' + task.json().task.id + '/rounds', { as: alice, json: {} });-- const roundId = rr.json().round.id;-- const e1 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'acme', model, payload: { html: '<p>one</p>' }, cost_usd: cost } });-- const e2 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'beta', model: model + '-b', payload: { html: '<p>two</p>' }, cost_usd: cost } });-- assert.strictEqual(e1.status, 201, e1.text);-- assert.strictEqual(e2.status, 201, e2.text);-- return roundId;-- };---- const rounds = [];-- for (let i = 0; i < 5; i++) rounds.push(await make('design.ui', 'A page, number ' + i, 'model-' + i, 0.01 * (i + 1)));-- const gameRound = await make('games.play', 'A game, one', 'gamer-1', 0.5);---- await check('the gallery lists the rounds', async () => {-- const r = await t.get('/gallery');-- assert.strictEqual(r.status, 200, r.text);-- assert.ok(r.text.includes('data-gallery="1"'), 'the gallery');-- assert.ok(r.text.includes('A page, number 0'), 'a prompt');-- assert.ok(r.text.includes('ga-card'), 'the cards');-- assert.ok(r.text.includes('model-0'), 'the model of an entry');-- });---- await check('at most six frames are live, the rest wait for a tap', async () => {-- const r = await t.get('/gallery');-- const live = (r.text.match(/class="ga-frame"/g) || []).length;-- assert.ok(live <= 6, 'live frames: ' + live);-- assert.ok(live >= 4, 'the first cards are live: ' + live);-- assert.ok(r.text.includes('data-ga-still'), 'the stills');-- assert.ok(r.text.includes('Live frame, on tap'), 'the tap hint');-- });---- await check('the filters narrow the gallery', async () => {-- const byKind = await t.get('/gallery?kind=games.play');-- assert.ok(byKind.text.includes('A game, one'), 'the game is there');-- assert.ok(!byKind.text.includes('A page, number 0'), 'the pages are not');---- const byModel = await t.get('/gallery?model=model-2');-- assert.ok(byModel.text.includes('A page, number 2'), 'the model is there');-- assert.ok(!byModel.text.includes('A page, number 3'), 'the others are not');---- const byPrice = await t.get('/gallery?price=0.02');-- assert.ok(byPrice.text.includes('A page, number 0'), 'the cheap one is there');-- assert.ok(!byPrice.text.includes('A page, number 4'), 'the expensive one is not');-- });---- await check('the gallery orders by newest and by most voted', async () => {-- const newest = await t.get('/gallery?order=new');-- const votes = await t.get('/gallery?order=votes');-- assert.strictEqual(newest.status, 200);-- assert.strictEqual(votes.status, 200);-- const firstNewest = /data-round-id="([^"]+)"/.exec(newest.text)[1];-- const firstVotes = /data-round-id="([^"]+)"/.exec(votes.text)[1];-- assert.ok(firstNewest, 'a round in the newest order');-- assert.ok(firstVotes, 'a round in the most voted order');-- // The last round made is the newest; no votes yet, so the most-voted order falls back to the newest.-- assert.strictEqual(firstNewest, firstVotes, 'without votes both orders agree');-- });---- await check('the gallery API answers the same thing', async () => {-- const r = await t.get('/api/v1/gallery?kind=design.ui');-- assert.strictEqual(r.status, 200, r.text);-- assert.ok(r.json().rounds.length >= 5, 'the rounds');-- assert.strictEqual(r.json().filters.kind, 'design.ui');-- const one = r.json().rounds[0];-- assert.ok(one.entries.length === 2, 'two entries');-- assert.ok(one.entries[0].wrapper_url.includes('/render/w/'), 'the sandboxed frame');-- });---- await check('an empty board says so, and offers a battle', async () => {-- const page = await t.get('/ratings');-- assert.strictEqual(page.status, 200, page.text);-- assert.ok(page.text.includes('No battles voted yet'), 'the empty board: ' + page.text.slice(0, 200));-- assert.ok(page.text.includes('Vote in a battle'), 'the button');-- assert.ok(page.text.includes('href="/battle"'), 'it goes to the battle');-- });---- await t.close();-- done();--})().catch((e) => { console.error(e); process.exit(1); });ddeleted file mode 100644@@@ -1,58 +0,0 @@--'use strict';--// The home on a phone (the owner's short-pages rule, 2026-10-30): the parts a reader rarely needs — the promises--// and the kinds for developers — fold closed, with a checkbox and a label and no script. Everything stays in the--// page for the crawlers, and where there is room the fold is open and looks like the section it replaces.--const assert = require('assert');--const fs = require('fs');--const path = require('path');--const { boot, check, done } = require('./helpers/boot');--const { pickBlock } = require('../server/render/short');----(async () => {-- const t = await boot();---- await check('the home folds the promises and the kinds for developers, and keeps both in the page', async () => {-- const r = await t.get('/');-- assert.strictEqual(r.status, 200, r.text.slice(0, 300));-- const home = r.text;-- for (const [id, title] of [['bm-why', 'What Bench promises'], ['bm-kinds', 'For developers: the arena kinds']]) {-- const re = new RegExp(`<div class="fold[^"]*" id="fold-${id}"><input class="fold-toggle" type="checkbox" id="fold-${id}-open">`-- + `<label class="fold-h" for="fold-${id}-open"><h2[^>]*>[^<]*${title}[^<]*</h2>.*?<div class="fold-body">`);-- assert.ok(re.test(home), `the ${title} fold is on the home`);-- }-- // The parts themselves are in the page, not removed: the four promises and every kind.-- assert.strictEqual((home.match(/<li><b>(Outputs, not just scores|Every run published|Value you can read|Free and open)<\/b>/g) || []).length, 4, 'the four promises are in the page');-- const kinds = (home.match(/class="bm-kind-link"/g) || []).length;-- assert.ok(kinds >= 9, `the kinds are in the page (${kinds})`);-- assert.match(home, /<h2 id="bm-kinds-h">For developers: the arena kinds<\/h2>/, 'the kinds keep their heading');-- // The benchmarks are the main thing on the home: they stay as they were.-- assert.match(home, /<section class="bm-section" aria-labelledby="bm-bench-h">/, 'the benchmarks section is where it was');-- });---- await check('the fold is closed on a phone and open where there is room', async () => {-- const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'bench.css'), 'utf8');-- const fold = css.slice(css.indexOf('/* The fold (the owner'));-- assert.ok(fold.length > 0, 'the fold rules are in bench.css');-- assert.match(fold, /\.fold-body \{ display: none; \}/, 'the body is closed by default');-- assert.match(fold, /\.fold-toggle:checked ~ \.fold-body \{ display: block; \}/, 'the title row opens it');-- assert.match(fold, /\.fold-toggle \{ display: none; \}/, 'the toggle is hidden where there is room');-- assert.match(fold, /\.fold-body \{ display: block; \}/, 'the body is open where there is room');-- assert.match(fold, /\.fold-bare > \.fold-h \{ display: none; \}/, 'a part with no heading of its own looks as it did');-- assert.ok(!/#[0-9a-f]{3,6}\b/i.test(fold.replace(/#[0-9a-f]{3,6}\b/gi, '')), 'no new colours in the fold rules');-- });---- await check('pickBlock makes link chips, not radios, and every chip points at a pane in the block', () => {-- const html = pickBlock({ key: 'k', label: 'Pick me', head: 'Headline', panes: [{ id: 'a', label: 'Alpha', html: '<p>one</p>' }, { id: 'b', label: 'Beta', html: '<p>two</p>' }] });-- assert.ok(!/type="radio"/.test(html), 'no radio input');-- assert.ok(!/role="radiogroup"/.test(html), 'the row is not a radiogroup');-- const chips = [...html.matchAll(/<a class="pick-chip" href="#(pp-k-[ab])">([^<]+)<\/a>/g)];-- assert.deepStrictEqual(chips.map((m) => [m[1], m[2]]), [['pp-k-a', 'Alpha'], ['pp-k-b', 'Beta']], 'a link per pane, in order');-- assert.ok(html.includes('<div class="pick-pane" id="pp-k-a">'), 'pane a is in the page');-- assert.ok(html.includes('<div class="pick-pane" id="pp-k-b">'), 'pane b is in the page');-- assert.ok(html.includes('<nav class="pick-row" aria-label="Pick me">'), 'the chips are a nav with the label');-- assert.ok(html.includes('<div class="pick-head"><h2>Headline</h2></div>'), 'the block keeps its heading');-- });---- await t.close();-- done();--})().catch((e) => { console.error(e); process.exit(1); });@@@ -297,12 +297,6 @@ function multipart(filename, bytes) {assert.strictEqual(runs[0].harness.name, 'inspect-ai');});-- await check('the run page says where it came from', async () => {-- const page = await t.get('/runs/' + runId, { as: alice });-- assert.strictEqual(page.status, 200, page.text.slice(0, 200));-- assert.ok(/Imported from inspect 0\.3\.286/.test(page.text), 'the label is there');-- });--await check('the file stays private to its owner', async () => {const mine = await t.get('/imports/' + importId + '/file', { as: alice });assert.strictEqual(mine.status, 200);@@@ -146,31 +146,6 @@ const { boot, check, done } = require('./helpers/boot');assert.strictEqual(claimed, null, 'the job runner never claims it');});-- await check('the battle page: two clips, blind, playable; no share card; the reveal names the pickers only', async () => {-- const fresh = await newRound();-- for (const v of ['v3', 'v4']) {-- const e = await t.get(`/api/v1/rounds/${fresh.id}/entries`, { bearer: live, json: { payload: clip(v), cost_usd: 0.001 } });-- assert.strictEqual(e.status, 201, e.text);-- }-- const viewer = t.network.addUser('battler');-- // The battle picks among waiting rounds at random; the newest (this one) comes first in its list.-- const was = t.ctx.random;-- t.ctx.random = () => 0;-- const page = await t.get('/battle?kind=live.clip', { as: viewer });-- t.ctx.random = was;-- assert.strictEqual(page.status, 200, page.text);-- const videos = page.text.match(/<video class="bt-clip"[^>]*src="https:\/\/media\.openvibe\.media\/p\/v[34]\.mp4/g) || [];-- assert.strictEqual(videos.length, 2, 'both clips play');-- assert.ok(!page.text.includes('The cat walks onto the keyboard'), 'no title: the vote is on the moment');-- assert.ok(!page.text.includes('moment-picker'), 'blind: no policy before the vote');-- assert.ok(!page.text.includes('/battle/card/'), 'no share card for a private battle');-- assert.ok(page.text.includes('hls.min.js'), 'HLS clips can play');-- const battleId = /data-battle="([^"]+)"/.exec(page.text)[1];-- assert.strictEqual((await t.get(`/battle/card/${battleId}.svg`)).status, 404);-- const csp = page.headers.get('content-security-policy') || '';-- assert.match(csp, /media-src[^;]*https:\/\/openvibe\.media/);-- });--await check('a live.clip round spends no AI: it skips the daily round limits and counts against none', async () => {const quota = await boot({ env: { BENCH_ROUNDS_PER_DAY: '1', BENCH_ROUNDS_DAILY: '1' } });const liveSvc = quota.network.serviceToken({ sub: 'svc:live', audience: 'openvibe.bench', cap: CAPS, ns: ['rec:vod_*'] });@@@ -1,29 +1,22 @@'use strict';--// SEO-33: /llms-full.txt holds the API page in full (it was one line a page, 314 characters in all), from the same--// view /docs renders, so a language model reading it learns the API without fetching HTML.++// The Bench pages moved to OpenVibe.Report (2026-11-08). Bench keeps the API, the agent manifest and its datasets;++// the two llms files answer 301 to Report's, which now describes the AI section.const assert = require('assert');const { boot, check, done } = require('./helpers/boot');(async () => {const t = await boot();-- await check('llms-full.txt: the home, then the API page in full', async () => {-- const r = await t.get('/llms-full.txt');-- assert.strictEqual(r.status, 200);-- assert.match(r.headers.get('content-type') || '', /^text\/plain/);-- assert.ok(r.text.startsWith('# OpenVibe.Bench'));-- assert.match(r.text, /### The Bench API\n\nURL: [^\n]*\/docs\n/);-- assert.ok(r.text.length > 2000, `${r.text.length} characters`);-- assert.ok(!/<\/?(div|p|span|a|code|pre|section|h[1-6]|li|ul|ol|table|tr|td|th|button|nav)\b[^>]*>/i.test(r.text.split('### The Bench API')[1] || ''), 'the page as text, no markup');++ await check('llms.txt and llms-full.txt answer 301 to Report', async () => {++ for (const p of ['/llms.txt', '/llms-full.txt']) {++ const r = await t.get(p);++ assert.strictEqual(r.status, 301, p);++ assert.strictEqual(r.headers.get('location'), `https://openvibe.report${p}`, p);++ }});-- await check('llms.txt (AGT-5): own pages at their Markdown twins, the API and files as they are, Optional last', async () => {-- const r = await t.get('/llms.txt');++ await check('the API docs stay readable as JSON on Bench', async () => {++ const r = await t.get('/openapi.json');assert.strictEqual(r.status, 200);-- assert.match(r.text, /\(https?:\/\/[^)]*\/index\.md\)/, 'the home links its twin');-- assert.match(r.text, /\(https?:\/\/[^)]*\/docs\.md\)/);-- assert.match(r.text, /\(https?:\/\/[^)]*\/api\/v1\/kinds\)/);-- assert.ok(r.text.includes('\n## Optional\n') && r.text.lastIndexOf('\n## ') === r.text.indexOf('\n## Optional\n'));-- const twin = await t.get('/docs.md');-- assert.strictEqual(twin.status, 200, 'the twin it links answers');++ assert.ok(r.json().paths && Object.keys(r.json().paths).length > 0);});await check('stop', async () => { await t.close(); });done();@@@ -77,23 +77,6 @@ const { KINDS } = require('../server/kinds');}}-- await check('the pick page answers from the data, without JavaScript', async () => {-- const r = await t.get('/pick?kind=design.page');-- assert.strictEqual(r.status, 200, r.text.slice(0, 300));-- assert.ok(r.text.includes('method="get"'), 'the form is a GET');-- assert.ok(r.text.includes('pk-table'), 'the frontier table');-- assert.ok(r.text.includes('alpha-1'), 'the top model');-- assert.ok(r.text.includes('because no other model beats it'), 'why the top pick is first');-- assert.ok(r.text.includes('pk-card') || r.text.includes('pk-shot'), 'a sample output');-- });---- await check('a model with fewer than three runs is greyed and says so', async () => {-- const r = await t.get('/pick?kind=design.page');-- assert.ok(r.text.includes('beta-2'), 'the thin model is shown');-- assert.ok(r.text.includes('too few runs'), 'it says the data is thin');-- assert.ok(/class="pk-thin"/.test(r.text), 'and it is greyed');-- });--await check('the API answers the same thing as the page', async () => {const api = await t.get('/api/v1/pick?kind=design.page');assert.strictEqual(api.status, 200, api.text.slice(0, 300));@@@ -120,10 +103,7 @@ const { KINDS } = require('../server/kinds');assert.ok(json.rows.every((r) => !r.median_ms || r.median_ms <= json.filters.fast_cap_ms), 'only the quick ones');});-- await check('the words set the controls, and the page says how it read them', async () => {-- const r = await t.get('/pick?q=' + encodeURIComponent('landing page under 2 cents, fast'));-- assert.strictEqual(r.status, 200);-- assert.ok(r.text.includes('design.page'), 'the kind it read');++ await check('the words set the controls, and the API says how it read them', async () => {const json = (await t.get('/api/v1/pick?q=' + encodeURIComponent('landing page under 2 cents, fast'))).json();assert.strictEqual(json.controls.kind, 'design.page');assert.strictEqual(json.controls.max_cost, 0.02);@@@ -131,12 +111,6 @@ const { KINDS } = require('../server/kinds');assert.ok(json.controls.notes.length >= 3, 'it says what it read');});-- await check('a kind with nothing in it says so, and invents nothing', async () => {-- const r = await t.get('/pick?kind=games.three');-- assert.strictEqual(r.status, 200);-- assert.ok(r.text.includes('Nothing to pick yet'), 'the empty answer');-- });--await t.close();done();})().catch((e) => { console.error(e); process.exit(1); });nnew file mode 100644@@@ -0,0 +1,127 @@++'use strict';++++/**++ * The move to OpenVibe.Report (owner, 2026-11-08): the public pages answer 301 to their Report counterparts while++ * the API keeps answering here, and the public reads Report needs (runs, overview, a model's summary) are exposed++ * with real run rows only.++ */++const assert = require('assert');++const { boot, check, done } = require('./helpers/boot');++++const TASKS = [++ { id: 't1', prompt: 'Build a one-screen arcade game.', files: [], expected: null, holdout: false },++ { id: 't2', prompt: 'Build a snake game.', files: [], expected: null, holdout: false },++];++++function definition(over = {}) {++ return {++ title: 'Playable games arena', description: 'A test set.', 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: 0, usd: 0.5 },++ matrix: { models: [], harnesses: [] }, ...over,++ };++}++++function runDoc(benchmark, over = {}) {++ return {++ benchmark, model: 'model-a', provider: 'acme', route: { key: 'acme' }, harness: { name: 'codes', version: '1' },++ executor: { kind: 'codes', id: 'task_1' }, tasks: [++ { task_id: 't1', score: 80, cost_usd: 0.02, ms: 1000, tokens: 10, artifacts: [] },++ { task_id: 't2', score: 60, cost_usd: 0.03, ms: 3000, tokens: 20, artifacts: [] },++ ],++ score: 70, cost_usd: 0.05, ms: 4000, tokens: 30, ...over,++ };++}++++(async () => {++ const t = await boot();++ const writer = t.network.serviceToken({ sub: 'svc:codes', audience: 'openvibe.bench', cap: ['bench.run.publish', 'bench.benchmark.write'] });++ let benchmarkId;++ let version;++++ try {++ const created = await t.get('/api/v1/benchmarks', { bearer: writer, json: definition() });++ assert.strictEqual(created.status, 201, created.text);++ benchmarkId = created.json().benchmark.id;++ const frozen = await t.get(`/api/v1/benchmarks/${benchmarkId}/versions`, { bearer: writer, json: {} });++ assert.strictEqual(frozen.status, 201, frozen.text);++ version = frozen.json().version.version;++ for (const [model, score, cost] of [['model-a', 70, 0.05], ['model-b', 90, 0.10]]) {++ const made = await t.get('/api/v1/runs', { bearer: writer, json: runDoc(`${benchmarkId}@${version}`, { model, score, cost_usd: cost }) });++ assert.strictEqual(made.status, 201, made.text);++ }++++ await check('public runs list newest first with the last run date', async () => {++ const r = await t.get(`/api/v1/runs?benchmark=${benchmarkId}&limit=2`);++ assert.strictEqual(r.status, 200, r.text);++ const body = r.json();++ assert.strictEqual(body.count, 2);++ assert.ok(body.last_run, 'a last run date');++ assert.deepStrictEqual(body.runs.map((x) => x.model), ['model-b', 'model-a']);++ assert.ok(body.runs.every((x) => x.provenance === 'verified'));++ const byKind = await t.get('/api/v1/runs?kind=games.play');++ assert.strictEqual(byKind.json().count, 2, 'the kind filter reads the benchmark kind');++ const byModel = await t.get('/api/v1/runs?model=model-a');++ assert.deepStrictEqual(byModel.json().runs.map((x) => x.model), ['model-a']);++ });++++ await check('overview counts real public benchmarks and runs only', async () => {++ const r = await t.get('/api/v1/overview');++ assert.strictEqual(r.status, 200, r.text);++ const body = r.json();++ const mine = body.list.find((b) => b.id === benchmarkId);++ assert.ok(mine, 'the test benchmark is listed');++ assert.strictEqual(mine.runs, 2);++ assert.ok(body.runs >= 2 && body.models >= 2 && body.tasks >= 2, JSON.stringify(body));++ assert.ok(body.last_run, 'the newest run');++ assert.ok(Array.isArray(body.recent) && body.recent.length >= 2, 'recent runs');++ assert.deepStrictEqual(body.model_list.map((m) => m.model).sort(), ['model-a', 'model-b'], 'every public model with its last run');++ assert.ok(!JSON.stringify(body).includes('t2 build a snake'), 'no task prompt copied into the overview');++ });++++ await check('one model summary carries its task best and worst, cost and runs', async () => {++ const r = await t.get('/api/v1/models/model-a/summary');++ assert.strictEqual(r.status, 200, r.text);++ const body = r.json();++ assert.strictEqual(body.model, 'model-a');++ assert.strictEqual(body.runs, 1);++ assert.strictEqual(body.benchmarks[0].score, 70);++ assert.strictEqual(body.total_cost_usd, 0.05);++ assert.strictEqual(body.best_task.task_id, 't1');++ assert.strictEqual(body.worst_task.task_id, 't2');++ assert.strictEqual(body.best_task.score, 80);++ assert.strictEqual(body.tasks.length, 2);++ assert.ok(body.last_run && body.recent_runs.length === 1);++ });++++ await check('HTML pages answer 301 to their Report counterparts', async () => {++ const root = await t.get('/');++ assert.strictEqual(root.status, 301);++ assert.strictEqual(root.headers.get('location'), 'https://openvibe.report/ai');++ const bench = await t.get(`/benchmarks/${benchmarkId}`);++ assert.strictEqual(bench.status, 301);++ assert.strictEqual(bench.headers.get('location'), `https://openvibe.report/ai/${benchmarkId}`);++ const model = await t.get('/models/model-a');++ assert.strictEqual(model.status, 301);++ assert.strictEqual(model.headers.get('location'), 'https://openvibe.report/ai/models/model-a');++ for (const [path, location] of [['/docs', 'https://openvibe.report/ai/method'], ['/compare', 'https://openvibe.report/ai/compare'], ['/showcase', 'https://openvibe.report/ai'], ['/gallery', 'https://openvibe.report/ai']]) {++ const r = await t.get(path);++ assert.strictEqual(r.status, 301, path);++ assert.strictEqual(r.headers.get('location'), location, path);++ }++ const tab = await t.get('/benchmarks?tab=pareto');++ assert.strictEqual(tab.headers.get('location'), 'https://openvibe.report/ai?tab=pareto');++ });++++ await check('the API, MCP and render origin stay on Bench (no redirect)', async () => {++ for (const path of ['/api/v1/kinds', '/api/v1/overview', `/api/v1/benchmarks/${benchmarkId}`, '/api/health']) {++ const r = await t.get(path);++ assert.strictEqual(r.status, 200, path);++ assert.ok(r.headers.get('location') == null, `${path} redirected`);++ }++ });++ } finally {++ await t.close();++ }++ done();++})().catch((e) => { console.error(e); process.exit(1); });ddeleted file mode 100644@@@ -1,212 +0,0 @@--'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 });-- const privatePage = await create({ title: 'Alice private prompts', visibility: 'private', tags: ['your-prompts'] }, alice);-- await publish(`${privatePage.id}@${privatePage.version}`, { model: 'model-best', score: 77, 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 <script>alert(1)</script> 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('the phone sections are all shown, and the chips are links that jump to them', async () => {-- const res = await t.get(`/benchmarks/${pub.id}`);-- const chips = [...res.text.matchAll(/<nav class="br-sec-pick" aria-label="Benchmark sections">([\s\S]*?)<\/nav>/g)];-- assert.strictEqual(chips.length, 1, 'one row of chips');-- assert.deepStrictEqual([...chips[0][1].matchAll(/<a href="#(br-[a-z]+)">/g)].map((m) => m[1]), ['br-rank', 'br-tasks', 'br-runs'], 'a link per section, in order');-- assert.ok(!/type="radio" name="br-view"/.test(res.text), 'no chip is a radio: nothing is picked');-- assert.match(res.text, /<div class="br-sections">[\s\S]*id="br-rank"[\s\S]*id="br-tasks"[\s\S]*id="br-runs"[\s\S]*<\/div>/, 'every section is in the page');-- const css = await t.get('/css/bench.css');-- assert.ok(!/\.br-sections > section \{ display: none/.test(css.text) && !css.text.includes('#br-view-rank:checked'), 'no section is ever hidden (the owner, 2026-10-31)');-- });---- await check('badges and embeds are one closed details on a public page, and absent on a private one', async () => {-- const pubPage = await t.get(`/benchmarks/${pub.id}`);-- assert.ok(pubPage.text.includes('<details class="br-usethis-details">'), 'one details');-- assert.ok(!pubPage.text.includes('<details class="br-usethis-details" open'), 'closed by default');-- assert.ok(pubPage.text.includes('Badges and embeds'), 'the summary');-- assert.ok(pubPage.text.includes('model-best'), 'the models are inside');-- const mine = await t.get(`/benchmarks/${privatePage.id}`, { as: alice });-- assert.strictEqual(mine.status, 200, mine.text.slice(0, 200));-- assert.ok(!mine.text.includes('Badges and embeds'), 'no badge section on a private benchmark');-- assert.ok(!mine.text.includes('br-usethis'), 'no use-this markup at all');-- const badge = await t.get(`/badge/${privatePage.id}/model-best.svg`);-- assert.strictEqual(badge.status, 404, badge.text.slice(0, 200));-- assert.ok(badge.text.includes('private benchmark'), 'the badge says why');-- });---- 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 <script>'), '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('the showcase page names itself: its canonical URL and its navbar link', async () => {-- const res = await t.get('/showcase');-- assert.strictEqual(res.status, 200, res.text.slice(0, 300));-- assert.match(res.text, /rel="canonical" href="https:\/\/bench\.openvibe\.services\/showcase"/, 'canonical is the showcase, not the home');-- const nav = JSON.parse(res.text.match(/window\.__OV_PAGE = (\{.*?\});/)[1]);-- const active = nav.navbar.links.filter((l) => l.active).map((l) => l.label);-- assert.deepStrictEqual(active, ['Showcase'], 'only Showcase is current');-- });---- await check('stop', async () => { await t.close(); });-- done();--})();@@@ -45,60 +45,6 @@ function fakeCodes() {assert.strictEqual(created.status, 201, created.text);const id = created.json().benchmark.id;assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}/versions`, { bearer: publisher, json: {} })).status, 201);-- const form = (fields) => new URLSearchParams(fields).toString();-- const post = (fields, o = {}) => t.get(`/benchmarks/${id}/run`, { as: alice, raw: form(fields), headers: { 'content-type': 'application/x-www-form-urlencoded' }, ...o });-- const runCalls = () => codes.calls.filter((c) => c.method === 'POST');---- await check('the benchmark page links to its run form', async () => {-- const res = await t.get(`/benchmarks/${id}`);-- assert.ok(res.text.includes(`href="/benchmarks/${id}/run">Run it yourself`), 'the button opens the form, not a bare Codes link');-- });---- await check('signed out, the form asks you to sign in', async () => {-- const res = await t.get(`/benchmarks/${id}/run`);-- assert.strictEqual(res.status, 200, res.text.slice(0, 200));-- assert.ok(res.text.includes('Sign in first'));-- assert.ok(!res.text.includes('<form class="br-form"'), 'no form for a stranger');-- });---- await check('signed in, the form lists the benchmark matrix and Codes harnesses', async () => {-- const res = await t.get(`/benchmarks/${id}/run`, { as: alice });-- assert.ok(res.text.includes('<form class="br-form"'));-- assert.ok(res.text.includes('value="model-a" checked'), 'the matrix model is picked');-- assert.ok(res.text.includes('value="claude-code"') && res.text.includes('Codex'), "Codes' harness list");-- });---- await check('an estimate above $1 asks once more and reaches nobody', async () => {-- const before = runCalls().length;-- const res = await post([['models', 'model-a'], ['more_models', 'model-b'], ['harnesses', 'codex'], ['repeat', '1']]);-- assert.strictEqual(res.status, 200, res.text.slice(0, 200));-- assert.ok(res.text.includes('may cost up to') && res.text.includes('$2.00'), '2 tasks × 2 models × $0.50');-- assert.ok(res.text.includes('name="confirm" value="1"') && res.text.includes('Confirm and start'));-- assert.strictEqual(runCalls().length, before, 'Codes was not called');-- });---- await check('a confirmed run reaches Codes once, as the person, and lands on their tasks', async () => {-- const before = runCalls().length;-- const res = await post([['models', 'model-a'], ['more_models', 'model-b'], ['harnesses', 'codex'], ['repeat', '1'], ['confirm', '1']]);-- assert.strictEqual(res.status, 303, res.text.slice(0, 200));-- assert.strictEqual(res.headers.get('location'), `${codes.url}/tasks`);-- const calls = runCalls().slice(before);-- assert.strictEqual(calls.length, 1);-- assert.deepStrictEqual(calls[0].body.models, ['model-a', 'model-b']);-- assert.match(String(calls[0].headers.authorization), /^Bearer /, "Bench's own service token");-- assert.ok(calls[0].headers['x-ov-subject'], 'names the person');-- });---- await check('nothing picked is said plainly', async () => {-- const res = await post([['repeat', '1']]);-- assert.strictEqual(res.status, 400);-- assert.ok(res.text.includes('non-empty lists'));-- });---- await check('a cross-site form post is refused', async () => {-- const res = await post([['models', 'model-a'], ['harnesses', 'codex'], ['repeat', '1']], { origin: 'https://evil.example' });-- assert.strictEqual(res.status, 403);-- });await check('the API answers the same way: 409 with the estimate, then 202', async () => {const body = { models: ['model-a', 'model-b'], harnesses: ['codex'], repeat: 1 };@@@ -120,13 +66,9 @@ function fakeCodes() {assert.strictEqual(res2.status, 201, res2.text);const nid = res2.json().benchmark.id;assert.strictEqual((await t.get(`/api/v1/benchmarks/${nid}/versions`, { bearer: publisher, json: {} })).status, 201);-- const before = runCalls().length;const ask = await t.get(`/api/v1/benchmarks/${nid}/run`, { as: alice, json: { models: ['m'], harnesses: ['codex'], repeat: 1 } });assert.strictEqual(ask.status, 409, ask.text);assert.strictEqual(ask.json().cost_estimate_usd, null, 'no estimate is invented');-- const page = await t.get(`/benchmarks/${nid}/run`, { as: alice, raw: form([['more_models', 'm'], ['harnesses', 'codex'], ['repeat', '1']]), headers: { 'content-type': 'application/x-www-form-urlencoded' } });-- assert.ok(page.text.includes('sets no dollar limit per task'), 'the form says why it asks');-- assert.strictEqual(runCalls().length, before, 'and nothing reached Codes');});await check('stop', async () => { await t.close(); codes.close(); });@@@ -180,19 +180,12 @@ const HOSTILE = [assert.ok(pair.slots[0].wrapper_url.startsWith(renderLib.RENDER_ORIGIN + '/render/w/'), 'the wrapper is on the render origin: ' + pair.slots[0].wrapper_url);});-- await check('the compare page is blind, and the vote reveals', async () => {++ await check('a round is blind, and the vote reveals', async () => {const before = await t.get('/api/v1/showcase/rounds/' + roundId, { as: bob });assert.strictEqual(before.status, 200, before.text);assert.strictEqual(before.json().round.revealed, false);assert.strictEqual(before.json().round.slots[0].model, undefined);-- const page = await t.get('/showcase/round/' + roundId, { as: bob });-- assert.strictEqual(page.status, 200, page.text);-- assert.ok(page.text.includes('data-slot="A"'), 'pane A');-- assert.ok(page.text.includes('data-slot="B"'), 'pane B');-- assert.ok(page.text.includes('data-vote-form="1"'), 'the vote form');-- assert.ok(!page.text.includes('alpha-1'), 'no model name before the vote');--const vote = await t.get('/api/v1/showcase/rounds/' + roundId + '/vote', { as: bob, json: { outcome: 'A' } });assert.strictEqual(vote.status, 201, vote.text);assert.strictEqual(vote.json().revealed, true);ddeleted file mode 100644@@@ -1,298 +0,0 @@--'use strict';----/**-- * B11: a person's own prompts as a private benchmark.-- *-- * Under test: the paste parser (lines, CSV with an optional expected answer, 5–50 bounds); a private benchmark by-- * default; another person gets a 404 and nothing private is listed, in the sitemap or in the open data; the owner's-- * leaderboard sits beside the public board for the same kind (bands and cost per solved task, never mixed); the run-- * goes through the existing launcher and still asks before it starts; an unlisted share link with a long random-- * token, its "shared by its owner" page, noindex, and unlisting/unsharing/public/private; CSRF.-- */--const assert = require('assert');--const http = require('http');--const { boot, check, done } = require('./helpers/boot');--const { parsePrompts, MAX_PROMPTS } = require('../server/bench/your-prompts');--const { shareTokenMatches } = require('../server/bench/registry');----function fakeCodes() {-- const calls = [];-- const server = http.createServer((req, res) => {-- let body = '';-- req.on('data', (c) => { body += c; });-- req.on('end', () => {-- calls.push({ method: req.method, url: req.url, headers: req.headers, body: body ? JSON.parse(body) : null });-- res.setHeader('content-type', 'application/json');-- if (req.url === '/api/v1/harnesses') return res.end(JSON.stringify({ harnesses: [{ id: 'codex', label: 'Codex' }, { id: 'claude-code', label: 'Claude Code' }] }));-- if (/^\/api\/v1\/benchmarks\/[^/]+\/run$/.test(req.url) && req.method === 'POST') {-- res.statusCode = 202;-- return res.end(JSON.stringify({ session: 'ses_b11', tasks: [{ id: 'tsk_b11' }], task: { id: 'tsk_b11' }, total: 1 }));-- }-- res.statusCode = 404; res.end('{}');-- });-- });-- return new Promise((resolve) => server.listen(0, '127.0.0.1', () => resolve({ url: `http://127.0.0.1:${server.address().port}`, calls, close: () => server.close() })));--}----(async () => {-- const codes = await fakeCodes();-- const t = await boot({ env: { CODES_BENCH_RUN_URL: codes.url } });-- 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 base = String(t.config.baseUrl).replace(/\/+$/, '');-- const MARK = 'ZEBRA-B11-PROMPT';---- const form = (o) => new URLSearchParams(o).toString();-- const post = (path, o, opt = {}) => t.get(path, { as: alice, raw: form(o), headers: { 'content-type': 'application/x-www-form-urlencoded' }, ...opt });-- const share = (id, visibility) => post(`/benchmarks/${id}/share`, { visibility });-- const tokenOf = (res) => {-- const m = /\/b\/([A-Za-z0-9_-]{20,120})/.exec(res.text);-- assert.ok(m, `a share token in the page: ${res.text.slice(0, 300)}`);-- return m[1];-- };-- const publish = async (ref, model, scores, { cost = 0.03 } = {}) => {-- const tasks = scores.map((score, i) => ({ task_id: `t${i + 1}`, score, cost_usd: cost / scores.length, ms: 1000 + i, tokens: 100, artifacts: [] }));-- const body = {-- benchmark: ref, model, route: { provider: 'acme' }, provider: 'acme',-- harness: { name: 'codex', version: '1.0' }, settings: {}, executor: { kind: 'codes', id: 'tsk_b11' },-- tasks, score: scores.reduce((a, b) => a + b, 0) / scores.length, cost_usd: cost, ms: 2000, tokens: 600,-- };-- const res = await t.get('/api/v1/runs', { bearer: publisher, json: body });-- assert.strictEqual(res.status, 201, res.text);-- return res.json().run.id;-- };---- let id;-- let version;-- let token;-- const prompts = Array.from({ length: 6 }, (_, i) => `${MARK} number ${i + 1}`);---- await check('the parser takes one prompt per line and enforces 5–50', async () => {-- const good = parsePrompts(prompts.join('\n'), { format: 'lines' });-- assert.strictEqual(good.ok, true, good.detail);-- assert.strictEqual(good.prompts.length, 6);-- assert.ok(good.prompts.every((p) => p.expected === null));-- assert.strictEqual(parsePrompts(prompts.slice(0, 4).join('\n')).ok, false, 'four is too few');-- assert.strictEqual(parsePrompts(Array.from({ length: MAX_PROMPTS + 1 }, (_, i) => `p${i}`).join('\n')).ok, false, 'fifty-one is too many');-- });---- await check('the parser reads CSV with an optional expected answer and quoted commas', async () => {-- const csv = 'prompt,expected\n"What is 2 + 2, exactly?","4"\n"Say hello, in French.",bonjour\nName one primary colour.,red\nWhat is 3 × 3?,9\nFinish the word: sun—,flower\n';-- const parsed = parsePrompts(csv);-- assert.strictEqual(parsed.ok, true, parsed.detail);-- assert.strictEqual(parsed.format, 'csv', 'a prompt/expected header is detected');-- assert.strictEqual(parsed.prompts.length, 5);-- assert.strictEqual(parsed.prompts[0].prompt, 'What is 2 + 2, exactly?');-- assert.strictEqual(parsed.prompts[0].expected, '4');-- assert.strictEqual(parsed.prompts[1].prompt, 'Say hello, in French.');-- assert.strictEqual(parsed.prompts[1].expected, 'bonjour');-- const noHeader = parsePrompts('one,two\nthree,four\nfive,six\nseven,eight\nnine,ten', { format: 'csv' });-- assert.strictEqual(noHeader.ok, true, noHeader.detail);-- assert.deepStrictEqual(noHeader.prompts[0], { prompt: 'one', expected: 'two' });-- });---- await check('a share token is stored as a hash and checked in constant time', async () => {-- assert.strictEqual(shareTokenMatches('abc', 'a'.repeat(64)), false);-- assert.strictEqual(shareTokenMatches('abc', null), false);-- });---- await check('the form makes a private benchmark from pasted prompts', async () => {-- const res = await post('/benchmarks/new', {-- title: 'My private prompts', description: 'Six real prompts.',-- kind: 'text.answer', format: 'lines', prompts: prompts.join('\n'),-- });-- assert.strictEqual(res.status, 303, res.text.slice(0, 400));-- id = res.headers.get('location').split('/').pop();-- const r = await t.get(`/api/v1/benchmarks/${id}`, { as: alice });-- assert.strictEqual(r.status, 200, r.text);-- assert.strictEqual(r.json().benchmark.visibility, 'private', 'private by default');-- assert.strictEqual(r.json().benchmark.title, 'My private prompts');-- assert.strictEqual(r.json().benchmark.latest_version, 1, 'frozen into a version');-- version = r.json().version.version;-- assert.strictEqual(r.json().definition.kind, 'text.answer');-- assert.strictEqual(r.json().definition.tasks.length, 6);-- assert.deepStrictEqual(r.json().definition.tasks.map((x) => x.id), ['t1', 't2', 't3', 't4', 't5', 't6']);-- assert.ok(r.json().definition.tasks.every((x) => x.prompt.startsWith(MARK)));-- assert.strictEqual(r.json().definition.tasks[0].expected, null);-- assert.strictEqual(r.json().definition.limits.usd, 0, 'no dollar cap is added or raised');-- assert.ok(!t.logs().includes(MARK), 'a prompt never reaches the log');-- });---- await check('a CSV paste keeps the expected answer of each row', async () => {-- const csv = 'prompt,expected\nQ1,A1\nQ2,A2\nQ3,A3\nQ4,A4\nQ5,A5\n';-- const res = await post('/benchmarks/new', { title: 'CSV prompts', kind: 'design.svg', format: 'csv', prompts: csv });-- assert.strictEqual(res.status, 303, res.text.slice(0, 400));-- const cid = res.headers.get('location').split('/').pop();-- const r = await t.get(`/api/v1/benchmarks/${cid}`, { as: alice });-- assert.strictEqual(r.status, 200, r.text);-- assert.strictEqual(r.json().definition.kind, 'design.svg', 'an existing kind can be chosen');-- assert.deepStrictEqual(r.json().definition.tasks[0].expected, { answer: 'A1' });-- assert.strictEqual(r.json().definition.tasks.length, 5);-- });---- await check('another person gets a 404 for the private benchmark everywhere', async () => {-- for (const [path, what] of [-- [`/benchmarks/${id}`, 'page'],-- [`/benchmarks/${id}/run`, 'run form'],-- [`/api/v1/benchmarks/${id}`, 'definition'],-- [`/api/v1/rankings?benchmark=${id}`, 'rankings'],-- [`/api/v1/percentile?benchmark=${id}&model=a&score=90`, 'percentile'],-- ]) {-- const res = await t.get(path, { as: bob });-- assert.strictEqual(res.status, 404, `${what}: ${res.status} ${res.text.slice(0, 200)}`);-- }-- const run = await post(`/benchmarks/${id}/run`, { models: 'a', harnesses: 'codex', repeat: '1' }, { as: bob });-- assert.strictEqual(run.status, 404, run.text.slice(0, 200));-- });---- await check('a private benchmark is never in a list, the sitemap or the open data', async () => {-- const list = await t.get('/api/v1/benchmarks');-- assert.ok(!list.json().benchmarks.some((b) => b.id === id), 'not in the public list');-- const page = await t.get('/benchmarks');-- assert.ok(!page.text.includes('My private prompts'), 'not on the benchmarks page');-- const sitemap = await t.get('/sitemap.xml');-- assert.ok(!sitemap.text.includes(id), 'not in the sitemap');-- const dataset = await t.get('/api/v1/datasets/benchmarks.jsonl');-- assert.ok(!dataset.text.includes(MARK), 'no prompt in the open data');-- const mine = await t.get('/api/v1/benchmarks?mine=1', { as: alice });-- assert.ok(mine.json().benchmarks.some((b) => b.id === id), 'the owner lists it');-- assert.strictEqual(mine.headers.get('cache-control'), 'private, no-store', 'the owner list is not publicly cacheable');-- const theirs = await t.get('/api/v1/benchmarks?mine=1', { as: bob });-- assert.ok(!theirs.json().benchmarks.some((b) => b.id === id), 'nobody else does');-- });---- await check('the owner sees the private page and its prompts', async () => {-- const res = await t.get(`/benchmarks/${id}`, { as: alice });-- assert.strictEqual(res.status, 200, res.text.slice(0, 200));-- assert.ok(res.text.includes('Private.'), 'the private notice');-- assert.ok(res.text.includes(prompts[0]), 'the owner sees the prompt');-- assert.ok(res.text.includes('noindex'), 'kept out of search');-- assert.ok(res.text.includes('Share an unlisted link'), 'the owner can share');-- assert.strictEqual(res.headers.get('cache-control'), 'private, no-store');-- });---- await check('runs published for the owner make the two separate leaderboards', async () => {-- // A public benchmark of the same kind, with a run, is "the public one beside it".-- const pub = await t.get('/api/v1/benchmarks', { bearer: publisher, json: {-- title: 'Public text answers', description: 'The public board.', kind: 'text.answer', tags: [], visibility: 'public',-- tasks: [{ id: 't1', prompt: 'A public question.', files: [], expected: null, holdout: false }],-- judges: [{ type: 'automatic_grader', version: '1.0', config: {} }], scoring: { method: 'mean', scale: { min: 0, max: 100 } },-- limits: { ms: 60000, tokens: 0, usd: 0 }, matrix: { models: [], harnesses: [] },-- } });-- assert.strictEqual(pub.status, 201, pub.text);-- const pubId = pub.json().benchmark.id;-- assert.strictEqual((await t.get(`/api/v1/benchmarks/${pubId}/versions`, { bearer: publisher, json: {} })).status, 201);-- await publish(`${pubId}@1`, 'public-model', [90], { cost: 0.01 });---- await publish(`${id}@${version}`, 'model-a', [80], { cost: 0.03 });-- await publish(`${id}@${version}`, 'model-a', [95], { cost: 0.03 });-- await publish(`${id}@${version}`, 'model-b', [85], { cost: 0.05 });-- await publish(`${id}@${version}`, 'model-b', [96], { cost: 0.05 });-- await publish(`${id}@${version}`, 'model-c', [50], { cost: 0.01 });-- await publish(`${id}@${version}`, 'model-c', [55], { cost: 0.01 });---- const res = await t.get(`/benchmarks/${id}`, { as: alice });-- assert.strictEqual(res.status, 200, res.text.slice(0, 200));-- assert.ok(res.text.includes('Your prompts, 6 runs'), 'the owner leaderboard label');-- assert.ok(res.text.includes('The public board for this kind'), 'the public board is beside it');-- assert.ok(res.text.includes('Cost / solved task'), 'cost per solved task');-- assert.ok(/1st.{0,2}2nd/.test(res.text), 'overlapping intervals share a band');-- assert.ok(res.text.includes('model-a') && res.text.includes('model-b') && res.text.includes('model-c'), 'the own models');-- assert.ok(res.text.includes('public-model'), 'the public model');-- const start = res.text.indexOf('The public board for this kind');-- const end = res.text.indexOf('id="br-tasks-h"', start);-- const publicPart = res.text.slice(start, end);-- assert.ok(!publicPart.includes('model-a') && !publicPart.includes('model-b'), 'the private ranks are not mixed into the public board');-- });---- await check('the run goes through the launcher and still asks before it starts', async () => {-- const before = codes.calls.filter((c) => c.method === 'POST').length;-- const tooFew = await post(`/benchmarks/${id}/run`, { models: 'model-a', harnesses: 'codex', repeat: '1' });-- assert.strictEqual(tooFew.status, 400, tooFew.text.slice(0, 200));-- assert.ok(tooFew.text.includes('3 to 6 models'), '3 to 6 models');-- assert.strictEqual(codes.calls.filter((c) => c.method === 'POST').length, before, 'nobody called Codes for one model');-- const ask = await post(`/benchmarks/${id}/run`, { models: 'model-a', more_models: 'model-b, model-c', harnesses: 'codex', repeat: '1' });-- assert.strictEqual(ask.status, 200, ask.text.slice(0, 200));-- assert.ok(ask.text.includes('sets no dollar limit'), 'the no-limit confirmation');-- assert.strictEqual(codes.calls.filter((c) => c.method === 'POST').length, before, 'Codes was not called yet');-- const go = await post(`/benchmarks/${id}/run`, { models: 'model-a', more_models: 'model-b, model-c', harnesses: 'codex', repeat: '1', confirm: '1' });-- assert.strictEqual(go.status, 303, go.text.slice(0, 200));-- const calls = codes.calls.filter((c) => c.method === 'POST').slice(before);-- assert.strictEqual(calls.length, 1);-- assert.deepStrictEqual(calls[0].body.models, ['model-a', 'model-b', 'model-c']);-- assert.ok(calls[0].headers['x-ov-subject'], 'as the person');-- assert.match(String(calls[0].headers.authorization), /^Bearer /, 'with Bench’s service token');-- const api = await t.get(`/api/v1/benchmarks/${id}/run`, { as: alice, json: { models: ['model-a', 'model-b', 'model-c'], harnesses: ['codex'], repeat: 1 } });-- assert.strictEqual(api.status, 409, api.text);-- assert.strictEqual(api.json().cost_estimate_usd, null, 'no estimate is invented');-- });---- await check('an unlisted share link opens the page once, with a shared note and noindex', async () => {-- const res = await share(id, 'unlisted');-- assert.strictEqual(res.status, 200, res.text.slice(0, 300));-- token = tokenOf(res);-- assert.ok(token.length >= 40, 'a long random token');-- const page = await t.get(`/b/${token}`);-- assert.strictEqual(page.status, 200, page.text.slice(0, 300));-- assert.ok(page.text.includes('Shared by its owner'), 'the shared note');-- assert.ok(page.text.includes('noindex'), 'noindex');-- assert.ok(page.text.includes(MARK), 'the prompts are readable by the link');-- assert.ok(!page.text.includes('Share an unlisted link'), 'a visitor cannot re-share');-- assert.strictEqual((await t.get(`/benchmarks/${id}`)).status, 404, 'the id alone no longer opens it');-- assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 404, 'nor its API definition');-- assert.strictEqual((await t.get('/b/not-a-real-token')).status, 404);-- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: bob })).status, 404);-- });---- await check('making it private again stops the link', async () => {-- const res = await share(id, 'private');-- assert.strictEqual(res.status, 200, res.text.slice(0, 200));-- assert.ok(res.text.includes('Private.'), 'private again');-- assert.strictEqual((await t.get(`/b/${token}`)).status, 404, 'the old link is dead');-- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: alice })).status, 200);-- });---- await check('sharing unlisted again makes a new link and invalidates the old one, then public lists it', async () => {-- const res = await share(id, 'unlisted');-- const second = tokenOf(res);-- assert.notStrictEqual(second, token, 'a new share is a new token');-- assert.strictEqual((await t.get(`/b/${token}`)).status, 404, 'the first link is dead');-- assert.strictEqual((await t.get(`/b/${second}`)).status, 200);-- const pub = await share(id, 'public');-- assert.strictEqual(pub.status, 200, pub.text.slice(0, 200));-- assert.strictEqual((await t.get(`/b/${second}`)).status, 404, 'the token is cleared when it becomes public');-- assert.strictEqual((await t.get(`/benchmarks/${id}`)).status, 200, 'public by id');-- const list = await t.get('/api/v1/benchmarks');-- assert.ok(list.json().benchmarks.some((b) => b.id === id), 'in the public list');-- const sitemap = await t.get('/sitemap.xml');-- assert.ok(sitemap.text.includes(id), 'in the sitemap');-- const dataset = await t.get('/api/v1/datasets/benchmarks.jsonl');-- assert.ok(dataset.text.includes(MARK), 'its prompts are in the open data');-- assert.ok(dataset.text.includes('benchmark_run'), 'with its runs');-- assert.ok(!dataset.text.includes(alice.subject), 'no person id in the open data');-- assert.ok(!dataset.text.includes('created_by'), 'no created_by field in the open data');-- });---- await check('making it private again takes it out of every public surface', async () => {-- await share(id, 'private');-- const list = await t.get('/api/v1/benchmarks');-- assert.ok(!list.json().benchmarks.some((b) => b.id === id));-- const dataset = await t.get('/api/v1/datasets/benchmarks.jsonl');-- assert.ok(!dataset.text.includes(MARK), 'the prompts leave the open data');-- const sitemap = await t.get('/sitemap.xml');-- assert.ok(!sitemap.text.includes(id));-- });---- await check('every form post is same-origin', async () => {-- const make = await post('/benchmarks/new', { kind: 'text.answer', format: 'lines', prompts: prompts.join('\n') }, { origin: 'https://evil.example' });-- assert.strictEqual(make.status, 403, make.text.slice(0, 200));-- const shared = await post(`/benchmarks/${id}/share`, { visibility: 'public' }, { origin: 'https://evil.example' });-- assert.strictEqual(shared.status, 403, shared.text.slice(0, 200));-- });---- await check('stop', async () => { await t.close(); codes.close(); });---- done();--})();--
@@ -3,6 +3,8 @@ NODE_ENV=production
@@ -3,6 +3,8 @@ NODE_ENV=production
PORT=5230
PORT=5230
HOST=127.0.0.1
HOST=127.0.0.1
BASE_URL=https://bench.openvibe.services
BASE_URL=https://bench.openvibe.services
+# The Report room owns the public pages since 2026-11-08; old HTML pages 301 here (the API stays on this origin).
+OV_REPORT_PUBLIC_URL=https://openvibe.report
# Codes handoff page opened by the benchmark's "Run it yourself" action.
# Codes handoff page opened by the benchmark's "Run it yourself" action.
CODES_BENCH_RUN_URL=https://openvibe.codes
CODES_BENCH_RUN_URL=https://openvibe.codes
TRUST_PROXY=2
TRUST_PROXY=2
@@ -2,6 +2,7 @@
@@ -2,6 +2,7 @@
## Unreleased
## Unreleased
+- **The public pages moved to OpenVibe.Report's AI section (2026-11-08).** `/`, `/docs`, `/benchmarks`, `/models/*`, `/compare` and the other page routes answer `301` to `https://openvibe.report/ai` counterparts; the API, MCP, render origin, badges and embeds stay on bench.openvibe.services. New public reads feed Report: `GET /api/v1/runs`, `/api/v1/overview` (with every public model and its last run) and `/api/v1/models/:model/summary` (per-benchmark and per-task medians, cost, recent runs). `/api/v1/benchmarks` now carries each benchmark's runs and last run.
- **The Exchange arena (`chat.arena`).** A new kind, and a first-party API only OpenVibe.Exchange may call:
- **The Exchange arena (`chat.arena`).** A new kind, and a first-party API only OpenVibe.Exchange may call:
`POST /api/v1/arena/rounds` builds a round from two answers the Exchange already has, and
`POST /api/v1/arena/rounds` builds a round from two answers the Exchange already has, and
`POST /api/v1/arena/rounds/:id/votes` records the person's own `human_pairwise` judgement from
`POST /api/v1/arena/rounds/:id/votes` records the person's own `human_pairwise` judgement from
@@ -4,6 +4,8 @@ The shared AI benchmark and comparison engine for every OpenVibe room (plan 2026
@@ -4,6 +4,8 @@ The shared AI benchmark and comparison engine for every OpenVibe room (plan 2026
Bench is the comparative-evaluation authority. AI stays the run authority, Report the analytics authority, Route the learner, and each room keeps its own pages. Bench owns kinds, tasks, pinned rounds, entries, judgements, ratings, boards, the open dataset and private evals.
Bench is the comparative-evaluation authority. AI stays the run authority, Report the analytics authority, Route the learner, and each room keeps its own pages. Bench owns kinds, tasks, pinned rounds, entries, judgements, ratings, boards, the open dataset and private evals.
+**The public pages moved to OpenVibe.Report's AI section (2026-11-08):** `https://openvibe.report/ai`, `/ai/<benchmark>` and `/ai/models/<model>`. This origin's old HTML pages answer `301` there; its API, MCP endpoint, render origin, badges and embeds stay here.
+
- Service id: `bench`
- Service id: `bench`
- Port: `5230`
- Port: `5230`
- Public origin: `https://bench.openvibe.services`
- Public origin: `https://bench.openvibe.services`
@@ -29,7 +31,9 @@ Tests use a temporary PGlite database, an in-process mock Network and a fake AI;
@@ -29,7 +31,9 @@ Tests use a temporary PGlite database, an in-process mock Network and a fake AI;
## API
## API
-JSON under `/api/v1`; errors are `application/problem+json`. See `/docs` for the full list. The MCP endpoint is `POST /mcp`.
+JSON under `/api/v1`; errors are `application/problem+json`. The full list is in `/openapi.json` and Report's `/ai/method`. The MCP endpoint is `POST /mcp`.
+
+The public reads Report's AI pages use are `GET /api/v1/overview`, `GET /api/v1/benchmarks`, `GET /api/v1/rankings`, `GET /api/v1/runs`, `GET /api/v1/models/:model/summary` and `GET /api/v1/runs/:id`; the long list is at `/openapi.json` and in Report's method page.
<!-- versions:start -->
<!-- versions:start -->
- openvibe-contracts: v0.338.0
- openvibe-contracts: v0.338.0
@@ -3,7 +3,7 @@
@@ -3,7 +3,7 @@
"name": "OpenVibe.Bench",
"name": "OpenVibe.Bench",
"version": "0.1.0",
"version": "0.1.0",
"status": "v1",
"status": "v1",
- "summary": "The shared AI benchmark and comparison engine for every room: kinds, pinned rounds, blind judgements, elo/pareto/cost boards, an open dataset per kind, private evals and an MCP endpoint.",
+ "summary": "The shared AI benchmark and comparison engine for every room: kinds, pinned rounds, blind judgements, elo/pareto/cost boards, an open dataset per kind, private evals and an MCP endpoint. Its public pages are OpenVibe.Report's AI section since 2026-11-08; this origin answers 301 for them and keeps the API.",
"contracts": "openvibe-contracts v0.338.0",
"contracts": "openvibe-contracts v0.338.0",
"sdk": "openvibe-sdk v0.94.0",
"sdk": "openvibe-sdk v0.94.0",
"shared": "openvibe-shared v4.155.0",
"shared": "openvibe-shared v4.155.0",
@@ -273,6 +273,9 @@ async function createApp(opts = {}) {
@@ -273,6 +273,9 @@ async function createApp(opts = {}) {
});
});
// ── Pages ───────────────────────────────────────────────
// ── Pages ───────────────────────────────────────────────
+ // The public pages moved to OpenVibe.Report's AI section (2026-11-08). The API, MCP, render, badge and embed
+ // routes above stay; an HTML page is a 301 to its Report counterpart.
+ app.use(require('./http/redirect').createReportRedirects({ reportUrl: config.reportUrl }));
app.use(rateLimit({ windowMs: 60_000, limit: Number(process.env.BENCH_RATE_LIMIT_PER_MIN) || 300, standardHeaders: true, legacyHeaders: false }));
app.use(rateLimit({ windowMs: 60_000, limit: Number(process.env.BENCH_RATE_LIMIT_PER_MIN) || 300, standardHeaders: true, legacyHeaders: false }));
app.use('/render', createRenderRoutes(ctx));
app.use('/render', createRenderRoutes(ctx));
app.use('/vendor', require('./http/vendor').createVendorRoutes());
app.use('/vendor', require('./http/vendor').createVendorRoutes());
@@ -996,6 +996,7 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
@@ -996,6 +996,7 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
cost_per_solved: medScore > 0 && medCost > 0 ? Math.round((medCost / (medScore / 100)) * 1e6) / 1e6 : null,
cost_per_solved: medScore > 0 && medCost > 0 ? Math.round((medCost / (medScore / 100)) * 1e6) / 1e6 : null,
contamination: contaminationOf(model, list.map((item) => item.run)),
contamination: contaminationOf(model, list.map((item) => item.run)),
horizon: horizonOfModel(list.map((item) => item.run)),
horizon: horizonOfModel(list.map((item) => item.run)),
+ last_run: list.reduce((at, item) => (!at || item.run.created_at > at ? item.run.created_at : at), null),
};
};
entries.push(entry);
entries.push(entry);
}
}
@@ -1191,7 +1192,7 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
@@ -1191,7 +1192,7 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
return new Map(rows.map((r) => [r.benchmark_id, num(r.n)]));
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 } = {}) {
+ async function listRuns({ benchmark = null, model = null, kind = null, createdBy = null, verifiedOnly = false, publicOnly = false, limit = 20, offset = 0 } = {}) {
const where = [];
const where = [];
const params = [];
const params = [];
const add = (clause, value) => { params.push(value); where.push(clause.replace('?', `$${params.length}`)); };
const add = (clause, value) => { params.push(value); where.push(clause.replace('?', `$${params.length}`)); };
@@ -1202,13 +1203,16 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
@@ -1202,13 +1203,16 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
if (ref.version != null) add('benchmark_version = ?', ref.version);
if (ref.version != null) add('benchmark_version = ?', ref.version);
}
}
if (model) add('model = ?', String(model));
if (model) add('model = ?', String(model));
+ if (kind) add("benchmark_id IN (SELECT id FROM bench_benchmarks WHERE kind = ? AND visibility = 'public')", String(kind));
if (createdBy) add('created_by = ?', String(createdBy));
if (createdBy) add('created_by = ?', String(createdBy));
if (verifiedOnly) where.push("provenance = 'verified'");
if (verifiedOnly) where.push("provenance = 'verified'");
// A listing across benchmarks (a model's page) shows the public benchmarks' runs only.
// 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')");
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)));
params.push(Math.min(100, Math.max(1, Number(limit) || 20)));
+ const limitAt = params.length;
+ params.push(Math.max(0, Number(offset) || 0));
const rows = await db.many(
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}`,
+ `SELECT * FROM bench_runs ${where.length ? `WHERE ${where.join(' AND ')}` : ''} ORDER BY created_at DESC, id DESC LIMIT $${limitAt} OFFSET $${params.length}`,
params);
params);
return rows.map((row) => wireRun(row));
return rows.map((row) => wireRun(row));
}
}
@@ -1248,10 +1252,125 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
@@ -1248,10 +1252,125 @@ function createRegistry(s, { log = console, sharing = null } = {}) {
return { model: String(model || ''), runs: rows.length, benchmarks: out };
return { model: String(model || ''), runs: rows.length, benchmarks: out };
}
}
+ /**
+ * Activity per public benchmark: how many runs it has and when its newest one landed. The overview and the
+ * benchmark list read this; a benchmark with no run has no date, never a made-up one.
+ */
+ async function runActivity() {
+ const rows = await db.many(
+ "SELECT r.benchmark_id, COUNT(*)::int AS runs, MAX(r.created_at) AS last_run"
+ + " FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id"
+ + " WHERE b.visibility = 'public' GROUP BY r.benchmark_id");
+ return new Map(rows.map((r) => [r.benchmark_id, { runs: num(r.runs), last_run: r.last_run ? iso(r.last_run) : null }]));
+ }
+
+ /** Public totals for the AI overview and its method page. Counts only; no model names, no invented numbers. */
+ async function overview() {
+ const [benchmarks, activity, recent] = await Promise.all([
+ listBenchmarks({ limit: 500 }),
+ runActivity(),
+ listRuns({ publicOnly: true, limit: 12 }),
+ ]);
+ const publicList = benchmarks.filter((b) => b.visibility === 'public');
+ for (const b of publicList) {
+ const a = activity.get(b.id);
+ b.runs = a ? a.runs : 0;
+ b.last_run = a ? a.last_run : null;
+ }
+ const totals = await db.maybe(
+ "SELECT COUNT(*)::int AS runs, COUNT(DISTINCT r.model)::int AS models, MAX(r.created_at) AS last_run"
+ + " FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id WHERE b.visibility = 'public'");
+ const tasks = await db.maybe(
+ 'SELECT COALESCE(SUM(jsonb_array_length(v.definition->\'tasks\')), 0)::int AS tasks'
+ + ' FROM bench_benchmark_versions v JOIN bench_benchmarks b ON b.id = v.benchmark_id'
+ + " WHERE b.visibility = 'public' AND v.version = b.latest_version");
+ const modelRows = await db.many(
+ 'SELECT r.model, COUNT(*)::int AS runs, MAX(r.created_at) AS last_run'
+ + ' FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id'
+ + " WHERE b.visibility = 'public' GROUP BY r.model ORDER BY MAX(r.created_at) DESC, r.model");
+ return {
+ kinds: require('../kinds').KINDS.length,
+ benchmarks: publicList.length,
+ tasks: num(tasks && tasks.tasks, 0),
+ runs: num(totals && totals.runs, 0),
+ models: num(totals && totals.models, 0),
+ last_run: totals && totals.last_run ? iso(totals.last_run) : null,
+ list: publicList.map((b) => ({
+ id: b.id, title: b.title, kind: b.kind, description: b.description,
+ latest_version: b.latest_version, runs: b.runs, last_run: b.last_run,
+ })),
+ model_list: modelRows.map((r) => ({ model: r.model, runs: num(r.runs), last_run: r.last_run ? iso(r.last_run) : null })),
+ recent,
+ };
+ }
+
+ /**
+ * One model's public record: what the verified runs say per benchmark, its task-level best and worst, every
+ * public run newest first, and the money and time those runs cost. A submitted run is labelled and never mixed
+ * into the medians.
+ */
+ async function modelDetail(model) {
+ const name = String(model || '');
+ const summary = await modelSummary(name);
+ const runs = await listRuns({ model: name, publicOnly: true, limit: 100 });
+ const lastByBenchmark = new Map();
+ for (const run of runs) {
+ if (!lastByBenchmark.has(run.benchmark_id)) lastByBenchmark.set(run.benchmark_id, run.created_at);
+ }
+ for (const b of summary.benchmarks) b.last_run = lastByBenchmark.get(b.benchmark_id) || null;
+
+ const taskRows = await db.many(
+ "SELECT r.benchmark_id, r.created_at, t.task_id, t.score"
+ + " FROM bench_run_tasks t JOIN bench_runs r ON r.id = t.run_id"
+ + " JOIN bench_benchmarks b ON b.id = r.benchmark_id"
+ + " WHERE r.model = $1 AND r.provenance = 'verified' AND b.visibility = 'public'"
+ + ' ORDER BY t.task_id, r.created_at', [name]);
+ const groups = new Map();
+ for (const row of taskRows) {
+ const key = `${row.benchmark_id}:${row.task_id}`;
+ if (!groups.has(key)) groups.set(key, { benchmark_id: row.benchmark_id, task_id: String(row.task_id), scores: [], last_run: null });
+ const g = groups.get(key);
+ g.scores.push(num(row.score));
+ const at = iso(row.created_at);
+ if (!g.last_run || at > g.last_run) g.last_run = at;
+ }
+ const titles = new Map(summary.benchmarks.map((b) => [b.benchmark_id, b.title]));
+ const tasks = [...groups.values()].map((g) => ({
+ benchmark_id: g.benchmark_id,
+ benchmark_title: titles.get(g.benchmark_id) || g.benchmark_id,
+ task_id: g.task_id,
+ runs: g.scores.length,
+ score: Math.round(median(g.scores) * 1000) / 1000,
+ best: Math.round(Math.max(...g.scores) * 1000) / 1000,
+ worst: Math.round(Math.min(...g.scores) * 1000) / 1000,
+ last_run: g.last_run,
+ })).sort((a, b) => b.score - a.score || a.task_id.localeCompare(b.task_id));
+
+ const totals = await db.maybe(
+ "SELECT COUNT(*)::int AS runs, COALESCE(SUM(r.cost_usd), 0)::numeric AS cost,"
+ + " COALESCE(SUM(r.ms), 0)::int AS ms, COALESCE(SUM(r.tokens), 0)::int AS tokens,"
+ + " MAX(r.created_at) AS last_run"
+ + " FROM bench_runs r JOIN bench_benchmarks b ON b.id = r.benchmark_id"
+ + " WHERE r.model = $1 AND r.provenance = 'verified' AND b.visibility = 'public'", [name]);
+ const round = (v, p) => (v == null ? null : Math.round(num(v) * p) / p);
+ return {
+ ...summary,
+ last_run: totals && totals.last_run ? iso(totals.last_run) : null,
+ total_cost_usd: round(totals && totals.cost, 1e6),
+ total_ms: round(totals && totals.ms, 1),
+ total_tokens: round(totals && totals.tokens, 1),
+ best_task: tasks.length ? tasks[0] : null,
+ worst_task: tasks.length ? tasks[tasks.length - 1] : null,
+ tasks,
+ recent_runs: runs,
+ };
+ }
+
return {
return {
createBenchmark, getBenchmark, getVersion, latestVersion, getDefinition, saveDraft, freezeVersion,
createBenchmark, getBenchmark, getVersion, latestVersion, getDefinition, saveDraft, freezeVersion,
forkBenchmark, setVisibility, findShared, publicDatasetLines,
forkBenchmark, setVisibility, findShared, publicDatasetLines,
listBenchmarks, publishRun, getRun, compareRuns, rankings, percentile, listRuns, modelSummary, runCounts,
listBenchmarks, publishRun, getRun, compareRuns, rankings, percentile, listRuns, modelSummary, runCounts,
+ runActivity, overview, modelDetail,
taskEstimateSummary, addTaskEstimate, modelHorizon,
taskEstimateSummary, addTaskEstimate, modelHorizon,
};
};
}
}
@@ -27,6 +27,9 @@ function load(env = process.env) {
@@ -27,6 +27,9 @@ function load(env = process.env) {
nodeEnv,
nodeEnv,
isProduction,
isProduction,
baseUrl,
baseUrl,
+ // The Report room now owns the public Bench pages (2026-11-08): an HTML page answers 301 here. The API,
+ // MCP, render origin, badges and embeds stay on this origin.
+ reportUrl: trim(env.OV_REPORT_PUBLIC_URL || 'https://openvibe.report'),
// "Run it yourself" on a benchmark page opens this openvibe.codes page (stage B3); unset, the page offers the API.
// "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,
codesRunUrl: env.CODES_BENCH_RUN_URL ? trim(env.CODES_BENCH_RUN_URL) : null,
trustProxy: env.TRUST_PROXY != null ? Number(env.TRUST_PROXY) : 2,
trustProxy: env.TRUST_PROXY != null ? Number(env.TRUST_PROXY) : 2,
new file mode 100644
new file mode 100644
@@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
+'use strict';
+
+/**
+ * The Bench pages moved into OpenVibe.Report's AI section (owner, 2026-11-08). Every public HTML page answers a
+ * permanent redirect to its Report counterpart; the API, MCP, render origin, badges, embeds, sign-in, legal pages
+ * and static assets stay here. A redirect keeps its query string only where Report reads one (`?tab=`).
+ *
+ * / → /ai
+ * /benchmarks → /ai
+ * /benchmarks/<id>[/…] → /ai/<id>
+ * /models/<model> → /ai/models/<model>
+ * /compare → /ai/compare
+ * /docs → /ai/method
+ * /showcase, /gallery, … → /ai
+ */
+
+const DEFAULT_REPORT = 'https://openvibe.report';
+const decode = (s) => { try { return decodeURIComponent(s); } catch { return s; } };
+
+/** Paths that are machine endpoints, frames or not pages of the old Bench site: never redirected. */
+const KEEP = [/^\/render(?:\/|$)/, /^\/vendor(?:\/|$)/, /^\/badge(?:\/|$)/, /^\/embed(?:\/|$)/,
+ /^\/api(?:\/|$)/, /^\/mcp(?:\/|$)/, /^\/auth(?:\/|$)/, /^\/shared(?:\/|$)/, /^\/\.well-known(?:\/|$)/,
+ /^\/metrics(?:\/|$)/, /^\/release(?:\.json|-metrics)?(?:\/|$)/, /^\/robots\.txt$/, /^\/sitemap\.xml$/,
+ /^\/favicon\.ico$/, /^\/assets(?:\/|$)/];
+
+function targetFor(pathname, reportUrl) {
+ const base = String(reportUrl || DEFAULT_REPORT).replace(/\/+$/, '');
+ const p = String(pathname || '/');
+ if (p === '/' || p === '') return `${base}/ai`;
+ if (p === '/docs' || p === '/docs/') return `${base}/ai/method`;
+ if (p === '/compare' || p === '/compare/') return `${base}/ai/compare`;
+ if (p === '/benchmarks' || p === '/benchmarks/') return `${base}/ai`;
+ if (p === '/benchmarks/new' || p === '/benchmarks/new/') return `${base}/ai`;
+ const bench = /^\/benchmarks\/([^/]+)(?:\/.*)?$/.exec(p);
+ if (bench) return `${base}/ai/${encodeURIComponent(decode(bench[1]))}`;
+ const model = /^\/models\/(.+)$/.exec(p);
+ if (model) return `${base}/ai/models/${encodeURIComponent(decode(model[1]))}`;
+ if (/^\/(?:showcase|gallery|pick|battle|ratings)(?:\/|$)/.test(p)) return `${base}/ai`;
+ if (/^\/b\/[^/]+$/.test(p)) return `${base}/ai`;
+ if (/^\/runs\/[^/]+$/.test(p)) return `${base}/ai`;
+ if (p === '/llms.txt' || p === '/llms-full.txt') return `${base}${p}`;
+ // A Markdown twin of a moved page: its Report twin is the moved page's own twin. An unknown twin is still a 404.
+ if (p.endsWith('.md')) {
+ const page = p === '/index.md' ? '/' : p.slice(0, -3);
+ const moved = targetFor(page, reportUrl);
+ return moved ? `${moved}.md` : null;
+ }
+ return null;
+}
+
+function createReportRedirects({ reportUrl = DEFAULT_REPORT } = {}) {
+ return function reportRedirect(req, res, next) {
+ if (req.method !== 'GET' && req.method !== 'HEAD') return next();
+ if (KEEP.some((re) => re.test(req.path))) return next();
+ const target = targetFor(req.path, reportUrl);
+ if (!target) return next();
+ // ?tab= is the one query Report's AI board reads from the old links; everything else is dropped.
+ const tab = req.query && typeof req.query.tab === 'string' && /^(elo|pareto|cost)$/.test(req.query.tab) ? `?tab=${req.query.tab}` : '';
+ res.set('Cache-Control', 'public, max-age=3600');
+ return res.redirect(301, target + tab);
+ };
+}
+
+module.exports = { createReportRedirects, targetFor, DEFAULT_REPORT };
@@ -186,6 +186,12 @@ function createRegistryApi(ctx) {
@@ -186,6 +186,12 @@ function createRegistryApi(ctx) {
q: req.query.q ? String(req.query.q) : null,
q: req.query.q ? String(req.query.q) : null,
limit: req.query.limit ? Number(req.query.limit) : 50,
limit: req.query.limit ? Number(req.query.limit) : 50,
});
});
+ const activity = await registry.runActivity();
+ for (const b of list) {
+ const a = activity.get(b.id);
+ b.runs = a ? a.runs : 0;
+ b.last_run = a ? a.last_run : null;
+ }
res.set('Cache-Control', mine ? 'private, no-store' : 'public, max-age=60');
res.set('Cache-Control', mine ? 'private, no-store' : 'public, max-age=60');
return res.json({ count: list.length, benchmarks: list });
return res.json({ count: list.length, benchmarks: list });
} catch (err) { return failed(res, err); }
} catch (err) { return failed(res, err); }
@@ -267,6 +273,49 @@ function createRegistryApi(ctx) {
@@ -267,6 +273,49 @@ function createRegistryApi(ctx) {
} catch (err) { return failed(res, err); }
} catch (err) { return failed(res, err); }
});
});
+ // The public run list: newest first, on public benchmarks only. It is what Report's AI pages read for a
+ // model's history, a benchmark's latest results and the new-results feed; a submitted run is labelled by
+ // `provenance` and is not in the verified rankings.
+ r.get('/runs', async (req, res) => {
+ try {
+ const benchmark = req.query.benchmark ? String(req.query.benchmark).slice(0, 120) : null;
+ const model = req.query.model ? String(req.query.model).slice(0, 160) : null;
+ const kind = req.query.kind ? String(req.query.kind).slice(0, 80) : null;
+ if (kind && !require('../kinds').kindById(kind)) return problem(res, 404, 'bench.kind_not_found', 'No such kind.');
+ if (benchmark) {
+ const bm = await registry.getBenchmark(benchmark.split('@')[0]);
+ if (!bm || !await mayRead(req, bm) || bm.visibility !== 'public') {
+ return problem(res, 404, 'bench.benchmark_not_found', 'No such public benchmark.');
+ }
+ }
+ const limit = Math.min(100, Math.max(1, Number(req.query.limit) || 20));
+ const offset = Math.max(0, Number(req.query.offset) || 0);
+ const runs = await registry.listRuns({ benchmark, model, kind, publicOnly: true, limit, offset });
+ res.set('Cache-Control', 'public, max-age=60');
+ return res.json({ count: runs.length, offset, runs, last_run: runs.length ? runs[0].created_at : null });
+ } catch (err) { return failed(res, err); }
+ });
+
+ // Public counts for the AI overview and method page. Nothing here is a person or an unlisted benchmark.
+ r.get('/overview', async (req, res) => {
+ try {
+ res.set('Cache-Control', 'public, max-age=60');
+ return res.json(await registry.overview());
+ } catch (err) { return failed(res, err); }
+ });
+
+ // One model's public record: verified medians per benchmark, its task-level best and worst, every public run
+ // newest first, and the cost and time those runs carried.
+ r.get('/models/:model/summary', async (req, res) => {
+ try {
+ const model = String(req.params.model || '').slice(0, 160);
+ if (!model) return invalid(res, 'model is required');
+ const out = await registry.modelDetail(model);
+ res.set('Cache-Control', 'public, max-age=60');
+ return res.json(out);
+ } catch (err) { return failed(res, err); }
+ });
+
r.get('/runs/:id', async (req, res) => {
r.get('/runs/:id', async (req, res) => {
try {
try {
const run = await registry.getRun(String(req.params.id || ''));
const run = await registry.getRun(String(req.params.id || ''));
@@ -82,6 +82,9 @@ ${ep('POST', '/api/v1/benchmarks/:id/share', '<code>{ visibility: "private" | "u
@@ -82,6 +82,9 @@ ${ep('POST', '/api/v1/benchmarks/:id/share', '<code>{ visibility: "private" | "u
${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('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/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|horizon&harness=&since=', 'Medians per model over verified runs; value is score per dollar, speed is score per minute, horizon is the 50% human-time task length. since=YYYY-MM scores only tasks added in or after that month.')}
${ep('GET', '/api/v1/rankings?benchmark=&metric=score|value|speed|consistency|horizon&harness=&since=', 'Medians per model over verified runs; value is score per dollar, speed is score per minute, horizon is the 50% human-time task length. since=YYYY-MM scores only tasks added in or after that month.')}
+${ep('GET', '/api/v1/runs?benchmark=&model=&kind=&limit=&offset=', 'Public runs newest first, with their score, cost, time and provenance. The pages at openvibe.report/ai read this list.')}
+${ep('GET', '/api/v1/models/:model/summary', 'One model’s verified record: per benchmark, its task-level best and worst, every public run and the total cost.')}
+${ep('GET', '/api/v1/overview', 'Public counts (benchmarks, tasks, runs, models), every public model with its last run, and the newest runs.')}
${ep('GET', '/api/v1/percentile?benchmark=&model=&score=', 'Where a score sits among a model’s verified runs.')}
${ep('GET', '/api/v1/percentile?benchmark=&model=&score=', 'Where a score sits among a model’s verified runs.')}
</dl></section>
</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>
<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>
@@ -1,63 +1,48 @@
@@ -1,63 +1,48 @@
'use strict';
'use strict';
-// SEO-1: the public pages' Markdown twins (server/agent-twins.js). A twin is the page rendered signed out, and the
-// page names it in its head and in the Link: header.
+// The Bench public pages moved into OpenVibe.Report's AI section (owner, 2026-11-08). Every old page and its
+// Markdown twin answer one permanent redirect to the Report counterpart; an unknown twin is still a 404, and the
+// API keeps answering here.
const assert = require('assert');
const assert = require('assert');
-const fs = require('fs');
-const path = require('path');
-const { boot } = require('./helpers/boot');
-const { TWIN_PATH_RE } = require('../server/agent-twins');
-
-// Public pages from the sitemap that render without data.
-const PAGES = ['/', '/docs', '/benchmarks'];
+const { boot, check, done } = require('./helpers/boot');
(async () => {
(async () => {
const t = await boot();
const t = await boot();
- const origin = String(t.config.baseUrl).replace(/\/+$/, '');
- const reEsc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- let failed = 0;
- const check = async (name, fn) => {
- try { await fn(); console.log(` ✓ ${name}`); } catch (e) { failed++; console.log(` ✗ ${name}\n ${String(e.stack || e).split('\n').slice(0, 6).join('\n ')}`); }
- };
+ const REPORT = 'https://openvibe.report';
try {
try {
- await check('every listed page has a twin, and the page names it', async () => {
- for (const p of PAGES) {
- assert.ok(TWIN_PATH_RE.test(p), `${p} is a twin path`);
- const page = await t.get(p);
- assert.strictEqual(page.status, 200, p);
- const twinPath = p === '/' ? '/index.md' : `${p}.md`;
- const href = `${origin}${twinPath}`;
- assert.ok(page.text.includes(`<link rel="alternate" type="text/markdown" href="${href}">`), `${p} names its twin in the head`);
- assert.match(String(page.headers.get('link') || ''), new RegExp(`<${reEsc(href)}>; rel="alternate"; type="text/markdown"`), `${p} names it in the Link header`);
- const twin = await t.get(twinPath);
- assert.strictEqual(twin.status, 200, twinPath);
- assert.match(String(twin.headers.get('content-type') || ''), /^text\/markdown/, `${twinPath} is Markdown`);
- assert.ok(twin.text.startsWith('---\ntitle:') && /\ncanonical: "/.test(twin.text), `${twinPath} carries the page's title and address`);
- // Markup only inside fenced code (a page's own code samples), never the page's scripts or styles.
- const prose = twin.text.replace(/~~~~[\s\S]*?~~~~/g, '');
- assert.ok(!/<(?:script|style)\b/i.test(prose) && twin.text.length > 150, `${twinPath} is the page's words`);
+ await check('the old public pages answer one 301 to their Report counterparts', async () => {
+ const list = await t.get('/api/v1/benchmarks?limit=1');
+ const b = list.json().benchmarks[0];
+ const pages = [
+ ['/', '/ai'],
+ ['/docs', '/ai/method'],
+ ['/benchmarks', '/ai'],
+ [`/benchmarks/${b.id}`, `/ai/${b.id}`],
+ ['/compare', '/ai/compare'],
+ ['/showcase', '/ai'],
+ ['/gallery', '/ai'],
+ ];
+ for (const [p, target] of pages) {
+ const r = await t.get(p);
+ assert.strictEqual(r.status, 301, p);
+ assert.strictEqual(r.headers.get('location'), `${REPORT}${target}`, p);
}
}
});
});
- await check('a path with no twin answers a Markdown 404, and a missing page names no twin', async () => {
- const twin = await t.get('/no-such-page-here.md');
- assert.strictEqual(twin.status, 404);
- const page = await t.get('/no-such-page-here');
- assert.ok(!/type="text\/markdown"/.test(page.text) && !/text\/markdown/.test(String(page.headers.get('link') || '')));
+
+ await check('a Markdown twin follows its page; an unknown twin stays a 404', async () => {
+ const docs = await t.get('/docs.md');
+ assert.strictEqual(docs.status, 301);
+ assert.strictEqual(docs.headers.get('location'), `${REPORT}/ai/method.md`);
+ const missing = await t.get('/no-such-page-here.md');
+ assert.strictEqual(missing.status, 404);
});
});
- if (t.network && typeof t.network.addUser === 'function') {
- await check('a page drawn for a signed-in person names no twin', async () => {
- const page = await t.get(PAGES[0], { as: t.network.addUser('twin-reader') });
- assert.ok(!/type="text\/markdown"/.test(page.text), 'no twin in the head');
- assert.ok(!/text\/markdown/.test(String(page.headers.get('link') || '')), 'no twin in the Link header');
- });
- }
- await check('the twins are mounted before the page routes', async () => {
- const src = fs.readFileSync(path.join(__dirname, '..', 'server', 'app.js'), 'utf8');
- const at = src.indexOf("require('./agent-twins').mountTwins(app,");
- assert.ok(at > 0 && at < src.indexOf('app.use(pageRoutes);'));
+
+ await check('the API and the agent manifest still answer here', async () => {
+ assert.strictEqual((await t.get('/api/v1/kinds')).status, 200);
+ assert.strictEqual((await t.get('/api/v1/overview')).status, 200);
+ assert.strictEqual((await t.get('/.well-known/agent.json')).status, 200);
});
});
} finally {
} finally {
await t.close();
await t.close();
}
}
- console.log(failed ? `\n${failed} failed` : '\nall passed');
- process.exit(failed ? 1 : 0);
-})();
+ done();
+})().catch((e) => { console.error(e); process.exit(1); });
@@ -131,21 +131,6 @@ function cookieOf(res) {
@@ -131,21 +131,6 @@ function cookieOf(res) {
assert.strictEqual(counted, 2, 'the refused vote was not counted');
assert.strictEqual(counted, 2, 'the refused vote was not counted');
});
});
- await check("Bench's own page votes without signing in", async () => {
- const page = await t.get('/showcase/round/' + rounds[2].id);
- assert.strictEqual(page.status, 200, page.text);
- const cookie = cookieOf(page);
- assert.ok(cookie.startsWith('bench_anon='), 'the page hands out the voter cookie');
- assert.ok(page.text.includes('data-vote-form="1"'), 'the signed-out visitor gets the form');
- assert.ok(!page.text.includes('Sign in to vote'), 'not the sign-in prompt');
- const vote = await t.get('/showcase/round/' + rounds[2].id, {
- method: 'POST', cookie, headers: FORM, body: 'outcome=A', ...SAME,
- });
- assert.strictEqual(vote.status, 302, vote.text);
- const row = await t.ctx.s.db.maybe("SELECT voter_kind FROM bench_judgements WHERE round_id = $1 ORDER BY at DESC LIMIT 1", [rounds[2].id]);
- assert.strictEqual(row.voter_kind, 'anon', 'the page vote is an anonymous vote');
- });
-
await check('a board says how many votes of each kind of voter it holds', async () => {
await check('a board says how many votes of each kind of voter it holds', async () => {
const person = await t.get('/api/v1/showcase/rounds/' + rounds[0].id, { as: bob });
const person = await t.get('/api/v1/showcase/rounds/' + rounds[0].id, { as: bob });
assert.strictEqual(person.status, 200, person.text);
assert.strictEqual(person.status, 200, person.text);
deleted file mode 100644
deleted file mode 100644
@@ -1,122 +0,0 @@
@@ -1,122 +0,0 @@
-'use strict';
-
-/**
- * The battles (stage B9): blind pairs, the rules, and a vote that works without JavaScript.
- */
-
-const assert = require('assert');
-const { boot, check, done } = require('./helpers/boot');
-
-const FORM = { 'content-type': 'application/x-www-form-urlencoded' };
-
-(async () => {
- // One vote per hour for this person: the rate limit is part of the rules under test.
- const t = await boot({ env: { BENCH_BATTLE_PERSON_PER_HOUR: '1' } });
- const alice = t.network.addUser('alice');
- const bob = t.network.addUser('bob');
- const svc = t.network.serviceToken({ sub: 'svc:games', audience: 'openvibe.bench', cap: ['bench.entry.create'] });
-
- // Two finished rounds, each with two entries: two battles to hand out.
- const rounds = [];
- for (const n of [1, 2]) {
- const task = await t.get('/api/v1/kinds/design.ui/tasks', { as: alice, json: { prompt: 'A dashboard, prompt ' + n + '.', public: true } });
- assert.strictEqual(task.status, 201, task.text);
- const rr = await t.get('/api/v1/tasks/' + task.json().task.id + '/rounds', { as: alice, json: {} });
- assert.strictEqual(rr.status, 201, rr.text);
- const roundId = rr.json().round.id;
- const e1 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'acme', model: 'alpha-' + n, payload: { html: '<p>alpha ' + n + '</p>' }, cost_usd: 0.01 } });
- assert.strictEqual(e1.status, 201, e1.text);
- const e2 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'beta', model: 'beta-' + n, payload: { html: '<p>beta ' + n + '</p>' }, cost_usd: 0.02 } });
- assert.strictEqual(e2.status, 201, e2.text);
- rounds.push({ id: roundId, a: e1.json().entry.id, b: e2.json().entry.id });
- }
-
- await check('an anonymous reader is asked to sign in, and sees no pair', async () => {
- const r = await t.get('/battle');
- assert.strictEqual(r.status, 200, r.text);
- assert.ok(r.text.includes('Sign in'), 'the sign-in prompt');
- assert.ok(!r.text.includes('data-battle='), 'no pair for an anonymous reader');
- });
-
- await check('a signed-in person gets a blind pair', async () => {
- const r = await t.get('/battle', { as: bob });
- assert.strictEqual(r.status, 200, r.text);
- assert.ok(r.text.includes('data-battle="bbs_'), 'the battle id: ' + r.text.slice(0, 200));
- assert.ok(r.text.includes('data-slot="A"') && r.text.includes('data-slot="B"'), 'two panes');
- assert.ok(r.text.includes('Which one looks more finished?'), 'the kind\'s own question');
- assert.ok(!r.text.includes('alpha-1') && !r.text.includes('beta-1'), 'no model names before the vote');
- });
-
- await check('the vote is a form post that works without JavaScript', async () => {
- const page = await t.get('/battle', { as: bob });
- const battleId = /data-battle="(bbs_[^"]+)"/.exec(page.text)[1];
- const vote = await t.get('/battle', { as: bob, method: 'POST', headers: FORM, body: 'battle=' + encodeURIComponent(battleId) + '&outcome=A&answer=A' });
- assert.strictEqual(vote.status, 303, vote.text);
- assert.ok(String(vote.headers.get('location') || '').startsWith('/battle/revealed/'), 'the reveal page: ' + vote.headers.get('location'));
- const reveal = await t.get(vote.headers.get('location'), { as: bob });
- assert.strictEqual(reveal.status, 200, reveal.text);
- assert.ok(reveal.text.includes('alpha-') || reveal.text.includes('beta-'), 'the names are open after the vote');
- const row = await t.ctx.s.db.maybe("SELECT outcome, question, answer FROM bench_judgements WHERE judge = 'human_pairwise' ORDER BY at DESC LIMIT 1");
- assert.strictEqual(row.outcome, 'left');
- assert.strictEqual(row.question, 'Which one looks more finished?');
- assert.strictEqual(row.answer, 'A');
- });
-
- await check('a person never sees the same pair twice', async () => {
- const seen = await t.ctx.s.db.many('SELECT round_id, pair_key, voted_at FROM bench_battle_seen WHERE subject = (SELECT subject FROM bench_judgements ORDER BY at DESC LIMIT 1)');
- const pairs = new Set(seen.map((s) => s.pair_key));
- assert.strictEqual(pairs.size, seen.length, 'no pair twice: ' + JSON.stringify(seen));
- // Both rounds are used up now: there is nothing left to show this person.
- const more = await t.get('/battle', { as: bob });
- assert.strictEqual(more.status, 200, more.text);
- assert.ok(more.text.includes('No battle is waiting'), 'no pair left: ' + more.text.slice(0, 300));
- });
-
- await check('the rate limit stops the second vote', async () => {
- const before = await t.ctx.s.db.value("SELECT COUNT(*)::int AS n FROM bench_judgements WHERE judge = 'human_pairwise'");
- const rows = await t.ctx.s.db.many('SELECT id, round_id FROM bench_battle_seen WHERE voted_at IS NULL');
- assert.ok(rows.length >= 1, 'a pair is waiting');
- const vote = await t.get('/battle', { as: bob, method: 'POST', headers: FORM, body: 'battle=' + encodeURIComponent(rows[0].id) + '&outcome=B' });
- assert.strictEqual(vote.status, 429, vote.text);
- const after = await t.ctx.s.db.value("SELECT COUNT(*)::int AS n FROM bench_judgements WHERE judge = 'human_pairwise'");
- assert.strictEqual(after, before, 'no vote was written');
- });
-
- await check('an anonymous vote is refused, and a cross-site vote is refused', async () => {
- const rows = await t.ctx.s.db.many('SELECT id FROM bench_battle_seen WHERE voted_at IS NULL');
- const anon = await t.get('/battle', { method: 'POST', headers: FORM, body: 'battle=' + encodeURIComponent(rows[0].id) + '&outcome=A' });
- assert.ok(anon.status === 302 || anon.status === 303, 'the anonymous vote is sent to sign in: ' + anon.status);
- assert.ok(String(anon.headers.get('location') || '').startsWith('/auth/login'), 'to the sign-in page');
- const cross = await t.get('/battle', { as: bob, method: 'POST', headers: FORM, origin: 'https://evil.example', body: 'battle=' + encodeURIComponent(rows[0].id) + '&outcome=A' });
- assert.strictEqual(cross.status, 403, cross.text);
- });
-
- await check('the ratings board counts the battle vote, and the open data has no voter ids', async () => {
- const page = await t.get('/ratings', { as: bob });
- assert.strictEqual(page.status, 200, page.text);
- assert.ok(page.text.includes('Raw') && page.text.includes('Style controlled'), 'both columns');
- assert.ok(page.text.includes('alpha-'), 'a model on the board');
- // The board is a table: a band, a model, the scores with their intervals on one scale, and the votes.
- assert.ok(page.text.includes('class="br-table rt-table"'), 'the ranking table');
- assert.ok(page.text.includes('>Band<') && page.text.includes('>Votes<'), 'the columns');
- assert.ok(page.text.includes('class="rt-bar"'), 'the interval bar');
- assert.ok(page.text.includes('rt-band'), 'the band');
- // The style is explained in words, not as a number.
- assert.ok(!page.text.includes('Elo per 1000 characters'), 'no raw effect numbers: ' + page.text.slice(0, 200));
-
- const dump = await t.get('/api/v1/datasets/votes.jsonl');
- assert.strictEqual(dump.status, 200, dump.text);
- const lines = dump.text.trim().split('\n').filter(Boolean);
- assert.ok(lines.length >= 1, 'the dump has the vote');
- const subject = await t.ctx.s.db.value("SELECT subject FROM bench_judgements WHERE judge = 'human_pairwise' LIMIT 1");
- for (const line of lines) {
- const v = JSON.parse(line);
- assert.ok(v.left && v.right && v.outcome, 'a vote line: ' + line);
- assert.ok(!('subject' in v) && !('voter' in v) && !('user' in v), 'no voter id in the line: ' + line);
- assert.ok(!line.includes(subject), 'the voter is nowhere in the dump');
- }
- });
-
- await t.close();
- done();
-})().catch((e) => { console.error(e); process.exit(1); });
@@ -30,8 +30,6 @@ const { boot, check, done } = require('./helpers/boot');
@@ -30,8 +30,6 @@ const { boot, check, done } = require('./helpers/boot');
assert.deepStrictEqual(s.owner_subject, { type: 'user', id: alice.subject });
assert.deepStrictEqual(s.owner_subject, { type: 'user', id: alice.subject });
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: alice })).status, 200);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: alice })).status, 200);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: bob })).status, 404);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: bob })).status, 404);
- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: bob })).status, 404);
- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: alice })).status, 200);
});
});
await check('Specific people: the named person reads it; a stranger gets 404 and the page never leaks the title', async () => {
await check('Specific people: the named person reads it; a stranger gets 404 and the page never leaks the title', async () => {
@@ -40,11 +38,9 @@ const { boot, check, done } = require('./helpers/boot');
@@ -40,11 +38,9 @@ const { boot, check, done } = require('./helpers/boot');
assert.strictEqual(put.status, 200, put.text);
assert.strictEqual(put.status, 200, put.text);
assert.strictEqual(put.json().audience, 'people');
assert.strictEqual(put.json().audience, 'people');
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: bob })).status, 200);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: bob })).status, 200);
- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: bob })).status, 200);
- const stranger = await t.get(`/benchmarks/${id}`, { as: sam });
+ const stranger = await t.get(`/api/v1/benchmarks/${id}`, { as: sam });
assert.strictEqual(stranger.status, 404);
assert.strictEqual(stranger.status, 404);
assert.ok(!stranger.text.includes('Board pack bench'));
assert.ok(!stranger.text.includes('Board pack bench'));
- assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: sam })).status, 404);
assert.strictEqual((await t.get(`/api/v1/benchmark/${id}/share`, { as: sam })).status, 404, 'the sheet is the owner’s');
assert.strictEqual((await t.get(`/api/v1/benchmark/${id}/share`, { as: sam })).status, 404, 'the sheet is the owner’s');
});
});
@@ -54,12 +50,11 @@ const { boot, check, done } = require('./helpers/boot');
@@ -54,12 +50,11 @@ const { boot, check, done } = require('./helpers/boot');
assert.strictEqual((await row(id)).audience, 'public');
assert.strictEqual((await row(id)).audience, 'public');
assert.strictEqual((await t.ctx.s.db.maybe('SELECT visibility FROM bench_benchmarks WHERE id = $1', [id])).visibility, 'public');
assert.strictEqual((await t.ctx.s.db.maybe('SELECT visibility FROM bench_benchmarks WHERE id = $1', [id])).visibility, 'public');
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 200);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 200);
- const list = (await t.get('/benchmarks')).text;
- assert.ok(list.includes('Going public'));
+ assert.ok((await t.get('/api/v1/benchmarks')).json().benchmarks.some((b) => b.id === id), 'in the API list');
assert.strictEqual((await putShare(id, { audience: 'private' })).status, 200);
assert.strictEqual((await putShare(id, { audience: 'private' })).status, 200);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 404);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 404);
- assert.ok(!(await t.get('/benchmarks')).text.includes('Going public'));
- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: alice })).status, 200, 'the owner still reads it');
+ assert.ok(!(await t.get('/api/v1/benchmarks')).json().benchmarks.some((b) => b.id === id), 'out of the API list');
+ assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`, { as: alice })).status, 200, 'the owner still reads it');
});
});
await check('the sheet lists the members to the owner, and the old form keeps the row in step', async () => {
await check('the sheet lists the members to the owner, and the old form keeps the row in step', async () => {
@@ -69,10 +64,10 @@ const { boot, check, done } = require('./helpers/boot');
@@ -69,10 +64,10 @@ const { boot, check, done } = require('./helpers/boot');
assert.strictEqual(mine.status, 200);
assert.strictEqual(mine.status, 200);
assert.strictEqual(mine.json().members[0].username, 'sam');
assert.strictEqual(mine.json().members[0].username, 'sam');
assert.strictEqual(mine.json().you.role, 'owner');
assert.strictEqual(mine.json().you.role, 'owner');
- // The registry's own form (POST /benchmarks/:id/share) is the old way to change visibility.
- const form = await t.get(`/benchmarks/${id}/share`, { as: alice, body: 'visibility=public', headers: { 'content-type': 'application/x-www-form-urlencoded', origin: 'https://bench.openvibe.services' } });
- assert.ok([200, 303].includes(form.status), form.text.slice(0, 200));
+ // The API is the one way now that the HTML pages are gone; it keeps the old visibility column in step.
+ assert.strictEqual((await putShare(id, { audience: 'public' })).status, 200);
assert.strictEqual((await row(id)).audience, 'public');
assert.strictEqual((await row(id)).audience, 'public');
+ assert.strictEqual((await t.ctx.s.db.maybe('SELECT visibility FROM bench_benchmarks WHERE id = $1', [id])).visibility, 'public');
});
});
await done();
await done();
@@ -1,49 +1,23 @@
@@ -1,49 +1,23 @@
'use strict';
'use strict';
-/**
- * The home page lists the kinds, so each kind it lists is a schema.org Dataset: the JSONL the API serves for that kind
- * is the dataset itself (url and distribution), the description restates the kind's own room, judge and entry type, and
- * no licence is claimed because Bench states none. /docs publishes no dataset, so it carries no Dataset node.
- */
+// The Bench pages (with their schema.org Dataset nodes) moved into OpenVibe.Report's AI section (2026-11-08).
+// The open datasets themselves stay on Bench's API, which still lists every public kind.
const assert = require('assert');
const assert = require('assert');
const { boot, check, done } = require('./helpers/boot');
const { boot, check, done } = require('./helpers/boot');
-// One JSON-LD script per page: every node lives in the page's @graph.
-const scripts = (html) => [...String(html).matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1]));
-const nodes = (html, type) => scripts(html)
- .flatMap((x) => (Array.isArray(x['@graph']) ? x['@graph'] : [x])).filter((x) => x['@type'] === type);
-
(async () => {
(async () => {
const t = await boot();
const t = await boot();
- let kinds;
-
- await check('the home page carries one Dataset per kind, describing what the page lists', async () => {
- const r = await t.get('/');
- assert.strictEqual(r.status, 200);
- kinds = (await t.get('/api/v1/kinds')).json().kinds;
- assert.ok(kinds.length >= 2, `only ${kinds.length} kinds`);
- assert.strictEqual(scripts(r.text).length, 1, 'the page carries exactly one JSON-LD script');
- const sets = nodes(r.text, 'Dataset');
- assert.deepStrictEqual(sets.map((d) => d.name), kinds.map((k) => k.name));
- for (const [i, d] of sets.entries()) {
- const k = kinds[i];
- const url = `https://bench.openvibe.services/api/v1/datasets/${encodeURIComponent(k.id)}.jsonl`;
- assert.strictEqual(d.url, url);
- assert.strictEqual(d.distribution['@type'], 'DataDownload');
- assert.strictEqual(d.distribution.contentUrl, url);
- assert.strictEqual(d.creator.name, 'OpenVibe.Bench');
- assert.ok(d.description.includes(k.room) && d.description.includes(k.name), d.description);
- assert.ok(!('license' in d), 'no licence is claimed for data Bench does not license');
- assert.ok(r.text.includes(`href="/api/v1/boards/${k.id}?type=elo"`), `${k.id} is not listed on the page`);
+ await check('the home page redirects and the dataset API stays on Bench', async () => {
+ const home = await t.get('/');
+ assert.strictEqual(home.status, 301);
+ const kinds = await t.get('/api/v1/kinds');
+ assert.strictEqual(kinds.status, 200);
+ const list = kinds.json().kinds;
+ assert.ok(list.length >= 2, `only ${list.length} kinds`);
+ for (const k of list) {
+ const data = await t.get(`/api/v1/datasets/${encodeURIComponent(k.id)}.jsonl`);
+ assert.strictEqual(data.status, 200, k.id);
}
}
});
});
-
- await check('/docs publishes no dataset and therefore carries no Dataset node', async () => {
- const r = await t.get('/docs');
- assert.strictEqual(r.status, 200);
- assert.deepStrictEqual(nodes(r.text, 'Dataset'), []);
- });
-
await check('stop', async () => { await t.close(); });
await check('stop', async () => { await t.close(); });
-
done();
done();
})();
})();
deleted file mode 100644
deleted file mode 100644
@@ -1,61 +0,0 @@
@@ -1,61 +0,0 @@
-'use strict';
-
-/** The embed (B10): only that route may be framed, no cookies, and voting opens Bench in a new tab. */
-
-const assert = require('assert');
-const { boot, check, done } = require('./helpers/boot');
-
-(async () => {
- const t = await boot();
- const alice = t.network.addUser('alice');
- const bob = t.network.addUser('bob'); // the round belongs to somebody else: a battle is never your own
- const entrySvc = t.network.serviceToken({ sub: 'svc:room', audience: 'openvibe.bench', cap: ['bench.entry.create'] });
-
- const task = await t.get('/api/v1/kinds/games.play/tasks', { as: bob, json: { prompt: 'A game for an embed.', public: true } });
- const rr = await t.get(`/api/v1/tasks/${task.json().task.id}/rounds`, { as: bob, json: {} });
- const roundId = rr.json().round.id;
- const e1 = await t.get(`/api/v1/rounds/${roundId}/entries`, { bearer: entrySvc, json: { provider: 'acme', model: 'alpha-1', payload: { html: '<h1>a</h1>' } } });
- const e2 = await t.get(`/api/v1/rounds/${roundId}/entries`, { bearer: entrySvc, json: { provider: 'beta', model: 'beta-2', payload: { html: '<h1>b</h1>' } } });
- assert.strictEqual(e1.status, 201, e1.text);
- assert.strictEqual(e2.status, 201, e2.text);
-
- // A battle needs a finished round: the job would do it, the test does it directly.
- await t.ctx.s.db.exec("UPDATE bench_rounds SET status = 'finished' WHERE id = $1", [roundId]);
- const battle = await t.get('/battle', { as: alice });
- assert.strictEqual(battle.status, 200, battle.text.slice(0, 200));
- const id = /data-battle="([^"]+)"/.exec(battle.text)[1];
-
- await check('the embed may be framed, and every other route may not', async () => {
- const e = await t.get('/embed/battle/' + id);
- assert.strictEqual(e.status, 200, e.text.slice(0, 200));
- assert.ok(/frame-ancestors \*/.test(e.headers.get('content-security-policy')), 'the embed may be framed: ' + e.headers.get('content-security-policy'));
- const home = await t.get('/');
- assert.ok(/frame-ancestors 'none'/.test(home.headers.get('content-security-policy')), 'the rest refuses frames: ' + home.headers.get('content-security-policy'));
- assert.strictEqual(home.headers.get('x-frame-options'), 'DENY');
- });
-
- await check('the embed reads and sets no cookie', async () => {
- const e = await t.get('/embed/battle/' + id);
- assert.strictEqual(e.headers.get('set-cookie'), null, 'no cookie is set');
- const home = await t.get('/');
- assert.strictEqual(home.headers.get('set-cookie'), null, 'and none on the other pages either');
- });
-
- await check('the embed keeps the frames sandboxed and sends the vote to a new tab', async () => {
- const e = await t.get('/embed/battle/' + id);
- assert.ok(e.text.includes('sandbox="allow-scripts allow-same-origin"'), 'the frames are sandboxed');
- assert.ok(e.text.includes('target="_blank"'), 'the vote opens a new tab');
- assert.ok(e.text.includes('rel="noopener"'), 'and it is safe');
- assert.ok(e.text.includes('noindex'), 'it is not indexed');
- assert.ok(e.text.includes('OpenVibe Bench'), 'it links back');
- });
-
- await check('an unknown battle is a plain 404, still framable', async () => {
- const e = await t.get('/embed/battle/bbs_nope');
- assert.strictEqual(e.status, 404);
- assert.ok(/frame-ancestors \*/.test(e.headers.get('content-security-policy')));
- });
-
- await t.close();
- done();
-})().catch((e) => { console.error(e); process.exit(1); });
@@ -114,11 +114,6 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01
@@ -114,11 +114,6 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01
assert.strictEqual(rankings.since.basis, 'tasks');
assert.strictEqual(rankings.since.basis, 'tasks');
assert.strictEqual(rankings.entries.find((e) => e.model === 'model-a').score, 20, 'only the fresh task is scored');
assert.strictEqual(rankings.entries.find((e) => e.model === 'model-a').score, 20, 'only the fresh task is scored');
assert.strictEqual(rankings.entries.find((e) => e.model === 'model-b').score, 80);
assert.strictEqual(rankings.entries.find((e) => e.model === 'model-b').score, 80);
-
- const page = await t.get(`/benchmarks/${made.id}?since=2026-03`);
- assert.strictEqual(page.status, 200, page.text.slice(0, 300));
- assert.ok(page.text.includes('On tasks since March 2026'), 'the page names the fresh set beside the board');
- assert.ok(page.text.includes('All-time board'), 'and offers the all-time board');
});
});
await check('a held-out task never leaks, and a submitted run gets no score for it', async () => {
await check('a held-out task never leaks, and a submitted run gets no score for it', async () => {
@@ -230,50 +225,9 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01
@@ -230,50 +225,9 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01
assert.strictEqual(entry.horizon.runs, 3);
assert.strictEqual(entry.horizon.runs, 3);
assert.ok(Math.abs(entry.horizon.horizon_minutes - 40) < 6, `horizon ${entry.horizon.horizon_minutes}`);
assert.ok(Math.abs(entry.horizon.horizon_minutes - 40) < 6, `horizon ${entry.horizon.horizon_minutes}`);
- const page = await t.get(`/benchmarks/${made.id}?metric=horizon`);
- assert.ok(page.text.includes('br-c-horizon'), 'the board has a horizon column');
- assert.ok(page.text.includes('min'), 'the horizon is a number of minutes');
- const model = await t.get('/models/model-timed');
- assert.strictEqual(model.status, 200, model.text.slice(0, 200));
- assert.ok(model.text.includes('reliably does about'), 'the model profile has the horizon line');
- const thin = await t.get('/models/model-a');
- assert.strictEqual(thin.status, 200, thin.text.slice(0, 200));
- assert.ok(thin.text.includes('not enough runs yet'), 'below the gate the model page says so instead of a number');
});
});
- await check('editors estimate on the task page: sign-in, same-origin, median of three, account data', async () => {
- const made = await create({
- title: 'Estimate arena',
- tasks: [baseTask('timed', { added_on: '2026-02-01' })],
- });
- const path = `/benchmarks/${made.id}/tasks/timed`;
- const anon = await t.get(path);
- assert.strictEqual(anon.status, 200, anon.text.slice(0, 200));
- assert.ok(anon.text.includes('Sign in to estimate'), 'a signed-out reader is asked to sign in');
- assert.strictEqual((await postForm(`/benchmarks/${made.id}/tasks/timed/estimates`, { minutes: '30', version: String(made.version) })).status, 401);
-
- const crossSite = await t.get(`/benchmarks/${made.id}/tasks/timed/estimates`, {
- as: alice, method: 'POST', raw: 'minutes=30', headers: { 'content-type': 'application/x-www-form-urlencoded', origin: 'https://evil.example' },
- });
- assert.strictEqual(crossSite.status, 403, 'a cross-site write is refused even when signed in');
-
- for (const [user, minutes] of [[alice, '30'], [bob, '40'], [carol, '50']]) {
- const res = await postForm(`/benchmarks/${made.id}/tasks/timed/estimates`, { minutes, version: String(made.version) }, user);
- assert.strictEqual(res.status, 303, res.text.slice(0, 300));
- }
- const page = await t.get(path);
- assert.ok(page.text.includes('median of 3 editor estimates'), page.text.slice(0, 600));
- assert.ok(page.text.includes('40 min'), 'the median is 40 minutes');
-
- const exported = await t.ctx.accountData.exportPart(alice.subject);
- const file = (exported.files || []).find((f) => f.name === 'bench_task_estimates.json');
- assert.ok(file && file.content.length === 1, 'the estimate is in the person\'s export');
- assert.strictEqual(Number(file.content[0].minutes), 30);
- const erased = await t.ctx.accountData.erase([alice.subject]);
- assert.ok(Number(erased.erased.bench_task_estimates) >= 1, 'the estimate is erased with the person');
- });
-
- await check('the contamination badge is shown, with both numbers and their bands', async () => {
+ await check('the contamination flag is in the API with both numbers', async () => {
const made = await create({
const made = await create({
title: 'Old and fresh arena',
title: 'Old and fresh arena',
tasks: [
tasks: [
@@ -287,10 +241,6 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01
@@ -287,10 +241,6 @@ const result = (task_id, score, extra = {}) => ({ task_id, score, cost_usd: 0.01
const board = await t.get(`/api/v1/rankings?benchmark=${made.id}&metric=score`);
const board = await t.get(`/api/v1/rankings?benchmark=${made.id}&metric=score`);
const entry = board.json().rankings.entries.find((e) => e.model === 'gpt-5');
const entry = board.json().rankings.entries.find((e) => e.model === 'gpt-5');
assert.strictEqual(entry.contamination.flag, true, JSON.stringify(entry.contamination));
assert.strictEqual(entry.contamination.flag, true, JSON.stringify(entry.contamination));
- const page = await t.get(`/benchmarks/${made.id}`);
- assert.ok(page.text.includes('Did much better on older tasks: possibly seen in training'), 'the exact badge text');
- assert.ok(page.text.includes('Older tasks'), 'both numbers');
- assert.ok(page.text.includes('fresh tasks'), 'and their bands');
});
});
await t.close();
await t.close();
deleted file mode 100644
deleted file mode 100644
@@ -1,96 +0,0 @@
@@ -1,96 +0,0 @@
-'use strict';
-
-/**
- * The gallery (stage B9): every round's outputs, filtered and ordered, with at most six live frames.
- */
-
-const assert = require('assert');
-const { boot, check, done } = require('./helpers/boot');
-
-(async () => {
- const t = await boot({ env: { BENCH_ROUNDS_PER_DAY: '30', BENCH_ROUNDS_DAILY: '100' } });
- const alice = t.network.addUser('alice');
- const svc = t.network.serviceToken({ sub: 'svc:games', audience: 'openvibe.bench', cap: ['bench.entry.create'] });
-
- const make = async (kind, prompt, model, cost) => {
- const task = await t.get('/api/v1/kinds/' + kind + '/tasks', { as: alice, json: { prompt, public: true } });
- assert.strictEqual(task.status, 201, task.text);
- const rr = await t.get('/api/v1/tasks/' + task.json().task.id + '/rounds', { as: alice, json: {} });
- const roundId = rr.json().round.id;
- const e1 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'acme', model, payload: { html: '<p>one</p>' }, cost_usd: cost } });
- const e2 = await t.get('/api/v1/rounds/' + roundId + '/entries', { bearer: svc, json: { provider: 'beta', model: model + '-b', payload: { html: '<p>two</p>' }, cost_usd: cost } });
- assert.strictEqual(e1.status, 201, e1.text);
- assert.strictEqual(e2.status, 201, e2.text);
- return roundId;
- };
-
- const rounds = [];
- for (let i = 0; i < 5; i++) rounds.push(await make('design.ui', 'A page, number ' + i, 'model-' + i, 0.01 * (i + 1)));
- const gameRound = await make('games.play', 'A game, one', 'gamer-1', 0.5);
-
- await check('the gallery lists the rounds', async () => {
- const r = await t.get('/gallery');
- assert.strictEqual(r.status, 200, r.text);
- assert.ok(r.text.includes('data-gallery="1"'), 'the gallery');
- assert.ok(r.text.includes('A page, number 0'), 'a prompt');
- assert.ok(r.text.includes('ga-card'), 'the cards');
- assert.ok(r.text.includes('model-0'), 'the model of an entry');
- });
-
- await check('at most six frames are live, the rest wait for a tap', async () => {
- const r = await t.get('/gallery');
- const live = (r.text.match(/class="ga-frame"/g) || []).length;
- assert.ok(live <= 6, 'live frames: ' + live);
- assert.ok(live >= 4, 'the first cards are live: ' + live);
- assert.ok(r.text.includes('data-ga-still'), 'the stills');
- assert.ok(r.text.includes('Live frame, on tap'), 'the tap hint');
- });
-
- await check('the filters narrow the gallery', async () => {
- const byKind = await t.get('/gallery?kind=games.play');
- assert.ok(byKind.text.includes('A game, one'), 'the game is there');
- assert.ok(!byKind.text.includes('A page, number 0'), 'the pages are not');
-
- const byModel = await t.get('/gallery?model=model-2');
- assert.ok(byModel.text.includes('A page, number 2'), 'the model is there');
- assert.ok(!byModel.text.includes('A page, number 3'), 'the others are not');
-
- const byPrice = await t.get('/gallery?price=0.02');
- assert.ok(byPrice.text.includes('A page, number 0'), 'the cheap one is there');
- assert.ok(!byPrice.text.includes('A page, number 4'), 'the expensive one is not');
- });
-
- await check('the gallery orders by newest and by most voted', async () => {
- const newest = await t.get('/gallery?order=new');
- const votes = await t.get('/gallery?order=votes');
- assert.strictEqual(newest.status, 200);
- assert.strictEqual(votes.status, 200);
- const firstNewest = /data-round-id="([^"]+)"/.exec(newest.text)[1];
- const firstVotes = /data-round-id="([^"]+)"/.exec(votes.text)[1];
- assert.ok(firstNewest, 'a round in the newest order');
- assert.ok(firstVotes, 'a round in the most voted order');
- // The last round made is the newest; no votes yet, so the most-voted order falls back to the newest.
- assert.strictEqual(firstNewest, firstVotes, 'without votes both orders agree');
- });
-
- await check('the gallery API answers the same thing', async () => {
- const r = await t.get('/api/v1/gallery?kind=design.ui');
- assert.strictEqual(r.status, 200, r.text);
- assert.ok(r.json().rounds.length >= 5, 'the rounds');
- assert.strictEqual(r.json().filters.kind, 'design.ui');
- const one = r.json().rounds[0];
- assert.ok(one.entries.length === 2, 'two entries');
- assert.ok(one.entries[0].wrapper_url.includes('/render/w/'), 'the sandboxed frame');
- });
-
- await check('an empty board says so, and offers a battle', async () => {
- const page = await t.get('/ratings');
- assert.strictEqual(page.status, 200, page.text);
- assert.ok(page.text.includes('No battles voted yet'), 'the empty board: ' + page.text.slice(0, 200));
- assert.ok(page.text.includes('Vote in a battle'), 'the button');
- assert.ok(page.text.includes('href="/battle"'), 'it goes to the battle');
- });
-
- await t.close();
- done();
-})().catch((e) => { console.error(e); process.exit(1); });
deleted file mode 100644
deleted file mode 100644
@@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
-'use strict';
-// The home on a phone (the owner's short-pages rule, 2026-10-30): the parts a reader rarely needs — the promises
-// and the kinds for developers — fold closed, with a checkbox and a label and no script. Everything stays in the
-// page for the crawlers, and where there is room the fold is open and looks like the section it replaces.
-const assert = require('assert');
-const fs = require('fs');
-const path = require('path');
-const { boot, check, done } = require('./helpers/boot');
-const { pickBlock } = require('../server/render/short');
-
-(async () => {
- const t = await boot();
-
- await check('the home folds the promises and the kinds for developers, and keeps both in the page', async () => {
- const r = await t.get('/');
- assert.strictEqual(r.status, 200, r.text.slice(0, 300));
- const home = r.text;
- for (const [id, title] of [['bm-why', 'What Bench promises'], ['bm-kinds', 'For developers: the arena kinds']]) {
- const re = new RegExp(`<div class="fold[^"]*" id="fold-${id}"><input class="fold-toggle" type="checkbox" id="fold-${id}-open">`
- + `<label class="fold-h" for="fold-${id}-open"><h2[^>]*>[^<]*${title}[^<]*</h2>.*?<div class="fold-body">`);
- assert.ok(re.test(home), `the ${title} fold is on the home`);
- }
- // The parts themselves are in the page, not removed: the four promises and every kind.
- assert.strictEqual((home.match(/<li><b>(Outputs, not just scores|Every run published|Value you can read|Free and open)<\/b>/g) || []).length, 4, 'the four promises are in the page');
- const kinds = (home.match(/class="bm-kind-link"/g) || []).length;
- assert.ok(kinds >= 9, `the kinds are in the page (${kinds})`);
- assert.match(home, /<h2 id="bm-kinds-h">For developers: the arena kinds<\/h2>/, 'the kinds keep their heading');
- // The benchmarks are the main thing on the home: they stay as they were.
- assert.match(home, /<section class="bm-section" aria-labelledby="bm-bench-h">/, 'the benchmarks section is where it was');
- });
-
- await check('the fold is closed on a phone and open where there is room', async () => {
- const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'bench.css'), 'utf8');
- const fold = css.slice(css.indexOf('/* The fold (the owner'));
- assert.ok(fold.length > 0, 'the fold rules are in bench.css');
- assert.match(fold, /\.fold-body \{ display: none; \}/, 'the body is closed by default');
- assert.match(fold, /\.fold-toggle:checked ~ \.fold-body \{ display: block; \}/, 'the title row opens it');
- assert.match(fold, /\.fold-toggle \{ display: none; \}/, 'the toggle is hidden where there is room');
- assert.match(fold, /\.fold-body \{ display: block; \}/, 'the body is open where there is room');
- assert.match(fold, /\.fold-bare > \.fold-h \{ display: none; \}/, 'a part with no heading of its own looks as it did');
- assert.ok(!/#[0-9a-f]{3,6}\b/i.test(fold.replace(/#[0-9a-f]{3,6}\b/gi, '')), 'no new colours in the fold rules');
- });
-
- await check('pickBlock makes link chips, not radios, and every chip points at a pane in the block', () => {
- const html = pickBlock({ key: 'k', label: 'Pick me', head: 'Headline', panes: [{ id: 'a', label: 'Alpha', html: '<p>one</p>' }, { id: 'b', label: 'Beta', html: '<p>two</p>' }] });
- assert.ok(!/type="radio"/.test(html), 'no radio input');
- assert.ok(!/role="radiogroup"/.test(html), 'the row is not a radiogroup');
- const chips = [...html.matchAll(/<a class="pick-chip" href="#(pp-k-[ab])">([^<]+)<\/a>/g)];
- assert.deepStrictEqual(chips.map((m) => [m[1], m[2]]), [['pp-k-a', 'Alpha'], ['pp-k-b', 'Beta']], 'a link per pane, in order');
- assert.ok(html.includes('<div class="pick-pane" id="pp-k-a">'), 'pane a is in the page');
- assert.ok(html.includes('<div class="pick-pane" id="pp-k-b">'), 'pane b is in the page');
- assert.ok(html.includes('<nav class="pick-row" aria-label="Pick me">'), 'the chips are a nav with the label');
- assert.ok(html.includes('<div class="pick-head"><h2>Headline</h2></div>'), 'the block keeps its heading');
- });
-
- await t.close();
- done();
-})().catch((e) => { console.error(e); process.exit(1); });
@@ -297,12 +297,6 @@ function multipart(filename, bytes) {
@@ -297,12 +297,6 @@ function multipart(filename, bytes) {
assert.strictEqual(runs[0].harness.name, 'inspect-ai');
assert.strictEqual(runs[0].harness.name, 'inspect-ai');
});
});
- await check('the run page says where it came from', async () => {
- const page = await t.get('/runs/' + runId, { as: alice });
- assert.strictEqual(page.status, 200, page.text.slice(0, 200));
- assert.ok(/Imported from inspect 0\.3\.286/.test(page.text), 'the label is there');
- });
-
await check('the file stays private to its owner', async () => {
await check('the file stays private to its owner', async () => {
const mine = await t.get('/imports/' + importId + '/file', { as: alice });
const mine = await t.get('/imports/' + importId + '/file', { as: alice });
assert.strictEqual(mine.status, 200);
assert.strictEqual(mine.status, 200);
@@ -146,31 +146,6 @@ const { boot, check, done } = require('./helpers/boot');
@@ -146,31 +146,6 @@ const { boot, check, done } = require('./helpers/boot');
assert.strictEqual(claimed, null, 'the job runner never claims it');
assert.strictEqual(claimed, null, 'the job runner never claims it');
});
});
- await check('the battle page: two clips, blind, playable; no share card; the reveal names the pickers only', async () => {
- const fresh = await newRound();
- for (const v of ['v3', 'v4']) {
- const e = await t.get(`/api/v1/rounds/${fresh.id}/entries`, { bearer: live, json: { payload: clip(v), cost_usd: 0.001 } });
- assert.strictEqual(e.status, 201, e.text);
- }
- const viewer = t.network.addUser('battler');
- // The battle picks among waiting rounds at random; the newest (this one) comes first in its list.
- const was = t.ctx.random;
- t.ctx.random = () => 0;
- const page = await t.get('/battle?kind=live.clip', { as: viewer });
- t.ctx.random = was;
- assert.strictEqual(page.status, 200, page.text);
- const videos = page.text.match(/<video class="bt-clip"[^>]*src="https:\/\/media\.openvibe\.media\/p\/v[34]\.mp4/g) || [];
- assert.strictEqual(videos.length, 2, 'both clips play');
- assert.ok(!page.text.includes('The cat walks onto the keyboard'), 'no title: the vote is on the moment');
- assert.ok(!page.text.includes('moment-picker'), 'blind: no policy before the vote');
- assert.ok(!page.text.includes('/battle/card/'), 'no share card for a private battle');
- assert.ok(page.text.includes('hls.min.js'), 'HLS clips can play');
- const battleId = /data-battle="([^"]+)"/.exec(page.text)[1];
- assert.strictEqual((await t.get(`/battle/card/${battleId}.svg`)).status, 404);
- const csp = page.headers.get('content-security-policy') || '';
- assert.match(csp, /media-src[^;]*https:\/\/openvibe\.media/);
- });
-
await check('a live.clip round spends no AI: it skips the daily round limits and counts against none', async () => {
await check('a live.clip round spends no AI: it skips the daily round limits and counts against none', async () => {
const quota = await boot({ env: { BENCH_ROUNDS_PER_DAY: '1', BENCH_ROUNDS_DAILY: '1' } });
const quota = await boot({ env: { BENCH_ROUNDS_PER_DAY: '1', BENCH_ROUNDS_DAILY: '1' } });
const liveSvc = quota.network.serviceToken({ sub: 'svc:live', audience: 'openvibe.bench', cap: CAPS, ns: ['rec:vod_*'] });
const liveSvc = quota.network.serviceToken({ sub: 'svc:live', audience: 'openvibe.bench', cap: CAPS, ns: ['rec:vod_*'] });
@@ -1,29 +1,22 @@
@@ -1,29 +1,22 @@
'use strict';
'use strict';
-// SEO-33: /llms-full.txt holds the API page in full (it was one line a page, 314 characters in all), from the same
-// view /docs renders, so a language model reading it learns the API without fetching HTML.
+// The Bench pages moved to OpenVibe.Report (2026-11-08). Bench keeps the API, the agent manifest and its datasets;
+// the two llms files answer 301 to Report's, which now describes the AI section.
const assert = require('assert');
const assert = require('assert');
const { boot, check, done } = require('./helpers/boot');
const { boot, check, done } = require('./helpers/boot');
(async () => {
(async () => {
const t = await boot();
const t = await boot();
- await check('llms-full.txt: the home, then the API page in full', async () => {
- const r = await t.get('/llms-full.txt');
- assert.strictEqual(r.status, 200);
- assert.match(r.headers.get('content-type') || '', /^text\/plain/);
- assert.ok(r.text.startsWith('# OpenVibe.Bench'));
- assert.match(r.text, /### The Bench API\n\nURL: [^\n]*\/docs\n/);
- assert.ok(r.text.length > 2000, `${r.text.length} characters`);
- assert.ok(!/<\/?(div|p|span|a|code|pre|section|h[1-6]|li|ul|ol|table|tr|td|th|button|nav)\b[^>]*>/i.test(r.text.split('### The Bench API')[1] || ''), 'the page as text, no markup');
+ await check('llms.txt and llms-full.txt answer 301 to Report', async () => {
+ for (const p of ['/llms.txt', '/llms-full.txt']) {
+ const r = await t.get(p);
+ assert.strictEqual(r.status, 301, p);
+ assert.strictEqual(r.headers.get('location'), `https://openvibe.report${p}`, p);
+ }
});
});
- await check('llms.txt (AGT-5): own pages at their Markdown twins, the API and files as they are, Optional last', async () => {
- const r = await t.get('/llms.txt');
+ await check('the API docs stay readable as JSON on Bench', async () => {
+ const r = await t.get('/openapi.json');
assert.strictEqual(r.status, 200);
assert.strictEqual(r.status, 200);
- assert.match(r.text, /\(https?:\/\/[^)]*\/index\.md\)/, 'the home links its twin');
- assert.match(r.text, /\(https?:\/\/[^)]*\/docs\.md\)/);
- assert.match(r.text, /\(https?:\/\/[^)]*\/api\/v1\/kinds\)/);
- assert.ok(r.text.includes('\n## Optional\n') && r.text.lastIndexOf('\n## ') === r.text.indexOf('\n## Optional\n'));
- const twin = await t.get('/docs.md');
- assert.strictEqual(twin.status, 200, 'the twin it links answers');
+ assert.ok(r.json().paths && Object.keys(r.json().paths).length > 0);
});
});
await check('stop', async () => { await t.close(); });
await check('stop', async () => { await t.close(); });
done();
done();
@@ -77,23 +77,6 @@ const { KINDS } = require('../server/kinds');
@@ -77,23 +77,6 @@ const { KINDS } = require('../server/kinds');
}
}
}
}
- await check('the pick page answers from the data, without JavaScript', async () => {
- const r = await t.get('/pick?kind=design.page');
- assert.strictEqual(r.status, 200, r.text.slice(0, 300));
- assert.ok(r.text.includes('method="get"'), 'the form is a GET');
- assert.ok(r.text.includes('pk-table'), 'the frontier table');
- assert.ok(r.text.includes('alpha-1'), 'the top model');
- assert.ok(r.text.includes('because no other model beats it'), 'why the top pick is first');
- assert.ok(r.text.includes('pk-card') || r.text.includes('pk-shot'), 'a sample output');
- });
-
- await check('a model with fewer than three runs is greyed and says so', async () => {
- const r = await t.get('/pick?kind=design.page');
- assert.ok(r.text.includes('beta-2'), 'the thin model is shown');
- assert.ok(r.text.includes('too few runs'), 'it says the data is thin');
- assert.ok(/class="pk-thin"/.test(r.text), 'and it is greyed');
- });
-
await check('the API answers the same thing as the page', async () => {
await check('the API answers the same thing as the page', async () => {
const api = await t.get('/api/v1/pick?kind=design.page');
const api = await t.get('/api/v1/pick?kind=design.page');
assert.strictEqual(api.status, 200, api.text.slice(0, 300));
assert.strictEqual(api.status, 200, api.text.slice(0, 300));
@@ -120,10 +103,7 @@ const { KINDS } = require('../server/kinds');
@@ -120,10 +103,7 @@ const { KINDS } = require('../server/kinds');
assert.ok(json.rows.every((r) => !r.median_ms || r.median_ms <= json.filters.fast_cap_ms), 'only the quick ones');
assert.ok(json.rows.every((r) => !r.median_ms || r.median_ms <= json.filters.fast_cap_ms), 'only the quick ones');
});
});
- await check('the words set the controls, and the page says how it read them', async () => {
- const r = await t.get('/pick?q=' + encodeURIComponent('landing page under 2 cents, fast'));
- assert.strictEqual(r.status, 200);
- assert.ok(r.text.includes('design.page'), 'the kind it read');
+ await check('the words set the controls, and the API says how it read them', async () => {
const json = (await t.get('/api/v1/pick?q=' + encodeURIComponent('landing page under 2 cents, fast'))).json();
const json = (await t.get('/api/v1/pick?q=' + encodeURIComponent('landing page under 2 cents, fast'))).json();
assert.strictEqual(json.controls.kind, 'design.page');
assert.strictEqual(json.controls.kind, 'design.page');
assert.strictEqual(json.controls.max_cost, 0.02);
assert.strictEqual(json.controls.max_cost, 0.02);
@@ -131,12 +111,6 @@ const { KINDS } = require('../server/kinds');
@@ -131,12 +111,6 @@ const { KINDS } = require('../server/kinds');
assert.ok(json.controls.notes.length >= 3, 'it says what it read');
assert.ok(json.controls.notes.length >= 3, 'it says what it read');
});
});
- await check('a kind with nothing in it says so, and invents nothing', async () => {
- const r = await t.get('/pick?kind=games.three');
- assert.strictEqual(r.status, 200);
- assert.ok(r.text.includes('Nothing to pick yet'), 'the empty answer');
- });
-
await t.close();
await t.close();
done();
done();
})().catch((e) => { console.error(e); process.exit(1); });
})().catch((e) => { console.error(e); process.exit(1); });
new file mode 100644
new file mode 100644
@@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
+'use strict';
+
+/**
+ * The move to OpenVibe.Report (owner, 2026-11-08): the public pages answer 301 to their Report counterparts while
+ * the API keeps answering here, and the public reads Report needs (runs, overview, a model's summary) are exposed
+ * with real run rows only.
+ */
+const assert = require('assert');
+const { boot, check, done } = require('./helpers/boot');
+
+const TASKS = [
+ { id: 't1', prompt: 'Build a one-screen arcade game.', files: [], expected: null, holdout: false },
+ { id: 't2', prompt: 'Build a snake game.', files: [], expected: null, holdout: false },
+];
+
+function definition(over = {}) {
+ return {
+ title: 'Playable games arena', description: 'A test set.', 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: 0, usd: 0.5 },
+ matrix: { models: [], harnesses: [] }, ...over,
+ };
+}
+
+function runDoc(benchmark, over = {}) {
+ return {
+ benchmark, model: 'model-a', provider: 'acme', route: { key: 'acme' }, harness: { name: 'codes', version: '1' },
+ executor: { kind: 'codes', id: 'task_1' }, tasks: [
+ { task_id: 't1', score: 80, cost_usd: 0.02, ms: 1000, tokens: 10, artifacts: [] },
+ { task_id: 't2', score: 60, cost_usd: 0.03, ms: 3000, tokens: 20, artifacts: [] },
+ ],
+ score: 70, cost_usd: 0.05, ms: 4000, tokens: 30, ...over,
+ };
+}
+
+(async () => {
+ const t = await boot();
+ const writer = t.network.serviceToken({ sub: 'svc:codes', audience: 'openvibe.bench', cap: ['bench.run.publish', 'bench.benchmark.write'] });
+ let benchmarkId;
+ let version;
+
+ try {
+ const created = await t.get('/api/v1/benchmarks', { bearer: writer, json: definition() });
+ assert.strictEqual(created.status, 201, created.text);
+ benchmarkId = created.json().benchmark.id;
+ const frozen = await t.get(`/api/v1/benchmarks/${benchmarkId}/versions`, { bearer: writer, json: {} });
+ assert.strictEqual(frozen.status, 201, frozen.text);
+ version = frozen.json().version.version;
+ for (const [model, score, cost] of [['model-a', 70, 0.05], ['model-b', 90, 0.10]]) {
+ const made = await t.get('/api/v1/runs', { bearer: writer, json: runDoc(`${benchmarkId}@${version}`, { model, score, cost_usd: cost }) });
+ assert.strictEqual(made.status, 201, made.text);
+ }
+
+ await check('public runs list newest first with the last run date', async () => {
+ const r = await t.get(`/api/v1/runs?benchmark=${benchmarkId}&limit=2`);
+ assert.strictEqual(r.status, 200, r.text);
+ const body = r.json();
+ assert.strictEqual(body.count, 2);
+ assert.ok(body.last_run, 'a last run date');
+ assert.deepStrictEqual(body.runs.map((x) => x.model), ['model-b', 'model-a']);
+ assert.ok(body.runs.every((x) => x.provenance === 'verified'));
+ const byKind = await t.get('/api/v1/runs?kind=games.play');
+ assert.strictEqual(byKind.json().count, 2, 'the kind filter reads the benchmark kind');
+ const byModel = await t.get('/api/v1/runs?model=model-a');
+ assert.deepStrictEqual(byModel.json().runs.map((x) => x.model), ['model-a']);
+ });
+
+ await check('overview counts real public benchmarks and runs only', async () => {
+ const r = await t.get('/api/v1/overview');
+ assert.strictEqual(r.status, 200, r.text);
+ const body = r.json();
+ const mine = body.list.find((b) => b.id === benchmarkId);
+ assert.ok(mine, 'the test benchmark is listed');
+ assert.strictEqual(mine.runs, 2);
+ assert.ok(body.runs >= 2 && body.models >= 2 && body.tasks >= 2, JSON.stringify(body));
+ assert.ok(body.last_run, 'the newest run');
+ assert.ok(Array.isArray(body.recent) && body.recent.length >= 2, 'recent runs');
+ assert.deepStrictEqual(body.model_list.map((m) => m.model).sort(), ['model-a', 'model-b'], 'every public model with its last run');
+ assert.ok(!JSON.stringify(body).includes('t2 build a snake'), 'no task prompt copied into the overview');
+ });
+
+ await check('one model summary carries its task best and worst, cost and runs', async () => {
+ const r = await t.get('/api/v1/models/model-a/summary');
+ assert.strictEqual(r.status, 200, r.text);
+ const body = r.json();
+ assert.strictEqual(body.model, 'model-a');
+ assert.strictEqual(body.runs, 1);
+ assert.strictEqual(body.benchmarks[0].score, 70);
+ assert.strictEqual(body.total_cost_usd, 0.05);
+ assert.strictEqual(body.best_task.task_id, 't1');
+ assert.strictEqual(body.worst_task.task_id, 't2');
+ assert.strictEqual(body.best_task.score, 80);
+ assert.strictEqual(body.tasks.length, 2);
+ assert.ok(body.last_run && body.recent_runs.length === 1);
+ });
+
+ await check('HTML pages answer 301 to their Report counterparts', async () => {
+ const root = await t.get('/');
+ assert.strictEqual(root.status, 301);
+ assert.strictEqual(root.headers.get('location'), 'https://openvibe.report/ai');
+ const bench = await t.get(`/benchmarks/${benchmarkId}`);
+ assert.strictEqual(bench.status, 301);
+ assert.strictEqual(bench.headers.get('location'), `https://openvibe.report/ai/${benchmarkId}`);
+ const model = await t.get('/models/model-a');
+ assert.strictEqual(model.status, 301);
+ assert.strictEqual(model.headers.get('location'), 'https://openvibe.report/ai/models/model-a');
+ for (const [path, location] of [['/docs', 'https://openvibe.report/ai/method'], ['/compare', 'https://openvibe.report/ai/compare'], ['/showcase', 'https://openvibe.report/ai'], ['/gallery', 'https://openvibe.report/ai']]) {
+ const r = await t.get(path);
+ assert.strictEqual(r.status, 301, path);
+ assert.strictEqual(r.headers.get('location'), location, path);
+ }
+ const tab = await t.get('/benchmarks?tab=pareto');
+ assert.strictEqual(tab.headers.get('location'), 'https://openvibe.report/ai?tab=pareto');
+ });
+
+ await check('the API, MCP and render origin stay on Bench (no redirect)', async () => {
+ for (const path of ['/api/v1/kinds', '/api/v1/overview', `/api/v1/benchmarks/${benchmarkId}`, '/api/health']) {
+ const r = await t.get(path);
+ assert.strictEqual(r.status, 200, path);
+ assert.ok(r.headers.get('location') == null, `${path} redirected`);
+ }
+ });
+ } finally {
+ await t.close();
+ }
+ done();
+})().catch((e) => { console.error(e); process.exit(1); });
deleted file mode 100644
deleted file mode 100644
@@ -1,212 +0,0 @@
@@ -1,212 +0,0 @@
-'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 });
- const privatePage = await create({ title: 'Alice private prompts', visibility: 'private', tags: ['your-prompts'] }, alice);
- await publish(`${privatePage.id}@${privatePage.version}`, { model: 'model-best', score: 77, 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 <script>alert(1)</script> 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('the phone sections are all shown, and the chips are links that jump to them', async () => {
- const res = await t.get(`/benchmarks/${pub.id}`);
- const chips = [...res.text.matchAll(/<nav class="br-sec-pick" aria-label="Benchmark sections">([\s\S]*?)<\/nav>/g)];
- assert.strictEqual(chips.length, 1, 'one row of chips');
- assert.deepStrictEqual([...chips[0][1].matchAll(/<a href="#(br-[a-z]+)">/g)].map((m) => m[1]), ['br-rank', 'br-tasks', 'br-runs'], 'a link per section, in order');
- assert.ok(!/type="radio" name="br-view"/.test(res.text), 'no chip is a radio: nothing is picked');
- assert.match(res.text, /<div class="br-sections">[\s\S]*id="br-rank"[\s\S]*id="br-tasks"[\s\S]*id="br-runs"[\s\S]*<\/div>/, 'every section is in the page');
- const css = await t.get('/css/bench.css');
- assert.ok(!/\.br-sections > section \{ display: none/.test(css.text) && !css.text.includes('#br-view-rank:checked'), 'no section is ever hidden (the owner, 2026-10-31)');
- });
-
- await check('badges and embeds are one closed details on a public page, and absent on a private one', async () => {
- const pubPage = await t.get(`/benchmarks/${pub.id}`);
- assert.ok(pubPage.text.includes('<details class="br-usethis-details">'), 'one details');
- assert.ok(!pubPage.text.includes('<details class="br-usethis-details" open'), 'closed by default');
- assert.ok(pubPage.text.includes('Badges and embeds'), 'the summary');
- assert.ok(pubPage.text.includes('model-best'), 'the models are inside');
- const mine = await t.get(`/benchmarks/${privatePage.id}`, { as: alice });
- assert.strictEqual(mine.status, 200, mine.text.slice(0, 200));
- assert.ok(!mine.text.includes('Badges and embeds'), 'no badge section on a private benchmark');
- assert.ok(!mine.text.includes('br-usethis'), 'no use-this markup at all');
- const badge = await t.get(`/badge/${privatePage.id}/model-best.svg`);
- assert.strictEqual(badge.status, 404, badge.text.slice(0, 200));
- assert.ok(badge.text.includes('private benchmark'), 'the badge says why');
- });
-
- 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 <script>'), '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('the showcase page names itself: its canonical URL and its navbar link', async () => {
- const res = await t.get('/showcase');
- assert.strictEqual(res.status, 200, res.text.slice(0, 300));
- assert.match(res.text, /rel="canonical" href="https:\/\/bench\.openvibe\.services\/showcase"/, 'canonical is the showcase, not the home');
- const nav = JSON.parse(res.text.match(/window\.__OV_PAGE = (\{.*?\});/)[1]);
- const active = nav.navbar.links.filter((l) => l.active).map((l) => l.label);
- assert.deepStrictEqual(active, ['Showcase'], 'only Showcase is current');
- });
-
- await check('stop', async () => { await t.close(); });
- done();
-})();
@@ -45,60 +45,6 @@ function fakeCodes() {
@@ -45,60 +45,6 @@ function fakeCodes() {
assert.strictEqual(created.status, 201, created.text);
assert.strictEqual(created.status, 201, created.text);
const id = created.json().benchmark.id;
const id = created.json().benchmark.id;
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}/versions`, { bearer: publisher, json: {} })).status, 201);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}/versions`, { bearer: publisher, json: {} })).status, 201);
- const form = (fields) => new URLSearchParams(fields).toString();
- const post = (fields, o = {}) => t.get(`/benchmarks/${id}/run`, { as: alice, raw: form(fields), headers: { 'content-type': 'application/x-www-form-urlencoded' }, ...o });
- const runCalls = () => codes.calls.filter((c) => c.method === 'POST');
-
- await check('the benchmark page links to its run form', async () => {
- const res = await t.get(`/benchmarks/${id}`);
- assert.ok(res.text.includes(`href="/benchmarks/${id}/run">Run it yourself`), 'the button opens the form, not a bare Codes link');
- });
-
- await check('signed out, the form asks you to sign in', async () => {
- const res = await t.get(`/benchmarks/${id}/run`);
- assert.strictEqual(res.status, 200, res.text.slice(0, 200));
- assert.ok(res.text.includes('Sign in first'));
- assert.ok(!res.text.includes('<form class="br-form"'), 'no form for a stranger');
- });
-
- await check('signed in, the form lists the benchmark matrix and Codes harnesses', async () => {
- const res = await t.get(`/benchmarks/${id}/run`, { as: alice });
- assert.ok(res.text.includes('<form class="br-form"'));
- assert.ok(res.text.includes('value="model-a" checked'), 'the matrix model is picked');
- assert.ok(res.text.includes('value="claude-code"') && res.text.includes('Codex'), "Codes' harness list");
- });
-
- await check('an estimate above $1 asks once more and reaches nobody', async () => {
- const before = runCalls().length;
- const res = await post([['models', 'model-a'], ['more_models', 'model-b'], ['harnesses', 'codex'], ['repeat', '1']]);
- assert.strictEqual(res.status, 200, res.text.slice(0, 200));
- assert.ok(res.text.includes('may cost up to') && res.text.includes('$2.00'), '2 tasks × 2 models × $0.50');
- assert.ok(res.text.includes('name="confirm" value="1"') && res.text.includes('Confirm and start'));
- assert.strictEqual(runCalls().length, before, 'Codes was not called');
- });
-
- await check('a confirmed run reaches Codes once, as the person, and lands on their tasks', async () => {
- const before = runCalls().length;
- const res = await post([['models', 'model-a'], ['more_models', 'model-b'], ['harnesses', 'codex'], ['repeat', '1'], ['confirm', '1']]);
- assert.strictEqual(res.status, 303, res.text.slice(0, 200));
- assert.strictEqual(res.headers.get('location'), `${codes.url}/tasks`);
- const calls = runCalls().slice(before);
- assert.strictEqual(calls.length, 1);
- assert.deepStrictEqual(calls[0].body.models, ['model-a', 'model-b']);
- assert.match(String(calls[0].headers.authorization), /^Bearer /, "Bench's own service token");
- assert.ok(calls[0].headers['x-ov-subject'], 'names the person');
- });
-
- await check('nothing picked is said plainly', async () => {
- const res = await post([['repeat', '1']]);
- assert.strictEqual(res.status, 400);
- assert.ok(res.text.includes('non-empty lists'));
- });
-
- await check('a cross-site form post is refused', async () => {
- const res = await post([['models', 'model-a'], ['harnesses', 'codex'], ['repeat', '1']], { origin: 'https://evil.example' });
- assert.strictEqual(res.status, 403);
- });
await check('the API answers the same way: 409 with the estimate, then 202', async () => {
await check('the API answers the same way: 409 with the estimate, then 202', async () => {
const body = { models: ['model-a', 'model-b'], harnesses: ['codex'], repeat: 1 };
const body = { models: ['model-a', 'model-b'], harnesses: ['codex'], repeat: 1 };
@@ -120,13 +66,9 @@ function fakeCodes() {
@@ -120,13 +66,9 @@ function fakeCodes() {
assert.strictEqual(res2.status, 201, res2.text);
assert.strictEqual(res2.status, 201, res2.text);
const nid = res2.json().benchmark.id;
const nid = res2.json().benchmark.id;
assert.strictEqual((await t.get(`/api/v1/benchmarks/${nid}/versions`, { bearer: publisher, json: {} })).status, 201);
assert.strictEqual((await t.get(`/api/v1/benchmarks/${nid}/versions`, { bearer: publisher, json: {} })).status, 201);
- const before = runCalls().length;
const ask = await t.get(`/api/v1/benchmarks/${nid}/run`, { as: alice, json: { models: ['m'], harnesses: ['codex'], repeat: 1 } });
const ask = await t.get(`/api/v1/benchmarks/${nid}/run`, { as: alice, json: { models: ['m'], harnesses: ['codex'], repeat: 1 } });
assert.strictEqual(ask.status, 409, ask.text);
assert.strictEqual(ask.status, 409, ask.text);
assert.strictEqual(ask.json().cost_estimate_usd, null, 'no estimate is invented');
assert.strictEqual(ask.json().cost_estimate_usd, null, 'no estimate is invented');
- const page = await t.get(`/benchmarks/${nid}/run`, { as: alice, raw: form([['more_models', 'm'], ['harnesses', 'codex'], ['repeat', '1']]), headers: { 'content-type': 'application/x-www-form-urlencoded' } });
- assert.ok(page.text.includes('sets no dollar limit per task'), 'the form says why it asks');
- assert.strictEqual(runCalls().length, before, 'and nothing reached Codes');
});
});
await check('stop', async () => { await t.close(); codes.close(); });
await check('stop', async () => { await t.close(); codes.close(); });
@@ -180,19 +180,12 @@ const HOSTILE = [
@@ -180,19 +180,12 @@ const HOSTILE = [
assert.ok(pair.slots[0].wrapper_url.startsWith(renderLib.RENDER_ORIGIN + '/render/w/'), 'the wrapper is on the render origin: ' + pair.slots[0].wrapper_url);
assert.ok(pair.slots[0].wrapper_url.startsWith(renderLib.RENDER_ORIGIN + '/render/w/'), 'the wrapper is on the render origin: ' + pair.slots[0].wrapper_url);
});
});
- await check('the compare page is blind, and the vote reveals', async () => {
+ await check('a round is blind, and the vote reveals', async () => {
const before = await t.get('/api/v1/showcase/rounds/' + roundId, { as: bob });
const before = await t.get('/api/v1/showcase/rounds/' + roundId, { as: bob });
assert.strictEqual(before.status, 200, before.text);
assert.strictEqual(before.status, 200, before.text);
assert.strictEqual(before.json().round.revealed, false);
assert.strictEqual(before.json().round.revealed, false);
assert.strictEqual(before.json().round.slots[0].model, undefined);
assert.strictEqual(before.json().round.slots[0].model, undefined);
- const page = await t.get('/showcase/round/' + roundId, { as: bob });
- assert.strictEqual(page.status, 200, page.text);
- assert.ok(page.text.includes('data-slot="A"'), 'pane A');
- assert.ok(page.text.includes('data-slot="B"'), 'pane B');
- assert.ok(page.text.includes('data-vote-form="1"'), 'the vote form');
- assert.ok(!page.text.includes('alpha-1'), 'no model name before the vote');
-
const vote = await t.get('/api/v1/showcase/rounds/' + roundId + '/vote', { as: bob, json: { outcome: 'A' } });
const vote = await t.get('/api/v1/showcase/rounds/' + roundId + '/vote', { as: bob, json: { outcome: 'A' } });
assert.strictEqual(vote.status, 201, vote.text);
assert.strictEqual(vote.status, 201, vote.text);
assert.strictEqual(vote.json().revealed, true);
assert.strictEqual(vote.json().revealed, true);
deleted file mode 100644
deleted file mode 100644
@@ -1,298 +0,0 @@
@@ -1,298 +0,0 @@
-'use strict';
-
-/**
- * B11: a person's own prompts as a private benchmark.
- *
- * Under test: the paste parser (lines, CSV with an optional expected answer, 5–50 bounds); a private benchmark by
- * default; another person gets a 404 and nothing private is listed, in the sitemap or in the open data; the owner's
- * leaderboard sits beside the public board for the same kind (bands and cost per solved task, never mixed); the run
- * goes through the existing launcher and still asks before it starts; an unlisted share link with a long random
- * token, its "shared by its owner" page, noindex, and unlisting/unsharing/public/private; CSRF.
- */
-const assert = require('assert');
-const http = require('http');
-const { boot, check, done } = require('./helpers/boot');
-const { parsePrompts, MAX_PROMPTS } = require('../server/bench/your-prompts');
-const { shareTokenMatches } = require('../server/bench/registry');
-
-function fakeCodes() {
- const calls = [];
- const server = http.createServer((req, res) => {
- let body = '';
- req.on('data', (c) => { body += c; });
- req.on('end', () => {
- calls.push({ method: req.method, url: req.url, headers: req.headers, body: body ? JSON.parse(body) : null });
- res.setHeader('content-type', 'application/json');
- if (req.url === '/api/v1/harnesses') return res.end(JSON.stringify({ harnesses: [{ id: 'codex', label: 'Codex' }, { id: 'claude-code', label: 'Claude Code' }] }));
- if (/^\/api\/v1\/benchmarks\/[^/]+\/run$/.test(req.url) && req.method === 'POST') {
- res.statusCode = 202;
- return res.end(JSON.stringify({ session: 'ses_b11', tasks: [{ id: 'tsk_b11' }], task: { id: 'tsk_b11' }, total: 1 }));
- }
- res.statusCode = 404; res.end('{}');
- });
- });
- return new Promise((resolve) => server.listen(0, '127.0.0.1', () => resolve({ url: `http://127.0.0.1:${server.address().port}`, calls, close: () => server.close() })));
-}
-
-(async () => {
- const codes = await fakeCodes();
- const t = await boot({ env: { CODES_BENCH_RUN_URL: codes.url } });
- 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 base = String(t.config.baseUrl).replace(/\/+$/, '');
- const MARK = 'ZEBRA-B11-PROMPT';
-
- const form = (o) => new URLSearchParams(o).toString();
- const post = (path, o, opt = {}) => t.get(path, { as: alice, raw: form(o), headers: { 'content-type': 'application/x-www-form-urlencoded' }, ...opt });
- const share = (id, visibility) => post(`/benchmarks/${id}/share`, { visibility });
- const tokenOf = (res) => {
- const m = /\/b\/([A-Za-z0-9_-]{20,120})/.exec(res.text);
- assert.ok(m, `a share token in the page: ${res.text.slice(0, 300)}`);
- return m[1];
- };
- const publish = async (ref, model, scores, { cost = 0.03 } = {}) => {
- const tasks = scores.map((score, i) => ({ task_id: `t${i + 1}`, score, cost_usd: cost / scores.length, ms: 1000 + i, tokens: 100, artifacts: [] }));
- const body = {
- benchmark: ref, model, route: { provider: 'acme' }, provider: 'acme',
- harness: { name: 'codex', version: '1.0' }, settings: {}, executor: { kind: 'codes', id: 'tsk_b11' },
- tasks, score: scores.reduce((a, b) => a + b, 0) / scores.length, cost_usd: cost, ms: 2000, tokens: 600,
- };
- const res = await t.get('/api/v1/runs', { bearer: publisher, json: body });
- assert.strictEqual(res.status, 201, res.text);
- return res.json().run.id;
- };
-
- let id;
- let version;
- let token;
- const prompts = Array.from({ length: 6 }, (_, i) => `${MARK} number ${i + 1}`);
-
- await check('the parser takes one prompt per line and enforces 5–50', async () => {
- const good = parsePrompts(prompts.join('\n'), { format: 'lines' });
- assert.strictEqual(good.ok, true, good.detail);
- assert.strictEqual(good.prompts.length, 6);
- assert.ok(good.prompts.every((p) => p.expected === null));
- assert.strictEqual(parsePrompts(prompts.slice(0, 4).join('\n')).ok, false, 'four is too few');
- assert.strictEqual(parsePrompts(Array.from({ length: MAX_PROMPTS + 1 }, (_, i) => `p${i}`).join('\n')).ok, false, 'fifty-one is too many');
- });
-
- await check('the parser reads CSV with an optional expected answer and quoted commas', async () => {
- const csv = 'prompt,expected\n"What is 2 + 2, exactly?","4"\n"Say hello, in French.",bonjour\nName one primary colour.,red\nWhat is 3 × 3?,9\nFinish the word: sun—,flower\n';
- const parsed = parsePrompts(csv);
- assert.strictEqual(parsed.ok, true, parsed.detail);
- assert.strictEqual(parsed.format, 'csv', 'a prompt/expected header is detected');
- assert.strictEqual(parsed.prompts.length, 5);
- assert.strictEqual(parsed.prompts[0].prompt, 'What is 2 + 2, exactly?');
- assert.strictEqual(parsed.prompts[0].expected, '4');
- assert.strictEqual(parsed.prompts[1].prompt, 'Say hello, in French.');
- assert.strictEqual(parsed.prompts[1].expected, 'bonjour');
- const noHeader = parsePrompts('one,two\nthree,four\nfive,six\nseven,eight\nnine,ten', { format: 'csv' });
- assert.strictEqual(noHeader.ok, true, noHeader.detail);
- assert.deepStrictEqual(noHeader.prompts[0], { prompt: 'one', expected: 'two' });
- });
-
- await check('a share token is stored as a hash and checked in constant time', async () => {
- assert.strictEqual(shareTokenMatches('abc', 'a'.repeat(64)), false);
- assert.strictEqual(shareTokenMatches('abc', null), false);
- });
-
- await check('the form makes a private benchmark from pasted prompts', async () => {
- const res = await post('/benchmarks/new', {
- title: 'My private prompts', description: 'Six real prompts.',
- kind: 'text.answer', format: 'lines', prompts: prompts.join('\n'),
- });
- assert.strictEqual(res.status, 303, res.text.slice(0, 400));
- id = res.headers.get('location').split('/').pop();
- const r = await t.get(`/api/v1/benchmarks/${id}`, { as: alice });
- assert.strictEqual(r.status, 200, r.text);
- assert.strictEqual(r.json().benchmark.visibility, 'private', 'private by default');
- assert.strictEqual(r.json().benchmark.title, 'My private prompts');
- assert.strictEqual(r.json().benchmark.latest_version, 1, 'frozen into a version');
- version = r.json().version.version;
- assert.strictEqual(r.json().definition.kind, 'text.answer');
- assert.strictEqual(r.json().definition.tasks.length, 6);
- assert.deepStrictEqual(r.json().definition.tasks.map((x) => x.id), ['t1', 't2', 't3', 't4', 't5', 't6']);
- assert.ok(r.json().definition.tasks.every((x) => x.prompt.startsWith(MARK)));
- assert.strictEqual(r.json().definition.tasks[0].expected, null);
- assert.strictEqual(r.json().definition.limits.usd, 0, 'no dollar cap is added or raised');
- assert.ok(!t.logs().includes(MARK), 'a prompt never reaches the log');
- });
-
- await check('a CSV paste keeps the expected answer of each row', async () => {
- const csv = 'prompt,expected\nQ1,A1\nQ2,A2\nQ3,A3\nQ4,A4\nQ5,A5\n';
- const res = await post('/benchmarks/new', { title: 'CSV prompts', kind: 'design.svg', format: 'csv', prompts: csv });
- assert.strictEqual(res.status, 303, res.text.slice(0, 400));
- const cid = res.headers.get('location').split('/').pop();
- const r = await t.get(`/api/v1/benchmarks/${cid}`, { as: alice });
- assert.strictEqual(r.status, 200, r.text);
- assert.strictEqual(r.json().definition.kind, 'design.svg', 'an existing kind can be chosen');
- assert.deepStrictEqual(r.json().definition.tasks[0].expected, { answer: 'A1' });
- assert.strictEqual(r.json().definition.tasks.length, 5);
- });
-
- await check('another person gets a 404 for the private benchmark everywhere', async () => {
- for (const [path, what] of [
- [`/benchmarks/${id}`, 'page'],
- [`/benchmarks/${id}/run`, 'run form'],
- [`/api/v1/benchmarks/${id}`, 'definition'],
- [`/api/v1/rankings?benchmark=${id}`, 'rankings'],
- [`/api/v1/percentile?benchmark=${id}&model=a&score=90`, 'percentile'],
- ]) {
- const res = await t.get(path, { as: bob });
- assert.strictEqual(res.status, 404, `${what}: ${res.status} ${res.text.slice(0, 200)}`);
- }
- const run = await post(`/benchmarks/${id}/run`, { models: 'a', harnesses: 'codex', repeat: '1' }, { as: bob });
- assert.strictEqual(run.status, 404, run.text.slice(0, 200));
- });
-
- await check('a private benchmark is never in a list, the sitemap or the open data', async () => {
- const list = await t.get('/api/v1/benchmarks');
- assert.ok(!list.json().benchmarks.some((b) => b.id === id), 'not in the public list');
- const page = await t.get('/benchmarks');
- assert.ok(!page.text.includes('My private prompts'), 'not on the benchmarks page');
- const sitemap = await t.get('/sitemap.xml');
- assert.ok(!sitemap.text.includes(id), 'not in the sitemap');
- const dataset = await t.get('/api/v1/datasets/benchmarks.jsonl');
- assert.ok(!dataset.text.includes(MARK), 'no prompt in the open data');
- const mine = await t.get('/api/v1/benchmarks?mine=1', { as: alice });
- assert.ok(mine.json().benchmarks.some((b) => b.id === id), 'the owner lists it');
- assert.strictEqual(mine.headers.get('cache-control'), 'private, no-store', 'the owner list is not publicly cacheable');
- const theirs = await t.get('/api/v1/benchmarks?mine=1', { as: bob });
- assert.ok(!theirs.json().benchmarks.some((b) => b.id === id), 'nobody else does');
- });
-
- await check('the owner sees the private page and its prompts', async () => {
- const res = await t.get(`/benchmarks/${id}`, { as: alice });
- assert.strictEqual(res.status, 200, res.text.slice(0, 200));
- assert.ok(res.text.includes('Private.'), 'the private notice');
- assert.ok(res.text.includes(prompts[0]), 'the owner sees the prompt');
- assert.ok(res.text.includes('noindex'), 'kept out of search');
- assert.ok(res.text.includes('Share an unlisted link'), 'the owner can share');
- assert.strictEqual(res.headers.get('cache-control'), 'private, no-store');
- });
-
- await check('runs published for the owner make the two separate leaderboards', async () => {
- // A public benchmark of the same kind, with a run, is "the public one beside it".
- const pub = await t.get('/api/v1/benchmarks', { bearer: publisher, json: {
- title: 'Public text answers', description: 'The public board.', kind: 'text.answer', tags: [], visibility: 'public',
- tasks: [{ id: 't1', prompt: 'A public question.', files: [], expected: null, holdout: false }],
- judges: [{ type: 'automatic_grader', version: '1.0', config: {} }], scoring: { method: 'mean', scale: { min: 0, max: 100 } },
- limits: { ms: 60000, tokens: 0, usd: 0 }, matrix: { models: [], harnesses: [] },
- } });
- assert.strictEqual(pub.status, 201, pub.text);
- const pubId = pub.json().benchmark.id;
- assert.strictEqual((await t.get(`/api/v1/benchmarks/${pubId}/versions`, { bearer: publisher, json: {} })).status, 201);
- await publish(`${pubId}@1`, 'public-model', [90], { cost: 0.01 });
-
- await publish(`${id}@${version}`, 'model-a', [80], { cost: 0.03 });
- await publish(`${id}@${version}`, 'model-a', [95], { cost: 0.03 });
- await publish(`${id}@${version}`, 'model-b', [85], { cost: 0.05 });
- await publish(`${id}@${version}`, 'model-b', [96], { cost: 0.05 });
- await publish(`${id}@${version}`, 'model-c', [50], { cost: 0.01 });
- await publish(`${id}@${version}`, 'model-c', [55], { cost: 0.01 });
-
- const res = await t.get(`/benchmarks/${id}`, { as: alice });
- assert.strictEqual(res.status, 200, res.text.slice(0, 200));
- assert.ok(res.text.includes('Your prompts, 6 runs'), 'the owner leaderboard label');
- assert.ok(res.text.includes('The public board for this kind'), 'the public board is beside it');
- assert.ok(res.text.includes('Cost / solved task'), 'cost per solved task');
- assert.ok(/1st.{0,2}2nd/.test(res.text), 'overlapping intervals share a band');
- assert.ok(res.text.includes('model-a') && res.text.includes('model-b') && res.text.includes('model-c'), 'the own models');
- assert.ok(res.text.includes('public-model'), 'the public model');
- const start = res.text.indexOf('The public board for this kind');
- const end = res.text.indexOf('id="br-tasks-h"', start);
- const publicPart = res.text.slice(start, end);
- assert.ok(!publicPart.includes('model-a') && !publicPart.includes('model-b'), 'the private ranks are not mixed into the public board');
- });
-
- await check('the run goes through the launcher and still asks before it starts', async () => {
- const before = codes.calls.filter((c) => c.method === 'POST').length;
- const tooFew = await post(`/benchmarks/${id}/run`, { models: 'model-a', harnesses: 'codex', repeat: '1' });
- assert.strictEqual(tooFew.status, 400, tooFew.text.slice(0, 200));
- assert.ok(tooFew.text.includes('3 to 6 models'), '3 to 6 models');
- assert.strictEqual(codes.calls.filter((c) => c.method === 'POST').length, before, 'nobody called Codes for one model');
- const ask = await post(`/benchmarks/${id}/run`, { models: 'model-a', more_models: 'model-b, model-c', harnesses: 'codex', repeat: '1' });
- assert.strictEqual(ask.status, 200, ask.text.slice(0, 200));
- assert.ok(ask.text.includes('sets no dollar limit'), 'the no-limit confirmation');
- assert.strictEqual(codes.calls.filter((c) => c.method === 'POST').length, before, 'Codes was not called yet');
- const go = await post(`/benchmarks/${id}/run`, { models: 'model-a', more_models: 'model-b, model-c', harnesses: 'codex', repeat: '1', confirm: '1' });
- assert.strictEqual(go.status, 303, go.text.slice(0, 200));
- const calls = codes.calls.filter((c) => c.method === 'POST').slice(before);
- assert.strictEqual(calls.length, 1);
- assert.deepStrictEqual(calls[0].body.models, ['model-a', 'model-b', 'model-c']);
- assert.ok(calls[0].headers['x-ov-subject'], 'as the person');
- assert.match(String(calls[0].headers.authorization), /^Bearer /, 'with Bench’s service token');
- const api = await t.get(`/api/v1/benchmarks/${id}/run`, { as: alice, json: { models: ['model-a', 'model-b', 'model-c'], harnesses: ['codex'], repeat: 1 } });
- assert.strictEqual(api.status, 409, api.text);
- assert.strictEqual(api.json().cost_estimate_usd, null, 'no estimate is invented');
- });
-
- await check('an unlisted share link opens the page once, with a shared note and noindex', async () => {
- const res = await share(id, 'unlisted');
- assert.strictEqual(res.status, 200, res.text.slice(0, 300));
- token = tokenOf(res);
- assert.ok(token.length >= 40, 'a long random token');
- const page = await t.get(`/b/${token}`);
- assert.strictEqual(page.status, 200, page.text.slice(0, 300));
- assert.ok(page.text.includes('Shared by its owner'), 'the shared note');
- assert.ok(page.text.includes('noindex'), 'noindex');
- assert.ok(page.text.includes(MARK), 'the prompts are readable by the link');
- assert.ok(!page.text.includes('Share an unlisted link'), 'a visitor cannot re-share');
- assert.strictEqual((await t.get(`/benchmarks/${id}`)).status, 404, 'the id alone no longer opens it');
- assert.strictEqual((await t.get(`/api/v1/benchmarks/${id}`)).status, 404, 'nor its API definition');
- assert.strictEqual((await t.get('/b/not-a-real-token')).status, 404);
- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: bob })).status, 404);
- });
-
- await check('making it private again stops the link', async () => {
- const res = await share(id, 'private');
- assert.strictEqual(res.status, 200, res.text.slice(0, 200));
- assert.ok(res.text.includes('Private.'), 'private again');
- assert.strictEqual((await t.get(`/b/${token}`)).status, 404, 'the old link is dead');
- assert.strictEqual((await t.get(`/benchmarks/${id}`, { as: alice })).status, 200);
- });
-
- await check('sharing unlisted again makes a new link and invalidates the old one, then public lists it', async () => {
- const res = await share(id, 'unlisted');
- const second = tokenOf(res);
- assert.notStrictEqual(second, token, 'a new share is a new token');
- assert.strictEqual((await t.get(`/b/${token}`)).status, 404, 'the first link is dead');
- assert.strictEqual((await t.get(`/b/${second}`)).status, 200);
- const pub = await share(id, 'public');
- assert.strictEqual(pub.status, 200, pub.text.slice(0, 200));
- assert.strictEqual((await t.get(`/b/${second}`)).status, 404, 'the token is cleared when it becomes public');
- assert.strictEqual((await t.get(`/benchmarks/${id}`)).status, 200, 'public by id');
- const list = await t.get('/api/v1/benchmarks');
- assert.ok(list.json().benchmarks.some((b) => b.id === id), 'in the public list');
- const sitemap = await t.get('/sitemap.xml');
- assert.ok(sitemap.text.includes(id), 'in the sitemap');
- const dataset = await t.get('/api/v1/datasets/benchmarks.jsonl');
- assert.ok(dataset.text.includes(MARK), 'its prompts are in the open data');
- assert.ok(dataset.text.includes('benchmark_run'), 'with its runs');
- assert.ok(!dataset.text.includes(alice.subject), 'no person id in the open data');
- assert.ok(!dataset.text.includes('created_by'), 'no created_by field in the open data');
- });
-
- await check('making it private again takes it out of every public surface', async () => {
- await share(id, 'private');
- const list = await t.get('/api/v1/benchmarks');
- assert.ok(!list.json().benchmarks.some((b) => b.id === id));
- const dataset = await t.get('/api/v1/datasets/benchmarks.jsonl');
- assert.ok(!dataset.text.includes(MARK), 'the prompts leave the open data');
- const sitemap = await t.get('/sitemap.xml');
- assert.ok(!sitemap.text.includes(id));
- });
-
- await check('every form post is same-origin', async () => {
- const make = await post('/benchmarks/new', { kind: 'text.answer', format: 'lines', prompts: prompts.join('\n') }, { origin: 'https://evil.example' });
- assert.strictEqual(make.status, 403, make.text.slice(0, 200));
- const shared = await post(`/benchmarks/${id}/share`, { visibility: 'public' }, { origin: 'https://evil.example' });
- assert.strictEqual(shared.status, 403, shared.text.slice(0, 200));
- });
-
- await check('stop', async () => { await t.close(); codes.close(); });
-
- done();
-})();
-