# OSHUN Mobile App

Expo Router + TypeScript baseline for the unified OSHUN shell.

## Watch Companions

- Native Apple Watch and Wear OS companion apps live in `apps/oshun/wearable/`
  (see its README for architecture, tests, and the phone ⇄ watch wire contract).
- Phone-side bridge: `src/wearable/` — `watchBridgeCodec.ts` (versioned envelope
  codec shared with Swift/Kotlin), `watchTransport.ts` (typed `not_configured`
  degradation when the native module is absent), `watchCompanionBridge.ts`
  (stats/preferences push, watch session-control routing into active-companion
  sessions, completed-session credit), and `WatchCompanionBridgeHost.tsx`
  (mounted in `app/_layout.tsx`).
- Native transport module: `modules/oshun-wearable-connectivity` (WCSession on
  iOS, Wearable Data Layer on Android; autolinked at prebuild).
- The watchOS app target is injected into the generated Xcode project at
  `expo prebuild` by `config/plugins/withOshunWatchApp.js`.

## Meditation Audio Engine

- `src/audio/meditationPlaybackEngine.ts` binds the shared Tara session state
  machine (`@oshun/domain-tara`) to a real `expo-audio` player: play/pause/
  seek, playback status feedback, completion detection, and lock-screen Now
  Playing metadata (`setActiveForLockScreen`). Attached per domain-route mount
  in `app/domain/[domainId].tsx`.

## Commands

- `pnpm nx run @oshun/mobile:start`
- `pnpm nx run @oshun/mobile:start:ios`
- `pnpm nx run @oshun/mobile:start:android`
- `pnpm nx run @oshun/mobile:start:web`
- `pnpm nx run @oshun/mobile:typecheck`
- `pnpm nx run @oshun/mobile:lint`

## EAS Build Profiles

- `development` development client + simulator/debug builds
- `internal` internal QA builds for device distribution
- `preview` release-like internal builds for pre-release validation
- `production` app store / play store release builds

Examples:

- `cd apps/oshun/mobile && eas build --profile development --platform ios`
- `cd apps/oshun/mobile && eas build --profile preview --platform android`
- `cd apps/oshun/mobile && eas build --profile production --platform all`
- `cd apps/oshun/mobile && eas submit --profile production --platform ios`

## Device Matrix QA

- Canonical device-matrix contract:
  - `e2e/device-matrix.yaml`
- Canonical redesign inventory:
  - `docs/releases/v1/reports/mobile-redesign-inventory.md`
- Canonical matrix runner:
  - `pnpm --dir apps/oshun/mobile run e2e:matrix`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:dry`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:nisaba`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:nisaba:dry`
- Matrix targets:
  - current iPhone profile: `iPhone 17`, with automatic fallback to
    `iPhone 17 Pro` and then `iPhone 16` when the preferred simulator is not
    installed
  - lower-tier Android profile: `OSHUN Lower Tier Android`
- The matrix always runs the mobile core suite (`e2e:core`) so auth, navigation,
  home, search, library, and assistant coverage are exercised against both
  device classes before launch signoff.
- Nisaba-specific mobile study coverage also has a dedicated matrix path
  (`e2e:matrix:nisaba`) so daily passage, highlight, and study-reminder
  regressions can be exercised as one focused mobile contract without running
  the entire core suite.

## Structure

- `app/` Expo Router routes
- `app/(tabs)/` primary structural tab shell (`Home`, `Explore`, `Activity`,
  `Library`, `Profile`)
- `src/` shared local app modules

## Shell Relationship Model

- `docs/adr/ADR-0056-oshun-mobile-shell-relationship-model.md` is the canonical
  mobile-shell relationship decision.
- Mobile structural tabs: `Home`, `Explore`, `Activity`, `Library`, `Profile`
- Mobile assistant behavior: global continuity sheet, not a peer tab route
- Mobile assistant links hydrate into a structural tab anchor plus an assistant
  route intent so shared links can open the global sheet without inventing a
  fake sixth tab.
- Mobile domain switching now exposes a lower-zone launcher plus grouped switch
  sheet so all six domains remain reachable without scrolling back to the top of
  the active surface.
- `src/navigation/phoneFirstIA.ts` is the measured phone-first IA contract for
  Home, domain switching, Library, Assistant, and Profile. It keeps the lower
  domain/assistant launcher pair to `44%` per side with a shared max width so
  the pair fits on 320 px small-phone layouts before native E2E runs.
- `src/accessibility/shellNavigationAccessibility.ts` is the canonical
  shell-navigation accessibility contract for:
  - explicit tab-bar accessibility labels and stable shell tab test IDs
  - thumb-reach domain-switcher launcher semantics
- `app/(tabs)/_layout.tsx` now opts every shell tab into explicit accessibility
  labels plus tab-bar font scaling.
- `src/components/MobileDomainSwitcherControl.tsx` and
  `src/components/MobileDomainSwitcherSheet.tsx` expose explicit launcher button
  semantics and a screen-reader heading for the switcher sheet title.

## Thumb-Zone Shell Chrome

- The structural tab shell now uses a calmer label-led bottom dock with a
  stronger active-state pill and keyboard-hide behavior so the lower band stays
  usable during repeated navigation.
- The global domain switcher launcher remains left-anchored in the thumb zone,
  but now presents itself as the current room affordance instead of a generic
  utility button.
- The global assistant launcher remains right-anchored above shell chrome, but
  now reads as a smaller guide-layer affordance instead of a competing primary
  card.
- The domain switcher and assistant launcher use shared phone-first geometry, so
  they remain side-by-side on Home, Activity, Library, and Profile and stack
  only where Explore already has a local thumb-reach search dock.
- `src/components/ThumbReachActionDock.tsx`,
  `src/components/MobileDomainSwitcherControl.tsx`, and
  `src/components/MobileAssistantLauncher.tsx` now share a tighter lower-band
  visual grammar so Explore’s local thumb dock and the global shell helpers feel
  like one system.
- `src/components/AssistantEntryCard.tsx` now supports a compact variant used
  across Home, Explore, Activity, Library, and Profile so contextual assistant
  entry preserves prompt seeding without adding another heavy card block to the
  route.

## Grouped Continuity And Library Density

- `src/components/QuickActionsTray.tsx` now presents the fixed six quick starts
  as a grouped core lane plus a lighter utility grid, instead of reading like a
  stack of independent action cards.
- `src/components/ContinuityCard.tsx` now uses a calmer continuation surface
  with lighter chrome so Home return journeys and shared domain continuation
  modules read as reopen cues, not dashboard tiles.
- `src/library/UnifiedLibraryView.tsx` now renders saved material inside a
  grouped rail with row dividers, lightweight metadata chips, and inline action
  pills so Library preserves density without becoming another feed of cards.
- `src/library/UnifiedLibraryView.tsx` exports `UnifiedLibraryItemsRail` so the
  redesigned library row treatment has direct unit-test coverage without
  depending on hookful container setup.

## Search-First Explore And Domain Entry

- `app/(tabs)/explore.tsx` now separates query-present and query-empty states so
  live results move directly under the search workspace, while empty search
  state starts with guided domain-entry rows instead of a generic feed.
- `src/components/ExploreDomainLaunchRail.tsx` is the grouped mode-first entry
  rail that lets Explore branch cleanly into Tara, Veritas, Nyx, Arete, or
  Nisaba without stacking another set of cards.
- `app/domain/[domainId].tsx` now opens every major domain route with a
  domain-specific entry hero that owns the primary launch decision for
  listening, reading, observation, check-in, and study.
- `src/components/DomainEntryHero.tsx` is the shared launch-hero primitive used
  to keep the domain entry contract consistent while preserving domain-specific
  copy, palette, and secondary-action choreography.
- `src/navigation/mobileNisabaReadingQueue.ts` now resolves a dedicated phone
  reading queue for Nisaba so continuation, daily anchor passages, reminder
  returns, saved study, and notebook or compare handoffs appear as one ordered
  mobile lane instead of unrelated cards.

## Compact Trust Grammar

- `src/components/CompactFactRow.tsx` is the shared mobile fact-rail primitive
  for disclosure, provenance, auth posture, governance summaries, and
  trust-preserving follow-up states.
- `src/components/AssistantDisclosureStrip.tsx`,
  `src/components/AssistantMemoryState.tsx`, and
  `src/components/MobileAssistantSheet.tsx` now use a calmer disclosure and
  memory hierarchy that keeps governance visible without dominating the sheet.
- `app/domain/[domainId].tsx`, `src/components/SourceListItem.tsx`,
  `src/components/EvidenceSidebar.tsx`, `src/components/EvidenceCard.tsx`, and
  `src/components/ClaimListItem.tsx` now treat Veritas evidence and provenance
  as scan-first reading support instead of a stack of dashboard cards.
- Nisaba’s focused study lane now exposes source/provenance and disclosure facts
  directly in the main reading lane, while Profile now surfaces compact
  notification, privacy, auth, and session governance summaries before the
  deeper controls.

## Recovery States

- `src/components/MobileRecoveryPanel.tsx` is the shared recovery surface for
  offline continuity, reconnecting work, retry-required sync, and crash-adjacent
  recovery states.
- `src/offline/shellOfflineExperience.ts` is the canonical copy contract for
  shell-level offline, reconnecting, retry, and queued-sync posture.
- `src/offline/GlobalOfflineBanner.tsx` and
  `src/offline/ShellOfflineContinuityCard.tsx` now use the same calmer recovery
  grammar as Activity, domain fallback, Profile offline cache governance, and
  the root `OshunErrorBoundary`.
- `app/(tabs)/activity.tsx` now treats queued sync, retry-required edits, and
  hydration fallback as local-first recovery states instead of stacked warning
  cards.
- `app/domain/[domainId].tsx` now opens offline and temporarily unavailable
  domain routes with one trust-preserving recovery panel before handing off to
  cached continuation paths.

## One-Handed Re-entry Validation

- `src/components/NotificationRoutingBridge.tsx` now has direct unit coverage
  for cold-start notification replay, duplicate-response suppression, and
  foreground delivery so push re-entry cannot double-route the shell.
- `e2e/flows/shell/one-handed-interruption-and-notification-reentry.yaml` is the
  integrated Maestro validation flow for:
  - thumb-zone shell launchers on Home
  - Home quick resume after returning from an interruption
  - Explore thumb-reach search re-entry
  - Activity-anchored notification handoff into routed domain surfaces
- `e2e/flows/shell/notification-route-handoffs.yaml` now also asserts the
  preserved `From activity` route context for domain notification launches, not
  just the deep-link focus payload.
- `e2e/flows/core-experience-suite.yaml` and `e2e/flows/regression-suite.yaml`
  now include the integrated re-entry audit so it runs in the shared mobile
  signoff paths.
- `e2e/flows/shell/tara-home-quick-start-completion.yaml` now covers the live
  Tara quick-start path from Home through session completion, reflection save,
  and return to Home by using a route-scoped Tara E2E completion/reflection
  harness instead of a synthetic post-session shortcut.

## Startup and Branding

- Native app icon, adaptive icon, splash image, and web favicon are configured
  in `app.json` and sourced from `assets/`.
- Brand fonts are bundled in `assets/fonts/` and loaded in `app/_layout.tsx`
  before initial route render.
- Startup flow keeps the native splash visible until:
  - initial app bootstrap delay completes
  - bundled OSHUN fonts are loaded (or font load fails gracefully)
- App start instrumentation (`shell_opened`) records startup latency and
  font-load health from `app/_layout.tsx`.
- Canonical startup budget contract: `src/performance/startupBudgets.ts`
  - cold start targets: `2.2s` iOS, `2.5s` Android
  - shared cold-start alert thresholds: warning at `4.0s` p95, critical at
    `8.0s` p95
  - experiment guardrail: `6.0s` p95
- Canonical animation and battery-impact budget contract:
  `src/performance/animationBudgets.ts`
  - core action ceiling: `700ms`
  - route transition ceiling: `500ms`
  - reduced-motion continuity cue ceiling: `120ms`
  - stagger ceiling: `8` elements across a `400ms` reveal window
  - every canonical motion recipe is budgeted: tap feedback, card entrance,
    bottom sheet, route transition, list stagger, shell control, sheet section,
    and tab-bar visibility
  - Android FPS profiling thresholds: `120` frames, `<= 5%` janky, `p95 <= 20ms`
    or `p90 <= 18ms`, `>= 57` estimated FPS
  - active-session battery budget: max `5%` drain per `30` minutes
  - long-running decorative loops on interactive shell surfaces: `0`
- Canonical offline-asset memory and cache budget contract:
  `src/performance/offlineAssetBudgets.ts`
  - memory ceilings: `150MB` idle, `300MB` active, `500MB` peak
  - download-manager storage caps:
    - `free`: `500MB` total, `25` items per domain, `50MB` per item
    - `pro`: `2GB` total, `100` items per domain, `200MB` per item
    - `premium`: `10GB` total, `500` items per domain, `500MB` per item
  - domain cache windows:
    - Tara: `3` recent meditations, `14` day retention
    - Veritas: `3` recent briefings, `3` day retention
    - Nisaba: `3` recent passages + `3` recent notes, `14` day retention

## Runtime Env and Feature Flags

- `src/config/runtime.ts` loads typed runtime config from `EXPO_PUBLIC_*`
  values.
- `src/config/runtime-context.tsx` provides app-wide runtime config and
  `useFeatureFlag(...)` helpers.
- Feature flag precedence:
  - defaults in `src/config/runtime.ts`
  - JSON overrides via `EXPO_PUBLIC_FEATURE_FLAGS`
  - per-flag env overrides via `EXPO_PUBLIC_FF_*` (highest priority)
- Root provider wiring: `app/_layout.tsx`.
- Example consumed flags:
  - `domainSwitcher` (home domain switcher section)
  - `quickActionsTray` (home and explore quick actions trays)

## Error Boundaries and Crash Reporting

- Root app tree is wrapped by `OshunErrorBoundary` in `app/_layout.tsx`.
- Boundary fallback UI provides a safe retry action instead of a hard crash
  screen.
- Crash reporting bootstrap: `src/observability/crash-reporting.ts`.
  - installs React Native global JS error handler (when available)
  - installs unhandled-promise capture hook for crash diagnostics
  - installs memory diagnostics hooks (startup snapshot, interval sampling,
    memory warning events)
  - captures boundary + global exceptions
  - logs local crash payloads and attempts non-blocking POST to:
    - `${EXPO_PUBLIC_OSHUN_BFF_URL}/v1/mobile/crashes`
  - logs diagnostics payloads and attempts non-blocking POST to:
    - `${EXPO_PUBLIC_OSHUN_BFF_URL}/v1/mobile/diagnostics`
- `src/observability/crash-reporting-screen-context.ts` and
  `src/observability/MobileCrashReportingScreenBridge.tsx` attach active screen
  metadata to crash and diagnostics payloads for the major shell surfaces: Home,
  Explore, Activity, Library, Profile, and the shared domain route.
- Crash reporting can be toggled with `crashReporting` feature flag.

## Network Layer (Retry + Timeout)

- Shared HTTP client: `src/network/http-client.ts`
  - request timeout via `AbortController`
  - retry policy with exponential backoff + jitter
  - retryable status handling (`408`, `425`, `429`, `5xx`)
  - non-retryable `4xx` fail-fast behavior
- BFF adapter client: `src/network/oshun-bff-client.ts`
  - typed `getNotifications(...)` wrapper
  - typed notification write wrappers (`markNotificationRead(...)`,
    `markNotificationsRead(...)`)
  - notification item payloads now preserve optional `actionPath` and
    `actionLabel` metadata for route handoff
  - optional bearer token injection for protected routes
- Notifications screen (`app/(tabs)/activity.tsx`) now hydrates from BFF when
  `EXPO_PUBLIC_OSHUN_DEV_AUTH_TOKEN` is provided, with local fallback + retry
  CTA.
- `src/notifications/registration.ts` installs foreground and response listeners
  plus last-response hydration for notification taps.
- `src/components/NotificationRoutingBridge.tsx` mounts in `app/_layout.tsx` and
  routes push responses into Expo Router using
  `src/notifications/notificationRouting.ts`.
- `src/notifications/NotificationRoutingPreferences.tsx`,
  `src/notifications/NotificationQuietHoursSchedule.tsx`, and
  `src/notifications/NotificationSensitivityPreferences.tsx` provide the
  reusable mobile notification policy controls used in Profile and the
  standalone notification settings screen.
- `src/notifications/NotificationRouteE2EHarness.tsx` exposes E2E-only
  notification response cases from Profile so Maestro can drive the live
  `NotificationRoutingBridge` handoff path without OS push-delivery
  dependencies.
- Quiet-hours schedule edits patch `/v1/notifications/preferences` when an auth
  token is available and fall back to optimistic local updates for guest and E2E
  runs.
- Sensitivity-aware delivery toggles update the canonical
  `personalization.notificationTopics` suppression model (`daily`, `events`,
  `achievements`, `research`) used by the shared personalization contract.

## Global Offline Queue

- Root offline provider: `src/offline/offline-provider.tsx`
  - native-aware connectivity state (`online`/`offline`/`reconnecting`) backed
    by `@react-native-community/netinfo` on iOS/Android and browser events on
    web
  - queued action buffering via `@oshun/offline` `OfflineSyncQueue`
  - automatic queue flush on reconnect and manual `sync now` support
- Connectivity normalization lives in `src/offline/connectivity.ts`.
- Global banner: `src/offline/GlobalOfflineBanner.tsx`
  - shown on all app surfaces from `app/_layout.tsx`
  - displays offline/reconnect status, queued action count, and sync controls
- Shared per-surface continuity notice:
  `src/offline/ShellOfflineContinuityCard.tsx`
  - mounted in Home, Explore, Activity, Library, and Profile
  - keeps capability copy consistent with the shell offline contract
- Activity read-state edits now queue while offline, surface queued/retrying/
  retry-required state in the timeline, and replay after reconnect.
- `src/offline/e2eOfflineReplayHarness.ts` arms one-shot replay failures in E2E
  automation so Maestro can exercise the retry-required recovery path without
  depending on flaky real network faults.
- `e2e/flows/offline-and-retry-suite.yaml` groups shell offline continuity,
  queued Activity replay, manual retry recovery, and offline cached-domain flows
  into one dedicated mobile signoff suite.
- Device-only dismiss actions stay local and are no longer presented as
  server-synced work.

## Expo Route Map

- `src/navigation/routes.ts` defines the typed Expo Router mapping for:
  - shell tab routes (`home`, `explore`, `activity`, `library`, `profile`)
  - assistant route intents anchored to a structural tab
  - domain launch route (`/domain/[domainId]`)
- Domain launches in `Home`, `Explore`, `Activity`, `Library`, and `Profile` use
  this contract instead of inline path literals.
- Domain launch metadata (icon, label, CTA, permissions) is sourced from
  `@oshun/domain-registry`.
- Deep/shared URL hydration:
  - `src/navigation/deepLinkHydration.ts` resolves canonical `oshun://...` and
    shared `https://oshun.app/...` links into internal Expo routes.
  - `src/navigation/useDeepLinkHydration.ts` hydrates initial and runtime URLs
    via `Linking` and `router.replace(...)`.
- Route transition instrumentation (`route_transition_completed`, `tab_viewed`)
  is captured from root layout route-context commits via
  `src/analytics/appPerformanceTelemetry.ts`.
  - emits normalized route path
  - emits shell-tab context when applicable
  - emits six-domain active-domain context for shell routes
  - emits explicit route-domain context for `/domain/[domainId]` routes
  - waits for current-domain hydration before emitting shell-route analytics so
    the first event is not attributed without domain context
- `app/_layout.tsx` mounts the global mobile assistant layer through
  `src/components/MobileDomainSwitcherControl.tsx`,
  `src/assistant/MobileAssistantProvider.tsx`,
  `src/navigation/MobileAssistantRouteIntentBridge.tsx`,
  `src/components/MobileAssistantLauncher.tsx`, and
  `src/components/MobileAssistantSheet.tsx`.
- `src/assistant/disclosure.ts`, `src/components/AssistantDisclosureStrip.tsx`,
  and `src/components/AssistantMemoryState.tsx` provide the reusable mobile
  assistant disclosure and memory-state surfaces used inside the global
  assistant sheet.
- Mobile assistant accessibility audits now cover the live voice/text entry
  surface through:
  - `src/components/AssistantDisclosureStrip.test.tsx`
  - `src/components/AssistantMemoryState.test.tsx`
  - `src/components/MobileAssistantSheet.test.tsx`
  - `e2e/flows/shell/assistant-accessibility-audit.yaml`
- The assistant sheet now exposes:
  - a screen-reader heading for the assistant title
  - explicit disclosure + memory summaries on the live sheet
  - quick-prompt button hints
  - text-composer label/hint semantics
  - a labeled sheet-dismiss action
  - synthetic-voice disclosure alongside session-memory disclosure
- Shell-navigation accessibility audits live in:
  - `src/accessibility/shellNavigationAccessibility.test.ts`
  - `app/(tabs)/_layout.test.tsx`
  - `src/components/MobileDomainSwitcherSheet.test.tsx`
  - `e2e/flows/shell/navigation-accessibility-audit.yaml`
- Core mobile E2E signoff now has an explicit Maestro suite:
  - `e2e/flows/core-experience-suite.yaml`
  - `pnpm --dir apps/oshun/mobile run e2e:core`
- The core suite is the launch bar for:
  - auth session creation and re-entry
  - structural tab navigation
  - home entry validation
  - Tara quick-start launch through completion and reflection return
  - explore search execution
  - library preservation handoff
  - assistant continuation from a saved-item journey
- Device-matrix execution for that suite is handled by
  `scripts/run-maestro-device-matrix.sh`, which resolves the current iPhone
  simulator automatically and lets QA bind a named lower-tier Android emulator
  through `ANDROID_LOWER_TIER_DEVICE_NAME` or `ANDROID_LOWER_TIER_DEVICE_ID`.
- Redesigned shell signoff now has a dedicated Maestro suite:
  - `e2e/flows/shell-redesign-signoff-suite.yaml`
  - `pnpm --dir apps/oshun/mobile run e2e:signoff`
- That suite is the redesign bar for:
  - structural tab and launcher navigation posture
  - Home hero, return, quick-action, and contextual assistant-entry hierarchy
  - continuation states across the six domain entry routes
  - contextual assistant entry continuity from Home, Explore, Activity, Profile,
    and Library
- QA can run the same suite across the existing device matrix through:
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:signoff`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:signoff:ios`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:signoff:android`
- Mobile assistant streaming transport has a dedicated lifecycle and device
  benchmark contract:
  - `pnpm --dir apps/oshun/mobile run e2e:assistant-transport`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:assistant-transport`
  - `pnpm --dir apps/oshun/mobile run e2e:matrix:assistant-transport:dry`
  - The streaming lifecycle suite requires Metro/the dev client to be built with
    `EXPO_PUBLIC_OSHUN_MOBILE_ASSISTANT_TRANSPORT=streaming`; buffered is the
    fail-closed default until retained measurements admit a winner.
  - `scripts/run-assistant-transport-android-benchmark.sh` captures native PSS,
    batterystats, TTFT, cancel/background/network outcomes, and raw hashes for
    either the `streaming` or `buffered` arm.
- Device-specific redesigned-shell visual capture signoff now has a dedicated
  flow and runner:
  - `e2e/flows/shell/redesigned-shell-visual-captures.yaml`
  - `pnpm --dir apps/oshun/mobile run e2e:captures:shell`
  - `pnpm --dir apps/oshun/mobile run e2e:captures:shell:ios`
  - `pnpm --dir apps/oshun/mobile run e2e:captures:shell:android`
  - `pnpm --dir apps/oshun/mobile run e2e:captures:shell:dry`
- The capture runner writes ignored review artifacts into:
  - `apps/oshun/mobile/e2e/artifacts/shell-redesign-signoff/screenshots/iphone-17`
  - `apps/oshun/mobile/e2e/artifacts/shell-redesign-signoff/screenshots/android-lower-tier`
- On Android, the capture runner can be pointed at Expo Go or a dev client with
  the existing `APP_ID`, `DEV_CLIENT_LINK`, `EXPO_GO_LINK`, and
  `VERITAS_ROUTE_LINK` overrides, and it now bootstraps the Maestro Android
  helper packages automatically when the emulator is missing them.

## Home Dashboard

- `app/(tabs)/index.tsx` now implements the mobile home workspace around the
  accepted Tara-first shell sequence instead of a generic dashboard stack:
  - dynamic daypart-led hero that resolves the canonical OSHUN morning, midday,
    and evening journey model through `src/navigation/mobileDaypartJourneys.ts`
  - one dominant current-arc action plus a quieter continuity-review handoff
  - explicit three-card daypart rail for morning anchoring, midday
    reorientation, and evening integration
  - one lead return-journey feature plus a calmer stacked continuation lane for
    alternate reopen points
  - companion-domain bridges rebuilt as one grouped support lane rather than a
    feed of repeated cards
  - contextual assistant entry now sits beneath the return/bridge sequence so it
    supports the current arc without competing with the first-view focal plane
  - direct launches rebuilt from a two-column domain card grid into a grouped
    domain rail with current-focus signaling
  - offline continuity and daily-footing surfaces moved beneath the primary
    ritual and re-entry sequence so Home opens with less density
  - quick actions tray now exposes a fixed six-action shell contract
    (`meditation`, `check-in`, `briefing`, `tonight`, `study`,
    `assistant-follow-up`) plus the existing utility controls underneath
  - reusable `src/components/ContinuityCard.tsx` continuity cards for Home and
    domain detail resume surfaces, with a lead feature treatment used on Home
  - Home continuity launches mapped through the domain adapter action +
    deep-link contracts for Tara, Veritas, Nyx, Arete, Nisaba, and Metis
  - stable home section, daypart, lead-return, and direct-launch test IDs used
    by Maestro shell coverage
- `src/navigation/mobileDaypartJourneys.ts` maps the shared daypart journey
  contract into mobile-native route intents:
  - domain route hydration for Tara, Arete, Veritas, Nyx, Nisaba, and Metis
  - assistant-sheet route intent for midday recovery
  - shared Library closure handoff for evening integration
- `src/navigation/mobileQuickActions.ts` maps the fixed quick-action grammar
  into daypart-aware mobile route intents reused by Home and Explore.

## Domain Launch Startup Skeletons

- `app/domain/[domainId].tsx` now renders startup skeleton placeholders while
  domain module metadata/hydration context initializes.
- Skeleton phase includes badge/title/body/card placeholders and then
  transitions to launch contract + permissions content.
- Domain unavailable fallback is handled in the same route with graceful
  retry/back/switch-domain actions (`unavailable`, `reason` query support).
- Domain offline fallback cards are handled in the same route with per-domain
  messaging/CTA/cache hints (`offline` query support plus launch-time device
  connectivity resolution).
- Tara session launch paths (`/meditate/resume/:sessionId`,
  `/meditate/session/:sessionId`) are hydrated into a concrete shell launch flow
  with stateful CTA + launch notice.
- `src/offline/taraOfflineCache.ts` now keeps the latest Tara meditations and
  their audio assets cached through the mobile shell download manager so recent
  sessions remain launchable offline.
- The Tara domain route now exposes a live offline-cache summary while online
  and cached meditation launch cards while offline when audio is ready.
- Profile settings now bind the existing `Offline downloads` preference to the
  Tara cache lifecycle and expose Tara cache status plus a clear-cache action.
- Reusable `src/components/EvidenceCard.tsx` and
  `src/components/EvidenceSidebar.tsx` primitives now power the Veritas claim
  evidence snapshot surface inside domain detail.
- Reusable `src/components/ClaimListItem.tsx`,
  `src/components/SourceListItem.tsx`, and `src/components/PassageListItem.tsx`
  now power Veritas claim/source rows and Nisaba study cards inside domain
  detail.
- Reusable `src/components/NotebookCard.tsx` and
  `src/components/CollectionCard.tsx` now power Nisaba notebook workspaces,
  Explore curated collections, and type-aware Library notebook/collection
  rendering.
- Entitlement middleware checks now run in the same route using shared profile
  state + `@oshun/auth-client` evaluators to block access, expose upgrade
  navigation context, and filter inaccessible domain shortcuts.
- Domain launch telemetry is emitted from
  `src/analytics/domainLaunchTelemetry.ts` (`domain_launch_requested`,
  `domain_launch_completed`, latency + success).
- The global lower-zone domain launcher remains available on domain detail
  routes so cross-domain switching no longer depends only on inline switch rows.
- `src/navigation/mobileDomainDeepLinkFocus.ts` resolves hydrated deep-link
  paths and query payloads into six-domain mobile focus cards so Tara, Veritas,
  Nyx, Arete, Nisaba, and Metis expose a visible, testable target contract on
  the shared domain route.
- The shared domain route now exposes first-class continuity cards for Tara,
  Arete, Veritas, Nyx, and Nisaba so every domain has a stable resume surface
  before deeper content modules.
- The same shared route now exposes drift-recovery cards for Tara routines,
  Arete habits, and Nisaba study threads so missed days reopen into a humane
  next step instead of a raw resume state.
- `e2e/flows/shell/offline-shell-continuity.yaml` adds Android-specific Maestro
  coverage for shell offline banner visibility and offline domain fallback
  routing, and `e2e/flows/regression-suite.yaml` now includes it conditionally
  on Android.
- `e2e/flows/shell/domain-continuation-cards.yaml` verifies that all six domain
  detail routes expose their expected continuity card and is included in the
  shared mobile regression suite.
- `e2e/flows/shell/deep-links-six-domains.yaml` verifies that high-value object
  links in all six domains hydrate into visible target focus cards.
- `e2e/flows/shell/domain-drift-recovery.yaml` verifies that Tara, Arete, and
  Nisaba expose their expected drift-recovery card states through the shared
  domain switcher.
- `e2e/flows/shell/tara-offline-cache.yaml` adds Android-specific regression
  coverage that seeds Tara cache online, confirms the Profile cache summary,
  then validates Tara cached-session affordances after going offline.
- `src/offline/veritasOfflineCache.ts` now keeps recent Veritas briefings in a
  policy-aware offline cache so only evidence-backed and credibility-eligible
  briefs are stored locally.
- `e2e/flows/shell/veritas-offline-cache.yaml` adds Android-specific regression
  coverage that seeds the Veritas cache online, confirms the Profile summary,
  and validates cached briefing continuation after going offline.
- `src/offline/nisabaOfflineCache.ts` now keeps recent Nisaba passages and
  notebook/compare note work cached through the shared download manifest so the
  Nisaba domain can reopen real study context offline instead of falling back to
  a generic unavailable screen.
- `e2e/flows/shell/nisaba-offline-cache.yaml` adds Android-specific regression
  coverage that seeds Nisaba cache online, confirms the Profile summary, then
  validates cached passage and note continuation after going offline.

## Universal Search UX

- `app/(tabs)/explore.tsx` implements cross-domain universal search with:
  - curated cross-domain collections (tap-to-apply themed query sets)
  - editorial spotlight cards with curated picks and domain launch/save actions
  - explicit domain filter chips and sort controls (`recency`, `popularity`)
  - Tara quick-resume + result launches routed through canonical Tara deep-link
    metadata
  - per-result save/unsave controls with explicit library handoff behavior
  - query input + clear control
  - reusable domain switcher entry (`All`, `Tara`, `Veritas`, `Nyx`, `Arete`,
    `Nisaba`) + direct domain launch CTA
  - quick actions tray entry with fixed `meditation`, `check-in`, `briefing`,
    `tonight`, `study`, and `assistant-follow-up` launches plus the legacy
    utility actions
  - contextual assistant entry for branching the current discovery state into
    the global assistant sheet
  - URL hydration support for `q`, `query`, `domain`, and `path` params
  - recent searches + trending intents
  - result cards with domain/type metadata and one-tap domain launch

## Library UX

- `app/(tabs)/library.tsx` implements the dedicated mobile preserve surface
  with:
  - relationship copy clarifying how Library fits with Home, Explore, Activity,
    Assistant, and Profile
  - saved-item, domain, and pending-sync summary cards
  - contextual assistant entry seeded with library-triage context
  - full `UnifiedLibraryView` browsing/filtering behavior using
    `originTab="library"`
- `src/library/LibraryPreviewStrip.tsx` replaces the old full-library embed
  inside Explore and now hands preserved context into the Library tab.

## Assistant Continuity Layer

- `Assistant` is a global mobile continuity sheet rather than a peer tab.
- `src/assistant/mobile-assistant-model.ts` resolves current shell/domain
  context into prompts, responses, and routing actions, including the Metis
  learning domain for course, tutoring, and assessment handoffs.
- `src/components/AssistantEntryCard.tsx` provides the reusable contextual
  assistant-entry surface used across Home, Explore, Activity, Library, and
  Profile.
- The launcher remains available from shell and domain surfaces, and assistant
  responses route users back into structural tabs or domain launches.
- Every primary tab now also exposes a local assistant entry with a seeded
  prompt, explicit source tag, and stable test ID so invocation can start from
  the current screen’s working context instead of only from the floating
  launcher.
- Learning-oriented assistant prompts route to Metis, while saved-material,
  settings, and continuity prompts still route to Library, Profile, and Activity
  respectively.

## Activity Timeline UX

- `app/(tabs)/activity.tsx` implements a unified cross-domain activity timeline
  with:
  - unread/priority/domain summary tiles
  - streak/progress summary card and milestone progress rows
  - cross-domain achievement cards with unlock/progress state
  - explicit hydration loading/failure cards with retry actions
  - offline-aware queued mutation behavior for timeline actions
  - optimistic mutation rollback rules for failed sync writes
  - timeline filters (`All`, `Unread`, `Priority`)
  - domain filters (`All`, `Tara`, `Veritas`, `Nyx`, `Arete`)
  - grouped sections (`Now`, `Today`, `Earlier`) with timeline rail affordance
  - per-entry actions (`Open`, `Read/Unread`, `Dismiss`)
  - `Open` now routes through the same notification-routing contract used by
    real push responses, so six-domain activity items hand off into exact domain
    paths instead of only domain roots
  - bulk `Mark all seen` action
  - contextual assistant entry for turning timeline change into a settings or
    next-surface handoff

## Profile and Settings UX

- `app/(tabs)/profile.tsx` implements a full account and preferences surface
  with:
  - identity and plan header
  - quick profile stats
  - shared profile identity/stats/domain state from `@oshun/auth-client`
    (`src/profile/store.ts`)
  - shared preferences model (theme/language/notifications) from
    `@oshun/auth-client` (`src/profile/store.ts`)
  - shared privacy + consent model from `@oshun/auth-client`
    (`src/profile/store.ts`)
  - shared billing status + recovery model from `@oshun/auth-client`
    (`src/profile/store.ts`)
  - general preferences (`theme`, `language`, `reduced motion`, `high contrast`,
    `haptics`)
  - notifications controls (`push`, `email digest`, `quiet hours`)
  - per-domain push routing so Tara, Veritas, Nyx, Arete, Nisaba, and Metis can
    each keep or mute push delivery independently without losing in-app or
    digest settings
  - per-persona push routing so OSHUN Navigator, Tara Guide, Zen River Guide,
    Stoic Research Guide, Nisaba Study Guide, Source Lineage Guide, and Heart
    Wisdom Guide can each be enabled or muted as notification voices
  - privacy/access toggles (`biometric lock`, `crash reporting`,
    `private profile`, `cross-domain personalization`, `offline`,
    `cellular sync`)
  - consent controls (`legal agreements`, `analytics`, `personalization`,
    `marketing`)
  - data rights requests UX (`export account data`,
    `request/cancel account deletion` with status history)
  - purchase/upgrade entry points with plan cards and upgrade-intent hydration
    (`intent=upgrade`, `domain`, `requiredTier`)
  - billing status and recovery actions (`retry payment`, `schedule retry`,
    `update payment method`, recovery notice states)
  - linked devices/session management (`require trusted sessions`, per-device
    revoke, sign out other devices)
  - support and feedback channels (`help center`, `contact support`,
    `report issue`, `product feedback`)
  - connected-domain rows with per-domain manage entry
  - contextual assistant entry for explaining settings and routing back into the
    right post-settings shell surface

## Gesture Patterns Implemented

- `swipe back`: enabled on stack routes (see `app/_layout.tsx`) with concrete
  domain route transitions.
- `cross-domain back stack`: domain launches now carry `origin` + `stack`
  context and `app/domain/[domainId].tsx` resolves deterministic in-shell back
  behavior across domain hops.
- `pull-to-refresh`: wired on home feed scroll (`app/(tabs)/index.tsx`)
- `sheet drag`: reusable draggable sheet at
  `src/components/DraggableBottomSheet.tsx`

## Haptic Helpers

- `src/feedback/haptics.ts` maps OSHUN intents to platform-safe haptic
  primitives:
  - `actionConfirmation` -> selection feedback
  - `success` -> success notification
  - `warning` -> warning notification
  - `lock` -> heavy impact
- Includes priority resolver and throttling controls aligned with
  `docs/releases/v1/design/haptics-guidelines.md`.
- `src/feedback/mobileFlowFeedback.ts` is the mobile-shell guidance map for the
  key launch-critical flows:
  - assistant open / submit / action handoff
  - domain switcher open / select
  - quick actions open / launch
  - home refresh start / completion
- The shell posture remains one haptic per gesture by default, with selection
  feedback for launcher/open actions and success reserved for completed refresh
  outcomes.
- `src/preferences/mobileExperienceSettings.ts` now makes in-app `haptics` and
  `reduced motion` preferences global shell inputs instead of Profile-only local
  state.

## Reduced Motion Support

- `src/accessibility/useReducedMotionPreference.ts` merges system `reduceMotion`
  with the in-app reduced-motion preference from
  `src/preferences/mobileExperienceSettings.ts`.
- `src/motion/mobileMotion.ts` codifies the canonical duration/easing tokens
  plus the shared recipes for tap feedback, card entrance, bottom sheet, route
  transitions, shell controls, sheet sections, and tab-bar visibility.
- `src/motion/MobileMotionSurface.tsx` applies those presets to battery-safe
  entrance and press feedback across floating shell controls, sheet actions, and
  tab buttons without introducing continuous or decorative loops.
- `src/components/MobileShellTabBarButton.tsx` and `app/(tabs)/_layout.tsx` now
  give the bottom tabs selected-state emphasis and explicit keyboard hide/show
  timing that collapse to instant when reduced motion is enabled.
- `app/_layout.tsx` now uses explicit route transition guidance instead of a
  single hard-coded animation, and still disables transitions when reduced
  motion is enabled.
- `src/components/DraggableBottomSheet.tsx` now uses shared bottom-sheet motion
  guidance for open, dismiss, and settle behavior, and collapses those
  transitions to instant when reduced motion is enabled.
- Assistant, quick-actions, domain-switcher, assistant-entry, and thumb-dock
  surfaces now share the same shell-control and sheet-section motion posture so
  redesigned mobile entry points feel related rather than independently tuned.
- `src/recommendations/RecommendationCarousel.tsx` suppresses layout animation
  when reduced motion is enabled.

## Android FPS Validation

- `scripts/validate-android-fps.sh` captures `adb shell dumpsys gfxinfo` for
  interaction profiling.
- `scripts/parse-gfxinfo.mjs` parses captured output using the shared profiling
  thresholds in `config/animation-budgets.json`.
- Validation guide: `docs/releases/v1/runbooks/android-60fps-validation.md`

Examples:

- `cd apps/oshun/mobile && pnpm run perf:fps:android com.oshun.mobile swipe-back`
- `cd apps/oshun/mobile && pnpm run perf:fps:android com.oshun.mobile pull-to-refresh`
- `cd apps/oshun/mobile && pnpm run perf:fps:android com.oshun.mobile sheet-drag`
