Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions tests/test_publication_claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ def test_public_claim_surfaces_use_the_frozen_lane_and_current_gate() -> None:


def test_employer_facing_site_links_current_evidence_and_metadata() -> None:
hero = Path("web/src/components/Hero.tsx").read_text()
results = Path("web/src/components/ResultsExplorer.tsx").read_text()
footer = Path("web/src/components/Footer.tsx").read_text()
index = Path("web/index.html").read_text()

assert "0 beat the scripted bar" in hero
assert "sota-v2-phase-one-2026-07-19" in hero
assert "Built by Ned Cutler" in footer
assert "Above scripted bar" in results
assert "modelsAboveBar" in results
assert "Holm-adjusted family test" in results
assert "sota-v2-phase-one-2026-07-19" in footer
assert "Ned Cutler" in footer
assert "REPRODUCING_SOTA_V2_RELEASE.md" in footer
assert 'href="%BASE_URL%favicon.svg"' in index
assert 'rel="canonical" href="https://nedcut.github.io/gm-bench/"' in index
Expand Down
26 changes: 26 additions & 0 deletions web/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="%BASE_URL%favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#F4F7F9" />
<meta name="theme-color" content="#EEF2F5" />
<meta
name="description"
content="GM-Bench is a deterministic, pre-registered test of long-horizon LLM planning. Eight models were evaluated under one frozen lane; none beat the strongest scripted heuristic."
content="GM-Bench: a deterministic front-office benchmark for LLM agents. Phase one — 0 models beat the strongest scripted heuristic under a frozen compute lane."
/>
<!-- Canonical metadata is not a fetched subresource, so SRI does not apply. -->
<link rel="canonical" href="https://nedcut.github.io/gm-bench/" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://nedcut.github.io/gm-bench/" />
<meta property="og:title" content="GM-Bench — Can a language model run a front office?" />
<meta property="og:title" content="GM-Bench — phase one standings" />
<meta
property="og:description"
content="Eight models were evaluated in a deterministic, pre-registered front-office benchmark. None beat the strongest scripted heuristic."
content="Phase one: 0 models beat the scripted bar under one frozen lane. Scores, tiers, and the red line."
/>
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="GM-Bench — Can a language model run a front office?" />
<meta name="twitter:title" content="GM-Bench — phase one standings" />
<meta
name="twitter:description"
content="Eight models were evaluated under one frozen lane. None beat the strongest scripted heuristic."
content="Phase one: 0 models beat the scripted bar under one frozen lane."
/>
<title>GM-Bench — Can a language model run a front office?</title>
<title>GM-Bench — phase one standings</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap"
href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@600;700&family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400&family=Newsreader:opsz,wght@6..72,500;6..72,600&display=swap"
rel="stylesheet"
/>
</head>
Expand Down
7 changes: 6 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"validate:data": "bun scripts/validate_results_data.ts",
"build": "bun run validate:data && tsc -b && vite build",
"lint": "oxlint",
"preview": "vite preview"
},
"dependencies": {
"d3-array": "^3.2.4",
"d3-scale": "^4.0.2",
"react": "^19.2.7",
"react-dom": "^19.2.7"
},
"devDependencies": {
"@types/d3-array": "^3.2.2",
"@types/d3-scale": "^4.0.9",
"@types/node": "^26.1.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
Expand Down
4 changes: 2 additions & 2 deletions web/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions web/scripts/validate_results_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import leaderboardData from "../src/data/leaderboard.json";
import { buildBenchmarkView } from "../src/benchmarkData";
import type { Leaderboard } from "../src/types";

const leaderboard = leaderboardData as Leaderboard;
const benchmark = buildBenchmarkView(leaderboard);

if (benchmark.modelCount !== leaderboard.publication.eligible_headline_models) {
throw new Error(
`Results UI has ${benchmark.modelCount} rows, but publication metadata declares ` +
`${leaderboard.publication.eligible_headline_models} eligible headline models`,
);
}

console.log(
`Validated ${benchmark.modelCount} result rows from one leaderboard source ` +
`(${benchmark.repeats} repeats; ${benchmark.modelsAboveBar} above scripted bar).`,
);
30 changes: 20 additions & 10 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
import { useState } from "react";
import snapshotData from "./data/snapshot.json";
import leaderboardData from "./data/leaderboard.json";
import type { Leaderboard as LeaderboardData, Snapshot } from "./types";
import { buildBenchmarkView } from "./benchmarkData";
import Nav from "./components/Nav";
import Hero from "./components/Hero";
import Leaderboard from "./components/Leaderboard";
import Integrity from "./components/Integrity";
import Results from "./components/Results";
import ResultsExplorer from "./components/ResultsExplorer";
import Analysis from "./components/Analysis";
import HowItWorks from "./components/HowItWorks";
import Adapters from "./components/Adapters";
import Quickstart from "./components/Quickstart";
import Footer from "./components/Footer";

const snapshot = snapshotData as Snapshot;
const leaderboard = leaderboardData as LeaderboardData;
const benchmark = buildBenchmarkView(leaderboard);

export default function App() {
const [selectedModelId, setSelectedModelId] = useState(
benchmark.models[0]?.id ?? "",
);

return (
<>
<Nav />
<main>
<Hero data={leaderboard} />
<Leaderboard data={leaderboard} />
<Integrity data={leaderboard} />
<ResultsExplorer
data={leaderboard}
benchmark={benchmark}
selectedModelId={selectedModelId}
onSelectModel={setSelectedModelId}
/>
<Analysis
benchmark={benchmark}
selectedModelId={selectedModelId}
onSelectModel={setSelectedModelId}
/>
<HowItWorks snapshot={snapshot} />
<Results snapshot={snapshot} />
<Adapters />
<Quickstart />
</main>
<Footer data={leaderboard} />
Expand Down
136 changes: 136 additions & 0 deletions web/src/benchmarkData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import type {
Leaderboard,
LeaderboardModel,
TieredLeaderboardModel,
} from "./types";

export type ResultModel = TieredLeaderboardModel & {
paired_lift: number;
ci95: [number, number];
cost_per_episode_usd: number;
};

export const MECHANICS = [
["cap_free_agency", "Cap & FA"],
["draft", "Draft"],
["information_memory", "Information"],
["lineup", "Lineup"],
["trades", "Trades"],
] as const;

export interface BenchmarkView {
models: ResultModel[];
modelCount: number;
modelsAboveBar: number;
repeats: number;
scriptedBar: number;
oracle: number;
}

function finite(value: unknown): value is number {
return typeof value === "number" && Number.isFinite(value);
}

function assertResultModel(model: TieredLeaderboardModel): asserts model is ResultModel {
if (!finite(model.mean_score)) {
throw new Error(`Leaderboard row ${model.id} is missing a finite mean_score`);
}
if (!finite(model.paired_lift)) {
throw new Error(`Leaderboard row ${model.id} is missing a finite paired_lift`);
}
if (
!Array.isArray(model.ci95) ||
model.ci95.length !== 2 ||
!finite(model.ci95[0]) ||
!finite(model.ci95[1]) ||
model.ci95[0] > model.ci95[1]
) {
throw new Error(`Leaderboard row ${model.id} has an invalid ci95`);
}
if (!finite(model.cost_per_episode_usd) || model.cost_per_episode_usd < 0) {
throw new Error(`Leaderboard row ${model.id} has an invalid cost_per_episode_usd`);
}
for (const [key] of MECHANICS) {
const outcome = model.mechanic_breakdown[key];
if (
outcome === undefined ||
!finite(outcome.accepted) ||
!finite(outcome.rejected) ||
outcome.accepted < 0 ||
outcome.rejected < 0
) {
throw new Error(`Leaderboard row ${model.id} has invalid ${key} outcomes`);
}
}
}

export function buildBenchmarkView(data: Leaderboard): BenchmarkView {
const ids = new Set<string>();
const completeModels: ResultModel[] = data.models.map((model) => {
if (ids.has(model.id)) {
throw new Error(`Leaderboard contains duplicate model id ${model.id}`);
}
ids.add(model.id);
assertResultModel(model);
return model;
});

if (data.models.length === 0 && data.publication.publishable_ranking) {
throw new Error("Publishable leaderboard has no model rows");
}

const models = [...completeModels].sort((a, b) => b.mean_score - a.mean_score);
const scriptedBar =
data.baselines.find((baseline) => baseline.agent === "pick-trader")?.mean_score ??
data.headroom.pick_trader;
if (!finite(scriptedBar) || !finite(data.headroom.oracle)) {
throw new Error("Leaderboard is missing a finite scripted bar or oracle ceiling");
}

const decisionPoints = models[0]?.decision_points ?? 0;
const denominator = data.preset.seeds.length * data.preset.decision_points_per_episode;
const repeats = denominator > 0 ? decisionPoints / denominator : 0;
if (!Number.isInteger(repeats) || repeats < 1) {
throw new Error("Leaderboard decision counts do not yield a whole repeat count");
}
for (const model of models) {
if (model.decision_points !== decisionPoints) {
throw new Error("Leaderboard rows disagree on decision_points");
}
}

return {
models,
modelCount: models.length,
modelsAboveBar: models.filter((model) => model.mean_score > scriptedBar).length,
repeats,
scriptedBar,
oracle: data.headroom.oracle,
};
}

export function shortModelName(model: string): string {
return model.split("/").pop() ?? model;
}

export function issueLabel(issue: string): string {
if (issue.includes("illegal actions")) return "Illegal actions";
if (issue.includes("fallback")) return "Adapter fallback";
if (issue.includes("failed queries")) return "Query errors";
if (issue.includes("strongest scripted baseline")) return "Below bar";
return "Protocol note";
}

export function issueLabels(model: LeaderboardModel): string[] {
return (model.sota_v2_issues ?? []).map(issueLabel);
}

export function rejectionRate(
model: LeaderboardModel,
mechanic: (typeof MECHANICS)[number][0],
): number {
const outcome = model.mechanic_breakdown[mechanic];
if (!outcome) return 0;
const total = outcome.accepted + outcome.rejected;
return total === 0 ? 0 : outcome.rejected / total;
}
Loading