Files
skills/skills/benchmark-skill/core/benchmark_core.py
alexion 97c0eeb055 feat: add all-skills leaderboard (task 0007)
Add a second pure transform to the benchmark core: a `--leaderboard` mode
that ranks per-skill results models into an index leaderboard, one row per
skill carrying both verdicts, links out to each per-skill report, and sorts
any red first (regressions above efficacy failures), then fragile-but-passing,
then clean green.

The fragile tier reuses the 0006 per-badge chips: the efficacy chip now
carries its win count so the leaderboard reads it against the pass floor
without recomputing margins, and fragility is scoped to the passing tier so a
red row never carries a chip. The runner's SKILL.md gains the no-argument
batch flow and the index invocation. The fixture test covers the tiered sort,
the not-applicable Regression cell, and the per-skill links.
2026-07-24 16:58:02 -04:00

853 lines
33 KiB
Python

#!/usr/bin/env python3
"""Deterministic scoring and rendering for a skill benchmark run.
A pure transform: given a collected run bundle (per-arm transcript usage,
hard-assertion results, and the per-trial judge verdicts for each comparison) it
produces the results model and a self-contained HTML report. No LLM, no network,
no clock — the `generatedAt` stamp is supplied by the caller so the transform
stays pure.
The model is parameterized over the arms and the comparisons, so one path serves
both the three-arm run (efficacy against no-skill and regression against the
previous version) and the degenerate two-arm run with no previous version.
When `--history` names a per-skill history file, the run's summary line is
appended to it, the file is trimmed oldest-first, and the report grows two trend
ribbons plotted from the recent tail. That file read-and-rewrite is the only side
effect, opt-in and deterministic given the same file contents and inputs.
Given `--leaderboard`, the positional arguments are read as several per-skill
results models instead, and the transform yields the index leaderboard: one row
per skill carrying both verdicts, sorted with regressions first, then efficacy
failures, then fragile-but-passing skills, then clean green.
Usage:
benchmark_core.py <bundle.json> [--json <out.json>] [--html <out.html>]
[--history <hist.jsonl>]
benchmark_core.py --leaderboard <results.json>... [--json <out.json>]
[--html <out.html>] [--generated-at <stamp>]
With no output flags it writes the model as JSON to stdout.
"""
import argparse
import html
import json
import sys
# Opus 4.8 per-million-token rates, in US dollars.
# Cache creation is the 5-minute-TTL write, priced at 1.25x input.
# Cache read is priced at 0.1x input.
# Cost is reported alongside quality but never gates a verdict.
DEFAULT_PRICING = {
"input": 5.0,
"output": 25.0,
"cacheCreation": 6.25,
"cacheRead": 0.5,
}
USAGE_COMPONENTS = ("input", "output", "cacheCreation", "cacheRead")
WIN, TIE, LOSS = "WIN", "TIE", "LOSS"
# The per-skill history is a JSON-lines tail of run summaries kept in the
# git-ignored reports directory.
# It is capped and trimmed oldest-first, and the ribbons plot only its most
# recent window.
HISTORY_CAP = 50
RIBBON_WINDOW = 7
# Efficacy passes at three wins, so this is the floor a green case sits above.
EFFICACY_WINS_FLOOR = 3
def _arm_usage_totals(bundle, arm_id):
"""Sum an arm's transcript usage across every case and trial."""
totals = {c: 0 for c in USAGE_COMPONENTS}
turns = 0
for case in bundle["cases"]:
for trial in case["trials"]:
usage = trial["usage"][arm_id]
for c in USAGE_COMPONENTS:
totals[c] += usage.get(c, 0)
turns += usage.get("turns", 0)
return totals, turns
def _arm_metrics(bundle, arm_id, pricing):
components, turns = _arm_usage_totals(bundle, arm_id)
raw_tokens = sum(components.values())
imputed_cost = sum(components[c] * pricing[c] for c in USAGE_COMPONENTS) / 1_000_000
# Cost-equivalent tokens: the dollar cost expressed in units of base input
# tokens, so a single figure captures the pricing-weighted total.
cost_equivalent_tokens = round(imputed_cost * 1_000_000 / pricing["input"])
return {
"turns": turns,
"rawTokens": raw_tokens,
"components": components,
"costEquivalentTokens": cost_equivalent_tokens,
"imputedCost": imputed_cost,
}
def _collapse_trial(verdict, comparison):
"""A trial's head-to-head becomes WIN, TIE, or LOSS for the new arm.
A tie is a non-win. Any winner that is neither the new nor the against arm
(a malformed verdict) is treated as a tie rather than crashing the run.
"""
winner = verdict.get("winner")
if winner == comparison["new"]:
return WIN
if winner == comparison["against"]:
return LOSS
return TIE
# Each comparison names a pass rule keyed here.
# Efficacy hunts for a reliable win, so it needs a wins floor.
# Regression hunts for a degradation, so a tie is already a success and only losses matter.
PASS_RULES = {
"efficacy": lambda wins, losses: wins >= 3 and losses <= 1,
"regression": lambda wins, losses: losses <= 1,
}
def _comparison_by_rule(comparisons, rule):
"""The comparison carrying a given pass rule, or None if absent."""
return next((c for c in comparisons if c["rule"] == rule), None)
def _score_case(case, comparisons):
hard_failed = any(
t.get("hard", {}).get("ran") and not t["hard"].get("pass", False)
for t in case["trials"]
)
per_comparison = {}
for comp in comparisons:
strip, flagged, evidence = [], [], []
for i, trial in enumerate(case["trials"]):
outcome = _collapse_trial(trial["comparisons"][comp["id"]], comp)
strip.append(outcome)
if outcome == LOSS:
flagged.append(i)
# The losing-trial output pair lets the report surface the new
# arm's output beside the one it lost to, for a human to eyeball.
outputs = trial.get("outputs", {})
evidence.append({
"trial": i,
"new": outputs.get(comp["new"], ""),
"against": outputs.get(comp["against"], ""),
})
wins = strip.count(WIN)
ties = strip.count(TIE)
losses = strip.count(LOSS)
head_to_head = PASS_RULES[comp["rule"]](wins, losses)
# The hard gate is new-arm-only and fails efficacy outright.
# It catches the current skill emitting malformed output.
# Regression is relative to the previous version, so it is judged purely on losses.
passed = head_to_head and not (hard_failed and comp["rule"] == "efficacy")
per_comparison[comp["id"]] = {
"trials": strip,
"wins": wins,
"ties": ties,
"losses": losses,
"passed": passed,
"flaggedLosses": flagged,
"evidence": evidence,
}
efficacy_id = _comparison_by_rule(comparisons, "efficacy")["id"]
regression = _comparison_by_rule(comparisons, "regression")
return {
"name": case["name"],
"description": case.get("description", ""),
"softCriteria": case.get("softCriteria", []),
"hardFailed": hard_failed,
"comparisons": per_comparison,
"efficacyPassed": per_comparison[efficacy_id]["passed"],
"regressionPassed": (
per_comparison[regression["id"]]["passed"] if regression else None
),
}
def _net_margin(cases, comparison_id):
"""Wins minus losses for a comparison, summed across every case."""
total = 0
for case in cases:
comp = case["comparisons"][comparison_id]
total += comp["wins"] - comp["losses"]
return total
def _flag_fragility(cases, efficacy_id, regression, efficacy_green, regression_green):
"""Chip the most fragile passing case on each green axis.
A green run still names its weakest case so a barely-green skill cannot pass
for robust. The efficacy chip lands on the single case closest to dropping
under the three-win floor, ties broken by authored order. The regression chip
lands on every case sitting at its one tolerated loss, so a case fragile on
both axes carries both.
"""
for case in cases:
case["chips"] = []
if efficacy_green and cases:
narrowest = min(cases, key=lambda c: c["comparisons"][efficacy_id]["wins"])
wins = narrowest["comparisons"][efficacy_id]["wins"]
narrowest["chips"].append({
"axis": "efficacy",
"wins": wins,
"label": f"narrowest efficacy margin · {wins}W",
})
if regression is not None and regression_green:
for case in cases:
if case["comparisons"][regression["id"]]["losses"] == 1:
case["chips"].append({
"axis": "regression",
"label": "one loss from regressing",
})
def build_results(bundle):
"""Turn a run bundle into the results model."""
pricing = DEFAULT_PRICING
comparisons = bundle["comparisons"]
arms = bundle["arms"]
regression = _comparison_by_rule(comparisons, "regression")
arm_metrics = {a["id"]: _arm_metrics(bundle, a["id"], pricing) for a in arms}
cases = [_score_case(c, comparisons) for c in bundle["cases"]]
efficacy_id = _comparison_by_rule(comparisons, "efficacy")["id"]
efficacy_green = all(c["efficacyPassed"] for c in cases)
# No previous-version arm means the regression axis has nothing to measure,
# so it reads not-applicable rather than green or red.
if regression is None:
regression_verdict = "not-applicable"
regression_net = None
else:
regression_verdict = (
"green" if all(c["regressionPassed"] for c in cases) else "red"
)
regression_net = _net_margin(cases, regression["id"])
_flag_fragility(
cases, efficacy_id, regression, efficacy_green, regression_verdict == "green"
)
return {
"skill": bundle["skill"],
"generatedAt": bundle.get("generatedAt", ""),
"temperature": bundle.get("temperature"),
"trialsPerCase": bundle.get("trialsPerCase"),
"armShape": "three-arm" if regression is not None else "two-arm",
"arms": arms,
"comparisons": comparisons,
"efficacyVerdict": "green" if efficacy_green else "red",
"regressionVerdict": regression_verdict,
"efficacyNetMargin": _net_margin(cases, efficacy_id),
"regressionNetMargin": regression_net,
"armMetrics": arm_metrics,
"cases": cases,
}
# --- History and trend --------------------------------------------------------
def summary_line(results):
"""The one-line run summary appended to the per-skill history.
It records each axis's net margin and pass/fail plus the arm shape — all the
ribbons need to redraw both sparklines and show a gap for any two-arm run. A
two-arm run has no regression measurement, so both its regression fields are
null.
"""
three_arm = results["armShape"] == "three-arm"
return {
"generatedAt": results.get("generatedAt", ""),
"armShape": results["armShape"],
"efficacyNet": results["efficacyNetMargin"],
"efficacyPass": results["efficacyVerdict"] == "green",
"regressionNet": results["regressionNetMargin"],
"regressionPass": (results["regressionVerdict"] == "green") if three_arm else None,
}
def load_history(path):
"""Read the JSON-lines history, tolerating a missing or empty file."""
try:
with open(path) as f:
return [json.loads(line) for line in f if line.strip()]
except FileNotFoundError:
return []
def trim_history(lines):
"""Keep the most recent runs, dropping the oldest past the cap."""
return lines[-HISTORY_CAP:]
def write_history(path, lines):
"""Rewrite the history file with the given lines, oldest first."""
with open(path, "w") as f:
for line in lines:
f.write(json.dumps(line) + "\n")
def build_trend(series):
"""Ribbon data for both axes over the most recent window of runs.
A run with no value on an axis — a two-arm run on the regression axis — plots
no dot and breaks the line, so the gap reads as absent rather than being
interpolated across. The delta compares against the previous run that actually
carries a value on that axis, so a two-arm gap does not blank the comparison.
"""
window = series[-RIBBON_WINDOW:]
def axis(net_key, pass_key):
points = [
{
"net": run.get(net_key),
"pass": run.get(pass_key),
"current": i == len(window) - 1,
}
for i, run in enumerate(window)
]
valued = [p for p in points if p["net"] is not None]
current_net = points[-1]["net"] if points else None
delta = None
if current_net is not None and len(valued) >= 2:
delta = valued[-1]["net"] - valued[-2]["net"]
return {
"points": points,
"current": current_net,
"delta": delta,
"green": sum(1 for p in points if p["pass"] is True),
"applicable": len(valued),
}
return {
"efficacy": axis("efficacyNet", "efficacyPass"),
"regression": axis("regressionNet", "regressionPass"),
}
# --- All-skills leaderboard ---------------------------------------------------
def _fragile_axes(results):
"""The axes on which a passing skill sits one trial-flip from failing.
Reuses the per-badge fragility chips rather than recomputing margins: the
regression chip already marks a case one loss from regressing, and the
efficacy chip marks the narrowest case, fragile exactly when its win count
rests on the pass floor. A green run always chips its narrowest efficacy
case, so the floor test is what tells a barely-green skill from a roomy one.
"""
axes = set()
for case in results["cases"]:
for chip in case.get("chips", []):
if chip["axis"] == "regression":
axes.add("regression")
elif chip["axis"] == "efficacy" and chip.get("wins") == EFFICACY_WINS_FLOOR:
axes.add("efficacy")
return sorted(axes)
def _leaderboard_tier(results, fragile):
"""The sort tier, most urgent first.
A regression outranks an efficacy failure because it means a skill that was
working just broke — the sharper signal mid-edit. A passing skill still one
flip from failing sorts above a clean sweep.
"""
if results["regressionVerdict"] == "red":
return 0
if results["efficacyVerdict"] == "red":
return 1
if fragile:
return 2
return 3
def build_leaderboard(models):
"""Rank per-skill results into leaderboard rows, most urgent first.
Rows within a tier order by skill name, so the board diffs cleanly run to run.
"""
rows = []
for m in models:
axes = _fragile_axes(m)
tier = _leaderboard_tier(m, bool(axes))
rows.append({
"skill": m["skill"],
"efficacyVerdict": m["efficacyVerdict"],
"regressionVerdict": m["regressionVerdict"],
# Fragility marks a passing skill one flip from failing, so a red skill
# carries no chip even when a still-passing axis is at its edge.
"fragileAxes": axes if tier == 2 else [],
"tier": tier,
"href": f"{m['skill']}.html",
})
rows.sort(key=lambda r: (r["tier"], r["skill"]))
return rows
# --- HTML rendering -----------------------------------------------------------
_STYLE = """
:root { color-scheme: light; }
body { font: 15px/1.5 -apple-system, system-ui, sans-serif; margin: 0; padding: 2rem;
color: #1a1a1a; background: #fafafa; }
.wrap { max-width: 60rem; margin: 0 auto; }
h1 { font-size: 1.4rem; margin: 0 0 .25rem; }
.badges { display: flex; gap: .5rem; margin: .25rem 0; flex-wrap: wrap; }
.badge { display: inline-block; padding: .2rem .7rem; border-radius: 999px;
font-weight: 600; font-size: .85rem; }
.badge.green { background: #d6f5df; color: #0f6b34; }
.badge.red { background: #fbdcdc; color: #9b1c1c; }
.badge.na { background: #eee; color: #666; }
.headline { font-size: 1.02rem; font-weight: 600; margin: .8rem 0 .2rem; }
.meta { color: #666; font-size: .85rem; margin: .5rem 0 1rem; }
.ribbons { display: grid; gap: .5rem; margin: .5rem 0 1.5rem; }
.ribbon { display: grid; grid-template-columns: 6.5rem 1fr auto; align-items: center;
gap: 1rem; border: 1px solid #e5e5e5; border-radius: 8px;
padding: .5rem .9rem; background: #fff; }
.rlabel { font-weight: 600; font-size: .85rem; }
.spark { width: 100%; height: 44px; display: block; }
.spark .line { fill: none; stroke: #c4c4c4; stroke-width: 1.5; }
.spark .zero { stroke: #e5e5e5; stroke-width: 1; stroke-dasharray: 2 3; }
.spark .dot.pass { fill: #2f9e5f; }
.spark .dot.fail { fill: #c0392b; }
.spark .ring { fill: none; stroke: #1a1a1a; stroke-width: 1.5; }
.readout { font-size: .8rem; color: #555; white-space: nowrap; text-align: right; }
.readout .up { color: #0f6b34; font-weight: 600; }
.readout .down { color: #9b1c1c; font-weight: 600; }
.chip { display: inline-block; margin-left: .5rem; padding: .05rem .55rem;
border-radius: 999px; font-size: .7rem; font-weight: 600;
vertical-align: middle; }
.chip.efficacy { background: #fff1cf; color: #8a5a00; }
.chip.regression { background: #e9e2fb; color: #5b3ea8; }
table { border-collapse: collapse; width: 100%; margin: .5rem 0; font-size: .9rem; }
th, td { text-align: right; padding: .4rem .6rem; border-bottom: 1px solid #e5e5e5; }
th:first-child, td:first-child { text-align: left; }
thead th { border-bottom: 2px solid #ccc; }
.footnote { color: #777; font-size: .8rem; margin: .3rem 0 1.5rem; }
.board td, .board th { text-align: left; }
.board td.verdict, .board th.verdict { text-align: right; }
.board a { color: #1a56c4; text-decoration: none; font-weight: 600; }
.board a:hover { text-decoration: underline; }
.case { border: 1px solid #e5e5e5; border-radius: 8px; padding: 1rem 1.25rem;
margin: .75rem 0; background: #fff; }
.case.fail { border-color: #f0b6b6; }
.case .desc { color: #666; font-size: .85rem; margin: 0 0 .6rem; }
.strip { display: flex; gap: .3rem; margin: .3rem 0; flex-wrap: wrap; }
.cell { width: 2.6rem; text-align: center; padding: .2rem 0; border-radius: 4px;
font-size: .72rem; font-weight: 700; letter-spacing: .02em; }
.cell.WIN { background: #d6f5df; color: #0f6b34; }
.cell.TIE { background: #eee; color: #555; }
.cell.LOSS { background: #fbdcdc; color: #9b1c1c; }
.result { font-size: .85rem; font-weight: 600; }
.result.pass { color: #0f6b34; }
.result.fail { color: #9b1c1c; }
.tag { font-size: .75rem; color: #9b1c1c; font-weight: 600; }
.crit { color: #555; font-size: .82rem; margin: .5rem 0 0; padding-left: 1.1rem; }
.case > summary { cursor: pointer; font-weight: 600; font-size: 1rem;
list-style-position: outside; }
.case > summary::marker { color: #999; }
.cmps { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
gap: 1rem; align-items: start; margin: .5rem 0 .2rem; }
.cmp { min-width: 0; }
.evidence { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
gap: .6rem; margin: .4rem 0 0; }
.evidence figure { margin: 0; }
.evidence figcaption { font-size: .72rem; color: #666; font-weight: 600;
margin: 0 0 .2rem; }
.evidence pre { margin: 0; padding: .5rem .6rem; background: #f6f6f6;
border: 1px solid #e5e5e5; border-radius: 4px; font-size: .78rem;
white-space: pre-wrap; overflow-wrap: anywhere; }
.evidence .lead { grid-column: 1 / -1; font-size: .78rem; color: #9b1c1c;
font-weight: 600; margin: .3rem 0 0; }
"""
def _document(title, body):
"""Wrap rendered body markup in the self-contained HTML document shell."""
return (
"<!doctype html><html><head><meta charset=\"utf-8\">"
f"<title>{title}</title><style>{_STYLE}</style></head><body>"
+ body
+ "</body></html>"
)
def _fmt_int(n):
return f"{n:,}"
def _fmt_cost(d):
return f"${d:,.4f}"
def _plural(n, noun):
return f"{n} {noun}" if n == 1 else f"{n} {noun}s"
def _headline(results):
"""One 'so what' sentence reading the two verdicts together.
Every clause it states is backed by a verdict or a case count rendered on the
same screen.
"""
eff = results["efficacyVerdict"]
reg = results["regressionVerdict"]
eff_fail = sum(1 for c in results["cases"] if not c["efficacyPassed"])
reg_fail = sum(1 for c in results["cases"] if c["regressionPassed"] is False)
if reg == "not-applicable":
if eff == "green":
return "This skill earns its keep against no-skill, with no previous version to regress against."
return (f"This skill does not beat no-skill on {_plural(eff_fail, 'case')}, "
"with no previous version to regress against.")
if eff == "green" and reg == "green":
return "This skill earns its keep and this edit held quality steady."
if eff == "green" and reg == "red":
return f"Still valuable, but this edit regressed {_plural(reg_fail, 'case')}."
if eff == "red" and reg == "green":
return (f"This skill does not beat no-skill on {_plural(eff_fail, 'case')}, "
"but this edit did not make it worse.")
return (f"This skill does not beat no-skill on {_plural(eff_fail, 'case')} "
f"and this edit regressed {_plural(reg_fail, 'case')}.")
_BADGE_TEXT = {"green": "GREEN", "red": "RED", "not-applicable": "N/A"}
_BADGE_CLASS = {"green": "green", "red": "red", "not-applicable": "na"}
def _state_badge(state):
return f"<span class=\"badge {_BADGE_CLASS[state]}\">{_BADGE_TEXT[state]}</span>"
def _badge(label, state):
return f"<span class=\"badge {_BADGE_CLASS[state]}\">{label}: {_BADGE_TEXT[state]}</span>"
def _sparkline(points, width=240, height=44):
"""An inline SVG net-margin sparkline where gaps break the line and drop no dot."""
nets = [p["net"] for p in points if p["net"] is not None]
if not nets:
return f"<svg viewBox=\"0 0 {width} {height}\" class=\"spark\"></svg>"
pad_x, pad_y = 10, 9
lo, hi = min(nets), max(nets)
n = len(points)
def x(i):
return width / 2 if n == 1 else pad_x + i * (width - 2 * pad_x) / (n - 1)
def y(v):
if hi == lo:
return height / 2
return height - pad_y - (v - lo) / (hi - lo) * (height - 2 * pad_y)
parts = [
f"<svg viewBox=\"0 0 {width} {height}\" class=\"spark\" "
"preserveAspectRatio=\"xMidYMid meet\" xmlns=\"http://www.w3.org/2000/svg\">"
]
if lo < 0 < hi:
zy = y(0)
parts.append(f"<line x1=\"0\" y1=\"{zy:.1f}\" x2=\"{width}\" y2=\"{zy:.1f}\" class=\"zero\"/>")
# Split the run into contiguous valued segments so a gap leaves a real break.
segments, current = [], []
for i, p in enumerate(points):
if p["net"] is None:
if current:
segments.append(current)
current = []
else:
current.append((x(i), y(p["net"])))
if current:
segments.append(current)
for seg in segments:
if len(seg) >= 2:
coords = " ".join(f"{px:.1f},{py:.1f}" for px, py in seg)
parts.append(f"<polyline points=\"{coords}\" class=\"line\"/>")
for i, p in enumerate(points):
if p["net"] is None:
continue
cx, cy = x(i), y(p["net"])
if p["current"]:
parts.append(f"<circle cx=\"{cx:.1f}\" cy=\"{cy:.1f}\" r=\"5.5\" class=\"ring\"/>")
cls = "dot pass" if p["pass"] else "dot fail"
parts.append(f"<circle cx=\"{cx:.1f}\" cy=\"{cy:.1f}\" r=\"3\" class=\"{cls}\"/>")
parts.append("</svg>")
return "".join(parts)
def _ribbon_readout(axis):
"""The net / delta-vs-previous / green-count line beneath a ribbon."""
net = "net —" if axis["current"] is None else f"net {axis['current']:+d}"
delta = axis["delta"]
if delta is None:
change = ""
elif delta > 0:
change = f"<span class=\"up\">▲ {delta}</span> vs prev"
elif delta < 0:
change = f"<span class=\"down\">▼ {abs(delta)}</span> vs prev"
else:
change = "±0 vs prev"
if axis["applicable"]:
green = f"{axis['green']}/{axis['applicable']} green"
else:
green = "no runs yet"
return " · ".join(bit for bit in (net, change, green) if bit)
def _render_ribbons(results):
"""Two stacked net-margin ribbons, empty when no history was supplied."""
trend = results.get("trend")
if not trend:
return ""
out = ["<div class=\"ribbons\">"]
for key, label in (("efficacy", "Efficacy"), ("regression", "Regression")):
axis = trend[key]
out.append("<div class=\"ribbon\">")
out.append(f"<div class=\"rlabel\">{label}</div>")
out.append(_sparkline(axis["points"]))
out.append(f"<div class=\"readout\">{_ribbon_readout(axis)}</div>")
out.append("</div>")
out.append("</div>")
return "".join(out)
def render_html(results):
e = html.escape
arm_labels = {a["id"]: a["label"] for a in results["arms"]}
three_arm = results["armShape"] == "three-arm"
out = []
out.append("<div class=\"wrap\">")
out.append(f"<h1>Benchmark — {e(results['skill'])}</h1>")
out.append("<div class=\"badges\">")
out.append(_badge("Efficacy", results["efficacyVerdict"]))
out.append(_badge("Regression", results["regressionVerdict"]))
out.append("</div>")
meta_bits = [f"arm shape: {e(results['armShape'])}"]
if results.get("trialsPerCase") is not None:
meta_bits.append(f"{results['trialsPerCase']} trials/case")
if results.get("temperature") is not None:
meta_bits.append(f"temperature {results['temperature']}")
if results.get("generatedAt"):
meta_bits.append(e(results["generatedAt"]))
out.append(f"<div class=\"meta\">{' · '.join(meta_bits)}</div>")
out.append(_render_ribbons(results))
out.append(f"<p class=\"headline\">{e(_headline(results))}</p>")
# Per-arm cost table.
out.append("<table><thead><tr><th>Arm</th><th>Turns</th><th>Raw tokens</th>"
"<th>Cost-equiv tokens</th><th>Imputed cost</th></tr></thead><tbody>")
for arm in results["arms"]:
m = results["armMetrics"][arm["id"]]
out.append(
f"<tr><td>{e(arm['label'])}</td><td>{_fmt_int(m['turns'])}</td>"
f"<td>{_fmt_int(m['rawTokens'])}</td>"
f"<td>{_fmt_int(m['costEquivalentTokens'])}</td>"
f"<td>{_fmt_cost(m['imputedCost'])}</td></tr>"
)
out.append("</tbody></table>")
if three_arm:
footnote = ("Absolute cost is inflated by shared-context cache overhead, "
"so the trustworthy signals are two ratios: new-vs-no-skill "
"(what the skill costs over nothing) and new-vs-old (what this "
"edit added or saved), not the absolute figures.")
else:
footnote = ("Absolute cost is inflated by shared-context cache overhead, "
"so the trustworthy signal is the new-vs-no-skill ratio, not "
"the absolute figures.")
out.append(f"<p class=\"footnote\">{footnote}</p>")
# Cases in stable authored order, each a collapsible panel that auto-expands
# when either comparison fails or carries a flagged loss.
for case in results["cases"]:
comps = case["comparisons"]
expanded = case["hardFailed"] or any(
c["flaggedLosses"] or not c["passed"] for c in comps.values()
)
failed = (not case["efficacyPassed"]) or case["regressionPassed"] is False
cls = "case fail" if failed else "case"
opened = " open" if expanded else ""
chips = "".join(
f"<span class=\"chip {ch['axis']}\">{e(ch['label'])}</span>"
for ch in case.get("chips", [])
)
out.append(f"<details class=\"{cls}\"{opened}>")
out.append(f"<summary>{e(case['name'])}{chips}</summary>")
if case["description"]:
out.append(f"<p class=\"desc\">{e(case['description'])}</p>")
if case["hardFailed"]:
out.append("<p class=\"tag\">Hard assertion failed — case fails outright.</p>")
out.append("<div class=\"cmps\">")
for comp in results["comparisons"]:
c = comps[comp["id"]]
out.append("<div class=\"cmp\">")
out.append(f"<div><strong>{e(comp['label'])}</strong></div>")
out.append("<div class=\"strip\">")
for outcome in c["trials"]:
out.append(f"<span class=\"cell {outcome}\">{outcome}</span>")
out.append("</div>")
rc = "pass" if c["passed"] else "fail"
summary = (f"{c['wins']}W / {c['ties']}T / {c['losses']}L — "
f"{'PASS' if c['passed'] else 'FAIL'}")
out.append(f"<div class=\"result {rc}\">{summary}</div>")
if c["flaggedLosses"]:
trials = ", ".join(f"#{i + 1}" for i in c["flaggedLosses"])
out.append(f"<div class=\"tag\">Loss flagged for review: trial {trials}</div>")
new_label = arm_labels.get(comp["new"], comp["new"])
against_label = arm_labels.get(comp["against"], comp["against"])
for ev in c["evidence"]:
out.append("<div class=\"evidence\">")
out.append(f"<p class=\"lead\">Trial #{ev['trial'] + 1} — losing output pair</p>")
out.append(
f"<figure><figcaption>{e(new_label)}</figcaption>"
f"<pre>{e(ev['new'])}</pre></figure>"
)
out.append(
f"<figure><figcaption>{e(against_label)}</figcaption>"
f"<pre>{e(ev['against'])}</pre></figure>"
)
out.append("</div>")
out.append("</div>")
out.append("</div>")
if case["softCriteria"]:
out.append("<ul class=\"crit\">")
for crit in case["softCriteria"]:
out.append(f"<li>{e(crit)}</li>")
out.append("</ul>")
out.append("</details>")
out.append("</div>")
return _document(f"Benchmark — {e(results['skill'])}", "".join(out))
_FRAGILE_LABEL = {"efficacy": "narrow efficacy", "regression": "near regressing"}
def render_leaderboard(rows, generated_at=""):
"""The all-skills index: one linked row per skill carrying both verdicts."""
e = html.escape
out = ["<div class=\"wrap\">", "<h1>Skill benchmarks</h1>"]
meta = [e(generated_at)] if generated_at else []
meta.append(_plural(len(rows), "skill"))
out.append(f"<div class=\"meta\">{' · '.join(meta)}</div>")
out.append("<table class=\"board\"><thead><tr><th>Skill</th>"
"<th class=\"verdict\">Efficacy</th>"
"<th class=\"verdict\">Regression</th></tr></thead><tbody>")
for r in rows:
chips = "".join(
f"<span class=\"chip {axis}\">{_FRAGILE_LABEL[axis]}</span>"
for axis in r["fragileAxes"]
)
out.append(
f"<tr><td><a href=\"{e(r['href'])}\">{e(r['skill'])}</a>{chips}</td>"
f"<td class=\"verdict\">{_state_badge(r['efficacyVerdict'])}</td>"
f"<td class=\"verdict\">{_state_badge(r['regressionVerdict'])}</td></tr>"
)
out.append("</tbody></table></div>")
return _document("Skill benchmarks", "".join(out))
def _write_outputs(model, render, json_out, html_out):
"""Emit a model as JSON and/or rendered HTML, or to stdout if neither given."""
if json_out:
with open(json_out, "w") as f:
json.dump(model, f, indent=2)
if html_out:
with open(html_out, "w") as f:
f.write(render())
if not json_out and not html_out:
json.dump(model, sys.stdout, indent=2)
sys.stdout.write("\n")
def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"inputs",
nargs="+",
help="the run-bundle JSON, or the per-skill results JSONs with --leaderboard",
)
parser.add_argument("--json", dest="json_out", help="write the model here")
parser.add_argument("--html", dest="html_out", help="write the HTML here")
parser.add_argument(
"--history",
dest="history",
help="append this run to the JSON-lines history file and draw the trend ribbons",
)
parser.add_argument(
"--leaderboard",
action="store_true",
help="render the index leaderboard over the given per-skill results JSONs",
)
parser.add_argument(
"--generated-at",
dest="generated_at",
default="",
help="timestamp stamped on the leaderboard index",
)
args = parser.parse_args(argv)
if args.leaderboard:
if args.history:
parser.error("--history does not apply in leaderboard mode")
models = []
for path in args.inputs:
with open(path) as f:
models.append(json.load(f))
rows = build_leaderboard(models)
_write_outputs(
rows, lambda: render_leaderboard(rows, args.generated_at),
args.json_out, args.html_out,
)
return 0
if len(args.inputs) != 1:
parser.error("scoring a run takes exactly one bundle; use --leaderboard for many")
with open(args.inputs[0]) as f:
bundle = json.load(f)
results = build_results(bundle)
if args.history:
series = load_history(args.history) + [summary_line(results)]
write_history(args.history, trim_history(series))
results["trend"] = build_trend(series)
_write_outputs(
results, lambda: render_html(results), args.json_out, args.html_out
)
return 0
if __name__ == "__main__":
raise SystemExit(main())