Monorepo Structure
Monorepo Structure — Turborepo
All Foundation projects are organized as a Turborepo monorepo. This provides unified build orchestration, dependency deduplication, and a single repository for all application code, infrastructure, and configuration.
Package manager: always use pnpm. Never use npm or yarn for JavaScript/TypeScript dependencies. All workspace scripts, CI commands, and documentation assume pnpm. The root package.json declares "packageManager": "pnpm@latest" to enforce this.
Directory Layout
/
├── ui/
│ ├── web-app/ # Authenticated web application — Vite + TanStack (Router + Query, TypeScript)
│ ├── marketing/ # Marketing site — Next.js 14 (App Router, TypeScript)
│ ├── ios/ # Mobile app — Expo (React Native, TypeScript)
│ ├── types/ # Shared TypeScript type definitions
│ ├── components/ # Shared React component library
│ ├── common/ # Shared TypeScript "business logic" libraries
├── backend/
│ ├── api/ # FastAPI HTTP service (Python)
│ ├── worker/ # Restate durable-execution handlers — durable / agent workflows (Python)
├── schema/ # Drizzle Kit — schema, migrations, RLS policies (single migration history; TypeScript)
├── policies/ # Cerbos authorization policies — app-level PDP that pairs with Neon RLS (YAML)
├── infra/ # Pulumi infrastructure-as-code (TypeScript) — includes restate.ts (Restate Cloud wiring)
├── dbt/ # dbt project — BigQuery adapter (Neon → BigQuery)
├── .github/
│ ├── copilot-instructions.md # AI assistant context
│ ├── skills/ # Repository copilot skills
│ └── workflows/ # GitHub Actions CI/CD
├── turbo.json
├── package.json # Root workspace manifest
├── pnpm-workspace.yaml # pnpm workspace packages
├── biome.json # BiomeJS config (JS/TS linting + formatting)
├── lefthook.yml # Git hooks
└── README.md
turbo.json
Defines pipelines for all monorepo tasks:
| Pipeline | Purpose | Key Config |
|---|---|---|
build | Compile all apps and packages | outputs: [".next/**", "dist/**"] |
test | Run test suites across all workspaces | No outputs (side-effect only) |
lint | Run Biome (JS/TS) checks | No outputs |
format | Auto-format all code | No outputs |
type-check | TypeScript strict type checking | No outputs |
Remote caching should be configured when available (left as a commented-out TODO in fresh scaffolds).
📥 Download turbo.json template
Root package.json
The root package.json serves as the workspace manifest:
{
"private": true,
"packageManager": "pnpm@latest",
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"format": "turbo run format",
"type-check": "turbo run type-check"
},
"devDependencies": {
"turbo": "latest",
"@biomejs/biome": "latest",
"lefthook": "latest"
}
}📥 Download root package.json template
Workspaces are declared in pnpm-workspace.yaml (pnpm does not use the workspaces field in package.json):
packages:
- "ui/*"
- "backend/*"
- "schema"Workspace Conventions
ui/— Frontend applications and shared UI libraries. Contains deployable apps (web-app/— Vite + TanStack applications frontend on Cloud Run;marketing/— Next.js 14 marketing site on Cloud Run;ios/— Expo React Native) and shared packages (types/,components/,common/). Each has its ownpackage.json, build command, and deployment target.backend/— Backend services. Contains the FastAPI HTTP service (api/) and the Restate durable-execution handlers (worker/) for async / agent workflows. Each has its ownpyproject.toml, build command, and deployment target.schema/— Drizzle Kit package: the single migration history, schema, and RLS policies (TypeScript, its owntsconfig.json). The Python backend reflects this schema — no Alembic.policies/— Cerbos authorization policies (YAML) — the app-level policy decision point that pairs with Neon RLS. Not part of the JS or Python workspace graph.infra/— Pulumi program (includesrestate.ts, which wires the managed Restate Cloud environment — no self-hosted node). Has its ownpackage.jsonand is not part of the Turborepo workspace graph (deployed separately).dbt/— dbt project (BigQuery adapter). Pure SQL + YAML, managed by Python tooling (not part of the JS workspace graph).
TypeScript Configuration
Foundation follows a one tsconfig.json per logical unit rule. A "logical unit" maps to each workspace package (deployable app or shared library package) — not to arbitrary subdirectories inside a package.
| Logical Unit | Config File | Covers |
|---|---|---|
| Web app | ui/web-app/tsconfig.json | Vite + TanStack app + all source under ui/web-app/ |
| Marketing site | ui/marketing/tsconfig.json | Next.js 14 app + all source under ui/marketing/ |
| Shared package | ui/<package-name>/tsconfig.json | That package's source (e.g., ui/components, ui/types, ui/common) |
| Mobile / Expo app | ui/ios/tsconfig.json | Expo React Native app + all source under ui/ios/ |
| Schema | schema/tsconfig.json | Drizzle schema + migrations under schema/ |
Do not create additional tsconfig.json files in nested subdirectories within a workspace package. Each package gets a single tsconfig.json; extra nested configs fragment the type-checking graph, break IDE go-to-definition across package boundaries, and make it harder for tools like Turborepo and Biome to reason about the project.
Typical settings
Each tsconfig should extend a shared base (e.g., @tsconfig/strictest or a root-level tsconfig.base.json):
// ui/marketing/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"plugins": [{ "name": "next" }],
"paths": { "@/*": ["./src/*"] }
},
"include": ["src", "next-env.d.ts", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}// ui/ios/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-native",
"paths": { "@/*": ["./src/*"] }
},
"include": ["src", "app", "*.ts", "*.tsx"],
"exclude": ["node_modules"]
}// ui/components/tsconfig.json (repeated pattern for every shared package)
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"declaration": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}If present, a root-level tsconfig.base.json at the repo root holds the common strict settings (strict: true, noUncheckedIndexedAccess, exactOptionalPropertyTypes, etc.) so each unit opts in by extending it rather than duplicating options.
Why Turborepo?
- Incremental builds. Only rebuilds what changed. Dramatically speeds up CI.
- Parallel execution. Tasks across workspaces run concurrently by default.
- Dependency graph awareness. Automatically determines build order based on workspace dependencies.
- Remote caching. (When enabled) Shares build artifacts across the team and CI.
- Simple configuration. Single
turbo.jsonfile, no complex plugin system.