This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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).
Your CI/CD → Ctrlplane (orchestrates) → Your Infrastructure
docker compose -f docker-compose.dev.yaml up -d # Start local services
pnpm i && pnpm build # Install deps and build
pnpm -F @ctrlplane/db migrate # Apply database migrations
pnpm dev # Start all dev servers
# Reset (wipe all Docker volumes first):
docker compose -f docker-compose.dev.yaml down -v
docker compose -f docker-compose.dev.yaml up -d
pnpm -F @ctrlplane/db migrate
pnpm devpnpm test— Run all TypeScript testspnpm lint— Lint all TypeScript codepnpm format:fix— Format all TypeScript codepnpm build— Build all packagespnpm typecheck— TypeScript type check across all packagespnpm -F <package-name> test— Run tests for a specific packagepnpm -F <package-name> test -- -t "test name"— Run a specific test
pnpm -F @ctrlplane/db migrate— Run migrationspnpm -F @ctrlplane/db push— Apply schema changes (dev, no migration file)pnpm -F @ctrlplane/db studio— Open Drizzle Studio UI
cd e2e
pnpm exec playwright test # Run all e2e tests
pnpm exec playwright test tests/api/resources.spec.ts # Run a specific file
pnpm test:api # Run all API tests
pnpm test:debug # Run in debug modeE2E 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.
cd apps/workspace-engine
go run ./... # Run without building
go build -o ./bin/workspace-engine . # Build binary
go test ./... # Run tests
golangci-lint run # Lint
go fmt ./... # Formatapps/
api/ # Node.js/Express REST API — core business logic
web/ # React 19 + React Router frontend
workspace-engine/ # Go reconciliation engine (multiple controllers)
relay/ # Go WebSocket relay for agent communication
packages/
db/ # Drizzle ORM schema + migrations (PostgreSQL)
trpc/ # tRPC server setup
auth/ # better-auth integration
workspace-engine-sdk/ # Published TypeScript SDK for external integrations
integrations/ # External service adapters
e2e/ # Playwright end-to-end tests (API + UI)
tooling/ # Shared ESLint, Prettier, TypeScript configs
Build system: Turborepo + pnpm workspaces. Package names use @ctrlplane/ scope.
- Web → API: tRPC (type-safe RPC via
/api/trpc) - API → workspace-engine: PostgreSQL work queue (
reconcile_work_scopetable) - Relay → Job Agents: WebSocket bidirectional streaming
- External webhooks (GitHub, ArgoCD, Terraform Cloud) hit
apps/api
The workspace-engine implements a PostgreSQL-backed work queue. Multiple controllers poll reconcile_work_scope for leased work items:
| Controller | Responsibility |
|---|---|
deploymentplan |
Compute which resources match a deployment selector |
desiredrelease |
Determine target release per resource |
policyeval |
Evaluate policy rules against release targets |
jobdispatch |
Route jobs to the correct job agent |
jobeligibility |
Check whether a job can run |
jobverificationmetric |
Poll verification metrics (Datadog, Prometheus, HTTP) |
environmentresourceselectoreval |
Evaluate env-level resource selectors |
relationshipeval |
Evaluate resource relationship rules |
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.
CI registers version
→ Release Target Planning (resource × environment fan-out)
→ Policy Evaluation (approvals, environment ordering, deploy windows)
→ Job Dispatch (GitHub Actions / ArgoCD / Terraform Cloud / custom agent)
→ Verification (metrics checks → promote or rollback)
Policies are CEL-based declarative rules with a selector field (CEL expression) matching resources/environments. Rule types:
policyRuleAnyApproval— require N approvalspolicyRuleEnvironmentProgression— enforce environment ordering (staging → prod)policyRuleDeploymentWindow— restrict deploy times (rrule-based schedules)policyRuleGradualRollout— sequential fan-out with intervalspolicyRuleVerification— check metrics before advancingpolicyRuleRetry/policyRuleRollback— failure handling
All policies for a release target must pass (AND between types, OR within a type).
Drizzle ORM manages the PostgreSQL schema. Key tables:
workspace— multi-tenant isolation; all tables includeworkspaceIddeployment/deploymentVersion— service definitions and buildsenvironment— staging, prod, etc.resource/resourceProvider— infrastructure inventoryrelease/releaseJob— deployment instances and execution unitsjob/jobAgent— execution units and executor configspolicy/policyRule*— CEL-based deployment gatesreconcile_work_scope— work queue (kind, scopeType, scopeId, priority, notBefore)
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.
- TypeScript with explicit types; prefer
interfacefor public APIs - Named imports; group by source (std lib → external → internal)
import type { Type }for type-only imports- Prettier via
@ctrlplane/prettier-config async/awaitover raw promises- React: functional components only; type as
const Foo: React.FC<Props> = () => {} - Tests use vitest with typed fixtures
- Use the builder pattern for complex object construction
- For Go (workspace-engine): see
apps/workspace-engine/CLAUDE.mdfor its own guidelines