---
path: /profile
surface: customer
domain: account
auth: signed-in
source: apps/oshun/web/src/app/profile/page.tsx
status: walked
last_walked:
  '2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data
  (2xx), console/page-errors, expected content, screenshot verified; live
  screen-reader, touch, offline, and telemetry-delivery checks pending a manual
  AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md'
  '2026-06-27 Codex route-state addendum — default signed-in profile, noindex
  metadata, privacy/data-rights highlight deep links, loading/offline/partial
  outage/error shell fallbacks, standalone PWA restore, anonymous auth gate,
  sign-out affordance, and upgrade-intent billing card are covered by the E2E
  references below.'
  '2026-06-28 Codex shortcut-map addendum — every account shortcut href and
  `WebProfileRoutePath` highlight target is enumerated here and verified in
  `profile-settings-flows.spec.ts` against the real `/profile` route.'
---

# Profile

## Purpose

The signed-in user's account hub — "your account, billing, privacy, devices, and
what OSHUN remembers." Renders inside the standard `ShellLayout` with
`active="profile"`, composing the `ProfileSettingsPanel` plus the shared
shell-route surface content resolver. Used as the landing for profile deep-links
and as the host for shell-driven upgrade intents.

## Entry points

- Shell nav: Profile (`active="profile"`)
- Direct URL / bookmark — yes (auth required)
- Upgrade-intent deep link: `?intent=<intent>&domain=<id>&requiredTier=<tier>`
  surfaces an upgrade-intent context to `ProfileSettingsPanel`
- Sub-route deep link: any `WebProfileRoutePath` resolved by
  `resolveWebProfileRoutePath(searchParams)` (e.g., billing, devices, privacy,
  account — each highlights a specific row)
- Cross-domain settings deep links (e.g., from `/aaa-upgrade`)
- Sign-out / sign-in confirmation handoff

## Layout regions

`page.tsx` mounts `<ShellLayout active="profile" breadcrumbs=[Home, Profile]>`
and inside it calls
`resolveShellRouteSurfaceContent({ surface: 'profile', searchParams, children: <ProfileSettingsPanel highlightPath={highlightPath} upgradeIntent={upgradeIntent} /> })`
— the resolver handles offline / fallback / unavailable framings, falling
through to the panel as children when the surface is healthy.

- **Shell header**: standard shell header with Profile active
- **Breadcrumbs**: Home → Profile (passed in via `ShellLayout` prop)
- **Surface resolver wrapper**: `resolveShellRouteSurfaceContent` decides
  whether to render the profile panel or a shell-route fallback (offline /
  unavailable / reason)
- **ProfileSettingsPanel**: the actual settings UI (account, billing, privacy,
  devices, memory, etc.) — composition lives in
  `apps/oshun/web/src/components/ProfileSettingsPanel.tsx`. The panel uses
  `highlightPath` to open / accent a specific row and `upgradeIntent` to surface
  an inline upgrade context

## States

- [x] **Loading** — handled by the resolver / panel; no explicit `loading.tsx`
      at this segment
- [x] **Default (no params)** — full settings panel renders
- [x] **Highlight path** — `searchParams.path` (or equivalent) resolves via
      `resolveWebProfileRoutePath`; the panel accents the matching sub-section
- [x] **Upgrade intent active** — when `intent`, `domain`, or `requiredTier` are
      present, the panel renders an upgrade-intent context above (or inside) the
      relevant row
- [x] **Offline** — shell-route resolver returns the offline fallback
- [x] **Unavailable / reason** — shell-route resolver returns partial outage,
      degraded, retry, and missing-segment fallbacks for the profile surface
- [x] **Error (recoverable)** — resolver error state keeps profile inside the
      shell with primary and secondary recovery affordances; row-level profile
      validation errors remain panel-owned and are covered by the relevant
      interaction specs
- [x] **Anonymous user** — middleware redirects to `/welcome`
- [x] **Standalone PWA** — shell restores `/profile` from standalone launch and
      keeps the mobile nav within the viewport

## Interactions

Interactions live inside `ProfileSettingsPanel`; the page itself only wires
highlight + upgrade-intent state. Current route-state coverage is:

- `apps/oshun/web/e2e/profile-settings-flows.spec.ts` — signed-in default
  profile, canonical/noindex metadata, privacy/data-rights highlight routing,
  full account shortcut href + highlight-target matrix, loading/offline/
  partial-outage/error resolver cards, standalone PWA restore, profile writes,
  preferences, data export, and legal navigation
- `apps/oshun/web/e2e/profile-channel-bindings.spec.ts` — "Notification
  channels" email/SMS binding lifecycle on the real `/profile` surface:
  address/code issue, BFF read-back, route reload persistence, revoke read-back,
  wrong-code non-verification, and 390 px 44 px/no-overflow controls with the
  real service worker enabled
- `apps/oshun/web/e2e/shell-surface-states.spec.ts` and
  `apps/oshun/web/e2e/shell-route-signoff.spec.ts` — exhaustive shell fallback
  matrix for the profile surface across loading, empty, offline, partial outage,
  degraded, retry, partial response, and error states
- `apps/oshun/web/e2e/customer-paywall-upgrade-surfaces.spec.ts` — `/profile`
  billing upgrade intent for free/pro/premium entitlement states and the
  no-intent control
- `apps/oshun/web/e2e/auth-entry-flows.spec.ts` — anonymous `/profile` redirect,
  signup/onboarding handoff back to `/profile`, and sign-out control coverage

### Surface-resolver framing

- [x] **Offline framing** — surfaces a network-issue message instead of the
      panel
- [x] **Unavailable framing** — surfaces partial outage / degraded shell
      recovery copy and trace/missing-segment notes instead of the panel

### Highlight target (panel-level)

- [x] **Highlight row open/accent** — when `highlightPath` is set, the panel
      opens and accents the corresponding row (account / billing / devices /
      privacy / memory / safety etc.)

### Upgrade-intent context (panel-level)

- [x] **Upgrade intent banner** — when any of `intent`, `domain`, `requiredTier`
      is set, the panel surfaces an in-context upgrade affordance referencing
      the intent

### Sub-route navigation

`ProfileSettingsPanel` exposes the account shortcut rail
(`data-profile-account-links`) with this route/highlight contract:

| Shortcut label     | Hook id           | Href                            | Highlight result                      |
| ------------------ | ----------------- | ------------------------------- | ------------------------------------- |
| Account            | `account`         | `/profile?path=account`         | Account shortcut active; no section   |
| Activity & stats   | `stats`           | `/profile?path=stats`           | Domain statistics section force-open  |
| Plans & billing    | `billing`         | `/profile?path=billing`         | Plans and billing section force-open  |
| Personalization    | `personalization` | `/profile?path=personalization` | Personalization section force-open    |
| Privacy & access   | `privacy`         | `/profile?path=privacy`         | Privacy and access section force-open |
| Data rights        | `data-rights`     | `/profile?path=data-rights`     | Data requests section force-open      |
| Devices & sessions | `sessions`        | `/profile?path=sessions`        | Devices & sessions section force-open |
| Operator access    | `operator-access` | `/profile?path=operator-access` | Operator access section force-open    |

The resolver also accepts alias-only highlight values that do not have rail
shortcuts: `billing-support` → billing, `preferences` → personalization,
`assistant` / `assistant-controls` / `memory` → Assistant and memory,
`notifications` → Notifications, `calendar` / `calendar-sync` → Calendar sync,
`consent` / `consent-controls` → Consent center, and `onboarding` /
`onboarding-setup` → Onboarding setup.

## Data & contracts

- **Reads**:
  - `resolveWebProfileRoutePath(searchParams)` →
    `WebProfileRoutePath | undefined`
  - `WEB_SHELL_ROUTE_PATHS.home` for the breadcrumb
  - `ProfileSettingsPanel` reads BFF account/billing/devices endpoints (verify
    exact list on walk)
- **Writes**: panel-driven (account update, billing, devices, etc.)
- **Realtime**: device sessions list may subscribe to a session feed (verify)
- **Caching**: SSR shell; SWR within the client panel
- **Auth/role check**: signed-in; middleware enforces
- **Metadata**: `title: 'Profile'`,
  `description: 'Your account, billing, privacy, devices, and what OSHUN remembers.'`,
  `robots: { index: false, follow: false }`, `alternates.canonical: '/profile'`

## Cross-references

- Shell: [`shell/01-app-shell.md`](../../shell/01-app-shell.md),
  [`shell/04-auth-session.md`](../../shell/04-auth-session.md)
- Sub-routes:
  - [`profile-memory.md`](./profile-memory.md)
  - [`profile-safety.md`](./profile-safety.md)
  - [`billing-crypto.md`](./billing-crypto.md)
- Upgrade flow: [`../13-system/aaa-upgrade.md`](../13-system/aaa-upgrade.md)
- E2E coverage:
  - `apps/oshun/web/e2e/profile-settings-flows.spec.ts`
  - `apps/oshun/web/e2e/profile-channel-bindings.spec.ts`
  - `apps/oshun/web/e2e/shell-surface-states.spec.ts`
  - `apps/oshun/web/e2e/shell-route-signoff.spec.ts`
  - `apps/oshun/web/e2e/customer-paywall-upgrade-surfaces.spec.ts`
  - `apps/oshun/web/e2e/auth-entry-flows.spec.ts`
- Component sources:
  - `apps/oshun/web/src/components/ProfileSettingsPanel.tsx`
  - `apps/oshun/web/src/components/ShellLayout.tsx`
  - `apps/oshun/web/src/app/shellRouteState.ts`
  - `apps/oshun/web/src/navigation/routes.ts`

## Open questions / known gaps

- [x] Enumerate every settings row exposed by `ProfileSettingsPanel` and map to
      `WebProfileRoutePath` highlight values — the shortcut matrix above is
      backed by `profile-settings-flows.spec.ts`, and alias-only highlight
      values are documented separately.
- [ ] Document the full set of supported `intent` values for `upgradeIntent` and
      their tier-requirement mapping
- [x] Confirm whether the panel exposes a sign-out affordance here or relies on
      the shell header — the panel exposes `data-profile-signout-btn`, covered
      in `auth-entry-flows.spec.ts` and `shared-shell-entry-routes.spec.ts`
- [ ] Confirm device-session realtime updates
- [ ] Document offline behaviour for in-flight settings edits
