Onboarding (day 1)
You have just been given access to this repo. This page is the shortest path from a clone to a merged PR; everything on it links to the page that owns the detail. Setup that only one person ever does — accounts, credentials, GitHub settings — is not here, it is the owner checklist.
Get the app running
- Clone and install.
bun install— Bun only; apreinstallguard rejects npm / yarn / pnpm, andbun.lockis the only lockfile (Conventions → Toolchain). - Check your toolchain.
bun run doctorprints every tool the lanes need, the version found, the version expected and a fix hint for each. It exits 1 only on a missing required tool (Bun / Node / git), so warnings about Maestro, Xcode or the Android SDK are fine until you work that lane (Toolchain check). - Get added to the EAS project. Ask the owner to invite your Expo account to the organisation that owns the project (expo.dev → account → Members), then
bun run eas login. Without it the next two steps fail with a permissions error (Environments and secrets). - Pull the environment.
bun run env:pullwrites.env.localfrom the EASdevelopmentenvironment. Never hand-edit it — EAS is the source of truth, andbun run env:checkvalidates what you have against the Zod schema (Environments and secrets → EAS environment variables). - Install the dev client. The app uses native modules, so Expo Go will not run it. Take the latest
developmentbuild from expo.dev (or an install link posted in Slack) and install it with Expo Orbit — on iOS your device has to be registered first (Device onboarding). A simulator / emulator needs no registration. - Start it.
bun run ios(orandroid/web) starts the dev server against that build, with Rozenite DevTools on.
Make a change and ship it
- Branch off
mainand make the change. The rules that lint and hooks enforce —testIDon every pressable, strings throught(), env through@/lib/env, noios/orandroid/folders — are in Conventions; the full script list with flags is Commands. - Run the gate locally before you push:
bun run lint && bun run typecheck && bun run test && bun run knip && bun run format:check && bun run i18n:check. Lefthook runs the fast half on commit and the slow half on push anyway (Conventions → Hooks). - Commit in Conventional Commits.
feat:/fix:/chore:…, lowercase subject — commitlint checks the message, and the same config checks your PR title, because the squash merge uses it (Conventions → Commits and PR titles). - Open the PR with
## Summary,## Test planandCloses #n(the template prefills them). One ticket, one PR, squash-merged as soon as it is green.
What the checks mean
| Check | Runs | Read |
|---|---|---|
CI jobs | Lint, typecheck, unit, knip, format, commitlint, secret scan, bundle budgets, Maestro web, docs build — all required | JS gate |
E2E (native) | Maestro flows on iOS + Android, from a fingerprint-matched build repacked with your JS | Native E2E |
Perf (Reassure) | Render-perf compare against the base commit; informational | Render-perf tests |
Fingerprint drift | Comments when your change alters the native fingerprint; informational, but it means a new build is needed before the next production promotion | Release ladder → Fingerprint drift |
Preview web | Deploys the web build to a pr-N alias and comments the URL | Release ladder → PR previews |
Every job in both systems, what triggers it and what to do when one is red: CI overview. A Maestro flow that fails then passes on retry is not yours to delete — file it against the flake budget.
After the merge
Your commit is on staging within a few minutes of the squash merge, as an OTA update. UAT and production are manual, approval-gated republishes of that same update group, and store builds come from a vX.Y.Z tag that release-please pushes. The whole runbook, including rollback and hotfix: Release ladder.