---
path: /domain/[domainId]
surface: mobile
domain: shell-routing
auth: anon (entry) | gated per-domain entitlement tier (paywall on lock)
source: apps/oshun/mobile/app/domain/[domainId].tsx
status: drafted
last_walked: 'not yet walked'
---

# Domain detail (mobile — polymorphic domain route)

## Purpose

`/domain/[domainId]` is the single polymorphic domain screen. It resolves
`domainId` (tara, veritas, nyx, arete, nisaba, metis), evaluates entitlement
access, and renders a domain-specific entry hero plus the full domain content
(continuity, claims, sky events, check-ins, passages, etc.) inside one
`ScrollView`. It also handles startup loading, access-locked, offline-cache,
temporarily-unavailable, planned-preview, and deep-link-focus states. At ~14k
lines it is by far the largest mobile screen.

## Entry points

- `buildDomainRouteHref(domainId, {...})` from Home, Explore, Activity, Library,
  Profile (and inter-domain switches). Params: `origin`, `stack`, `path`, `ref`,
  `unavailable`, `reason`, `offline`, plus per-domain hydration query.
- Notification routing / deep links resolving to a domain path.
- Stack screen `domain/[domainId]` (title "Domain", headerBackTitle "Back",
  gesture-enabled) declared in `_layout.tsx`.

## Layout regions

- **Header**: stack header titled "Domain" with Back; gesture back enabled.
- **Hero / above-the-fold**: domain-specific `DomainEntryHero` (eyebrow, title,
  body, stateLabel, metaItems, primary + secondary actions) — variants per
  domain (`mobile-domain-entry-hero-<domain>`).
- **Main**: `mobile-domain-screen-<domain>` ScrollView — badge, title, route
  intro body, route-context chips, planned-preview card (Metis), deep-link-focus
  card, E2E harnesses (Tara, flagged), then the domain-specific content blocks.
- **Alternate full-screen states**: loading skeleton; access-locked; offline
  fallback (Tara/Veritas/Nisaba cached lists or generic recovery panel);
  unavailable recovery panel + domain switcher.

## States

- [ ] **Loading** — `startupLoading` renders skeleton badge/title/body/cards +
      "Loading <title> module...".
- [ ] **Empty** — per-domain empty handling inside content blocks (e.g. no saved
      objects); hero still leads with a primary action.
- [ ] **Populated (short/long)** — full domain content; long scroll.
- [ ] **Error (recoverable)** — `domainUnavailable` →
      `mobile-domain-unavailable-recovery-panel` with Retry domain launch + "Try
      another domain" switcher (filtered to allowed/available domains).
- [ ] **Error (unrecoverable)** — caught by shell error boundary; unknown
      `domainId` falls through to `unknown` testIDs.
- [ ] **Offline** — `offlineMode` (≠ nyx) → cached Tara meditations / Veritas
      briefings / Nisaba passages+notes lists, each launchable from cache;
      otherwise generic `mobile-domain-offline-<domain>` recovery panel. Nyx has
      its own offline sky-map path.
- [ ] **Permissions-denied** — N/A at this screen (capture is on Home).
- [ ] **Gated** — `domainAccess.allowed === false` → "<title> access locked"
      with current/required tier, reason, **Upgrade plan** (→ Profile paywall),
      Back, and an available-domains switcher.
- [ ] **iOS vs Android** — Nyx night-mode palette; otherwise dark theme; back
      gesture enabled (`fullScreenGestureEnabled`).

## Interactions

- [ ] **Domain entry hero — primary action**
      (`mobile-domain-entry-primary-<domain>`)
  - Function: per-domain (Tara: launch/resume meditation or course / write
    reflection; Veritas: resume reading; Nyx: resume observation; Arete: start
    check-in / view streaks; Nisaba: open passage). Pushes domain path or starts
    an in-screen session.
- [ ] **Domain entry hero — secondary actions** (`...-secondary-<domain>-*`)
  - Function: open trending brief / top claim / saved brief (Veritas); sky map /
    highlight / log (Nyx); top goal / journal prompt (Arete); workspace /
    passage (Nisaba); etc.
- [ ] **Upgrade plan** (access-locked, Pressable)
  - Function: `openUpgradeFlow(requiredTier)` → push Profile w/
    `intent=upgrade`, `domain`, `requiredTier` params.
- [ ] **Back to <previous>** (access-locked / offline / unavailable)
  - Function: `handleBackInShell` → `router.replace` previous domain or origin
    tab; haptic.
- [ ] **Domain switch chip** (`switchChip`, access-locked + unavailable states)
  - Function: `switchDomain(domain)` (only allowed + available domains shown).
- [ ] **Retry domain launch** (offline/unavailable recovery panels)
  - Function: `retryDomainLaunch`.
- [ ] **Offline cached card** (Tara/Veritas: `ContinuityCard`; Nisaba:
      `PassageListItem`/`NotebookCard`)
  - Function: set offline route path, exit offline mode, launch from cache /
    open cached brief/passage/note; haptic + notice. testIDs
    `<domain>-offline-cache-offline-card/passage/note-<id>`.
- [ ] **Deep-link focus CTA** (`mobile-domain-deep-link-cta-<domain>`)
  - Function: `handleOpenDeepLinkFocus` (opens the resolved deep-link target).
- [ ] **E2E Tara complete session** (`tara-e2e-complete-session`, flagged)
  - Function: `handleCompleteTaraSessionForE2E` (forces session completion for
    Maestro). Also `TaraReflectionCaptureCard` (note input, suggestions, save).
- [ ] **Per-domain content controls** — extensive: Tara quick-reset/sleep-timer
      selectors + audio session; Veritas save/follow article+claim, topic
      follow; Nyx save/remind/night-mode/sky-map; Arete
      goals/habits/journal/balance/routine; Nisaba annotations (note input,
      color, visibility, save/edit), compare, grounded evidence. (Each block has
      its own testIDs; document per-domain in a follow-up pass if needed.)

## Data & contracts

- **Reads**: `@oshun/domain-registry` (`getDomainMetadata`, `isKnownDomain`,
  offline-fallback + launch-permission), per-domain libs
  (`@oshun/domain-tara| veritas|nyx|arete|nisaba`, `@oshun/shell-routines`), BFF
  client (Nisaba passage/compare/grounded-evidence/manuscript/annotations),
  offline caches, auth + profile + offline-queue context, runtime config.
- **Writes**: BFF annotation save; library/topic/object follow + save state;
  Tara reflection records; offline-mode toggles.
- **Realtime**: shared audio session manager (`getSharedAudioSessionManager`).
- **Caching**: per-domain offline caches; in-memory fixtures
  (`TARA_CONTINUE_SESSION`, `VERITAS_*`, `NYX_*`, `ARETE_*`, `NISABA_*`).
- **Auth/role check**: `evaluateDomainAccess(domain, entitlements)` →
  allowed/required tier; Metis surfaces enforce tier before navigation.

## Cross-references

- Entry builders: `@/navigation/routes` (`buildDomainRouteHref`,
  `buildTabRouteHref`); context parse `parseDomainRouteContext`.
- Upgrade target: profile.md (`intent=upgrade` paywall).
- Component sources: `@/components/MobileRecoveryPanel`, `DomainEntryHero`,
  `ContinuityCard`, `PassageListItem`, `NotebookCard`,
  `TaraReflectionCaptureCard`.

## Open questions / known gaps

- [ ] Per-domain content blocks are extensive (each domain has dozens of
      controls) — this file documents the shared scaffold + hero + state
      machine; a per-domain deep walk should follow.
- [ ] Many domain datasets are in-module fixtures alongside live BFF reads
      (notably Nisaba) — confirm which are live in production.
- [ ] Nyx is excluded from the generic offline/unavailable branches (has its own
      night/offline-sky handling) — verify Nyx offline parity.
