improvement(library): single JSON-LD source, Markdown articles, and IndexNow pings

Merged
WA
6d ago
+595-59

Summary

  • Library and blog posts emit structured data only as JSON-LD: removed the duplicate microdata BlogPosting (publisher as plain text) and FAQPage from the post layout and FAQ. Markup is unchanged apart from the attributes, an empty <meta> branch, and an empty FAQ wrapper div. The speakable selectors now point at data-speakable hooks instead of the removed itemprops
  • BlogPosting gets @id (<canonical>#article) and isPartOf → the site WebSite node (#website). Authors link to their on-site profile (url plus a shared @id that the author page's Person node uses too), and their external profile stays in sameAs
  • Library articles are served as Markdown at /library/<slug>.md and at /library/<slug> for Accept: text/markdown (afterFiles rewrites, so /library/tags and the feeds are untouched). Responses carry Vary: Accept, are no-store like the HTML page, and send a canonical Link header. Drafts and unknown slugs return a Markdown 404. Post pages advertise <link rel="alternate" type="text/markdown">, and llms.txt notes the .md convention (matching docs)
  • Blog and library post pages advertise their RSS feed. buildPostMetadata now takes the section at every call site
  • IndexNow: a public key file under apps/sim/public, and scripts/ping-indexnow.ts, which submits the library and comparison URLs whose source changed between the previous and new main head (renamed and deleted posts included, drafts never; every comparison when Sim's own profile changes). A new indexnow job runs it after trigger-promote confirms the prod cutover. The job uses continue-on-error with a 5-minute timeout, nothing depends on it, and it skips when nothing changed

Type of Change

  • Improvement

Testing

  • scripts/ping-indexnow.test.ts (6 cases against a real git repo). Mutation-checked: removing the draft guard, rename handling, sim-profile fan-out, or profile-id lookup each turns a case red
  • Dry run of URL collection on the last main release: 5 library URLs, no request sent
  • Extended test-landing-seo-e2e.ts (CI's seo HTTP group): .md and Accept negotiation, Vary, no-store, canonical Link, alternates, /library/tags still HTML, Markdown 404, no microdata, @id/isPartOf/author @id cross-page match, and speakable selectors resolve. The live site fails these checks before this change (verified with curl)
  • Existing tests: content-routing.test.tsx, customers/page.test.tsx, lib/content/* (145 passing), dev-deployment-workflow, desktop-prune-workflow
  • bun run lint, check:comment-hygiene, source-conventions, file-names, site-urls, library-content, client-boundary, boundaries, unused-exports, utils, test-patterns. type-check and check:audits are left to CI

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing (new tests pass the test-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)