ProjectR Docs
Getting Started

Conventions

Git flow, commitlint, dependency pins, husky gates.

Git flow

Work lands on develop. Maintainers may push to it directly; everyone else branches and opens a PR into develop. develop reaches main through a develop → main PR, and the mobile pre-commit hook refuses commits on main and master.

The check workflows (type-check, jest, lint, fallow, backend CI, dashboard CI) run on pushes and PRs to main, master and develop, with path filters. Any packages/backend/** change also triggers the mobile and dashboard checks, and that cross-trigger is deliberate. The e2e and EAS build workflows have their own triggers, and a push to develop starts no build. The two docs freshness checks run for main only.

Commits touching apps/mobile are commitlint-checked (@commitlint/config-conventional) and run the mobile gate pre-commit. Dashboard commits run gitleaks plus lint-staged; type-check runs pre-push.

Releases

Every release bumps version in whichever of apps/mobile/package.json, apps/dashboard/package.json, and packages/backend/package.json shipped, as one chore(release): mobile x.y.z, dashboard x.y.z, backend x.y.z commit in the release PR.

Once that commit exists, run bun run docs:changelog from apps/docs and commit the regenerated content/docs/changelog.mdx in the same PR. The docs-changelog-freshness CI check fails the build otherwise. The generator (apps/docs/scripts/generate-changelog.ts) walks the commits between this release and the previous one, buckets each feat/fix/perf commit by the package(s) it touched, and writes the entry. A chore(release) commit with a real body (like the change summary in the commit itself) is used verbatim instead of the generated bullets.

Husky hooks (root dispatchers)

HookWhat runs
pre-commitPer staged path: mobile → branch protection, hardcoded trycloudflare/ngrok URL check, gitleaks, lint-staged, full check-all; dashboard → gitleaks + lint-staged; backend → typecheck + lint
pre-pushDashboard type-check if dashboard files are in the pushed range. bun run docs:api if backend convex/ or apps/docs files are, and the push fails when the regenerated API catalog differs from the committed pages
commit-msgcommitlint for mobile-touching commits
post-mergebun install if bun.lock changed

Dependency pins that move together

The auth cluster — one atomic batch or not at all:

  • convex 1.48.0 (exact), better-auth 1.6.33 (exact), @convex-dev/better-auth 0.12.6
  • better-auth 1.7 is blocked until @convex-dev/better-auth 0.13 ships (peer range <1.7.0); the 0.13 upgrade carries an account-issuer data migration on prod auth tables
  • A peer mismatch does not fail build or type-check — it breaks login at runtime only. After any bump, smoke a real sign-in, not a page load
  • Dashboard pins always follow packages/backend/package.json + root overrides — never bump independently

React is one copy for the whole tree. react and react-dom are pinned to 19.3.0 exact in apps/mobile, apps/dashboard and apps/docs, and @types/react follows in the root overrides. Two react copies in the mobile bundle crash with invalid-hook errors. Bump all three workspaces together, then run bun run doctor in apps/mobile. That script sets DOTENV_CONFIG_QUIET=true. Without it, dotenv 17 writes log lines into the JSON that expo-doctor parses, and a bare bunx expo-doctor fails. Doctor should pass all 20 checks. Its one deliberate exclusion is listed under SDK 58 workarounds.

Bun 1.4.0 is pinned in four places that move together. They are packageManager in the root and mobile package.json, the installCommand in apps/dashboard/vercel.json, bun in every apps/mobile/eas.json profile, and bun-version in CI. The mobile, backend and dashboard workflows take CI's pin from .github/actions/setup-node-bun-install/action.yml. The two docs freshness workflows and the test job in e2e-android.yml set it in the workflow file.

Node is 22 in every workflow that uses the shared setup action and in apps/mobile/.nvmrc. Expo SDK 58 and React Native 0.88 need Node ^22.13, ^24.3 or 26+, and 22 matches the EAS build images. .github/actions/setup-node-bun-install/action.yml sets node-version: 22, so a Node bump edits that action and .nvmrc together. The backend and dashboard CI workflows use that action as well, because Vitest 5 needs Node 22.12 or newer. The two docs freshness workflows do not use it and set no Node version.

Mobile scripts are a fingerprint source. apps/mobile/package.json scripts participate in the EAS fingerprint runtime version — adding a script casually orphans OTA updates for shipped binaries.

Status/role casing

  • Roles are lowercase everywhere (apps < dashboard < admin < super_admin); use normalizeRole() from the dashboard's src/types/ui-config.ts when handling input of unknown casing.
  • Statuses are lowercase in the backend; a few frontend configs (comment moderation) display uppercase. Hooks lowercase status values before sending mutations.

On this page