Skip to content

SEO content plays: /compare pages, FAQ schema, cookbook infra#5219

Open
NicholasKissel wants to merge 13 commits into
mainfrom
NicholasKissel/seo-content-plays
Open

SEO content plays: /compare pages, FAQ schema, cookbook infra#5219
NicholasKissel wants to merge 13 commits into
mainfrom
NicholasKissel/seo-content-plays

Conversation

@NicholasKissel

@NicholasKissel NicholasKissel commented Jun 11, 2026

Copy link
Copy Markdown
Member

Implements the SEO content plays for rivet.dev.

Play 1: Comparison pages at /compare/*

  • Typed data registry (src/data/compare/) + shared React components + one dynamic Astro route; page N+1 = one data file + one registry line
  • Rivet vs Cloudflare Durable Objects: migrated from the orphaned 1039-line RivetVsCloudflareWorkersPage.tsx (now deleted), with copy fixes and an updated SQLite row
  • Rivet vs Temporal: new page; every public claim (latency floor, actions pricing, SDK count, license, self-host architecture) verified against Temporal's own docs/pricing as of June 2026
  • Hub page at /compare/, footer link, BreadcrumbList + WebPage + FAQPage JSON-LD per page, CollectionPage/ItemList on the hub

Play 2: FAQ system + FAQPage JSON-LD

  • One TS data module per page feeds both the visible rendering and the schema, so they cannot diverge
  • Hook-free FaqSection/FaqList (native <details>, server-renders with zero JS) + FaqJsonLd.astro emitter (one FAQPage per URL)
  • FAQs on /cloud, /actors (static render, no client directive), /agent-os/pricing (existing Q&As moved to the shared system)

Play 3: Cookbook expansion (1 → 8 entries)

  • Populated templates registry (turns on cookbook index filter chips), TechArticle + BreadcrumbList JSON-LD on cookbook pages, Cookbook nav item
  • Six new wave-1 entries: AI Agent, Chat Room, Cron Jobs, Live Cursors, Database per Tenant, Collaborative Text Editor
  • AI Agent Workspaces (/cookbook/ai-agent-workspace/): agentOS pattern guide anchored on examples/agent-os; one agentOs() actor per agent covering filesystem, processes, shells, preview URLs, cron, host tools, Pi sessions, and sandbox mounting; content adversarially verified against the example and rivetkit/agent-os source; cross-linked from the AI Agent entry and the agentOS docs overview
  • One-line docs cross-links from schedule, sqlite, sqlite-drizzle, events, connections, and queues docs to their related cookbook entries

Verification

  • Full pnpm build passes (247 pages)
  • Script-asserted: every ld+json block parses, exactly one FAQPage per URL, every FAQ question visible in page HTML
  • Sitemap includes all /compare/ and /cookbook/ URLs; comparison tables are server-rendered; FAQs toggle with JS disabled

🤖 Generated with Claude Code

@railway-app

railway-app Bot commented Jun 11, 2026

Copy link
Copy Markdown

🚅 Deployed to the rivet-pr-5219 environment in rivet-frontend

Service Status Web Updated (UTC)
website 😴 Sleeping (View Logs) Web Jun 12, 2026 at 8:37 am
kitchen-sink ❌ Build Failed (View Logs) Web Jun 12, 2026 at 7:33 am
mcp-hub ✅ Success (View Logs) Web Jun 12, 2026 at 6:25 am
frontend-cloud ❌ Build Failed (View Logs) Web Jun 11, 2026 at 9:02 am
ladle ❌ Build Failed (View Logs) Web Jun 11, 2026 at 9:02 am
frontend-inspector ❌ Build Failed (View Logs) Web Jun 11, 2026 at 9:02 am

@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

PR #5219 Review — SEO Content Plays

Overall this is a solid, well-scoped content PR. The typed data registry pattern for compare pages is clean and extensible, the FAQ system correctly avoids divergence between visible content and schema, and the cookbook expansion is thorough. A few issues need attention before merge.


Critical

SVG <defs> ID collisions across covers (CookbookCover.tsx, lines ~238–1148)

Each per-cover art function (AiAgentArt, WorkspacesArt, TenantsArt, etc.) defines gradient/mask IDs like c1ref, c2ref, c3ref inside their inline <svg> <defs>. SVG <defs> IDs are document-scoped, not per-<svg>, so when the cookbook index renders all covers simultaneously, the last cover's definitions overwrite the earlier ones and earlier covers render with wrong gradients or masks. FallbackArt correctly scopes IDs as fb-${slug}-${part} — all per-cover functions should follow that same pattern.

FeatureStatus switch has no default arm (FeatureStatus.tsx, line ~17)

let icon, bgColor, textColor are declared without defaults and the switch on ComparisonStatus has no default: case. Adding a new status value and using it in a data file before updating this switch will cause a runtime crash when icon is undefined. Convert to a lookup object or add defaults.


Major

FAQPage schema present but FAQ markup is inside a client:load island (agent-os/pricing.astro)

FaqJsonLd is emitted in the static shell, but the actual FAQ <details>/<summary> elements live inside <AgentOSPricingPage client:load />. Google's FAQPage rich-result guidelines require that schema content be visible in the static HTML. cloud.astro renders FaqSection outside the island — pricing.astro should do the same.

Tailwind hover selector syntax is inverted (ComparisonTable.tsx, line ~22)

[&_a]:hover:text-zinc-300 fires on the component's :hover state and targets a descendants, not on a:hover. The correct form is [&_a:hover]:text-zinc-300. As written, link hover styles in the comparison table do not apply.

New /compare/ routes missing from sitemap (website/src/sitemap/mod.ts)

/compare/, /compare/rivet-vs-cloudflare-durable-objects, and /compare/rivet-vs-temporal have no entries in sitemap/mod.ts. Per CLAUDE.md: "When adding new docs pages, update website/src/sitemap/mod.ts so the page appears in the sidebar."


Minor

Pre-existing JSON.stringify in cloud.astro lacks XSS escaping (cloud.astro, line ~69)

jsonLdString was introduced in this PR to escape < in JSON-LD. The existing JSON.stringify(productSchema) in the same file was not migrated. Should be jsonLdString(productSchema) for consistency.

<th> elements missing scope="col" (ComparisonTable.tsx, lines ~25–43)

WCAG 1.3.1 (Level A). Add scope="col" to all four <th> elements.

Verdict paragraphs use content as React key (ComparePage.tsx, line ~145)

entry.verdict.map((paragraph) => <p key={paragraph}> uses paragraph text as the key. If two paragraphs are identical, React silently drops the duplicate. Use key={index}.

TechArticle schema missing datePublished (cookbook/slug.astro, lines ~65–73)

Schema has headline, description, url, author, publisher but no datePublished. If cookbook MDX frontmatter has a date field, surface it here for article freshness signals.

/compare/index.astro missing active prop on <MarketingLayout> (compare/index.astro, line ~44)

All other top-level section hub pages pass active="..." to highlight the nav item. The compare hub omits it.


Nit

  • CookbookCover.tsx at 1231 lines will slow TypeScript checking and HMR as more entries are added. Consider splitting art functions into per-entry files under CookbookCover/.
  • faqPageSchema builder in types.ts mixes data shapes with schema construction logic. Belongs in lib/jsonLd.ts or lib/faqSchema.ts.
  • WebPage schema on compare pages has dateModified but no datePublished (compare/slug.astro, lines ~40–48).

🤖 Generated with Claude Code

NicholasKissel commented Jun 12, 2026

Copy link
Copy Markdown
Member Author

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.

1 participant