|
1 | | -# Ctrlplane Development Guide |
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What Is Ctrlplane? |
| 6 | + |
| 7 | +Ctrlplane is the **orchestration layer between CI/CD pipelines and infrastructure** — it decides when releases are ready, where they should deploy, and what gates they must pass (environment promotion, verification, approvals, rollbacks). |
| 8 | + |
| 9 | +```text |
| 10 | +Your CI/CD → Ctrlplane (orchestrates) → Your Infrastructure |
| 11 | +``` |
2 | 12 |
|
3 | 13 | ## Common Commands |
4 | 14 |
|
5 | | -- `pnpm build` - Build all packages |
6 | | -- `pnpm lint` - Run ESLint |
7 | | -- `pnpm lint:fix` - Run ESLint with auto-fix |
8 | | -- `pnpm format` - Check formatting |
9 | | -- `pnpm format:fix` - Fix formatting |
10 | | -- `pnpm typecheck` - Type check all packages |
11 | | -- `pnpm test` - Run all tests |
12 | | -- `pnpm -F <package-name> test` - Run tests for a specific package |
13 | | -- `pnpm -F <package-name> test -- -t "test name"` - Run a specific test |
| 15 | +### Setup (first time) |
| 16 | +```bash |
| 17 | +docker compose -f docker-compose.dev.yaml up -d # Start local services |
| 18 | +pnpm i && pnpm build # Install deps and build |
| 19 | +pnpm -F @ctrlplane/db migrate # Apply database migrations |
| 20 | +pnpm dev # Start all dev servers |
| 21 | +# Reset (wipe all Docker volumes first): |
| 22 | +docker compose -f docker-compose.dev.yaml down -v |
| 23 | +docker compose -f docker-compose.dev.yaml up -d |
| 24 | +pnpm -F @ctrlplane/db migrate |
| 25 | +pnpm dev |
| 26 | +``` |
14 | 27 |
|
15 | | -## Code Style |
| 28 | +### Day-to-day |
| 29 | +- `pnpm test` — Run all TypeScript tests |
| 30 | +- `pnpm lint` — Lint all TypeScript code |
| 31 | +- `pnpm format:fix` — Format all TypeScript code |
| 32 | +- `pnpm build` — Build all packages |
| 33 | +- `pnpm typecheck` — TypeScript type check across all packages |
| 34 | +- `pnpm -F <package-name> test` — Run tests for a specific package |
| 35 | +- `pnpm -F <package-name> test -- -t "test name"` — Run a specific test |
| 36 | + |
| 37 | +### Database |
| 38 | +- `pnpm -F @ctrlplane/db migrate` — Run migrations |
| 39 | +- `pnpm -F @ctrlplane/db push` — Apply schema changes (dev, no migration file) |
| 40 | +- `pnpm -F @ctrlplane/db studio` — Open Drizzle Studio UI |
16 | 41 |
|
17 | | -- Use TypeScript with explicit types (prefer interfaces for public APIs) |
18 | | -- Import styles: Use named imports, group imports by source (std lib > external > internal) |
19 | | -- Consistent type imports: `import type { Type } from "module"` |
20 | | -- Formatting: Prettier is used with `@ctrlplane/prettier-config` |
21 | | -- Prefer async/await over raw promises |
22 | | -- Adhere to file/directory naming conventions in each package |
23 | | -- Handle errors explicitly (use try/catch and typed error responses) |
24 | | -- Document public APIs and complex logic |
25 | | -- For tests, use vitest with mocks and typed test fixtures |
| 42 | +### E2E Tests (Playwright) |
| 43 | +```bash |
| 44 | +cd e2e |
| 45 | +pnpm exec playwright test # Run all e2e tests |
| 46 | +pnpm exec playwright test tests/api/resources.spec.ts # Run a specific file |
| 47 | +pnpm test:api # Run all API tests |
| 48 | +pnpm test:debug # Run in debug mode |
| 49 | +``` |
| 50 | +E2E tests use YAML fixture files (`.spec.yaml` alongside `.spec.ts`) to declare test entities. `importEntitiesFromYaml` loads them; `cleanupImportedEntities` tears them down. Use `addRandomPrefix: true` when parallel runs may conflict. |
| 51 | + |
| 52 | +### workspace-engine (Go) |
| 53 | +```bash |
| 54 | +cd apps/workspace-engine |
| 55 | +go run ./... # Run without building |
| 56 | +go build -o ./bin/workspace-engine . # Build binary |
| 57 | +go test ./... # Run tests |
| 58 | +golangci-lint run # Lint |
| 59 | +go fmt ./... # Format |
| 60 | +``` |
26 | 61 |
|
27 | 62 | ## Monorepo Structure |
28 | 63 |
|
29 | | -- Packages are organized in apps/, packages/, integrations/ directories |
30 | | -- Turborepo manages the build pipeline and dependencies |
31 | | -- Shared configs are in the tooling/ directory |
| 64 | +```text |
| 65 | +apps/ |
| 66 | + api/ # Node.js/Express REST API — core business logic |
| 67 | + web/ # React 19 + React Router frontend |
| 68 | + workspace-engine/ # Go reconciliation engine (multiple controllers) |
| 69 | + relay/ # Go WebSocket relay for agent communication |
| 70 | +packages/ |
| 71 | + db/ # Drizzle ORM schema + migrations (PostgreSQL) |
| 72 | + trpc/ # tRPC server setup |
| 73 | + auth/ # better-auth integration |
| 74 | + workspace-engine-sdk/ # Published TypeScript SDK for external integrations |
| 75 | +integrations/ # External service adapters |
| 76 | +e2e/ # Playwright end-to-end tests (API + UI) |
| 77 | +tooling/ # Shared ESLint, Prettier, TypeScript configs |
| 78 | +``` |
| 79 | + |
| 80 | +**Build system**: Turborepo + pnpm workspaces. Package names use `@ctrlplane/` scope. |
| 81 | + |
| 82 | +## Architecture |
| 83 | + |
| 84 | +### Service Communication |
| 85 | + |
| 86 | +- **Web → API**: tRPC (type-safe RPC via `/api/trpc`) |
| 87 | +- **API → workspace-engine**: PostgreSQL work queue (`reconcile_work_scope` table) |
| 88 | +- **Relay → Job Agents**: WebSocket bidirectional streaming |
| 89 | +- **External webhooks** (GitHub, ArgoCD, Terraform Cloud) hit `apps/api` |
| 90 | + |
| 91 | +### Reconciliation / Work Queue |
| 92 | + |
| 93 | +The workspace-engine implements a PostgreSQL-backed work queue. Multiple controllers poll `reconcile_work_scope` for leased work items: |
| 94 | + |
| 95 | +| Controller | Responsibility | |
| 96 | +|---|---| |
| 97 | +| `deploymentplan` | Compute which resources match a deployment selector | |
| 98 | +| `desiredrelease` | Determine target release per resource | |
| 99 | +| `policyeval` | Evaluate policy rules against release targets | |
| 100 | +| `jobdispatch` | Route jobs to the correct job agent | |
| 101 | +| `jobeligibility` | Check whether a job can run | |
| 102 | +| `jobverificationmetric` | Poll verification metrics (Datadog, Prometheus, HTTP) | |
| 103 | +| `environmentresourceselectoreval` | Evaluate env-level resource selectors | |
| 104 | +| `relationshipeval` | Evaluate resource relationship rules | |
| 105 | + |
| 106 | +Controllers use lease-based locking to prevent duplicate processing and support `Result.RequeueAfter` for scheduled retries. The engine is horizontally scalable — use `SERVICES` env var to activate specific controllers per instance. |
| 107 | + |
| 108 | +### Release & Deployment Flow |
| 109 | + |
| 110 | +```text |
| 111 | +CI registers version |
| 112 | + → Release Target Planning (resource × environment fan-out) |
| 113 | + → Policy Evaluation (approvals, environment ordering, deploy windows) |
| 114 | + → Job Dispatch (GitHub Actions / ArgoCD / Terraform Cloud / custom agent) |
| 115 | + → Verification (metrics checks → promote or rollback) |
| 116 | +``` |
| 117 | + |
| 118 | +### Policy Engine |
| 119 | + |
| 120 | +Policies are **CEL-based declarative rules** with a `selector` field (CEL expression) matching resources/environments. Rule types: |
| 121 | + |
| 122 | +- `policyRuleAnyApproval` — require N approvals |
| 123 | +- `policyRuleEnvironmentProgression` — enforce environment ordering (staging → prod) |
| 124 | +- `policyRuleDeploymentWindow` — restrict deploy times (rrule-based schedules) |
| 125 | +- `policyRuleGradualRollout` — sequential fan-out with intervals |
| 126 | +- `policyRuleVerification` — check metrics before advancing |
| 127 | +- `policyRuleRetry` / `policyRuleRollback` — failure handling |
| 128 | + |
| 129 | +All policies for a release target must pass (AND between types, OR within a type). |
| 130 | + |
| 131 | +### Database Schema (packages/db) |
| 132 | + |
| 133 | +Drizzle ORM manages the PostgreSQL schema. Key tables: |
| 134 | + |
| 135 | +- `workspace` — multi-tenant isolation; all tables include `workspaceId` |
| 136 | +- `deployment` / `deploymentVersion` — service definitions and builds |
| 137 | +- `environment` — staging, prod, etc. |
| 138 | +- `resource` / `resourceProvider` — infrastructure inventory |
| 139 | +- `release` / `releaseJob` — deployment instances and execution units |
| 140 | +- `job` / `jobAgent` — execution units and executor configs |
| 141 | +- `policy` / `policyRule*` — CEL-based deployment gates |
| 142 | +- `reconcile_work_scope` — work queue (kind, scopeType, scopeId, priority, notBefore) |
| 143 | + |
| 144 | +### Job Agents |
| 145 | + |
| 146 | +Job agents are execution adapters stored in the `jobAgent` table. Supported agents: GitHub Actions, ArgoCD, Kubernetes Jobs, Terraform Cloud, Argo Workflows. The `jobdispatch` controller routes jobs to agents; each translates a job spec to agent-native format and correlates results via `externalId`. |
| 147 | + |
| 148 | +## Code Style |
| 149 | + |
| 150 | +- TypeScript with explicit types; prefer `interface` for public APIs |
| 151 | +- Named imports; group by source (std lib → external → internal) |
| 152 | +- `import type { Type }` for type-only imports |
| 153 | +- Prettier via `@ctrlplane/prettier-config` |
| 154 | +- `async/await` over raw promises |
| 155 | +- React: functional components only; type as `const Foo: React.FC<Props> = () => {}` |
| 156 | +- Tests use vitest with typed fixtures |
| 157 | +- Use the builder pattern for complex object construction |
| 158 | +- For Go (workspace-engine): see `apps/workspace-engine/CLAUDE.md` for its own guidelines |
0 commit comments