# AGENTS.md — Vertical Feature Monorepo This is a **Turborepo + pnpm monorepo** organized by vertical features. Each feature package owns its own Clean Architecture layers (entities, application, infrastructure, interface-adapters) and integrations (CMS collections, tRPC routers, UI components). Core packages provide foundation: primitives, design system, CMS composition, API aggregation, and tRPC client platform. --- ## Package Map | Package | Tag | Purpose | |---|---|---| | `@repo/core-shared` | core | Generic primitives (Zod, env, Payload hooks/fields/blocks, tRPC init/context) | | `@repo/core-ui` | core | Design system (atoms, molecules, generic organisms, templates) | | `@repo/core-api` | core-composition | tRPC router aggregator — imports `@repo//api` only | | `@repo/core-cms` | core-composition | Payload config aggregator — imports `@repo//cms` only | | `@repo/core-trpc` | core-composition | Frontend tRPC client + framework-specific providers (Next.js, TanStack) | | `@repo/auth` | feature | Users collection + sign-in/up/out | | `@repo/blog` | feature | Articles collection + article use-cases | | `@repo/media` | feature | Media collection + upload helpers | | `@repo/marketing-pages` | feature | Pages collection + SiteSettings global | | `@repo/navigation` | feature | Header global | | `@repo/core-eslint` | tooling | Shared ESLint 9 flat configs (base, next, react-internal, boundaries) | | `@repo/core-typescript` | tooling | Shared TypeScript base configs + Vitest base | --- ## Boundary Rules ### Five tags - **app** (4 packages) — `apps/web-next`, `apps/web-tanstack`, `apps/cms`, `apps/storybook` - **core-composition** (3 packages) — `packages/core-api`, `core-cms`, `core-trpc` - **core** (2 packages) — `packages/core-shared`, `core-ui` - **feature** (5 packages) — `packages/auth`, `blog`, `media`, `marketing-pages`, `navigation` - **tooling** (2 packages) — `packages/core-eslint`, `core-typescript` ### Allowed dependency directions | Tag | May depend on | |---|---| | app | app, core, core-composition, feature, tooling | | core-composition | core, core-composition, feature, tooling | | core | core, core-composition, tooling | | feature | core, tooling | | tooling | tooling | ### Composition exceptions 1. **`core-api`** may import `@repo//api` subpath exports only (to compose tRPC routers). 2. **`core-cms`** may import `@repo//cms` subpath exports only (to compose Payload collections). 3. **`core-trpc`** reaches features transitively through `core-api`'s `AppRouter` type. No other cross-package boundary deviations are permitted. ### Four enforcement layers 1. **`package.json` dependencies** — only allowed deps are declared; illegal imports fail at install time. 2. **`exports` maps** — feature packages expose `.`, `./cms`, `./api`, `./di/bind-production` only; no deep source paths exist. 3. **ESLint `eslint-plugin-boundaries`** (lint-time) — configured in `packages/core-eslint/`: - Enforces the five-tag rules at linting - Feature packages may import from `core` and tooling only. - `core-shared`, `core-ui` may not import any feature. - `core-api` restricted to `@repo//api` imports. - `core-cms` restricted to `@repo//cms` imports. - No `../../../` cross-package relative imports. 4. **Turborepo `boundaries`** (build-graph time) — configured in root `turbo.json`: - Validates the entire workspace dependency graph, including transitive dependencies - Catches issues ESLint might miss (e.g., transitive feature reaches through composition packages) - Run with `pnpm turbo boundaries` --- ## Adding a Feature Start with `docs/guides/adding-a-feature.md` for a step-by-step walkthrough covering: - New feature scaffold (folder structure, `package.json`, `tsconfig.json`, `vitest.config.ts`) - Clean Architecture layers (entities, use cases, repositories, DI container, controllers) - Payload integration (collections, hooks, Payload repository binding) - tRPC integration (routers, procedure binding) - Core wiring (`core-api`, `core-cms`, path aliases, app bootstrap) - Testing and lint validation --- ## Key Commands ```bash pnpm install # Install all dependencies pnpm dev # Start all dev servers (Next.js :3000, CMS :3001, Storybook :6006) pnpm typecheck # Type-check all packages pnpm lint # Lint all packages (ESLint boundaries enforced) pnpm turbo boundaries # Validate workspace dependency graph (Turbo boundaries) pnpm test # Run all unit + integration tests (Vitest) pnpm test:e2e # Run e2e tests (Playwright across both apps) pnpm build # Build all packages (Turborepo) docker compose up -d # Start PostgreSQL # Filtered commands pnpm dev --filter @repo/web-next # Only Next.js app pnpm dev --filter @repo/cms # Only CMS admin pnpm dev --filter @repo/storybook # Only Storybook pnpm typecheck --filter @repo/blog # Only blog feature pnpm test --filter @repo/blog # Only blog unit/integration tests ``` --- ## Per-Package Conventions ### Source files use RELATIVE imports (not @/) Inside `src/` files, import from sibling layers using relative paths: ```typescript // packages/blog/src/application/use-cases/get-article.use-case.ts import type { IArticlesRepository } from "../repositories/articles.repository.interface.js"; import { ARTICLES_REPOSITORY } from "../../di/symbols.js"; import type { Article } from "../../entities/article.js"; ``` This keeps source code portable and avoids circular alias issues. ### Test files use @/ alias Test files (`*.test.ts`) use the `@/` alias to import from `src/`: ```typescript // packages/blog/src/application/use-cases/get-article.use-case.test.ts import { getArticleUseCase } from "@/application/use-cases/get-article.use-case.js"; ``` ### vitest.config.ts MUST declare @/ alias Every package's `vitest.config.ts` must define the alias: ```typescript import path from "path"; import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "node", globals: true }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, }); ``` ### tsconfig.json rootDir = "." TypeScript configs must set `"rootDir": "."` to allow both `src/` and test files to coexist: ```json { "extends": "@repo/core-typescript/base.json", "compilerOptions": { "rootDir": ".", "outDir": "dist" }, "include": ["src/**/*", "tests/**/*"], "exclude": ["node_modules", "dist"] } ``` ### Payload-backed features use constructor injection Feature packages that need Payload (e.g., `@repo/blog/infrastructure/repositories/payload-articles.repository.ts`) receive the Payload config via constructor, not via `@repo/core-cms` dependency: ```typescript export class PayloadArticlesRepository implements IArticlesRepository { constructor(private config: Config) {} async getById(id: string): Promise
{ const payload = await getPayload({ config: this.config }); return payload.findByID({ collection: "articles", id }); } } ``` The config comes from the app at boot time (see below). ### Apps call `bindProduction*()` per feature at boot Each app (`web-next`, `web-tanstack`, `cms`) imports feature containers and binds production Payload repos at startup: ```typescript // apps/web-next/src/app/layout.tsx (Next.js) import { bindProductionArticles } from "@repo/blog/di/bind-production"; import { bindProductionUsers } from "@repo/auth/di/bind-production"; import { bindProductionMedia } from "@repo/media/di/bind-production"; import { payloadConfig } from "@repo/core-cms"; // At app boot: await bindProductionArticles(blogContainer, payloadConfig); await bindProductionUsers(authContainer, payloadConfig); // ... etc for each feature ``` --- ## Specification & Guides - **Vertical Feature Spec** — `docs/architecture/vertical-feature-spec.md` — full design, rationale, decision log - **Architecture Overview** — `docs/architecture/overview.md` — package responsibilities, data flow - **Dependency Flow** — `docs/architecture/dependency-flow.md` — allowed directions and composition pattern - **Adding a Feature Guide** — `docs/guides/adding-a-feature.md` — step-by-step new feature walkthrough - **Testing Strategy** — `docs/guides/testing-strategy.md` — test placement, Vitest per-package, Playwright e2e Per-package documentation lives in each `AGENTS.md`: - `packages/core-shared/AGENTS.md` - `packages/core-api/AGENTS.md`, `core-cms/AGENTS.md`, `core-trpc/AGENTS.md` - `packages/core-ui/AGENTS.md` - `packages/auth/AGENTS.md`, `blog/AGENTS.md`, `media/AGENTS.md`, `marketing-pages/AGENTS.md`, `navigation/AGENTS.md` - `packages/core-eslint/AGENTS.md`, `core-typescript/AGENTS.md` - `apps/cms/AGENTS.md`, `web-next/AGENTS.md`, `web-tanstack/AGENTS.md`, `storybook/AGENTS.md`