V1 Web PWA · Surface walkthrough

Shell: Routing and layouts

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

unspecified
9sections3 minread

On this page

Source: apps/oshun/web/src/app/, apps/oshun/web/src/proxy.ts (Next 16 — exports proxy + a config matcher; there is no middleware.ts)

How Next.js route groups, segment layouts, error boundaries, and loading states compose under the root layout. Walk this when you suspect a bug is in the layout stack, not the leaf page.

Route groups in app/#

  • (workspace)/ — workspace surface; catch-all at /workspace/[[...path]]
  • All other top-level segments are addressable routes.

Catch-all routes#

  • /app/[surface]/[[...path]] — generic surface proxy; honors ?surface=pwa query for installed-PWA cold-start
  • /(workspace)/workspace/[[...path]] — workspace shell proxy
  • /d/[domain]/[[...path]] — short-link entry; redirects to canonical domain route
  • /domains/[domainId] — legacy domain landing; redirect or render?

Layout files#

Walk the layout file (if any) for each top-level segment. Each segment that has a layout.tsx defines header/nav state for everything beneath.

  • Rootapps/oshun/web/src/app/layout.tsx (shell composition; see 01-app-shell.md)
  • /(workspace)/ — workspace shell layout
  • /studio/ — studio-specific layout (if present)
  • /operator/ — operator-specific layout (if present)
  • Domain layouts/tara/layout.tsx, /arete/layout.tsx, etc., where present, set domain chrome

For each layout, verify:

  • children always renders — no conditional return that drops the slot
  • generateMetadata (if used) returns valid metadata
  • Suspense boundary at expected depth — loading.tsx triggers, not a blank screen
  • Error boundary at expected depth — error.tsx catches, not the root global-error

Special files#

  • app/error.tsx — segment-level error boundary; shows recoverable error UI with reset() button
  • app/global-error.tsx — catastrophic error UI (replaces <html>); logs to telemetry; offers reload
  • app/not-found.tsx — 404 UI; preserves shell chrome where possible; includes search affordance
  • app/loading.tsx (if present) — skeleton matches final layout

Proxy (proxy.ts)#

Authentication and entitlement gates run at the edge before page rendering. (Next 16 renamed middlewareproxy; the file exports proxy and a config matcher. There is no middleware.ts.)

  • Public path allowlist/welcome, /landing, /legal/*, /lilith, /system, /status, /v2, /v3/landing, opengraph/twitter images
  • Public prefix allowlist/_next, /api, /icons, /images, /fonts, /screenshots, /scene, /studio/compose
  • AAA-only studio routesAAA_ONLY_STUDIO_ROUTES; non-AAA users get gated UI
  • OSHUN_PWA_RELAUNCH_TARGET_COOKIE — preserved across SSR redirects
  • Auth bounce — sessionless user on a gated route → redirect to /welcome with ?redirect= preserving the deep link

Route prefetching#

  • Shell route links prefetch on hover/focus (Next.js default)
  • Domain switcher prefetches the destination domain layout
  • <MobileBottomNav> does not aggressively prefetch (battery cost on cellular)

E2E coverage#

  • Backed by apps/oshun/web/e2e/shell-route-signoff.spec.ts — authenticated shell route signoff for Home, Explore, Library, Activity, Search, Profile, profile notifications, the shell notifications drawer, contextual assistant dock, and shell loading/empty/offline/error previews. The 2026-06-25 update removed the legacy Nisaba workspace fixture from this signoff path: the Nisaba compare and passage evidence now uses a real per-session notebook seed/readback through /v1/nisaba/notebooks, clears stale offline Nisaba snapshots, and waits for live /v1/nisaba/passages/daily or /v1/nisaba/passages/:id hydration before asserting route posture and screenshots.

Cross-references#

Open questions / known gaps#

  • Document every per-segment layout.tsx that exists today (apps/oshun/web/src/app/**/layout.tsx) — first sweep should produce that inventory