Skip to content

Commit edd0543

Browse files
committed
Merge branch 'main' into docs/self-host-env-vars
2 parents 40e2085 + 097fab0 commit edd0543

File tree

670 files changed

+77727
-4475
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

670 files changed

+77727
-4475
lines changed

.changeset/fix-batch-queue-processing.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/metal-steaks-try.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.changeset/modern-boxes-watch.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.claude/rules/database-safety.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
paths:
3+
- "internal-packages/database/**"
4+
---
5+
6+
# Database Migration Safety
7+
8+
- When adding indexes to **existing tables**, use `CREATE INDEX CONCURRENTLY IF NOT EXISTS` to avoid table locks. These must be in their own separate migration file (one index per file).
9+
- Indexes on **newly created tables** (same migration as `CREATE TABLE`) do not need CONCURRENTLY.
10+
- When indexing a **new column on an existing table**, split into two migrations: first `ADD COLUMN IF NOT EXISTS`, then `CREATE INDEX CONCURRENTLY IF NOT EXISTS` in a separate file.
11+
- After generating a migration with Prisma, remove extraneous lines for: `_BackgroundWorkerToBackgroundWorkerFile`, `_BackgroundWorkerToTaskQueue`, `_TaskRunToTaskRunTag`, `_WaitpointRunConnections`, `_completedWaitpoints`, `SecretStore_key_idx`, and unrelated TaskRun indexes.
12+
- Never drop columns or tables without explicit approval.
13+
- New code should target `RunEngineVersion.V2` only.

.claude/rules/docs-writing.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
paths:
3+
- "docs/**"
4+
---
5+
6+
# Documentation Writing Rules
7+
8+
- Use Mintlify MDX format. Frontmatter: `title`, `description`, `sidebarTitle` (optional).
9+
- After creating a new page, add it to `docs.json` navigation under the correct group.
10+
- Use Mintlify components: `<Note>`, `<Warning>`, `<Info>`, `<Tip>`, `<CodeGroup>`, `<Expandable>`, `<Steps>`/`<Step>`.
11+
- Code examples should be complete and runnable where possible.
12+
- Always import from `@trigger.dev/sdk`, never `@trigger.dev/sdk/v3`.
13+
- Keep paragraphs short. Use headers to break up content.
14+
- Link to related pages using relative paths (e.g., `[Tasks](/tasks/overview)`).

.claude/rules/legacy-v3-code.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
paths:
3+
- "apps/webapp/app/v3/**"
4+
---
5+
6+
# Legacy V1 Engine Code in `app/v3/`
7+
8+
The `v3/` directory name is misleading - most code here is actively used by the current V2 engine. Only the specific files below are legacy V1-only code.
9+
10+
## V1-Only Files - Never Modify
11+
12+
- `marqs/` directory (entire MarQS queue system: sharedQueueConsumer, devQueueConsumer, fairDequeuingStrategy, devPubSub)
13+
- `legacyRunEngineWorker.server.ts` (V1 background job worker)
14+
- `services/triggerTaskV1.server.ts` (deprecated V1 task triggering)
15+
- `services/cancelTaskRunV1.server.ts` (deprecated V1 cancellation)
16+
- `authenticatedSocketConnection.server.ts` (V1 dev WebSocket using DevQueueConsumer)
17+
- `sharedSocketConnection.ts` (V1 shared queue socket using SharedQueueConsumer)
18+
19+
## V1/V2 Branching Pattern
20+
21+
Some services act as routers that branch on `RunEngineVersion`:
22+
- `services/cancelTaskRun.server.ts` - calls V1 service or `engine.cancelRun()` for V2
23+
- `services/batchTriggerV3.server.ts` - uses marqs for V1 path, run-engine for V2
24+
25+
When editing these shared services, only modify V2 code paths.
26+
27+
## V2 Modern Stack
28+
29+
- **Run lifecycle**: `@internal/run-engine` (internal-packages/run-engine)
30+
- **Background jobs**: `@trigger.dev/redis-worker` (not graphile-worker/zodworker)
31+
- **Queue operations**: RunQueue inside run-engine (not MarQS)
32+
- **V2 engine singleton**: `runEngine.server.ts`, `runEngineHandlers.server.ts`
33+
- **V2 workers**: `commonWorker.server.ts`, `alertsWorker.server.ts`, `batchTriggerWorker.server.ts`

.claude/rules/sdk-packages.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
paths:
3+
- "packages/**"
4+
---
5+
6+
# Public Package Rules
7+
8+
- Changes to `packages/` are **customer-facing**. Always add a changeset: `pnpm run changeset:add`
9+
- Default to **patch**. Get maintainer approval for minor. Never select major without explicit approval.
10+
- `@trigger.dev/core`: **Never import the root**. Always use subpath imports (e.g., `@trigger.dev/core/v3`).
11+
- Do NOT update `rules/` or `.claude/skills/trigger-dev-tasks/` unless explicitly asked. These are maintained in separate dedicated passes.
12+
- Test changes using `references/hello-world` reference project.

.claude/rules/server-apps.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
paths:
3+
- "apps/**"
4+
---
5+
6+
# Server App Changes
7+
8+
When modifying server apps (webapp, supervisor, coordinator, etc.) with **no package changes**, add a `.server-changes/` file instead of a changeset:
9+
10+
```bash
11+
cat > .server-changes/descriptive-name.md << 'EOF'
12+
---
13+
area: webapp
14+
type: fix
15+
---
16+
17+
Brief description of what changed and why.
18+
EOF
19+
```
20+
21+
- **area**: `webapp` | `supervisor` | `coordinator` | `kubernetes-provider` | `docker-provider`
22+
- **type**: `feature` | `fix` | `improvement` | `breaking`
23+
- If the PR also touches `packages/`, just the changeset is sufficient (no `.server-changes/` needed).
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
name: span-timeline-events
3+
description: Use when adding, modifying, or debugging OTel span timeline events in the trace view. Covers event structure, ClickHouse storage constraints, rendering in SpanTimeline component, admin visibility, and the step-by-step process for adding new events.
4+
allowed-tools: Read, Write, Edit, Glob, Grep, Bash
5+
---
6+
7+
# Span Timeline Events
8+
9+
The trace view's right panel shows a timeline of events for the selected span. These are OTel span events rendered by `app/utils/timelineSpanEvents.ts` and the `SpanTimeline` component.
10+
11+
## How They Work
12+
13+
1. **Span events** in OTel are attached to a parent span. In ClickHouse, they're stored as separate rows with `kind: "SPAN_EVENT"` sharing the parent span's `span_id`. The `#mergeRecordsIntoSpanDetail` method reassembles them into the span's `events` array at query time.
14+
2. The timeline only renders events whose `name` starts with `trigger.dev/` - all others are silently filtered out.
15+
3. The **display name** comes from `properties.event` (not the span event name), mapped through `getFriendlyNameForEvent()`.
16+
4. Events are shown on the **span they belong to** - events on one span don't appear in another span's timeline.
17+
18+
## ClickHouse Storage Constraint
19+
20+
When events are written to ClickHouse, `spanEventsToTaskEventV1Input()` filters out events whose `start_time` is not greater than the parent span's `startTime`. Events at or before the span start are silently dropped. This means span events must have timestamps strictly after the span's own `startTimeUnixNano`.
21+
22+
## Timeline Rendering (SpanTimeline component)
23+
24+
The `SpanTimeline` component in `app/components/run/RunTimeline.tsx` renders:
25+
26+
1. **Events** (thin 1px line with hollow dots) - all events from `createTimelineSpanEventsFromSpanEvents()`
27+
2. **"Started"** marker (thick cap) - at the span's `startTime`
28+
3. **Duration bar** (thick 7px line) - from "Started" to "Finished"
29+
4. **"Finished"** marker (thick cap) - at `startTime + duration`
30+
31+
The thin line before "Started" only appears when there are events with timestamps between the span start and the first child span. For the Attempt span this works well (Dequeued -> Pod scheduled -> Launched -> etc. all happen before execution starts). Events all get `lineVariant: "light"` (thin) while the execution bar gets `variant: "normal"` (thick).
32+
33+
## Trace View Sort Order
34+
35+
Sibling spans (same parent) are sorted by `start_time ASC` from the ClickHouse query. The `createTreeFromFlatItems` function preserves this order. Event timestamps don't affect sort order - only the span's own `start_time`.
36+
37+
## Event Structure
38+
39+
```typescript
40+
// OTel span event format
41+
{
42+
name: "trigger.dev/run", // Must start with "trigger.dev/" to render
43+
timeUnixNano: "1711200000000000000",
44+
attributes: [
45+
{ key: "event", value: { stringValue: "dequeue" } }, // The actual event type
46+
{ key: "duration", value: { intValue: 150 } }, // Optional: duration in ms
47+
]
48+
}
49+
```
50+
51+
## Admin-Only Events
52+
53+
`getAdminOnlyForEvent()` controls visibility. Events default to **admin-only** (`true`).
54+
55+
| Event | Admin-only | Friendly name |
56+
|-------|-----------|---------------|
57+
| `dequeue` | No | Dequeued |
58+
| `fork` | No | Launched |
59+
| `import` | No (if no fork event) | Importing task file |
60+
| `create_attempt` | Yes | Attempt created |
61+
| `lazy_payload` | Yes | Lazy attempt initialized |
62+
| `pod_scheduled` | Yes | Pod scheduled |
63+
| (default) | Yes | (raw event name) |
64+
65+
## Adding New Timeline Events
66+
67+
1. Add OTLP span event with `name: "trigger.dev/<scope>"` and `properties.event: "<type>"`
68+
2. Event timestamp must be strictly after the parent span's `startTimeUnixNano` (ClickHouse drops earlier events)
69+
3. Add friendly name in `getFriendlyNameForEvent()` in `app/utils/timelineSpanEvents.ts`
70+
4. Set admin visibility in `getAdminOnlyForEvent()`
71+
5. Optionally add help text in `getHelpTextForEvent()`
72+
73+
## Key Files
74+
75+
- `app/utils/timelineSpanEvents.ts` - filtering, naming, admin logic
76+
- `app/components/run/RunTimeline.tsx` - `SpanTimeline` component (thin line + thick bar rendering)
77+
- `app/presenters/v3/SpanPresenter.server.ts` - loads span data including events
78+
- `app/v3/eventRepository/clickhouseEventRepository.server.ts` - `spanEventsToTaskEventV1Input()` (storage filter), `#mergeRecordsIntoSpanDetail` (reassembly)

.cursor/rules/writing-tasks.mdc

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ alwaysApply: false
1414

1515
## Essential requirements when generating task code
1616

17-
1. You MUST use `@trigger.dev/sdk/v3`
17+
1. You MUST import from `@trigger.dev/sdk` (NEVER `@trigger.dev/sdk/v3`)
1818
2. You MUST NEVER use `client.defineJob`
1919
3. YOU MUST `export` every task, including subtasks
2020
4. If you are able to generate an example payload for a task, do so.
@@ -53,7 +53,7 @@ Instead, you MUST ALWAYS generate ONLY this pattern:
5353
```ts
5454
// ✅ ALWAYS GENERATE THIS EXACT PATTERN
5555

56-
import { task } from "@trigger.dev/sdk/v3";
56+
import { task } from "@trigger.dev/sdk";
5757

5858
//1. You need to export each task, even if it's a subtask
5959
export const helloWorld = task({
@@ -71,7 +71,7 @@ export const helloWorld = task({
7171
A task is a function that can run for a long time with resilience to failure:
7272

7373
```ts
74-
import { task } from "@trigger.dev/sdk/v3";
74+
import { task } from "@trigger.dev/sdk";
7575

7676
export const helloWorld = task({
7777
id: "hello-world",
@@ -271,7 +271,7 @@ Global lifecycle hooks can also be defined in `trigger.config.ts` to apply to al
271271
## Correct Schedules task (cron) implementations
272272

273273
```ts
274-
import { schedules } from "@trigger.dev/sdk/v3";
274+
import { schedules } from "@trigger.dev/sdk";
275275

276276
export const firstScheduledTask = schedules.task({
277277
id: "first-scheduled-task",
@@ -312,7 +312,7 @@ export const firstScheduledTask = schedules.task({
312312
### Attach a Declarative schedule
313313

314314
```ts
315-
import { schedules } from "@trigger.dev/sdk/v3";
315+
import { schedules } from "@trigger.dev/sdk";
316316

317317
// Sepcify a cron pattern (UTC)
318318
export const firstScheduledTask = schedules.task({
@@ -326,7 +326,7 @@ export const firstScheduledTask = schedules.task({
326326
```
327327

328328
```ts
329-
import { schedules } from "@trigger.dev/sdk/v3";
329+
import { schedules } from "@trigger.dev/sdk";
330330

331331
// Specify a specific timezone like this:
332332
export const secondScheduledTask = schedules.task({
@@ -375,7 +375,7 @@ const createdSchedule = await schedules.create({
375375
Schema tasks validate payloads against a schema before execution:
376376

377377
```ts
378-
import { schemaTask } from "@trigger.dev/sdk/v3";
378+
import { schemaTask } from "@trigger.dev/sdk";
379379
import { z } from "zod";
380380

381381
const myTask = schemaTask({
@@ -400,7 +400,7 @@ When you trigger a task from your backend code, you need to set the `TRIGGER_SEC
400400
Triggers a single run of a task with specified payload and options without importing the task. Use type-only imports for full type checking.
401401

402402
```ts
403-
import { tasks } from "@trigger.dev/sdk/v3";
403+
import { tasks } from "@trigger.dev/sdk";
404404
import type { emailSequence } from "~/trigger/emails";
405405

406406
export async function POST(request: Request) {
@@ -418,7 +418,7 @@ export async function POST(request: Request) {
418418
Triggers multiple runs of a single task with different payloads without importing the task.
419419

420420
```ts
421-
import { tasks } from "@trigger.dev/sdk/v3";
421+
import { tasks } from "@trigger.dev/sdk";
422422
import type { emailSequence } from "~/trigger/emails";
423423

424424
export async function POST(request: Request) {
@@ -436,7 +436,7 @@ export async function POST(request: Request) {
436436
Triggers multiple runs of different tasks at once, useful when you need to execute multiple tasks simultaneously.
437437

438438
```ts
439-
import { batch } from "@trigger.dev/sdk/v3";
439+
import { batch } from "@trigger.dev/sdk";
440440
import type { myTask1, myTask2 } from "~/trigger/myTasks";
441441

442442
export async function POST(request: Request) {
@@ -621,7 +621,7 @@ const handle = await myTask.trigger(
621621
Access metadata inside a run:
622622

623623
```ts
624-
import { task, metadata } from "@trigger.dev/sdk/v3";
624+
import { task, metadata } from "@trigger.dev/sdk";
625625

626626
export const myTask = task({
627627
id: "my-task",
@@ -713,7 +713,7 @@ Trigger.dev Realtime enables subscribing to runs for real-time updates on run st
713713
Subscribe to a run after triggering a task:
714714

715715
```ts
716-
import { runs, tasks } from "@trigger.dev/sdk/v3";
716+
import { runs, tasks } from "@trigger.dev/sdk";
717717

718718
async function myBackend() {
719719
const handle = await tasks.trigger("my-task", { some: "data" });
@@ -735,7 +735,7 @@ async function myBackend() {
735735
You can infer types of run's payload and output by passing the task type:
736736

737737
```ts
738-
import { runs } from "@trigger.dev/sdk/v3";
738+
import { runs } from "@trigger.dev/sdk";
739739
import type { myTask } from "./trigger/my-task";
740740

741741
for await (const run of runs.subscribeToRun<typeof myTask>(handle.id)) {
@@ -752,7 +752,7 @@ for await (const run of runs.subscribeToRun<typeof myTask>(handle.id)) {
752752
Stream data in realtime from inside your tasks using the metadata system:
753753

754754
```ts
755-
import { task, metadata } from "@trigger.dev/sdk/v3";
755+
import { task, metadata } from "@trigger.dev/sdk";
756756
import OpenAI from "openai";
757757

758758
export type STREAMS = {
@@ -947,7 +947,7 @@ For most use cases, Realtime hooks are preferred over SWR hooks with polling due
947947
For client-side usage, generate a public access token with appropriate scopes:
948948

949949
```ts
950-
import { auth } from "@trigger.dev/sdk/v3";
950+
import { auth } from "@trigger.dev/sdk";
951951

952952
const publicToken = await auth.createPublicToken({
953953
scopes: {
@@ -967,7 +967,7 @@ Idempotency ensures that an operation produces the same result when called multi
967967
Provide an `idempotencyKey` when triggering a task to ensure it runs only once with that key:
968968

969969
```ts
970-
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
970+
import { idempotencyKeys, task } from "@trigger.dev/sdk";
971971

972972
export const myTask = task({
973973
id: "my-task",
@@ -1058,7 +1058,7 @@ function hash(payload: any): string {
10581058

10591059
```ts
10601060
// onFailure executes after all retries are exhausted; use for notifications, logging, or side effects on final failure:
1061-
import { task, logger } from "@trigger.dev/sdk/v3";
1061+
import { task, logger } from "@trigger.dev/sdk";
10621062

10631063
export const loggingExample = task({
10641064
id: "logging-example",
@@ -1078,7 +1078,7 @@ export const loggingExample = task({
10781078
The `trigger.config.ts` file configures your Trigger.dev project, specifying task locations, retry settings, telemetry, and build options.
10791079

10801080
```ts
1081-
import { defineConfig } from "@trigger.dev/sdk/v3";
1081+
import { defineConfig } from "@trigger.dev/sdk";
10821082

10831083
export default defineConfig({
10841084
project: "<project ref>",
@@ -1226,7 +1226,7 @@ await myTask.trigger({ name: "Alice", age: 30 });
12261226

12271227
Before generating any code, you MUST verify:
12281228

1229-
1. Are you importing from `@trigger.dev/sdk/v3`? If not, STOP and FIX.
1229+
1. Are you importing from `@trigger.dev/sdk` (NOT `@trigger.dev/sdk/v3`)? If not, STOP and FIX.
12301230
2. Have you exported every task? If not, STOP and FIX.
12311231
3. Have you generated any DEPRECATED code patterns? If yes, STOP and FIX.
12321232

0 commit comments

Comments
 (0)