Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ refs/

# Derived pyright config for the CE036 contract engine (tests/lint/pyright_config.py)
.pyright-tests.json

# Local evalboard dataset for `next dev` (EVALBOARD_LOCAL_RUNS_DIR)
evalboard/.local-runs/
139 changes: 9 additions & 130 deletions evalboard/app/_components/col-help.tsx
Original file line number Diff line number Diff line change
@@ -1,131 +1,10 @@
"use client";

import { useEffect, useState } from "react";

// Shared column-help bubble. A small ⓘ next to a column header opens a static,
// selectable popover explaining what the number is, what drives it up, and how
// to bring it down. Used by the run-page task grid (TaskGrid) and the task-page
// message timeline so the two stay consistent.
export type ColHelp = {
title: string;
body: string;
causes?: string; // common causes of high values
fix?: string; // potential fixes
};

// Token-column help shared verbatim across pages. Wording is page-neutral (no
// "this task" / "this call") so it reads correctly on both the per-task grid
// and the per-message timeline.
// Column definitions, surfaced as native `title` tooltips on table headers.
// Shared by the run-page task grid and the task-page message timeline so the two
// describe the same number the same way. Wording is page-neutral (no "this task"
// / "this call") because both surfaces read the same string.
export const TOKEN_COLUMN_HELP = {
input: {
title: "Input tokens (uncached)",
body: "Fresh prompt input billed at the full input rate — the slice that was neither written to nor read from the prompt cache (input_tokens minus cache-creation and cache-read).",
causes: "new content added to the prompt that wasn't cacheable yet — the latest user/tool message, anything past the cached prefix.",
fix: "this is usually small and unavoidable; the cache columns (cache-write / cache-read) are where prompt-input cost concentrates.",
},
output: {
title: "Output tokens",
body: "Text, code, tool arguments and reasoning the model generated.",
causes: "verbose final answers, large file rewrites, heavy reasoning.",
fix: "ask for concise output, scope edits to smaller diffs, cap max_output_tokens / max_turns.",
},
cw: {
title: "Cache-write tokens",
body: "Context newly written into the prompt cache (cache_creation_input_tokens).",
causes: "the cached prefix keeps changing — new files read mid-run, a growing transcript — so it's re-written instead of reused.",
fix: "keep stable content (system prompt, skills, instructions) at the front of the prompt; don't inject volatile content early; reuse sessions.",
},
cr: {
title: "Cache-read tokens",
body: "Cached input re-billed on every later call (cache_read_input_tokens). Usually the dominant cost line.",
causes: "large context (big files, long transcript, many skills/tools) replayed on every call.",
fix: "put less in context (smaller file reads, fewer files), shorten the run (fewer turns), trim system/skill payloads, compact long transcripts.",
},
} satisfies Record<string, ColHelp>;

export function HelpPopover({
help,
align,
}: {
help: ColHelp;
align: "left" | "right";
}) {
return (
<div
role="tooltip"
// Anchor under the ⓘ; align to the same edge as the column text so
// it stays inside the table on the right-aligned columns.
className={`absolute top-full z-20 mt-1.5 w-72 cursor-auto rounded-md border border-gray-200 bg-white p-3 text-left text-xs font-normal leading-snug text-gray-600 shadow-lg ${
align === "right" ? "right-0" : "left-0"
}`}
// Keep clicks inside the card from sorting / closing.
onClick={(e) => e.stopPropagation()}
>
<div className="font-semibold text-gray-900">{help.title}</div>
<p className="mt-1">{help.body}</p>
{help.causes && (
<p className="mt-2">
<span className="font-medium text-gray-700">
Common causes:
</span>{" "}
{help.causes}
</p>
)}
{help.fix && (
<p className="mt-1">
<span className="font-medium text-gray-700">Reduce by:</span>{" "}
{help.fix}
</p>
)}
</div>
);
}

// Self-contained ⓘ icon + popover with its own open state (click to toggle,
// click-outside / Escape to close). Drop-in for headers that aren't managed by
// a parent's shared open-state — e.g. the message-timeline header, which is a
// server component. The click-outside check keys off the `[data-col-help]`
// wrapper, so clicking a different icon closes this one (one open at a time).
export function ColHelpIcon({
help,
align = "right",
}: {
help: ColHelp;
align?: "left" | "right";
}) {
const [open, setOpen] = useState(false);
useEffect(() => {
if (!open) return;
const onDown = (e: MouseEvent) => {
const el = e.target as Element | null;
if (!el?.closest("[data-col-help]")) setOpen(false);
};
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") setOpen(false);
};
document.addEventListener("mousedown", onDown);
document.addEventListener("keydown", onKey);
return () => {
document.removeEventListener("mousedown", onDown);
document.removeEventListener("keydown", onKey);
};
}, [open]);
return (
<span data-col-help className="relative inline-flex">
<button
type="button"
aria-label={`What is ${help.title}?`}
aria-expanded={open}
onClick={() => setOpen((o) => !o)}
className={`flex h-4 w-4 items-center justify-center rounded-full border text-[10px] font-semibold leading-none transition-colors ${
open
? "border-studio-blue text-studio-blue"
: "border-gray-300 text-gray-400 hover:border-gray-400 hover:text-gray-600"
}`}
>
i
</button>
{open && <HelpPopover help={help} align={align} />}
</span>
);
}
input: "Input tokens (uncached): fresh prompt input billed at the full input rate — the slice that was neither written to nor read from the prompt cache.",
output: "Output tokens: text, code, tool arguments and reasoning the model generated.",
cw: "Cache-write tokens: context newly written into the prompt cache (cache_creation_input_tokens).",
cr: "Cache-read tokens: cached input re-billed on every later call (cache_read_input_tokens). Usually the dominant cost line.",
} satisfies Record<string, string>;
80 changes: 80 additions & 0 deletions evalboard/app/_overview/__tests__/efficiency-charts.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { describe, expect, test } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import type { RunPoint } from "@/lib/overview";
import { EfficiencyCharts } from "../efficiency-charts";

function point(overrides: Partial<RunPoint> = {}): RunPoint {
return {
runId: "2026-08-18_04-51-58",
timestamp: Date.UTC(2026, 7, 18),
harness: "codex",
successRate: 96,
turnBudgetRate: 87,
withinExpectedTimeRate: 78,
timePerPassedTask: 192,
...overrides,
};
}

function renderCharts(scoped = false) {
return render(
<EfficiencyCharts
data={[point()]}
harnesses={["codex"]}
windowStart={Date.UTC(2026, 7, 1)}
windowEnd={Date.UTC(2026, 7, 31)}
scoped={scoped}
/>,
);
}

describe("EfficiencyCharts", () => {
test("opens on the wall-clock metric", () => {
renderCharts();
expect(screen.getByRole("heading")).toHaveTextContent(
"Time per Passed Task",
);
expect(
screen.getByRole("tab", { name: "Time" }),
).toHaveAttribute("aria-selected", "true");
});

test("the Turns tab still reaches the retired turn-budget chart", () => {
// The turn budget is kept visible while the derived expected-time line
// is being watched; retiring it is deleting this tab.
renderCharts();
fireEvent.click(screen.getByRole("tab", { name: "Turns" }));
expect(screen.getByRole("heading")).toHaveTextContent(
"Within Expected Turns (%)",
);
expect(screen.getByText(/1.5× their expected turns/)).toBeInTheDocument();
expect(
screen.getByRole("tab", { name: "Turns" }),
).toHaveAttribute("aria-selected", "true");
expect(screen.getByRole("tab", { name: "Time" })).toHaveAttribute(
"aria-selected",
"false",
);
});

test("switching back restores the wall-clock blurb", () => {
renderCharts();
fireEvent.click(screen.getByRole("tab", { name: "Turns" }));
fireEvent.click(screen.getByRole("tab", { name: "Time" }));
expect(
screen.getByText(/the number that passed/),
).toBeInTheDocument();
});

test("says so when the numbers are filter-scoped", () => {
renderCharts(true);
expect(
screen.getByText(/scoped to the active filter/),
).toBeInTheDocument();
});

test("no filter note when the window is unscoped", () => {
renderCharts();
expect(screen.queryByText(/scoped to the active filter/)).toBeNull();
});
});
38 changes: 38 additions & 0 deletions evalboard/app/_overview/__tests__/harness-legend.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,44 @@ describe("HarnessLegend", () => {
describe("HarnessTooltip", () => {
const s = series("claude-code", "codex");

test("formats the value with the chart's own formatter", () => {
render(
<HarnessTooltip
active
label={1_700_000_000_000}
series={s}
suffix="per passed task"
emptyText="no passing tasks"
format={(v) => `${Math.round(v)}s`}
payload={[{ dataKey: s[1].dataKey, value: 192.3 }]}
/>,
);
expect(screen.getByText("192s per passed task")).toBeInTheDocument();
});

test("adds the secondary line for the hovered point only", () => {
// The within-expected rate belongs to one run, so it rides the hover on
// that run's point instead of being summarized over the whole chart.
render(
<HarnessTooltip
active
label={1_700_000_000_000}
series={s}
suffix="per passed task"
emptyText="no passing tasks"
secondary={(harness) =>
harness === "codex" ? "78% within 2× expected" : null
}
payload={[
{ dataKey: s[0].dataKey, value: 300 },
{ dataKey: s[1].dataKey, value: 192 },
]}
/>,
);
expect(screen.getByText("78% within 2× expected")).toBeInTheDocument();
expect(screen.queryAllByText(/within/)).toHaveLength(1);
});

test("shows only the harnesses that actually ran at the hovered x", () => {
// Recharts hands over every series, including the ones with no value
// here. Listing those would invent runs that never happened.
Expand Down
10 changes: 6 additions & 4 deletions evalboard/app/_overview/__tests__/harness-series.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ function point(
harness: string,
timestamp: number,
successRate: number | null,
turnBudgetRate: number | null = null,
withinExpectedTimeRate: number | null = null,
): RunPoint {
return {
runId: `run-${timestamp}`,
timestamp,
harness,
successRate,
turnBudgetRate,
turnBudgetRate: null,
withinExpectedTimeRate,
timePerPassedTask: null,
};
}

Expand Down Expand Up @@ -99,7 +101,7 @@ describe("pivotByHarness", () => {
timestamp: 100,
[key]: 90,
});
expect(pivotByHarness(pts, ["codex"], "turnBudgetRate").rows[0]).toEqual(
expect(pivotByHarness(pts, ["codex"], "withinExpectedTimeRate").rows[0]).toEqual(
{ timestamp: 100, [key]: 40 },
);
});
Expand All @@ -111,7 +113,7 @@ describe("pivotByHarness", () => {
const { rows } = pivotByHarness(
[point("codex", 100, 90, null)],
["codex"],
"turnBudgetRate",
"withinExpectedTimeRate",
);
expect(rows).toEqual([]);
});
Expand Down
Loading