Skip to content

feat: allow start() to be called directly inside workflow functions#1491

Merged
TooTallNate merged 8 commits into
mainfrom
pgp/revert-revert-start
Apr 17, 2026
Merged

feat: allow start() to be called directly inside workflow functions#1491
TooTallNate merged 8 commits into
mainfrom
pgp/revert-revert-start

Conversation

@pranaygp
Copy link
Copy Markdown
Contributor

@pranaygp pranaygp commented Mar 23, 2026

Summary

Add "use step" to start() so it can be called directly from "use workflow" functions, without needing a wrapper step function.

This is a dramatically simplified replacement for the original PR approach. Instead of complex VM-side proxy classes and builtin step delegation, the entire implementation is:

  1. Add "use step" to start() in packages/core/src/runtime/start.ts
  2. Re-export start from @workflow/core/runtime/start in api-workflow.ts (instead of a throwing stub)

This works because:

Changes

  • packages/core/src/runtime/start.ts: Add "use step" directive
  • packages/workflow/src/api-workflow.ts: Re-export start from core instead of stub
  • workbench/example/workflows/99_e2e.ts: Add startFromWorkflow and fibonacciWorkflow e2e workflows
  • packages/core/e2e/e2e.test.ts: Add e2e tests
  • workbench/nextjs-turbopack/app/workflows/definitions.ts: Add default args

E2E Tests

  • startFromWorkflow: Parent spawns child via start(), child signals parent via hook, verifies bidirectional communication
  • fibonacciWorkflow: Recursive workflow composition — fib(6) = 8 via tree of child workflows using Promise.all([start(...), start(...)])

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Mar 23, 2026

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

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Apr 17, 2026 6:48am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Apr 17, 2026 6:48am
example-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-astro-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-express-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-fastify-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-hono-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-nitro-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-nuxt-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-sveltekit-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workbench-vite-workflow Ready Ready Preview, Comment Apr 17, 2026 6:48am
workflow-docs Ready Ready Preview, Comment, Open in v0 Apr 17, 2026 6:48am
workflow-swc-playground Ready Ready Preview, Comment Apr 17, 2026 6:48am
workflow-web Ready Ready Preview, Comment Apr 17, 2026 6:48am

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 23, 2026

🦋 Changeset detected

Latest commit: 5826c86

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

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

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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 23, 2026

📊 Benchmark Results

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

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 0.042s (-2.3%) 1.005s (~) 0.963s 10 1.00x
💻 Local Express 0.043s (-3.6%) 1.005s (~) 0.963s 10 1.01x
💻 Local Next.js (Turbopack) 0.050s 1.005s 0.955s 10 1.18x
🐘 Postgres Next.js (Turbopack) 0.057s 1.009s 0.953s 10 1.35x
🐘 Postgres Express 0.060s (+3.8%) 1.010s (~) 0.950s 10 1.43x
🐘 Postgres Nitro 0.087s (-9.0% 🟢) 1.029s (-1.3%) 0.942s 10 2.06x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.255s (+1.5%) 1.912s (-18.0% 🟢) 1.657s 10 1.00x
▲ Vercel Nitro 0.413s (+0.9%) 2.209s (-12.0% 🟢) 1.795s 10 1.62x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 1.116s 2.006s 0.890s 10 1.00x
💻 Local Express 1.128s (~) 2.006s (~) 0.878s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.136s 2.010s 0.874s 10 1.02x
💻 Local Nitro 1.137s (~) 2.006s (~) 0.870s 10 1.02x
🐘 Postgres Express 1.145s (~) 2.009s (~) 0.864s 10 1.03x
🐘 Postgres Nitro 1.150s (+0.9%) 2.009s (~) 0.859s 10 1.03x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.841s (-52.7% 🟢) 3.610s (-38.9% 🟢) 1.769s 10 1.00x
▲ Vercel Next.js (Turbopack) 1.941s (-4.6%) 3.671s (-4.2%) 1.731s 10 1.05x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 10.791s 11.023s 0.232s 3 1.00x
🐘 Postgres Nitro 10.846s (~) 11.019s (~) 0.173s 3 1.01x
🐘 Postgres Express 10.866s (-0.9%) 11.018s (~) 0.151s 3 1.01x
🐘 Postgres Next.js (Turbopack) 10.897s 11.024s 0.127s 3 1.01x
💻 Local Express 10.935s (~) 11.024s (~) 0.088s 3 1.01x
💻 Local Nitro 10.956s (~) 11.024s (~) 0.068s 3 1.02x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 16.899s (-28.8% 🟢) 18.812s (-25.1% 🟢) 1.912s 2 1.00x
▲ Vercel Next.js (Turbopack) 17.099s (-1.3%) 18.986s (-2.1%) 1.887s 2 1.01x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 14.427s 15.022s 0.595s 4 1.00x
🐘 Postgres Nitro 14.487s (-0.7%) 15.027s (~) 0.540s 4 1.00x
🐘 Postgres Express 14.497s (-0.6%) 15.021s (~) 0.524s 4 1.00x
💻 Local Next.js (Turbopack) 14.599s 15.030s 0.431s 4 1.01x
💻 Local Express 14.977s (~) 15.280s (+1.7%) 0.303s 4 1.04x
💻 Local Nitro 15.014s (~) 15.531s (-3.1%) 0.516s 4 1.04x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 31.103s (-51.7% 🟢) 33.088s (-50.3% 🟢) 1.985s 2 1.00x
▲ Vercel Next.js (Turbopack) 34.283s (-34.8% 🟢) 36.218s (-33.7% 🟢) 1.935s 2 1.10x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 13.777s (-1.6%) 14.021s (-3.9%) 0.244s 7 1.00x
🐘 Postgres Nitro 13.849s (-0.8%) 14.023s (-2.0%) 0.174s 7 1.01x
🐘 Postgres Next.js (Turbopack) 13.905s 14.024s 0.119s 7 1.01x
💻 Local Next.js (Turbopack) 15.836s 16.029s 0.193s 6 1.15x
💻 Local Express 16.630s (~) 17.030s (~) 0.400s 6 1.21x
💻 Local Nitro 16.773s (~) 17.031s (~) 0.258s 6 1.22x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 54.152s (-86.2% 🟢) 55.639s (-85.9% 🟢) 1.487s 2 1.00x
▲ Vercel Nitro 56.014s (-86.8% 🟢) 57.859s (-86.4% 🟢) 1.845s 2 1.03x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.237s 2.009s 0.772s 15 1.00x
🐘 Postgres Nitro 1.259s (-1.2%) 2.011s (~) 0.752s 15 1.02x
🐘 Postgres Express 1.260s (~) 2.008s (~) 0.748s 15 1.02x
💻 Local Next.js (Turbopack) 1.467s 2.005s 0.538s 15 1.19x
💻 Local Express 1.539s (+3.4%) 2.006s (~) 0.467s 15 1.24x
💻 Local Nitro 1.584s (-2.9%) 2.006s (-3.3%) 0.422s 15 1.28x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.295s (-18.6% 🟢) 3.768s (-12.8% 🟢) 1.473s 8 1.00x
▲ Vercel Next.js (Turbopack) 2.682s (-21.1% 🟢) 4.363s (-11.5% 🟢) 1.681s 7 1.17x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.323s (-1.2%) 3.010s (~) 0.687s 10 1.00x
🐘 Postgres Express 2.371s (~) 3.008s (~) 0.637s 10 1.02x
🐘 Postgres Next.js (Turbopack) 2.411s 3.009s 0.598s 10 1.04x
💻 Local Next.js (Turbopack) 2.864s 3.208s 0.344s 10 1.23x
💻 Local Express 3.041s (+3.0%) 3.885s (+12.5% 🔺) 0.844s 8 1.31x
💻 Local Nitro 3.188s (+1.4%) 3.886s (~) 0.697s 8 1.37x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.493s (-38.5% 🟢) 4.128s (-30.3% 🟢) 1.635s 8 1.00x
▲ Vercel Next.js (Turbopack) 3.312s (-53.3% 🟢) 4.688s (-47.4% 🟢) 1.375s 7 1.33x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.479s (~) 4.010s (~) 0.531s 8 1.00x
🐘 Postgres Express 3.486s (~) 4.011s (~) 0.525s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.646s 4.011s 0.365s 8 1.05x
💻 Local Next.js (Turbopack) 7.295s 8.015s 0.720s 4 2.10x
💻 Local Express 8.300s (~) 9.021s (~) 0.721s 4 2.39x
💻 Local Nitro 9.008s (+7.9% 🔺) 9.520s (+5.5% 🔺) 0.512s 4 2.59x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.317s (-5.9% 🟢) 5.227s (-5.5% 🟢) 1.910s 6 1.00x
▲ Vercel Next.js (Turbopack) 3.665s (-58.9% 🟢) 5.170s (-52.8% 🟢) 1.505s 6 1.10x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.229s 2.008s 0.780s 15 1.00x
🐘 Postgres Nitro 1.253s (~) 2.008s (~) 0.755s 15 1.02x
🐘 Postgres Express 1.265s (+0.6%) 2.007s (~) 0.742s 15 1.03x
💻 Local Next.js (Turbopack) 1.480s 2.006s 0.526s 15 1.20x
💻 Local Express 1.537s (-18.8% 🟢) 2.006s (-15.1% 🟢) 0.469s 15 1.25x
💻 Local Nitro 1.581s (-15.3% 🟢) 2.007s (-14.3% 🟢) 0.426s 15 1.29x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.065s (-16.0% 🟢) 3.741s (-10.3% 🟢) 1.676s 9 1.00x
▲ Vercel Next.js (Turbopack) 2.073s (-29.3% 🟢) 3.679s (-20.7% 🟢) 1.606s 9 1.00x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.328s (-0.5%) 3.011s (~) 0.683s 10 1.00x
🐘 Postgres Express 2.371s (+1.3%) 3.009s (~) 0.638s 10 1.02x
🐘 Postgres Next.js (Turbopack) 2.395s 3.008s 0.613s 10 1.03x
💻 Local Next.js (Turbopack) 2.899s 3.341s 0.443s 9 1.25x
💻 Local Express 3.031s (-3.2%) 3.883s (+3.2%) 0.852s 8 1.30x
💻 Local Nitro 3.064s (~) 3.885s (~) 0.821s 8 1.32x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.511s (-22.3% 🟢) 3.996s (-21.3% 🟢) 1.485s 8 1.00x
▲ Vercel Next.js (Turbopack) 2.943s (-6.4% 🟢) 4.388s (-3.0%) 1.445s 8 1.17x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.478s (-0.6%) 4.011s (~) 0.533s 8 1.00x
🐘 Postgres Nitro 3.481s (~) 4.011s (~) 0.530s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.657s 4.012s 0.355s 8 1.05x
💻 Local Next.js (Turbopack) 8.041s 8.767s 0.726s 4 2.31x
💻 Local Express 8.919s (+1.3%) 9.271s (~) 0.352s 4 2.56x
💻 Local Nitro 9.163s (~) 10.022s (~) 0.859s 3 2.63x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.059s (-39.9% 🟢) 4.889s (-28.3% 🟢) 1.830s 7 1.00x
▲ Vercel Next.js (Turbopack) 3.409s (-49.6% 🟢) 5.067s (-40.7% 🟢) 1.659s 6 1.11x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.779s 1.023s 0.244s 59 1.00x
🐘 Postgres Express 0.799s (-4.7%) 1.006s (-1.7%) 0.207s 60 1.03x
💻 Local Next.js (Turbopack) 0.829s 1.004s 0.176s 60 1.06x
🐘 Postgres Nitro 0.834s (+1.6%) 1.006s (~) 0.172s 60 1.07x
💻 Local Express 0.999s (+1.5%) 1.369s (+27.2% 🔺) 0.370s 44 1.28x
💻 Local Nitro 1.016s (+3.6%) 1.611s (+47.3% 🔺) 0.595s 38 1.30x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 9.843s (-55.4% 🟢) 12.413s (-48.3% 🟢) 2.570s 5 1.00x
▲ Vercel Next.js (Turbopack) 12.026s (-17.1% 🟢) 13.929s (-13.4% 🟢) 1.903s 5 1.22x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.884s (-4.7%) 2.075s (-8.1% 🟢) 0.192s 44 1.00x
🐘 Postgres Next.js (Turbopack) 1.940s 2.202s 0.263s 41 1.03x
🐘 Postgres Nitro 2.009s (+4.2%) 2.580s (+22.8% 🔺) 0.571s 35 1.07x
💻 Local Next.js (Turbopack) 2.607s 3.008s 0.401s 30 1.38x
💻 Local Nitro 3.062s (+0.9%) 3.842s (+2.2%) 0.780s 24 1.63x
💻 Local Express 3.083s (+2.2%) 3.843s (+7.2% 🔺) 0.760s 24 1.64x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 29.627s (-25.0% 🟢) 31.818s (-23.0% 🟢) 2.191s 3 1.00x
▲ Vercel Next.js (Turbopack) 30.003s (-39.8% 🟢) 31.773s (-38.6% 🟢) 1.770s 3 1.01x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.838s (-3.8%) 4.042s (-7.5% 🟢) 0.204s 30 1.00x
🐘 Postgres Next.js (Turbopack) 3.845s 4.043s 0.198s 30 1.00x
🐘 Postgres Nitro 4.114s (~) 4.742s (+3.0%) 0.628s 26 1.07x
💻 Local Next.js (Turbopack) 8.439s 9.017s 0.578s 14 2.20x
💻 Local Express 9.229s (~) 9.941s (-0.8%) 0.712s 13 2.40x
💻 Local Nitro 9.362s (+0.7%) 10.103s (+0.8%) 0.741s 12 2.44x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 79.683s (-17.8% 🟢) 81.801s (-16.9% 🟢) 2.118s 2 1.00x
▲ Vercel Next.js (Turbopack) 83.234s (-22.3% 🟢) 84.822s (-22.1% 🟢) 1.588s 2 1.04x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.254s 1.007s 0.753s 60 1.00x
🐘 Postgres Express 0.286s (+1.3%) 1.007s (~) 0.720s 60 1.13x
🐘 Postgres Nitro 0.292s (+3.2%) 1.008s (~) 0.715s 60 1.15x
💻 Local Express 0.591s (+5.5% 🔺) 1.005s (~) 0.413s 60 2.33x
💻 Local Nitro 0.604s (~) 1.005s (-1.7%) 0.401s 60 2.38x
💻 Local Next.js (Turbopack) 0.636s 1.096s 0.460s 55 2.50x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.935s (-4.3%) 3.931s (+3.6%) 1.996s 16 1.00x
▲ Vercel Nitro 2.045s (+23.1% 🔺) 3.924s (+17.1% 🔺) 1.879s 16 1.06x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.475s 1.006s 0.531s 90 1.00x
🐘 Postgres Express 0.488s (-4.3%) 1.006s (~) 0.518s 90 1.03x
🐘 Postgres Nitro 0.498s (~) 1.007s (~) 0.508s 90 1.05x
💻 Local Next.js (Turbopack) 2.523s 3.009s 0.485s 30 5.31x
💻 Local Express 2.549s (+1.4%) 3.010s (~) 0.461s 30 5.36x
💻 Local Nitro 2.595s (+2.2%) 3.010s (~) 0.415s 30 5.46x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.216s (~) 5.023s (+4.2%) 1.806s 18 1.00x
▲ Vercel Next.js (Turbopack) 3.287s (-7.0% 🟢) 5.034s (-3.1%) 1.746s 18 1.02x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.774s (-5.4% 🟢) 1.007s (-1.0%) 0.233s 120 1.00x
🐘 Postgres Next.js (Turbopack) 0.777s 1.006s 0.230s 120 1.00x
🐘 Postgres Nitro 0.794s (~) 1.008s (~) 0.214s 120 1.03x
💻 Local Next.js (Turbopack) 10.324s 10.943s 0.619s 12 13.34x
💻 Local Nitro 11.556s (+3.3%) 12.130s (+4.0%) 0.574s 10 14.93x
💻 Local Express 11.688s (+4.4%) 12.329s (+3.3%) 0.641s 10 15.10x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 6.003s (-22.3% 🟢) 7.762s (-17.4% 🟢) 1.759s 16 1.00x
▲ Vercel Next.js (Turbopack) 13.494s (+30.7% 🔺) 15.200s (+23.7% 🔺) 1.707s 11 2.25x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 0.179s 1.003s 0.012s 1.018s 0.839s 10 1.00x
💻 Local Express 0.203s (+1.8%) 1.004s (~) 0.012s (-3.3%) 1.018s (~) 0.815s 10 1.13x
🐘 Postgres Express 0.203s (-0.9%) 0.992s (-0.7%) 0.002s (~) 1.010s (~) 0.806s 10 1.14x
💻 Local Nitro 0.204s (-4.5%) 1.004s (~) 0.012s (-4.8%) 1.018s (~) 0.814s 10 1.14x
🐘 Postgres Next.js (Turbopack) 0.207s 1.001s 0.002s 1.011s 0.804s 10 1.16x
🐘 Postgres Nitro 0.210s (+2.5%) 0.995s (~) 0.001s (-20.0% 🟢) 1.009s (~) 0.799s 10 1.18x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.595s (-76.7% 🟢) 3.061s (-64.6% 🟢) 0.709s (+12.1% 🔺) 4.212s (-57.0% 🟢) 2.617s 10 1.00x
▲ Vercel Nitro 1.672s (-56.4% 🟢) 3.060s (-42.0% 🟢) 0.780s (+5.1% 🔺) 4.359s (-32.8% 🟢) 2.687s 10 1.05x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.605s (-3.9%) 1.005s (~) 0.004s (+2.2%) 1.022s (~) 0.417s 59 1.00x
🐘 Postgres Next.js (Turbopack) 0.607s 1.009s 0.006s 1.024s 0.417s 59 1.00x
🐘 Postgres Nitro 0.627s (~) 1.003s (~) 0.004s (-4.5%) 1.022s (~) 0.395s 59 1.04x
💻 Local Next.js (Turbopack) 0.756s 1.011s 0.020s 1.126s 0.370s 54 1.25x
💻 Local Express 0.794s (+4.8%) 1.012s (-1.6%) 0.015s (+54.8% 🔺) 1.029s (-1.1%) 0.235s 59 1.31x
💻 Local Nitro 0.968s (+15.5% 🔺) 1.012s (~) 0.010s (+6.7% 🔺) 1.228s (+10.0% 🔺) 0.260s 49 1.60x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.375s (-74.1% 🟢) 6.053s (-66.8% 🟢) 0.188s (-11.1% 🟢) 6.702s (-64.6% 🟢) 2.327s 9 1.00x
▲ Vercel Nitro 4.545s (-84.6% 🟢) 6.277s (-79.6% 🟢) 0.225s (+101.1% 🔺) 6.958s (-78.1% 🟢) 2.413s 9 1.04x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.937s 1.203s 0.000s 1.216s 0.279s 50 1.00x
🐘 Postgres Nitro 0.948s (-2.1%) 1.167s (-6.4% 🟢) 0.000s (-7.7% 🟢) 1.181s (-6.1% 🟢) 0.233s 52 1.01x
🐘 Postgres Express 0.953s (-0.9%) 1.269s (-0.7%) 0.000s (-51.1% 🟢) 1.294s (-0.9%) 0.341s 47 1.02x
💻 Local Express 1.252s (+2.3%) 2.024s (~) 0.000s (~) 2.026s (~) 0.773s 30 1.34x
💻 Local Next.js (Turbopack) 1.281s 2.021s 0.001s 2.024s 0.742s 30 1.37x
💻 Local Nitro 1.420s (+16.1% 🔺) 2.021s (~) 0.000s (+221.4% 🔺) 2.202s (+8.9% 🔺) 0.782s 28 1.52x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.005s (-1.5%) 4.107s (-6.5% 🟢) 0.000s (-100.0% 🟢) 4.696s (-2.4%) 1.691s 13 1.00x
▲ Vercel Next.js (Turbopack) 3.462s (-66.0% 🟢) 5.066s (-56.0% 🟢) 0.000s (NaN%) 5.508s (-54.3% 🟢) 2.045s 11 1.15x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

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

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.669s (-5.8% 🟢) 2.058s (-5.5% 🟢) 0.000s (+Infinity% 🔺) 2.082s (-5.3% 🟢) 0.413s 29 1.00x
🐘 Postgres Next.js (Turbopack) 1.790s 2.072s 0.000s 2.083s 0.293s 29 1.07x
🐘 Postgres Nitro 1.825s (+1.9%) 2.258s (+5.4% 🔺) 0.000s (+107.4% 🔺) 2.269s (+4.3%) 0.444s 27 1.09x
💻 Local Express 3.530s (+1.8%) 4.034s (~) 0.000s (-41.7% 🟢) 4.038s (~) 0.508s 15 2.12x
💻 Local Nitro 3.649s (+7.7% 🔺) 4.099s (+1.6%) 0.001s (+62.5% 🔺) 4.102s (+1.6%) 0.453s 15 2.19x
💻 Local Next.js (Turbopack) 3.847s 4.389s 0.000s 4.395s 0.547s 14 2.31x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.955s (-3.4%) 5.118s (-4.8%) 0.000s (-100.0% 🟢) 5.592s (-3.5%) 1.637s 11 1.00x
▲ Vercel Next.js (Turbopack) 4.537s (-19.2% 🟢) 5.937s (-15.0% 🟢) 0.000s (-100.0% 🟢) 6.464s (-14.3% 🟢) 1.927s 10 1.15x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Next.js (Turbopack) 17/21
🐘 Postgres Next.js (Turbopack) 9/21
▲ Vercel Nitro 16/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 18/21
Next.js (Turbopack) 🐘 Postgres 16/21
Nitro 🐘 Postgres 16/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
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 23, 2026

🧪 E2E Test Results

All tests passed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 945 0 67 1012
✅ 💻 Local Development 1018 0 86 1104
✅ 📦 Local Production 1018 0 86 1104
✅ 🐘 Local Postgres 1018 0 86 1104
✅ 🪟 Windows 92 0 0 92
✅ 📋 Other 258 0 18 276
Total 4349 0 343 4692

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 85 0 7
✅ example 85 0 7
✅ express 85 0 7
✅ fastify 85 0 7
✅ hono 85 0 7
✅ nextjs-turbopack 90 0 2
✅ nextjs-webpack 90 0 2
✅ nitro 85 0 7
✅ nuxt 85 0 7
✅ sveltekit 85 0 7
✅ vite 85 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 86 0 6
✅ express-stable 86 0 6
✅ fastify-stable 86 0 6
✅ hono-stable 86 0 6
✅ nextjs-turbopack-canary 73 0 19
✅ nextjs-turbopack-stable 92 0 0
✅ nextjs-webpack-canary 73 0 19
✅ nextjs-webpack-stable 92 0 0
✅ nitro-stable 86 0 6
✅ nuxt-stable 86 0 6
✅ sveltekit-stable 86 0 6
✅ vite-stable 86 0 6
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 86 0 6
✅ express-stable 86 0 6
✅ fastify-stable 86 0 6
✅ hono-stable 86 0 6
✅ nextjs-turbopack-canary 73 0 19
✅ nextjs-turbopack-stable 92 0 0
✅ nextjs-webpack-canary 73 0 19
✅ nextjs-webpack-stable 92 0 0
✅ nitro-stable 86 0 6
✅ nuxt-stable 86 0 6
✅ sveltekit-stable 86 0 6
✅ vite-stable 86 0 6
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 86 0 6
✅ express-stable 86 0 6
✅ fastify-stable 86 0 6
✅ hono-stable 86 0 6
✅ nextjs-turbopack-canary 73 0 19
✅ nextjs-turbopack-stable 92 0 0
✅ nextjs-webpack-canary 73 0 19
✅ nextjs-webpack-stable 92 0 0
✅ nitro-stable 86 0 6
✅ nuxt-stable 86 0 6
✅ sveltekit-stable 86 0 6
✅ vite-stable 86 0 6
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 92 0 0
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 86 0 6
✅ e2e-local-postgres-nest-stable 86 0 6
✅ e2e-local-prod-nest-stable 86 0 6

📋 View full workflow run

@pranaygp pranaygp changed the title Revert "Revert \"Add support for calling start() inside workflow functions\"" Revert Revert "Add support for calling start() inside workflow functions" Mar 23, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Re-enables calling start() from workflow/api directly inside "use workflow" functions by routing the call through internal built-in steps, with Run objects working in workflow VM context (step-backed getters/methods) and improved observability UI hydration/rendering for run references.

Changes:

  • Add workflow-context start() delegation via WORKFLOW_START, backed by a built-in start step and a workflow-VM WorkflowRun proxy class.
  • Extend serialization/hydration to support Run/WorkflowRun across boundaries and render Run references as clickable UI elements.
  • Update builders, plugin behavior/docs, and add unit + e2e coverage for startFromWorkflow and recursive fibonacciWorkflow.

Reviewed changes

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

Show a summary per file
File Description
workbench/nextjs-turbopack/app/workflows/definitions.ts Adds default args for new e2e workflows.
workbench/example/workflows/99_e2e.ts Adds startFromWorkflow and recursive fibonacciWorkflow workflows for e2e.
skills/workflow/SKILL.md Updates skill guidance to reflect start() support inside workflows.
packages/workflow/src/runtime.ts Re-exports getRun and start from core runtime for rewritten step imports.
packages/workflow/src/internal/builtins.ts Adds built-in start step and Run.* step-backed static methods.
packages/workflow/src/api-workflow.ts Routes start() in workflow bundle via WORKFLOW_START injection.
packages/web/app/components/run-detail-view.tsx Adds run-ref click navigation wiring.
packages/web-shared/src/lib/hydration.ts Exposes RunRef helpers for UI hydration.
packages/web-shared/src/components/workflow-traces/trace-span-construction.ts Improves span naming fallback behavior.
packages/web-shared/src/components/workflow-trace-view.tsx Plumbs onRunClick and resets selection on run change.
packages/web-shared/src/components/ui/data-inspector.tsx Adds RunRef rendering + “opaque ref” collapsing + click contexts.
packages/web-shared/src/components/sidebar/entity-detail-panel.tsx Passes onRunClick into detail panel rendering.
packages/web-shared/src/components/sidebar/attribute-panel.tsx Adds Run click context and improves stepName display fallback.
packages/web-shared/src/components/run-trace-view.tsx Threads onRunClick through trace view components.
packages/swc-plugin-workflow/transform/src/lib.rs Removes __builtin* step-id special casing to use normal IDs.
packages/swc-plugin-workflow/spec.md Updates spec examples to include builtins module-specifier IDs.
packages/next/src/builder-deferred.ts Rewrites bare specifiers (incl. dynamic import) and ensures builtins are included in step files.
packages/core/src/workflow/start.ts Implements workflow-VM createStart() using internal start built-in step.
packages/core/src/workflow/start.test.ts Adds unit tests for workflow-context start() behavior and option validation.
packages/core/src/workflow/run.ts Adds workflow-VM WorkflowRun proxy delegating to built-in Run steps.
packages/core/src/workflow/run.test.ts Adds unit tests asserting step delegation naming/arguments.
packages/core/src/workflow/builtin-step-id.ts Adds helper to construct built-in step IDs.
packages/core/src/workflow.ts Injects WORKFLOW_START, registers WorkflowRun, and updates response builtins to qualified IDs.
packages/core/src/symbols.ts Adds WORKFLOW_START symbol.
packages/core/src/serialization.ts Adds Run reducer/reviver using __serializable marker + class registry.
packages/core/src/serialization-format.ts Adds RunRef marker/type + reviver mapping Run → RunRef for o11y.
packages/core/src/runtime/step-handler.ts Registers runtime Run class in host class registry for Run reviver.
packages/core/src/runtime/start.ts Delegates start() to injected workflow implementation when in workflow VM.
packages/core/src/runtime/run.ts Adds Run.__serializable marker for serialization.
packages/core/src/private.ts Extends builtin step aliasing to accept fully-qualified builtins step IDs.
packages/core/e2e/e2e.test.ts Adds e2e tests validating workflow-context start() and recursion.
docs/proxy.ts Formatting-only string quote normalization.
docs/lib/ai-agent-detection.ts Formatting-only string quote normalization.
docs/content/docs/foundations/starting-workflows.mdx Documents calling start() inside workflow functions and recursive patterns.
docs/content/docs/foundations/common-patterns.mdx Updates patterns docs to use direct start() in workflows.
docs/content/docs/api-reference/workflow-api/start.mdx Updates API reference to include workflow-context usage.
AGENTS.md Updates local e2e instructions to include WORKFLOW_PUBLIC_MANIFEST=1.
.changeset/start-in-workflow.md Declares patch release for workflow-context start() support.

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

Comment thread packages/workflow/src/api-workflow.ts Outdated
Comment thread packages/core/src/workflow/run.ts Outdated
Comment thread packages/swc-plugin-workflow/spec.md Outdated
Comment thread packages/core/src/serialization-format.ts
Comment thread packages/web-shared/src/components/ui/data-inspector.tsx
Comment thread packages/next/src/builder-deferred.ts Outdated
Add 'use step' to start() so it can be called directly from workflow
code. The SWC compiler strips the function body in workflow mode and
replaces it with a step proxy. When called from a workflow:

1. The workflow function reference is serialized via WorkflowFunction
   reducer (serializes { workflowId })
2. start() executes in the step context with full Node.js access
3. The returned Run is serialized via WORKFLOW_SERIALIZE and deserialized
   back in the workflow VM
4. Run getters (.status, .returnValue, etc.) are 'use step' getters
   that each execute as separate steps

Also re-exports start from @workflow/core/runtime/start in api-workflow.ts
instead of using a throwing stub, adds e2e tests for startFromWorkflow
(with hook communication) and fibonacciWorkflow (recursive composition).
… duplicate classes

Files belonging to packages (detected by walking up to find a
package.json with a name field) are imported via relative path
instead of being copied to __workflow_step_files__/. Copying creates
a second module instance which breaks JS native private field (#)
brand checks when the runtime creates instances from one copy and
the step handler accesses fields from the other.
…t all package step files

Regular package step files (like fetch) must still be copied to ensure
the SWC loader registers them. Only serde class files from packages are
excluded from copying since those define classes with JS native private
fields (#) that break when duplicated.
…d of full copies

For package files that define serde classes (like Run), generate a thin
wrapper that imports the original class and registers steps/classes from
the manifest. This avoids duplicating the class definition (which breaks
JS native private field brand checks) while still registering all step
functions and the class in the serialization registry.

Regular package step files (like fetch) are still copied as before.
… step mode

Instead of copying package serde+step files (which creates duplicate
classes with #private brand check issues) or generating fragile wrappers,
add the original file paths to a shared forceStepModeFiles set. The
loader checks this set and transforms those files in step mode directly,
so the SWC plugin generates proper step registrations on the original
class — no duplication, no reimplemented registration logic.
…just copies

The loader now selects step mode for any file that has 'use step'
directives or serde patterns, regardless of whether it's a deferred
step copy. Step mode is a superset of client mode — the only addition
is step registry IIFEs, which are harmless for non-step consumers.

This means package serde+step files (like Run) no longer need to be
copied to get step registrations. They're imported directly in the
step route and the loader transforms the original file in step mode.
One class instance, no duplication, no wrapper generation.
@VaguelySerious
Copy link
Copy Markdown
Member

Fails nextjs-turbopack tests

argsOrOptions?: TArgs | StartOptions,
options?: StartOptions
) {
'use step';
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.

Nice

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants