Skip to content

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

@pranaygp@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[v5 only] docs: restore start-in-workflow documentation by pranaygp · Pull Request #1803 · vercel/workflow · GitHub
Skip to content

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

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

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

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

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

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

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

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

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

@pranaygp@TooTallNate@VaguelySerious
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); [v5 only] docs: restore start-in-workflow documentation by pranaygp · Pull Request #1803 · vercel/workflow · GitHub
Skip to content

[v5 only] docs: restore start-in-workflow documentation - #1803

Merged
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs
Aug 3, 2026
Merged

[v5 only] docs: restore start-in-workflow documentation#1803
VaguelySerious merged 4 commits into
mainfrom
pranaygp/codex/start-in-workflow-docs

Conversation

@pranaygp

@pranaygppranaygp commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • restore the missing docs for calling start() directly inside workflow functions
  • document background child workflow execution and recursive/self-chaining patterns
  • add an empty changeset file to satisfy repo PR policy without shipping additional runtime changes

Note

  • this feature is implemented in Workflow v5 only; when these docs are ported through the versioned docs flow, keep them behind the docs version switcher and do not backport this content into earlier versions

Testing

  • not run (docs-only change; this worktree does not have installed node_modules)

@pranaygp
pranaygp requested a review from a team as a code ownerApril 17, 2026 19:53
CopilotAI review requested due to automatic review settings April 17, 2026 19:53
@changeset-bot

changeset-botBot commented Apr 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: edc110e

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

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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 Apr 17, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackBuildingBuildingPreviewAug 3, 2026 10:32pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 3, 2026 10:32pm
example-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-astro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-express-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-fastify-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-hono-workflowBuildingBuildingPreviewAug 3, 2026 10:32pm
workbench-nestjs-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nitro-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-nuxt-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-sveltekit-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workbench-vite-workflowReadyReadyPreviewAug 3, 2026 10:32pm
workflow-docsReadyReadyPreview, v0Aug 3, 2026 10:32pm
workflow-swc-playgroundReadyReadyPreviewAug 3, 2026 10:32pm
workflow-tarballsReadyReadyPreviewAug 3, 2026 10:32pm
workflow-webReadyReadyPreviewAug 3, 2026 10:32pm

@github-actions

github-actionsBot commented Apr 17, 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🥇 Nitro0.037s (-14.6% 🟢)1.004s (~)0.968s101.00x
💻 LocalExpress0.044s (~)1.005s (~)0.961s101.20x
💻 LocalNext.js (Turbopack)0.046s1.005s0.959s101.25x
🐘 PostgresExpress0.046s (-20.0% 🟢)1.011s (~)0.964s101.26x
🐘 PostgresNext.js (Turbopack)0.056s1.009s0.953s101.53x
🐘 PostgresNitro0.060s (-37.0% 🟢)1.009s (-3.2%)0.949s101.63x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)0.230s (-8.5% 🟢)1.923s (-17.6% 🟢)1.693s101.00x
▲ VercelExpress0.244s (+3.7%)2.297s (+7.6% 🔺)2.053s101.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro1.099s (-2.8%)2.005s (~)0.905s101.00x
🐘 PostgresExpress1.116s (-2.6%)2.011s (~)0.894s101.02x
💻 LocalNext.js (Turbopack)1.123s2.006s0.883s101.02x
💻 LocalExpress1.128s (~)2.005s (~)0.877s101.03x
🐘 PostgresNext.js (Turbopack)1.144s2.009s0.864s101.04x
🐘 PostgresNitro1.147s (+0.6%)2.010s (~)0.863s101.04x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.914s (+2.1%)3.674s (-3.5%)1.760s101.00x
▲ VercelNext.js (Turbopack)2.097s (+3.1%)3.572s (-6.8% 🟢)1.474s101.10x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro10.634s (-2.8%)11.023s (~)0.388s31.00x
🐘 PostgresExpress10.698s (-2.4%)11.026s (~)0.328s31.01x
🐘 PostgresNext.js (Turbopack)10.825s11.020s0.195s31.02x
💻 LocalNext.js (Turbopack)10.842s11.022s0.180s31.02x
🐘 PostgresNitro10.865s (~)11.017s (~)0.152s31.02x
💻 LocalExpress10.919s (~)11.023s (~)0.104s31.03x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express18.085s (+6.5% 🔺)20.081s (~)1.996s21.00x
▲ VercelNext.js (Turbopack)18.200s (+5.1% 🔺)19.815s (+2.1%)1.616s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express14.081s (-3.4%)15.024s (~)0.943s41.00x
💻 LocalNitro14.240s (-5.5% 🟢)15.029s (-6.2% 🟢)0.789s41.01x
🐘 PostgresNext.js (Turbopack)14.433s15.022s0.589s41.02x
🐘 PostgresNitro14.509s (-0.6%)15.022s (~)0.512s41.03x
💻 LocalNext.js (Turbopack)14.579s15.029s0.450s41.04x
💻 LocalExpress14.961s (~)15.030s (~)0.068s41.06x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)32.258s (-38.6% 🟢)33.944s (-37.8% 🟢)1.686s21.00x
▲ VercelExpress34.708s (-31.0% 🟢)36.764s (-30.1% 🟢)2.056s21.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 50 sequential steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express13.078s (-6.6% 🟢)13.734s (-5.9% 🟢)0.657s71.00x
🐘 PostgresNext.js (Turbopack)13.760s14.020s0.260s71.05x
🐘 PostgresNitro13.909s (~)14.164s (-1.0%)0.255s71.06x
💻 LocalNitro14.958s (-10.9% 🟢)15.027s (-11.8% 🟢)0.069s61.14x
💻 LocalNext.js (Turbopack)16.101s16.697s0.596s61.23x
💻 LocalExpress16.620s (~)17.031s (~)0.411s61.27x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)57.867s (-85.3% 🟢)58.931s (-85.1% 🟢)1.063s21.00x
▲ VercelExpress58.194s (-52.0% 🟢)60.327s (-51.2% 🟢)2.133s21.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.all with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.207s (-4.2%)2.009s (~)0.802s151.00x
🐘 PostgresNext.js (Turbopack)1.235s2.009s0.774s151.02x
🐘 PostgresNitro1.256s (-1.4%)2.010s (~)0.753s151.04x
💻 LocalNext.js (Turbopack)1.521s2.005s0.485s151.26x
💻 LocalExpress1.547s (+3.9%)2.006s (~)0.460s151.28x
💻 LocalNitro1.865s (+14.3% 🔺)2.392s (+15.3% 🔺)0.527s131.54x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.496s (-12.7% 🟢)4.297s (-7.0% 🟢)1.802s71.00x
▲ VercelNext.js (Turbopack)2.705s (-20.4% 🟢)4.074s (-17.4% 🟢)1.368s81.08x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.272s (-3.7%)3.008s (~)0.735s101.00x
🐘 PostgresNitro2.334s (-0.7%)3.008s (~)0.674s101.03x
🐘 PostgresNext.js (Turbopack)2.413s3.008s0.595s101.06x
💻 LocalNitro2.649s (-15.7% 🟢)3.008s (-22.6% 🟢)0.359s101.17x
💻 LocalExpress2.901s (-1.7%)3.209s (-7.1% 🟢)0.308s101.28x
💻 LocalNext.js (Turbopack)2.936s3.565s0.629s91.29x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.235s (-38.3% 🟢)3.688s (-27.8% 🟢)1.453s91.00x
▲ VercelNext.js (Turbopack)3.083s (-56.6% 🟢)4.713s (-47.1% 🟢)1.631s71.38x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.383s (-3.0%)4.011s (~)0.628s81.00x
🐘 PostgresNitro3.483s (~)4.013s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.655s4.012s0.356s81.08x
💻 LocalNitro6.940s (-16.9% 🟢)7.517s (-16.7% 🟢)0.577s42.05x
💻 LocalNext.js (Turbopack)7.905s8.522s0.617s42.34x
💻 LocalExpress8.199s (-1.7%)9.025s (~)0.826s42.42x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.692s (-36.5% 🟢)4.487s (-26.8% 🟢)1.795s71.00x
▲ VercelNext.js (Turbopack)4.072s (-54.3% 🟢)5.283s (-51.8% 🟢)1.211s61.51x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.190s (-5.4% 🟢)2.007s (~)0.818s151.00x
🐘 PostgresNext.js (Turbopack)1.216s2.008s0.793s151.02x
🐘 PostgresNitro1.257s (~)2.008s (~)0.751s151.06x
💻 LocalNitro1.476s (-20.9% 🟢)2.006s (-14.3% 🟢)0.530s151.24x
💻 LocalNext.js (Turbopack)1.528s2.006s0.478s151.28x
💻 LocalExpress1.550s (-18.1% 🟢)2.007s (-15.1% 🟢)0.456s151.30x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)2.070s (-29.4% 🟢)3.532s (-23.9% 🟢)1.462s91.00x
▲ VercelExpress2.361s (-8.5% 🟢)4.056s (-6.7% 🟢)1.695s81.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 25 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.265s (-3.3%)3.009s (~)0.744s101.00x
🐘 PostgresNitro2.334s (~)3.010s (~)0.676s101.03x
🐘 PostgresNext.js (Turbopack)2.420s3.010s0.590s101.07x
💻 LocalNitro2.694s (-12.1% 🟢)3.008s (-22.6% 🟢)0.314s101.19x
💻 LocalExpress3.047s (-2.7%)3.760s (~)0.713s81.35x
💻 LocalNext.js (Turbopack)3.117s3.676s0.559s91.38x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)3.010s (-4.2%)4.678s (+3.4%)1.668s71.00x
▲ VercelExpress3.200s (~)5.100s (+6.4% 🔺)1.900s61.06x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

Promise.race with 50 concurrent steps

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express3.389s (-3.1%)4.009s (~)0.620s81.00x
🐘 PostgresNitro3.482s (~)4.012s (~)0.530s81.03x
🐘 PostgresNext.js (Turbopack)3.651s4.009s0.358s81.08x
💻 LocalNitro7.396s (-19.1% 🟢)8.017s (-20.0% 🟢)0.620s42.18x
💻 LocalExpress8.671s (-1.5%)9.026s (-2.7%)0.355s42.56x
💻 LocalNext.js (Turbopack)8.962s9.520s0.558s42.64x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.609s (-59.3% 🟢)4.579s (-44.0% 🟢)1.969s71.00x
▲ VercelNext.js (Turbopack)3.451s (-48.9% 🟢)5.037s (-41.0% 🟢)1.586s61.32x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.605s (-27.9% 🟢)1.006s (-1.6%)0.401s601.00x
💻 LocalNitro0.702s (-28.4% 🟢)1.004s (-8.2% 🟢)0.302s601.16x
🐘 PostgresNext.js (Turbopack)0.750s1.006s0.256s601.24x
🐘 PostgresNitro0.821s (~)1.006s (~)0.185s601.36x
💻 LocalNext.js (Turbopack)0.839s1.022s0.183s591.39x
💻 LocalExpress0.996s (+1.2%)1.309s (+21.7% 🔺)0.313s461.65x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express10.272s (-46.0% 🟢)12.087s (-43.3% 🟢)1.815s51.00x
▲ VercelNext.js (Turbopack)10.356s (-28.6% 🟢)12.650s (-21.4% 🟢)2.294s51.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.435s (-27.4% 🟢)2.008s (-11.1% 🟢)0.573s451.00x
🐘 PostgresNext.js (Turbopack)1.859s2.053s0.194s441.30x
🐘 PostgresNitro1.876s (-2.7%)2.051s (-2.3%)0.175s451.31x
💻 LocalNitro2.296s (-24.4% 🟢)3.008s (-20.0% 🟢)0.712s301.60x
💻 LocalNext.js (Turbopack)2.668s3.008s0.340s301.86x
💻 LocalExpress3.223s (+6.9% 🔺)3.884s (+8.3% 🔺)0.661s242.25x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express29.607s (-14.3% 🟢)32.066s (-12.9% 🟢)2.459s31.00x
▲ VercelNext.js (Turbopack)35.134s (-29.5% 🟢)36.951s (-28.5% 🟢)1.816s31.19x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express2.962s (-25.8% 🟢)3.193s (-26.9% 🟢)0.231s381.00x
🐘 PostgresNext.js (Turbopack)3.800s4.075s0.276s301.28x
🐘 PostgresNitro4.030s (-1.8%)4.704s (+2.2%)0.674s261.36x
💻 LocalNitro7.429s (-20.1% 🟢)8.016s (-20.0% 🟢)0.587s152.51x
💻 LocalNext.js (Turbopack)8.676s9.089s0.413s142.93x
💻 LocalExpress9.156s (-0.6%)9.787s (-2.3%)0.631s133.09x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express76.856s (-40.9% 🟢)78.782s (-40.4% 🟢)1.926s21.00x
▲ VercelNext.js (Turbopack)79.484s (-25.8% 🟢)81.246s (-25.4% 🟢)1.762s21.03x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.218s (-22.8% 🟢)1.007s (~)0.789s601.00x
🐘 PostgresNext.js (Turbopack)0.252s1.007s0.755s601.16x
🐘 PostgresNitro0.284s (~)1.007s (~)0.723s601.30x
💻 LocalNext.js (Turbopack)0.551s1.004s0.454s602.53x
💻 LocalNitro0.577s (-4.6%)1.004s (-1.7%)0.427s602.65x
💻 LocalExpress0.681s (+21.5% 🔺)1.096s (+9.1% 🔺)0.415s553.12x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Next.js (Turbopack)1.618s (-20.0% 🟢)3.383s (-10.8% 🟢)1.765s181.00x
▲ VercelExpress1.619s (-17.2% 🟢)3.026s (-16.8% 🟢)1.407s201.00x
▲ VercelNitro⚠️missing----

🔍 Observability: Next.js (Turbopack) | Express

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.363s (-28.9% 🟢)1.006s (~)0.644s901.00x
🐘 PostgresNext.js (Turbopack)0.475s1.006s0.530s901.31x
🐘 PostgresNitro0.506s (+2.0%)1.007s (~)0.500s901.40x
💻 LocalNitro2.443s (-3.8%)3.007s (~)0.564s306.74x
💻 LocalExpress2.510s (~)3.008s (~)0.498s306.93x
💻 LocalNext.js (Turbopack)2.609s3.008s0.399s307.20x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.829s (-7.1% 🟢)4.530s (-5.8% 🟢)1.701s201.00x
▲ VercelNext.js (Turbopack)3.213s (-9.1% 🟢)4.649s (-10.5% 🟢)1.435s201.14x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.574s (-29.9% 🟢)1.006s (-1.1%)0.432s1201.00x
🐘 PostgresNext.js (Turbopack)0.761s1.023s0.262s1181.33x
🐘 PostgresNitro0.820s (+3.8%)1.009s (~)0.189s1191.43x
💻 LocalNitro10.441s (-6.7% 🟢)11.116s (-4.7%)0.675s1118.20x
💻 LocalNext.js (Turbopack)10.831s11.298s0.467s1118.88x
💻 LocalExpress11.153s (~)11.754s (-1.6%)0.601s1119.44x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express8.245s (+11.1% 🔺)10.147s (+9.8% 🔺)1.903s121.00x
▲ VercelNext.js (Turbopack)8.321s (-19.4% 🟢)10.133s (-17.5% 🟢)1.812s131.01x
▲ VercelNitro⚠️missing----

🔍 Observability: Express | Next.js (Turbopack)

Stream Benchmarks(includes TTFB metrics)
workflow with stream

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
💻 Local🥇 Nitro0.140s (-34.5% 🟢)1.004s (~)0.011s (-12.8% 🟢)1.018s (~)0.878s101.00x
🐘 PostgresExpress0.153s (-25.4% 🟢)0.999s (~)0.001s (-37.5% 🟢)1.009s (~)0.856s101.09x
💻 LocalNext.js (Turbopack)0.169s1.003s0.013s1.018s0.850s101.20x
🐘 PostgresNext.js (Turbopack)0.195s1.001s0.001s1.010s0.816s101.39x
💻 LocalExpress0.201s (+0.7%)1.004s (~)0.012s (-0.8%)1.018s (~)0.818s101.43x
🐘 PostgresNitro0.201s (-2.0%)0.995s (-0.5%)0.001s (-20.0% 🟢)1.010s (~)0.809s101.43x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express1.563s (-37.6% 🟢)3.059s (-25.2% 🟢)1.133s (+17.9% 🔺)4.644s (-16.9% 🟢)3.081s101.00x
▲ VercelNext.js (Turbopack)1.588s (-76.8% 🟢)2.948s (-65.9% 🟢)0.918s (+45.3% 🔺)4.258s (-56.5% 🟢)2.670s101.02x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express0.516s (-18.1% 🟢)1.007s (~)0.005s (+34.5% 🔺)1.023s (~)0.507s591.00x
🐘 PostgresNext.js (Turbopack)0.610s1.026s0.004s1.041s0.431s581.18x
🐘 PostgresNitro0.628s (+0.6%)1.025s (+1.8%)0.004s (-7.4% 🟢)1.039s (+1.6%)0.411s581.22x
💻 LocalNitro0.698s (-16.8% 🟢)1.011s (~)0.010s (+4.7%)1.115s (~)0.417s541.35x
💻 LocalExpress0.836s (+10.4% 🔺)1.012s (-1.6%)0.010s (+4.5%)1.112s (+6.9% 🔺)0.276s571.62x
💻 LocalNext.js (Turbopack)0.995s1.009s0.010s1.364s0.369s441.93x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express4.234s (-34.9% 🟢)5.957s (-25.6% 🟢)0.229s (-44.0% 🟢)6.692s (-24.2% 🟢)2.458s91.00x
▲ VercelNext.js (Turbopack)5.182s (-69.4% 🟢)6.517s (-64.3% 🟢)0.188s (-11.1% 🟢)7.154s (-62.2% 🟢)1.971s91.22x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Next.js (Turbopack)0.911s1.154s0.000s1.161s0.250s521.00x
🐘 PostgresExpress0.920s (-4.2%)1.071s (-16.2% 🟢)0.000s (-17.9% 🟢)1.079s (-17.4% 🟢)0.158s561.01x
🐘 PostgresNitro0.965s (~)1.274s (+2.1%)0.000s (+2.1%)1.285s (+2.2%)0.320s471.06x
💻 LocalNitro1.199s (-2.0%)2.019s (~)0.001s (+433.3% 🔺)2.021s (~)0.822s301.32x
💻 LocalNext.js (Turbopack)1.229s2.017s0.001s2.020s0.791s301.35x
💻 LocalExpress1.236s (+0.9%)2.021s (~)0.001s (+60.0% 🔺)2.024s (~)0.788s301.36x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express2.662s (-28.8% 🟢)3.853s (-24.5% 🟢)0.000s (-100.0% 🟢)4.312s (-22.0% 🟢)1.650s141.00x
▲ VercelNext.js (Turbopack)3.171s (-68.9% 🟢)4.375s (-62.0% 🟢)0.000s (NaN%)4.788s (-60.3% 🟢)1.617s131.19x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

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

💻 Local Development

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
🐘 Postgres🥇 Express1.649s (-6.9% 🟢)2.070s (-5.0%)0.000s (+Infinity% 🔺)2.078s (-5.5% 🟢)0.429s291.00x
🐘 PostgresNitro1.760s (-1.8%)2.065s (-3.6%)0.000s (-100.0% 🟢)2.092s (-3.8%)0.332s291.07x
🐘 PostgresNext.js (Turbopack)1.787s2.106s0.000s2.114s0.327s291.08x
💻 LocalNitro3.554s (+4.9%)4.032s (~)0.001s (~)4.035s (~)0.481s152.16x
💻 LocalNext.js (Turbopack)3.667s4.030s0.000s4.034s0.366s152.22x
💻 LocalExpress3.755s (+8.3% 🔺)4.234s (+5.0%)0.001s (+33.3% 🔺)4.237s (+5.0%)0.482s152.28x

▲ Production (Vercel)

WorldFrameworkWorkflow TimeTTFBSlurpWall TimeOverheadSamplesvs Fastest
▲ Vercel🥇 Express3.929s (-14.3% 🟢)5.227s (-13.2% 🟢)0.000s (NaN%)5.677s (-12.1% 🟢)1.748s111.00x
▲ VercelNext.js (Turbopack)4.425s (-21.2% 🟢)5.729s (-17.9% 🟢)0.000s (-100.0% 🟢)6.201s (-17.8% 🟢)1.775s101.13x
▲ VercelNitro⚠️missing-----

🔍 Observability: Express | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World🥇 Fastest FrameworkWins
💻 LocalNitro19/21
🐘 PostgresExpress20/21
▲ VercelExpress15/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework🥇 Fastest WorldWins
Express🐘 Postgres17/21
Next.js (Turbopack)🐘 Postgres17/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)

📋 View full workflow run

@github-actions

github-actionsBot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

Summary

PassedFailedSkippedTotal
❌ ▲ Vercel Production9441671012
✅ 💻 Local Development10180861104
✅ 📦 Local Production10180861104
❌ 🐘 Local Postgres10144861104
✅ 🪟 Windows920092
✅ 📋 Other258018276
Total434453434692

❌ Failed Tests

▲ Vercel Production (1 failed)

sveltekit (1 failed):

🐘 Local Postgres (4 failed)

hono-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

nitro-stable (2 failed):

  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KPEH0CWGA29TSWRF3NQWECVD
  • health check (queue-based) - workflow and step endpoints respond to health check messages

Details by Category

❌ ▲ Vercel Production
AppPassedFailedSkipped
✅ astro8507
✅ example8507
✅ express8507
✅ fastify8507
✅ hono8507
✅ nextjs-turbopack9002
✅ nextjs-webpack9002
✅ nitro8507
✅ nuxt8507
❌ sveltekit8417
✅ vite8507
✅ 💻 Local Development
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 📦 Local Production
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
✅ hono-stable8606
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
✅ nitro-stable8606
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
❌ 🐘 Local Postgres
AppPassedFailedSkipped
✅ astro-stable8606
✅ express-stable8606
✅ fastify-stable8606
❌ hono-stable8426
✅ nextjs-turbopack-canary73019
✅ nextjs-turbopack-stable9200
✅ nextjs-webpack-canary73019
✅ nextjs-webpack-stable9200
❌ nitro-stable8426
✅ nuxt-stable8606
✅ sveltekit-stable8606
✅ vite-stable8606
✅ 🪟 Windows
AppPassedFailedSkipped
✅ nextjs-turbopack9200
✅ 📋 Other
AppPassedFailedSkipped
✅ e2e-local-dev-nest-stable8606
✅ e2e-local-postgres-nest-stable8606
✅ e2e-local-prod-nest-stable8606

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Restores and expands documentation around starting workflows, especially using start() from within workflow functions (child workflows, background execution, and self-chaining/recursive patterns), and adds a no-op changeset to satisfy repo policy.

Changes:

  • Add docs/examples for calling start() inside workflow functions and explain determinism/step behavior.
  • Document recursive/self-chaining patterns and guidance for deploymentId: "latest".
  • Update “Common Patterns” background execution example to use start() directly (no wrapper step).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

FileDescription
docs/content/docs/foundations/starting-workflows.mdxAdds a new “Starting Workflows from Workflow Functions” section plus recursion/self-chaining guidance.
docs/content/docs/foundations/common-patterns.mdxUpdates background execution pattern to use start() directly inside workflows and adds a clarifying callout.
docs/content/docs/api-reference/workflow-api/start.mdxUpdates “Good to Know” and adds an example for using start() inside workflow functions.
.changeset/fresh-rules-glow.mdAdds a no-op/metadata-only changeset file per PR policy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 53 to 55
* The `start()` function can be used in any context: runtime code (API routes, Server Actions), step functions, or directly inside workflow functions.
* This is different from calling workflow functions directly, which is the typical pattern in Next.js applications.
* The function returns immediately after enqueuing the workflow - it doesn't wait for the workflow to complete.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The updated "Good to Know" bullet says start() can be used directly inside workflow functions, but the page frontmatter summary still states it's for starting runs "from outside a workflow function." Consider updating the frontmatter summary (and/or intro) so it doesn't contradict this expanded usage.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
---
---

Restore the missing documentation for calling `start()` directly inside workflow functions.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

PR description says this is an empty changeset to satisfy policy, but this changeset includes descriptive body text. If the intent is to keep it a true no-op changeset (like other empty entries in .changeset/), consider removing the body text or updating the PR description to match what’s being added.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +105
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

<Callout type="info">
Inside workflow functions, each `Run` property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

The text here implies that accessing run.runId inside a workflow triggers a step, but runId is a plain string field on Run (not an async getter with a "use step" directive). Consider rewording to clarify that asyncRun getters/methods (e.g. status, returnValue, cancel(), getReadable()) run as steps, while runId does not.

Suggested change
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.
<Callouttype="info">
Inside workflow functions, each`Run`property access (e.g., `run.status`, `run.returnValue`) triggers a workflow step. This means each access is recorded in the event log and replayed deterministically.
When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — plain fields like `.runId` are available directly, while async getters/methods such as `.status`, `.returnValue`, `.cancel()`, and `.getReadable()` can also be used inside the workflow.
<Callouttype="info">
Inside workflow functions, async`Run`getters and methods (for example `run.status`, `run.returnValue`, `run.cancel()`, and `run.getReadable()`) each trigger a workflow step. Plain fields like `run.runId` do not. These step-backed accesses are recorded in the event log and replayed deterministically.

Copilot uses AI. Check for mistakes.
</Callout>

<Callout type="info">
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.

CopilotAIApr 17, 2026

Copy link

Choose a reason for hiding this comment

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

This callout lists .runId among Run properties that execute as steps inside workflow functions, but runId is a synchronous field on Run. Suggest updating the wording/examples to distinguish between synchronous fields (e.g. runId) and step-backed async getters/methods (e.g. status, returnValue, cancel()).

Suggested change
Inside workflow functions, `start()` returns a full `Run` object. You can access `.runId`, `.status`, `.returnValue`, `.cancel()`, and other `Run` properties — each access executes as a separate step.
Inside workflow functions, `start()` returns a full `Run` object. Some members are synchronous fields, such as `.runId`, which you can read immediately. Others, such as `.status`, `.returnValue`, and `.cancel()`, are step-backed async getters/methods that execute as separate steps inside the workflow.

Copilot uses AI. Check for mistakes.
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
</Callout>

<Callout type="info">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Documentation incorrectly states .runId executes as a separate workflow step, when it is actually a plain synchronous string field.

Fix on Vercel

@VaguelySeriousVaguelySerious left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Docs LGTM but again I think we should callout that it's only on v5. That will also help us split out the v4/v5 docs when we make that PR, and won't confuse stable users in the meantime

@TooTallNateTooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review

Good restoration. Feature was added in #1133, reverted in #1475, re-added in #1491 (via the simpler 'use step' approach on the existing start() function). The docs accurately describe the re-introduced implementation.

Accuracy checks

All technical claims verified against the current implementation:

  • start() works inside workflows'use step' directive at packages/core/src/runtime/start.ts:122 makes it a step call inside a workflow context (#1491).
  • Returns a full Run objectRun has WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE at run.ts:73-82, so it survives the step boundary.
  • Each property access is a step — Verified for .status, .returnValue, .cancel(), .exists, .workflowName, .createdAt, .startedAt, .completedAt, .wakeUp(), .getReadable() — all have 'use step'.
  • returnValue polls every 1 second — Confirmed at run.ts:295-346 (#pollReturnValue with setTimeout(resolve, 1_000)).
  • Worker held alive during polling — True; since returnValue is a step, the step runtime holds the worker until the step resolves (or times out against the function's maxDuration).
  • Recursive/self-chaining pattern — Works because start() is fire-and-forget; parent completes after enqueueing the child.
  • deploymentId: "latest" — Supported (start.ts:158-165 resolves via world.resolveLatestDeploymentId).

Good calls

  • PR body's v5-only note + no-backport instruction is correct. The feature doesn't exist on stable (v4), so these docs would be wrong there. docs/content/ IS maintained on stable per AGENTS.md, so the backport action could pull this in if the backport-stable label is applied — but shouldn't be. Worth the explicit call-out in the PR body.
  • Polling caveat on returnValue is an important gotcha that users need to know. The suggested workaround (fire-and-forget + hooks for completion notification) is sound and matches the actual implementation of hooks.

Minor inaccuracy

See inline comment: "each property access or method call executes as a separate step" is slightly over-broad. .runId is a plain field (not a getter) and .readable is a non-'use step' getter. Only "most" properties are steps.

Changeset

The empty changeset is defensible here (docs-only change). Worth noting though: docs/content/docs/foundations/*.mdx is bundled into @workflow/core and workflow via prepack scripts, and docs/content/docs/api-reference/workflow-api/*.mdx is bundled into workflow. So the new docs won't reach npm consumers until the next release that bumps those packages for other reasons. If you want the docs to ship specifically with this PR, add workflow and @workflow/core as patch bumps. Otherwise the current empty changeset is fine — the new docs will ride along with the next beta release.

}
```

When `start()` is called inside a workflow function, it automatically executes through an internal step to maintain deterministic replay. The returned `Run` object works just like it does outside workflows — properties like `.runId`, `.status`, `.returnValue`, and methods like `.cancel()` are all available. Each property access or method call executes as a separate step under the hood.

@TooTallNateTooTallNateApr 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Minor: "Each property access or method call executes as a separate step under the hood" is slightly overbroad.

Verified against packages/core/src/runtime/run.ts:

MemberStep?
runIdNo (plain field, line 87)
readableNo (plain getter, line 244)
statusYes (line 184)
returnValueYes (line 195)
cancel()Yes (line 154)
wakeUp()Yes (line 146)
existsYes (line 166)
workflowNameYes (line 203)
createdAt / startedAt / completedAtYes
getReadable()Yes (line 263)

runId being free is actually nice for users (e.g., logging run.runId inside a loop doesn't cost N steps). Consider rewording to something like: "Most Run properties — .status, .returnValue, .cancel(), etc. — execute as separate steps. Plain fields like .runId are free."

Resolves conflicts from the versioned-docs restructure (v4/ + v5/) and the
cookbook split that landed after this branch was opened.
Resolutions:
- docs/content/docs/foundations/common-patterns.mdx (modify/delete): took
main's deletion. #1850 split that page into cookbook/common-patterns/*,
and v5/cookbook/common-patterns/workflow-composition.mdx already carries
this branch's Background Execution rewrite, including the same
`const reportRun = await start(...)` shape and a "start() can run inside
workflows" note.
- api-reference/workflow-api/start.mdx: git's rename heuristic had applied
this branch's edits to v4/, which contradicts the branch being v5-only.
Reverted v4/ to main and re-targeted v5/. Kept main's "Good to Know"
bullets (main already documents start()-in-workflow there, and links to
the cookbook and versioning pages) and kept the
start-invalid-workflow-function Callout that this branch would have
deleted. Carried over only the genuinely new "Inside a Workflow Function"
example.
- foundations/starting-workflows.mdx: auto-merged into v5/, which is the
right target. Dropped the now-stale "a future release will use internal
hooks" claim: the hook-based alternative ships today as the startAndWait()
recipe, and returnValue still polls at 1s (run.ts #pollReturnValue).
Replaced the duplicated deploymentId: "latest" self-chaining example with
a link to versioning.mdx#self-upgrading-workflows, which covers it in
full. Removed an unused `sleep` import.
Verified: pnpm test:docs typechecks all new samples, docs lint:links reports
no new errors (the 2 remaining are pre-existing on main in files this branch
does not touch).
@VaguelySerious
VaguelySerious merged commit 4a192c8 into mainAug 3, 2026
55 checks passed
@VaguelySerious
VaguelySerious deleted the pranaygp/codex/start-in-workflow-docs branch August 3, 2026 23:37
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 4a192c8 (AI decision).

This is documentation for a v5-only capability (calling start() inside workflow functions, background child runs, recursive self-chaining), i.e. documenting new functionality rather than correcting content already on stable — and the PR itself says not to port it to earlier versions. The two touched files (docs/content/docs/v5/api-reference/workflow-api/start.mdx and docs/content/docs/v5/foundations/starting-workflows.mdx) do not exist on origin/stable (verified with git ls-tree), and the only remaining changes are an empty changeset plus a one-line wording tweak, neither of which justifies a backport.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

4a192c85c8499f098f4c4ec495b2404abca97e5a

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.

4 participants

@pranaygp@TooTallNate@VaguelySerious