# OSHUN Wearable Companions

Native watch companions for the OSHUN mobile app (`apps/oshun/mobile`), one per
platform:

- `watchos/` — Apple Watch app (SwiftUI, Swift Package)
- `wearos/` — Wear OS app (Kotlin, Jetpack Compose)

Both implement the same companion contract as `libs/oshun/shell-wearable` (the
canonical TypeScript source of truth): one phone-selected Rail channel, streaks
and milestone ladders, watch-face complications, guided breath sessions with
amplitude-mapped haptic programs and battery/skin budgets, quiet hours, daily
summaries, and consent-gated mindful-session health writes. Contract parity is
enforced by tests on every side — the same constants (domain colors, milestone
days, haptic pulse timings, budget caps, score formulas) are asserted in vitest
(TS), XCTest (Swift), and JUnit (Kotlin).

## Why native (not React Native) on the watches

watchOS does not support React Native — watch apps must be SwiftUI. On Wear OS,
Compose is the platform-supported UI stack for tiles, complications, rotary
input, and ongoing activities. The React Native investment stays on the phones;
the watch apps consume the same wire contract (`ConnectivityCodec`) so the phone
shell remains the source of truth for user state.

## Phone ⇄ watch wire contract

Envelope: `{"v":1,"type":<type>,"payload":{...}}` over WatchConnectivity (iOS)
and the Wearable Data Layer (`/oshun/v1/<type>` paths, Android). Types:
`stats_snapshot`, `preferences`, `breath_timeline`, `rail_tile`,
`session_event`, `session_completed`, `sync_request`. Phone-side codec:
`apps/oshun/mobile/src/wearable/`.

## watchOS

- Swift package: `watchos/Package.swift` — `OshunWatchCore` (portable logic;
  builds and tests on macOS) + `OshunWatchApp` (SwiftUI surfaces,
  WatchConnectivity, HealthKit, WidgetKit complications; watchOS-gated).
- The Xcode watch target is injected into the phone project at `expo prebuild`
  by `apps/oshun/mobile/config/plugins/withOshunWatchApp.js` (copies the Swift
  sources into `ios/OshunWatch/`, creates the `watch2_app` target, embeds it,
  applies watchOS build settings + HealthKit entitlement).
- Tests: `pnpm nx run @oshun/wearable:test-watchos` (or `swift test` from
  `watchos/`).
- watchOS compile check: `pnpm nx run @oshun/wearable:build-watchos`.

Surfaces: Rail channel (one selected, non-adult channel), Today (daily-footing
gauge with the canonical score formula), Breath (guided session with per-event
haptics, pause/resume, budget refusal), Streaks (milestone ladder), Settings
(haptics, Health consent, sync status). Complications: streak, habits, daily
summary (accessory circular/corner/rectangular/inline).

## Platform-surface status (verified 2026-07-03)

Native-real today: watch apps (this directory) with complications/tiles,
on-wrist guided breath sessions, HealthKit mindful-session writes (watchOS,
consent-gated), phone lock-screen Now Playing controls (expo-audio
`setActiveForLockScreen` via the meditation engine), push notifications
(expo-notifications), deep/universal links, camera/mic capture permissions, and
offline media caching. Still in-app-modeled rather than native extension
targets: iOS WidgetKit lock-screen widgets and ActivityKit Live Activities — the
shell renders their surface models (`active-session-surfaces.ts`) in-app, and
`NSSupportsLiveActivities` is declared; shipping the native extension targets
can reuse the `withOshunWatchApp` plugin machinery (app_extension target +
shared app-group defaults publisher).

## Wear OS

- Standalone Gradle project: `wearos/` (wrapper committed; no Android Studio
  required).
- App module `wearos/app`: Compose screens (Rail channel, Today, Breath,
  Streaks, Settings), one selected-channel Rail tile, streak/daily-summary
  complications, Data Layer listener service, vibration composer mapping the
  shared haptic vocabulary onto `VibrationEffect` primitives with waveform
  fallback, Health Connect mindful-session writes (consent-gated).
- Tests: `pnpm nx run @oshun/wearable:test-wearos` (JUnit + Robolectric).
- Debug build: `pnpm nx run @oshun/wearable:build-wearos`.
