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
14 changes: 13 additions & 1 deletion src/app/alternatives/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,19 @@ export default async function AlternativePage({
datePublished: getBenchCreatedAt(bench.slug).toISOString(),
...(citableAsOf(bench) ? { dateModified: bench.lastRunAt } : {}),
variableMeasured: bench.metric,
isBasedOn: benchUrl,
// Cross-doc @id-only refs get validated as standalone
// incomplete Datasets by Google (same failure mode as PR #1442's
// isBasedOn fix). Inline the required fields per bench.
isBasedOn: {
"@type": "Dataset" as const,
"@id": `${benchUrl}#dataset`,
name: bench.title,
description: bench.subtitle,
url: benchUrl,
creator: CREATOR_PUBLISHER,
license: DATASET_LICENSE,
isAccessibleForFree: true,
},
distribution: [
{
"@type": "DataDownload",
Expand Down
15 changes: 14 additions & 1 deletion src/app/answers/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ export default async function AnswerPage({
text: ans.question,
acceptedAnswer: {
"@type": "Answer",
// Google's Rich Results validator flags Answer nodes without a
// `name` field (parity with the compare-page FAQPage builder).
name: ans.question,
text: capDescription(shortAnswer, 990),
url,
author: { "@id": `${SITE.url}/#org` },
Expand All @@ -190,6 +193,7 @@ export default async function AnswerPage({
if (expertTake) {
questionNode.suggestedAnswer = {
"@type": "Answer",
name: ans.question,
text: capDescription(expertTake, 990),
url,
author: { "@id": PERSON_ID },
Expand Down Expand Up @@ -218,7 +222,16 @@ export default async function AnswerPage({
author: { "@id": `${SITE.url}/#org` },
publisher: { "@id": `${SITE.url}/#org` },
image: `${SITE.url}/api/og/${bench.slug}`,
isBasedOn: benchUrl,
// Inline the referenced bench Dataset (bare URL ref gets
// validated as a standalone incomplete Dataset by Google, same
// failure mode as PR #1442's isBasedOn fix).
isBasedOn: {
"@type": "Dataset",
"@id": `${benchUrl}#dataset`,
name: bench.title,
description: bench.subtitle,
url: benchUrl,
},
},
buildBreadcrumbJsonLd([
{ name: "Home", item: SITE.url },
Expand Down
35 changes: 31 additions & 4 deletions src/app/compare/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -717,12 +717,36 @@ export default async function ComparePage({
isAccessibleForFree: true,
license: DATASET_LICENSE,
measurementTechnique: `${SITE.url}/methodology`,
variableMeasured: shared.map((s) => ({
"@type": "PropertyValue",
// PropertyValue in a Dataset variableMeasured needs a numeric `value`
// for Google Dataset Search + academic LLM tools to extract the
// structured fact. Fallback to the aggregate winner's p50 in the
// shared unit; skip PropertyValue.value entirely when neither side
// returned data so we never publish a fabricated zero.
variableMeasured: shared.map((s) => {
const winner = s.aggregateWinner === "a" ? s.aResult : s.bResult;
const v = winner?.p50;
return {
"@type": "PropertyValue" as const,
name: s.title,
unitText: s.unit,
...(typeof v === "number" && v > 0 ? { value: v } : {}),
};
}),
// isBasedOn is a cross-doc reference to each bench Dataset. Google
// validates each Dataset in isolation and does not stitch bare-URL
// refs, so previously flagged them as standalone Datasets without
// name/description (same failure mode as PR #1442's isBasedOn fix
// in dataset-jsonld.ts). Inline the required fields per bench.
isBasedOn: shared.map((s) => ({
"@type": "Dataset" as const,
"@id": `${SITE.url}/benchmarks/${s.slug}#dataset`,
name: s.title,
unitText: s.unit,
description: `${s.metric} (${s.unit}) benchmark on OpenChainBench: live measurements for ${a.name} and ${b.name}.`,
url: `${SITE.url}/benchmarks/${s.slug}`,
creator: CREATOR_PUBLISHER,
license: DATASET_LICENSE,
isAccessibleForFree: true,
})),
isBasedOn: shared.map((s) => `${SITE.url}/benchmarks/${s.slug}`),
distribution: shared.map((s) => ({
"@type": "DataDownload",
encodingFormat: "application/json",
Expand Down Expand Up @@ -772,6 +796,9 @@ export default async function ComparePage({
const faqJsonLd = {
"@context": "https://schema.org",
"@type": "FAQPage",
// GSC's Rich Results tester flags FAQPage nodes without an `@id`
// (every other FAQPage on the site emits one via the shared builder).
"@id": `${url}#faq`,
// Google's Rich Results validator flags missing `name` on the
// FAQPage parent even when Question.name is set. Add here + on
// Answer nodes so the compare page matches the shared FAQ builder
Expand Down
10 changes: 10 additions & 0 deletions src/app/products/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { Breadcrumb } from "@/components/breadcrumb";
import { buildBreadcrumbJsonLd, safeJsonLd } from "@/lib/jsonld";
import { CREATOR_PUBLISHER, CITABLE_JSON_URL, DATASET_LICENSE } from "@/lib/dataset-jsonld";
import { getBenchCreatedAt } from "@/lib/seo/bench-dates";
import {
fetchHlBuilderStats,
isHlBuilderSlug,
Expand Down Expand Up @@ -446,9 +447,18 @@ export default async function ProviderPage({
...(sameAs.length > 0 ? { sameAs } : {}),
subjectOf: sorted.map((a) => ({
"@type": "Dataset",
// GSC + Google Dataset Search flag anonymous Datasets
// ("Unnamed item" with recommended fields missing) when the
// @id + datePublished are absent (audit 2026-07-26). Match
// the shape used by the bench page's own Dataset node so
// cross-page identifiers align.
"@id": `${SITE.url}/benchmarks/${a.benchmark.slug}#dataset`,
name: a.benchmark.title,
description: capDescription(a.benchmark.subtitle, 990),
url: `${SITE.url}/benchmarks/${a.benchmark.slug}`,
identifier: a.benchmark.slug,
variableMeasured: a.benchmark.metric,
datePublished: getBenchCreatedAt(a.benchmark.slug).toISOString(),
creator: CREATOR_PUBLISHER,
publisher: CREATOR_PUBLISHER,
isAccessibleForFree: true,
Expand Down
Loading