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=pwaquery 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.
- Root —
apps/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:
-
childrenalways 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 withreset()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 middleware → proxy; 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 routes —
AAA_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
/welcomewith?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/dailyor/v1/nisaba/passages/:idhydration before asserting route posture and screenshots.
Cross-references#
- 01-app-shell.md
- 03-pwa-behavior.md — service worker intercepts navigation
- 04-auth-session.md — what the proxy checks against
Open questions / known gaps#
- Document every per-segment
layout.tsxthat exists today (apps/oshun/web/src/app/**/layout.tsx) — first sweep should produce that inventory