Skip to content

Make product docs landing pages stronger for SEO - #3738

Open
marcklingen wants to merge 8 commits into
mainfrom
cursor/product-overview-seo-9d5d
Open

Make product docs landing pages stronger for SEO#3738
marcklingen wants to merge 8 commits into
mainfrom
cursor/product-overview-seo-9d5d

Conversation

@marcklingen

@marcklingen marcklingen commented Sep 4, 2026

Copy link
Copy Markdown
Member

Move the product overview pages linked from the top-nav Product menu to shorter, keyword-rich URLs and treat them as SEO pillar pages.

What changed

URL swap

  • /docs/observability/overview/docs/observability
  • /docs/prompt-management/overview/docs/prompt-management
  • /docs/evaluation/overview/docs/evaluation
  • /docs/metrics/overview/docs/metrics

Old /overview and /overview.md URLs redirect to the section roots. Redirect chains such as /observability/docs/tracing → overview, /docs/scores/docs/scores/overview → evaluation, and /docs/tracing-features/docs/tracing are flattened. /docs/prompts now goes to prompt management (not get-started). /docs/api-and-data-platform is unchanged.

On-page

Each landing page has a keyword-aligned seoTitle and H1, a short definition and audience, an eager-loaded hero, concrete jobs-to-be-done, and an FAQ. /docs stays the platform hub.

Current titles:

  • Agent observability and tracing
  • Prompt management (no LLM prefix)
  • Agent evals and experiments
  • Agent metrics and analytics

Nav and footer use Agent Observability, Prompt Management, Agent Evals, and Agent Analytics.

Cannibalization

  • Deleted /faq/all/llm-observability (“What is LLM Observability & Monitoring?”). That page was already canonicalized to the pillar, so it added no independent ranking value. Its unique explainer (why agents need observability; observability vs tracing vs monitoring; what an agent observability platform is; how to monitor outputs) now lives in the observability FAQ accordion.
  • /faq/all/llm-observability 301s to /docs/observability#faq
  • Get-started, concepts, academy, and feature pages link to the product hub
  • Changelog, blog, nav, footer, and homepage tool cards point at the new URLs

Technical

  • Visible breadcrumbs on product landings are Docs > Observability (folder name, not an extra Overview crumb)
  • BreadcrumbList JSON-LD on docs-chrome pages
  • FAQPage JSON-LD on the four product pages
  • SoftwareApplication JSON-LD on /docs
  • Sitemap priority 0.9 for the five product URLs
  • Those URLs are pinned at the top of the docs llms.txt section
  • Product landing paths live in lib/product-overview-paths.js so sitemap + llms.txt stay in sync
  • GitHub “Edit this page” maps folder landings to index.mdx without importing Node fs (that import failed Vercel preview tracing)

Verification

Titles, H1s, FAQ JSON-LD, and the FAQ 301 were checked against the local dev server. Browser walkthrough confirmed the Product menu labels, landing H1s, expanded observability FAQs, and the old FAQ URL landing on #faq. GitHub Actions format, H1, build, and sitemap checks passed. Vercel preview failed starting at the fs.existsSync Edit-on-GitHub commit; that import is removed on this head.

Product menu: Agent Observability, Prompt Management, Agent Evals, Agent Analytics
Observability landing H1: Agent observability and tracing
Observability FAQ accordion with folded explainer questions
Prompt management H1 without an LLM prefix
Evaluation landing H1: Agent evals and experiments
Metrics landing H1: Agent metrics and analytics
Old FAQ URL redirects to /docs/observability#faq
agent_product_titles_and_faq_redirect.mp4

Open in Web Open in Cursor 

Move Observability, Prompt Management, Evaluation, and Metrics from
/overview to the section root. Rewrite those pages as keyword-aligned
pillar content, add FAQ and breadcrumb structured data, raise sitemap
priority, pin the pages in llms.txt, and point spoke docs at the hubs.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>
@vercel

vercel Bot commented Sep 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
langfuse-docs Ready Ready Preview Sep 4, 2026 8:23pm UTC

Request Review

Treat each product index.mdx as the folder page so breadcrumbs read Docs > Observability (not Overview). Point homepage tool cards and a few stale hub links at the new section roots, and hop old /scores, /tracing-features, and /experimentation URLs straight to their destinations.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>
@greptile-apps

greptile-apps Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review (193 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown

@claude review

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

No review was started: this request came from a bot account. Manual reviews can only be requested by someone with write access to this repository. Ask a maintainer to comment @claude review, or have your automation post the comment from a user account with write access.

Tip: disable this comment in your organization's Code Review settings.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T19:13:27.614716Z 1fb2680 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1fb26800e0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/redirects.js
Comment thread next-sitemap.config.js Outdated
Comment thread lib/json-ld.ts
Comment thread lib/redirects.js
Sitemap priority and llms.txt pinning now read the same CJS list so a rename cannot update one without the other. Also hop /docs/{product}/overview.md to the new section-root markdown URLs so agent bookmarks do not 404.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>
Structured data now includes Home, known section/product landings, and the current page. Intermediate paths such as /docs/observability/features have no index page, so they are skipped instead of pointing crawlers at 404s.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🔴 components/DocsTocFooter.tsx — getGithubEditUrl still builds <contentDir>/<slugPath>.mdx for every non-root path, so on the four new product landing pages it now links to content/docs/observability.mdx (etc.) — a file this PR deleted (moved to observability/index.mdx). The 'Edit this page on GitHub' action on /docs/observability, /docs/prompt-management, /docs/evaluation, and /docs/metrics now opens GitHub's create-new-file view instead of editing the real file. Fix: special-case a slug that resolves to a folder with an index.mdx (or generally try <slugPath>.mdx then fall back to <slugPath>/index.mdx), mirroring the fallback getContributors already has a few lines below.

    Extended reasoning...

    getGithubEditUrl(path) splits path into section+slugParts and computes filePath = contentDir/slugPath.mdx unless slugParts is empty (root case, mapped to index.mdx). It has no equivalent handling for a slug that maps to a folder whose content lives in index.mdx one level deeper. Before this PR, /docs/observability didn't exist as a page at all (it 302'd), and /docs/observability/overview correctly produced content/docs/observability/overview.mdx, which existed. After this PR, content/docs/observability/overview.mdx was deleted and content/docs/observability/index.mdx added, and the route /docs/observability is now a real, highly-trafficked landing page — but getGithubEditUrl (untouched by the diff) still computes content/docs/observability.mdx, which does not exist on disk, so every visitor who clicks 'Edit this page on GitHub' on the four new landing pages lands on a nonexistent-file edit screen instead of the real source file.

    Verification: normal (low-impact but a real newly-introduced regression). getGithubEditUrl in components/DocsTocFooter.tsx:33-36 computes filePath = ${contentDir}/${slugPath === "" ? "index" : slugPath}.mdx. It only maps the empty-slug (root) case to index; a slug that resolves to a folder whose content lives in index.mdx is not handled. For pathname /docs/observability: section="docs" (in…

Comment on lines 82 to +97
if (node.type === "page") {
const prev = i > 0 ? path[i - 1] : undefined;
const isFolderIndex =
prev?.type === "folder" &&
(prev.index === node || prev.index?.url === node.url);
// Folder index pages use the folder name (Observability), not the
// page sidebar title (Overview). The section root is the leading crumb.
if (isFolderIndex || node.url === resolveSectionUrl(root)) continue;
result.push({ name: node.name, url: node.url });
} else if (node.type === "folder") {
// The active root folder is rendered as the leading crumb below.
if (node.root) continue;
// Fumadocs collapses a folder and its index page into a single crumb.
if (i === path.length - 1 || node.index !== path[i + 1]) {
result.push({
name: node.name,
url: node.index?.url ?? resolveFirstUrl(node),
});
}
result.push({
name: node.name,
url: node.index?.url ?? resolveFirstUrl(node),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) The folder-index collapsing rule was flipped to always show the folder's meta.json title and skip the index page's own name for EVERY folder with an index page sitewide, not only the four new product pages this PR manually checked. Existing folders that already used index.mdx with a distinct sidebarTitle (e.g. self-hosting/configuration, self-hosting/upgrade) now show a less specific breadcrumb label than before. Fix: only apply the folder-name-wins rule for the new product-section landing pages, or preserve using the page's own name/sidebarTitle when it differs from the folder title, so previously informative breadcrumbs on unrelated pre-existing pages aren't silently degraded.

Extended reasoning...

Before this diff, for a folder whose index page is the currently viewed page, the code pushed the folder crumb only when node.index !== path[i+1] (line removed); when the index page IS the next node, the folder crumb was skipped and the page node was pushed instead using node.name, which fumadocs derives from the page's own sidebarTitle frontmatter. Several already-published folders use index.mdx with a sidebarTitle distinct from the folder's meta.json title: content/self-hosting/configuration/index.mdx has sidebarTitle: "Environment Variables" while content/self-hosting/configuration/meta.json has "title": "Configuration"; content/self-hosting/upgrade/index.mdx has sidebarTitle: "How to Upgrade" vs meta.json "title": "Upgrade". After this diff, the folder crumb is now pushed unconditionally (new line 94-97) and the page crumb is skipped whenever isFolderIndex is true (new lines 84-89), so visiting /self-hosting/configuration now renders the breadcrumb ending in "Configuration" instead of the previous "Environment Variables", and…

Verification: nit. The reworked folder-index rule at components/DocsBreadcrumb.tsx:84-97 applies to EVERY folder whose index is the current page, not only the four product pages. For a folder-index page, fumadocs' searchPath returns the trail [folder, folder.index] (corroborated by the removed guard node.index !== path[i + 1]). Old code skipped the folder crumb and pushed the index page with its own .name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this as-is for this PR. Showing the folder title (Observability, Configuration) instead of the index page’s sidebarTitle (Overview, Environment Variables) is the intended breadcrumb for a folder landing. The product pages were the ones that looked wrong with a trailing Overview crumb. Happy to revisit self-hosting labels in a follow-up if you want those index pages to keep a distinct crumb name.

Comment thread lib/json-ld.ts
Comment on lines +25 to +33
function humanizeSegment(segment: string): string {
return (
SLUG_LABELS[segment] ??
segment
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ")
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (optional) breadcrumbListJsonLd derives ListItem names by title-casing raw URL slugs (falling back when not in the small SLUG_LABELS map), so the BreadcrumbList JSON-LD emitted on every docs page (wired in DocsChromePage.tsx) mismatches the visible breadcrumb rendered by DocsBreadcrumb.tsx, which uses the real page-tree titles. E.g. folder "sdk" (title "SDKs") renders as "Sdk", and page "url" (title "Trace URLs") renders as "Url" in the structured data — a Rich Results / Search Console breadcrumb-mismatch warning, undermining this PR's own SEO goal. Fix: build breadcrumb names from the actual page-tree node names used by DocsBreadcrumb (or pass them through), not from humanized URL segments.

Extended reasoning...

DocsChromePage.tsx calls breadcrumbListJsonLd(pageUrl) for every docs page and injects it via . The function (lib/json-ld.ts:37-59) only knows the URL, so for each segment it calls humanizeSegment (line 25-33), which looks up a tiny hardcoded SLUG_LABELS dict and otherwise just capitalizes each hyphen-split word. For content/docs/observability/sdk/meta.json the folder title is "SDKs" (shown in the visible breadcrumb via DocsBreadcrumb.tsx's node.name), but the JSON-LD for /docs/observability/sdk/... emits name: "Sdk". Same for content/docs/observability/features/url.mdx, titled "Trace URLs", where JSON-LD says "Url". Google's structured-data guidelines require breadcrumb JSON-LD to match the on-page breadcrumb; this mismatch is systemic across most nested docs pages, not a one-off, and is entirely new code introduced by this diff.

Verification: nit. breadcrumbListJsonLd (lib/json-ld.ts:37-59) names each crumb from the URL slug via humanizeSegment (lines 25-33): SLUG_LABELS lookup, else capitalize hyphen-split tokens. DocsChromePage.tsx:61,76 injects it on every sidebar docs page. The visible breadcrumb (DocsBreadcrumb.tsx:90,94,101) instead uses real page-tree titles (node.name/root.name) from meta.json/frontmatter. Verified…

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed this is a real polish item, but it needs the page-tree titles on the server, not URL slug humanizing. Out of scope for this PR — the JSON-LD trail now at least points at resolvable URLs. Fine to take as a follow-up so structured names match the visible crumbs (SDKs, Trace URLs, …).

Product landings live at content/docs/<product>/index.mdx. The TOC footer used to build content/docs/<product>.mdx, which does not exist. Resolve the source file on the server by trying the slug file first and falling back to index.mdx.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>
@cursor

cursor Bot commented Sep 4, 2026

Copy link
Copy Markdown

Fixed the Edit-on-GitHub regression for the four product landings. getGithubEditUrl now runs on the server and tries <slug>.mdx then <slug>/index.mdx, so /docs/observability opens content/docs/observability/index.mdx instead of a missing observability.mdx.

Use Agent in product titles, nav, and hub links for observability,
evals, and metrics. Drop the LLM prefix from prompt management.
Move the standalone LLM observability FAQ into the pillar FAQ
accordion and 301 the old URL so one page ranks for the topic.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>
The Vercel preview started failing on the commit that added
fs.existsSync to every docs-chrome page. Map folder landings to
index.mdx from a static path set instead so the server module no
longer pulls in a Node builtin.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b0ebc20. Configure here.

Comment thread lib/github-edit-url.ts Outdated
Keep the static path map so this module does not import Node fs.
Cookbook, integration, and engineering pages that live as .md
now get that extension instead of a missing .mdx file.

Co-authored-by: marc.klingen <marc.klingen@clickhouse.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants