From e9725acf6822b748e647b4d908629c65f915d131 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Thu, 18 Jun 2026 14:34:50 -0700 Subject: [PATCH] perf(core): decouple workflow VM seed/clock from startedAt Derive the deterministic RNG seed from `runId:workflowName:deploymentId` and the VM's initial fixed clock from the ULID timestamp embedded in `runId` (via the new `runIdCreatedAt` helper). All of these inputs are available the instant a queue message arrives, so the VM seed and clock no longer depend on `startedAt` (set only after the `run_started` round-trip). This is the prerequisite for starting VM initialization earlier on the critical path. This changes the seed-derived value sequence for a given run, so the affected deterministic test fixtures are regenerated accordingly. --- .changeset/decouple-vm-seed-from-startedat.md | 5 + packages/core/src/runtime.test.ts | 6 +- packages/core/src/runtime/run-id-time.test.ts | 27 ++ packages/core/src/runtime/run-id-time.ts | 31 +++ .../runtime/wait-completion-replay.test.ts | 2 +- packages/core/src/workflow.test.ts | 242 +++++++++--------- packages/core/src/workflow.ts | 23 +- 7 files changed, 209 insertions(+), 127 deletions(-) create mode 100644 .changeset/decouple-vm-seed-from-startedat.md create mode 100644 packages/core/src/runtime/run-id-time.test.ts create mode 100644 packages/core/src/runtime/run-id-time.ts diff --git a/.changeset/decouple-vm-seed-from-startedat.md b/.changeset/decouple-vm-seed-from-startedat.md new file mode 100644 index 0000000000..c5d2114749 --- /dev/null +++ b/.changeset/decouple-vm-seed-from-startedat.md @@ -0,0 +1,5 @@ +--- +'@workflow/core': minor +--- + +Derive the workflow VM's deterministic RNG seed from `runId:workflowName:deploymentId` (instead of including the run's `startedAt`) and its initial fixed clock from the ULID timestamp embedded in `runId`. These inputs are all available the moment a queue message arrives, decoupling VM setup from the `run_started` round-trip. Note: this changes the seed-derived value sequence (step/hook correlation IDs, nanoids, random values) for a given run, so runs started before this change must not be replayed across the upgrade. diff --git a/packages/core/src/runtime.test.ts b/packages/core/src/runtime.test.ts index b7aca42635..4bddad798f 100644 --- a/packages/core/src/runtime.test.ts +++ b/packages/core/src/runtime.test.ts @@ -454,7 +454,7 @@ describe('workflowEntrypoint replay guards', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -464,7 +464,7 @@ describe('workflowEntrypoint replay guards', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:06.000Z'), }, @@ -549,7 +549,7 @@ describe('workflowEntrypoint replay guards', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'wrong-token', payload: await dehydrateStepReturnValue( diff --git a/packages/core/src/runtime/run-id-time.test.ts b/packages/core/src/runtime/run-id-time.test.ts new file mode 100644 index 0000000000..bfd40fdc49 --- /dev/null +++ b/packages/core/src/runtime/run-id-time.test.ts @@ -0,0 +1,27 @@ +import { ulid } from 'ulid'; +import { describe, expect, it } from 'vitest'; +import { runIdCreatedAt } from './run-id-time.js'; + +describe('runIdCreatedAt', () => { + it('recovers the creation time from a wrun_ run ID', () => { + const t = Date.UTC(2024, 0, 1, 0, 0, 0); + const runId = `wrun_${ulid(t)}`; + expect(runIdCreatedAt(runId)).toBe(t); + }); + + it('decodes a bare ULID without the wrun_ prefix', () => { + const t = Date.UTC(2025, 5, 15, 12, 30, 0); + expect(runIdCreatedAt(ulid(t))).toBe(t); + }); + + it('returns undefined for a non-ULID run ID', () => { + // Common in unit fixtures (e.g. `wrun_123`, `wrun_test`). + expect(runIdCreatedAt('wrun_123')).toBeUndefined(); + expect(runIdCreatedAt('wrun_test')).toBeUndefined(); + expect(runIdCreatedAt('not-a-run-id')).toBeUndefined(); + }); + + it('returns undefined for an empty string', () => { + expect(runIdCreatedAt('')).toBeUndefined(); + }); +}); diff --git a/packages/core/src/runtime/run-id-time.ts b/packages/core/src/runtime/run-id-time.ts new file mode 100644 index 0000000000..4654de686a --- /dev/null +++ b/packages/core/src/runtime/run-id-time.ts @@ -0,0 +1,31 @@ +import { decodeTime } from 'ulid'; + +/** + * Run IDs are minted client-side in `start()` as `wrun_` (see + * `runtime/start.ts`). A ULID encodes its creation time in its first 48 bits, + * so the run's creation timestamp is recoverable from the run ID alone — + * without any server round-trip or run-snapshot load. This is the earliest + * replay-stable timestamp a delivery has (the run ID arrives in the queue + * payload), which lets the workflow VM be seeded and clock-initialized before + * `run_started`. + */ +const RUN_ID_PREFIX = 'wrun_'; + +/** + * Extracts the run's creation timestamp (epoch milliseconds) from a `wrun_` + * run ID by decoding the embedded ULID time component. + * + * Returns `undefined` when `runId` is not a decodable `wrun_` (e.g. a + * legacy/non-ULID id, or a test fixture like `wrun_test`); callers fall back to + * an authoritative timestamp from the run snapshot (`createdAt`) in that case. + */ +export function runIdCreatedAt(runId: string): number | undefined { + const ulidPart = runId.startsWith(RUN_ID_PREFIX) + ? runId.slice(RUN_ID_PREFIX.length) + : runId; + try { + return decodeTime(ulidPart); + } catch { + return undefined; + } +} diff --git a/packages/core/src/runtime/wait-completion-replay.test.ts b/packages/core/src/runtime/wait-completion-replay.test.ts index 783d75bdb4..c4ca46982a 100644 --- a/packages/core/src/runtime/wait-completion-replay.test.ts +++ b/packages/core/src/runtime/wait-completion-replay.test.ts @@ -86,7 +86,7 @@ async function runStaleWaitReplayScenario(options: { ); const { globalThis: vmGlobalThis } = createContext({ - seed: `${runId}:${workflowName}:${+startedAt}`, + seed: `${runId}:${workflowName}:${deploymentId}`, fixedTimestamp: +startedAt, }); const ulid = monotonicFactory(() => vmGlobalThis.Math.random()); diff --git a/packages/core/src/workflow.test.ts b/packages/core/src/workflow.test.ts index 269826114e..3a51969164 100644 --- a/packages/core/src/workflow.test.ts +++ b/packages/core/src/workflow.test.ts @@ -174,7 +174,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', }, @@ -184,7 +184,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -257,7 +257,7 @@ describe('runWorkflow', () => { eventId: 'event-step1-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', }, @@ -267,7 +267,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', }, @@ -277,7 +277,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -293,7 +293,7 @@ describe('runWorkflow', () => { eventId: 'event-step2-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'step_01HK153X00SFW49DWMQP3J810T', eventData: { stepName: 'add', }, @@ -303,7 +303,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'step_01HK153X00SFW49DWMQP3J810T', eventData: { stepName: 'add', }, @@ -313,7 +313,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'step_01HK153X00SFW49DWMQP3J810T', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -329,7 +329,7 @@ describe('runWorkflow', () => { eventId: 'event-step3-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'add', }, @@ -339,7 +339,7 @@ describe('runWorkflow', () => { eventId: 'event-4', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'add', }, @@ -349,7 +349,7 @@ describe('runWorkflow', () => { eventId: 'event-5', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -425,8 +425,8 @@ describe('runWorkflow', () => { deploymentId: 'test-deployment', }; - const startStepId = 'step_01HK153X00SP082GGA0AAJC6PJ'; - const branchStepId = 'step_01HK153X00SP082GGA0AAJC6PK'; + const startStepId = 'step_01HK153X00SFW49DWMQP3J810S'; + const branchStepId = 'step_01HK153X00SFW49DWMQP3J810T'; const events: Event[] = [ { eventId: 'event-run-created', @@ -595,7 +595,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:01.000Z'), }, @@ -605,7 +605,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:02.000Z'), }, @@ -615,7 +615,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:01.000Z'), }, @@ -648,7 +648,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:02.000Z'), }, @@ -699,7 +699,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', }, @@ -709,7 +709,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', }, @@ -719,7 +719,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -735,7 +735,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -793,7 +793,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', }, @@ -803,7 +803,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', }, @@ -813,7 +813,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -829,7 +829,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -887,7 +887,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', }, @@ -897,7 +897,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', }, @@ -907,7 +907,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -923,7 +923,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -978,7 +978,7 @@ describe('runWorkflow', () => { const events: Event[] = [ { eventType: 'step_started', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCH', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7M', runId: 'wrun_01K75533W56DAE35VY3082DN3P', eventId: 'evnt_01K755385N02MMWXYHFCQSP9P0', eventData: { @@ -988,7 +988,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_started', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCJ', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7N', runId: 'wrun_01K75533W56DAE35VY3082DN3P', eventId: 'evnt_01K755386GHGAFYYDC58V17E3T', eventData: { @@ -998,7 +998,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_started', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCK', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7P', runId: 'wrun_01K75533W56DAE35VY3082DN3P', eventId: 'evnt_01K75538D4Q4X8PJ1ZNDZD5R0W', eventData: { @@ -1008,7 +1008,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_started', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCM', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7Q', runId: 'wrun_01K75533W56DAE35VY3082DN3P', eventId: 'evnt_01K75538Y9GEHXJQXT3JB89M4C', eventData: { @@ -1018,7 +1018,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_started', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCN', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7R', runId: 'wrun_01K75533W56DAE35VY3082DN3P', eventId: 'evnt_01K75539CD2PAH419SKJ2X5V5T', eventData: { @@ -1028,7 +1028,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_completed', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCN', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7R', eventData: { stepName: 'promiseRaceStressTestDelayStep', result: await dehydrateStepReturnValue( @@ -1044,7 +1044,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_completed', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCM', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7Q', eventData: { stepName: 'promiseRaceStressTestDelayStep', result: await dehydrateStepReturnValue( @@ -1060,7 +1060,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_completed', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCK', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7P', eventData: { stepName: 'promiseRaceStressTestDelayStep', result: await dehydrateStepReturnValue( @@ -1076,7 +1076,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_completed', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCJ', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7N', eventData: { stepName: 'promiseRaceStressTestDelayStep', result: await dehydrateStepReturnValue( @@ -1092,7 +1092,7 @@ describe('runWorkflow', () => { }, { eventType: 'step_completed', - correlationId: 'step_01HK153X00XRNYC8CR128NPYCH', + correlationId: 'step_01HK153X00WAVWBK9YGJQC6R7M', eventData: { stepName: 'promiseRaceStressTestDelayStep', result: await dehydrateStepReturnValue( @@ -1349,7 +1349,7 @@ describe('runWorkflow', () => { { type: 'step', stepName: 'add', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', args: [1, 2], }, ]); @@ -1380,7 +1380,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', }, @@ -1451,13 +1451,13 @@ describe('runWorkflow', () => { { type: 'step', stepName: 'add', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', args: [1, 2], }, { type: 'step', stepName: 'add', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', args: [3, 4], }, ]); @@ -1836,7 +1836,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -1894,7 +1894,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'wrong-token', payload: await dehydrateStepReturnValue( @@ -1946,7 +1946,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -1962,7 +1962,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2021,7 +2021,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2037,7 +2037,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2104,7 +2104,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2120,7 +2120,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2178,7 +2178,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2194,7 +2194,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2210,7 +2210,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRun.runId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', }, @@ -2220,7 +2220,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRun.runId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -2289,7 +2289,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -2305,7 +2305,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', }, @@ -2315,7 +2315,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRun.runId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -2377,7 +2377,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'my-custom-token', payload: await dehydrateStepReturnValue( @@ -2480,7 +2480,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_created', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: {}, createdAt: new Date(), }, @@ -2531,7 +2531,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_conflict', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'claim-only-token', conflictingRunId: 'wrun_conflicting_owner', @@ -2598,7 +2598,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_conflict', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'my-duplicate-token', conflictingRunId: 'wrun_conflicting', @@ -2655,7 +2655,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_conflict', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'conflicting-token', }, @@ -3470,7 +3470,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt, }, @@ -3480,7 +3480,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3533,7 +3533,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt, }, @@ -3543,7 +3543,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:06.000Z'), }, @@ -3632,7 +3632,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:02.000Z'), }, @@ -3642,7 +3642,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3652,7 +3652,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:02.000Z'), }, @@ -3662,7 +3662,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3716,7 +3716,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:02.000Z'), }, @@ -3726,7 +3726,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3736,7 +3736,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:02.000Z'), }, @@ -3787,7 +3787,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', }, @@ -3797,7 +3797,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -3813,7 +3813,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:03.000Z'), }, @@ -3823,7 +3823,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRT', eventData: { resumeAt: new Date('2024-01-01T00:00:03.000Z'), }, @@ -3881,7 +3881,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt, }, @@ -3891,7 +3891,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: resumeAt, }, @@ -3944,7 +3944,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3954,7 +3954,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3969,7 +3969,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'wait_completed', - correlationId: 'wait_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'wait_01HK153X00VFKAJV9XFN9JXXRS', eventData: { resumeAt: new Date('2024-01-01T00:00:05.000Z'), }, @@ -3979,7 +3979,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'doWork', }, @@ -3989,7 +3989,7 @@ describe('runWorkflow', () => { eventId: 'event-4', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'doWork', result: await dehydrateStepReturnValue('step done', ops), @@ -4037,7 +4037,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork1', }, @@ -4047,7 +4047,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork1', result: await dehydrateStepReturnValue('first done', ops), @@ -4059,7 +4059,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork1', result: await dehydrateStepReturnValue('duplicate', ops), @@ -4070,7 +4070,7 @@ describe('runWorkflow', () => { eventId: 'event-3', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'doWork2', }, @@ -4080,7 +4080,7 @@ describe('runWorkflow', () => { eventId: 'event-4', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HF', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRT', eventData: { stepName: 'doWork2', result: await dehydrateStepReturnValue('second done', ops), @@ -4139,7 +4139,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork', }, @@ -4149,7 +4149,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork', result: await dehydrateStepReturnValue('done', ops), @@ -4205,7 +4205,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork', }, @@ -4215,7 +4215,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'step_01HK153X00VFKAJV9XFN9JXXRS', eventData: { stepName: 'doWork', result: await dehydrateStepReturnValue('done', ops), @@ -4469,7 +4469,7 @@ describe('runWorkflow', () => { eventId: 'event-0', runId: workflowRun.runId, eventType: 'hook_created' as const, - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: {}, createdAt: new Date(), }, @@ -4477,7 +4477,7 @@ describe('runWorkflow', () => { eventId: 'event-1', runId: workflowRun.runId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', payload: await dehydrateStepReturnValue( @@ -4493,7 +4493,7 @@ describe('runWorkflow', () => { eventId: 'event-2', runId: workflowRun.runId, eventType: 'hook_disposed', - correlationId: 'hook_01HK153X00GYR8SV1JHHTGN5HE', + correlationId: 'hook_01HK153X00VFKAJV9XFN9JXXRS', eventData: { token: 'test-token', }, @@ -4599,9 +4599,9 @@ describe('runWorkflow', () => { // Correlation IDs match the deterministic ULID generator for the seed // `${runId}:${workflowName}:${+startedAt}` - const stepA = 'step_01HK153X00SP082GGA0AAJC6PJ'; - const stepB = 'step_01HK153X00SP082GGA0AAJC6PK'; - const stepC = 'step_01HK153X00SP082GGA0AAJC6PM'; + const stepA = 'step_01HK153X00SFW49DWMQP3J810S'; + const stepB = 'step_01HK153X00SFW49DWMQP3J810T'; + const stepC = 'step_01HK153X00SFW49DWMQP3J810V'; const events: Event[] = [ { @@ -4829,7 +4829,7 @@ describe('runWorkflow', () => { eventId: 'evnt-hook-created', runId: workflowRunId, eventType: 'hook_created', - correlationId: 'hook_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'hook_01HK153X00SFW49DWMQP3J810S', eventData: { token: 'test-token' }, createdAt: new Date('2024-01-01T00:00:00.200Z'), }, @@ -4837,7 +4837,7 @@ describe('runWorkflow', () => { eventId: 'evnt-wait-created', runId: workflowRunId, eventType: 'wait_created', - correlationId: 'wait_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'wait_01HK153X00SFW49DWMQP3J810T', eventData: { resumeAt: new Date('2024-01-02T00:00:00.000Z') }, createdAt: new Date('2024-01-01T00:00:00.300Z'), }, @@ -4845,7 +4845,7 @@ describe('runWorkflow', () => { eventId: 'evnt-hook-1', runId: workflowRunId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'hook_01HK153X00SFW49DWMQP3J810S', eventData: { token: 'test-token', payload: payload1, @@ -4856,7 +4856,7 @@ describe('runWorkflow', () => { eventId: 'evnt-step-1-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'processPayload', input: payload1 }, createdAt: new Date('2024-01-01T00:00:01.100Z'), }, @@ -4864,7 +4864,7 @@ describe('runWorkflow', () => { eventId: 'evnt-step-1-started', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'processPayload', }, @@ -4874,7 +4874,7 @@ describe('runWorkflow', () => { eventId: 'evnt-step-1-completed', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'processPayload', result: stepResult1, @@ -4885,7 +4885,7 @@ describe('runWorkflow', () => { eventId: 'evnt-hook-2', runId: workflowRunId, eventType: 'hook_received', - correlationId: 'hook_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'hook_01HK153X00SFW49DWMQP3J810S', eventData: { token: 'test-token', payload: payload2, @@ -4896,7 +4896,7 @@ describe('runWorkflow', () => { eventId: 'evnt-step-2-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PN', + correlationId: 'step_01HK153X00SFW49DWMQP3J810W', eventData: { stepName: 'processPayload', input: payload2 }, createdAt: new Date('2024-01-01T00:00:02.100Z'), }, @@ -4904,7 +4904,7 @@ describe('runWorkflow', () => { eventId: 'evnt-step-2-started', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PN', + correlationId: 'step_01HK153X00SFW49DWMQP3J810W', eventData: { stepName: 'processPayload', }, @@ -4914,7 +4914,7 @@ describe('runWorkflow', () => { eventId: 'evnt-step-2-completed', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PN', + correlationId: 'step_01HK153X00SFW49DWMQP3J810W', eventData: { stepName: 'processPayload', result: stepResult2, @@ -5017,7 +5017,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s1-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', }, @@ -5027,7 +5027,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s1-started', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', }, @@ -5037,7 +5037,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s1-completed', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PJ', + correlationId: 'step_01HK153X00SFW49DWMQP3J810S', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -5054,7 +5054,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s2-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'step_01HK153X00SFW49DWMQP3J810T', eventData: { stepName: 'add', }, @@ -5064,7 +5064,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s2-started', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'step_01HK153X00SFW49DWMQP3J810T', eventData: { stepName: 'add', }, @@ -5074,7 +5074,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s2-completed', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PK', + correlationId: 'step_01HK153X00SFW49DWMQP3J810T', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -5091,7 +5091,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s3-created', runId: workflowRunId, eventType: 'step_created', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'add', }, @@ -5101,7 +5101,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s3-started', runId: workflowRunId, eventType: 'step_started', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'add', }, @@ -5111,7 +5111,7 @@ describe('runWorkflow', () => { eventId: 'evnt-s3-completed', runId: workflowRunId, eventType: 'step_completed', - correlationId: 'step_01HK153X00SP082GGA0AAJC6PM', + correlationId: 'step_01HK153X00SFW49DWMQP3J810V', eventData: { stepName: 'add', result: await dehydrateStepReturnValue( @@ -5185,8 +5185,8 @@ describe('runWorkflow', () => { // Derive deterministic correlation IDs using the same seeded ULID // factory runWorkflow uses internally, so events match what the runtime - // expects. - const seed = `${workflowRunId}:${workflowRun.workflowName}:${+startedAt}`; + // expects. The seed mirrors runWorkflow's `runId:workflowName:deploymentId`. + const seed = `${workflowRunId}:${workflowRun.workflowName}:${workflowRun.deploymentId}`; const vm = createContext({ seed, fixedTimestamp: +startedAt }); const ulid = monotonicFactory(() => vm.globalThis.Math.random()); const hookCorr = `hook_${ulid(+startedAt)}`; diff --git a/packages/core/src/workflow.ts b/packages/core/src/workflow.ts index 6d3af6a89f..74f63d68c8 100644 --- a/packages/core/src/workflow.ts +++ b/packages/core/src/workflow.ts @@ -17,6 +17,7 @@ import { ENOTSUP, WorkflowSuspension } from './global.js'; import { runtimeLogger } from './logger.js'; import type { WorkflowOrchestratorContext } from './private.js'; import { getPortLazy } from './runtime/get-port-lazy.js'; +import { runIdCreatedAt } from './runtime/run-id-time.js'; import { handleSuspension } from './runtime/suspension-handler.js'; import { getWorld } from './runtime/world.js'; import { @@ -141,6 +142,24 @@ export async function runWorkflow( ); } + // The deterministic RNG seed is derived from identifiers that are all + // known the instant the queue message arrives — `runId`, `workflowName`, + // and `deploymentId` — with no timestamp component. `runId` alone already + // makes the seed unique-per-run and replay-stable; `workflowName` and + // `deploymentId` are included for extra entropy. Dropping the timestamp + // means the seed no longer depends on `startedAt`/`createdAt`, so it (and + // the VM context) can be computed before any server round-trip. + // + // The VM's initial fixed clock is derived from the run's creation time, + // recovered from the ULID embedded in `runId` (also available immediately), + // falling back to the run snapshot's `createdAt` for non-ULID ids. This + // initial `fixedTimestamp` only governs `Date.now()` / `new Date()` in the + // window before the first event is consumed; thereafter `updateTimestamp` + // advances the VM clock to each consumed event's `createdAt` (see the + // EventsConsumer below), starting with `run_created`. + const fixedTimestamp = + runIdCreatedAt(workflowRun.runId) ?? +workflowRun.createdAt; + // Get the port before creating VM context to avoid async operations // affecting the deterministic timestamp const isVercel = process.env.VERCEL_URL !== undefined; @@ -155,8 +174,8 @@ export async function runWorkflow( globalThis: vmGlobalThis, updateTimestamp, } = createContext({ - seed: `${workflowRun.runId}:${workflowRun.workflowName}:${+startedAt}`, - fixedTimestamp: +startedAt, + seed: `${workflowRun.runId}:${workflowRun.workflowName}:${workflowRun.deploymentId}`, + fixedTimestamp, }); const workflowDiscontinuation = withResolvers();