|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Build and Development Commands |
| 6 | + |
| 7 | +This is a pnpm 10.23.0 monorepo using Turborepo. Run commands from root with `pnpm run`. |
| 8 | + |
| 9 | +### Essential Commands |
| 10 | + |
| 11 | +```bash |
| 12 | +# Start Docker services (PostgreSQL, Redis, Electric) |
| 13 | +pnpm run docker |
| 14 | + |
| 15 | +# Run database migrations |
| 16 | +pnpm run db:migrate |
| 17 | + |
| 18 | +# Seed the database (required for reference projects) |
| 19 | +pnpm run db:seed |
| 20 | + |
| 21 | +# Build packages (required before running) |
| 22 | +pnpm run build --filter webapp && pnpm run build --filter trigger.dev && pnpm run build --filter @trigger.dev/sdk |
| 23 | + |
| 24 | +# Run webapp in development mode (http://localhost:3030) |
| 25 | +pnpm run dev --filter webapp |
| 26 | + |
| 27 | +# Build and watch for changes (CLI and packages) |
| 28 | +pnpm run dev --filter trigger.dev --filter "@trigger.dev/*" |
| 29 | +``` |
| 30 | + |
| 31 | +### Testing |
| 32 | + |
| 33 | +We use vitest exclusively. **Never mock anything** - use testcontainers instead. |
| 34 | + |
| 35 | +```bash |
| 36 | +# Run all tests for a package |
| 37 | +pnpm run test --filter webapp |
| 38 | + |
| 39 | +# Run a single test file (preferred - cd into directory first) |
| 40 | +cd internal-packages/run-engine |
| 41 | +pnpm run test ./src/engine/tests/ttl.test.ts --run |
| 42 | + |
| 43 | +# May need to build dependencies first |
| 44 | +pnpm run build --filter @internal/run-engine |
| 45 | +``` |
| 46 | + |
| 47 | +Test files go next to source files (e.g., `MyService.ts` → `MyService.test.ts`). |
| 48 | + |
| 49 | +#### Testcontainers for Redis/PostgreSQL |
| 50 | + |
| 51 | +```typescript |
| 52 | +import { redisTest, postgresTest, containerTest } from "@internal/testcontainers"; |
| 53 | + |
| 54 | +// Redis only |
| 55 | +redisTest("should use redis", async ({ redisOptions }) => { |
| 56 | + /* ... */ |
| 57 | +}); |
| 58 | + |
| 59 | +// PostgreSQL only |
| 60 | +postgresTest("should use postgres", async ({ prisma }) => { |
| 61 | + /* ... */ |
| 62 | +}); |
| 63 | + |
| 64 | +// Both Redis and PostgreSQL |
| 65 | +containerTest("should use both", async ({ prisma, redisOptions }) => { |
| 66 | + /* ... */ |
| 67 | +}); |
| 68 | +``` |
| 69 | + |
| 70 | +### Changesets |
| 71 | + |
| 72 | +When modifying any public package (`packages/*` or `integrations/*`), add a changeset: |
| 73 | + |
| 74 | +```bash |
| 75 | +pnpm run changeset:add |
| 76 | +``` |
| 77 | + |
| 78 | +- Default to **patch** for bug fixes and minor changes |
| 79 | +- Confirm with maintainers before selecting **minor** (new features) |
| 80 | +- **Never** select major (breaking changes) without explicit approval |
| 81 | + |
| 82 | +## Architecture Overview |
| 83 | + |
| 84 | +### Apps |
| 85 | + |
| 86 | +- **apps/webapp**: Remix 2.1.0 app - main API, dashboard, and Docker image. Uses Express server. |
| 87 | +- **apps/supervisor**: Node.js app handling task execution, interfacing with Docker/Kubernetes. |
| 88 | + |
| 89 | +### Public Packages |
| 90 | + |
| 91 | +- **packages/trigger-sdk** (`@trigger.dev/sdk`): Main SDK |
| 92 | +- **packages/cli-v3** (`trigger.dev`): CLI package |
| 93 | +- **packages/core** (`@trigger.dev/core`): Shared code between SDK and webapp. Import subpaths only (never root). |
| 94 | +- **packages/build**: Build extensions and types |
| 95 | +- **packages/react-hooks**: React hooks for realtime and triggering |
| 96 | +- **packages/redis-worker** (`@trigger.dev/redis-worker`): Custom Redis-based background job system |
| 97 | + |
| 98 | +### Internal Packages |
| 99 | + |
| 100 | +- **internal-packages/database** (`@trigger.dev/database`): Prisma 6.14.0 client and schema |
| 101 | +- **internal-packages/clickhouse** (`@internal/clickhouse`): ClickHouse client and schema migrations |
| 102 | +- **internal-packages/run-engine** (`@internal/run-engine`): "Run Engine 2.0" - run lifecycle management |
| 103 | +- **internal-packages/redis** (`@internal/redis`): Redis client creation utilities |
| 104 | +- **internal-packages/testcontainers** (`@internal/testcontainers`): Test helpers for Redis/PostgreSQL containers |
| 105 | +- **internal-packages/zodworker** (`@internal/zodworker`): Graphile-worker wrapper (being replaced by redis-worker) |
| 106 | + |
| 107 | +### Reference Projects |
| 108 | + |
| 109 | +The `references/` directory contains test workspaces for developing and testing new SDK and platform features. Use these projects (e.g., `references/hello-world`) to manually test changes to the CLI, SDK, core packages, and webapp before submitting PRs. |
| 110 | + |
| 111 | +## Webapp Development |
| 112 | + |
| 113 | +### Key Locations |
| 114 | + |
| 115 | +- Trigger API: `apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts` |
| 116 | +- Batch trigger: `apps/webapp/app/routes/api.v1.tasks.batch.ts` |
| 117 | +- Prisma setup: `apps/webapp/app/db.server.ts` |
| 118 | +- Run engine config: `apps/webapp/app/v3/runEngine.server.ts` |
| 119 | +- Services: `apps/webapp/app/v3/services/**/*.server.ts` |
| 120 | +- Presenters: `apps/webapp/app/v3/presenters/**/*.server.ts` |
| 121 | +- OTEL endpoints: `apps/webapp/app/routes/otel.v1.logs.ts`, `otel.v1.traces.ts` |
| 122 | + |
| 123 | +### Environment Variables |
| 124 | + |
| 125 | +Access via `env` export from `apps/webapp/app/env.server.ts`, never `process.env` directly. |
| 126 | + |
| 127 | +For testable code, **never import env.server.ts** in test files. Pass configuration as options instead. Example pattern: |
| 128 | + |
| 129 | +- `realtimeClient.server.ts` (testable service) |
| 130 | +- `realtimeClientGlobal.server.ts` (configuration) |
| 131 | + |
| 132 | +### Legacy vs Run Engine 2.0 |
| 133 | + |
| 134 | +The codebase is transitioning from the "legacy run engine" (spread across codebase) to "Run Engine 2.0" (`@internal/run-engine`). Focus on Run Engine 2.0 for new work. |
| 135 | + |
| 136 | +## Database Migrations (PostgreSQL) |
| 137 | + |
| 138 | +1. Edit `internal-packages/database/prisma/schema.prisma` |
| 139 | +2. Create migration: |
| 140 | + ```bash |
| 141 | + cd internal-packages/database |
| 142 | + pnpm run db:migrate:dev:create --name "add_new_column" |
| 143 | + ``` |
| 144 | +3. **Important**: Generated migration includes extraneous changes. Remove lines related to: |
| 145 | + - `_BackgroundWorkerToBackgroundWorkerFile` |
| 146 | + - `_BackgroundWorkerToTaskQueue` |
| 147 | + - `_TaskRunToTaskRunTag` |
| 148 | + - `_WaitpointRunConnections` |
| 149 | + - `_completedWaitpoints` |
| 150 | + - `SecretStore_key_idx` |
| 151 | + - Various `TaskRun` indexes unless you added them |
| 152 | +4. Apply migration: |
| 153 | + ```bash |
| 154 | + pnpm run db:migrate:deploy && pnpm run generate |
| 155 | + ``` |
| 156 | + |
| 157 | +### Index Migration Rules |
| 158 | + |
| 159 | +- Indexes **must use CONCURRENTLY** to avoid table locks |
| 160 | +- **CONCURRENTLY indexes must be in their own separate migration file** - they cannot be combined with other schema changes |
| 161 | + |
| 162 | +## ClickHouse Migrations |
| 163 | + |
| 164 | +ClickHouse migrations use Goose format and live in `internal-packages/clickhouse/schema/`. |
| 165 | + |
| 166 | +1. Create a new numbered SQL file (e.g., `010_add_new_column.sql`) |
| 167 | +2. Use Goose markers: |
| 168 | + |
| 169 | + ```sql |
| 170 | + -- +goose Up |
| 171 | + ALTER TABLE trigger_dev.your_table |
| 172 | + ADD COLUMN new_column String DEFAULT ''; |
| 173 | + |
| 174 | + -- +goose Down |
| 175 | + ALTER TABLE trigger_dev.your_table |
| 176 | + DROP COLUMN new_column; |
| 177 | + ``` |
| 178 | + |
| 179 | +Follow naming conventions in `internal-packages/clickhouse/README.md`: |
| 180 | + |
| 181 | +- `raw_` prefix for input tables |
| 182 | +- `_v1`, `_v2` suffixes for versioning |
| 183 | +- `_mv_v1` suffix for materialized views |
| 184 | + |
| 185 | +## Writing Trigger.dev Tasks |
| 186 | + |
| 187 | +Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` or deprecated `client.defineJob` pattern. |
| 188 | + |
| 189 | +```typescript |
| 190 | +import { task } from "@trigger.dev/sdk"; |
| 191 | + |
| 192 | +// Every task must be exported |
| 193 | +export const myTask = task({ |
| 194 | + id: "my-task", // Unique ID |
| 195 | + run: async (payload: { message: string }) => { |
| 196 | + // Task logic - no timeouts |
| 197 | + }, |
| 198 | +}); |
| 199 | +``` |
| 200 | + |
| 201 | +## Testing with hello-world Reference Project |
| 202 | + |
| 203 | +First-time setup: |
| 204 | + |
| 205 | +1. Run `pnpm run db:seed` to seed the database (creates the hello-world project) |
| 206 | +2. Build CLI: `pnpm run build --filter trigger.dev && pnpm i` |
| 207 | +3. Authorize CLI: `cd references/hello-world && pnpm exec trigger login -a http://localhost:3030` |
| 208 | + |
| 209 | +Running: |
| 210 | + |
| 211 | +```bash |
| 212 | +cd references/hello-world |
| 213 | +pnpm exec trigger dev # or with --log-level debug |
| 214 | +``` |
0 commit comments