# Pathrule Pattern: Expo (React Native) (1.0.0)
# ::pathrule:package:expo-react-native

### [RULE] Never hand-edit ios/ or android/ when CNG is enabled  (path: /)
<!-- scope: project | priority: high | strict -->

Expo's Continuous Native Generation (CNG) regenerates the `ios/` and `android/` directories from `app.config.ts` (or `app.json`) and Expo config plugins each time `expo prebuild` runs. Any manual edit to those directories will be silently overwritten on the next prebuild.

- Add native dependencies that require code changes via an Expo config plugin, not by editing `MainApplication.kt`, `AppDelegate.swift`, `Podfile`, or any native source file directly.
- If a library lacks a config plugin, write one (`withDangerousMod` / `withInfoPlist` / `withAndroidManifest`) rather than touching generated files.
- Commit `ios/` and `android/` only when you have abandoned CNG for that project (a deliberate "bare workflow" decision). A team using EAS Build on a managed or CNG project should add both directories to `.gitignore`.
- Track native dependency intent in `app.config.ts` plugins and in `package.json`; that is the reproducible source of truth, not the generated directory contents.

---

### [RULE] Bump runtimeVersion before any OTA update that touches native code  (path: /)
<!-- scope: project | priority: high | strict -->

EAS Update delivers JavaScript-only over-the-air. The `runtimeVersion` field in `app.config.ts` is the contract between a binary and the JavaScript it can run. If you push an OTA update that imports a native module (or changes a module's API) that is absent in already-installed binaries, those binaries will crash on launch with no user-visible error.

- Set `runtimeVersion: { policy: 'appVersion' }` in `app.config.ts` so the runtime version tracks the app version string and bumping the app version automatically gates the update to matching binaries. This is the safest default for most teams.
- Alternatively use `fingerprintRuntimeVersion` (Expo's hash-based policy) to derive the runtime version from a reproducible fingerprint of the native layer. Run `npx expo-updates fingerprint:generate` to inspect what the hash covers.
- When you add or upgrade a native module that requires a new build, always ship a new EAS Build before or alongside the OTA update. Never push the OTA update first.
- Keep `eas.json` update channels aligned with build profiles: development updates go to `development`, preview to `preview`, production to `production`. Do not push production JavaScript to a channel whose binaries still target an older runtime version.

---

### [RULE] Navigate with Expo Router typed routes; do not import from react-navigation directly  (path: /app)
<!-- scope: folder | priority: high | advisory -->

Expo Router is the file-based router built on top of react-navigation. Bypassing it and importing from `@react-navigation/native` or `@react-navigation/stack` directly creates two conflicting navigators, breaks deep-linking, and loses static typed routes.

- Navigate with `<Link href="...">`, `router.push(...)`, `router.replace(...)`, or `router.navigate(...)` imported from `expo-router`, not `useNavigation().navigate()`.
- Read route params with `useLocalSearchParams<{ id: string }>()` or `useGlobalSearchParams()` from `expo-router`, not `useRoute().params`.
- Enable typed routes by adding `{ web: { bundler: 'metro' }, experiments: { typedRoutes: true } }` to `app.config.ts`. With typed routes, string literals in `href` are validated at build time.
- Define shared layouts with `_layout.tsx` files; use `<Stack>`, `<Tabs>`, or `<Drawer>` from `expo-router`, not from react-navigation directly.
- For programmatic deep links from native code or push notifications, use `expo-router`'s `router.navigate` or the `Linking` module; do not construct `react-navigation` actions manually.

---

### [MEMORY] SDK 56 and New Architecture: what is on by default and what changed  (path: /)

Expo SDK 56 (2025) ships with the New Architecture enabled and bridgeless mode on by default. This changes the runtime assumptions that older libraries make.

- Libraries that access `NativeModules.SomeModule` directly (the old bridge API) no longer work in bridgeless mode. Replace them with TurboModule-compatible alternatives or open an issue upstream.
- JSI-based native code is synchronous; avoid blocking the JS thread with heavy synchronous calls in JSI modules.
- `useAnimatedRef`, `measure`, and Reanimated's worklets run on the UI thread via JSI. Do not access React state or closures from a worklet without using `useSharedValue` or `runOnJS`.
- Check third-party library compatibility at https://reactnative.directory and filter by New Architecture support before adding dependencies. Libraries marked "New Architecture: supported" use the interop layer only; the full native module must be TurboModule-compatible for optimal performance.
- `expo-modules-core` >= 1.12 (bundled with SDK 56) provides the Swift/Kotlin Expo Modules API that generates a TurboModule automatically; prefer it when writing custom native code over manual TurboModule boilerplate.

---

### [MEMORY] Expo Router file conventions and layout nesting  (path: /app)

Expo Router uses the `app/` directory for all screens and layouts. The filename maps directly to the URL and navigation stack.

- `_layout.tsx` defines the navigator for its directory level. Every directory that has more than one screen should have a `_layout.tsx`; without one, Expo Router inserts an implicit Stack.
- `index.tsx` is the index screen for its directory. A file named `(tab).tsx` renders as a tab route, not an extra folder segment, because of the group syntax.
- Use `(groupName)/` directories to group related screens under a shared layout without adding a URL segment. Use `[param]` for dynamic segments and `[...rest]` for catch-all segments.
- `+not-found.tsx` at the root of `app/` renders when no route matches a deep link or path.
- `+html.tsx` customises the root HTML shell for web targets only; it does not affect native.
- `app/_layout.tsx` (the root layout) is the right place for providers (theme, auth context, i18n) that wrap the whole app. Keep the root layout minimal and defer slow providers behind a loading splash so the first frame paints quickly.
- Static routes in `app/` are discovered at build time for typed routes; the codegen runs automatically when you run `expo start` or `expo export`.

---

### [MEMORY] EAS Build and Update channel discipline  (path: /)

EAS Build and EAS Update together form the CI/CD pipeline for Expo apps. Their channel and runtime-version contracts must be kept consistent to avoid delivering incompatible JavaScript to installed binaries.

- `eas.json` defines `build` profiles (`development`, `preview`, `production`) and links each to an update channel with `channel: 'production'`. Updates are only delivered to binaries built with the same channel.
- The `development` profile should set `developmentClient: true` and build an `expo-dev-client` binary. This build runs local or hosted dev bundles and should never receive production OTA updates.
- Run `eas build --profile preview` for QA binaries; push OTA updates to the `preview` channel for that audience, and to `production` only after QA approval.
- Use `eas update --branch <name> --channel <channel>` to publish; always specify the channel explicitly. The `main` branch of a channel receives the update; create named branches (e.g. `rollout-1.2.0`) to stage rollouts before promoting.
- After publishing, check `eas update:list` to confirm the runtime version on the published update matches the runtime version on the target binaries. A mismatch means the update will be silently skipped by those binaries.

---

### [SKILL] expo-review  (path: /)

---
name: expo-review
description: Review checklist for Expo (SDK 56+) React Native apps on the New Architecture. Use before merging changes that touch native dependencies, app.config.ts, EAS config, app/ routes, or OTA update workflows.
---

# Expo review

## Native config and CNG
- [ ] No hand edits to `ios/` or `android/` when CNG is in use; native config changes go through `app.config.ts` and config plugins.
- [ ] New native dependencies have an Expo config plugin, or a custom one has been written rather than editing generated files.
- [ ] `ios/` and `android/` are gitignored (or CNG has been explicitly abandoned).

## OTA safety
- [ ] If any native module was added, removed, or upgraded, a new EAS Build precedes or accompanies the OTA update.
- [ ] `runtimeVersion` policy is set (`appVersion` or `fingerprintRuntimeVersion`); it has not been hard-coded to a stale value.
- [ ] The update channel in `eas.json` aligns with the build profile (development/preview/production); no production JS is targeted at a development channel.

## Expo Router
- [ ] Navigation uses `Link`, `router.push/replace/navigate` from `expo-router`, not `useNavigation().navigate()`.
- [ ] Route params are read with `useLocalSearchParams` from `expo-router`, not `useRoute().params`.
- [ ] `typedRoutes: true` is set and `href` strings are valid route paths.
- [ ] Each directory with multiple screens has a `_layout.tsx`; group directories `(name)/` are used instead of empty path segments.
- [ ] Providers that wrap the whole app are in the root `app/_layout.tsx`, not duplicated across screens.

## New Architecture compatibility
- [ ] No third-party library uses `NativeModules.X` (old bridge API) without a verified bridgeless-compatible replacement.
- [ ] Reanimated worklets do not access React state directly; `useSharedValue` and `runOnJS` are used across the thread boundary.
- [ ] New native modules use `expo-modules-core` (Swift/Kotlin Expo Modules API), not manual TurboModule boilerplate.

## EAS discipline
- [ ] `eas build` and `eas update` channel/profile assignments are consistent in `eas.json`.
- [ ] After publishing, `eas update:list` confirms the runtime version matches the target binaries.
