feat(seo): public read mode, bot dynamic rendering & Lighthouse/bundle gates#2
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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)
AdaptiveShellchooses the layout per request:AppShell(authed users), a lightweight crawlablePublicShell(guests on routes markedhandle.public), or redirect to/login(private routes)./:handle+ tabs (replies / media / likes / followers / following), and/:handle/status/:postId+ photo.OptionalAuthGuard.2. Dynamic rendering (backend + nginx)
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.index.html.3. Client metadata (complement)
<Seo>component uses React 19 native metadata hoisting for per-route title / description / canonical / OG / Twitter; helpers inlib/seo.ts.4. Benchmark gates (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-limitbrotli budgets on always-loaded chunks (app entry <=25 kB, react-vendor <=95 kB) as a standalone job; VitemanualChunksvendor split.@lhci/cliis dev-only;tmppinned via a rootoverridesto clear a high-severity advisory (npm audit --omit=devstays clean).Reviewer notes
SeoModule+ prerender/sitemap/robots,AdaptiveShell/PublicShell, the<Seo>component, Lighthouse + size-limit config, and assorted a11y/perf/security-header fixes across components.docs/adr/0010-seo-crawlability-and-dynamic-rendering.md,docs/adr/0011-seo-benchmark-gates.md.Verification (local)
PublicShell(/@handle,/@handle/status/:id).scripts/seed-lighthouse.mjscreates a public profile + post via the API.🤖 Generated with Claude Code