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. Markedrobots: { index: false, follow: false }inmetadata(apps/oshun/web/src/app/assistant/page.tsxlines 8–14), so it is intentionally non-indexed. - Shell assistant surface map —
buildShellSurfaceLiveHref('assistant')returns/assistant(apps/oshun/web/src/lib/shell/shellSurfaceState.tslines 57–58); anyShellSurfaceStatePreviewfor theassistantsurface whose primary action is "Open live Assistant" links here (apps/oshun/web/src/components/ShellSurfaceStatePreview.tsx, primaryLinkhref={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 breadcrumbHome › Assistant(apps/oshun/web/src/app/assistant/page.tsxlines 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 nohref, 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 assistantwith aMessageSquarePlusicon (<button data-assistant-entry-route-open>, lines 61–89).
- Eyebrow: monospace uppercase
- Main (preview mode) —
ShellSurfaceStatePreview surface="assistant": a state card with eyebrowAssistant state, a serif headline, a mode badge (e.g.Loading/Empty/Offline/Degraded), an informational/loading body, a notes rail, and two footer linksOpen live AssistantandReturn Home(ShellSurfaceStatePreview.tsx; copy in theassistantentries ofSURFACE_STATE_COPYlines 227–247 andSURFACE_STATE_SUPPORTlines 414–428). - Assistant dock / overlay — owned by
ShellLayout; opens in response to theoshun:assistant-openevent this route dispatches (ShellLayout.tsxhandleAssistantOpen, 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.tsxonlyawait props.searchParams, no fetch). The assistant surface loading preview is reachable at/assistant?shellState=loading(ShellSurfaceStatePreview, badgeLoading, 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 fromSURFACE_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(badgeRetrying,retry-afternote). 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): badgeDegraded, error codeASSISTANT_SURFACE_LOAD_FAILED, BFF traceASSISTANT_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 fromofflineMessage). - Gated — auth-gating is enforced by
apps/oshun/web/src/proxy.ts./assistantis not inPUBLIC_PATHS/PUBLIC_PREFIXES, so anonymous hits redirect to/welcome?redirect=%2Fassistant; signed-in requests pass through. Covered bymiddleware-public-paths.test.ts. - Standalone PWA — renders inside the customer
ShellLayoutlike 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.tsxlines 61–89)- Function: on click, calls
dispatchOshunAssistantOpen({ source: 'assistant-route', prompt: 'Help me choose the next useful OSHUN route.' })(@/navigation/assistant-entry), which dispatches theoshun:assistant-open(OSHUN_ASSISTANT_OPEN_EVENT) window CustomEvent.ShellLayout'shandleAssistantOpenopens the assistant at the customer inline point with the prompt asseedMessageandentrySourceOverride = 'assistant-route'(ShellLayout.tsxlines 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-labeloverride in source). - Touch target:
minHeight: 44, centeredinline-flex, horizontal padding16px; measured at a 390 px viewport byshared-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 carriessource: '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.
- Function: on click, calls
- Auto-open on mount (effect —
AssistantEntryRoute.tsxlines 9–18, not a clickable element)- Function: a
useEffectscheduleswindow.setTimeout(…, 0)that fires the samedispatchOshunAssistantOpen({ source: 'assistant-route', prompt: 'Help me choose the next useful OSHUN route.' }), so landing on/assistantopens 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).
- Function: a
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 theshellStateparam). Covered byshell-surface-states.spec.ts.
- Function: navigates to
- Return Home (
Link,[data-shell-surface-state-secondary])- Function: navigates to
SURFACE_STATE_COPY.assistant.secondaryHref=/(labelReturn Home,ShellSurfaceStatePreview.tsxlines 245–246). Covered byshell-surface-states.spec.ts.
- Function: navigates to
Data & contracts#
- Reads: none from this route.
page.tsxonlyawait props.searchParams;AssistantEntryRoutereads no data; the preview card reads only static copy tables inShellSurfaceStatePreview.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
searchParamsawait. No SWR/ISR fetch in source. - Auth/role check: signed-in route. The gate lives in
apps/oshun/web/src/proxy.ts:/assistantis 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.tsxapps/oshun/web/src/app/assistant/AssistantEntryRoute.tsxapps/oshun/web/src/app/shellRouteState.tsxapps/oshun/web/src/navigation/assistant-entry.tsapps/oshun/web/src/components/ShellSurfaceStatePreview.tsxapps/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 foroshun: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/assistantand secondary link to/.apps/oshun/web/src/__tests__/middleware-public-paths.test.ts— asserts anonymous/assistantredirects with the original deep link preserved and signed-in/assistantpasses through.
Open questions / known gaps#
- Auth enforcement is in
proxy.ts. Anonymous/assistantredirects to/welcome?redirect=%2Fassistant; signed-in/assistantpasses. Covered bymiddleware-public-paths.test.ts. - Touch-target size. The
Open assistantbutton now hasminHeight: 44and 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.
-
requestedModeIdis never set by this route's dispatch calls, so the assistant opens in its default interaction mode. Confirm that is intended for the dedicated/assistantentry.