Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/app/alternatives/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SITE } from "@/data/site";
import { loadAlternative } from "@/lib/alternatives";
import { Breadcrumb } from "@/components/breadcrumb";
import { buildBreadcrumbJsonLd, safeJsonLd } from "@/lib/jsonld";
import { citableAsOf } from "@/lib/citation";
import { ProviderLogo } from "@/components/provider-logo";
import { ProviderTypeBadge } from "@/components/provider-type-badge";
import { isRegion } from "@/lib/brand";
Expand Down Expand Up @@ -124,7 +125,7 @@ export default async function AlternativePage({
isAccessibleForFree: true,
license: "https://creativecommons.org/licenses/by/4.0/",
datePublished: getBenchCreatedAt(bench.slug).toISOString(),
dateModified: bench.lastRunAt,
...(citableAsOf(bench) ? { dateModified: bench.lastRunAt } : {}),
variableMeasured: bench.metric,
isBasedOn: benchUrl,
},
Expand All @@ -137,7 +138,7 @@ export default async function AlternativePage({
mainEntityOfPage: url,
articleBody: alt.intro,
datePublished: getBenchCreatedAt(bench.slug).toISOString(),
dateModified: bench.lastRunAt,
...(citableAsOf(bench) ? { dateModified: bench.lastRunAt } : {}),
author: { "@id": `${SITE.url}/#org` },
publisher: { "@id": `${SITE.url}/#org` },
about: { "@id": `${url}#dataset` },
Expand Down
18 changes: 13 additions & 5 deletions src/app/answers/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
hasLiveDataTokens,
benchDataPendingFallback,
} from "@/lib/answers-template";
import { leader } from "@/lib/citation";
import { citableAsOf, leader } from "@/lib/citation";
import { Breadcrumb } from "@/components/breadcrumb";
import { Pill } from "@/components/pill";
import { ProviderLogo } from "@/components/provider-logo";
Expand Down Expand Up @@ -105,7 +105,13 @@ export default async function AnswerPage({
// YAML's slug reference) fall through to a neutral fallback via
// cleanLeftoverTokens so a placeholder string never reaches the SERP.
const render = (s: string) => cleanLeftoverTokens(renderTemplate(s, bench));
const asOfUtc = fmtAsOfUtc(bench.lastRunAt);
// Real measurement timestamp on live benches, null on drafts (whose
// lastRunAt is a wall-clock placeholder from the loader). Prevents the
// visible "Data as of ..." line and the associated <time dateTime>
// structured-data anchor from spoofing freshness for a bench that has
// never actually been measured.
const asOfStamp = citableAsOf(bench);
const asOfUtc = asOfStamp ? fmtAsOfUtc(asOfStamp) : null;
// Detect the "referenced bench has no defensible leader AND the
// source YAML depends on live tokens" case: without this guard the
// per-token fallback rewrites {{best_name}} to "The current leader"
Expand Down Expand Up @@ -165,7 +171,9 @@ export default async function AnswerPage({
mainEntityOfPage: url,
articleBody: `${shortAnswer}\n\n${intro}\n\n${methodology}`,
datePublished: getBenchCreatedAt(bench.slug).toISOString(),
dateModified: bench.lastRunAt,
...(citableAsOf(bench)
? { dateModified: bench.lastRunAt }
: {}),
author: { "@id": `${SITE.url}/#org` },
publisher: { "@id": `${SITE.url}/#org` },
image: `${SITE.url}/api/og/${bench.slug}`,
Expand Down Expand Up @@ -229,9 +237,9 @@ export default async function AnswerPage({
Answer engines quote a claim far more readily when the date
of measurement sits beside it. Uses the referenced bench's
lastRunAt (real data timestamp), not build time. */}
{asOfUtc && (
{asOfUtc && asOfStamp && (
<p className="mt-3 text-[11px] text-ink-faint">
Data as of <time dateTime={bench.lastRunAt}>{asOfUtc}</time>,
Data as of <time dateTime={asOfStamp}>{asOfUtc}</time>,
refreshed continuously.
</p>
)}
Expand Down
20 changes: 16 additions & 4 deletions src/app/api/citable/[date]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { NextResponse } from "next/server";
import { getBenchmarks } from "@/data/benchmarks";
import { SITE } from "@/data/site";
import { AllBenchmarksDraftError } from "@/lib/spec";
import { citeBundle, fieldValue, leader, headlineSentence } from "@/lib/citation";
import { citableAsOf, citeBundle, fieldValue, leader, headlineSentence } from "@/lib/citation";
import { valueInDeclaredUnit } from "@/lib/format";
import { clientKey, rateLimit, tooManyRequests } from "@/lib/rate-limit";

export const runtime = "nodejs";
Expand Down Expand Up @@ -111,24 +112,35 @@ export async function GET(
const data = benches.map((b) => {
const top = leader(b);
const insufficient = b.dataConfidence === "insufficient";
// `value` and `leader.value` are published in the declared `unit`.
// Latency benches with unit "s" store ms internally (fmtUnit
// convention); valueInDeclaredUnit converts so a snapshot never
// claims 627 seconds for a 627 ms head lag. Same conversion as
// the live /api/citable route; missing here caused sub-1 unit
// values to leak through as their internal ms representation.
const raw = insufficient ? null : fieldValue(b);
return {
slug: b.slug,
title: b.title,
category: b.category,
metric: b.metric,
unit: b.unit,
status: b.status,
value: insufficient ? null : fieldValue(b),
value: raw == null ? null : valueInDeclaredUnit(raw, b.unit),
leader:
insufficient
? null
: top
? { name: top.name, slug: top.slug, value: top.value }
? {
name: top.name,
slug: top.slug,
value: valueInDeclaredUnit(top.value, b.unit),
}
: null,
sampleSize: b.sampleSize,
expectedN: b.expectedN,
dataConfidence: b.dataConfidence,
asOf: b.lastRunAt,
asOf: citableAsOf(b),
headline: headlineSentence(b),
url: `${SITE.url}/benchmarks/${b.slug}`,
api: `${SITE.url}/api/stat/${b.slug}`,
Expand Down
4 changes: 2 additions & 2 deletions src/app/api/citable/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
import { getBenchmarks } from "@/data/benchmarks";
import { SITE } from "@/data/site";
import { AllBenchmarksDraftError } from "@/lib/spec";
import { citeBundle, fieldValue, leader, headlineSentence } from "@/lib/citation";
import { citableAsOf, citeBundle, fieldValue, leader, headlineSentence } from "@/lib/citation";
import { valueInDeclaredUnit } from "@/lib/format";
import { clientKey, rateLimit, tooManyRequests } from "@/lib/rate-limit";

Expand Down Expand Up @@ -80,7 +80,7 @@ export async function GET(req: Request) {
sampleSize: b.sampleSize,
expectedN: b.expectedN,
dataConfidence: b.dataConfidence,
asOf: b.lastRunAt,
asOf: citableAsOf(b),
headline: headlineSentence(b),
url: `${SITE.url}/benchmarks/${b.slug}`,
api: `${SITE.url}/api/stat/${b.slug}`,
Expand Down
12 changes: 8 additions & 4 deletions src/app/api/mcp/[transport]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from "zod";
import { getBenchmark, getBenchmarks } from "@/data/benchmarks";
import { SITE } from "@/data/site";
import {
citableAsOf,
citationQuote,
fieldValue,
headlineSentence,
Expand Down Expand Up @@ -225,7 +226,7 @@ const mcpHandler = createMcpHandler(
leader: top,
headline: headlineSentence(b),
url: `${SITE.url}/benchmarks/${b.slug}`,
asOf: b.lastRunAt,
asOf: citableAsOf(b),
};
});
return {
Expand Down Expand Up @@ -321,7 +322,7 @@ const mcpHandler = createMcpHandler(
quote: citationQuote(b, SITE.url),
pageUrl: `${SITE.url}/benchmarks/${b.slug}`,
ogImage: `${SITE.url}/api/og/${b.slug}`,
asOf: b.lastRunAt,
asOf: citableAsOf(b),
methodology: b.methodology,
source: b.source,
};
Expand Down Expand Up @@ -497,7 +498,10 @@ const mcpHandler = createMcpHandler(
md.push(`- Page: ${SITE.url}/benchmarks/${b.slug}`);
md.push(`- Source: ${b.source}`);
md.push(`- License: CC-BY-4.0`);
md.push(`- Last sample: ${b.lastRunAt}`);
{
const asOf = citableAsOf(b);
md.push(`- Last sample: ${asOf ?? "(no measurement yet, draft)"}`);
}
md.push("");
md.push(`**Headline.** ${headlineSentence(b)}`);
md.push("");
Expand Down Expand Up @@ -555,7 +559,7 @@ const mcpHandler = createMcpHandler(
headline: headlineSentence(b),
quote: citationQuote(b, SITE.url),
pageUrl: `${SITE.url}/benchmarks/${b.slug}`,
asOf: b.lastRunAt,
asOf: citableAsOf(b),
methodology: b.methodology,
source: b.source,
};
Expand Down
34 changes: 32 additions & 2 deletions src/app/api/stat/[slug]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
import { getBenchmark } from "@/data/benchmarks";
import { SITE } from "@/data/site";
import {
citableAsOf,
citationQuote,
citeBundle,
fieldValue,
Expand Down Expand Up @@ -33,7 +34,31 @@ export async function GET(
if (!SLUG_RE.test(slug)) {
return NextResponse.json({ error: "bad_slug" }, { status: 400 });
}
const b = await getBenchmark(slug);
// Dimension query params (?chain=, ?region=, ?kind=, ?venue=) mirror
// the same client-side selector on the bench page, so a citer asking
// "fastest ethereum us-east RPC" gets the per cell leader instead of
// the cross chain aggregate. Values pass through to the loader
// unchanged. An unknown value (`?chain=nonexistent`) does NOT fall
// back to the unfiltered aggregate — the loader returns undefined and
// the route below 404s. That is intentional: silently substituting
// the aggregate for a mistyped filter would make citers cite the
// wrong number without knowing.
const url = new URL(req.url);
const filters: {
chain?: string;
region?: string;
kind?: string;
venue?: string;
} = {};
const chainParam = url.searchParams.get("chain");
const regionParam = url.searchParams.get("region");
const kindParam = url.searchParams.get("kind");
const venueParam = url.searchParams.get("venue");
if (chainParam && chainParam !== "all") filters.chain = chainParam;
if (regionParam && regionParam !== "all") filters.region = regionParam;
if (kindParam && kindParam !== "all") filters.kind = kindParam;
if (venueParam && venueParam !== "all") filters.venue = venueParam;
const b = await getBenchmark(slug, filters);
if (!b || b.editorialStatus !== "live") {
return NextResponse.json(
{ error: "unknown_slug", slug },
Expand All @@ -58,6 +83,11 @@ export async function GET(
unit: b.unit,
status: b.status,
higherIsBetter: b.higherIsBetter,
// Echo the applied dimension filter so a citer can verify which
// cell (chain / region / kind / venue) their answer covers.
// Missing key = "all" for that dimension.
filters:
Object.keys(filters).length > 0 ? filters : null,
// Aggregate is "insufficient" (median per-provider sample health
// below 10 percent of expected_n): refuse to publish a value or
// leader; the headline is rewritten by headlineSentence so the
Expand Down Expand Up @@ -88,7 +118,7 @@ export async function GET(
sampleSize: b.sampleSize,
expectedN: b.expectedN,
dataConfidence: b.dataConfidence,
asOf: b.lastRunAt,
asOf: citableAsOf(b),
headline: headlineSentence(b),
quote: citationQuote(b, SITE.url),
cite: citeBundle(b, SITE.url),
Expand Down
5 changes: 4 additions & 1 deletion src/app/benchmarks/[slug]/[chain]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Link from "next/link";
import { ArrowLeft, ArrowUpRight } from "lucide-react";
import { getBenchmark } from "@/data/benchmarks";
import { Breadcrumb } from "@/components/breadcrumb";
import { citableAsOf } from "@/lib/citation";
import { liveResults } from "@/lib/provider-filters";
import { fmtUnit } from "@/lib/format";
import { capDescription } from "@/lib/seo-text";
Expand Down Expand Up @@ -363,7 +364,9 @@ export default async function BenchmarkChainPage({
articleBody: `${keyFacts} ${stripInlineMarkdown(explainer.body)}`,
image: `${SITE.url}/api/og/${benchmark.slug}`,
datePublished: getBenchCreatedAt(benchmark.slug).toISOString(),
dateModified: benchmark.lastRunAt,
...(citableAsOf(benchmark)
? { dateModified: benchmark.lastRunAt }
: {}),
author: { "@id": `${SITE.url}/#org` },
publisher: { "@id": `${SITE.url}/#org` },
about: { "@id": `${benchmarkUrl}#dataset` },
Expand Down
12 changes: 10 additions & 2 deletions src/app/benchmarks/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ExportVideoSection } from "@/components/export-video-section";
import { ReportSection } from "@/components/report-section";
import { CATEGORY_COLOR } from "@/lib/category-colors";
import {
citableAsOf,
groundingTraceLine,
groundingTraceParts,
headlineSentence,
Expand Down Expand Up @@ -310,7 +311,12 @@ export default async function BenchmarkPage({
variableMeasured,
category: benchmark.category,
datePublished: getBenchCreatedAt(benchmark.slug).toISOString(),
dateModified: benchmark.lastRunAt,
// For drafts (no measurement history) skip dateModified so the
// structured-data channel does not spoof freshness. Same rule
// applied to /api/citable, /api/stat and the MCP surface.
...(citableAsOf(benchmark)
? { dateModified: benchmark.lastRunAt }
: {}),
measurementTechnique: benchmark.methodology.join(" "),
}),
// Re-bind creator + publisher to the global @id reference so the bench
Expand Down Expand Up @@ -365,7 +371,9 @@ export default async function BenchmarkPage({
// Clears the "Missing field image" warning in Rich Results Test.
image: `${SITE.url}/api/og/${benchmark.slug}`,
datePublished: getBenchCreatedAt(benchmark.slug).toISOString(),
dateModified: benchmark.lastRunAt,
...(citableAsOf(benchmark)
? { dateModified: benchmark.lastRunAt }
: {}),
author: { "@id": `${SITE.url}/#org` },
publisher: { "@id": `${SITE.url}/#org` },
about: { "@id": `${benchmarkUrl}#dataset` },
Expand Down
2 changes: 1 addition & 1 deletion src/data/benchmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const getBenchmarksSafe = cache(loadAllBenchmarksSafe);

export async function getBenchmark(
slug: string,
options: { chain?: string; region?: string } = {}
options: { chain?: string; region?: string; kind?: string; venue?: string } = {}
): Promise<Benchmark | undefined> {
return loadBenchmark(slug, options);
}
Expand Down
10 changes: 10 additions & 0 deletions src/lib/citation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ function citationCandidates(b: Benchmark): ProviderResult[] {
return live.filter((r) => r.dataConfidence !== "insufficient");
}

/** Timestamp of the last real measurement, or null when the bench has
* no measurement history yet (draft placeholder). Draft benches carry
* a wall-clock `lastRunAt` for type safety (Benchmark.lastRunAt is a
* non-nullable string), which downstream JSON, JSON-LD and MCP surfaces
* would otherwise expose as a real freshness signal to LLM crawlers.
* Use this helper on every machine-readable surface. */
export function citableAsOf(b: Benchmark): string | null {
return b.status === "draft" ? null : b.lastRunAt;
}

/** Median value of the benchmark (the field shown in the headline). */
export function fieldValue(b: Benchmark): number | null {
if (b.status !== "live") return null;
Expand Down
Loading