V1 Web PWA · Surface walkthrough

App canonical-shell redirect

A per-surface walkthrough of the V1 Web PWA workspace surface: layout, states, interactions, data, and cross-references.

walked
8sections4 minread1table

On this page

Context. surface workspace · domain workspace (canonical shell-route alias / redirect) · route /app/[surface]/[[...path]] · auth anon (redirect-only; downstream route enforces auth) · source apps/oshun/web/src/proxy.ts; apps/oshun/web/src/app/app/[surface]/[[...path]]/page.tsx

Last walked. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; live screen-reader, touch, offline, and telemetry-delivery checks pending a manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content re-verified 2026-06-03 against current source; alias redirect contract re-verified 2026-06-30 with apps/oshun/web/e2e/canonical-deep-links.spec.ts and middleware-public-paths unit coverage

Purpose#

A redirect-only catch-all that maps /app/<surface>/<rest> to the canonical shell route for <surface>. Used as a URL-stable alias so external links, push notifications, deep links, and standalone-PWA launches can reference /app/<surface> without baking in the canonical path (which may evolve). The proxy resolves the alias before the auth gate so the alias itself stays anonymous-accessible; the canonical downstream route still applies its own auth and shell policy. The page component remains as the route-level fallback and returns null after issuing the same redirect.

Entry points#

  • PWA start_url (per manifest.json) routes to /?surface=pwa, but /app/[surface] is the canonical-alias form used in deep links outside the app shell
  • Push notification deep links with surface-stable targets
  • Marketing site linking into specific shell surfaces by name
  • /app/search/... rewritesparams.surface === 'search' maps to 'explore' (search lives inside Explore)
  • Email / SMS deep links referencing surface by name rather than path

Layout regions#

None. The page returns null after issuing a server-side redirect().

States#

  • Valid surfaceparams.surface matches isWebShellRoute (home | explore | activity | library | profile | studio) or 'search' (rewrites to explore)
    • Behavior: build target URL via WEB_SHELL_ROUTE_PATHS[targetSurface], append serialized search params + path joined; redirect(target)
  • Unknown surfaceparams.surface not in the allowlist → standard not-found.tsx shell via proxy rewrite before auth.
  • No path segmentsparams.path empty; target is just WEB_SHELL_ROUTE_PATHS[targetSurface] (+ search params if any)
  • Path segments presentparams.path = ['a','b']targetParams.set('path', 'a/b'); appended to target as ?path=a/b
  • Search params present — preserved via toSearchParams (strings set; arrays appended)
  • ?surface=pwa query (or any other key) — passed through to the target as a query param

Verified 2026-06-30 by canonical-deep-links.spec.ts against local web + BFF dev servers: every V1 shell surface alias lands on its canonical route with path/query preservation, search lands on Explore, studio lands in the admin shell, and unknown aliases render the standard not-found page. The same contracts are unit-covered in middleware-public-paths.test.ts, including the anonymous-before-auth proxy redirect and raw Cache-Control: no-store header.

Interactions#

None visible. The route is a one-shot server redirect — no user- facing interaction. Users land here from external links and are immediately taken to the canonical shell route.

Data & contracts#

  • Reads: none (no data fetch)
  • Writes: none
  • Realtime: none
  • Caching: proxy redirect sends Cache-Control: no-store; SW does not cache redirects per default policy
  • Auth/role check: the alias itself is anonymous-accessible because proxy resolution runs before the auth gate — redirect target is whatever the downstream shell route enforces (typically signed-in for /, /explore, /activity, /library, /profile; /studio resolves to the admin shell)

Surface allowlist#

From apps/oshun/web/src/navigation/routes.ts (WEB_SHELL_ROUTE_PATHS):

surface param redirect target
home /
explore /explore
activity /activity
library /library
profile /profile
studio /studio
search /explore (rewrite)
anything else notFound() → 404 page

Note: isWebShellRoute is the canonical type guard. 'search' is rewritten to 'explore' as a special-case alias because search lives inside Explore in V1's IA.

Cross-references#

Open questions / known gaps#

  • Document the external systems that link to /app/<surface>/... so a future routes-module change (renaming or adding a shell surface) updates them in lock-step
  • Confirm whether unknown aliases yield the same UI as direct notFound() from another route — verified 2026-06-30; proxy rewrites to /_not-found and Playwright asserts the standard "Page not found" heading.
  • Verify cache headers on the redirect response — verified 2026-06-30; proxy alias redirects set Cache-Control: no-store so the alias stays redirectable if shell routes move.
  • Should 'search' mapping to 'explore' add a ?focus=search or similar hint? Today the path goes to /explore as-is; the user may expect the search input to be focused on landing