Skip to content

[core] Flow v2, eager-processing of workflows, combine flow/step bundles#1338

Draft
VaguelySerious wants to merge 68 commits intomainfrom
peter/v2-flow
Draft

[core] Flow v2, eager-processing of workflows, combine flow/step bundles#1338
VaguelySerious wants to merge 68 commits intomainfrom
peter/v2-flow

Conversation

@VaguelySerious
Copy link
Member

@VaguelySerious VaguelySerious commented Mar 11, 2026

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 11, 2026

🦋 Changeset detected

Latest commit: 3625e94

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

This PR includes changesets to release 20 packages
Name Type
@workflow/core Patch
@workflow/builders Patch
@workflow/next Patch
@workflow/nest Patch
@workflow/sveltekit Patch
@workflow/nitro Patch
@workflow/astro Patch
@workflow/world Patch
workflow Patch
@workflow/cli Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/world-testing Patch
@workflow/rollup Patch
@workflow/vite Patch
@workflow/nuxt Patch
@workflow/world-local Patch
@workflow/world-postgres Patch
@workflow/world-vercel Patch
@workflow/ai 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

@vercel
Copy link
Contributor

vercel bot commented Mar 11, 2026

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

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Mar 16, 2026 4:28pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Mar 16, 2026 4:28pm
example-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-astro-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-express-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-fastify-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-hono-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-nitro-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-nuxt-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workbench-vite-workflow Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Mar 16, 2026 4:28pm
workflow-nest Ready Ready Preview, Comment Mar 16, 2026 4:28pm
workflow-swc-playground Ready Ready Preview, Comment Mar 16, 2026 4:28pm

@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

📊 Benchmark Results

⚠️ Results below are stale and not from the latest commit. This comment will be updated when CI completes on the latest run.

Benchmarks are running...


Started at: 2026-03-16T16:25:53Z


📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.041s (-2.1%) 1.006s (~) 0.965s 10 1.00x
💻 Local Nitro 0.043s (+4.9%) 1.006s (~) 0.963s 10 1.03x
🐘 Postgres Express 0.058s (-11.0% 🟢) 1.011s (~) 0.954s 10 1.40x
🐘 Postgres Nitro 0.063s (+7.3% 🔺) 1.012s (~) 0.949s 10 1.53x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.588s (+22.2% 🔺) 2.531s (~) 1.943s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.098s (-2.6%) 2.006s (~) 0.908s 10 1.00x
💻 Local Nitro 1.101s (-2.5%) 2.006s (~) 0.905s 10 1.00x
🐘 Postgres Nitro 1.127s (-1.6%) 2.013s (~) 0.887s 10 1.03x
🐘 Postgres Express 1.134s (~) 2.013s (~) 0.879s 10 1.03x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.838s (-9.5% 🟢) 3.706s (+1.2%) 1.868s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 10.606s (-2.6%) 11.023s (~) 0.417s 3 1.00x
💻 Local Express 10.625s (-2.5%) 11.022s (~) 0.397s 3 1.00x
🐘 Postgres Nitro 10.705s (-1.7%) 11.042s (~) 0.337s 3 1.01x
🐘 Postgres Express 10.747s (-1.5%) 11.044s (~) 0.298s 3 1.01x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 18.303s (+7.9% 🔺) 20.339s (+5.1% 🔺) 2.036s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 26.463s (-3.6%) 27.051s (-3.6%) 0.588s 3 1.00x
💻 Local Express 26.501s (-3.4%) 27.050s (-3.6%) 0.548s 3 1.00x
🐘 Postgres Nitro 26.622s (-1.8%) 27.062s (-3.5%) 0.440s 3 1.01x
🐘 Postgres Express 26.834s (-1.1%) 27.400s (-1.2%) 0.566s 3 1.01x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 122.425s (+167.9% 🔺) 124.939s (+163.9% 🔺) 2.514s 1 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 52.933s (-6.4% 🟢) 53.100s (-7.0% 🟢) 0.167s 2 1.00x
💻 Local Nitro 52.940s (-6.4% 🟢) 53.097s (-7.0% 🟢) 0.157s 2 1.00x
🐘 Postgres Nitro 53.202s (-1.9%) 54.121s (-1.8%) 0.919s 2 1.01x
🐘 Postgres Express 53.309s (-1.5%) 54.099s (-1.8%) 0.790s 2 1.01x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 167.537s (+81.9% 🔺) 169.356s (+79.8% 🔺) 1.819s 1 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.399s (-1.0%) 2.013s (~) 0.613s 15 1.00x
🐘 Postgres Nitro 1.413s (+0.8%) 2.011s (~) 0.598s 15 1.01x
💻 Local Express 1.534s (+0.8%) 2.005s (~) 0.471s 15 1.10x
💻 Local Nitro 1.539s (+4.0%) 2.006s (~) 0.467s 15 1.10x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.818s (+22.4% 🔺) 4.505s (+22.6% 🔺) 1.688s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.514s (~) 3.012s (~) 0.497s 10 1.00x
💻 Local Express 2.810s (-1.3%) 3.342s (+11.1% 🔺) 0.532s 9 1.12x
💻 Local Nitro 2.895s (-1.2%) 3.341s (+4.1%) 0.446s 9 1.15x
🐘 Postgres Express 2.917s (+12.3% 🔺) 3.351s (+11.1% 🔺) 0.434s 9 1.16x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.608s (+41.7% 🔺) 5.057s (+25.6% 🔺) 1.449s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 8.020s (-0.5%) 8.517s (~) 0.497s 4 1.00x
💻 Local Express 8.383s (+4.8%) 9.017s (+5.8% 🔺) 0.634s 4 1.05x
🐘 Postgres Express 25.076s (+530.1% 🔺) 25.581s (+457.8% 🔺) 0.506s 2 3.13x
🐘 Postgres Nitro 30.093s (+658.6% 🔺) 30.582s (+610.7% 🔺) 0.488s 2 3.75x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.870s (+122.3% 🔺) 8.672s (+83.4% 🔺) 1.801s 4 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.400s (-1.2%) 2.012s (~) 0.611s 15 1.00x
🐘 Postgres Express 1.431s (+1.9%) 2.012s (~) 0.581s 15 1.02x
💻 Local Express 1.625s (+7.7% 🔺) 2.073s (+3.4%) 0.448s 15 1.16x
💻 Local Nitro 1.671s (+9.4% 🔺) 2.149s (+7.2% 🔺) 0.479s 14 1.19x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.818s (+26.5% 🔺) 4.007s (+5.1% 🔺) 1.189s 8 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.495s (-4.2%) 3.013s (~) 0.518s 10 1.00x
🐘 Postgres Nitro 2.769s (+6.3% 🔺) 3.350s (+11.2% 🔺) 0.581s 9 1.11x
💻 Local Express 2.911s (-7.6% 🟢) 3.564s (-8.3% 🟢) 0.653s 9 1.17x
💻 Local Nitro 2.922s (-6.9% 🟢) 3.564s (-8.3% 🟢) 0.642s 9 1.17x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.038s (+53.7% 🔺) 5.238s (+26.1% 🔺) 1.199s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 8.701s (-0.7%) 9.270s (+2.8%) 0.569s 4 1.00x
💻 Local Express 8.846s (+4.3%) 9.521s (+5.5% 🔺) 0.675s 4 1.02x
🐘 Postgres Nitro 19.983s (+400.5% 🔺) 20.572s (+348.4% 🔺) 0.589s 2 2.30x
🐘 Postgres Express 23.276s (+464.3% 🔺) 23.579s (+413.3% 🔺) 0.303s 2 2.68x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.205s (+87.8% 🔺) 7.629s (+61.1% 🔺) 1.424s 4 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 0.174s (-10.6% 🟢) 1.003s (~) 0.012s (+1.8%) 1.017s (~) 0.844s 10 1.00x
💻 Local Express 0.174s (-10.0% 🟢) 1.003s (~) 0.011s (+1.8%) 1.017s (~) 0.843s 10 1.00x
🐘 Postgres Nitro 0.188s (-15.5% 🟢) 1.000s (~) 0.001s (-30.0% 🟢) 1.012s (~) 0.824s 10 1.08x
🐘 Postgres Express 0.202s (-7.5% 🟢) 0.998s (~) 0.001s (-7.1% 🟢) 1.012s (~) 0.810s 10 1.16x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.510s (-9.0% 🟢) 1.908s (-17.3% 🟢) 0.005s (-7.8% 🟢) 2.644s (-7.4% 🟢) 1.133s 10 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Express 7/12
🐘 Postgres Nitro 8/12
▲ Vercel Next.js (Turbopack) 12/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 💻 Local 9/12
Next.js (Turbopack) ▲ Vercel 12/12
Nitro 💻 Local 8/12
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)

📋 View full workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 160 0 53 213
❌ 💻 Local Development 544 2 235 781
❌ 📦 Local Production 598 2 252 852
✅ 🐘 Local Postgres 552 0 229 781
✅ 🪟 Windows 54 0 17 71
❌ 🌍 Community Worlds 116 55 15 186
✅ 📋 Other 96 0 46 142
Total 2120 59 847 3026

❌ Failed Tests

💻 Local Development (2 failed)

astro-stable (1 failed):

  • health check (CLI) - workflow health command reports healthy endpoints

sveltekit-stable (1 failed):

  • health check (CLI) - workflow health command reports healthy endpoints
📦 Local Production (2 failed)

astro-stable (1 failed):

  • health check (CLI) - workflow health command reports healthy endpoints

sveltekit-stable (1 failed):

  • health check (CLI) - workflow health command reports healthy endpoints
🌍 Community Worlds (55 failed)

mongodb (3 failed):

  • hookWorkflow is not resumable via public webhook endpoint
  • webhookWorkflow
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously

redis (2 failed):

  • hookWorkflow is not resumable via public webhook endpoint
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously

turso (50 failed):

  • addTenWorkflow
  • addTenWorkflow
  • wellKnownAgentWorkflow (.well-known/agent)
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • importedStepOnlyWorkflow
  • hookWorkflow
  • hookWorkflow is not resumable via public webhook endpoint
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • 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()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • cancelRun - cancelling a running workflow
  • cancelRun via CLI - cancelling a running workflow
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control)

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ nextjs-turbopack 55 0 16
✅ nextjs-webpack 55 0 16
✅ sveltekit 50 0 21
❌ 💻 Local Development
App Passed Failed Skipped
❌ astro-stable 47 1 23
✅ express-stable 48 0 23
✅ fastify-stable 48 0 23
✅ hono-stable 48 0 23
✅ nextjs-turbopack-canary 54 0 17
✅ nextjs-turbopack-stable 54 0 17
✅ nextjs-webpack-stable 54 0 17
✅ nitro-stable 48 0 23
✅ nuxt-stable 48 0 23
❌ sveltekit-stable 47 1 23
✅ vite-stable 48 0 23
❌ 📦 Local Production
App Passed Failed Skipped
❌ astro-stable 47 1 23
✅ express-stable 48 0 23
✅ fastify-stable 48 0 23
✅ hono-stable 48 0 23
✅ nextjs-turbopack-canary 54 0 17
✅ nextjs-turbopack-stable 54 0 17
✅ nextjs-webpack-canary 54 0 17
✅ nextjs-webpack-stable 54 0 17
✅ nitro-stable 48 0 23
✅ nuxt-stable 48 0 23
❌ sveltekit-stable 47 1 23
✅ vite-stable 48 0 23
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 48 0 23
✅ express-stable 48 0 23
✅ fastify-stable 48 0 23
✅ hono-stable 48 0 23
✅ nextjs-turbopack-canary 54 0 17
✅ nextjs-turbopack-stable 54 0 17
✅ nextjs-webpack-canary 54 0 17
✅ nextjs-webpack-stable 54 0 17
✅ nitro-stable 48 0 23
✅ nuxt-stable 48 0 23
✅ vite-stable 48 0 23
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 54 0 17
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 2
❌ mongodb 51 3 3
✅ redis-dev 3 0 2
❌ redis 52 2 3
✅ turso-dev 3 0 2
❌ turso 4 50 3
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 48 0 23
✅ e2e-local-prod-nest-stable 48 0 23

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: cancelled
  • Local Dev: failure
  • Local Prod: failure
  • Local Postgres: cancelled
  • Windows: success

Check the workflow run for details.

TooTallNate and others added 3 commits March 11, 2026 17:08
…nd step handler

Transient network errors (ECONNRESET, etc.) during infrastructure calls
(event listing, event creation) were caught by a shared try/catch that
also handles user code errors, incorrectly marking runs as run_failed
or steps as step_failed instead of letting the queue redeliver.

- runtime.ts: Move infrastructure calls outside the user-code try/catch
  so errors propagate to the queue handler for automatic retry
- step-handler.ts: Same structural separation — only stepFn.apply() is
  wrapped in the try/catch that produces step_failed/step_retrying
- helpers.ts: Add isTransientNetworkError() and update withServerErrorRetry
  to retry network errors in addition to 5xx responses
- helpers.test.ts: Add tests for network error detection and retry
Merge flow and step routes into a single combined handler that executes
steps inline when possible, reducing function invocations and queue
overhead. Serial workflows can now complete in a single function
invocation instead of 2N+1 invocations.

Key changes:
- Add `combinedEntrypoint()` to core runtime with inline step execution loop
- Extract reusable step execution logic into `step-executor.ts`
- Add `handleSuspensionV2()` that creates events without queuing steps
- Add `stepId` field to `WorkflowInvokePayload` for background step dispatch
- Add `createCombinedBundle()` to base builder
- Update Next.js builder to generate combined route at v1/flow
- Update health check e2e tests for single-route architecture

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vercel bot and others added 9 commits March 12, 2026 21:17
…ing `<` and `>` as JSX tag delimiters.

This commit fixes the issue reported at docs/content/docs/changelog/eager-processing.mdx:129

**Bug explanation:**

In MDX v3 (used by this project via `@mdx-js/mdx` ^3.1.1 and fumadocs-mdx), all content is parsed as a mix of Markdown and JSX. The `<` character in prose text is interpreted as the start of a JSX element. When the MDX parser encounters `<=50%`, it sees `<` as opening a JSX tag and then `=` as the next character, which is not a valid character to start an element name (element names must start with a letter, `$`, or `_`). This causes a parse error:

```
124:143: eager-processing.mdx:124:143: Unexpected character `=` (U+003D) before name, expected a character that can start a name, such as a letter, `$`, or `_`
```

This directly caused the Vercel build failure (deployment `dpl_GLdsAtQ1WM7xFGXUFT2SvswuNWkW`), as the `docs#build` task failed.

**Fix explanation:**

Replaced `<=50%` with `≤50%` (Unicode "less-than or equal to" symbol, U+2264) and `>=50%` with `≥50%` (Unicode "greater-than or equal to" symbol, U+2265). These Unicode characters are semantically equivalent, render correctly in browsers, and are not interpreted as JSX syntax by the MDX parser. This is a common and clean approach for MDX content — arguably even more readable than the ASCII approximations.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
Merge fix/separate-infra-user-error-handling into peter/v2-flow.

Resolves conflicts in runtime.ts and ports #1339's changes to the
V2 combined entrypoint:
- Remove withThrottleRetry wrapper (undici RetryAgent handles retries)
- Remove serverErrorRetryCount tracking
- Remove withServerErrorRetry from step-executor.ts
- Separate infrastructure vs user code error handling

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Take main's runtime.ts as base for V1 workflowEntrypoint (with #1339's
final error handling structure), then re-apply V2 combinedEntrypoint
and helpers on top. Fix getAllWorkflowRunEvents to return Event[] again
(WithCursor variant for V2 incremental loading).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port the V2 combined bundle to all remaining frameworks:
- Standalone builder
- Vercel Build Output API builder (used by Nitro, Astro production)
- NestJS builder
- SvelteKit builder
- Nitro local builder
- Astro local builder

Each builder now uses createCombinedBundle() instead of separate
createStepsBundle() + createWorkflowsBundle(). The step route is
no longer generated. Framework-specific post-processing (SvelteKit,
Astro request normalization) updated to match combinedEntrypoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove:
- V1 workflowEntrypoint (replaced by combined handler)
- V1 handleSuspension (replaced by V2 that returns pending steps)
- runStep placeholder
- stepEntrypoint re-export from workflow/runtime
- STEP_QUEUE_TRIGGER and COMBINED_QUEUE_TRIGGER constants
- stepEntrypoint export from createStepsBundle virtual entry

Rename:
- combinedEntrypoint → workflowEntrypoint
- handleSuspensionV2 → handleSuspension
- SuspensionHandlerV2Result → SuspensionHandlerResult
- COMBINED_QUEUE_TRIGGER → WORKFLOW_QUEUE_TRIGGER

The step-handler.ts and step-executor.ts both remain — step-handler
is the V1 queue handler (may still be useful for testing), and
step-executor is the shared execution logic used by the combined
handler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Replace all references to combinedEntrypoint, handleSuspensionV2,
COMBINED_QUEUE_TRIGGER, stepEntrypoint with their final names.
Remove mentions of V1 backward compatibility since V1 code is deleted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Take our runtime.ts (V1 removed), main's http-client.ts (H2/undici),
main's pnpm-lock.yaml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
correlationId: e.correlationId,
}));

for (const waitEvent of waitsToComplete) {
Copy link
Contributor

@vercel vercel bot Mar 13, 2026

Choose a reason for hiding this comment

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

Event cache duplication bug: wait_completed events pushed locally without advancing eventsCursor cause duplicate events on next incremental fetch, leading to WorkflowRuntimeError.

Fix on Vercel

VaguelySerious and others added 15 commits March 13, 2026 14:14
- Resolve runtime.ts/lockfile/changeset conflicts with main
- Add 'node' condition to workflow package exports to prevent esbuild
  from resolving to typescript-plugin.cjs via the 'require' condition
- Revert standalone builder to use separate step/workflow bundles
  (createCombinedBundle re-bundling creates duplicate module instances)
- Update world-testing server: side-effect import for step registrations,
  remove step route (V2 handles everything via flow route)

Known issue: standalone builder's separate bundles have duplicate
@workflow/core/private instances — step registrations populate one map,
runtime looks up from another. Will fix by inlining step registrations
into the combined route's virtual entry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When bundleFinalOutput is true, the combined route imports the step
registrations file which gets re-bundled by esbuild. This still creates
separate module scopes for the step registrations and runtime.

The standalone builder (used by world-testing/CLI) hits this issue:
registerStepFunction and getStepFunction use different Maps because
esbuild wraps each source module in isolated scopes even within the
same output file.

This is a known limitation for bundleFinalOutput: true. Framework
integrations (Next.js, SvelteKit, etc.) use bundleFinalOutput: false
where the framework's own bundler handles resolution correctly.

Also adds "node" condition to workflow package.json exports to prevent
esbuild from resolving to typescript-plugin.cjs via "require" condition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Change the registeredSteps Map in @workflow/core/private from a
module-scoped variable to a globalThis singleton via Symbol.for.
This ensures all esbuild module scopes within the same bundle
share the same Map, fixing step registration lookup for standalone
and Vercel BOA builds (bundleFinalOutput: true).

The Symbol.for pattern is already used by:
- @workflow/core/runtime/world.ts (World singleton)
- @workflow/core/symbols.ts (class registry)

Also simplifies createCombinedBundle by removing the unnecessary
bundleFinalOutput branching — both paths produce identical code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Make registeredSteps Map and contextStorage AsyncLocalStorage into
globalThis singletons via Symbol.for. This fixes the module scope
duplication issue where esbuild creates separate instances in
re-bundled output (bundleFinalOutput: true).

Also:
- Add "node" condition to workflow package exports to prevent
  esbuild from resolving to typescript-plugin.cjs
- Set WORKFLOW_LOCAL_QUEUE_CONCURRENCY=1 in world-testing to
  prevent concurrent replay interference in local world
- Document non-Next.js integration challenges in changelog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update SvelteKit and Astro generatedStepPath in create-test-matrix.mjs
  to match V2 output (__step_registrations.js instead of step/+server.js)
- Set WORKFLOW_POSTGRES_WORKER_CONCURRENCY=1 in test server to prevent
  concurrent replay interference in postgres world tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nitro: remove virtual handler for /.well-known/workflow/v1/step
since V2 handles everything via the flow route.

NestJS: remove @post('step') handler, import step registrations
as side effects in the flow handler.

Also serialize postgres worker concurrency in tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ings

All ESM builders (Nitro, NestJS, SvelteKit, Astro) need bundleFinalOutput: false
and externalizeNonSteps: true to avoid "Dynamic require of X is not supported"
errors. When bundleFinalOutput: true with ESM format, esbuild wraps CJS require()
calls in a polyfill that fails in ESM context.

Only CJS builders (standalone, Vercel BOA) can use bundleFinalOutput: true safely.
Also explicitly set format: 'cjs' for Vercel BOA to match its commonjs package.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The deferred builder was writing step registrations to a .temp file,
which canary Turbopack rejected as an unknown module type. Write directly
to the final __step_registrations.js name since it doesn't need the
copyFileIfChanged temp mechanism (it's a side-effect import, not a route).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SvelteKit and Astro builders now remove the old V1 step route directory/file
during build. This prevents Vercel build cache from preserving stale files
that import the removed stepEntrypoint function.

Also adds ESM bundleFinalOutput documentation to changelog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The beforeExit hook that patches .vc-config.json was still trying to
configure the V1 step.func directory, which no longer exists in V2.
Remove the step entry and add an existence check for safety.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The steps bundle only contains side-effect code (registerStepFunction
calls) with no exports. When rollup processes the combined route that
imports this module, it tree-shakes the entire module away because it
has no used exports.

Fix: add a sentinel export (__steps_registered) to the steps bundle and
import it in the combined route. This gives rollup a used binding to
track, preventing it from dropping the module and its side effects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the V2 inline execution loop advances ahead (e.g., completes batch 1
inline and creates step_created events for batch 2), concurrent replays
from batch 1 background step continuations may encounter batch 2's events
without matching subscribers.

Fix: the EventsConsumer's onUnconsumedEvent callback now returns true to
skip step lifecycle events (step_created, step_started, step_completed,
step_failed, step_retrying) that have a corresponding step_created event
in the log — confirming they're from a legitimate concurrent handler.
Orphaned events with unknown correlationIds still error.

Also: steps bundle exports __steps_registered sentinel to prevent rollup
from tree-shaking the side-effect-only module.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VaguelySerious and others added 4 commits March 15, 2026 12:59
…ation

Two fixes:

1. Step executor: await ops (stream writes) with a 5-second bounded wait
   before creating step_completed. In V1, each step was a separate function
   invocation and waitUntil ensured ops completed before the function ended.
   In V2 inline execution, the handler loop continues immediately, leaving
   stream data uncommitted. The bounded wait ensures data reaches S3 before
   proceeding, with waitUntil as a safety net for ops that need more time.

2. Step executor: only enforce max retries when step.error exists (actual
   retry after failure). V2 concurrent replays can inflate the attempt
   counter via simultaneous step_started calls without any prior failure.
   With N parallel steps completing, up to N concurrent continuations may
   race to start the same step, each incrementing attempt. The first
   completion wins (step_completed idempotency), but premature "exceeded
   max retries" failures must be prevented.

Also documents all integration challenges in the changelog.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a step has pending background operations (e.g., WritableStream data
being piped to S3), the V2 inline execution loop must not continue to
the next replay iteration. Instead, queue a continuation and return to
give waitUntil time to flush the ops.

In V1, each step ran in a separate function invocation. After the step
completed, the function returned and waitUntil flushed the stream writes
to S3 before the test could read the data. In V2, the inline loop
continues processing, keeping the function alive and preventing
waitUntil from flushing. The test's stream reader blocks forever
because S3 data never arrives.

Fix: executeStep now returns hasPendingOps when the step had background
ops. The V2 handler checks this and breaks the loop, queueing a
continuation instead of looping inline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add changelog entries for:
- Concurrent step_started inflating attempt counter (promiseRaceStressTest fix)
- Inline step execution with pending stream operations (outputStream fix)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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.

2 participants