---
path: /assistant
surface: customer
domain: shell-routing
auth: signed-in
source: apps/oshun/web/src/app/assistant/page.tsx
status: walked
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'
---

# Assistant entry route

## 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`](../../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 map** — `buildShellSurfaceLiveHref('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.

- [x] **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"`).
- [x] **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`).
- [x] **Populated (short)** — live entry card renders its single fixed panel
      (eyebrow + title + subhead + one button). There is no item list.
- [x] **Populated (long)** — N/A; this route renders no collection — no list,
      pagination, or virtualization exists in `AssistantEntryRoute`.
- [x] **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.
- [x] **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.
- [x] **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`).
- [x] **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

- [x] **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`](../../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.
- [x] **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)

- [x] **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`.
- [x] **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`](../../shell/12-overlays-and-panels.md)
- App shell: [`../../shell/01-app-shell.md`](../../shell/01-app-shell.md)
- Sibling shell-entry routes in this folder: [`messages.md`](./messages.md) ·
  [`switcher.md`](./switcher.md) · [`activity.md`](./activity.md) ·
  [`explore.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

- [x] **Auth enforcement is in `proxy.ts`.** Anonymous `/assistant` redirects to
      `/welcome?redirect=%2Fassistant`; signed-in `/assistant` passes. Covered
      by `middleware-public-paths.test.ts`.
- [x] **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.
