diff --git a/apps/presentation/dashboard/smoke/benchmark-study-contract-smoke.ts b/apps/presentation/dashboard/smoke/benchmark-study-contract-smoke.ts index d33197f68a..b34dd59e3f 100644 --- a/apps/presentation/dashboard/smoke/benchmark-study-contract-smoke.ts +++ b/apps/presentation/dashboard/smoke/benchmark-study-contract-smoke.ts @@ -2,9 +2,19 @@ import fixture from "../public/benchmark-study.example.json" with { type: "json" import { parseBenchmarkStudyDashboard, resolveBenchmarkStudyDashboardUrl, + selectBenchmarkStudyRun, } from "../src/data/benchmark-study.js"; const packet = parseBenchmarkStudyDashboard(fixture); +const first = packet.runs[0]!; +const second = { ...first, case_id: "another-case" }; +if (selectBenchmarkStudyRun([first, second], first.run_id, second.case_id) !== second) throw new Error("run selection lost its case"); +if (selectBenchmarkStudyRun([first, second], first.run_id, "") !== undefined) throw new Error("ambiguous legacy link selected a run"); +if (selectBenchmarkStudyRun([first], first.run_id, "") !== first) throw new Error("unique legacy link stopped resolving"); +if (selectBenchmarkStudyRun([first], first.run_id, "missing-case") !== undefined) throw new Error("stale case link selected another case"); +if (selectBenchmarkStudyRun([first], "missing-run", "") !== undefined) throw new Error("stale run link selected another run"); +if (selectBenchmarkStudyRun([first, first], first.run_id, first.case_id) !== undefined) throw new Error("duplicate full identity selected a run"); +if (selectBenchmarkStudyRun([first], "", "") !== first) throw new Error("unselected runs view lost its default"); if (packet.status !== "provisional") throw new Error("fixture must expose provisional coverage"); if (packet.campaign.intended_cell_denominator !== 8) throw new Error("campaign denominator drifted"); if (packet.campaign.selected_score_countable_cell_count !== 6) throw new Error("countable numerator drifted"); diff --git a/apps/presentation/dashboard/src/data/benchmark-study.ts b/apps/presentation/dashboard/src/data/benchmark-study.ts index b81f388393..ffda1dc700 100644 --- a/apps/presentation/dashboard/src/data/benchmark-study.ts +++ b/apps/presentation/dashboard/src/data/benchmark-study.ts @@ -196,6 +196,15 @@ export type BenchmarkStudyCase = BenchmarkStudyDashboard["cases"][number]; export type BenchmarkStudyRun = BenchmarkStudyDashboard["runs"][number]; export type BenchmarkStudyView = "campaign" | "arms" | "cases" | "runs"; +export function selectBenchmarkStudyRun( + runs: BenchmarkStudyRun[], runId: string, caseId: string, +): BenchmarkStudyRun | undefined { + if (!runId && !caseId) return runs[0]; + const matches = runs.filter((run) => run.run_id === runId && (!caseId || run.case_id === caseId)); + // Legacy runId-only bookmarks are safe only when they identify exactly one run. + return matches.length === 1 ? matches[0] : undefined; +} + export function parseBenchmarkStudyDashboard(value: unknown): BenchmarkStudyDashboard { return benchmarkStudyDashboardSchema.parse(value); } diff --git a/apps/presentation/dashboard/src/router.tsx b/apps/presentation/dashboard/src/router.tsx index cf1101ee4e..f6730d4eb7 100644 --- a/apps/presentation/dashboard/src/router.tsx +++ b/apps/presentation/dashboard/src/router.tsx @@ -38,6 +38,7 @@ const benchmarkStudySearchSchema = z.object({ dashboardUrl: z.string().optional().default(""), view: z.enum(["campaign", "arms", "cases", "runs"]).optional().default("campaign"), runId: z.string().optional().default(""), + caseId: z.string().optional().default(""), }); // Bookmarks remain valid, but the retired boards no longer ship a second UI. diff --git a/apps/presentation/dashboard/src/views/benchmark-study-page.tsx b/apps/presentation/dashboard/src/views/benchmark-study-page.tsx index 9017f0f0c2..a4928bee29 100644 --- a/apps/presentation/dashboard/src/views/benchmark-study-page.tsx +++ b/apps/presentation/dashboard/src/views/benchmark-study-page.tsx @@ -13,6 +13,7 @@ import { benchmarkStudyRoute } from "../router"; import { parseBenchmarkStudyDashboard, resolveBenchmarkStudyDashboardUrl, + selectBenchmarkStudyRun, type BenchmarkStudyArm, type BenchmarkStudyCase, type BenchmarkStudyDashboard, @@ -190,7 +191,7 @@ function ArmsView({ packet }: { packet: BenchmarkStudyDashboard }) { ); } -function CasesView({ packet, primaryMetric, onOpenRun }: { packet: BenchmarkStudyDashboard; primaryMetric: string; onOpenRun: (runId: string) => void }) { +function CasesView({ packet, primaryMetric, onOpenRun }: { packet: BenchmarkStudyDashboard; primaryMetric: string; onOpenRun: (runId: string, caseId: string) => void }) { return (
@@ -208,7 +209,7 @@ function CasesView({ packet, primaryMetric, onOpenRun }: { packet: BenchmarkStud {item.arms.map((cell) => (
{cell.selected_run_id && cell.score_countable ? ( -