# Expo + SQLite + Drizzle (Local-First Mobile App) — AI Agent Guidelines & Architecture Rules

> Rules for an offline-first Expo SDK 57 app: expo-sqlite with Drizzle migrations bundled in the app, no backend by default, sync as an optional module, EAS Build and Update.
> Technologies: Expo, React Native, Expo Router, SQLite, Drizzle, TypeScript, NativeWind

---

## AGENTS.md
```markdown
# Project Architecture & Guidelines (Expo + SQLite + Drizzle, Local-First)

## 1. System Architecture
- **Framework**: Expo SDK 57 with Expo Router (file-based routes in `app/`). The New Architecture is the only architecture from SDK 55 on.
- **Storage**: `expo-sqlite` on the device is the source of truth, accessed through Drizzle ORM (`drizzle-orm/expo-sqlite`).
- **Migrations**: Generated by `drizzle-kit` with the `expo` driver and bundled into the app; applied on startup with `useMigrations()` before any screen reads data.
- **Settings**: Small key-value state in `expo-sqlite/kv-store` or MMKV, never in the relational schema.
- **Styling**: NativeWind (Tailwind classes) with a small token set.
- **Releases**: EAS Build for store binaries, EAS Update for JavaScript fixes.

## 2. File Layout
- `app/`: Routes only. `_layout.tsx` runs migrations and provides the database; screens import from `src/features/`.
- `src/db/schema.ts`: Every table in one file. `src/db/client.ts`: the `openDatabaseSync()` handle wrapped by `drizzle()`.
- `drizzle/`: Generated migrations plus `migrations.js`; committed, never edited by hand.
- `src/features/<workflow>/`: Queries, hooks, components and tests for one workflow together.
- `src/features/sync/` and `src/features/purchases/`: Only when those features exist, behind their own interface.

## 3. Data Rules
- The app must work with the network off. Any feature that needs a server is optional and degrades to the local path.
- Reads go through Drizzle queries in the feature folder; use `useLiveQuery()` for screens that must update when data changes.
- Every schema change is a new migration generated from `schema.ts`. Never alter tables at runtime.
- Store ids as client-generated UUIDs so records can sync later without remapping keys.
- Export and import (a JSON or CSV file through `expo-file-system` and the share sheet) is the first sync story; it is enough until someone asks for a second device.

## 4. Adding Sync Later
- Sync lives in `src/features/sync/` and talks to the rest of the app only through the local database.
- Prefer an engine with visible queues and a client you can inspect (PowerSync, or a small Hono API with a change log) over opaque magic.
- Accounts exist only when sync is on; a user who never syncs never signs in.

## 5. Coding Standards
- Strict TypeScript, zero `any`. Types come from the Drizzle schema (`typeof items.$inferSelect`), not hand-written interfaces.
- No native modules for storage, files or settings; the Expo SDK covers them.
- Keep screens thin: a screen composes hooks from its feature folder and renders.

## 6. Testing Conventions
- Unit test queries and data logic with Jest (`jest-expo`) against an in-memory SQLite database.
- Maestro flows cover the core workflow with airplane mode on before anything network-related.
- Test a fresh install and an upgrade from the previous release's database, so migrations are exercised on real data.

## 7. Git Workflow & PR Conventions
- Conventional Commits scoped to the feature: `feat(export): CSV export from the history screen`.
- A schema change ships with its generated migration in the same commit.
- Native config changes (plugins in `app.json`) need a new EAS Build; JavaScript-only fixes go out with EAS Update.
```

---

## CLAUDE.md
```markdown
# CLAUDE.md — Expo + SQLite Local-First Commands & Conventions

> Claude Code also reads `AGENTS.md` when no `CLAUDE.md` is present — keep this file for Claude-specific directives.

## Common Commands
- `npx expo start` - Start the dev server
- `npx drizzle-kit generate` - Generate a migration after editing `src/db/schema.ts`
- `npm test` - Run Jest (`jest-expo`) unit tests
- `maestro test maestro/` - Run device flows
- `eas build --profile preview` / `eas update --channel preview` - Build or push an update

## Code Style Guidelines
- Never call a server from the core workflow; if it needs the network, it belongs in `src/features/sync/`.
- Edit `schema.ts`, then generate the migration; never write SQL migrations by hand.
```

---

## .cursor/rules/stack.mdc
```markdown
---
description: Expo local-first app with expo-sqlite and Drizzle
globs: ["app/**/*.tsx", "src/**/*.ts", "src/**/*.tsx"]
alwaysApply: true
---

# Expo + SQLite + Drizzle (Local-First)

- Expo SDK 57, Expo Router, New Architecture only.
- `expo-sqlite` + Drizzle is the source of truth; migrations from `drizzle-kit`, applied with `useMigrations()` at startup.
- The core workflow works offline; sync and purchases are optional modules in their own folders.
- Client-generated UUIDs for every record.
- Types from the Drizzle schema; no hand-written duplicates.
- Maestro flows run in airplane mode first.
```

---

## Architecture Overview & Best Practices
## Architecture Overview

Guidelines for an **offline-first mobile app** on **Expo SDK 57**: data lives on the device in **SQLite** through **Drizzle ORM**, migrations ship inside the app, and sync is an optional module added only when users ask for it.

### Key Advantages

- **No backend to run**: the whole app is one TypeScript project an agent can read, run and test end to end.
- **Typed local data**: Drizzle gives the device database the same schema discipline as a server database.
- **Sync without a rewrite**: client UUIDs and a dedicated sync module let a server arrive later without touching the core workflow.