Skip to content

QuickJS engine: threshold-based VM-memory snapshotting (WORKFLOW_SNAPSHOT_THRESHOLD) - #3251

Open
TooTallNate wants to merge 3 commits into
quickjs-vm-snapshotsfrom
quickjs-vm-threshold-snapshots
Open

QuickJS engine: threshold-based VM-memory snapshotting (WORKFLOW_SNAPSHOT_THRESHOLD)#3251
TooTallNate wants to merge 3 commits into
quickjs-vm-snapshotsfrom
quickjs-vm-threshold-snapshots

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Note

Supersedes #3053, which GitHub auto-marked as merged (and auto-deleted the head branch) when a restacking mistake briefly force-pushed the head branch to the same commit as its base. Same content, freshly rebased on the stack.

Stacked PR — based on #3050 (quickjs-vm-snapshots) ← #3049#3048. Review only the top commit here until the bases merge.

Summary

PR 4 of the QuickJS VM roadmap: threshold-based VM-memory snapshotting — the middle ground that motivated reviving this effort (see #1298 / #1300 discussion). Instead of snapshotting at every suspension (the original branch's model, which cost ~25% on e2e wall clock), snapshots are taken only once WORKFLOW_SNAPSHOT_THRESHOLD events have been processed since the last one:

  • Short-lived runs never snapshot — they keep PR 1/2's pure replay behavior with zero snapshot overhead.
  • Long/forever runs stop scaling their resume cost with event-log length — a resumption restores the VM heap and replays only the delta events since the snapshot's cursor.

How it works

  • WORKFLOW_SNAPSHOT_THRESHOLD env var (default 0 = disabled) or per-run executionContext.snapshotThreshold, stamped at start() for run affinity like WORKFLOW_VM.
  • Save (suspension exit, threshold met): capture live VM memory (session.snapshot()) → compress (zstd/gzip via the shared serialization pipeline; QuickJS heaps compress ~4×, measured 16.5 MB → 3.9 MB) → encrypt with the run's key when configured → world.snapshots.save with the events cursor at the VM's feed frontier.
  • Restore (subsequent invocation): world.snapshots.load → decrypt → decompress → QuickJS.restore over the cached WASM module, re-register host callbacks, fetch events from the snapshot's cursor and feed only the delta. Runs seamlessly through PR 2's inline continuation loop.
  • Delete on run completion/failure.
  • Fallback is always full replay: missing snapshot, load error, corrupt bytes, or restore failure logs a warning and boots fresh against the full event log — the log remains the source of truth; snapshots are strictly an optimization.

Determinism model (restore + partial replay)

The threshold model's new mechanism vs. the original branch: a resumption may restore a snapshot older than the log head (suspensions since the snapshot weren't persisted) and must deterministically re-derive everything in between:

  • The PRNG seed mixes in the restored snapshot's eventsCursor: the heap already consumed pre-snapshot draws, so re-seeding from the base would replay the first-N draws and collide with recorded correlationIds. The cursor is identical for every resume from the same snapshot (concurrent resumes still collide ids for the world's dedup) and advances only when a newer snapshot is taken.
  • Feeding already-consumed events is harmless by construction (consumed resolvers are gone; hook deliveries are deduped by eventId in the VM heap, which travels with the snapshot), so imprecise cursors only cost redundant scanning.
  • Covered by dedicated unit tests, including restore-from-older-snapshot with multi-suspension partial replay and identical post-restore correlationIds across concurrent resumes.

Validation

  • 135/135 e2e on nextjs-turbopack with WORKFLOW_SNAPSHOT_THRESHOLD=1 (maximum churn: snapshot on every qualifying suspension), wall clock within ~10% of the node baseline
  • Verified via debug diagnostics: restored: true resumptions, save/restore/delete lifecycle, and threshold gating (threshold=100 short run ⇒ zero snapshots, pure replay)
  • Full core unit suite green (1,573 tests); new tests for the config knobs and the snapshot/restore/partial-replay determinism
  • CI: new quickjs-snapshot matrix leg (nextjs-turbopack, threshold=1) across local dev/prod/postgres e2e jobs

Notes / follow-ups

  • Version skew: snapshot bytes are tied to the quickjs-wasi build that produced them. Per the deployment contract (runs continue on the version they started on — free on Vercel), this is a non-issue in production; environments without skew protection are covered by the restore-failure fallback to full replay.
  • An ID-divergence window exists when concurrent invocations resume from different snapshot generations; the world's per-(run, correlation) uniqueness rejects duplicates and the log-consistent invocation drives progress, with full-replay convergence as the backstop. Noted in code comments.
  • Docs: WORKFLOW_SNAPSHOT_THRESHOLD section added to v5 Runtime Tuning.

Copilot AI review requested due to automatic review settings July 31, 2026 03:22
@TooTallNate
TooTallNate requested review from a team and ijjk as code owners July 31, 2026 03:22
@changeset-bot

changeset-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e294ed1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@workflow/core Minor
workflow Minor
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/web Patch
@workflow/world-testing Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview Aug 4, 2026 12:32am
example-nextjs-workflow-webpack Ready Ready Preview Aug 4, 2026 12:32am
example-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-astro-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-express-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-fastify-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-hono-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-nestjs-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-nitro-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-nuxt-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-sveltekit-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-tanstack-start-workflow Ready Ready Preview Aug 4, 2026 12:32am
workbench-vite-workflow Ready Ready Preview Aug 4, 2026 12:32am
workflow-docs Ready Ready Preview, v0 Aug 4, 2026 12:32am
workflow-swc-playground Ready Ready Preview Aug 4, 2026 12:32am
workflow-tarballs Ready Ready Preview Aug 4, 2026 12:32am
workflow-web Ready Ready Preview Aug 4, 2026 12:32am

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (510 failed)

astro-node (24 failed):

  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y | 🔍 observability
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KZ53C0BF0GPY9V7E2515W234 | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J | 🔍 observability
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_41KZ53CJ6C0GQS11PYHC2E26XY | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works

astro-quickjs (12 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability

example-node (21 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step

example-quickjs (41 failed):

  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y | 🔍 observability
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KZ53C0BF0GPY9V7E2515W234 | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J | 🔍 observability
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-hook-first]: hook.then → addEventListener → resumeHook → abort()
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_41KZ53BH7Q0GXA54F2J3YARZ0E | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_41KZ53BKFT0GWR0RGFQNKPC6XZ | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_41KZ53BNF40GT9DW27YWPHFBB7 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_41KZ53GR5C0GP70TERWF8KP6VB | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_41KZ53GXNC0GX4F53EN9RK31C7 | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_41KZ53H0MZ0GRP47D5XD507QAX | 🔍 observability
  • distributedAbortController - reconnect to existing controller | wrun_41KZ53BQR60GGFT0T0DMQXMMHX | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events

express-node (17 failed):

  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability

express-quickjs (19 failed):

  • sleepWinsRaceWorkflow | wrun_41KZ52ZQ4Z0GSVH3S6STZP15GM | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • AbortController abortParallelWorkflow: abort cancels all parallel steps

fastify-node (40 failed):

  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y | 🔍 observability
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KZ53C0BF0GPY9V7E2515W234 | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J | 🔍 observability
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_41KZ53CJ6C0GQS11PYHC2E26XY | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortFetchInFlightWorkflow: aborting cancels an in-flight fetch
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_41KZ53BH7Q0GXA54F2J3YARZ0E | 🔍 observability
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_41KZ53BKFT0GWR0RGFQNKPC6XZ | 🔍 observability
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_41KZ53BNF40GT9DW27YWPHFBB7 | 🔍 observability
  • getterStepWorkflow - getter functions with "use step" directive | wrun_41KZ53GR5C0GP70TERWF8KP6VB | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_41KZ53GXNC0GX4F53EN9RK31C7 | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_41KZ53H0MZ0GRP47D5XD507QAX | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-quickjs (27 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ | 🔍 observability
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay

hono-node (18 failed):

  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability

hono-quickjs (25 failed):

  • sleepWinsRaceWorkflow | wrun_41KZ52ZQ4Z0GSVH3S6STZP15GM | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y | 🔍 observability
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_41KZ53CJ6C0GQS11PYHC2E26XY | 🔍 observability
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal

nextjs-turbopack-node (23 failed):

  • error handling not registered WorkflowNotRegisteredError fails the run when workflow does not exist
  • error handling not registered StepNotRegisteredError fails the step but workflow can catch it
  • error handling not registered StepNotRegisteredError fails the run when not caught in workflow
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41KZ536MS70GQRTHFBDDX1EQ73 | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41KZ536ZEG0GVH4STTZ1CE1VCK | 🔍 observability
  • hookGetConflictWorkflow - awaiting hook.getConflict() registers hook without payload | wrun_41KZ537ASC0GWBFMJYJ9ZR5SMW | 🔍 observability
  • 'hookGetConflictWithPriorStepWorkflow' - hook.getConflict() does not block step execution | wrun_41KZ537D1V0GXE9VEYS0SE5JPG | 🔍 observability
  • 'hookGetConflictWithParallelStepWorkfl…' - hook.getConflict() does not block step execution | wrun_41KZ537FBS0GMPGHSY42VVAGBF | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41KZ537YD00GP59CFHWZ3SQDHZ | 🔍 observability
  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability

nextjs-turbopack-quickjs (14 failed):

  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • error handling catchability workflow throw round-trips FatalError + cause through run_failed event
  • error handling catchability step throw of a non-Error value preserves it as cause on the wrapping FatalError
  • error handling not registered WorkflowNotRegisteredError fails the run when workflow does not exist
  • error handling not registered StepNotRegisteredError fails the step but workflow can catch it
  • error handling not registered StepNotRegisteredError fails the run when not caught in workflow
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41KZ536MS70GQRTHFBDDX1EQ73 | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41KZ536ZEG0GVH4STTZ1CE1VCK | 🔍 observability
  • hookGetConflictWorkflow - awaiting hook.getConflict() registers hook without payload | wrun_41KZ537ASC0GWBFMJYJ9ZR5SMW | 🔍 observability
  • 'hookGetConflictWithParallelStepWorkfl…' - hook.getConflict() does not block step execution | wrun_41KZ537FBS0GMPGHSY42VVAGBF | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability

nextjs-webpack-node (15 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • pages router sleepingWorkflow via pages router

nextjs-webpack-quickjs (21 failed):

  • sleepWinsRaceWorkflow | wrun_41KZ52ZQ4Z0GSVH3S6STZP15GM | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41KZ536ZEG0GVH4STTZ1CE1VCK | 🔍 observability
  • hookGetConflictWorkflow - awaiting hook.getConflict() registers hook without payload | wrun_41KZ537ASC0GWBFMJYJ9ZR5SMW | 🔍 observability
  • 'hookGetConflictWithPriorStepWorkflow' - hook.getConflict() does not block step execution | wrun_41KZ537D1V0GXE9VEYS0SE5JPG | 🔍 observability
  • 'hookGetConflictWithParallelStepWorkfl…' - hook.getConflict() does not block step execution | wrun_41KZ537FBS0GMPGHSY42VVAGBF | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41KZ537YD00GP59CFHWZ3SQDHZ | 🔍 observability
  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability

nitro-node (15 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J | 🔍 observability

nitro-quickjs (27 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KZ53C0BF0GPY9V7E2515W234 | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay

nuxt-node (14 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ | 🔍 observability

nuxt-quickjs (16 failed):

  • 'hookGetConflictWithParallelStepWorkfl…' - hook.getConflict() does not block step execution | wrun_41KZ537FBS0GMPGHSY42VVAGBF | 🔍 observability
  • hookGetConflictThenStepParallelWorkflow - hook.getConflict() continuation step runs alongside other steps | wrun_41KZ537HR80GWSTY3W9MRDQ1CP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41KZ537YD00GP59CFHWZ3SQDHZ | 🔍 observability
  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability

sveltekit-node (13 failed):

  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step

sveltekit-quickjs (25 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals

vite-node (50 failed):

  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y | 🔍 observability
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KZ53C0BF0GPY9V7E2515W234 | 🔍 observability
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J | 🔍 observability
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_41KZ53CJ6C0GQS11PYHC2E26XY | 🔍 observability
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ | 🔍 observability
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortParallelWorkflow: abort cancels all parallel steps
  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortAnyInWorkflowWorkflow: AbortSignal.any composes signals inside the workflow VM
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals
  • AbortController abortSurvivesReplayWorkflow: controller state consistent across replay
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries
  • AbortController abortReasonTypesWorkflow: various abort reason types propagate correctly
  • AbortController abortFetchUncaughtWorkflow: uncaught fetch AbortError is FatalError, no retries
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortListenerWorkflow: signal.addEventListener fires on the deserialized step signal
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook
  • getterStepWorkflow - getter functions with "use step" directive | wrun_41KZ53GR5C0GP70TERWF8KP6VB | 🔍 observability
  • distributedAbortController - manual abort triggers signal | wrun_41KZ53GXNC0GX4F53EN9RK31C7 | 🔍 observability
  • distributedAbortController - TTL expiration triggers signal | wrun_41KZ53H0MZ0GRP47D5XD507QAX | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (33 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS | 🔍 observability
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B | 🔍 observability
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY | 🔍 observability
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93 | 🔍 observability
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N | 🔍 observability
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ | 🔍 observability
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT | 🔍 observability
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5 | 🔍 observability
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF | 🔍 observability
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD | 🔍 observability
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS | 🔍 observability
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM | 🔍 observability
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX | 🔍 observability
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77 | 🔍 observability
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN | 🔍 observability
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV | 🔍 observability
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK | 🔍 observability
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW | 🔍 observability
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK | 🔍 observability
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9 | 🔍 observability
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P | 🔍 observability
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK | 🔍 observability
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY | 🔍 observability
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortDeterministicBranchWorkflow: if-check takes same path on first-run and replay
  • AbortController abortThrowIfAbortedMidFlightWorkflow: throwIfAborted in a polling loop bails when abort fires
  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
  • AbortController abortHookOrderingWorkflow [listener-first-abort-first]: addEventListener → hook.then → abort() → resumeHook
  • AbortController abortHookOrderingWorkflow [listener-first-hook-first]: addEventListener → hook.then → resumeHook → abort()
  • AbortController abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook

💻 Local Development (1 failed)

astro-stable-node (1 failed):

  • webhookWorkflow | wrun_41KZ52YCGS0GVW8E67N5RYPY2V
📋 Other (84 failed)

e2e-vercel-prod-nest-node (19 failed):

  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK
  • cancelRun via CLI - cancelling a running workflow | wrun_41KZ53BQQ80GKBZRYV6DC0MB1Y
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ
  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step
  • AbortController abortAlreadyAbortedWorkflow: pre-aborted signal seen by step
  • AbortController abortReasonWorkflow: abort reason preserved across boundaries
  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries

e2e-vercel-prod-nest-quickjs (23 failed):

  • hookClaimOnlyMutexWorkflow - hook works as a pure run mutex without payload data | wrun_41KZ538QE20GYAKSYYHQJXPDSS
  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_41KZ53ANK80GK0S4CBRRX1DJEK
  • cancelRun - cancelling a running workflow | wrun_41KZ53BJH20GJA5P3R5PE0KHQY
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortAnyInStepWorkflow: AbortSignal.any inside a step composes deserialized signals

e2e-vercel-prod-tanstack-start-node (22 failed):

  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_41KZ53AD0C0GQ082K0X9KJR0SV
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KZ53AJR40GYW61JSPGHT6FMK
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_41KZ53B2DH0GWTGJNS9H37E0T9
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KZ53C0BF0GPY9V7E2515W234
  • hookWithSleepFinalStepWorkflow - step only on final payload | wrun_41KZ53CAQA0GZ8H2VHBF7HFP2J
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_41KZ53CWMN0GWBXKKTS3S547SZ
  • AbortController abortTimeoutWorkflow: timeout cancels long-running step
  • AbortController abortAfterCompletionWorkflow: abort after step completes is a no-op
  • AbortController abortViaHookWorkflow: external hook triggers abort on in-flight step
  • AbortController abortExternalSignalWorkflow: signal passed as workflow input
  • AbortController abortExternalSignalInFlightWorkflow: external abort fires mid-flight, propagates to nested steps
  • AbortController abortVoidSleepTimeoutWorkflow: documented void sleep().then(abort) pattern works

e2e-vercel-prod-tanstack-start-quickjs (20 failed):

  • hookAdoptOwnerResultWorkflow - duplicate adopts the owner result via conflict.returnValue | wrun_41KZ538T420GGSQ450GCT0JA0B
  • hookSignalOwnerWorkflow - duplicate forwards its payload to the owner via resumeHook | wrun_41KZ538XY60GJWX7ETJAAGQZQY
  • hookSupersedeOwnerWorkflow - duplicate cancels the owner and claims the released token | wrun_41KZ5391910GQNAA2MERJ3PA93
  • resume-or-start route pattern - resumeHook retried after start() reaches the new run | wrun_41KZ5398770GVX7D8XQNQ50G0N
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_41KZ539AZH0GN9G1FKVQ4Z5BDZ
  • hookTokenReuseLoopWorkflow - same run recreates a hook with the same token after dispose() | wrun_41KZ539EMK0GYFM0K1GJQ0DKYT
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_41KZ539KR00GGQXF6T310KEDZ5
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_41KZ53AA6Y0GRS6060WDN0DKGF
  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KZ53AE210GKHYJGW62Y1AZAD
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_41KZ539S8Y0GYCV9CCHB9P5X2E
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_41KZ53AVRV0GT2FX2N93YDJWWS
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_41KZ53AZ9Y0GPCR7E4TH8TPHXM
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_41KZ53AGPT0GN1NF0F24VH6YCX
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_41KZ53A6Y50GM7CRW6HMHBTV77
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_41KZ53A1EN0GQYY1PQ53HTD5BN
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_41KZ53A7BE0GXFKWDSEHNN3YSX
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KZ53ACS10GPRJ3P8BVMEMMRW
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_41KZ53AJ130GMXXMR5W3R499WK
  • errorSubclassRoundTripWorkflow - first-class Error subclasses survive every serialization boundary | wrun_41KZ53B9690GNRS1HY24CKNC2P
  • distributedAbortController - TTL expiration triggers signal | wrun_41KZ53H0MZ0GRP47D5XD507QAX

E2E Test Summary

Summary
Passed Failed Skipped Total
❌ ▲ Vercel Production 2422 510 478 3410
❌ 💻 Local Development 3284 1 435 3720
✅ 📦 Local Production 3421 0 454 3875
✅ 🐘 Local Postgres 3421 0 454 3875
✅ 🪟 Windows 310 0 0 310
❌ 📋 Other 1972 84 424 2480
✅ vercel-multi-region 27 0 0 27
Total 14857 595 2245 17697
Details by Category

❌ ▲ Vercel Production

App Passed Failed Skipped
❌ astro-node 103 24 28
❌ astro-quickjs 115 12 28
❌ example-node 106 21 28
❌ example-quickjs 86 41 28
❌ express-node 110 17 28
❌ express-quickjs 108 19 28
❌ fastify-node 87 40 28
❌ fastify-quickjs 100 27 28
❌ hono-node 109 18 28
❌ hono-quickjs 102 25 28
❌ nextjs-turbopack-node 129 23 3
❌ nextjs-turbopack-quickjs 138 14 3
❌ nextjs-webpack-node 137 15 3
❌ nextjs-webpack-quickjs 131 21 3
❌ nitro-node 112 15 28
❌ nitro-quickjs 100 27 28
❌ nuxt-node 113 14 28
❌ nuxt-quickjs 111 16 28
❌ sveltekit-node 133 13 9
❌ sveltekit-quickjs 121 25 9
❌ vite-node 77 50 28
❌ vite-quickjs 94 33 28

❌ 💻 Local Development

App Passed Failed Skipped
❌ astro-stable-node 128 1 26
✅ astro-stable-quickjs 129 0 26
✅ express-stable-node 129 0 26
✅ express-stable-quickjs 129 0 26
✅ fastify-stable-node 129 0 26
✅ fastify-stable-quickjs 129 0 26
✅ hono-stable-node 129 0 26
✅ hono-stable-quickjs 129 0 26
✅ nextjs-turbopack-canary-node 136 0 19
✅ nextjs-turbopack-canary-quickjs 136 0 19
✅ nextjs-turbopack-quickjs-snapshot 155 0 0
✅ nextjs-turbopack-stable-node 155 0 0
✅ nextjs-turbopack-stable-quickjs 155 0 0
✅ nextjs-webpack-canary-quickjs 136 0 19
✅ nextjs-webpack-stable-node 155 0 0
✅ nextjs-webpack-stable-quickjs 155 0 0
✅ nitro-stable-node 129 0 26
✅ nitro-stable-quickjs 129 0 26
✅ nuxt-stable-node 129 0 26
✅ nuxt-stable-quickjs 129 0 26
✅ sveltekit-stable-node 148 0 7
✅ sveltekit-stable-quickjs 148 0 7
✅ vite-stable-node 129 0 26
✅ vite-stable-quickjs 129 0 26

✅ 📦 Local Production

App Passed Failed Skipped
✅ astro-stable-node 129 0 26
✅ astro-stable-quickjs 129 0 26
✅ express-stable-node 129 0 26
✅ express-stable-quickjs 129 0 26
✅ fastify-stable-node 129 0 26
✅ fastify-stable-quickjs 129 0 26
✅ hono-stable-node 129 0 26
✅ hono-stable-quickjs 129 0 26
✅ nextjs-turbopack-canary-node 136 0 19
✅ nextjs-turbopack-canary-quickjs 136 0 19
✅ nextjs-turbopack-quickjs-snapshot 155 0 0
✅ nextjs-turbopack-stable-node 155 0 0
✅ nextjs-turbopack-stable-quickjs 155 0 0
✅ nextjs-webpack-canary-node 136 0 19
✅ nextjs-webpack-canary-quickjs 136 0 19
✅ nextjs-webpack-stable-node 155 0 0
✅ nextjs-webpack-stable-quickjs 155 0 0
✅ nitro-stable-node 129 0 26
✅ nitro-stable-quickjs 129 0 26
✅ nuxt-stable-node 129 0 26
✅ nuxt-stable-quickjs 129 0 26
✅ sveltekit-stable-node 148 0 7
✅ sveltekit-stable-quickjs 148 0 7
✅ vite-stable-node 129 0 26
✅ vite-stable-quickjs 129 0 26

✅ 🐘 Local Postgres

App Passed Failed Skipped
✅ astro-stable-node 129 0 26
✅ astro-stable-quickjs 129 0 26
✅ express-stable-node 129 0 26
✅ express-stable-quickjs 129 0 26
✅ fastify-stable-node 129 0 26
✅ fastify-stable-quickjs 129 0 26
✅ hono-stable-node 129 0 26
✅ hono-stable-quickjs 129 0 26
✅ nextjs-turbopack-canary-node 136 0 19
✅ nextjs-turbopack-canary-quickjs 136 0 19
✅ nextjs-turbopack-quickjs-snapshot 155 0 0
✅ nextjs-turbopack-stable-node 155 0 0
✅ nextjs-turbopack-stable-quickjs 155 0 0
✅ nextjs-webpack-canary-node 136 0 19
✅ nextjs-webpack-canary-quickjs 136 0 19
✅ nextjs-webpack-stable-node 155 0 0
✅ nextjs-webpack-stable-quickjs 155 0 0
✅ nitro-stable-node 129 0 26
✅ nitro-stable-quickjs 129 0 26
✅ nuxt-stable-node 129 0 26
✅ nuxt-stable-quickjs 129 0 26
✅ sveltekit-stable-node 148 0 7
✅ sveltekit-stable-quickjs 148 0 7
✅ vite-stable-node 129 0 26
✅ vite-stable-quickjs 129 0 26

✅ 🪟 Windows

App Passed Failed Skipped
✅ nextjs-turbopack-node 155 0 0
✅ nextjs-turbopack-quickjs 155 0 0

❌ 📋 Other

App Passed Failed Skipped
✅ e2e-local-dev-nest-stable-node 129 0 26
✅ e2e-local-dev-nest-stable-quickjs 129 0 26
✅ e2e-local-dev-tanstack-start-node 129 0 26
✅ e2e-local-dev-tanstack-start-quickjs 129 0 26
✅ e2e-local-postgres-nest-stable-node 129 0 26
✅ e2e-local-postgres-nest-stable-quickjs 129 0 26
✅ e2e-local-postgres-tanstack-start-node 129 0 26
✅ e2e-local-postgres-tanstack-start-quickjs 129 0 26
✅ e2e-local-prod-nest-stable-node 129 0 26
✅ e2e-local-prod-nest-stable-quickjs 129 0 26
✅ e2e-local-prod-tanstack-start-node 129 0 26
✅ e2e-local-prod-tanstack-start-quickjs 129 0 26
❌ e2e-vercel-prod-nest-node 108 19 28
❌ e2e-vercel-prod-nest-quickjs 104 23 28
❌ e2e-vercel-prod-tanstack-start-node 105 22 28
❌ e2e-vercel-prod-tanstack-start-quickjs 107 20 28

✅ vercel-multi-region

App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run

@VaguelySerious VaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review: blocking issues found

Comment thread packages/core/src/runtime/quickjs-entrypoint.ts
Comment thread packages/core/src/runtime/quickjs-runtime.ts Outdated
Comment thread packages/core/src/runtime/quickjs-runtime.ts Outdated
Comment thread packages/core/src/runtime/quickjs-entrypoint.ts Outdated
Comment thread packages/core/src/runtime/quickjs-entrypoint.ts
Comment thread packages/core/src/runtime/quickjs-entrypoint.ts Outdated
Comment thread packages/core/src/runtime/quickjs-entrypoint.ts Outdated
…based PRNG fast-forward, unified host-callback list, lifecycle hardening

- SnapshotMetadata gains eventCount, rngDraws and formatVersion. The
  max-events guard now compares restored total + delta (both at entry
  and per loop turn) — previously a run that kept snapshotting could
  never accumulate enough delta to trip the ceiling it exists for.
- Correlation-id generation is position-based across snapshots: the
  runtime seeds from the BASE seed and fast-forwards the persisted draw
  count instead of mixing the snapshot cursor into the seed. Ids are now
  identical across snapshot generations AND identical to a no-snapshot
  run, so overlapping invocations straddling a snapshot save still
  collide on the world's dedup (new test pins restored ids == full-replay
  ids). Snapshots without a draw count fall back to full replay.
- Host callbacks are declared in ONE list that drives both the fresh-boot
  install and the restore re-registration, so adding a callback can't
  silently skip the restore path.
- Preloaded events are used again with snapshotting enabled (the first
  qualifying suspension skips its save — no cursor yet); short runs keep
  the zero-round-trip fast path.
- Snapshot persist runs off the response path (waitUntil), with a 32MB
  plaintext size ceiling (skip + warn). Loads that fail format/shape
  checks warn instead of silently miming a miss. Terminal deletes are
  gated on a snapshot actually existing and now also fire on the runGone
  path; a server-side TTL remains the backstop for unobserved
  cancellations.
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit e294ed1 · Tue, 04 Aug 2026 00:47:10 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 254 (-63%) 💚 1367 🔴 (+43%) 🔻 1400 🔴 (+41%) 🔻 1454 🔴 (-11%) 30
TTFS stream 313 (-66%) 💚 1394 🔴 (+43%) 🔻 1427 🔴 (+44%) 🔻 1430 🔴 (+40%) 🔻 30
TTFS hook + stream 1243 (+246%) 🔻 1632 🔴 (+34%) 🔻 1657 🔴 (+25%) 🔻 1733 🔴 (+1.2%) 30
STSO 1020 steps (inline) 84 (±0%) 128 (-17%) 💚 152 (-16%) 💚 298 (-10%) 1018
STSO 1020 steps (queue-hop) 3050 (+46%) 🔻 3050 (+46%) 🔻 3050 (+46%) 🔻 3050 (+46%) 🔻 1
WO 1020 steps 131203 (-16%) 💚 131203 (-16%) 💚 131203 (-16%) 💚 131203 (-16%) 💚 1
SL stream latency 94 (+2.2%) 162 🔴 (-33%) 💚 306 🔴 (-52%) 💚 3248 🔴 (+111%) 🔻 30
SO stream overhead (text) 107 (-20%) 💚 172 (-34%) 💚 201 (-37%) 💚 3691 🔴 (+781%) 🔻 30
SO stream overhead (structured) 109 (-9.9%) 189 (-14%) 211 (-38%) 💚 266 (-58%) 💚 30
📈 STSO distribution vs main (inline / queue-hop histograms)

1020 steps (inline)

Cumulative STSO time: main 152131ms → this run 126830ms (Δ -25301ms, -17%)

   50-100 ms  █░┃                       main   3  this 112  +109
  100-150 ms  █████████████████████░░┃  main 713  this 798   +85
  150-200 ms  █┃█████                   main 237  this  70  -167
  200-250 ms  ┃                         main  40  this  11   -29
  250-300 ms  ┃                         main  10  this  17    +7
  300-350 ms  ┃                         main   6  this   3    -3
  350-400 ms  ┃                         main   4  this   3    -1
  400-450 ms  ┃                         main   0  this   1    +1
  450-500 ms  ┃                         main   1  this   1    +0
  550-600 ms  ┃                         main   1  this   1    +0
  600-650 ms  ┃                         main   1  this   1    +0
1000-1050 ms  ┃                         main   1  this   0    -1
3350-3400 ms  ┃                         main   1  this   0    -1

1020 steps (queue-hop)

Cumulative STSO time: main 2084ms → this run 3050ms (Δ +966ms, +46%)

2000-2500 ms  ┃███████████████████████  main 1  this 0  -1
3000-3500 ms  ░░░░░░░░░░░░░░░░░░░░░░░┃  main 0  this 1  +1
📜 Previous results (1)

c139c2a

Fri, 31 Jul 2026 23:29:13 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
STSO 1020 steps (inline) 159 (+23%) 🔻 444 (-2.2%) 497 (-2.2%) 704 (-1.8%) 1016
STSO 1020 steps (queue-hop) 2036 (+37%) 🔻 3105 (-1.6%) 3105 (-1.6%) 3105 (-1.6%) 3
WO 1020 steps 380383 (-2.5%) 380383 (-2.5%) 380383 (-2.5%) 380383 (-2.5%) 1
SO stream overhead (text) 96 (-4.0%) 146 (+7.4%) 174 (+2.4%) 217 (-0.9%) 30
SO stream overhead (structured) 94 (-7.8%) 149 (-22%) 💚 219 (-6.8%) 350 (-46%) 💚 30
ℹ️ Metric definitions & methodology

The collapsed STSO distribution section above buckets every step gap of the sequential-steps run (not a sampled window), split by whether the step ending the gap ran inline — in the same warm process as the step before it, so the gap is pure framework overhead — or after a queue-hop — the first step of a fresh process, which pays queue dispatch, client reinit and event-log replay. Bars overlay the two runs: is main, marks where this run lands, bridges the gap when this run has more samples in a bucket.

Best/P75/P90/P99 deltas compare against the most recent benchmark run on main at the time of this run. 🔻 flags a delta worse than +15%, 💚 one better than −15%.

Metrics — TTFS: time to first step body (in-deployment start() → first step body, deployment clocks) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) · SL: stream latency (in-deployment write → read propagation, readAt - writtenAt) · SO: stream overhead (end-to-end write+consume time beyond the modelled generation window)

Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · stream latency: parallel reader/writer steps on a dedicated stream; SL is the in-deployment write->read propagation (readAt - writtenAt) · stream overhead (text): writer streams 300 variable-length text token deltas paced at 100/s for 3s (a haiku-size LLM's token throughput) while a parallel reader drains the whole stream; SO is the end-to-end write+consume time beyond the 3s generation window (overhead/backpressure) · stream overhead (structured): same workload as stream overhead (text), but each delta is an AI-SDK-style structured object ({ type: 'text-delta', id, text }) instead of a raw string, so the SO gap vs the text scenario is the added serialization cost

🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 · SL 50/60/125 · SO 250/500/1000

All metrics are measured from deployment-side timestamps only. Runs are triggered by an in-deployment route that stamps the anchor (clientStart) right before start(), so the CI runner’s request and its path through api.vercel.com sit outside every measured window. TTFS = in-deployment start() → first step body (turbo uses the in-process fast path, non-turbo the dispatch path), and includes the VQS dispatch hop plus any /flow cold start. STSO/WO are measured between step bodies on the deployment. SL is measured inside the workflow (parallel reader/writer steps), so it no longer includes the api.vercel.com read path.

Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the /flow invocation for a large fraction of runs, inflating P75+; the Best column shows the fastest (warm-start) sample for comparison.

@pranaygp pranaygp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the incremental diff (12 files, +926) and ran the stack top locally. Not merge-ready — headline finding inline at the load gate: snapshots never restore on world-postgres or world-vercel, and nothing in CI can tell, because a snapshot that never restores falls back to full replay, which is correct behavior. The quickjs-snapshot CI legs are currently red only on the inherited #3049 overflow bug; once that's fixed they'd go green while snapshots remain pure cost on both production worlds.

Local validation of what does work (world-local): snapshot lifecycle is real — 101 snapshot_saved / 283 snapshot_load diag checkpoints across a full e2e leg, 4 MB-ish .bin/.json pairs created and deleted on completion, restored: true resumptions observed. The position-based PRNG fast-forward design is correct: the restored-run-produces-identical-correlationIds property is the right one to pin, and I could not construct a divergence; the per-(runId, correlationId) dedup backstop can't wedge. Host-callback re-registration via the unified list is complete (no other newFunction sites). The eventsCursor frontier is sound across all three worlds (strictly exclusive cursors, no off-by-one). Compression is already workerd-safe.

One n=1 observation from a full snapshot-leg run worth your eyes: parallelStepsThenWebhookWorkflow - no hook_conflict from same-tick replay race failed once with a correctness assertion (harness posted body-<tokenA> for a token it listed from storage, workflow expected body-<tokenB>), i.e. a post-restore disagreement about the run's own webhook token. 10/10 passes in isolation afterwards and absent from a second full leg — but a restore-path token/PRNG-position determinism bug is what it would smell like if it recurs.

Non-blocking: first-invocation captures with no cursor are taken then dropped; the restore drain loop lacks the boot path's iteration-bound warning; deleteSnapshotIfAny early-returns when the threshold is 0, so flipping the env off strands stored snapshots (and a waitUntil save landing after a terminal delete re-creates one); __hookPayloadBuffer.__processedEventIds grows monotonically in-heap (snapshot size for long-lived reusable hooks only increases); __wdk_env isn't refreshed after restore; the docs say invalid threshold values "throw at startup" but getSnapshotThresholdFromEnv throws on first invocation. Changeset omits @workflow/world (this PR modifies packages/world/src/snapshots.ts).

Stack coordination: #3263 rebases under this per its own description — do that first. Three silent semantic breaks to check on the rebase: the inline __generateUlid registration won't be re-registered on restore (the branch's own comment says host callbacks must never be inline); the restore path's "serde survives in the heap" comment becomes false (serde is host-side after #3263createQuickJSSerde must run on restore); and the ULID monotonic factory's state moves host-side, so it's no longer captured by the snapshot and the rngDraws fast-forward desynchronizes.

const version = loaded.metadata.formatVersion;
if (
(version !== undefined && version !== SNAPSHOT_FORMAT_VERSION) ||
loaded.metadata.rngDraws === undefined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: this gate rejects every snapshot on world-postgres and world-vercel, so the feature never restores on either production world. Neither world persists the new metadata fields: world-postgres writes/reads only eventsCursor + createdAt (migration 0018 has no columns for eventCount/rngDraws/formatVersion), and world-vercel sends/parses only the two original headers (carrying the new fields also needs a workflow-server change). Only world-local round-trips them, which is why the local tests pass.

Net effect on prod worlds: every qualifying suspension pays session.snapshot() (two full heap copies) + compression + encryption + a 5–15 MB PUT, and every resume throws it away and full-replays — strictly worse than WORKFLOW_SNAPSHOT_THRESHOLD=0. Persisting the fields in both worlds is the right fix; tolerating missing rngDraws is not safe (a restored heap with rngDraws: 0 re-draws and collides correlationIds).

// its next qualifying suspension instead.)
const snapshot = capturedSnapshot;
const totalEventCount = restoredEventCount + seenEventIds.size;
safeWaitUntil(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Off the response path" is false on the default codec: this IIFE runs synchronously up to its first real await, and compress with the default write codec is zlib.zstdCompressSync — synchronous compression of a multi-MB heap image blocks the event loop before the response flushes. The gzip fallback (CompressionStream) is genuinely async, so this only bites the default path. Defer past the current tick (Promise.resolve().then(...)) or use the async codec for snapshots.

// (pre-snapshot count persisted in the metadata + delta) — otherwise a
// run that keeps snapshotting would never accumulate enough delta to
// trip the ceiling it exists to enforce.
const restoredEventCount = existingSnapshot?.metadata.eventCount ?? 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restoredEventCount double-counts after a restore failure. The restore-failure catch resets existingSnapshot/lastEventsCursor to fall back to full replay but can't reset this const; the ceiling check then adds the stale count to a seenEventIds set that now holds the entire log, so MaxEventsExceededError can fire well below the real limit — and the next save stamps the inflated eventCount, compounding. Currently masked by the load-gate finding (no restores on prod worlds ⇒ no failures to fall back from), which is exactly the latent-bug shape that surfaces the day that's fixed.

) {
try {
capturedSnapshot = session.snapshot();
if (capturedSnapshot.data.byteLength > MAX_SNAPSHOT_PLAINTEXT_BYTES) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ceiling enforced after the expensive work: session.snapshot() has already made two full copies of the WASM heap by this check, and since WASM linear memory never shrinks, a run that once crossed 32 MB re-pays both copies at every subsequent suspension and discards the result every time. Gate on VM memory size before snapshotting, or latch a per-run "too big" flag on first rejection.

* Current snapshot format version, bumped when the heap layout or the
* metadata contract changes incompatibly.
*/
export const SNAPSHOT_FORMAT_VERSION = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatVersion covers the SDK's metadata envelope, not the heap image — the QJSS header that deserializeSnapshot validates is identical across quickjs-wasi builds, so bytes from one build restored by another pass validation and execute as undefined behavior in the interpreter. Real deploy-skew hazard (a quickjs-wasi bump mid-rollout has live snapshots from the old build), and a data-corruption-class failure. Cheap close: the library exposes vm.versions — persist it in metadata and reject a mismatch. Related smaller gap: the deterministic clock's high-water mark (vmNowMs) isn't persisted either, so Date.now() in a restored VM can regress until the first event advances it.

…-snapshots

Carries the #3049 merge down the stack. Resolution notes:
- inline loop's post-batch feed keeps BOTH sides: zero-event feeds raise
  pendingRequeueSignal before breaking (ack-without-requeue fix from
  #3049) and non-empty feeds advance eventsProcessedSinceSnapshot
- quickjs-runtime.test.ts keeps both trailing suites (VM snapshot/restore
  + hook dispose-then-sleep replay)

Validated: core 1899 passed; hook (26/26) and promiseRace e2e green
under WORKFLOW_VM=quickjs with WORKFLOW_SNAPSHOT_THRESHOLD=5.
// (pre-snapshot count persisted in the metadata + delta) — otherwise a
// run that keeps snapshotting would never accumulate enough delta to
// trip the ceiling it exists to enforce.
const restoredEventCount = existingSnapshot?.metadata.eventCount ?? 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restoredEventCount is captured as a const before snapshot restore and cannot be reset when restore fails and the code falls back to full event-log replay, causing the pre-snapshot count to be added on top of the now-full events/seenEventIds.

Fix on Vercel

// its next qualifying suspension instead.)
const snapshot = capturedSnapshot;
const totalEventCount = restoredEventCount + seenEventIds.size;
safeWaitUntil(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snapshot compression claimed to run off the response path via safeWaitUntil actually runs synchronously on the response-path call stack for the default zstd codec, blocking the event loop while compressing a multi-MB heap.

Fix on Vercel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants