diff --git a/src/app/benchmarks/[slug]/page.tsx b/src/app/benchmarks/[slug]/page.tsx index bccc9343..04f96bf5 100644 --- a/src/app/benchmarks/[slug]/page.tsx +++ b/src/app/benchmarks/[slug]/page.tsx @@ -271,46 +271,10 @@ export default async function BenchmarkPage({ const variants: Record = { [variantKey(null, null, null)]: aggregate, }; - // Single-origin surface guard. When this build pins one region - // (aggregate_filters.region) and declares no region dimension, the page - // must not ship the other regions' data in its payload either: the blob - // carries every region the worker computed, and whatever is passed to a - // client component lands in the RSC payload, readable in view-source even - // though nothing renders it. Keep the pinned region's cells, drop the - // rest. Benches that declare region tabs are untouched. - const pinnedRegion = aggregate.aggregateFilters?.region; - const singleOrigin = regionOptions.length === 0 && typeof pinnedRegion === "string"; - const samePinned = (r: string) => canonRegion(r) === canonRegion(pinnedRegion ?? ""); - const benchmark = !singleOrigin - ? aggregate - : { - ...aggregate, - extras: { - ...aggregate.extras, - regions: Object.fromEntries( - Object.entries(aggregate.extras.regions ?? {}).map(([slug, pts]) => [ - slug, - (pts ?? []).filter((pt) => samePinned(pt.region)), - ]), - ), - seriesByRegion24h: aggregate.extras.seriesByRegion24h - ? Object.fromEntries( - Object.entries(aggregate.extras.seriesByRegion24h).map(([slug, byRegion]) => [ - slug, - Object.fromEntries(Object.entries(byRegion).filter(([r]) => samePinned(r))), - ]), - ) - : undefined, - }, - cellRanks: aggregate.cellRanks - ? Object.fromEntries( - Object.entries(aggregate.cellRanks).filter(([key]) => { - const r = key.split("|").pop() ?? ""; - return r === "" || r === "all" || samePinned(r); - }), - ) - : undefined, - }; + // Region confinement for single-origin builds lives in overlayEditorial + // (src/lib/spec.ts), so `aggregate` and everything derived from it, + // including `variants`, is already scoped by the time it reaches here. + const benchmark = aggregate; const isDraft = benchmark.status === "draft"; const isAwaiting = isDraft && benchmark.editorialStatus === "live"; diff --git a/src/lib/spec.ts b/src/lib/spec.ts index d1548fbe..dfb04e7d 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -161,7 +161,63 @@ export function overlayEditorial(stored: Benchmark, spec: Spec): Benchmark { // hits overlayEditorial directly). The materialise sweep also passes // its output through renderBenchmarkText; calling it here on the // fallback path keeps the two paths consistent. - return renderBenchmarkText(overlaid); + return renderBenchmarkText(confineToPinnedRegion(overlaid, spec)); +} + +// Region keys the harness emits vs the values specs declare: the Singapore +// probe was historically labelled ap-southeast. +const REGION_KEY_ALIASES: Record = { "ap-southeast": "sgp" }; +const canonRegionKey = (r: string) => REGION_KEY_ALIASES[r] ?? r; + +/** + * Single-origin surface guard. When THIS build declares no region + * dimension but pins one region via `aggregate_filters`, the stored + * snapshot can still carry every region the worker computed: the worker + * runs from its own checkout, which may declare more regions than this + * build does. Everything left on the object reaches client components and + * therefore the RSC payload, readable in view-source even though nothing + * renders it (2026-09-08: keyed-rpc-robinhood shipped its us-east series + * and ranks inside `variants` while the page only ever showed Singapore). + * Keep the pinned region's cells, drop the rest. Benches that declare + * region tabs, or pin nothing, are returned untouched. + */ +export function confineToPinnedRegion(b: Benchmark, spec: Spec): Benchmark { + const declared = spec.dimensions?.region ?? []; + const pinned = (spec.aggregate_filters as { region?: string } | undefined)?.region; + if (declared.length > 0 || typeof pinned !== "string") return b; + const keep = (r: string) => canonRegionKey(r) === canonRegionKey(pinned); + const byRegion = (m?: Record>) => + m + ? Object.fromEntries( + Object.entries(m).map(([slug, per]) => [ + slug, + Object.fromEntries(Object.entries(per).filter(([r]) => keep(r))), + ]), + ) + : undefined; + return { + ...b, + extras: { + ...b.extras, + regions: Object.fromEntries( + Object.entries(b.extras.regions ?? {}).map(([slug, pts]) => [ + slug, + (pts ?? []).filter((pt) => keep(pt.region)), + ]), + ), + seriesByRegion24h: byRegion(b.extras.seriesByRegion24h), + seriesByRegion7d: byRegion(b.extras.seriesByRegion7d), + seriesByRegion30d: byRegion(b.extras.seriesByRegion30d), + }, + cellRanks: b.cellRanks + ? Object.fromEntries( + Object.entries(b.cellRanks).filter(([key]) => { + const r = key.split("|").pop() ?? ""; + return r === "" || r === "all" || keep(r); + }), + ) + : undefined, + }; } // Strip lazy-loadable series fields from the cached Benchmark to bring