Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Forward-port stable CI hardening - #2017

Draft
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci
Draft

Forward-port stable CI hardening#2017
pranaygp wants to merge 1 commit into
mainfrom
pranaygp/codex/forward-port-stable-ci

Conversation

@pranaygp

@pranaygppranaygp commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Forward-ports the stable CI hardening from #1986 onto main now that it has landed in stable.

This carries over the production-code stability fixes and the applicable test harness updates:

  • bound queue-based health checks so queue delivery, stream opening, stream reads, and poll sleeps all share the requested timeout budget
  • add abort signals to Vercel stream write/close mutations so stuck stream writes fail and retry instead of pinning steps indefinitely
  • wait for step return stream serialization before recording step_completed
  • materialize manual webhook response bodies before returning from hook resume handling
  • speed up local workflow port detection by checking Node/libuv listening handles before slower OS-specific fallbacks
  • make generated workflow health endpoints support HEAD, including Astro, SvelteKit, Nest, and combined generated routes on main
  • share route-export replacement logic that handles embedded source map comments and throws when wrapping fails

Forward-port note

This PR should not be backported. It is itself a forward-port of changes that already landed on stable in #1986.

Additional changes made on main

After the initial forward-port, CI exposed a few failures that were not fixed by the original #1986 changes alone. These are intentionally called out as extra stabilization work on top of the stable forward-port:

  • V2 background step continuations now enqueue an idempotent workflow continuation instead of letting every completed background step handler continue replay inline. In CI, the inline replay path let racing handlers enter the next batch more than once, producing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild for modified files in watch mode. The previous modified-file path only rebuilt existing esbuild contexts, which updated generated route code but did not refresh the workflow manifest when an API route import graph changed. The nextjs-webpack dev CI test for adding a workflow file was timing out on that stale manifest.
  • The dev E2E harness was adjusted to match the Next workbench layout: added workflow files are imported through the app-root @/workflows/... alias, the Next POST route is hit so the module actually compiles, and cleanup gets a longer timeout for slower Windows dev-server prewarm/teardown.

These additions are still part of making main stable after the forward-port, but they are not meant to change the no-backport decision: this PR is itself a forward-port, and the extra changes are for failures observed on this main PR.

Validation

  • fnm exec --using v22.18.0 pnpm --filter @workflow/core... --filter @workflow/world-vercel... --filter @workflow/builders... build
  • fnm exec --using v22.18.0 pnpm exec vitest run packages/core/src/runtime/helpers.test.ts packages/world-vercel/src/streamer.test.ts packages/builders/src/request-converter.test.ts
  • fnm exec --using v22.18.0 pnpm exec biome check .changeset/stable-ci-e2e-cleanup.md packages/astro/src/builder.ts packages/builders/src/base-builder.ts packages/builders/src/index.ts packages/builders/src/request-converter.test.ts packages/builders/src/request-converter.ts packages/core/e2e/dev.test.ts packages/core/e2e/local-build.test.ts packages/core/e2e/utils.ts packages/core/src/runtime/helpers.test.ts packages/core/src/runtime/helpers.ts packages/core/src/runtime/resume-hook.ts packages/core/src/runtime/step-handler.ts packages/nest/src/workflow.controller.ts packages/sveltekit/src/builder.ts packages/utils/src/get-port.test.ts packages/utils/src/get-port.ts packages/world-vercel/src/streamer.test.ts packages/world-vercel/src/streamer.ts (warnings only: existing complexity/style warnings)
  • git diff --check HEAD~1..HEAD

@changeset-bot

changeset-botBot commented May 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 459bc08

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

This PR includes changesets to release 23 packages
NameType
@workflow/astroPatch
@workflow/buildersPatch
@workflow/corePatch
@workflow/nestPatch
@workflow/sveltekitPatch
@workflow/utilsPatch
@workflow/world-vercelPatch
workflowPatch
tarballsPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/rollupPatch
@workflow/vitePatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-testingPatch
@workflow/errorsPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/aiPatch
@workflow/nuxtPatch

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

vercelBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production119912191419
✅ 💻 Local Development134802001548
❌ 📦 Local Production1577102191806
❌ 🐘 Local Postgres158432191806
✅ 🪟 Windows12900129
❌ 📋 Other7243176903
Total65611710337611

❌ Failed Tests

▲ Vercel Production (1 failed)

express (1 failed):

📦 Local Production (10 failed)

express-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

hono-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-canary (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

nitro-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

sveltekit-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step
🐘 Local Postgres (3 failed)

nextjs-turbopack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response

nextjs-webpack-stable-lazy-discovery-disabled (1 failed):

  • addTenWorkflow | wrun_01KRZ48T4PQ7QA010AR1EMJBXX

nextjs-webpack-stable-lazy-discovery-enabled (1 failed):

  • DurableAgent e2e core basic text response
📋 Other (3 failed)

e2e-local-dev-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-nest-stable (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

e2e-local-prod-tanstack-start- (1 failed):

  • AbortController abortDeterministicBranchFromStepWorkflow: branches stay consistent when abort comes from a step

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro103026
✅ example103026
❌ express102126
✅ fastify103026
✅ hono103026
✅ nextjs-turbopack12702
✅ nextjs-webpack12702
✅ nitro103026
✅ nuxt103026
✅ sveltekit12207
✅ vite103026
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
✅ nextjs-turbopack-stable-lazy-discovery-enabled12900
✅ nextjs-webpack-stable-lazy-discovery-disabled12900
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
❌ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable104025
❌ express-stable103125
✅ fastify-stable104025
❌ hono-stable103125
❌ nextjs-turbopack-canary109119
❌ nextjs-turbopack-stable-lazy-discovery-disabled12810
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
❌ nextjs-webpack-canary109119
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
❌ nitro-stable103125
✅ nuxt-stable104025
❌ sveltekit-stable12216
✅ vite-stable104025
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable104025
✅ express-stable104025
✅ fastify-stable104025
✅ hono-stable104025
✅ nextjs-turbopack-canary110019
✅ nextjs-turbopack-stable-lazy-discovery-disabled12900
❌ nextjs-turbopack-stable-lazy-discovery-enabled12810
✅ nextjs-webpack-canary110019
❌ nextjs-webpack-stable-lazy-discovery-disabled12810
❌ nextjs-webpack-stable-lazy-discovery-enabled12810
✅ nitro-stable104025
✅ nuxt-stable104025
✅ sveltekit-stable12306
✅ vite-stable104025
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack12900
❌ 📋 Other
AppPassedFailedSkipped
❌ e2e-local-dev-nest-stable103125
✅ e2e-local-dev-tanstack-start-104025
✅ e2e-local-postgres-nest-stable104025
✅ e2e-local-postgres-tanstack-start-104025
❌ e2e-local-prod-nest-stable103125
❌ e2e-local-prod-tanstack-start-103125
✅ e2e-vercel-prod-tanstack-start103026

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actionsBot commented May 19, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express0.030s (-31.6% 🟢)1.005s (~)0.975s101.00x
💻 LocalNitro0.033s (-23.9% 🟢)1.005s (~)0.972s101.08x
💻 LocalNext.js (Turbopack)0.035s1.004s0.969s101.15x
🐘 PostgresExpress0.043s (-25.3% 🟢)1.011s (~)0.967s101.43x
🐘 PostgresNitro0.048s (-49.6% 🟢)1.011s (-3.0%)0.963s101.58x
🐘 PostgresNext.js (Turbopack)0.051s1.012s0.960s101.69x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express0.446s (+89.3% 🔺)2.409s (+12.8% 🔺)1.963s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.065s (-5.4% 🟢)2.006s (~)0.941s101.00x
💻 LocalNext.js (Turbopack)1.066s2.006s0.940s101.00x
💻 LocalNitro1.071s (-5.3% 🟢)2.006s (~)0.936s101.01x
🐘 PostgresNitro1.080s (-5.2% 🟢)2.008s (~)0.928s101.01x
🐘 PostgresExpress1.081s (-5.7% 🟢)2.008s (~)0.927s101.02x
🐘 PostgresNext.js (Turbopack)1.090s2.008s0.918s101.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.649s (-12.1% 🟢)3.776s (-0.8%)2.127s101.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.401s (-5.0%)11.022s (~)0.620s31.00x
💻 LocalExpress10.402s (-4.8%)11.021s (~)0.619s31.00x
💻 LocalNext.js (Turbopack)10.410s11.020s0.609s31.00x
🐘 PostgresNitro10.413s (-4.2%)11.015s (~)0.602s31.00x
🐘 PostgresExpress10.425s (-4.9%)11.012s (~)0.587s31.00x
🐘 PostgresNext.js (Turbopack)10.524s11.019s0.495s31.01x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express13.523s (-20.4% 🟢)15.264s (-23.8% 🟢)1.742s21.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Next.js (Turbopack)13.406s14.026s0.620s51.00x
💻 LocalNitro13.461s (-10.6% 🟢)14.026s (-12.5% 🟢)0.566s51.00x
🐘 PostgresNitro13.468s (-7.7% 🟢)14.018s (-6.7% 🟢)0.549s51.00x
🐘 PostgresExpress13.489s (-7.5% 🟢)14.018s (-6.7% 🟢)0.528s51.01x
💻 LocalExpress13.508s (-9.8% 🟢)14.026s (-6.7% 🟢)0.518s51.01x
🐘 PostgresNext.js (Turbopack)13.663s14.023s0.361s51.02x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express21.355s (-57.6% 🟢)23.458s (-55.4% 🟢)2.103s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express11.813s (-28.8% 🟢)12.022s (-29.4% 🟢)0.208s81.00x
💻 LocalNext.js (Turbopack)11.838s12.021s0.183s81.00x
💻 LocalNitro11.893s (-29.1% 🟢)12.147s (-28.7% 🟢)0.255s81.01x
🐘 PostgresExpress12.041s (-14.0% 🟢)12.514s (-14.2% 🟢)0.473s81.02x
🐘 PostgresNitro12.044s (-13.8% 🟢)12.519s (-12.5% 🟢)0.475s81.02x
🐘 PostgresNext.js (Turbopack)12.422s13.016s0.594s71.05x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express33.298s (-72.5% 🟢)35.645s (-71.2% 🟢)2.348s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro1.127s (-11.5% 🟢)2.007s (~)0.880s151.00x
🐘 PostgresExpress1.135s (-10.0% 🟢)2.007s (~)0.872s151.01x
💻 LocalExpress1.181s (-20.7% 🟢)2.006s (~)0.825s151.05x
💻 LocalNitro1.190s (-27.0% 🟢)2.005s (-3.3%)0.815s151.06x
💻 LocalNext.js (Turbopack)1.195s2.005s0.810s151.06x
🐘 PostgresNext.js (Turbopack)1.220s2.007s0.788s151.08x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.695s (-5.8% 🟢)4.697s (+1.6%)2.002s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.181s (-50.0% 🟢)2.007s (-33.3% 🟢)0.826s151.00x
🐘 PostgresNitro1.194s (-49.2% 🟢)2.007s (-33.3% 🟢)0.813s151.01x
🐘 PostgresNext.js (Turbopack)1.379s2.009s0.630s151.17x
💻 LocalNext.js (Turbopack)1.393s2.004s0.611s151.18x
💻 LocalExpress1.762s (-40.3% 🟢)2.005s (-41.9% 🟢)0.243s151.49x
💻 LocalNitro1.812s (-42.4% 🟢)2.005s (-48.4% 🟢)0.193s151.53x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.614s (~)5.286s (+3.4%)1.672s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.7% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.311s (-62.3% 🟢)2.008s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.670s2.007s0.337s151.32x
💻 LocalNext.js (Turbopack)3.751s4.439s0.688s72.96x
💻 LocalExpress4.495s (-46.1% 🟢)5.010s (-44.5% 🟢)0.514s63.55x
💻 LocalNitro5.333s (-36.1% 🟢)6.013s (-33.3% 🟢)0.680s54.22x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.082s (+90.6% 🔺)10.231s (+67.0% 🔺)2.149s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.126s (-10.4% 🟢)2.007s (~)0.881s151.00x
🐘 PostgresNitro1.131s (-10.0% 🟢)2.008s (~)0.877s151.00x
💻 LocalNext.js (Turbopack)1.156s2.005s0.849s151.03x
💻 LocalExpress1.208s (-36.2% 🟢)2.006s (-15.1% 🟢)0.798s151.07x
🐘 PostgresNext.js (Turbopack)1.210s2.010s0.801s151.07x
💻 LocalNitro1.222s (-34.5% 🟢)2.006s (-14.3% 🟢)0.784s151.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.069s (+18.9% 🔺)5.001s (+15.0% 🔺)1.932s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.178s (-49.7% 🟢)2.008s (-33.3% 🟢)0.831s151.00x
🐘 PostgresNitro1.205s (-48.5% 🟢)2.009s (-33.3% 🟢)0.804s151.02x
🐘 PostgresNext.js (Turbopack)1.364s2.010s0.645s151.16x
💻 LocalNext.js (Turbopack)1.525s2.006s0.481s151.29x
💻 LocalExpress1.887s (-39.8% 🟢)2.005s (-46.7% 🟢)0.119s151.60x
💻 LocalNitro1.888s (-38.4% 🟢)2.005s (-48.4% 🟢)0.117s151.60x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.666s (+14.8% 🔺)5.322s (+11.1% 🔺)1.656s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.265s (-63.8% 🟢)2.008s (-49.9% 🟢)0.743s151.00x
🐘 PostgresNitro1.312s (-62.3% 🟢)2.009s (-49.9% 🟢)0.697s151.04x
🐘 PostgresNext.js (Turbopack)1.658s2.009s0.351s151.31x
💻 LocalNext.js (Turbopack)3.686s4.295s0.609s72.91x
💻 LocalExpress5.452s (-38.1% 🟢)6.012s (-35.2% 🟢)0.560s54.31x
💻 LocalNitro5.564s (-39.1% 🟢)6.012s (-40.0% 🟢)0.447s54.40x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.066s (-21.1% 🟢)6.658s (-18.6% 🟢)1.592s51.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.440s (-46.4% 🟢)1.008s (~)0.568s601.00x
💻 LocalExpress0.463s (-53.0% 🟢)1.004s (-6.7% 🟢)0.541s601.05x
🐘 PostgresExpress0.463s (-44.8% 🟢)1.023s (~)0.560s591.05x
💻 LocalNitro0.474s (-51.7% 🟢)1.004s (-8.2% 🟢)0.530s601.08x
💻 LocalNext.js (Turbopack)0.537s1.012s0.476s601.22x
🐘 PostgresNext.js (Turbopack)0.553s1.007s0.453s601.26x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.483s (-71.2% 🟢)6.989s (-67.2% 🟢)1.506s91.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.025s (-48.1% 🟢)1.613s (-28.5% 🟢)0.589s561.00x
🐘 PostgresNitro1.087s (-43.6% 🟢)1.844s (-12.2% 🟢)0.757s491.06x
💻 LocalExpress1.151s (-61.8% 🟢)2.005s (-44.1% 🟢)0.854s451.12x
💻 LocalNitro1.175s (-61.3% 🟢)2.005s (-46.6% 🟢)0.831s451.15x
💻 LocalNext.js (Turbopack)1.281s2.030s0.750s451.25x
🐘 PostgresNext.js (Turbopack)1.304s2.031s0.727s451.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express12.876s (-62.7% 🟢)15.083s (-59.0% 🟢)2.207s61.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.976s (-50.5% 🟢)2.314s (-47.0% 🟢)0.339s521.00x
🐘 PostgresNitro2.204s (-46.3% 🟢)2.912s (-36.7% 🟢)0.709s421.12x
🐘 PostgresNext.js (Turbopack)2.402s3.034s0.632s401.22x
💻 LocalExpress2.627s (-71.5% 🟢)3.008s (-70.0% 🟢)0.380s401.33x
💻 LocalNitro2.647s (-71.5% 🟢)3.007s (-70.0% 🟢)0.360s401.34x
💻 LocalNext.js (Turbopack)2.800s3.309s0.509s371.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express39.900s (-69.3% 🟢)41.994s (-68.2% 🟢)2.095s31.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.165s (-41.5% 🟢)1.006s (~)0.841s601.00x
🐘 PostgresNitro0.178s (-37.1% 🟢)1.006s (~)0.828s601.08x
🐘 PostgresNext.js (Turbopack)0.218s1.006s0.788s601.32x
💻 LocalNext.js (Turbopack)0.361s1.005s0.644s602.18x
💻 LocalExpress0.380s (-32.3% 🟢)1.004s (~)0.624s602.30x
💻 LocalNitro0.388s (-35.9% 🟢)1.004s (-1.8%)0.616s602.35x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.572s (+31.6% 🔺)4.350s (+19.6% 🔺)1.778s141.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.274s (-46.3% 🟢)1.005s (~)0.731s901.00x
🐘 PostgresNitro0.293s (-41.0% 🟢)1.006s (~)0.713s901.07x
🐘 PostgresNext.js (Turbopack)0.399s1.006s0.607s901.46x
💻 LocalNext.js (Turbopack)1.718s2.007s0.289s456.27x
💻 LocalExpress1.949s (-22.5% 🟢)2.402s (-20.2% 🟢)0.453s387.11x
💻 LocalNitro2.002s (-21.1% 🟢)2.534s (-15.8% 🟢)0.532s367.31x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.424s (+110.9% 🔺)8.128s (+69.1% 🔺)1.704s121.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.548s (-33.1% 🟢)1.005s (-1.2%)0.458s1201.00x
🐘 PostgresNitro0.593s (-25.0% 🟢)1.006s (~)0.413s1201.08x
🐘 PostgresNext.js (Turbopack)0.807s1.006s0.199s1201.47x
💻 LocalNext.js (Turbopack)8.490s9.022s0.532s1415.50x
💻 LocalExpress9.545s (-14.7% 🟢)10.107s (-15.4% 🟢)0.561s1217.43x
💻 LocalNitro9.876s (-11.7% 🟢)10.274s (-11.9% 🟢)0.398s1218.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express16.131s (+117.4% 🔺)17.804s (+92.6% 🔺)1.673s71.00x
▲ VercelNext.js (Turbopack)⚠️missing----
▲ VercelNitro⚠️missing----

🔍 Observability: Express

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.113s (+459.1% 🔺)2.005s (+99.6% 🔺)0.011s (-12.4% 🟢)2.018s (+98.2% 🔺)0.905s101.00x
💻 LocalNitro1.117s (+422.5% 🔺)2.005s (+99.6% 🔺)0.011s (-16.0% 🟢)2.018s (+98.0% 🔺)0.901s101.00x
💻 LocalNext.js (Turbopack)1.117s2.003s0.008s2.014s0.897s101.00x
🐘 PostgresNitro1.124s (+448.1% 🔺)2.002s (+100.3% 🔺)0.002s (~)2.011s (+98.9% 🔺)0.887s101.01x
🐘 PostgresExpress1.131s (+451.6% 🔺)2.000s (+100.3% 🔺)0.001s (-18.8% 🟢)2.009s (+98.6% 🔺)0.878s101.02x
🐘 PostgresNext.js (Turbopack)1.153s2.002s0.001s2.011s0.859s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.137s (-14.7% 🟢)3.233s (-21.0% 🟢)1.758s (+82.9% 🔺)5.512s (-1.4%)3.375s101.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Express1.508s (+99.1% 🔺)2.012s (+95.5% 🔺)0.008s (-17.9% 🟢)2.022s (+94.4% 🔺)0.514s301.00x
🐘 PostgresExpress1.512s (+140.0% 🔺)2.006s (+99.3% 🔺)0.003s (-11.2% 🟢)2.023s (+97.7% 🔺)0.511s301.00x
🐘 PostgresNitro1.514s (+142.7% 🔺)2.009s (+99.5% 🔺)0.004s (-6.5% 🟢)2.024s (+98.0% 🔺)0.510s301.00x
💻 LocalNitro1.523s (+81.5% 🔺)2.010s (+98.6% 🔺)0.010s (+7.4% 🔺)2.022s (+81.2% 🔺)0.499s301.01x
🐘 PostgresNext.js (Turbopack)1.582s2.010s0.004s2.025s0.443s301.05x
💻 LocalNext.js (Turbopack)1.648s2.008s0.009s2.198s0.550s281.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express6.009s (-7.6% 🟢)7.281s (-9.1% 🟢)0.360s (-11.8% 🟢)8.139s (-7.9% 🟢)2.130s81.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Nitro0.607s (-37.4% 🟢)1.035s (-17.0% 🟢)0.000s (+24.1% 🔺)1.042s (-17.2% 🟢)0.435s581.00x
🐘 PostgresExpress0.614s (-36.1% 🟢)1.034s (-19.1% 🟢)0.000s (-20.7% 🟢)1.042s (-20.3% 🟢)0.428s581.01x
🐘 PostgresNext.js (Turbopack)0.700s1.053s0.000s1.060s0.360s571.15x
💻 LocalNext.js (Turbopack)0.945s1.189s0.000s1.192s0.246s511.56x
💻 LocalNitro0.949s (-22.4% 🟢)1.236s (-38.8% 🟢)0.000s (+360.0% 🔺)1.238s (-38.7% 🟢)0.289s501.56x
💻 LocalExpress1.029s (-16.0% 🟢)1.095s (-45.8% 🟢)0.000s (-11.8% 🟢)1.195s (-40.9% 🟢)0.166s511.70x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.629s (-3.0%)4.700s (-7.9% 🟢)0.000s (-54.2% 🟢)5.188s (-6.2% 🟢)1.559s121.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

fan-out fan-in 10 streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.191s (-32.8% 🟢)1.905s (-12.5% 🟢)0.000s (NaN%)1.944s (-11.6% 🟢)0.753s311.00x
🐘 PostgresNitro1.257s (-29.9% 🟢)2.001s (-6.6% 🟢)0.000s (-6.7% 🟢)2.024s (-6.9% 🟢)0.767s301.06x
🐘 PostgresNext.js (Turbopack)1.385s2.183s0.000s2.191s0.806s281.16x
💻 LocalNext.js (Turbopack)2.467s3.027s0.001s3.031s0.564s202.07x
💻 LocalNitro2.716s (-19.8% 🟢)3.424s (-15.1% 🟢)0.001s (+25.0% 🔺)3.427s (-15.1% 🟢)0.711s182.28x
💻 LocalExpress2.837s (-18.2% 🟢)3.296s (-18.3% 🟢)0.000s (-47.4% 🟢)3.300s (-18.2% 🟢)0.463s192.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express5.303s (+15.6% 🔺)7.004s (+16.3% 🔺)0.000s (+Infinity% 🔺)7.529s (+16.6% 🔺)2.226s91.00x
▲ VercelNext.js (Turbopack)⚠️missing-----
▲ VercelNitro⚠️missing-----

🔍 Observability: Express

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNext.js (Turbopack)11/21
🐘 PostgresExpress14/21
▲ VercelExpress21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres14/21
Next.js (Turbopack)🐘 Postgres11/21
Nitro🐘 Postgres15/21
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)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5dca003 to 7ce5318CompareMay 19, 2026 00:59
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from fcb87b0 to 5759e34CompareMay 19, 2026 02:25
@pranaygp
pranaygpforce-pushed the pranaygp/codex/forward-port-stable-ci branch from 5759e34 to 978bdf3CompareMay 19, 2026 02:32
@pranaygp

Copy link
Copy Markdown
ContributorAuthor

Added a note to the PR description calling out the extra changes I made after the initial forward-port.

For visibility here too: these changes are deviations from the original #1986 payload, and they were added because CI on the main PR exposed failures that the stable forward-port alone did not cover:

  • V2 background step handlers now enqueue an idempotent workflow continuation instead of replaying inline from every completed background handler. The old path let racing handlers enter the next batch more than once, causing duplicate step_started / hook_received events and Unconsumed event in event log failures.
  • The eager Next builder now does a full workflow rebuild on modified files in watch mode. The previous modified-file rebuild refreshed generated route code but left the workflow manifest stale when an API route import graph changed, which is why the nextjs-webpack dev test timed out after adding a workflow file.
  • The dev E2E harness now imports the added workflow through the app-root @/workflows/... alias for Next, triggers the POST route so the module is actually compiled, and gives cleanup more time for slower Windows dev-server teardown.

These are still in service of making this forward-port pass on main, but they are intentionally called out as additional main stabilization work rather than changes that came directly from #1986. The no-backport note still applies: this PR itself should not be backported.

Sign up for freeto 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.

1 participant

@pranaygp