Skip to main content
The Reflections monorepo uses pnpm workspaces with Turborepo for build orchestration. The codebase is split into deployable apps and shared packages, with strict rules governing how they depend on each other.

Apps

Apps are the deployable units of the platform. Each app serves a distinct runtime concern.

Packages

Packages contain shared logic consumed by apps through published entrypoints. They never depend on apps.

Dependency direction

The dependency graph flows in one direction: apps depend on packages, never the reverse.
Packages must never import from apps/*. This is enforced by the monorepo structure (packages do not list apps as dependencies) and verified by architecture guard scripts in CI.

Import rules within packages

Not all packages can import from each other freely:
  • packages/brain-core cannot import from @reflection/db/queries/admin — this enforces the read-only realtime invariant.
  • packages/vendors is the only package allowed to import directly from @anthropic-ai/sdk and openai. All other packages access vendor APIs through @reflection/vendors.
  • packages/shared has no dependencies on other @reflection/* packages — it sits at the bottom of the dependency graph.
  • packages/schemas depends only on Zod — it defines the cross-layer type contract with no runtime dependencies.

How Turborepo orchestrates builds

Turborepo manages the build pipeline through the turbo.json configuration at the repo root.
Turborepo uses dependsOn declarations in turbo.json to build packages before the apps that consume them. When you run pnpm build, Turborepo:
  1. Builds leaf packages first (packages/schemas, packages/shared).
  2. Builds packages that depend on those (packages/db, packages/vendors, packages/eval, packages/brain-core, packages/api-client).
  3. Builds apps last (apps/api, apps/workers, apps/web).
All packages use tsup for bundling, producing ESM output.
Turborepo caches build outputs based on file hashes. If a package’s source files have not changed, its build step is skipped and the cached output is reused. This makes incremental builds fast — typically only the changed packages and their downstream consumers rebuild.
Key Turborepo tasks and what they run:
  • buildtsup (packages) or framework build (apps). Respects dependency ordering.
  • lint — ESLint across all workspaces in parallel.
  • typechecktsc --noEmit across all workspaces in parallel.
  • test — Vitest across all workspaces in parallel.
The pnpm check command runs format:check, lint, typecheck, test, and build in the correct order as the local CI gate.
The iOS app (apps/ios) is a pure Xcode project and does not participate in the pnpm/Turborepo pipeline. It is built and tested separately via xcodebuild. The contract between iOS and the API is maintained through shared Zod schemas that generate Swift-compatible types.

Enforcement

Import boundaries between packages are enforced at three layers:
  1. ESLintno-restricted-imports rules block deep imports, admin query access from the realtime plane, and direct vendor SDK imports.
  2. Architecture guardsimplicity-guard.mjs runs in CI and catches dynamic imports that bypass ESLint.
  3. File-scan tests — dedicated tests in affected packages verify import restrictions and cannot be disabled with inline comments.
See the boundary matrix for per-plane allowed and forbidden operations.

Further reading

  • ADR-0001 — the original decision record for the monorepo and package boundary design.
  • Two-plane architecture — how the realtime and background planes map to apps.
  • System invariants — the non-negotiable rules that package boundaries enforce.