Skip to content

feat(seo): public read mode, bot dynamic rendering & Lighthouse/bundle gates#2

Merged
mitekk merged 1 commit into
masterfrom
feat/seo-crawlability
Jun 11, 2026
Merged

feat(seo): public read mode, bot dynamic rendering & Lighthouse/bundle gates#2
mitekk merged 1 commit into
masterfrom
feat/seo-crawlability

Conversation

@mitekk

@mitekk mitekk commented Jun 11, 2026

Copy link
Copy Markdown
Owner

What

Makes PULSE's public content crawlable and link-previewable, and gates the build on SEO/quality benchmarks so the posture can't silently regress. Implements ADR-0010 (crawlability + dynamic rendering) and ADR-0011 (benchmark gates).

Layers

1. Public read mode (frontend)

  • AdaptiveShell chooses the layout per request: AppShell (authed users), a lightweight crawlable PublicShell (guests on routes marked handle.public), or redirect to /login (private routes).
  • Public routes: /:handle + tabs (replies / media / likes / followers / following), and /:handle/status/:postId + photo.
  • Privacy preserved: private accounts still render the protected lock; DMs, notifications, bookmarks and settings stay auth-gated. No backend authorization change — the read endpoints were already OptionalAuthGuard.

2. Dynamic rendering (backend + nginx)

  • New SeoModule: GET /seo/prerender (server-rendered Open Graph + Twitter Card + JSON-LD for non-JS social scrapers), DB-backed /seo/sitemap.xml, host-aware /seo/robots.txt.
  • nginx maps crawler/social user-agents to an internal prerender location; humans get the SPA index.html.

3. Client metadata (complement)

  • <Seo> component uses React 19 native metadata hoisting for per-route title / description / canonical / OG / Twitter; helpers in lib/seo.ts.

4. Benchmark gates (CI)

  • Lighthouse CI (lighthouserc.cjs) against the dockerized stack with deterministic seeded content (scripts/seed-lighthouse.mjs). Gates SEO 1.0, A11y 0.90, Best Practices 0.90 (error) and Performance 0.80 (warn — CI CPU variance). Desktop preset.
  • size-limit brotli budgets on always-loaded chunks (app entry <=25 kB, react-vendor <=95 kB) as a standalone job; Vite manualChunks vendor split.
  • @lhci/cli is dev-only; tmp pinned via a root overrides to clear a high-severity advisory (npm audit --omit=dev stays clean).

Reviewer notes

  • 56 files, +4577 / -108. Bulk is the new SeoModule + prerender/sitemap/robots, AdaptiveShell/PublicShell, the <Seo> component, Lighthouse + size-limit config, and assorted a11y/perf/security-header fixes across components.
  • Design: docs/adr/0010-seo-crawlability-and-dynamic-rendering.md, docs/adr/0011-seo-benchmark-gates.md.

Verification (local)

  • Dockerized stack boots; public profile and post routes return HTTP 200 and render for guests via PublicShell (/@handle, /@handle/status/:id).
  • scripts/seed-lighthouse.mjs creates a public profile + post via the API.
  • The new Lighthouse and bundle-size CI gates will run on this PR.

Independent of #1 (the dev-compose JWT fix), which is a separate PR.

🤖 Generated with Claude Code

…e gates

Make public content crawlable and link-previewable, and gate the build on
SEO/quality benchmarks so it cannot silently regress. See ADR-0010 and ADR-0011.

Crawlability (frontend):
- AdaptiveShell picks the layout per request: AppShell for authed users, a
  lightweight crawlable PublicShell (sign-in CTAs, no realtime socket) for
  guests on routes marked handle.public, redirect to /login otherwise.
- Public routes: /:handle (+ replies/media/likes/followers/following) and
  /:handle/status/:postId (+ photo). Private accounts, DMs, notifications,
  bookmarks and settings stay auth-gated. No backend authz change — the read
  endpoints were already OptionalAuthGuard.

Dynamic rendering (backend + nginx):
- New SeoModule: GET /seo/prerender (server-rendered OG + Twitter Card + JSON-LD
  for non-JS social scrapers), DB-backed /seo/sitemap.xml, host-aware
  /seo/robots.txt. nginx maps crawler/social user-agents to an internal
  prerender location; humans get the SPA index.html.

Client metadata (complement):
- <Seo> component uses React 19 native metadata hoisting for per-route
  title/description/canonical/OG/Twitter; helpers in lib/seo.ts.

Benchmark gates (CI):
- Lighthouse CI (lighthouserc.cjs) against the dockerized stack with
  deterministic seeded content (scripts/seed-lighthouse.mjs). Gates SEO=1.0,
  A11y=0.9, Best Practices=0.9 (error); Performance=0.8 (warn) due to CI CPU
  variance. Desktop preset.
- size-limit brotli budgets on always-loaded chunks (app entry <=25kB,
  react-vendor <=95kB) as a standalone job; Vite manualChunks for the vendor split.
- @lhci/cli is dev-only; tmp pinned via root overrides to clear a high advisory.

Also adds robots.txt, site.webmanifest, OG/app icons, and assorted
accessibility/performance/security-header fixes across components.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mitekk mitekk merged commit 174d4ed into master Jun 11, 2026
7 checks passed
@mitekk mitekk deleted the feat/seo-crawlability branch June 11, 2026 14:43
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