Skip to content

Add experimental idempotent start hooks#2684

Closed
NathanColosimo wants to merge 5 commits into
mainfrom
codex/idempotent-start-hooks
Closed

Add experimental idempotent start hooks#2684
NathanColosimo wants to merge 5 commits into
mainfrom
codex/idempotent-start-hooks

Conversation

@NathanColosimo

@NathanColosimo NathanColosimo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Refs #2376 (gaps 1 and 2). Paired server PR: https://github.com/vercel/workflow-server/pull/561.

Adds experimental start-hook idempotency: start() can atomically reserve a hook token as part of run admission, so duplicate starts are rejected before any duplicate workflow code can run.

const run = await start(processOrder, [orderId], {
  experimentalStartHook: {
    token: `order:${orderId}`,
    experimental_ttl: '30 days',
  },
});
  • Only one live run may own a token; conflicts throw HookConflictError with the owning conflictingRunId.
  • Local / Postgres worlds (event-first): admission (run + run_created event + token claim) commits atomically before the run is queued; if queueing then fails, start() cancels the run, which releases the unmaterialized claim so the caller can retry immediately.
  • Vercel world (queue-first): the queue message is accepted first, then workflow-server admission atomically creates the run, run_created event, and token claim. A queued run that loses admission acknowledges without running user code (turbo mode is disabled for start-hook runs). If the queue accepts but admission cannot be confirmed, start() throws the new WorkflowStartError carrying runId / stage / queued / retryable.
  • The workflow materializes the reserved claim with createHook({ token }) as usual; after disposal or run completion/failure the claim is retained until the TTL so duplicates stay fenced. Cancellation releases claims that never materialized.
  • Vercel caps: TTL ≤ 30 days, token ≤ 255 bytes (rejected client-side before queueing); cross-deployment starts require the target deployment to support queued start-hook handling (capability-probed).

Docs Preview

Page Preview
Foundations → Idempotency /v5/docs/foundations/idempotency#atomic-start-hook-idempotency
API → start() /v5/docs/api-reference/workflow-api/start
Errors → WorkflowStartError /v5/docs/api-reference/workflow-errors/workflow-start-error
Errors index /v5/docs/api-reference/workflow-errors

@changeset-bot

changeset-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f0dae38

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

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

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

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

@vercel

vercel Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Jul 1, 2026 11:56pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jul 1, 2026 11:56pm
example-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-astro-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-express-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-fastify-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-hono-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-nitro-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-nuxt-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workbench-vite-workflow Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Jul 1, 2026 11:56pm
workflow-swc-playground Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workflow-tarballs Ready Ready Preview, Comment Jul 1, 2026 11:56pm
workflow-web Ready Ready Preview, Comment Jul 1, 2026 11:56pm

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 1442 0 241 1683
❌ 💻 Local Development 1616 1 219 1836
✅ 📦 Local Production 1617 0 219 1836
✅ 🐘 Local Postgres 1605 0 231 1836
✅ 🪟 Windows 153 0 0 153
✅ 📋 Other 891 0 180 1071
Total 7324 1 1090 8415

❌ Failed Tests

💻 Local Development (1 failed)

nextjs-webpack-canary (1 failed):

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

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 125 0 28
✅ example 125 0 28
✅ express 125 0 28
✅ fastify 125 0 28
✅ hono 125 0 28
✅ nextjs-turbopack 149 0 4
✅ nextjs-webpack 149 0 4
✅ nitro 125 0 28
✅ nuxt 125 0 28
✅ sveltekit 144 0 9
✅ vite 125 0 28
❌ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 128 0 25
✅ express-stable 128 0 25
✅ fastify-stable 128 0 25
✅ hono-stable 128 0 25
✅ nextjs-turbopack-canary 134 0 19
✅ nextjs-turbopack-stable 153 0 0
❌ nextjs-webpack-canary 133 1 19
✅ nextjs-webpack-stable 153 0 0
✅ nitro-stable 128 0 25
✅ nuxt-stable 128 0 25
✅ sveltekit-stable 147 0 6
✅ vite-stable 128 0 25
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 128 0 25
✅ express-stable 128 0 25
✅ fastify-stable 128 0 25
✅ hono-stable 128 0 25
✅ nextjs-turbopack-canary 134 0 19
✅ nextjs-turbopack-stable 153 0 0
✅ nextjs-webpack-canary 134 0 19
✅ nextjs-webpack-stable 153 0 0
✅ nitro-stable 128 0 25
✅ nuxt-stable 128 0 25
✅ sveltekit-stable 147 0 6
✅ vite-stable 128 0 25
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 127 0 26
✅ express-stable 127 0 26
✅ fastify-stable 127 0 26
✅ hono-stable 127 0 26
✅ nextjs-turbopack-canary 133 0 20
✅ nextjs-turbopack-stable 152 0 1
✅ nextjs-webpack-canary 133 0 20
✅ nextjs-webpack-stable 152 0 1
✅ nitro-stable 127 0 26
✅ nuxt-stable 127 0 26
✅ sveltekit-stable 146 0 7
✅ vite-stable 127 0 26
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 153 0 0
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 128 0 25
✅ e2e-local-dev-tanstack-start- 128 0 25
✅ e2e-local-postgres-nest-stable 127 0 26
✅ e2e-local-postgres-tanstack-start- 127 0 26
✅ e2e-local-prod-nest-stable 128 0 25
✅ e2e-local-prod-tanstack-start- 128 0 25
✅ e2e-vercel-prod-tanstack-start 125 0 28

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 0.048s (+9.3% 🔺) 1.005s (~) 0.957s 10 1.00x
💻 Local Express 0.051s (+10.4% 🔺) 1.006s (~) 0.955s 10 1.06x
💻 Local Next.js (Turbopack) 0.055s (+2.2%) 1.007s (~) 0.952s 10 1.14x
🐘 Postgres Next.js (Turbopack) 0.058s (-48.0% 🟢) 1.012s (-2.7%) 0.954s 10 1.19x
🐘 Postgres Nitro 0.069s (+9.5% 🔺) 1.012s (~) 0.943s 10 1.43x
🐘 Postgres Express 0.074s (+0.8%) 1.013s (~) 0.939s 10 1.53x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 0.206s (+1.8%) 1.579s (-20.1% 🟢) 1.373s 10 1.00x
▲ Vercel Nitro 0.223s (-39.0% 🟢) 1.433s (-21.8% 🟢) 1.210s 10 1.08x
▲ Vercel Next.js (Turbopack) 0.743s (+60.8% 🔺) 2.572s (~) 1.829s 10 3.61x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.083s (~) 2.007s (~) 0.924s 10 1.00x
💻 Local Express 1.085s (~) 2.006s (~) 0.921s 10 1.00x
💻 Local Next.js (Turbopack) 1.090s (+0.9%) 2.006s (~) 0.916s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.093s (-0.8%) 2.009s (~) 0.917s 10 1.01x
🐘 Postgres Nitro 1.101s (+0.9%) 2.010s (~) 0.909s 10 1.02x
🐘 Postgres Express 1.109s (+1.1%) 2.010s (~) 0.900s 10 1.02x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 1.377s (+1.1%) 2.823s (+7.2% 🔺) 1.446s 10 1.00x
▲ Vercel Nitro 1.412s (+5.0%) 3.114s (+10.1% 🔺) 1.702s 10 1.03x
▲ Vercel Next.js (Turbopack) 2.132s (-5.0% 🟢) 3.702s (-3.0%) 1.571s 10 1.55x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 10.465s (~) 11.022s (~) 0.557s 3 1.00x
🐘 Postgres Next.js (Turbopack) 10.477s (+0.8%) 11.013s (~) 0.536s 3 1.00x
🐘 Postgres Nitro 10.484s (~) 11.017s (~) 0.533s 3 1.00x
💻 Local Express 10.487s (~) 11.022s (~) 0.535s 3 1.00x
💻 Local Next.js (Turbopack) 10.514s (~) 11.022s (~) 0.508s 3 1.00x
🐘 Postgres Express 10.566s (+0.8%) 11.021s (~) 0.455s 3 1.01x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 11.606s (-2.5%) 13.853s (+1.3%) 2.247s 3 1.00x
▲ Vercel Express 11.743s (-2.6%) 14.192s (+4.1%) 2.449s 3 1.01x
▲ Vercel Next.js (Turbopack) 13.066s (+3.1%) 15.445s (+4.8%) 2.379s 2 1.13x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 13.664s (+0.7%) 14.027s (~) 0.362s 5 1.00x
🐘 Postgres Nitro 13.681s (~) 14.019s (~) 0.338s 5 1.00x
💻 Local Express 13.688s (~) 14.027s (~) 0.339s 5 1.00x
🐘 Postgres Express 13.762s (+1.0%) 14.021s (~) 0.260s 5 1.01x
💻 Local Next.js (Turbopack) 13.778s (+0.8%) 14.029s (~) 0.250s 5 1.01x
🐘 Postgres Next.js (Turbopack) 13.780s (+1.0%) 14.018s (~) 0.239s 5 1.01x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 16.314s (-2.7%) 18.610s (+1.0%) 2.296s 4 1.00x
▲ Vercel Express 16.618s (+1.2%) 18.814s (+0.5%) 2.196s 4 1.02x
▲ Vercel Next.js (Turbopack) 18.145s (+5.5% 🔺) 20.151s (+4.4%) 2.006s 3 1.11x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 12.198s (+0.7%) 13.025s (~) 0.827s 7 1.00x
🐘 Postgres Next.js (Turbopack) 12.246s (-1.5%) 13.016s (+0.8%) 0.770s 7 1.00x
💻 Local Express 12.255s (+0.6%) 13.026s (~) 0.772s 7 1.00x
🐘 Postgres Nitro 12.291s (+0.8%) 13.019s (~) 0.729s 7 1.01x
🐘 Postgres Express 12.373s (-0.9%) 13.019s (~) 0.646s 7 1.01x
💻 Local Next.js (Turbopack) 12.435s (+2.1%) 13.026s (~) 0.591s 7 1.02x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 18.136s (+2.9%) 20.146s (+4.2%) 2.009s 5 1.00x
▲ Vercel Nitro 18.253s (+5.2% 🔺) 20.327s (+7.2% 🔺) 2.074s 5 1.01x
▲ Vercel Next.js (Turbopack) 20.113s (+1.3%) 22.571s (+3.2%) 2.458s 4 1.11x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.189s (~) 2.008s (~) 0.820s 15 1.00x
🐘 Postgres Nitro 1.192s (-0.8%) 2.007s (~) 0.815s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.233s (-3.1%) 2.008s (-1.6%) 0.775s 15 1.04x
💻 Local Express 1.425s (+2.3%) 2.006s (~) 0.581s 15 1.20x
💻 Local Nitro 1.443s (+3.0%) 2.006s (~) 0.563s 15 1.21x
💻 Local Next.js (Turbopack) 1.550s (+8.9% 🔺) 2.074s (+3.4%) 0.524s 15 1.30x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.107s (+6.7% 🔺) 3.972s (+6.5% 🔺) 1.865s 8 1.00x
▲ Vercel Nitro 2.308s (+5.5% 🔺) 4.148s (+9.2% 🔺) 1.840s 8 1.10x
▲ Vercel Next.js (Turbopack) 4.022s (+21.4% 🔺) 5.861s (+14.4% 🔺) 1.838s 6 1.91x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.375s (+4.0%) 2.592s (-8.3% 🟢) 1.217s 12 1.00x
🐘 Postgres Express 1.376s (+1.5%) 2.469s (+6.6% 🔺) 1.093s 13 1.00x
🐘 Postgres Next.js (Turbopack) 1.463s (+9.9% 🔺) 3.110s (+25.4% 🔺) 1.646s 10 1.06x
💻 Local Express 2.343s (-9.3% 🟢) 3.008s (~) 0.665s 10 1.70x
💻 Local Nitro 2.416s (-4.2%) 3.009s (-3.2%) 0.593s 10 1.76x
💻 Local Next.js (Turbopack) 2.663s (-2.8%) 3.210s (+3.2%) 0.546s 10 1.94x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.447s (+44.2% 🔺) 5.134s (+34.1% 🔺) 1.688s 7 1.00x
▲ Vercel Express 3.638s (+37.3% 🔺) 5.451s (+35.5% 🔺) 1.813s 6 1.06x
▲ Vercel Next.js (Turbopack) 6.028s (+66.2% 🔺) 8.132s (+40.8% 🔺) 2.104s 4 1.75x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.605s (+1.0%) 4.137s (+3.1%) 2.532s 8 1.00x
🐘 Postgres Express 1.646s (+3.6%) 4.299s (+7.1% 🔺) 2.653s 7 1.03x
🐘 Postgres Next.js (Turbopack) 3.013s (+65.6% 🔺) 6.218s (+27.1% 🔺) 3.204s 5 1.88x
💻 Local Nitro 4.021s (-34.6% 🟢) 5.180s (-24.0% 🟢) 1.159s 6 2.51x
💻 Local Next.js (Turbopack) 4.208s (-46.3% 🟢) 5.179s (-35.4% 🟢) 0.972s 6 2.62x
💻 Local Express 4.868s (-26.9% 🟢) 5.348s (-27.9% 🟢) 0.480s 6 3.03x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.099s (+15.6% 🔺) 5.221s (+20.4% 🔺) 2.121s 6 1.00x
▲ Vercel Nitro 3.439s (+30.0% 🔺) 5.457s (+29.0% 🔺) 2.018s 6 1.11x
▲ Vercel Next.js (Turbopack) 6.223s (+50.0% 🔺) 8.009s (+31.1% 🔺) 1.786s 5 2.01x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.189s (~) 2.008s (~) 0.819s 15 1.00x
🐘 Postgres Nitro 1.194s (+1.9%) 2.008s (~) 0.814s 15 1.00x
🐘 Postgres Express 1.197s (+1.5%) 2.007s (~) 0.810s 15 1.01x
💻 Local Nitro 1.428s (+4.2%) 2.007s (~) 0.578s 15 1.20x
💻 Local Next.js (Turbopack) 1.452s (+1.6%) 2.006s (~) 0.555s 15 1.22x
💻 Local Express 1.477s (+2.1%) 2.006s (~) 0.530s 15 1.24x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.960s (+2.2%) 4.026s (+13.9% 🔺) 2.066s 8 1.00x
▲ Vercel Express 2.011s (+6.3% 🔺) 3.967s (+6.8% 🔺) 1.956s 8 1.03x
▲ Vercel Next.js (Turbopack) 3.478s (+13.0% 🔺) 5.279s (+7.0% 🔺) 1.801s 6 1.78x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.307s (-7.0% 🟢) 3.008s (+18.8% 🔺) 1.701s 10 1.00x
🐘 Postgres Express 1.318s (+0.9%) 2.393s (-3.2%) 1.074s 13 1.01x
🐘 Postgres Nitro 1.352s (+2.9%) 2.593s (~) 1.241s 12 1.03x
💻 Local Next.js (Turbopack) 2.398s (-10.1% 🟢) 2.918s (-9.1% 🟢) 0.519s 11 1.84x
💻 Local Nitro 2.489s (-9.1% 🟢) 3.008s (-10.0% 🟢) 0.519s 10 1.90x
💻 Local Express 2.560s (-3.0%) 3.008s (~) 0.449s 10 1.96x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.344s (-6.5% 🟢) 3.943s (~) 1.600s 8 1.00x
▲ Vercel Nitro 2.451s (+5.7% 🔺) 4.317s (+14.3% 🔺) 1.866s 7 1.05x
▲ Vercel Next.js (Turbopack) 3.921s (+15.1% 🔺) 5.495s (+4.3%) 1.574s 6 1.67x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.675s (+1.9%) 4.302s (~) 2.627s 7 1.00x
🐘 Postgres Express 1.683s (+5.6% 🔺) 4.586s (+10.9% 🔺) 2.904s 7 1.00x
🐘 Postgres Next.js (Turbopack) 2.981s (+14.4% 🔺) 6.014s (+15.0% 🔺) 3.033s 5 1.78x
💻 Local Nitro 4.934s (-23.4% 🟢) 6.014s (-18.9% 🟢) 1.080s 5 2.95x
💻 Local Express 5.518s (-12.9% 🟢) 6.214s (-8.8% 🟢) 0.697s 5 3.29x
💻 Local Next.js (Turbopack) 5.846s (-8.1% 🟢) 6.418s (-11.1% 🟢) 0.572s 5 3.49x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.780s (-1.7%) 4.739s (+4.0%) 1.960s 7 1.00x
▲ Vercel Nitro 2.858s (+0.7%) 4.805s (+9.6% 🔺) 1.948s 7 1.03x
▲ Vercel Next.js (Turbopack) 4.925s (+12.5% 🔺) 6.871s (+11.3% 🔺) 1.945s 5 1.77x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.544s (-10.5% 🟢) 1.006s (-4.3%) 0.463s 60 1.00x
🐘 Postgres Nitro 0.579s (+2.7%) 1.023s (~) 0.444s 59 1.06x
🐘 Postgres Express 0.600s (+8.6% 🔺) 1.023s (+1.7%) 0.424s 59 1.10x
💻 Local Express 0.603s (+9.2% 🔺) 1.005s (~) 0.402s 60 1.11x
💻 Local Nitro 0.612s (+1.5%) 1.005s (-3.3%) 0.393s 60 1.13x
💻 Local Next.js (Turbopack) 0.682s (+3.1%) 1.040s (-1.7%) 0.358s 58 1.25x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.518s (-8.5% 🟢) 4.142s (-7.0% 🟢) 1.624s 15 1.00x
▲ Vercel Express 2.553s (-5.1% 🟢) 4.460s (+4.0%) 1.906s 14 1.01x
▲ Vercel Next.js (Turbopack) 4.067s (+12.6% 🔺) 6.187s (+15.0% 🔺) 2.120s 10 1.61x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.310s (-12.0% 🟢) 2.007s (-6.1% 🟢) 0.697s 45 1.00x
🐘 Postgres Nitro 1.363s (+3.8%) 2.007s (-2.2%) 0.644s 45 1.04x
🐘 Postgres Express 1.465s (+8.4% 🔺) 2.076s (+2.3%) 0.612s 44 1.12x
💻 Local Nitro 1.518s (+6.2% 🔺) 2.006s (~) 0.488s 45 1.16x
💻 Local Express 1.520s (+8.5% 🔺) 2.006s (~) 0.486s 45 1.16x
💻 Local Next.js (Turbopack) 1.617s (+8.6% 🔺) 2.028s (+1.1%) 0.412s 45 1.23x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 5.943s (-3.5%) 7.851s (~) 1.907s 12 1.00x
▲ Vercel Nitro 5.975s (-2.6%) 7.743s (+2.2%) 1.768s 12 1.01x
▲ Vercel Next.js (Turbopack) 8.323s (+4.9%) 10.458s (+6.8% 🔺) 2.135s 9 1.40x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 2.557s (-13.2% 🟢) 3.008s (-12.8% 🟢) 0.451s 40 1.00x
🐘 Postgres Nitro 2.743s (+5.4% 🔺) 3.108s (+2.4%) 0.365s 40 1.07x
🐘 Postgres Express 2.850s (+8.4% 🔺) 3.167s (+2.7%) 0.317s 38 1.11x
💻 Local Nitro 3.252s (+2.3%) 4.010s (+1.6%) 0.758s 30 1.27x
💻 Local Next.js (Turbopack) 3.505s (+9.5% 🔺) 4.076s (+1.7%) 0.571s 30 1.37x
💻 Local Express 3.531s (+18.2% 🔺) 4.109s (+18.6% 🔺) 0.578s 30 1.38x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 12.071s (+1.9%) 14.318s (+6.8% 🔺) 2.247s 9 1.00x
▲ Vercel Express 12.075s (+3.1%) 14.184s (+5.9% 🔺) 2.109s 9 1.00x
▲ Vercel Next.js (Turbopack) 17.391s (+3.1%) 19.446s (+4.0%) 2.056s 7 1.44x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.175s (-24.3% 🟢) 1.006s (~) 0.831s 60 1.00x
🐘 Postgres Express 0.222s (~) 1.006s (~) 0.784s 60 1.27x
🐘 Postgres Nitro 0.226s (+5.4% 🔺) 1.006s (~) 0.780s 60 1.29x
💻 Local Nitro 0.525s (+22.1% 🔺) 1.005s (~) 0.480s 60 3.00x
💻 Local Express 0.528s (+17.4% 🔺) 1.005s (~) 0.477s 60 3.02x
💻 Local Next.js (Turbopack) 0.639s (+1.7%) 1.022s (+1.7%) 0.383s 59 3.65x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.047s (+2.1%) 2.765s (+13.4% 🔺) 1.718s 22 1.00x
▲ Vercel Express 1.222s (+31.2% 🔺) 2.869s (+15.8% 🔺) 1.647s 21 1.17x
▲ Vercel Next.js (Turbopack) 2.409s (+21.5% 🔺) 4.179s (+8.7% 🔺) 1.771s 15 2.30x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.272s (-14.4% 🟢) 1.006s (-1.5%) 0.734s 90 1.00x
🐘 Postgres Nitro 0.337s (~) 1.007s (~) 0.669s 90 1.24x
🐘 Postgres Express 0.346s (+3.8%) 1.017s (+1.1%) 0.671s 89 1.27x
💻 Local Nitro 2.485s (+29.1% 🔺) 3.043s (+21.3% 🔺) 0.558s 30 9.13x
💻 Local Express 2.504s (+15.1% 🔺) 3.010s (+8.8% 🔺) 0.506s 30 9.20x
💻 Local Next.js (Turbopack) 2.594s (-11.4% 🟢) 3.008s (-11.0% 🟢) 0.414s 30 9.53x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.455s (-10.1% 🟢) 3.223s (+7.2% 🔺) 1.769s 29 1.00x
▲ Vercel Express 1.498s (+5.3% 🔺) 3.065s (+2.0%) 1.567s 30 1.03x
▲ Vercel Next.js (Turbopack) 2.995s (+20.1% 🔺) 4.975s (+21.1% 🔺) 1.981s 19 2.06x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.483s (-3.5%) 2.985s (+18.7% 🔺) 2.502s 41 1.00x
🐘 Postgres Nitro 0.566s (+3.0%) 1.118s (-1.0%) 0.552s 108 1.17x
🐘 Postgres Express 0.587s (+8.1% 🔺) 1.118s (~) 0.531s 108 1.22x
💻 Local Express 4.713s (-49.0% 🟢) 8.160s (-21.9% 🟢) 3.447s 15 9.76x
💻 Local Nitro 5.748s (-40.4% 🟢) 8.666s (-19.6% 🟢) 2.918s 14 11.91x
💻 Local Next.js (Turbopack) 6.150s (-43.1% 🟢) 9.025s (-23.2% 🟢) 2.875s 14 12.74x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.073s (+16.7% 🔺) 4.208s (+15.0% 🔺) 2.135s 29 1.00x
▲ Vercel Nitro 2.089s (+24.6% 🔺) 4.084s (+18.4% 🔺) 1.994s 30 1.01x
▲ Vercel Next.js (Turbopack) 4.551s (+12.0% 🔺) 6.536s (+11.4% 🔺) 1.985s 19 2.19x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.159s (-5.1% 🟢) 2.000s (+0.6%) 0.001s (+22.2% 🔺) 2.011s (~) 0.852s 10 1.00x
💻 Local Nitro 1.160s (~) 2.004s (~) 0.013s (+18.7% 🔺) 2.020s (~) 0.860s 10 1.00x
💻 Local Express 1.160s (+1.7%) 2.005s (~) 0.012s (+15.1% 🔺) 2.020s (~) 0.860s 10 1.00x
🐘 Postgres Express 1.164s (+1.0%) 1.999s (~) 0.002s (+41.7% 🔺) 2.011s (~) 0.847s 10 1.00x
🐘 Postgres Nitro 1.175s (-1.1%) 1.999s (~) 0.002s (+25.0% 🔺) 2.011s (~) 0.836s 10 1.01x
💻 Local Next.js (Turbopack) 1.186s (+4.1%) 1.957s (~) 0.012s (~) 2.019s (~) 0.834s 10 1.02x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.983s (+3.8%) 3.484s (+12.0% 🔺) 1.751s (+7.0% 🔺) 5.761s (+11.5% 🔺) 3.778s 10 1.00x
▲ Vercel Express 2.094s (+2.7%) 3.439s (+2.6%) 1.939s (+40.0% 🔺) 5.907s (+12.8% 🔺) 3.813s 10 1.06x
▲ Vercel Next.js (Turbopack) 4.014s (+25.0% 🔺) 4.364s (+9.2% 🔺) 1.364s (+65.1% 🔺) 7.358s (+20.4% 🔺) 3.344s 10 2.02x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.565s (~) 2.010s (~) 0.014s (+5.1% 🔺) 2.027s (~) 0.462s 30 1.00x
🐘 Postgres Next.js (Turbopack) 1.582s (-16.3% 🟢) 2.009s (-11.3% 🟢) 0.005s (-31.6% 🟢) 2.025s (-12.0% 🟢) 0.443s 30 1.01x
💻 Local Express 1.593s (+3.5%) 2.008s (~) 0.012s (-7.4% 🟢) 2.026s (~) 0.432s 30 1.02x
🐘 Postgres Nitro 1.612s (+2.5%) 1.999s (~) 0.005s (-11.0% 🟢) 2.028s (~) 0.415s 30 1.03x
🐘 Postgres Express 1.615s (+1.8%) 2.007s (-1.4%) 0.005s (-2.6%) 2.028s (-1.5%) 0.413s 30 1.03x
💻 Local Next.js (Turbopack) 1.648s (+4.4%) 1.967s (~) 0.013s (+12.4% 🔺) 2.027s (~) 0.379s 30 1.05x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.493s (-3.0%) 7.420s (+6.5% 🔺) 0.364s (+28.2% 🔺) 8.358s (+8.9% 🔺) 2.865s 8 1.00x
▲ Vercel Express 5.568s (-1.3%) 7.487s (+4.5%) 0.500s (+145.1% 🔺) 8.505s (+7.3% 🔺) 2.937s 8 1.01x
▲ Vercel Next.js (Turbopack) 10.527s (+11.7% 🔺) 11.459s (+6.6% 🔺) 0.341s (+33.0% 🔺) 13.111s (+10.9% 🔺) 2.584s 5 1.92x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.825s (+7.2% 🔺) 1.098s (-0.6%) 0.000s (-79.6% 🟢) 1.142s (+2.1%) 0.316s 53 1.00x
🐘 Postgres Express 0.848s (+8.9% 🔺) 1.102s (+3.9%) 0.000s (+Infinity% 🔺) 1.119s (+3.7%) 0.271s 54 1.03x
🐘 Postgres Next.js (Turbopack) 0.924s (-32.9% 🟢) 1.303s (-33.5% 🟢) 0.000s (NaN%) 1.312s (-34.5% 🟢) 0.388s 46 1.12x
💻 Local Express 1.298s (+1.9%) 1.949s (-3.2%) 0.000s (-59.7% 🟢) 1.952s (-3.2%) 0.654s 31 1.57x
💻 Local Next.js (Turbopack) 1.336s (-3.3%) 1.975s (~) 0.000s (-30.0% 🟢) 2.015s (~) 0.679s 30 1.62x
💻 Local Nitro 1.338s (+3.1%) 1.948s (-3.2%) 0.000s (-30.9% 🟢) 1.951s (-3.2%) 0.613s 31 1.62x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.093s (+7.5% 🔺) 4.604s (+6.3% 🔺) 0.000s (NaN%) 5.162s (+7.1% 🔺) 2.068s 12 1.00x
▲ Vercel Nitro 3.119s (-98.3% 🟢) 4.758s (-97.4% 🟢) 0.000s (+Infinity% 🔺) 5.271s (-97.2% 🟢) 2.152s 12 1.01x
▲ Vercel Next.js (Turbopack) 6.842s (+61.5% 🔺) 7.634s (+40.6% 🔺) 0.000s (NaN%) 8.906s (+43.7% 🔺) 2.063s 7 2.21x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

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

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.892s (~) 2.431s (~) 0.000s (-100.0% 🟢) 2.463s (+0.5%) 0.572s 25 1.00x
🐘 Postgres Express 1.955s (+13.0% 🔺) 2.577s (+12.6% 🔺) 0.000s (+125.0% 🔺) 2.610s (+13.2% 🔺) 0.655s 24 1.03x
🐘 Postgres Next.js (Turbopack) 2.826s (~) 3.266s (-2.8%) 0.000s (NaN%) 3.274s (-3.5%) 0.449s 19 1.49x
💻 Local Nitro 3.294s (-10.6% 🟢) 3.778s (-12.4% 🟢) 0.001s (+65.3% 🔺) 3.781s (-12.4% 🟢) 0.488s 16 1.74x
💻 Local Express 3.391s (~) 3.902s (-3.0%) 0.001s (-25.0% 🟢) 3.905s (-3.1%) 0.514s 16 1.79x
💻 Local Next.js (Turbopack) 3.676s (+5.4% 🔺) 4.123s (+3.4%) 0.000s (-60.0% 🟢) 4.165s (+3.3%) 0.489s 15 1.94x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.166s (+18.8% 🔺) 6.666s (+13.2% 🔺) 0.000s (NaN%) 7.199s (+11.5% 🔺) 2.033s 9 1.00x
▲ Vercel Express 7.424s (+71.9% 🔺) 8.925s (+57.1% 🔺) 0.000s (NaN%) 9.474s (+54.2% 🔺) 2.050s 7 1.44x
▲ Vercel Next.js (Turbopack) 8.562s (+23.4% 🔺) 9.464s (+19.4% 🔺) 0.000s (+Infinity% 🔺) 10.704s (+21.1% 🔺) 2.142s 6 1.66x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Nitro 15/21
🐘 Postgres Next.js (Turbopack) 14/21
▲ Vercel Nitro 11/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 14/21
Next.js (Turbopack) 🐘 Postgres 18/21
Nitro 🐘 Postgres 14/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)
  • 🌐 Platformatic: Community world (local development)

📋 View full workflow run

@NathanColosimo NathanColosimo force-pushed the codex/idempotent-start-hooks branch from 0c1320a to de1af62 Compare June 27, 2026 19:33
- Bump experimentalStartHookLoserAck capability cutoff to 5.0.0-beta.27
  (beta.26 was published from main without this change) and rename it
  away from colliding with World.experimentalStartHookAdmission
- Derive WorkflowStartError.queued from stage (collapses the options
  union and its serialized/reducer/reviver/web-hydration copies)
- Share ExperimentalStartHookSchema/type from @workflow/world; align the
  queue-side token validation (min(1)) with the event schema
- Classify 409 hook_conflict inside the shared errorForResponse so v3
  and v4 request paths map it identically
- Postgres: extract getOwnStartHookClaim/claimStartHookTokenInTx (the
  two run-creation paths had drifted), parallelize hook/claim snapshot
  reads, settle disposed-hook claims with guarded statements instead of
  SELECT-then-branch, drop the retention transaction (statements touch
  disjoint rows), and GC expired claim debris of terminal runs
- Local: one claim lock per token (reclaim and materialize now mutually
  exclude), expiry pre-check before taking the reclaim lock, shared
  settleClaimForDisposedHook, parallel tokens-dir sweep that skips
  already-settled claims and GCs expired debris
- start(): hoist repeated enqueue/create calls, contractError helper,
  drop the positional verifyRunId flag; stop logging raw hook tokens
@NathanColosimo

Copy link
Copy Markdown
Contributor Author

Superseded by the stacked split: #2762 (core API) → #2763 (world-local) → #2764 (world-postgres) → #2765 (world-vercel). Server counterpart remains vercel/workflow-server#561.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant