V1 Web PWA · Surface walkthrough

Assistant entry route

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

walked
8sections8 minread

On this page

Context. surface customer · domain shell-routing · route /assistant · auth signed-in · source apps/oshun/web/src/app/assistant/page.tsx

Last walked. 2026-06-24 by Codex (explicit button dispatch covered), against commit HEAD' '2026-06-25 by Codex (mobile tap target, preview actions, and proxy gate covered), against commit HEAD

Purpose#

/assistant is a thin shell-utility entry route whose only job is to invoke the shell's contextual OSHUN assistant from a bookmarkable URL. It renders a one-card "Shell utility / Assistant" panel inside the customer ShellLayout, auto-fires the oshun:assistant-open event on mount, and offers a single "Open assistant" button that re-fires the same event. The actual conversation surface (dock / overlay) is owned by ShellLayout, not by this route — see ../../shell/12-overlays-and-panels.md.

Entry points#

  • Direct URL / bookmark/assistant; the canonical use. Marked robots: { index: false, follow: false } in metadata (apps/oshun/web/src/app/assistant/page.tsx lines 8–14), so it is intentionally non-indexed.
  • Shell assistant surface mapbuildShellSurfaceLiveHref('assistant') returns /assistant (apps/oshun/web/src/lib/shell/shellSurfaceState.ts lines 57–58); any ShellSurfaceStatePreview for the assistant surface whose primary action is "Open live Assistant" links here (apps/oshun/web/src/components/ShellSurfaceStatePreview.tsx, primary Link href={primaryHref}).
  • Shell state preview deep links/assistant?shellState=<mode> renders the preview card instead of the live entry (see States).
  • Not a primary shell-nav tab. The page mounts with active="home" and a breadcrumb Home › Assistant (apps/oshun/web/src/app/assistant/page.tsx lines 22–25); there is no bottom-nav / sidebar item labelled "Assistant" wired to this route in the source read.

Layout regions#

page.tsx is a server component: ShellLayout active="home" wrapping resolveShellRouteSurfaceContent({ surface: 'assistant', searchParams, children: <AssistantEntryRoute /> }). In live mode the child AssistantEntryRoute renders; in any preview mode the ShellSurfaceStatePreview renders instead (apps/oshun/web/src/app/shellRouteState.tsx lines 11–27).

  • Header: customer shell header + breadcrumbs Home › Assistant (the second crumb has no href, so it is the current-page label).
  • Main (live mode)AssistantEntryRoute (apps/oshun/web/src/app/assistant/AssistantEntryRoute.tsx), a single centered <section data-shell-entry-route="assistant"> (max-width 760) containing:
    • Eyebrow: monospace uppercase Shell utility (lines 32–43).
    • Title: serif Assistant <h1> (lines 44–55).
    • Subhead: Current shell context is ready for navigation, grounded answers, and domain actions. (lines 56–58).
    • Primary button: Open assistant with a MessageSquarePlus icon (<button data-assistant-entry-route-open>, lines 61–89).
  • Main (preview mode)ShellSurfaceStatePreview surface="assistant": a state card with eyebrow Assistant state, a serif headline, a mode badge (e.g. Loading / Empty / Offline / Degraded), an informational/loading body, a notes rail, and two footer links Open live Assistant and Return Home (ShellSurfaceStatePreview.tsx; copy in the assistant entries of SURFACE_STATE_COPY lines 227–247 and SURFACE_STATE_SUPPORT lines 414–428).
  • Assistant dock / overlay — owned by ShellLayout; opens in response to the oshun:assistant-open event this route dispatches (ShellLayout.tsx handleAssistantOpen, lines 1286–1313). Surfaced as [data-shell-assistant-dock] or [data-assistant-presentation="overlay"].
  • Footer / sticky bottom: shell-provided; not defined by this route.

States#

The live entry card (AssistantEntryRoute) is static — it reads no data and has no async fetch, so most data-driven states cannot occur on this route itself; they belong to the assistant dock the shell owns. The preview-card states are reachable via the ?shellState= query.

  • Loading — N/A for the live entry card (no async data; page.tsx only await props.searchParams, no fetch). The assistant surface loading preview is reachable at /assistant?shellState=loading (ShellSurfaceStatePreview, badge Loading, headline "Restoring assistant context", data-shell-surface-state-context="loading").
  • Empty — N/A for the live entry card (it always renders the same fixed copy). The assistant empty preview is reachable at /assistant?shellState=empty (headline "Assistant has no context yet", body from SURFACE_STATE_COPY.assistant.emptyMessage).
  • Populated (short) — live entry card renders its single fixed panel (eyebrow + title + subhead + one button). There is no item list.
  • Populated (long) — N/A; this route renders no collection — no list, pagination, or virtualization exists in AssistantEntryRoute.
  • Error (recoverable) — N/A for the live entry card (no query to fail). The assistant retry preview is reachable at /assistant?shellState=retry (badge Retrying, retry-after note). A genuine recoverable error would surface inside the shell-owned assistant dock, not here.
  • Error (unrecoverable) — N/A for the live entry card. The assistant degraded/error preview is reachable at /assistant?shellState=error (or =degraded): badge Degraded, error code ASSISTANT_SURFACE_LOAD_FAILED, BFF trace ASSISTANT_BFF_PARTIAL_… (SURFACE_STATE_COPY.assistant, lines 242–243). No 403/500 boundary is defined by this route file itself.
  • Offline — N/A for the live entry card (no network use). The assistant offline preview is reachable at /assistant?shellState=offline (headline "Assistant is limited while offline", body from offlineMessage).
  • Gated — auth-gating is enforced by apps/oshun/web/src/proxy.ts. /assistant is not in PUBLIC_PATHS/PUBLIC_PREFIXES, so anonymous hits redirect to /welcome?redirect=%2Fassistant; signed-in requests pass through. Covered by middleware-public-paths.test.ts.
  • Standalone PWA — renders inside the customer ShellLayout like the other shell-entry routes; no route-specific PWA handling. Verify safe-area / URL-bar behavior during a live walk.

Interactions#

  • Open assistant (button — <button type="button" data-assistant-entry-route-open>, AssistantEntryRoute.tsx lines 61–89)
    • Function: on click, calls dispatchOshunAssistantOpen({ source: 'assistant-route', prompt: 'Help me choose the next useful OSHUN route.' }) (@/navigation/assistant-entry), which dispatches the oshun:assistant-open (OSHUN_ASSISTANT_OPEN_EVENT) window CustomEvent. ShellLayout's handleAssistantOpen opens the assistant at the customer inline point with the prompt as seedMessage and entrySourceOverride = 'assistant-route' (ShellLayout.tsx lines 1286–1313).
    • Keyboard: native <button> — Enter/Space activate; single interactive element on the route, first in tab order within the section.
    • Screen reader: announces "Open assistant, button" (visible text label; no aria-label override in source).
    • Touch target: minHeight: 44, centered inline-flex, horizontal padding 16px; measured at a 390 px viewport by shared-shell-entry-routes.spec.ts.
    • Mobile (≤ 640 px): width: 'fit-content'; no route-specific layout delta — the 760px-max section centers, and the button target is measured at 390 px.
    • Disabled when: never — no disabled state in source.
    • Offline behavior: no-op beyond firing the window event; the route itself does no network I/O. Whether the shell-owned dock degrades offline is documented in ../../shell/12-overlays-and-panels.md.
    • Telemetry: no data-telemetry-* attribute on this button in source; the invocation carries source: 'assistant-route' into the assistant open handler, which is the de-facto entry-source signal. No explicit analytics event name is emitted from this file.
  • Auto-open on mount (effect — AssistantEntryRoute.tsx lines 9–18, not a clickable element)
    • Function: a useEffect schedules window.setTimeout(…, 0) that fires the same dispatchOshunAssistantOpen({ source: 'assistant-route', prompt: 'Help me choose the next useful OSHUN route.' }), so landing on /assistant opens the assistant automatically; the timer is cleared on unmount.
    • This is why the existing spec asserts the dock/overlay becomes visible just from navigating to the route (see Data & contracts / spec coverage).

Preview-card interactions (only in ?shellState=<mode> modes)#

  • Open live Assistant (Link, [data-shell-surface-state-primary])
    • Function: navigates to buildShellSurfaceLiveHref('assistant') = /assistant (the live entry, dropping the shellState param). Covered by shell-surface-states.spec.ts.
  • Return Home (Link, [data-shell-surface-state-secondary])
    • Function: navigates to SURFACE_STATE_COPY.assistant.secondaryHref = / (label Return Home, ShellSurfaceStatePreview.tsx lines 245–246). Covered by shell-surface-states.spec.ts.

Data & contracts#

  • Reads: none from this route. page.tsx only await props.searchParams; AssistantEntryRoute reads no data; the preview card reads only static copy tables in ShellSurfaceStatePreview.tsx.
  • Writes: none via HTTP. The only side effect is the in-page window CustomEvent oshun:assistant-open (OSHUN_ASSISTANT_OPEN_EVENT, @/navigation/assistant-entry) carrying { source: 'assistant-route', prompt: 'Help me choose the next useful OSHUN route.' }.
  • Realtime: none on this route. Any streamed assistant transcript belongs to the shell-owned dock, not here.
  • Caching: server-rendered; the route component is static apart from the searchParams await. No SWR/ISR fetch in source.
  • Auth/role check: signed-in route. The gate lives in apps/oshun/web/src/proxy.ts: /assistant is not public, anonymous requests redirect to /welcome?redirect=%2Fassistant, and signed-in requests pass through. The page itself performs no role/feature-flag check.

Contracts: this route defines no BFF contract; OshunAssistantEntryDetail (apps/oshun/web/src/navigation/assistant-entry.ts) is the event payload shape, and AssistantInteractionModeId comes from @oshun/shell-assistant/interaction-modes.

Cross-references#

  • Shell overlays / assistant dock: ../../shell/12-overlays-and-panels.md
  • App shell: ../../shell/01-app-shell.md
  • Sibling shell-entry routes in this folder: messages.md · switcher.md · activity.md · explore.md
  • Component sources:
    • apps/oshun/web/src/app/assistant/page.tsx
    • apps/oshun/web/src/app/assistant/AssistantEntryRoute.tsx
    • apps/oshun/web/src/app/shellRouteState.tsx
    • apps/oshun/web/src/navigation/assistant-entry.ts
    • apps/oshun/web/src/components/ShellSurfaceStatePreview.tsx
    • apps/oshun/web/src/components/ShellLayout.tsx (lines 1286–1343)
  • E2E specs touching this route:
    • apps/oshun/web/e2e/shared-shell-entry-routes.spec.ts — loads /assistant, asserts [data-shell-entry-route="assistant"], "Current shell context|Open assistant" body text, and that [data-shell-assistant-dock], [data-assistant-presentation="overlay"] becomes visible (auto-open). The 2026-06-24 deepening installs a browser listener for oshun:assistant-open, dismisses the auto-opened assistant surface, clicks [data-assistant-entry-route-open], and asserts the explicit click re-dispatches { source: 'assistant-route', prompt: 'Help me choose the next useful OSHUN route.' } while reopening the assistant surface. The 2026-06-25 deepening measures the explicit button as at least 44×44 CSS px at a 390 px viewport.
    • apps/oshun/web/e2e/shell-surface-states.spec.ts — drives /assistant?shellState=<mode> across all 8 preview modes and asserts the state card, badge, rail note, and primary/secondary actions; 2026-06-25 clicks the assistant preview primary link to /assistant and secondary link to /.
    • apps/oshun/web/src/__tests__/middleware-public-paths.test.ts — asserts anonymous /assistant redirects with the original deep link preserved and signed-in /assistant passes through.

Open questions / known gaps#

  • Auth enforcement is in proxy.ts. Anonymous /assistant redirects to /welcome?redirect=%2Fassistant; signed-in /assistant passes. Covered by middleware-public-paths.test.ts.
  • Touch-target size. The Open assistant button now has minHeight: 44 and is measured at a 390 px viewport.
  • No explicit telemetry event is emitted from the button or the auto-open effect (only source: 'assistant-route' rides the event). Confirm whether the shell's assistant-open handler emits an analytics event downstream, or whether an entry-source telemetry event is expected here.
  • Auto-open + manual button while already open. Landing fires the open event on mount and the button can fire it again. The covered path dismisses the assistant surface first, then proves the button reopens it; the already-open re-seed/dedupe behavior is still not specified.
  • requestedModeId is never set by this route's dispatch calls, so the assistant opens in its default interaction mode. Confirm that is intended for the dedicated /assistant entry.