Skip to content

Fix Vitest project isolation and paginated recovery filtering#1818

Closed
tomdale wants to merge 7 commits into
mainfrom
tomdale/local-world-isolation
Closed

Fix Vitest project isolation and paginated recovery filtering#1818
tomdale wants to merge 7 commits into
mainfrom
tomdale/local-world-isolation

Conversation

@tomdale
Copy link
Copy Markdown
Contributor

@tomdale tomdale commented Apr 21, 2026

Summary

Before this change, @workflow/vitest passed custom test directories through process-wide env vars, so Vitest workspace projects and config reloads could leak one project's paths into another. world-local also added tagged recovery for Vitest workers, but its paginated filesystem query dropped the tag filter after the first page, which could re-enqueue runs owned by other workers in shared local data.

This branch moves the Vitest harness to project-scoped provided context. workflow() resolves cwd, rootDir, dataDir, and outDir once, passes them through Vitest's per-project context, and global-setup plus setup-file read that resolved state when building bundles and creating the local test world. The package metadata and lockfile now also declare the Vitest test dependency and script used by the package.

On the recovery side, world-local now keeps fileIdFilter applied while paginating and uses that path to scope startup recovery to the active tag. It also adds an explicit recoverActiveRuns opt-out and disables recovery in the Vitest harness, so worker startup does not re-enqueue stale runs before direct handlers are registered.

Testing

  • pnpm exec vitest run packages/vitest/src/index.test.ts packages/world-local/src/fs.test.ts packages/world-local/src/reenqueue.test.ts
  • pnpm exec tsc -p packages/vitest/tsconfig.json --noEmit
  • pnpm exec tsc -p packages/world-local/tsconfig.json --noEmit

Scope local-world startup recovery to the active tag so Vitest workers do not re-enqueue runs owned by other workers. Also add an opt-out for recovery, disable it in the Vitest harness, and allow callers to override the test data and bundle directories.

Add regression coverage for tagged recovery, recovery opt-out, and custom Vitest directory configuration.
@tomdale tomdale requested a review from a team as a code owner April 21, 2026 16:24
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 21, 2026

🦋 Changeset detected

Latest commit: c9282a0

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

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

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

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

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 21, 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 May 2, 2026 2:25am
example-nextjs-workflow-webpack Ready Ready Preview, Comment May 2, 2026 2:25am
example-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-astro-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-express-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-fastify-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-hono-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-nitro-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-nuxt-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-sveltekit-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workbench-vite-workflow Ready Ready Preview, Comment May 2, 2026 2:25am
workflow-docs Ready Ready Preview, Comment, Open in v0 May 2, 2026 2:25am
workflow-swc-playground Ready Ready Preview, Comment May 2, 2026 2:25am
workflow-web Ready Ready Preview, Comment May 2, 2026 2:25am

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 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 🥇 Next.js (Turbopack) 0.040s 1.005s 0.965s 10 1.00x
💻 Local Nitro 0.042s (-3.0%) 1.005s (~) 0.963s 10 1.06x
💻 Local Express 0.043s (-3.8%) 1.005s (~) 0.963s 10 1.08x
🐘 Postgres Nitro 0.057s (-40.3% 🟢) 1.009s (-3.2%) 0.952s 10 1.43x
🐘 Postgres Express 0.060s (+3.3%) 1.013s (~) 0.953s 10 1.51x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.187s (-54.4% 🟢) 1.728s (-31.1% 🟢) 1.541s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 1.100s 2.005s 0.904s 10 1.00x
🐘 Postgres Express 1.117s (-2.6%) 2.009s (~) 0.892s 10 1.02x
💻 Local Nitro 1.129s (~) 2.005s (~) 0.876s 10 1.03x
💻 Local Express 1.140s (+1.3%) 2.005s (~) 0.865s 10 1.04x
🐘 Postgres Nitro 1.141s (~) 2.010s (~) 0.869s 10 1.04x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.054s (-47.2% 🟢) 3.742s (-36.7% 🟢) 1.688s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 10.636s 11.024s 0.388s 3 1.00x
🐘 Postgres Express 10.719s (-2.2%) 11.018s (~) 0.299s 3 1.01x
🐘 Postgres Nitro 10.912s (~) 11.019s (~) 0.108s 3 1.03x
💻 Local Nitro 10.944s (~) 11.024s (~) 0.080s 3 1.03x
💻 Local Express 10.968s (~) 11.023s (~) 0.055s 3 1.03x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 17.144s (-27.8% 🟢) 19.085s (-24.0% 🟢) 1.941s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 14.152s 15.027s 0.875s 4 1.00x
🐘 Postgres Express 14.161s (-2.9%) 15.019s (~) 0.858s 4 1.00x
🐘 Postgres Nitro 14.541s (~) 15.022s (~) 0.481s 4 1.03x
💻 Local Nitro 15.001s (~) 15.280s (-4.7%) 0.280s 4 1.06x
💻 Local Express 15.069s (+0.7%) 15.280s (+1.7%) 0.212s 4 1.06x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 31.465s (-51.2% 🟢) 33.025s (-50.4% 🟢) 1.560s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 13.257s (-5.4% 🟢) 14.019s (-3.9%) 0.762s 7 1.00x
🐘 Postgres Nitro 13.815s (-1.1%) 14.018s (-2.0%) 0.203s 7 1.04x
💻 Local Next.js (Turbopack) 14.776s 15.028s 0.252s 6 1.11x
💻 Local Nitro 16.737s (~) 17.030s (~) 0.293s 6 1.26x
💻 Local Express 16.768s (+1.0%) 17.032s (~) 0.264s 6 1.26x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 55.666s (-86.8% 🟢) 57.243s (-86.5% 🟢) 1.577s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.217s (-3.5%) 2.007s (~) 0.790s 15 1.00x
🐘 Postgres Nitro 1.265s (-0.7%) 2.010s (~) 0.744s 15 1.04x
💻 Local Next.js (Turbopack) 1.443s 2.006s 0.563s 15 1.19x
💻 Local Express 1.494s (~) 2.005s (~) 0.511s 15 1.23x
💻 Local Nitro 1.569s (-3.9%) 2.006s (-3.3%) 0.437s 15 1.29x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.420s (-14.1% 🟢) 4.175s (-3.4%) 1.755s 8 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.284s (-3.3%) 3.009s (~) 0.725s 10 1.00x
🐘 Postgres Nitro 2.325s (-1.1%) 3.009s (~) 0.684s 10 1.02x
💻 Local Next.js (Turbopack) 2.613s 3.007s 0.394s 10 1.14x
💻 Local Express 3.132s (+6.1% 🔺) 3.884s (+12.5% 🔺) 0.752s 8 1.37x
💻 Local Nitro 3.199s (+1.8%) 3.886s (~) 0.687s 8 1.40x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.537s (-37.4% 🟢) 4.797s (-19.0% 🟢) 2.260s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.395s (-2.6%) 4.008s (~) 0.613s 8 1.00x
🐘 Postgres Nitro 3.466s (~) 4.010s (~) 0.545s 8 1.02x
💻 Local Next.js (Turbopack) 6.649s 7.013s 0.364s 5 1.96x
💻 Local Express 8.457s (+1.4%) 9.024s (~) 0.567s 4 2.49x
💻 Local Nitro 8.652s (+3.6%) 9.020s (~) 0.367s 4 2.55x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.816s (-20.1% 🟢) 4.501s (-18.7% 🟢) 1.686s 8 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.202s (-4.4%) 2.007s (~) 0.805s 15 1.00x
🐘 Postgres Nitro 1.262s (~) 2.008s (~) 0.746s 15 1.05x
💻 Local Next.js (Turbopack) 1.458s 2.005s 0.547s 15 1.21x
💻 Local Express 1.522s (-19.6% 🟢) 2.006s (-15.1% 🟢) 0.484s 15 1.27x
💻 Local Nitro 1.608s (-13.8% 🟢) 2.073s (-11.4% 🟢) 0.465s 15 1.34x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.193s (-10.8% 🟢) 4.144s (-0.6%) 1.951s 8 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 2.257s (-3.6%) 3.009s (~) 0.752s 10 1.00x
🐘 Postgres Nitro 2.339s (~) 3.011s (~) 0.672s 10 1.04x
💻 Local Next.js (Turbopack) 2.719s 3.107s 0.388s 10 1.20x
💻 Local Nitro 2.993s (-2.4%) 3.885s (~) 0.892s 8 1.33x
💻 Local Express 2.994s (-4.4%) 3.760s (~) 0.766s 8 1.33x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.159s (-33.2% 🟢) 3.780s (-25.5% 🟢) 1.621s 8 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.362s (-3.9%) 4.009s (~) 0.647s 8 1.00x
🐘 Postgres Nitro 3.477s (~) 4.010s (~) 0.534s 8 1.03x
💻 Local Next.js (Turbopack) 7.559s 8.017s 0.458s 4 2.25x
💻 Local Express 8.670s (-1.5%) 9.274s (~) 0.604s 4 2.58x
💻 Local Nitro 8.842s (-3.3%) 9.026s (-10.0% 🟢) 0.184s 4 2.63x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.881s (-43.4% 🟢) 5.010s (-26.5% 🟢) 2.129s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.657s (-21.7% 🟢) 1.006s (-1.7%) 0.349s 60 1.00x
💻 Local Next.js (Turbopack) 0.674s 1.004s 0.330s 60 1.03x
🐘 Postgres Nitro 0.805s (-1.8%) 1.006s (~) 0.200s 60 1.23x
💻 Local Express 0.994s (+1.0%) 1.225s (+13.8% 🔺) 0.231s 50 1.51x
💻 Local Nitro 1.116s (+13.8% 🔺) 1.424s (+30.2% 🔺) 0.308s 43 1.70x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 9.164s (-58.4% 🟢) 11.145s (-53.6% 🟢) 1.981s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.575s (-20.3% 🟢) 2.007s (-11.1% 🟢) 0.431s 45 1.00x
🐘 Postgres Nitro 1.867s (-3.1%) 2.029s (-3.4%) 0.163s 45 1.19x
💻 Local Next.js (Turbopack) 2.125s 3.007s 0.882s 30 1.35x
💻 Local Express 3.027s (~) 3.649s (+1.8%) 0.622s 25 1.92x
💻 Local Nitro 3.052s (+0.5%) 3.729s (-0.8%) 0.678s 25 1.94x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 25.489s (-35.4% 🟢) 27.415s (-33.6% 🟢) 1.926s 4 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 3.233s (-19.0% 🟢) 4.008s (-8.3% 🟢) 0.774s 30 1.00x
🐘 Postgres Nitro 3.782s (-7.8% 🟢) 4.008s (-13.0% 🟢) 0.225s 30 1.17x
💻 Local Next.js (Turbopack) 7.014s 7.514s 0.500s 16 2.17x
💻 Local Express 9.235s (~) 9.941s (-0.8%) 0.706s 13 2.86x
💻 Local Nitro 9.304s (~) 10.019s (~) 0.715s 12 2.88x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 68.716s (-29.1% 🟢) 70.793s (-28.1% 🟢) 2.077s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.224s (-20.5% 🟢) 1.007s (~) 0.782s 60 1.00x
🐘 Postgres Nitro 0.285s (+0.7%) 1.007s (~) 0.722s 60 1.27x
💻 Local Next.js (Turbopack) 0.554s 1.004s 0.450s 60 2.47x
💻 Local Nitro 0.610s (+0.8%) 1.005s (-1.6%) 0.395s 60 2.72x
💻 Local Express 0.689s (+22.9% 🔺) 1.116s (+11.1% 🔺) 0.427s 54 3.07x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.509s (-9.2% 🟢) 3.110s (-7.2% 🟢) 1.601s 20 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.396s (-22.3% 🟢) 1.005s (~) 0.609s 90 1.00x
🐘 Postgres Nitro 0.485s (-2.4%) 1.006s (~) 0.521s 90 1.22x
💻 Local Next.js (Turbopack) 2.442s 3.008s 0.565s 30 6.16x
💻 Local Nitro 2.648s (+4.3%) 3.010s (~) 0.362s 30 6.68x
💻 Local Express 2.686s (+6.9% 🔺) 3.077s (+2.3%) 0.391s 30 6.78x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.050s (-5.5% 🟢) 4.903s (+1.7%) 1.853s 19 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.609s (-25.6% 🟢) 1.005s (-1.2%) 0.397s 120 1.00x
🐘 Postgres Nitro 0.795s (+0.6%) 1.025s (+1.7%) 0.230s 118 1.31x
💻 Local Next.js (Turbopack) 9.247s 9.866s 0.619s 13 15.19x
💻 Local Express 11.134s (-0.5%) 11.666s (-2.3%) 0.533s 11 18.29x
💻 Local Nitro 11.448s (+2.3%) 12.030s (+3.1%) 0.582s 10 18.80x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 6.682s (-13.5% 🟢) 8.514s (-9.4% 🟢) 1.832s 15 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - -

🔍 Observability: Nitro

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.131s 1.002s 0.009s 1.015s 0.884s 10 1.00x
🐘 Postgres Express 0.169s (-17.5% 🟢) 1.000s (~) 0.001s (-56.3% 🟢) 1.008s (~) 0.839s 10 1.30x
💻 Local Express 0.202s (+1.4%) 1.004s (~) 0.013s (+3.3%) 1.019s (~) 0.817s 10 1.55x
🐘 Postgres Nitro 0.205s (~) 0.994s (-0.6%) 0.001s (-6.7% 🟢) 1.009s (~) 0.805s 10 1.57x
💻 Local Nitro 0.205s (-4.2%) 1.004s (~) 0.013s (~) 1.019s (~) 0.814s 10 1.57x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.744s (-54.5% 🟢) 3.047s (-42.3% 🟢) 1.884s (+153.9% 🔺) 5.363s (-17.3% 🟢) 3.619s 10 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - - -

🔍 Observability: 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.560s (-11.1% 🟢) 1.040s (+3.4%) 0.003s (-18.0% 🟢) 1.054s (+3.1%) 0.494s 57 1.00x
🐘 Postgres Nitro 0.605s (-3.1%) 1.005s (~) 0.004s (+4.9%) 1.022s (~) 0.417s 59 1.08x
💻 Local Next.js (Turbopack) 0.666s 1.010s 0.009s 1.115s 0.448s 54 1.19x
💻 Local Express 0.745s (-1.6%) 1.012s (-1.6%) 0.008s (-10.9% 🟢) 1.022s (-1.7%) 0.277s 59 1.33x
💻 Local Nitro 0.865s (+3.2%) 1.012s (~) 0.012s (+25.1% 🔺) 1.119s (~) 0.253s 54 1.55x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.650s (-87.6% 🟢) 5.236s (-83.0% 🟢) 0.507s (+352.4% 🔺) 6.181s (-80.5% 🟢) 2.531s 10 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - - -

🔍 Observability: Nitro

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.967s (~) 1.165s (-6.7% 🟢) 0.000s (+84.6% 🔺) 1.181s (-6.1% 🟢) 0.213s 52 1.00x
🐘 Postgres Express 0.987s (+2.8%) 1.181s (-7.6% 🟢) 0.000s (-100.0% 🟢) 1.244s (-4.8%) 0.257s 49 1.02x
💻 Local Next.js (Turbopack) 1.188s 2.016s 0.000s 2.018s 0.831s 30 1.23x
💻 Local Express 1.231s (+0.5%) 2.021s (~) 0.000s (+20.0% 🔺) 2.023s (~) 0.791s 30 1.27x
💻 Local Nitro 1.293s (+5.8% 🔺) 2.022s (~) 0.000s (+133.3% 🔺) 2.024s (~) 0.731s 30 1.34x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.953s (-3.2%) 4.496s (+2.4%) 0.000s (+100.0% 🔺) 4.957s (+3.1%) 2.004s 13 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - - -

🔍 Observability: Nitro

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.785s (~) 2.173s (+1.5%) 0.000s (~) 2.217s (+2.0%) 0.432s 28 1.00x
🐘 Postgres Express 1.860s (+4.9%) 2.399s (+10.2% 🔺) 0.000s (+Infinity% 🔺) 2.427s (+10.4% 🔺) 0.568s 25 1.04x
💻 Local Express 3.423s (-1.3%) 4.035s (~) 0.001s (-8.3% 🟢) 4.038s (~) 0.615s 15 1.92x
💻 Local Next.js (Turbopack) 3.483s 4.031s 0.000s 4.034s 0.551s 15 1.95x
💻 Local Nitro 3.804s (+12.3% 🔺) 4.392s (+8.9% 🔺) 0.000s (-46.4% 🟢) 4.394s (+8.9% 🔺) 0.590s 14 2.13x
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.811s (-6.9% 🟢) 5.234s (-2.6%) 0.000s (-100.0% 🟢) 5.655s (-2.4%) 1.844s 11 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Next.js (Turbopack) ⚠️ missing - - - - -

🔍 Observability: Nitro

Summary

Fastest Framework by World

Winner determined by most benchmark wins

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

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 20/21
Next.js (Turbopack) 💻 Local 21/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 Apr 21, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 999 1 67 1067
✅ 💻 Local Development 1078 0 86 1164
✅ 📦 Local Production 1078 0 86 1164
✅ 🐘 Local Postgres 1078 0 86 1164
✅ 📋 Other 273 0 18 291
Total 4506 1 343 4850

❌ Failed Tests

▲ Vercel Production (1 failed)

nextjs-webpack (1 failed):

  • runClassSerializationWorkflow - Run instances serialize across workflow/step boundaries | wrun_01KQK8A7TEB02RGAKJYWKS09K7 | 🔍 observability

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 90 0 7
✅ example 90 0 7
✅ express 90 0 7
✅ fastify 90 0 7
✅ hono 90 0 7
✅ nextjs-turbopack 95 0 2
❌ nextjs-webpack 94 1 2
✅ nitro 90 0 7
✅ nuxt 90 0 7
✅ sveltekit 90 0 7
✅ vite 90 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 91 0 6
✅ express-stable 91 0 6
✅ fastify-stable 91 0 6
✅ hono-stable 91 0 6
✅ nextjs-turbopack-canary 78 0 19
✅ nextjs-turbopack-stable 97 0 0
✅ nextjs-webpack-canary 78 0 19
✅ nextjs-webpack-stable 97 0 0
✅ nitro-stable 91 0 6
✅ nuxt-stable 91 0 6
✅ sveltekit-stable 91 0 6
✅ vite-stable 91 0 6
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 91 0 6
✅ express-stable 91 0 6
✅ fastify-stable 91 0 6
✅ hono-stable 91 0 6
✅ nextjs-turbopack-canary 78 0 19
✅ nextjs-turbopack-stable 97 0 0
✅ nextjs-webpack-canary 78 0 19
✅ nextjs-webpack-stable 97 0 0
✅ nitro-stable 91 0 6
✅ nuxt-stable 91 0 6
✅ sveltekit-stable 91 0 6
✅ vite-stable 91 0 6
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 91 0 6
✅ express-stable 91 0 6
✅ fastify-stable 91 0 6
✅ hono-stable 91 0 6
✅ nextjs-turbopack-canary 78 0 19
✅ nextjs-turbopack-stable 97 0 0
✅ nextjs-webpack-canary 78 0 19
✅ nextjs-webpack-stable 97 0 0
✅ nitro-stable 91 0 6
✅ nuxt-stable 91 0 6
✅ sveltekit-stable 91 0 6
✅ vite-stable 91 0 6
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 91 0 6
✅ e2e-local-postgres-nest-stable 91 0 6
✅ e2e-local-prod-nest-stable 91 0 6

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

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: blocking issues found

Comment thread packages/vitest/src/index.test.ts Outdated
path.join(os.tmpdir(), 'workflow-vitest-build-')
);
tempDirs.push(rootDir);
const cwd = '/repo/app';
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: Blocking

The new tests in this file hardcode POSIX-style absolute paths (/repo/app, /tmp/workflow-vitest-root, etc.) and compare them directly against the output of resolve(...) inside options.ts. On Windows, path.resolve('/repo/app') expands to the current drive (e.g. D:\repo\app), so the assertions diverge and the test file fails.

This is why Unit Tests (windows-latest) is failing on this PR (4 failed in src/index.test.ts) while it passes on main. Lines that bleed into the flake: 108 (cwd), 186 (cwd), 226–229 / 236–239 / 265–268 (the __workflowVitestOptions objects passed to global-setup and setup-file).

Either compute the expected values with path.resolve(...) / path.join(...) at assertion time, or anchor the fixtures to os.tmpdir() (the filesystem-touching tests above already do this). Without that, the Windows unit-test lane stays red.

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) Fixed in aecde1a — the test fixtures now pass absolute paths through path.resolve(...) / path.join(...), so the expected values match the internal resolve() output on both POSIX and Windows.

* Directory for generated workflow and step bundles.
* Defaults to `<rootDir>/.workflow-vitest`.
*/
outDir?: string;
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: Blocking

This adds three new user-facing options to WorkflowTestOptions (rootDir, dataDir, outDir), and the workflow() signature below now accepts a WorkflowTestOptions argument — previously it took none. The reference docs under docs/content/docs/api-reference/vitest/index.mdx still document only cwd on WorkflowTestOptions and still show workflow() invoked with no arguments.

Per the repo convention that user-facing changes in vercel/workflow are documented in docs, please update that page to: (1) add rootDir, dataDir, outDir to the WorkflowTestOptions table, noting how relative paths resolve against cwd and the <rootDir>/.workflow-data / <rootDir>/.workflow-vitest defaults; (2) mention that workflow() accepts an optional WorkflowTestOptions and show a usage snippet for a workspace with a non-standard layout — that is the motivating use case for this PR, so it's worth surfacing.

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) Fixed in aecde1a — the @workflow/vitest reference page now documents rootDir, dataDir, outDir with their resolution rules and default locations, notes that workflow() accepts an optional WorkflowTestOptions, and shows a usage snippet for a non-standard layout.

Comment thread packages/world-local/src/index.ts Outdated
storage.runs.list({
...params,
fileIdFilter: (fileId: string) => hasTag(fileId, tag),
} as any)) as typeof storage.runs.list,
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: Nit

This wrapper re-implements storage.runs.list with two as any casts (on params and on the call itself) and therefore bypasses the outer instrumentObject('world.runs', ...) span for the recovery call — the inner storage.runs.list(...) call is still instrumented, so telemetry is preserved, but the shape is awkward.

A cleaner alternative would be to plumb fileIdFilter through an internal extension of ListWorkflowRunsParams (still kept out of the public Storage['runs']['list'] surface) so the wrapper can drop the double as any. Not blocking — the code is internal and correct — just flagging for future cleanup.

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) Addressed in aecde1a — plumbed fileIdFilter through a local-only LocalListWorkflowRunsParams (and LocalRunsStorage / LocalStorage) that structurally assigns to Storage['runs'] at the reenqueueActiveRuns boundary. The recovery wrapper in createLocalWorld no longer needs the double as any, and the option stays off the public @workflow/world surface.

world = createLocalWorld({
dataDir: join(cwd, '.workflow-data'),
dataDir,
recoverActiveRuns: false,
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

Good call registering handlers before world.start?.() — defensive against any future change where recoverActiveRuns gets re-enabled here (or a caller of setupWorkflowTests passes it through). Since the harness hardcodes recoverActiveRuns: false today the ordering is defensive, not load-bearing; a short comment noting that register-before-start is intentional would help prevent a later cosmetic reorder from silently reintroducing the race this PR just fixed.

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) Added an inline comment in aecde1a noting that register-before-start is intentional and that reordering would reintroduce the race if recoverActiveRuns is ever re-enabled or passed through by a caller.

@VaguelySerious
Copy link
Copy Markdown
Member

I like the approach overall and would love to ship this after fixing the blocking issues

- Use path.resolve() in vitest test fixtures so assertions match on Windows
- Document rootDir/dataDir/outDir and workflow() options in docs
- Note intent of register-before-start ordering in setupWorkflowTests
- Plumb fileIdFilter via internal LocalListWorkflowRunsParams type to
  drop the double `as any` on the recovery wrapper

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
…next test

The 'should include steps discovered from workflow imports' test creates a
workflow + step file pair, lets the deferred builder discover them, and
restores cleanup via afterEach. Since #1796 the generated step route bundle
imports the discovered step source directly (no copy), so when the source is
deleted the bundle is left with a literal `import '../workflows/discovered-via-workflow-step.ts'`
until the deferred builder rebuilds and `filterExistingFiles` prunes it.

On Windows the rebuild can lag the deletion, leaving the step route bundle
unable to compile for the next test file (which shares the same dev server).
Every step request then returns 500 and the e2e suite hangs until the 30-min
job timeout. The bug was masked on main by an earlier dev test failure that
short-circuits the suite.

Tear the test down in-test instead of via afterEach: restore the api file,
delete the workflow + step files, and poll the manifest until the discovered
step is gone before returning. Also reorder afterEach to restore content
before deleting files, so the dev server never sees an api import pointing
at a missing module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants