> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reflections.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# ADR-0020: Expo mobile app in pnpm monorepo

> Treat Expo mobile configuration as a contract and enforce it in-repo via tests and minimal explicit configuration.

<Info>**Status:** Superseded **Date:** 2026-02-10 **Deciders:** Core team</Info>

<Note>
  This ADR has been superseded. The React Native app was removed in favor of a native Swift iOS app
  (`apps/ios/`).
</Note>

## Context

The monorepo runs pnpm + Turborepo with strict TypeScript and workspace packages. The Expo (Expo Router) mobile app in `apps/mobile` was sensitive to:

* pnpm's non-flattened `node_modules` layout (symlinks + virtual store)
* Monorepo workspace package boundaries and hoisting
* Metro resolver settings (watch folders, node module search paths)
* Babel plugin ordering requirements (notably `react-native-reanimated/plugin` must be last)
* Expo Router route availability (e.g. OAuth callback path)
* EAS build profiles (dev-client vs production)

These are high-churn configuration surfaces that tend to regress silently. Deterministic, reviewable configuration was needed to prevent "works on my machine" failures.

Constraints:

* pnpm remains the monorepo package manager.
* Avoid speculative infra or abstractions (0-user mode).
* Fast feedback when config drifts (tests over tribal knowledge).

## Decision

Treat Expo mobile configuration as a contract and enforce it in-repo via tests and minimal explicit configuration:

* Keep the Expo app in `apps/mobile` using Expo Router.
* Commit an EAS configuration with a dev-client profile (`developmentClient: true`) as the default for local device/simulator testing.
* Add a Metro configuration compatible with pnpm workspaces that preserves hierarchical module lookup while explicitly setting workspace watch folders and node module search paths.
* Enforce critical invariants via lightweight config contract tests (Vitest), including:
  * `app.json` required fields and plugin configuration
  * Babel config includes `react-native-reanimated/plugin` and is last
  * Metro config does not disable hierarchical lookup and includes required node module paths
  * Required runtime dependencies exist
  * OAuth callback route exists and redirect helper produces stable URLs

## Alternatives considered

### Alternative 1: Switch package manager for Expo compatibility

Switch the monorepo (or just mobile) to a package manager with a flatter `node_modules` layout.

Pros:

* Fewer Metro/pnpm edge cases.
* More "default path" Expo documentation applies without translation.

Cons:

* Breaks repo-wide tooling and workflows built around pnpm.
* Introduces churn and risk across unrelated apps/packages.

Not chosen because pnpm is a repo-wide standard and changing it is disproportionate to the problem.

### Alternative 2: Metro with `disableHierarchicalLookup = true`

Force Metro to resolve only via explicit `nodeModulesPaths`.

Pros:

* Deterministic resolution when a single node\_modules directory is authoritative.

Cons:

* pnpm workspaces rely on hierarchical lookup to resolve from the virtual store.
* Increased risk of resolving the wrong version of a dependency or failing to resolve at all.

Not chosen because it caused version skew/failures with pnpm-style installs.

### Alternative 3: No explicit contracts; rely on `expo doctor` / CI failures

Keep configs "best effort" and let runtime failures surface issues.

Pros:

* Less test code.

Cons:

* Slow feedback loops; failures often appear late (CI/device-only).
* Harder code review; config intent is not explicit.

Not chosen because config regressions are common and costly; fast, local failure is worth a small set of contract tests.

## Consequences

**Benefits:**

* Reduced Metro/EAS drift and "works on my machine" failures.
* Clear reviewable intent for critical Expo build/runtime requirements.
* Faster regression detection via cheap unit-style tests.

**Costs:**

* Contract tests add maintenance overhead when intentional config changes occur.
* Some invariants are necessarily coupled to Expo/Metro implementation details.

**Risks:**

* Contract tests could become brittle and block legitimate upgrades. Mitigation: keep tests focused on truly load-bearing invariants; update tests alongside deliberate upgrades.
* Metro config remains a subtle failure surface as pnpm/Expo evolves. Mitigation: keep configuration minimal and documented; validate with `expo export` in CI-like builds.

## Related ADRs

* [ADR-0001: Monorepo and package boundaries](/decisions/adr-0001)
* [ADR-0002: Runtime and build standards](/decisions/adr-0002)
* [ADR-0012: CI/CD quality and release gates](/decisions/adr-0012)
* [ADR-0019: Voice runtime provider strategy](/decisions/adr-0019)
