From 6f1789c899be2dc7d3acf13c969a6e91a8e29550 Mon Sep 17 00:00:00 2001 From: Misato Takahashi Date: Thu, 9 Apr 2026 11:56:12 +0900 Subject: [PATCH] docs(workflow): add deterministic execution requirement section Document the suspend/resume execution model and the requirement that job code must be deterministic when using .trigger(). Include examples of correct and incorrect usage patterns (loops, timestamps, external API calls) to help users avoid argument hash mismatch errors. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/sdk/services/workflow.md | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) 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: