diff --git a/docs/sdk/services/workflow.md b/docs/sdk/services/workflow.md index 683d0b6..e9b71df 100644 --- a/docs/sdk/services/workflow.md +++ b/docs/sdk/services/workflow.md @@ -115,6 +115,44 @@ export const mainJob = createWorkflowJob({ **Important:** On the Tailor runtime, job triggers are executed synchronously. This means `Promise.all([jobA.trigger(), jobB.trigger()])` will not run jobs in parallel. +### Deterministic Execution Requirement + +Workflow jobs use a **suspend/resume execution model**. When a job calls `.trigger()`, the runtime suspends the current job, executes the triggered job, and then **re-executes the calling job from the beginning** with cached results from previous triggers. + +This means that **job code must be deterministic** — every re-execution must produce the same sequence of `.trigger()` calls with the same arguments in the same order. + +Using `.trigger()` inside a loop works correctly, as long as the loop is deterministic: + +```typescript +// ✅ OK: deterministic loop — same calls in the same order on every execution +const regions = ["us", "eu", "ap"]; +for (const region of regions) { + const result = await fetchData.trigger({ region }); + results.push(result); +} +``` + +```typescript +// ❌ Bad: non-deterministic — argument changes between executions +await processJob.trigger({ timestamp: Date.now() }); +``` + +```typescript +// ❌ Bad: non-deterministic — external data may change between executions +const items = await fetch("https://api.example.com/items").then((r) => r.json()); +for (const item of items) { + await processItem.trigger({ id: item.id }); +} +``` + +If the runtime detects that a `.trigger()` call at the same position has different arguments than the previous execution, it will throw an **argument hash mismatch error**. + +**Guidelines:** + +- Do not use non-deterministic values (random numbers, timestamps, external API responses) as `.trigger()` arguments. +- Do not use conditions that may change between executions to decide whether to call `.trigger()`. +- Any data that varies between executions should be fetched **inside the triggered job**, not passed as an argument from the calling job. + ## Workflow Definition Define a workflow using `createWorkflow` and export it as default: