V1 Web PWA · Journey

Journey: First-time anonymous visitor

A source-reconciled journey record for V1 Web PWA — shipped behavior, state boundaries, failure modes, and the automation evidence available today.

automated · 11 specs verdict: partial· 2026-05-29
9sections14 minread

On this page
Journey at a glance
ActorsMarketing-campaign click-throughEditorial readerCrawler / link-preview botDirect-URL signed-in route attempt
Automation11 E2E spec(s) · verdict: partial· 2026-05-29

Journey flow#

Generated from the authored steps below — click a node to jump to that section.

flowchart TD s1["1. Crawler / link-preview pre-flight"] s2["2. Cold visit — anonymous user lands on /"] s3["3. Anonymous attempt at a gated route preserv…"] s4["4. Editorial / marketing route browse (option…"] s5["5. Accept (or reject) cookie consent"] s6["6. Choose sign-up from /welcome"] s7["7. Email verification round-trip"] s8["8. Onboarding wizard — 10 steps"] s9["9. Onboarding completion handoff"] s10["10. First landing on signed-in surface"] s11["11. Telemetry funnel trail"] s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 --> s10 --> s11 click s1 href "#1-crawler--link-preview-pre-flight" click s2 href "#2-cold-visit--anonymous-user-lands-on-" click s3 href "#3-anonymous-attempt-at-a-gated-route-preserves-the-deep-link" click s4 href "#4-editorial--marketing-route-browse-optional-branch" click s5 href "#5-accept-or-reject-cookie-consent" click s6 href "#6-choose-sign-up-from-welcome" click s7 href "#7-email-verification-round-trip" click s8 href "#8-onboarding-wizard--10-steps" click s9 href "#9-onboarding-completion-handoff" click s10 href "#10-first-landing-on-signed-in-surface" click s11 href "#11-telemetry-funnel-trail" s11 --> v(["verdict: partial"]) click v href "../results/first-time-anonymous-visitor.html" style v stroke:#d6a531,color:#d6a531

The end-to-end path from an unknown browser hitting the domain through completed onboarding and a first landing on Home. Catches public-surface gating, OpenGraph crawler exposure, sign-up funnel telemetry, the magic-link / email verification round-trip, and the onboarding handoff into the signed-in shell — bugs that look fine in any single per-view file but stack into broken funnels.

Personas#

  • Marketing-campaign click-through — primary; lands on /welcome or /landing from a paid ad
  • Editorial reader — lands on /landing, reads the manuscript lede before starting sign-up
  • Crawler / link-preview bot — verifies OpenGraph and Twitter card metadata resolve before any human visits
  • Direct-URL signed-in route attempt — anonymous user pastes /tara or /library; middleware must redirect with ?redirect= preserved

Pre-conditions#

  • Browser has never visited oshun.app; no cookies, no service worker, no IndexedDB
  • Network is online
  • A fresh, deliverable inbox is available for the magic-link / verification email
  • BFF and outbound mail (Mailpit in dev) are healthy
  • PUBLIC_PATHS and PUBLIC_PREFIXES in proxy.ts match the deployed build. Note: the implementation lives only in apps/oshun/web/src/proxy.ts — there is no middleware.ts. Next 16 picks up the exported proxy function via the config matcher natively.

Steps#

  • Issue a GET / with a User-Agent: facebookexternalhit/1.1 and verify it resolves to public HTML at / or the anonymous /welcome bounce
  • Issue a GET /welcome and inspect <head> for OpenGraph + Twitter card tags pointing at /welcome/opengraph-image and /welcome/twitter-image
  • Issue GET /opengraph-image, GET /twitter-image, GET /welcome/opengraph-image, and GET /welcome/twitter-image; confirm 200 + PNG content-type
  • Confirm metadataBase is https://oshun.app so canonical URLs in <link rel="canonical"> resolve absolute; /welcome emits https://oshun.app/welcome
  • Confirm JSON-LD WebApplication includes a free Offer and a featureList mentioning all six domains (tara, arete, veritas, nyx, nisaba, metis)
  • Verify: shell/01-app-shell.md Metadata and SEO section
  • Verify: customer/00-public/welcome.md OpenGraph route handlers

2. Cold visit — anonymous user lands on /#

  • Browser navigates to the root URL
  • Middleware sees no session cookie and redirects to /welcome?redirect=%2F
  • Cookie consent banner mounts at bottom of viewport until accept/reject
  • Service-worker support is present and /sw.js resolves with a JavaScript content-type; manifest and install assets are verified by the PWA smoke suite
  • Theme color is emitted in metadata; root layout defines cream #f1ebdd (light) and #241c12 (dark)
  • Cormorant Garamond, Inter, and JetBrains Mono are loaded through next/font with display: 'swap'
  • <html lang="en" dir="ltr"> is emitted by the root layout
  • Verify: shell/02-routing-layouts.md Middleware → Public path allowlist

Before signing up, the user pastes a deep link to a signed-in route to test gating.

  • Browser navigates to a protected route such as /profile
  • Middleware redirects to /welcome?redirect=%2Fprofile
  • WelcomePageView reads the redirect query via the page server component; WelcomeAuthPanel carries the redirect through buildPublicAuthEntryPath
  • Domain launch CTAs preserve encoded deep links such as /domains/nisaba?origin=home
  • ?expired=1 and ?reauth=1 variants force sign-in mode; the redirect param is sanitized by sanitizeRedirectPath
  • Verify: shell/04-auth-session.md Session expired / Sign-in flow / Sign-in success

4. Editorial / marketing route browse (optional branch)#

  • Navigate to /landing directly
  • MarketingLanding renders the OSHUN masthead, product preview strip, three-column manuscript lede, six room inventory, three static launch scenarios, and three-tier membership row
  • Pricing tier CTAs link to /welcome?mode=signup with entry=marketing-landing and exact tier=Solo|Hearth|Institutional query params
  • The Hearth tier CTA records public_auth_funnel_cta_clicked with step=marketing_tier_signup
  • The text-only marketing footer renders without descendant legal anchors
  • A warmed /landing document replays from the real service worker while offline; standalone 390 px launch has no horizontal overflow and keeps conversion targets at least 44 px
  • Cookie consent banner persists across /landing/welcome navigation
  • Verify: customer/00-public/landing.md Pricing tier CTAs
  • Click "Accept" on the CookieConsentBanner
  • The banner persists a versioned localStorage preference with essential, analytics, functional, and marketing all allowed, then dismisses. It does not itself create analytics cookies; downstream code must consult the stored permission before optional processing.
  • Reload — banner does not reappear
  • Reject branch persists essential-only permission with analytics, functional, and marketing false; the banner dismisses.
  • PwaUpdatePrompt is suppressed only while consent is unresolved; accept or reject both resolve consent, so prompts become eligible after either decision
  • Verify: shell/04-auth-session.md Cookie consent gate

6. Choose sign-up from /welcome#

  • Default mode resolves to signup (no ?mode= query)
  • WelcomeAuthPanel shows the Sign up / Sign in / Recover tab list
  • Switching tabs reflects in URL state; Left/Right arrows navigate the tablist
  • Enter name / email / password
  • Public CTAs fire trackPublicAuthFunnelCtaClicked({ entrySource, mode }); form submission and completion fire public_auth_funnel_submitted and public_auth_funnel_completed
  • WelcomeAuthPanel calls useAuth().signUp, which posts to /api/auth/signup; the Next route forwards to BFF /v1/auth/signup, creates the session cookies, sends the verification email, and routes the signed-in browser to /onboarding
  • Verify: customer/00-public/welcome.md WelcomeAuthPanel mode tabs + submit

7. Email verification round-trip#

  • Mailpit receives the verification email with subject Verify your Oshun email address; the delivered link preserves ?next=/tara (or the original redirect)
  • Click the actual verification link from the delivered email
  • BFF marks the account verified; the signup browser already has HttpOnly oshun-session and oshun-access cookies, and a fresh browser opening the link receives fresh HttpOnly oshun-session and oshun-access cookies for that device
  • The verify-email page refreshes /api/auth/session and reads verified: true
  • User is bounced to /onboarding?redirect=/tara (not the original ?next= surface yet — the shell intercepts unfinished onboarding first)
  • Verify: shell/04-auth-session.md Sign-up flow

8. Onboarding wizard — 10 steps#

OnboardingWizard is the only thing /onboarding/page.tsx renders.

  • Welcome step — "Begin" CTA primary
  • Goals — multi-select goal cards (OnboardingGoalId)
  • Domains — six domain tiles (OshunDomainId) match getShellNavigationDomains()
  • Interests — multi-select interest chips, each with a recommended-domains hint
  • Rhythm (Routine) — daypart timing picker (OnboardingRoutineTimingId)
  • Guide — assistant persona, teacher tone, content tone preferences
  • Alerts (Notifications)OnboardingNotificationPreferences channels
  • Access (Accessibility)OnboardingAccessibilityNeeds toggles
  • MemoryOnboardingMemoryPreferences scope choices
  • Ready (Complete) — final "Finish" submit calls patchProfilePreferences(patch) and writes the local oshunWebPreferencesStore
  • Back / Continue / Finish keyboard focus order is sane; Back disabled on first step
  • Step deep link ?step=memory mounts directly at that step
  • Verify: customer/01-onboarding/onboarding.md step-by-step interactions

9. Onboarding completion handoff#

  • After Finish, the wizard navigates to the sanitised redirectPath if set (e.g. /tara from step 3) — otherwise to resolveFirstRunEntryTarget(entrySource)
  • No ?next= leak in the URL after handoff
  • Verify: customer/01-onboarding/onboarding.md Completion handoff

10. First landing on signed-in surface#

  • If redirect was /profile, ProfileDashboard renders after onboarding completion
  • If redirect was a public domain launch, the requested domain surface renders; the covered Nisaba branch lands at /domains/nisaba?origin=home
  • If redirect was absent or /, resolveFirstRunEntryTarget(entrySource) chooses the deliberate first-run domain instead of dropping context
  • If redirect was /tara, the Tara journey coverage takes over and verifies today's sit, course, and teachers
  • If Home is the final target, HomeWorkspace renders Tara ritual continuation, Arete practice, Nyx perspective, Metis study continuation, Daypart rail, Domain card grid, Quick actions, Activity feed
  • OnboardingResumeBanner is hidden (onboarding now complete)
  • Shell header shows the five primary tabs with aria-current="page" on the active one
  • Mobile bottom nav appears on coarse pointer + viewport <= 640 px
  • Verify: customer/02-home-discovery/home.md Returning user, full data
  • Verify: customer/03-tara/tara.md Today's sit card

11. Telemetry funnel trail#

  • WelcomePageView, PublicAuthFunnelLink, MarketingAuthCta, and WelcomeAuthPanel emit the verified public-auth events: viewed, CTA clicked, submitted, completed, and failed.
  • The consent decision is durably represented by the versioned oshun-cookie-consent preference record.
  • Emit and verify a dedicated consent analytics event. No cookie_consent_accepted / cookie_consent_rejected symbol exists in the current source.
  • Wire the exported onboarding step/completion telemetry helpers into OnboardingWizard; exporting them from taraAnalytics.ts is not proof of emission.
  • Emit a service-worker registration event. The current PWA telemetry covers sync queued and update applied, not pwa_sw_registered.
  • Define and emit the first signed-in landing event. Neither home_dashboard_loaded nor tara_hub_viewed exists in the current web source.

Post-conditions#

  • User has a valid session cookie scoped to the host
  • Onboarding is complete; oshunWebPreferencesStore reflects the user's choices
  • The auth proxy admits signed-in routes and preserves safe redirect targets. Entitlement checks remain owned by downstream route/surface gates; the proxy does not validate every product tier globally.
  • Cookie consent decision persisted
  • Service worker registered with oshun-static-v<N> precaching the public shell
  • No leaked ?redirect= or ?next= query params in the address bar after handoff

Failure modes to verify#

  • Magic-link email never arrives/welcome exposes recover-mode affordances so the user can request access recovery; recovery request and confirm hit the real BFF routes
  • User clicks verification link on a different device than they signed up from — verification succeeds; new device gets a fresh session; original device can return manually and refresh session state
  • Onboarding abandoned mid-wizard — return to / later; the home OnboardingResumeBanner "Continue onboarding" CTA links into /onboarding?mode=resume at the last incomplete step
  • Crawler hits /welcome with no Accept-Encoding for images/welcome/opengraph-image route handler still responds with a usable static image
  • Cookie-consent unresolved — analytics CTAs still fire local events but no remote tracking pixels load; PwaInstallPrompt and PwaUpdatePrompt stay suppressed only while consent is undecided (PwaBootstrap.tsx:242 gates on cookieConsent !== null). Deciding — accept OR reject — makes the prompts eligible; reject resolves consent rather than suppressing them.
  • Signed-in user pastes /welcome URL — current behavior is intentional public-route rendering: middleware does not bounce signed-in users away from /welcome; the page renders [data-auth-entry-signed-in] with the continuation action.

E2E coverage#

  • Backed by apps/oshun/web/e2e/auth-entry-flows.spec.ts, apps/oshun/web/e2e/welcome-marketing.spec.ts, apps/oshun/web/e2e/pwa-smoke.spec.ts, and apps/oshun/web/e2e/cookie-consent-compliance.spec.ts — together these exercise the redirect-to-welcome gate, sign-up and onboarding completion, OpenGraph/Twitter metadata, install prompt basics, and cookie-consent accept/reject storage.
  • Backed by apps/oshun/web/e2e/first-time-visitor-deepening.spec.ts — drives the step-1 crawler-facing JSON-LD featureList (asserts it lists all six domains under a facebookexternalhit UA), the steps 3–4 expired-session re-entry (/welcome?redirect=%2Ftara&expired=1 opens with the signin tab selected plus the session-expired banner and re-entry guide), the /landing primary CTA carrying entry=marketing-landing through to /welcome, and the failure mode where a signed-in user visiting /welcome stays on /welcome and sees the continuation panel rather than being redirected.
  • Backed by apps/oshun/web/e2e/public-marketing.spec.ts — drives the dedicated /landing branch: fixture-backed six-room inventory, static letter scenarios, all three tier CTA href contracts, Hearth tier-click telemetry, text-only footer boundary, 390 px standalone no-overflow and 44 px CTA targets, real sw.js warm-cache offline replay, social metadata/PNG previews, and sitemap exposure.
  • Backed by apps/oshun/web/e2e/first-time-deepening-2.spec.ts — drives the ?reauth=1 sign-in re-entry variant plus reauth-specific banner and asserts the crawler JSON-LD shape is a schema.org WebApplication with a free Offer.
  • Backed by apps/oshun/web/e2e/email-verify-mailpit.spec.ts — drives the real dev-infra email leg for step 7: /welcome signup creates the browser's HttpOnly oshun-session and oshun-access cookies, the BFF sends the verification email over SMTP to Mailpit (:1025), the spec polls Mailpit's API (:8025) by the unique recipient address, opens the actual delivered /auth/verify-email?token=...&next=/tara link, confirms the public BFF token through the verify-email page, auto-hands the already signed-in browser back to /onboarding?redirect=/tara without clicking Continue, refreshes /api/auth/session, and asserts the account is now verified: true. It also opens the same delivered Mailpit link in a fresh browser context with no pre-existing auth cookies and proves the verify-email proxy sets fresh HttpOnly oshun-session and oshun-access cookies for that device before landing in /onboarding?redirect=/tara. The signup handoff to /onboarding is also asserted before the email link is opened.
  • Backed by apps/oshun/web/e2e/email-verify-roundtrip.spec.ts — drives the verify-email page-level states and failure modes, including a fresh token success, replayed-token error, no-token missing state, delayed verifying state, already-verified copy, safe next=/tara Continue handoff, unsafe external next fallback to /, and a 44x44 px Continue target at a 390 px viewport.
  • Backed by apps/oshun/web/e2e/onboarding-lifecycle.spec.ts — drives the abandonment failure mode: real signup against the dev BFF, leaving a draft at Rhythm, Home OnboardingResumeBanner return to /onboarding?mode=resume, and returning sign-in resuming with redirect=/profile preserved.
  • Backed by apps/oshun/web/e2e/public-legal-pages.spec.ts — drives the public legal branch reached from footer/cookie-consent links: anonymous access for /legal/privacy, /legal/terms, /legal/cookies, /legal/accessibility, /legal/ccpa, /legal/dpa, /legal/lilith, and /legal/lilith/privacy; exact section inventories; cross-legal nav; TOC/back-to-top/print shell controls; 390 px no-overflow and 44 px targets; offline-after-load stability; no telemetry hooks; axe; and the Lilith non-clickable footer markers.
  • Coverage depth: deep — see coverage.md.
  • Remaining delegation: shell chrome, full Home dashboard detail, and the Tara daily-sit content are verified in their own shell/home/Tara walkthroughs and specs; this journey verifies the first-time handoff into those surfaces.

Per-view files touched by this journey#

Cross-references#

  • Feature spec: V1/features.md
  • Architecture: V1/ARCHITECTURE.md
  • Related journeys:
  • Shell docs:
  • Component sources:
    • apps/oshun/web/src/proxy.ts
    • apps/oshun/web/src/app/welcome/page.tsx
    • apps/oshun/web/src/components/welcome/WelcomePageView.tsx
    • apps/oshun/web/src/components/welcome/WelcomeAuthPanel.tsx
    • apps/oshun/web/src/components/onboarding/OnboardingWizard.tsx
    • apps/oshun/web/src/lib/onboarding-routing.ts
    • apps/oshun/web/src/profile/preferences-sync.ts
    • apps/oshun/web/src/components/CookieConsentBanner.tsx

Open questions / known gaps#

  • Anonymous gate lives in proxy.tsproxy.ts holds the full anonymous-gate logic (PUBLIC_PATHS, redirect-to-welcome, expiry, studio boundary) and exports proxy + a config matcher, which Next 16 picks up natively (there is no middleware.ts). Covered by src/__tests__/middleware-public-paths.test.ts.
  • OpenGraph / Twitter image routes were crashing in @vercel/og due to var(--l-*) CSS variables inside social-preview.tsx. Satori has no CSS context, so it choked with TypeError: Cannot read properties of null (reading '1'). Replaced with literal hex/rgba constants (kept in sync with the cream variant in design-system/lilith/lilith.css).
  • WelcomeAuthPanel auth endpoints — the panel calls useAuth(); signUp posts /api/auth/signup -> BFF /v1/auth/signup, signIn posts /api/auth/login -> BFF /v1/auth/login, requestRecovery posts /api/auth/recovery/request -> BFF /v1/auth/recovery/request, and recoverAccount posts /api/auth/recovery/confirm -> BFF /v1/auth/recovery/confirm.
  • Signed-in /welcome behaviorproxy.ts keeps /welcome public; signed-in users stay on /welcome and see [data-auth-entry-signed-in], covered by first-time-visitor-deepening.
  • Onboarding save semantics — per-step drafts persist to oshun.onboarding; final submit writes the local preference store and syncs via /v1/preferences, covered by onboarding-lifecycle and onboarding-deepening.
  • Cookie rejection and PWA prompts — reject resolves consent; update prompts are suppressed only while consent is unresolved, covered by pwa-install-update-offline.
  • Mailpit verification subject/copy — Mailpit specs assert subject Verify your Oshun email address for the unique recipient and extract the delivered /auth/verify-email?token=... link from the message body.
  • Connect the onboarding and first-landing telemetry declared in the feature contract to their real UI actions, then add browser assertions for the emitted payloads.