diff --git a/src/lib/snapshot.ts b/src/lib/snapshot.ts index eff4d9992..5cba45411 100644 --- a/src/lib/snapshot.ts +++ b/src/lib/snapshot.ts @@ -46,7 +46,7 @@ const KEY_PREFIX = "ocb:snap:v1:"; * new field don't try to deserialize old-shape values. The Zod schema * below would also reject those, but the version prefix lets us * invalidate without writing strict-mode parsers. */ -const SCHEMA_VERSION = 1 as const; +const SCHEMA_VERSION = 2 as const; // Minimal runtime payload. Editorial metadata isn't snapshotted because // it lives in YAML and is rebuilt from the spec on every read. @@ -57,6 +57,8 @@ const SnapshotSchema = z.object({ sampleSize: z.number(), results: z.array(z.any()), extras: z.any(), + bestPerChain: z.record(z.string(), z.any()).optional(), + worstPerChain: z.record(z.string(), z.any()).optional(), }); export type SnapshotPayload = { @@ -64,6 +66,8 @@ export type SnapshotPayload = { extras: ResultExtras; sampleSize: number; lastRunAt: string; + bestPerChain?: Record; + worstPerChain?: Record; }; function isConfigured(): boolean { @@ -168,6 +172,12 @@ export async function readSnapshot( extras: parsed.data.extras as ResultExtras, sampleSize: parsed.data.sampleSize, lastRunAt: parsed.data.lastRunAt, + bestPerChain: parsed.data.bestPerChain as + | Record + | undefined, + worstPerChain: parsed.data.worstPerChain as + | Record + | undefined, }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); @@ -190,5 +200,13 @@ export function snapshotFromBenchmark(b: Benchmark): SnapshotPayload { extras: b.extras, sampleSize: b.sampleSize, lastRunAt: b.lastRunAt, + // Persist per-chain leader stash so the snapshot-recovery path + // (loadBenchmarkUnfilteredCached's KV fallback in spec.ts) can + // reconstruct a Benchmark that still resolves the + // `{{best_name:chain:X}}` / `{{best_p50:chain:X}}` family of + // placeholders. Without these, a cold-start fetched from the + // snapshot serves the raw placeholder string to the page. + bestPerChain: b.bestPerChain, + worstPerChain: b.worstPerChain, }; } diff --git a/src/lib/spec.ts b/src/lib/spec.ts index 21cf66eca..53f53112b 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -87,7 +87,10 @@ const loadBenchmarkUnfilteredCached = unstable_cache( }, // Version key bumped when Benchmark shape changes so stale cache entries // from a previous deploy can't surface objects missing newer fields. - ["bench-unfiltered-v2"], + // v3: added bestPerChain + worstPerChain stash; cached objects from + // v2 deploys lack those fields, which made `{{best_name:chain:X}}` + // placeholders fall through to the raw token on the rendered page. + ["bench-unfiltered-v3"], { revalidate: 60, tags: ["benchmarks"] }, );