Verification
What green actually means: checks, tests, and behavioral gates.
Static gates
bun run check-all from the root runs, per package:
| Package | Commands |
|---|---|
| Mobile | eslint + tsc --noEmit + translation lint; jest for tests |
| Dashboard | type-check (tsc) + lint:strict (eslint --max-warnings=0); vitest for tests |
| Backend | typecheck (tsc on convex/) + eslint on convex/; vitest for tests |
A backend change gates on all three — because of the type contract, a backend edit can break either app.
CI
Workflows live in .github/workflows with branch and path filters. The check workflows (type-check.yml, test.yml, lint-ts.yml, fallow-dead-code.yml, backend-ci.yml, dashboard-ci.yml) run on pushes and PRs to main, master and develop. dashboard-ci.yml and the mobile workflows fire on packages/backend/** too. test.yml runs bun run test:ci, which adds coverage and the time-zone reruns (test:tz) to the jest suite. expo-doctor.yml runs when the root, mobile or backend package.json or bun.lock changes, on pushes to main, master or develop and on any PR. Mobile E2E (Maestro on an Android emulator) runs on demand via PR labels.
The behavioral gate
A green type-check alone does not prove behavior. For user-facing changes, drive the running app:
- For mobile, run the Maestro flows in
apps/mobile/.maestro/against a Release build.bun run e2e-testruns the three smoke flows, and its login flow spends onesendOtptoken. Journeys run by folder withmaestro test .maestro/journeys/ -e APP_ID=com.tipeer.projectr. They mutate real dev data and share the 3-per-hour OTP limit, so they are not the default. Testing has the full recipe. - For the dashboard, open it in a real browser, sign in, exercise the flow, and capture what you saw.
What "done" looks like
bun run check-allgreen from the root- Package tests green
- The affected flow driven in a running app
- If backend: deployed in the right order (backend → dashboard) and the flow re-verified against the deployed environment