Uh oh!
There was an error while loading. Please reload this page.
feat(frontend): public-beta SEO + shell polish (B8: #169 #170 #171 #187 #172 #188 #191) - #466
Conversation
Warning Review limit reached
Next review available in:21 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (23)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with |
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs | frontend-staging | f118d9f | Commit Preview URL Branch Preview URL | Jul 30 2026, 07:26 AM |
AndresL230
commented
Jul 30, 2026
Code reviewFound 1 issue:
Sapling/frontend/src/app/(shell)/loading.tsx Lines 13 to 20 in 818840d Sub-threshold notes (verified, being fixed in a follow-up commit on this PR, scores <80): 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
- (shell)/loading.tsx: drop the fabricated topbar row — the fallback mounts inside ShellFrame's <main>, under the real persistent TopNav/SideNav, so it must be content-only (double-topbar flash, score 100). - deployGuard.resolveSiteUrl: DEPLOY_ENV now wins unconditionally (ADR-0022 precedence, matching resolveFrontendEnv); NEXT_PUBLIC_SITE_URL is the local/preview fallback only, and the build-time explicit lock in checkFrontendDeployEnv now classifies it too (score 75). - UserContext: a definitive 401/404 clear on a shell route now hard-bounces to /?error=session_expired (same greppable code as the middleware) instead of stranding a gated screen with no identity; pure shouldBounceToSignin predicate exported and pinned by tests (score 65). - Persist gate now exercised against the REAL provider: persist:false writes nothing, default persists (score 30). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#188#191) - #169: full root metadata — metadataBase (DEPLOY_ENV-derived via new deployGuard.resolveSiteUrl), title template, OG/Twitter cards with a generated public/og.png, canonical. - #170: robots.ts (staging fully disallowed; private app surface blocked), sitemap.ts (public routes + job slugs), manifest.ts (brand identity). - #171: per-page metadata on about/privacy/terms/careers; careers page split into a server wrapper + CareersList client child; per-job generateMetadata. - #187: unknown careers slug now hard-404s via notFound(). - #172: real app/global-error.tsx (own <html>/<body>, branded ErrorFallback); the misnamed root segment boundary renamed GlobalError → RootError. - #188: (shell)/loading.tsx paints an instant skeleton on route transitions. - #191: localStorage identity persists only after a confirmed /me (setActiveUser persist gate through callback + SignInModal broadcast); UserContext clears stale identity on definitive 401/404 from the existing profile fetch — 5xx/network keep the session. - e2e: public-seo.spec.ts pins robots/sitemap/manifest/careers-404/og tags. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- (shell)/loading.tsx: drop the fabricated topbar row — the fallback mounts inside ShellFrame's <main>, under the real persistent TopNav/SideNav, so it must be content-only (double-topbar flash, score 100). - deployGuard.resolveSiteUrl: DEPLOY_ENV now wins unconditionally (ADR-0022 precedence, matching resolveFrontendEnv); NEXT_PUBLIC_SITE_URL is the local/preview fallback only, and the build-time explicit lock in checkFrontendDeployEnv now classifies it too (score 75). - UserContext: a definitive 401/404 clear on a shell route now hard-bounces to /?error=session_expired (same greppable code as the middleware) instead of stranding a gated screen with no identity; pure shouldBounceToSignin predicate exported and pinned by tests (score 65). - Persist gate now exercised against the REAL provider: persist:false writes nothing, default persists (score 30). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bundle B8 — public-beta polish
Closes#169, closes#170, closes#171, closes#187, closes#172, closes#188, closes#191.
SEO (#169#170#171#187)
app/layout.tsx):metadataBasederived fromDEPLOY_ENVvia a newdeployGuard.resolveSiteUrl(ADR-0022 single-knob;NEXT_PUBLIC_SITE_URLoverrides for local/preview), title template, OpenGraph + Twitter cards backed by a generatedpublic/og.png(1200×630), canonical.robots.ts: allows the public surface, disallows the entire private app shell (mirrorsmiddleware.tsPROTECTED) — and disallows everything on staging so staging twins never compete with canonical pages.sitemap.ts: public marketing/legal routes + per-job careers slugs.manifest.ts: brand name/colors/icon.about/privacy/termsexportmetadata;careerssplit into a server wrapper (page.tsx, holds metadata) +CareersList.tsxclient child (UI unchanged, moved verbatim);careers/[slug]gets per-jobgenerateMetadata.notFound()→ real HTTP 404 instead of a 200 soft-404.Shell (#172#188)
app/global-error.tsxrendering its own<html>/<body>+ brandedErrorFallback(importsglobals.csssince it replaces the root layout); the misnamed segment boundaryerror.tsxrenamedGlobalError→RootError.(shell)/loading.tsx: instant neutral skeleton on shell route transitions.Auth state honesty (#191)
setActiveUsergains a{ persist }option: localStorage identity is written only after GET /api/auth/me confirms the session (gated in the OAuth callback, and through the popup broadcast via a newprofileConfirmedpayload field consumed bySignInModal). On a failed/me, the tab still signs in (context hydrates) but nothing persists — the provider's cookie fallback reconciles on the next full load.UserContext.fetchProfileData(already fired on every authed mount) now clears the stale identity on a definitive 401/404; 5xx/network failures keep the session (a server blip must not sign anyone out).Tests
seo.test.ts(metadata/robots/sitemap/manifest/notFound +resolveSiteUrl),globalError.test.tsx,shellLoading.test.tsx,UserContext.reconcile.test.tsx(401/404 clear, 5xx/network keep),callback.test.tsx(persist gate + clean failure).e2e/public-seo.spec.ts: robots/sitemap/manifest resolve, careers hard-404, OG tags on the landing HTML.tsc --noEmitclean;eslint .0 errors with no suppressions-baseline changes (the one new exhaustive-deps hit was fixed properly viauseCallback).Notes for review
next buildcannot run inside the worktree (Turbopack rejects the symlinkednode_modules); the build is exercised by the pre-merge e2e cycle from the primary checkout (stack build) before merge.docs/frontend-testids.md/eslint.config.mjschanges needed: no new interactive elements on testid'd surfaces (the journey is pure HTTP).🤖 Generated with Claude Code