Workspace layout

The workspace root is a plain folder, not a Git repository. Each surface is its own nested Git clone, checked out on the dev branch.
Run Git, installs, tests and commits inside the repository that owns the file. Nothing at the root is committed with the application code. The root package.json only exists to start the API and the web app together: tools/wait-for-api.sh polls http://127.0.0.1:3031/healthz for up to 90 seconds. tools/ensure-core-api.sh does the same check and, with --start, launches pnpm dev in backend in the background and writes its output to a log file in /tmp.

backend

A pnpm workspace built with Turborepo. Workspace globs are apps/* and packages/*.
Two package scopes exist for a historical reason. @perform/* packages were written for the backend. @repo/* packages were ported from the web repository during the server consolidation and keep their original scope and code style. tsconfig.base.json maps @perform/* to packages/*/src/index.ts, so the API type-checks against package source. The @repo/* packages are consumed through their built dist output. That difference is the cause of the stale dist problem described in Troubleshooting. Domain modules follow a five-file pattern: <name>.routes.ts, <name>.controller.ts, <name>.service.ts, <name>.repository.ts and <name>.schema.ts.

frontend

A pnpm workspace built with Turborepo. Workspace globs are apps/*, packages/* and tooling/*. Shared dependency versions live in the catalog block of pnpm-workspace.yaml.
The folders apps/docs, apps/mail-preview, packages/api, packages/database, packages/ai, packages/mail and packages/storage still exist on disk in some checkouts but contain only a leftover node_modules folder. They have no package.json and are not part of the workspace. The code they held now lives in the backend.
apps/saas/tsconfig.json defines the path aliases: @/* for the app root, and @shared/*, @auth/*, @organizations/*, @payments/*, @settings/*, @admin/*, @onboarding/*, @i18n/* and @config for the feature modules.

mobile

A single Expo package managed with pnpm. It has no Turborepo setup.
The android and ios folders are generated by expo prebuild and are not the source of truth. Native configuration lives in app.json, the config plugins and the local module.

Where the same logic lives twice

Some rules are implemented in more than one repository and must be changed together. Search for the function name in all three repositories before changing one of these:
  • Form field conditions: backend/apps/core-api/src/modules/forms/form-conditions.ts, frontend/apps/saas/modules/shared/lib/form-conditions.ts, and the forms feature in mobile/src/features/forms/lib.
  • MBP portion math: backend/apps/core-api/src/modules/foods/mbp.ts, frontend/apps/saas/modules/shared/lib/mbp.ts and mobile/src/features/nutrition/lib/mbp.ts.
  • Workout time estimates: mobile/src/features/workouts/lib/estimateWorkout.ts and the trainee service in the backend.
This list is not complete. When a rule produces a number or a visibility decision that both a coach and a trainee see, assume it has a twin.