Skip to content

fix: serialize Run across runtime and workflow VM#1616

Merged
TooTallNate merged 5 commits into
mainfrom
codex/run-class-serialization-e2e
Apr 9, 2026
Merged

fix: serialize Run across runtime and workflow VM#1616
TooTallNate merged 5 commits into
mainfrom
codex/run-class-serialization-e2e

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

Summary

  • switch Run in core runtime to custom class serialization (WORKFLOW_SERIALIZE / WORKFLOW_DESERIALIZE) and register it in the class registry
  • make workflow-VM Run (workflow/api) deserializable and registry-registered so Run instances can cross step/workflow boundaries without bespoke reducer paths
  • add e2e coverage for Run boundary roundtrips in workbench/example/workflows/99_e2e.ts and packages/core/e2e/e2e.test.ts, plus runtime unit tests for class serde behavior
  • add a changeset for @workflow/core and workflow

Testing

  • pnpm --filter @workflow/core typecheck
  • pnpm --filter workflow typecheck
  • pnpm vitest run src/runtime/runs.test.ts -t "Run custom serialization" (from packages/core)

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 6, 2026

🦋 Changeset detected

Latest commit: 407a19f

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

This PR includes changesets to release 16 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/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

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 6, 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 9, 2026 9:24pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Apr 9, 2026 9:24pm
example-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-astro-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-express-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-fastify-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-hono-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-nitro-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-nuxt-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workbench-vite-workflow Ready Ready Preview, Comment Apr 9, 2026 9:24pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Apr 9, 2026 9:24pm
workflow-swc-playground Ready Ready Preview, Comment Apr 9, 2026 9:24pm

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 6, 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 🥇 Express 0.043s (-0.7%) 1.005s (~) 0.963s 10 1.00x
💻 Local Nitro 0.044s (+17.5% 🔺) 1.005s (~) 0.961s 10 1.03x
🐘 Postgres Next.js (Turbopack) 0.056s 1.010s 0.955s 10 1.32x
🐘 Postgres Express 0.060s (-10.1% 🟢) 1.011s (~) 0.951s 10 1.40x
🐘 Postgres Nitro 0.061s (-2.7%) 1.009s (~) 0.948s 10 1.44x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.207s (-26.7% 🟢) 2.309s (-7.0% 🟢) 2.102s 10 1.00x
▲ Vercel Next.js (Turbopack) 0.236s (-46.9% 🟢) 2.099s (-5.5% 🟢) 1.863s 10 1.14x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.127s (+2.2%) 2.006s (~) 0.880s 10 1.00x
💻 Local Express 1.130s (~) 2.005s (~) 0.875s 10 1.00x
🐘 Postgres Express 1.142s (~) 2.011s (~) 0.869s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.142s 2.011s 0.869s 10 1.01x
🐘 Postgres Nitro 1.150s (~) 2.010s (~) 0.861s 10 1.02x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.885s (-10.1% 🟢) 3.489s (-9.1% 🟢) 1.604s 10 1.00x
▲ Vercel Next.js (Turbopack) 1.916s (-7.5% 🟢) 3.517s (-4.8%) 1.600s 10 1.02x
▲ 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 🥇 Express 10.896s (~) 11.022s (~) 0.126s 3 1.00x
🐘 Postgres Nitro 10.905s (~) 11.018s (~) 0.113s 3 1.00x
🐘 Postgres Next.js (Turbopack) 10.907s 11.019s 0.112s 3 1.00x
💻 Local Nitro 10.909s (+2.2%) 11.024s (~) 0.115s 3 1.00x
🐘 Postgres Express 10.941s (+0.7%) 11.024s (~) 0.083s 3 1.00x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 16.877s (~) 18.761s (-1.2%) 1.885s 2 1.00x
▲ Vercel Next.js (Turbopack) 17.610s (-0.9%) 19.170s (+1.1%) 1.560s 2 1.04x
▲ 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 🥇 Nitro 14.482s (~) 15.024s (~) 0.541s 4 1.00x
🐘 Postgres Next.js (Turbopack) 14.511s 15.026s 0.515s 4 1.00x
🐘 Postgres Express 14.589s (~) 15.024s (~) 0.435s 4 1.01x
💻 Local Express 14.890s (-1.1%) 15.029s (-6.2% 🟢) 0.139s 4 1.03x
💻 Local Nitro 15.019s (+5.6% 🔺) 15.781s (+5.0% 🔺) 0.762s 4 1.04x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 30.535s (-2.8%) 32.926s (-2.1%) 2.392s 2 1.00x
▲ Vercel Next.js (Turbopack) 35.051s (+3.3%) 37.323s (+3.8%) 2.272s 2 1.15x
▲ 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 🥇 Next.js (Turbopack) 13.862s 14.164s 0.302s 7 1.00x
🐘 Postgres Nitro 13.905s (~) 14.161s (+1.0%) 0.256s 7 1.00x
🐘 Postgres Express 14.308s (+1.6%) 15.021s (+1.9%) 0.713s 6 1.03x
💻 Local Express 16.460s (-2.8%) 17.030s (~) 0.569s 6 1.19x
💻 Local Nitro 16.904s (+13.3% 🔺) 17.031s (+13.3% 🔺) 0.127s 6 1.22x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 206.024s (+282.0% 🔺) 208.069s (+272.0% 🔺) 2.045s 2 1.00x
▲ Vercel Next.js (Turbopack) 208.680s (+281.3% 🔺) 210.330s (+268.7% 🔺) 1.650s 2 1.01x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.232s 2.010s 0.778s 15 1.00x
🐘 Postgres Nitro 1.276s (+0.8%) 2.011s (~) 0.735s 15 1.04x
🐘 Postgres Express 1.283s (+0.8%) 2.010s (~) 0.727s 15 1.04x
💻 Local Express 1.527s (~) 2.006s (~) 0.479s 15 1.24x
💻 Local Nitro 1.530s (+2.3%) 2.006s (~) 0.476s 15 1.24x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.035s (-18.4% 🟢) 3.514s (-10.8% 🟢) 1.479s 9 1.00x
▲ Vercel Next.js (Turbopack) 2.512s (+5.8% 🔺) 3.966s (+2.5%) 1.453s 8 1.23x
▲ 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.347s (-0.5%) 3.007s (~) 0.661s 10 1.00x
🐘 Postgres Express 2.369s (+1.7%) 3.009s (~) 0.640s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.421s 3.010s 0.589s 10 1.03x
💻 Local Express 2.861s (-10.1% 🟢) 3.008s (-25.0% 🟢) 0.147s 10 1.22x
💻 Local Nitro 3.054s (+11.9% 🔺) 3.884s (+21.1% 🔺) 0.830s 8 1.30x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.156s (+2.8%) 5.019s (+11.4% 🔺) 1.863s 6 1.00x
▲ Vercel Next.js (Turbopack) 6.418s (+112.3% 🔺) 8.231s (+86.8% 🔺) 1.813s 5 2.03x
▲ 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 🥇 Express 3.471s (-0.9%) 4.008s (~) 0.537s 8 1.00x
🐘 Postgres Nitro 3.473s (-0.9%) 4.012s (~) 0.539s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.903s 4.263s 0.360s 8 1.12x
💻 Local Express 7.878s (-5.6% 🟢) 8.520s (-5.6% 🟢) 0.642s 4 2.27x
💻 Local Nitro 8.893s (+14.8% 🔺) 9.277s (+8.9% 🔺) 0.384s 4 2.56x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.211s (+5.7% 🔺) 4.847s (+5.2% 🔺) 1.636s 7 1.00x
▲ Vercel Nitro 5.803s (+93.1% 🔺) 7.681s (+64.5% 🔺) 1.878s 4 1.81x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 1.247s 2.008s 0.761s 15 1.00x
🐘 Postgres Express 1.254s (-3.2%) 2.009s (~) 0.755s 15 1.01x
🐘 Postgres Nitro 1.260s (~) 2.008s (~) 0.748s 15 1.01x
💻 Local Express 1.544s (-1.3%) 2.005s (~) 0.462s 15 1.24x
💻 Local Nitro 1.555s (+3.3%) 2.006s (~) 0.451s 15 1.25x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.051s (-5.4% 🟢) 3.746s (+4.6%) 1.696s 9 1.00x
▲ Vercel Nitro 2.245s (+14.9% 🔺) 3.869s (+1.5%) 1.623s 8 1.10x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.336s (-1.8%) 3.010s (~) 0.674s 10 1.00x
🐘 Postgres Nitro 2.357s (+0.9%) 3.009s (~) 0.652s 10 1.01x
🐘 Postgres Next.js (Turbopack) 2.424s 3.008s 0.583s 10 1.04x
💻 Local Nitro 3.095s (-11.5% 🟢) 4.010s (~) 0.915s 8 1.32x
💻 Local Express 3.236s (+2.0%) 4.009s (+3.2%) 0.774s 8 1.39x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.518s (-3.8%) 4.005s (-6.1% 🟢) 1.487s 8 1.00x
▲ Vercel Next.js (Turbopack) 2.602s (+4.9%) 3.841s (-0.7%) 1.240s 8 1.03x
▲ 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.472s (~) 4.010s (~) 0.538s 8 1.00x
🐘 Postgres Nitro 3.474s (~) 4.009s (~) 0.535s 8 1.00x
🐘 Postgres Next.js (Turbopack) 3.667s 4.011s 0.344s 8 1.06x
💻 Local Express 8.582s (-16.0% 🟢) 9.021s (-18.2% 🟢) 0.439s 4 2.47x
💻 Local Nitro 9.450s (+11.2% 🔺) 10.023s (+11.1% 🔺) 0.573s 3 2.72x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.624s (+1.3%) 4.211s (-1.9%) 1.587s 8 1.00x
▲ Vercel Next.js (Turbopack) 3.409s (-1.6%) 5.089s (+5.6% 🔺) 1.680s 7 1.30x
▲ 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 🥇 Nitro 0.799s (-4.4%) 1.006s (-1.7%) 0.206s 60 1.00x
🐘 Postgres Next.js (Turbopack) 0.818s 1.023s 0.205s 59 1.02x
🐘 Postgres Express 0.832s (-3.7%) 1.006s (~) 0.174s 60 1.04x
💻 Local Nitro 1.024s (+39.0% 🔺) 1.654s (+64.7% 🔺) 0.630s 37 1.28x
💻 Local Express 1.058s (+4.3%) 1.142s (-32.8% 🟢) 0.084s 58 1.32x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 8.747s (+2.1%) 10.776s (+5.5% 🔺) 2.029s 6 1.00x
▲ Vercel Next.js (Turbopack) 8.892s (-0.5%) 10.457s (-1.9%) 1.565s 6 1.02x
▲ 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 🥇 Nitro 1.915s (-1.5%) 2.101s (~) 0.186s 43 1.00x
🐘 Postgres Express 1.951s (-9.5% 🟢) 2.101s (-29.5% 🟢) 0.150s 43 1.02x
🐘 Postgres Next.js (Turbopack) 2.003s 2.377s 0.375s 38 1.05x
💻 Local Express 2.992s (-3.2%) 3.416s (-14.8% 🟢) 0.423s 27 1.56x
💻 Local Nitro 3.029s (+18.0% 🔺) 3.689s (+16.0% 🔺) 0.661s 25 1.58x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 26.784s (-9.0% 🟢) 28.529s (-9.5% 🟢) 1.745s 4 1.00x
▲ Vercel Next.js (Turbopack) 29.112s (+1.7%) 31.094s (+2.4%) 1.982s 3 1.09x
▲ 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 🥇 Nitro 3.822s (-4.2%) 4.009s (-6.7% 🟢) 0.187s 30 1.00x
🐘 Postgres Next.js (Turbopack) 3.974s 4.297s 0.322s 28 1.04x
🐘 Postgres Express 4.004s (-7.2% 🟢) 4.567s (-8.9% 🟢) 0.563s 27 1.05x
💻 Local Express 9.092s (-1.3%) 9.556s (-4.6%) 0.464s 13 2.38x
💻 Local Nitro 9.290s (+20.4% 🔺) 10.019s (+21.9% 🔺) 0.729s 12 2.43x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 70.585s (-1.5%) 71.947s (-3.3%) 1.361s 2 1.00x
▲ Vercel Next.js (Turbopack) 76.593s (~) 78.389s (~) 1.797s 2 1.09x
▲ 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.248s 1.007s 0.759s 60 1.00x
🐘 Postgres Nitro 0.284s (+0.7%) 1.006s (~) 0.722s 60 1.14x
🐘 Postgres Express 0.285s (-3.5%) 1.007s (~) 0.722s 60 1.15x
💻 Local Express 0.593s (-0.6%) 1.004s (-1.7%) 0.411s 60 2.39x
💻 Local Nitro 0.609s (-8.2% 🟢) 1.022s (+1.7%) 0.413s 59 2.46x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.404s (-48.6% 🟢) 2.702s (-39.3% 🟢) 1.298s 23 1.00x
▲ Vercel Next.js (Turbopack) 1.943s (+34.1% 🔺) 3.609s (+20.0% 🔺) 1.666s 17 1.38x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.489s 1.006s 0.518s 90 1.00x
🐘 Postgres Nitro 0.497s (+1.8%) 1.006s (~) 0.509s 90 1.02x
🐘 Postgres Express 0.508s (-3.3%) 1.006s (~) 0.499s 90 1.04x
💻 Local Express 2.443s (-2.7%) 3.008s (~) 0.566s 30 5.00x
💻 Local Nitro 2.574s (-6.3% 🟢) 3.011s (-5.4% 🟢) 0.437s 30 5.27x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.506s (+1.4%) 4.098s (-1.0%) 1.593s 22 1.00x
▲ Vercel Next.js (Turbopack) 3.287s (-2.3%) 4.863s (+0.7%) 1.576s 19 1.31x
▲ 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 🥇 Next.js (Turbopack) 0.776s 1.006s 0.231s 120 1.00x
🐘 Postgres Nitro 0.777s (-1.2%) 1.007s (~) 0.230s 120 1.00x
🐘 Postgres Express 0.807s (-4.0%) 1.009s (-1.0%) 0.202s 119 1.04x
💻 Local Express 10.917s (-0.8%) 11.391s (-2.3%) 0.474s 11 14.07x
💻 Local Nitro 11.341s (+1.8%) 12.029s (+1.5%) 0.687s 10 14.62x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.259s (-13.5% 🟢) 7.901s (-8.3% 🟢) 1.642s 16 1.00x
▲ Vercel Nitro 7.888s (+2.7%) 9.732s (+4.1%) 1.843s 13 1.26x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.198s 1.000s 0.001s 1.010s 0.812s 10 1.00x
💻 Local Express 0.199s (-4.7%) 1.004s (~) 0.011s (-6.5% 🟢) 1.017s (~) 0.818s 10 1.01x
🐘 Postgres Nitro 0.208s (+4.4%) 0.992s (-0.7%) 0.001s (-18.8% 🟢) 1.011s (~) 0.803s 10 1.05x
💻 Local Nitro 0.208s (+41.2% 🔺) 1.004s (~) 0.013s (+37.4% 🔺) 1.019s (~) 0.810s 10 1.05x
🐘 Postgres Express 0.215s (~) 0.998s (~) 0.002s (-5.9% 🟢) 1.009s (~) 0.794s 10 1.09x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.469s (-10.0% 🟢) 2.801s (-5.8% 🟢) 0.621s (+44.3% 🔺) 3.890s (+2.7%) 2.421s 10 1.00x
▲ Vercel Nitro 1.501s (-3.4%) 2.785s (-6.9% 🟢) 0.465s (-20.6% 🟢) 3.691s (-7.9% 🟢) 2.191s 10 1.02x
▲ 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 🥇 Nitro 0.606s (~) 1.004s (~) 0.004s (-5.1% 🟢) 1.022s (~) 0.417s 59 1.00x
🐘 Postgres Next.js (Turbopack) 0.611s 1.009s 0.004s 1.022s 0.411s 59 1.01x
🐘 Postgres Express 0.657s (+1.6%) 1.001s (-0.5%) 0.004s (-25.6% 🟢) 1.023s (~) 0.366s 59 1.08x
💻 Local Express 0.719s (-2.3%) 1.012s (~) 0.009s (-1.1%) 1.023s (~) 0.305s 59 1.19x
💻 Local Nitro 0.833s (+26.0% 🔺) 1.013s (~) 0.010s (-5.9% 🟢) 1.118s (+9.0% 🔺) 0.284s 54 1.38x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.984s (-9.0% 🟢) 5.377s (-10.3% 🟢) 0.247s (-16.2% 🟢) 6.046s (-11.3% 🟢) 2.061s 10 1.00x
▲ Vercel Next.js (Turbopack) 4.436s (+7.9% 🔺) 6.001s (+7.5% 🔺) 0.222s (-69.4% 🟢) 6.697s (~) 2.261s 9 1.11x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Next.js (Turbopack) 0.932s 1.091s 0.000s 1.099s 0.167s 55 1.00x
🐘 Postgres Nitro 0.949s (~) 1.123s (-2.1%) 0.000s (-67.3% 🟢) 1.141s (-1.8%) 0.192s 53 1.02x
🐘 Postgres Express 0.986s (-0.6%) 1.260s (-5.7% 🟢) 0.000s (-55.1% 🟢) 1.286s (-6.1% 🟢) 0.300s 49 1.06x
💻 Local Express 1.225s (-3.8%) 2.020s (~) 0.000s (-45.5% 🟢) 2.022s (~) 0.796s 30 1.32x
💻 Local Nitro 1.258s (-5.0% 🟢) 2.022s (~) 0.000s (-17.6% 🟢) 2.024s (~) 0.765s 30 1.35x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.656s (+4.3%) 3.750s (+4.6%) 0.000s (-100.0% 🟢) 4.203s (+2.4%) 1.547s 15 1.00x
▲ Vercel Next.js (Turbopack) 3.038s (-6.6% 🟢) 4.232s (-3.5%) 0.000s (-7.1% 🟢) 4.624s (-3.0%) 1.585s 14 1.14x
▲ 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 🥇 Nitro 1.762s (-1.2%) 2.174s (+3.7%) 0.000s (+107.1% 🔺) 2.187s (+3.6%) 0.426s 28 1.00x
🐘 Postgres Express 1.792s (~) 2.102s (-1.9%) 0.000s (NaN%) 2.136s (-0.8%) 0.343s 29 1.02x
🐘 Postgres Next.js (Turbopack) 1.833s 2.146s 0.000s 2.153s 0.320s 28 1.04x
💻 Local Express 3.366s (-8.1% 🟢) 4.032s (-3.3%) 0.001s (+45.5% 🔺) 4.037s (-3.2%) 0.671s 15 1.91x
💻 Local Nitro 3.505s (-11.2% 🟢) 4.034s (-11.0% 🟢) 0.001s (-15.2% 🟢) 4.037s (-11.0% 🟢) 0.532s 15 1.99x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.455s (-2.8%) 4.584s (-6.3% 🟢) 0.000s (-69.2% 🟢) 5.022s (-5.7% 🟢) 1.568s 13 1.00x
▲ Vercel Next.js (Turbopack) 3.550s (-18.4% 🟢) 4.574s (-18.0% 🟢) 0.000s (-69.4% 🟢) 5.020s (-16.0% 🟢) 1.470s 12 1.03x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

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

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 17/21
Next.js (Turbopack) 🐘 Postgres 19/21
Nitro 🐘 Postgres 18/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


Some benchmark jobs failed:

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

Check the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 6, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 923 0 67 990
✅ 💻 Local Development 898 0 182 1080
✅ 📦 Local Production 898 0 182 1080
✅ 🐘 Local Postgres 898 0 182 1080
✅ 🪟 Windows 82 0 8 90
❌ 🌍 Community Worlds 133 74 24 231
✅ 📋 Other 228 0 42 270
Total 4060 74 687 4821

❌ Failed Tests

🌍 Community Worlds (74 failed)

mongodb (7 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNT37ZE6WDGF4553Q141Y7NE
  • webhookWorkflow | wrun_01KNT387HNCXSKZ8CGVS8T9WKG
  • fetchWorkflow | wrun_01KNT3BN2N7E8SR774M62AT213
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNT3FDT6KJ761D25DPCAV4SM
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • health check (CLI) - workflow health command reports healthy endpoints
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KNT3NDJTMN0763SWB09J8W7Z

redis (7 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNT37ZE6WDGF4553Q141Y7NE
  • webhookWorkflow | wrun_01KNT387HNCXSKZ8CGVS8T9WKG
  • fetchWorkflow | wrun_01KNT3BN2N7E8SR774M62AT213
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNT3FDT6KJ761D25DPCAV4SM
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • health check (CLI) - workflow health command reports healthy endpoints
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KNT3NDJTMN0763SWB09J8W7Z

turso (60 failed):

  • addTenWorkflow | wrun_01KNT36X7A2W7081S89BPNCM5N
  • addTenWorkflow | wrun_01KNT36X7A2W7081S89BPNCM5N
  • wellKnownAgentWorkflow (.well-known/agent) | wrun_01KNT373ZVKNTJ4NB95G4NRN6F
  • should work with react rendering in step
  • promiseAllWorkflow | wrun_01KNT3732FYZ50WJ04GCY0XJF2
  • promiseRaceWorkflow | wrun_01KNT3773GGE9TZNCME8EPSCKR
  • promiseAnyWorkflow | wrun_01KNT3792GZRG1N36XYBJ4W872
  • importedStepOnlyWorkflow | wrun_01KNT37FTQFE51WDXQ5SKED09J
  • hookWorkflow | wrun_01KNT37NQD5JJ2A0HD53B1ZCJV
  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNT37ZE6WDGF4553Q141Y7NE
  • webhookWorkflow | wrun_01KNT387HNCXSKZ8CGVS8T9WKG
  • sleepingWorkflow | wrun_01KNT38D1B6EMCY4V99P5M9D66
  • parallelSleepWorkflow | wrun_01KNT38S4N7BK32HFNESP73XDV
  • nullByteWorkflow | wrun_01KNT38YGEY5M2DY2DJ7J1F8TX
  • workflowAndStepMetadataWorkflow | wrun_01KNT390J8322X255QYGW5ZXVR
  • fetchWorkflow | wrun_01KNT3BN2N7E8SR774M62AT213
  • promiseRaceStressTestWorkflow | wrun_01KNT3BRMBWF2T1RTG9YP3PY9V
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • error handling not registered WorkflowNotRegisteredError fails the run when workflow does not exist
  • error handling not registered StepNotRegisteredError fails the step but workflow can catch it
  • error handling not registered StepNotRegisteredError fails the run when not caught in workflow
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_01KNT3EVB6PCA4SYTK6MY35FDV
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNT3FDT6KJ761D25DPCAV4SM
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_01KNT3G0N2PGGK9S92HFD510ZR
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_01KNT3GM2CPB5ES894X96KEEHM
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_01KNT3GVDD80CW9EHD8KFQS8GG
  • closureVariableWorkflow - nested step functions with closure variables | wrun_01KNT3H04EEMMV7K2822A1SB2Q
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_01KNT3H1ZJ0YW85ZSRH7NK9X3A
  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_01KNT3HB9P31NCXQ2R3KH98XKW
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • health check (CLI) - workflow health command reports healthy endpoints
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_01KNT3HRC47VT316SDCZE10ZNK
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_01KNT3HX28N40FQHGA4C16PKAK
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_01KNT3J2ZEWCNHJR3CZ8K12BMD
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_01KNT3J8PP8RYWXFE4GGF9TQ32
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_01KNT3JEB237KA64K5BAA7A2VP
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_01KNT3JKZ1W22W53JP2VZVG8JS
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_01KNT3JTR7PT2TVFPBQSJNMFM1
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KNT3K47NBMH23R8HD6D6WCPR
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KNT3KB6YHKE0Z7MKFZFGYFWN
  • cancelRun - cancelling a running workflow | wrun_01KNT3KJ7SP6EW64B581QQBCD1
  • cancelRun via CLI - cancelling a running workflow | wrun_01KNT3KTTH3Z2ABEBYST6SHJYZ
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_01KNT3M5GZC1GXJKD2R6C6QVV4
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KNT3MS1Y1XQ0EP8G2NZKK5FK
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KNT3N3QCQTM3WZR4QHW7CQKY
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KNT3N9VPCHPWWGZGV9WTV97F
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577) | wrun_01KNT3NBTAZ36Q2YMYVHMJQS14
  • resilient start: addTenWorkflow completes when run_created returns 500 | wrun_01KNT3NDJTMN0763SWB09J8W7Z
  • getterStepWorkflow - getter functions with "use step" directive | wrun_01KNT3NGHNP4JFF65AZ5KTV95W

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 83 0 7
✅ example 83 0 7
✅ express 83 0 7
✅ fastify 83 0 7
✅ hono 83 0 7
✅ nextjs-turbopack 88 0 2
✅ nextjs-webpack 88 0 2
✅ nitro 83 0 7
✅ nuxt 83 0 7
✅ sveltekit 83 0 7
✅ vite 83 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 76 0 14
✅ express-stable 76 0 14
✅ fastify-stable 76 0 14
✅ hono-stable 76 0 14
✅ nextjs-turbopack-canary 63 0 27
✅ nextjs-turbopack-stable 82 0 8
✅ nextjs-webpack-canary 63 0 27
✅ nextjs-webpack-stable 82 0 8
✅ nitro-stable 76 0 14
✅ nuxt-stable 76 0 14
✅ sveltekit-stable 76 0 14
✅ vite-stable 76 0 14
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 76 0 14
✅ express-stable 76 0 14
✅ fastify-stable 76 0 14
✅ hono-stable 76 0 14
✅ nextjs-turbopack-canary 63 0 27
✅ nextjs-turbopack-stable 82 0 8
✅ nextjs-webpack-canary 63 0 27
✅ nextjs-webpack-stable 82 0 8
✅ nitro-stable 76 0 14
✅ nuxt-stable 76 0 14
✅ sveltekit-stable 76 0 14
✅ vite-stable 76 0 14
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 76 0 14
✅ express-stable 76 0 14
✅ fastify-stable 76 0 14
✅ hono-stable 76 0 14
✅ nextjs-turbopack-canary 63 0 27
✅ nextjs-turbopack-stable 82 0 8
✅ nextjs-webpack-canary 63 0 27
✅ nextjs-webpack-stable 82 0 8
✅ nitro-stable 76 0 14
✅ nuxt-stable 76 0 14
✅ sveltekit-stable 76 0 14
✅ vite-stable 76 0 14
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 82 0 8
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 6 0 0
❌ mongodb 56 7 8
✅ redis-dev 6 0 0
❌ redis 56 7 8
✅ turso-dev 6 0 0
❌ turso 3 60 8
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 76 0 14
✅ e2e-local-postgres-nest-stable 76 0 14
✅ e2e-local-prod-nest-stable 76 0 14

📋 View full workflow run

Copy link
Copy Markdown
Contributor

@vercel vercel Bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Getter step registrations (instance and static) generate bare registerStepFunction(...) calls referencing an undefined function, causing runtime failures since workflow/internal/private was removed.

Fix on Vercel

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

This PR aims to make Run instances serializable/deserializable across step/runtime and workflow-VM boundaries via the class registry + WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE, and adds tests to validate roundtrips.

Changes:

  • Add custom class serialization hooks to @workflow/core’s Run and update runtime behavior to be workflow/step boundary friendly.
  • Expose Run from workflow/api and add runtime + e2e tests covering boundary roundtrips.
  • Add a changeset bumping @workflow/core and workflow for the new behavior.

Reviewed changes

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

Show a summary per file
File Description
workbench/example/workflows/99_e2e.ts Adds an example workflow that passes Run across workflow/step boundaries and asserts instanceof/behavior.
packages/workflow/src/api-workflow.ts Changes workflow-conditional workflow/api surface to export Run (previously a throwing stub).
packages/core/src/runtime/runs.test.ts Adds unit tests for Run custom serialization and step boundary roundtrip.
packages/core/src/runtime/run.ts Implements Run class serialization hooks and makes world access lazy; annotates methods/getters with 'use step'.
packages/core/package.json Adds a new ./runtime/run export.
packages/core/e2e/e2e.test.ts Adds an e2e test ensuring Run roundtrips across workflow/step boundaries in real execution.
.changeset/neat-runs-serialize.md Declares minor version bumps for the new Run serialization behavior.

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

Comment thread packages/workflow/src/api-workflow.ts
Comment thread packages/core/package.json
Comment thread packages/core/src/runtime/run.ts Outdated
Comment on lines +74 to +78
return { runId: instance.runId };
}

static [WORKFLOW_DESERIALIZE](data: { runId: string }) {
return new Run(data.runId);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

Run instances created by start() can carry resilientStart state that changes pollReturnValue() behavior (retries on WorkflowRunNotFoundError). The custom class serialization currently only persists { runId }, so after crossing a step/workflow boundary the deserialized Run will lose resilientStart and may fail fast on transient 404s. Include resilientStart (and set it in deserialization) so behavior is preserved across serialization boundaries.

Suggested change
return { runId: instance.runId };
}
static [WORKFLOW_DESERIALIZE](data: { runId: string }) {
return new Run(data.runId);
const { runId, resilientStart } = instance as Run<unknown> & {
resilientStart?: boolean;
};
return { runId, resilientStart };
}
static [WORKFLOW_DESERIALIZE](data: {
runId: string;
resilientStart?: boolean;
}) {
const run = new Run(data.runId);
(run as Run<unknown> & { resilientStart?: boolean }).resilientStart =
data.resilientStart;
return run;

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch. Fixed — WORKFLOW_SERIALIZE now includes resilientStart in the payload and WORKFLOW_DESERIALIZE passes it to the constructor. Unit test updated.

Copy link
Copy Markdown
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

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

AI review: no blocking issues

Comment thread packages/core/src/runtime/run.ts Outdated
}

static [WORKFLOW_DESERIALIZE](data: { runId: string }) {
return new Run(data.runId);
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.

AI Review: Note

WORKFLOW_DESERIALIZE always constructs new Run(data.runId) and drops constructor options. start() can return new Run(runId, { resilientStart: true }) (see start.ts). If a Run with resilientStart: true is ever passed across a serde boundary, returnValue polling would lose the extra 404 retry behavior. If that combination is not supported, consider documenting it; if it might happen, consider including resilientStart in the serialized payload.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in the same commit — resilientStart is now included in the serialized payload.

Copy link
Copy Markdown
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

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

AI review: no blocking issues

Comment thread packages/core/package.json
Copy link
Copy Markdown
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

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

AI review: no blocking issues

Comment thread packages/core/src/runtime/run.ts
Single Run class implementation:
- Add WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE to core Run class
- Mark all runtime-dependent methods/getters with "use step" so SWC
  strips their bodies from the workflow bundle
- Remove duplicate Run stub from api-workflow.ts; re-export core Run
- SWC auto-registers the class (no manual registerSerializationClass)
- Add e2e test for Run serialization across workflow/step boundaries
- Add unit tests for serde roundtrip
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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


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

Comment on lines +246 to +256
const serialized = Run[WORKFLOW_SERIALIZE](run);
const deserialized = Run[WORKFLOW_DESERIALIZE]({
runId: 'wrun_deserialize',
});

expect(serialized).toEqual({
runId: 'wrun_serialize',
resilientStart: false,
});
expect(deserialized).toBeInstanceOf(Run);
expect(deserialized.runId).toBe('wrun_deserialize');
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The new serialization unit test only asserts resilientStart: false. Since resilientStart is now part of the serialized payload (and affects returnValue retry behavior), add a case that serializes/deserializes a Run created with resilientStart: true and asserts it roundtrips as true to prevent regressions.

Suggested change
const serialized = Run[WORKFLOW_SERIALIZE](run);
const deserialized = Run[WORKFLOW_DESERIALIZE]({
runId: 'wrun_deserialize',
});
expect(serialized).toEqual({
runId: 'wrun_serialize',
resilientStart: false,
});
expect(deserialized).toBeInstanceOf(Run);
expect(deserialized.runId).toBe('wrun_deserialize');
const serialized = Run[WORKFLOW_SERIALIZE](run);
const resilientRun = new Run('wrun_serialize_resilient', {
resilientStart: true,
});
const resilientSerialized = Run[WORKFLOW_SERIALIZE](resilientRun);
const deserialized = Run[WORKFLOW_DESERIALIZE]({
runId: 'wrun_deserialize',
});
const resilientDeserialized = Run[WORKFLOW_DESERIALIZE]({
runId: 'wrun_deserialize_resilient',
resilientStart: true,
});
expect(serialized).toEqual({
runId: 'wrun_serialize',
resilientStart: false,
});
expect(resilientSerialized).toEqual({
runId: 'wrun_serialize_resilient',
resilientStart: true,
});
expect(deserialized).toBeInstanceOf(Run);
expect(deserialized.runId).toBe('wrun_deserialize');
expect(deserialized.resilientStart).toBe(false);
expect(resilientDeserialized).toBeInstanceOf(Run);
expect(resilientDeserialized.runId).toBe('wrun_deserialize_resilient');
expect(resilientDeserialized.resilientStart).toBe(true);

Copilot uses AI. Check for mistakes.
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