Skip to content

docs: March docs audit and alignment#1466

Open
johnlindquist wants to merge 14 commits intomainfrom
march-docs-audit
Open

docs: March docs audit and alignment#1466
johnlindquist wants to merge 14 commits intomainfrom
march-docs-audit

Conversation

@johnlindquist
Copy link
Contributor

@johnlindquist johnlindquist commented Mar 20, 2026

Summary

  • Align documentation across observability, NestJS setup, start() troubleshooting, framework integrations, encryption, event sourcing, server-based testing, and durable agent API reference
  • Tighten NestJS CommonJS guidance and Next.js monorepo setup docs
  • Update docs-typecheck README to reflect actual tool behavior

Test plan

  • Verify docs site builds cleanly
  • Spot-check updated docs pages render correctly

Prevent confusion when Next.js apps live below the repository root and workflow code imports sibling workspace packages.

This documents the output tracing root requirement at the point where users configure withWorkflow, so monorepo setups follow the same working patterns as the shipped Next.js integration instead of failing due to unresolved workspace imports.

Ploop-Iter: 1
Automated checkpoint commit.

Ploop-Iter: 2
Automated checkpoint commit.

Ploop-Iter: 3
Capture recent workflow documentation updates so the public docs and package guidance stay aligned with the implementation and current docs-typecheck behavior.

Ploop-Iter: 1
Document the current docs verification contract so contributors do not assume JavaScript examples are type-checked when only TypeScript snippets are enforced today.

Add regression coverage around the README language and framework integration guidance to keep those docs aligned with the implemented Next.js and docs-typecheck behavior as future changes land.

Ploop-Iter: 2
Document the most common causes of the invalid workflow function error so users can resolve start() failures from the API docs and Next.js setup flow without having to infer build-time requirements from runtime behavior.

Keep the new troubleshooting page aligned with the shipped runtime message and add regression coverage so future wording or cross-link changes do not silently break that guidance.

Ploop-Iter: 3
Document both supported NestJS module formats and add a regression check so the getting-started guide stays aligned with the package README as the integration evolves.

Ploop-Iter: 1
Keep the NestJS getting-started guide consistent across the ESM and CommonJS paths so readers do not mix module settings or import styles mid-setup.

Strengthen the docs regression coverage around the later guide sections so future edits are more likely to preserve the supported CommonJS path documented in the package README.

Ploop-Iter: 2
Document the recently added troubleshooting and observability patterns so the public docs stay aligned with the behavior users now encounter in practice.

This keeps the NestJS guide, workflow API reference, and docs regression coverage in sync with the runtime-facing guidance from recent changes.

Ploop-Iter: 3
Why: keep the docs aligned with recent API and runtime behavior changes so examples and reference pages don’t drift from the supported surface.

Ploop-Iter: 1
Add regression coverage for doc surfaces that are easy to drift from implementation so docs audits catch mismatches early and keep published guidance aligned with the supported API surface.

Ploop-Iter: 2
Keep new observability and server-testing guidance anchored to machine-readable interfaces so follow-up implementation changes do not silently drift away from the documented agent and automation patterns.

Ploop-Iter: 3
Keep the docs consistent so users get the same guidance when debugging hook token collisions and correlating workflow events with platform logs.

This prevents the event-sourcing reference from drifting away from the observability and error docs, and adds guard tests to catch regressions.

Ploop-Iter: 1
@johnlindquist johnlindquist requested review from a team and ijjk as code owners March 20, 2026 14:56
@changeset-bot
Copy link

changeset-bot bot commented Mar 20, 2026

⚠️ No Changeset found

Latest commit: 3dd651e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

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

Click here to learn what changesets are, and how to add one.

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 756 2 67 825
✅ 💻 Local Development 782 0 118 900
✅ 📦 Local Production 782 0 118 900
✅ 🐘 Local Postgres 782 0 118 900
✅ 🪟 Windows 72 0 3 75
❌ 🌍 Community Worlds 118 56 15 189
✅ 📋 Other 198 0 27 225
Total 3490 58 466 4014

❌ Failed Tests

▲ Vercel Production (2 failed)

express (1 failed):

  • DurableAgent e2e core single tool call

nuxt (1 failed):

  • DurableAgent e2e core single tool call
🌍 Community Worlds (56 failed)

mongodb (3 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KM65JQR3YJ56GD2CMTPKQR0H
  • webhookWorkflow | wrun_01KM65JZZNJXF69AG6CBWMWX0J
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KM65R4DVX37DQSS33JSPCJFR

redis (2 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KM65JQR3YJ56GD2CMTPKQR0H
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KM65R4DVX37DQSS33JSPCJFR

turso (51 failed):

  • addTenWorkflow | wrun_01KM65HPC21GTA2T927WGAVAMH
  • addTenWorkflow | wrun_01KM65HPC21GTA2T927WGAVAMH
  • wellKnownAgentWorkflow (.well-known/agent) | wrun_01KM65KD3YX2DCRVG2A165QG2Y
  • should work with react rendering in step
  • promiseAllWorkflow | wrun_01KM65HX4NYJ5DPY3Q6W3AJKDB
  • promiseRaceWorkflow | wrun_01KM65J18G2N820ZDBKWFYTJN8
  • promiseAnyWorkflow | wrun_01KM65J30314T83736Q58ETZB6
  • importedStepOnlyWorkflow | wrun_01KM65KS7G1W4BQ221RE5TPGWB
  • hookWorkflow | wrun_01KM65JEGMF9KA6SAJDT5D1A7R
  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KM65JQR3YJ56GD2CMTPKQR0H
  • webhookWorkflow | wrun_01KM65JZZNJXF69AG6CBWMWX0J
  • sleepingWorkflow | wrun_01KM65K63VT6C5466GRKEQ22B7
  • parallelSleepWorkflow | wrun_01KM65KJ2BZ1JT0A4QZ53X032V
  • nullByteWorkflow | wrun_01KM65KP95T95QBRT153QTH8PT
  • workflowAndStepMetadataWorkflow | wrun_01KM65KR1RD3KSS9E98VAZWQ2Y
  • fetchWorkflow | wrun_01KM65MPKP5D6JGT0624WB24B0
  • promiseRaceStressTestWorkflow | wrun_01KM65MSY54TJ0G3JKAM1T1HFD
  • 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()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_01KM65QJ469KJZ5DCJK3X216K5
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KM65R4DVX37DQSS33JSPCJFR
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_01KM65RPRQDPFEGQTVM30W6N6C
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_01KM65SAEPZ6Z8EBJD5XA7H6VT
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_01KM65SJX6WY2X7DN6WJBY9BZA
  • closureVariableWorkflow - nested step functions with closure variables | wrun_01KM65SQJ6PP4KCDK6C2TXPW92
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_01KM65SSAJK3S84KPXH85QN5MF
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_01KM65T6BKJKFYESF2PSVR1BWP
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_01KM65TBQZCQY4A7CRSWCSFWQQ
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_01KM65THCKS1SD9JRH4G8AH1DQ
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_01KM65TPYWJ4M6SWCEAFS87AW0
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_01KM65TWP5ZCJTB4PZQFPGC5A5
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_01KM65V2FZ54ZAX0SREQJYXZ8G
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_01KM65V8SRBKQ4JXCW62WWTJCM
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KM65VJ5ATMC2VKJF6H4T8TRP
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KM65VRXG1XKJD4QVDYD0E1YW
  • cancelRun - cancelling a running workflow | wrun_01KM65W1YE311WRK2Y1ZMFDKFT
  • cancelRun via CLI - cancelling a running workflow | wrun_01KM65WAQQGQTSWNXJWHYPT9W8
  • 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_01KM65WNQS27CVSS6NA7JQ4CZC
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KM65X87N4E5CBC6Q82KPA14E
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KM65XHQRNZWGRJ166B7C8012

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 68 0 7
✅ example 68 0 7
❌ express 67 1 7
✅ fastify 68 0 7
✅ hono 68 0 7
✅ nextjs-turbopack 73 0 2
✅ nextjs-webpack 73 0 2
✅ nitro 68 0 7
❌ nuxt 67 1 7
✅ sveltekit 68 0 7
✅ vite 68 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 66 0 9
✅ express-stable 66 0 9
✅ fastify-stable 66 0 9
✅ hono-stable 66 0 9
✅ nextjs-turbopack-canary 55 0 20
✅ nextjs-turbopack-stable 72 0 3
✅ nextjs-webpack-canary 55 0 20
✅ nextjs-webpack-stable 72 0 3
✅ nitro-stable 66 0 9
✅ nuxt-stable 66 0 9
✅ sveltekit-stable 66 0 9
✅ vite-stable 66 0 9
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 66 0 9
✅ express-stable 66 0 9
✅ fastify-stable 66 0 9
✅ hono-stable 66 0 9
✅ nextjs-turbopack-canary 55 0 20
✅ nextjs-turbopack-stable 72 0 3
✅ nextjs-webpack-canary 55 0 20
✅ nextjs-webpack-stable 72 0 3
✅ nitro-stable 66 0 9
✅ nuxt-stable 66 0 9
✅ sveltekit-stable 66 0 9
✅ vite-stable 66 0 9
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 66 0 9
✅ express-stable 66 0 9
✅ fastify-stable 66 0 9
✅ hono-stable 66 0 9
✅ nextjs-turbopack-canary 55 0 20
✅ nextjs-turbopack-stable 72 0 3
✅ nextjs-webpack-canary 55 0 20
✅ nextjs-webpack-stable 72 0 3
✅ nitro-stable 66 0 9
✅ nuxt-stable 66 0 9
✅ sveltekit-stable 66 0 9
✅ vite-stable 66 0 9
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 72 0 3
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 2
❌ mongodb 52 3 3
✅ redis-dev 3 0 2
❌ redis 53 2 3
✅ turso-dev 3 0 2
❌ turso 4 51 3
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 66 0 9
✅ e2e-local-postgres-nest-stable 66 0 9
✅ e2e-local-prod-nest-stable 66 0 9

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 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.039s (-13.4% 🟢) 1.005s (~) 0.966s 10 1.00x
💻 Local Nitro 0.045s (-5.1% 🟢) 1.005s (~) 0.960s 10 1.16x
🐘 Postgres Nitro 0.051s (-26.1% 🟢) 1.011s (~) 0.960s 10 1.32x
🐘 Postgres Express 0.065s (-8.0% 🟢) 1.014s (~) 0.949s 10 1.69x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 0.478s (-17.0% 🟢) 2.571s (+0.6%) 2.093s 10 1.00x
▲ Vercel Nitro 0.503s (+1.4%) 2.470s (-2.9%) 1.967s 10 1.05x
▲ Vercel Next.js (Turbopack) 0.535s (-25.8% 🟢) 2.576s (~) 2.041s 10 1.12x

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

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.098s (-2.7%) 2.005s (~) 0.907s 10 1.00x
🐘 Postgres Nitro 1.124s (-1.6%) 2.013s (~) 0.889s 10 1.02x
💻 Local Nitro 1.127s (~) 2.006s (~) 0.879s 10 1.03x
🐘 Postgres Express 1.167s (+2.4%) 2.013s (~) 0.846s 10 1.06x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.039s (-12.2% 🟢) 3.688s (-3.2%) 1.649s 10 1.00x
▲ Vercel Express 2.096s (-5.0% 🟢) 3.756s (-3.5%) 1.660s 10 1.03x
▲ Vercel Nitro 2.118s (-9.4% 🟢) 3.519s (-9.2% 🟢) 1.401s 10 1.04x

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

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 10.658s (-2.3%) 11.023s (~) 0.365s 3 1.00x
🐘 Postgres Nitro 10.801s (-1.3%) 11.041s (~) 0.240s 3 1.01x
💻 Local Nitro 10.893s (~) 11.023s (~) 0.131s 3 1.02x
🐘 Postgres Express 10.984s (+0.7%) 11.379s (+3.1%) 0.395s 3 1.03x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 16.596s (-4.5%) 18.278s (-5.6% 🟢) 1.681s 2 1.00x
▲ Vercel Next.js (Turbopack) 16.867s (-5.0% 🟢) 18.385s (-7.1% 🟢) 1.518s 2 1.02x
▲ Vercel Nitro 17.600s (+1.2%) 19.265s (+2.1%) 1.665s 2 1.06x

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

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 14.205s (-5.3% 🟢) 15.029s (-3.2%) 0.824s 4 1.00x
🐘 Postgres Nitro 14.208s (-4.1%) 15.043s (~) 0.835s 4 1.00x
🐘 Postgres Express 14.797s (+1.5%) 15.044s (~) 0.247s 4 1.04x
💻 Local Nitro 14.936s (~) 15.028s (~) 0.092s 4 1.05x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 31.532s (-6.6% 🟢) 33.377s (-6.2% 🟢) 1.845s 2 1.00x
▲ Vercel Next.js (Turbopack) 33.281s (+2.5%) 34.807s (~) 1.526s 2 1.06x
▲ Vercel Nitro 33.642s (+4.1%) 34.892s (~) 1.249s 2 1.07x

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

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 13.117s (-8.7% 🟢) 14.038s (-6.7% 🟢) 0.921s 7 1.00x
🐘 Postgres Express 14.276s (+1.9%) 15.042s (+4.0%) 0.766s 6 1.09x
💻 Local Express 14.920s (-10.6% 🟢) 15.028s (-11.8% 🟢) 0.108s 6 1.14x
💻 Local Nitro 16.525s (~) 17.030s (~) 0.505s 6 1.26x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 54.307s (-5.9% 🟢) 56.428s (-5.1% 🟢) 2.121s 2 1.00x
▲ Vercel Nitro 54.508s (-3.1%) 55.907s (-2.4%) 1.399s 2 1.00x
▲ Vercel Next.js (Turbopack) 57.264s (-8.8% 🟢) 58.728s (-8.9% 🟢) 1.464s 2 1.05x

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

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.208s (-6.6% 🟢) 2.011s (~) 0.803s 15 1.00x
🐘 Postgres Express 1.286s (+1.6%) 2.011s (~) 0.725s 15 1.06x
💻 Local Express 1.466s (-2.7%) 2.006s (~) 0.540s 15 1.21x
💻 Local Nitro 1.510s (+1.8%) 2.005s (~) 0.495s 15 1.25x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.244s (-14.3% 🟢) 3.567s (-15.1% 🟢) 1.323s 9 1.00x
▲ Vercel Express 2.291s (-17.0% 🟢) 3.774s (-18.2% 🟢) 1.484s 8 1.02x
▲ Vercel Next.js (Turbopack) 2.591s (+4.0%) 4.031s (+3.3%) 1.440s 8 1.15x

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

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.357s (-4.6%) 3.012s (~) 0.655s 10 1.00x
🐘 Postgres Express 2.469s (~) 3.013s (~) 0.544s 10 1.05x
💻 Local Express 2.598s (-13.3% 🟢) 3.008s (-15.6% 🟢) 0.409s 10 1.10x
💻 Local Nitro 2.969s (+3.4%) 3.564s (+14.7% 🔺) 0.595s 9 1.26x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.469s (-2.7%) 3.767s (-1.1%) 1.298s 8 1.00x
▲ Vercel Next.js (Turbopack) 2.757s (+4.8%) 4.238s (+5.3% 🔺) 1.481s 8 1.12x
▲ Vercel Express 2.805s (-1.8%) 4.495s (+0.7%) 1.690s 7 1.14x

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

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.519s (-3.1%) 4.014s (~) 0.495s 8 1.00x
🐘 Postgres Express 3.647s (+1.6%) 4.014s (~) 0.368s 8 1.04x
💻 Local Express 7.044s (-16.9% 🟢) 7.515s (-16.7% 🟢) 0.471s 4 2.00x
💻 Local Nitro 8.520s (+4.9%) 9.022s (+2.9%) 0.502s 4 2.42x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.749s (-9.7% 🟢) 3.978s (-11.0% 🟢) 1.229s 8 1.00x
▲ Vercel Express 3.144s (-4.3%) 4.417s (-5.3% 🟢) 1.272s 7 1.14x
▲ Vercel Next.js (Turbopack) 3.397s (+1.3%) 5.076s (+1.3%) 1.679s 6 1.24x

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

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.199s (-6.1% 🟢) 2.010s (~) 0.812s 15 1.00x
🐘 Postgres Express 1.284s (+1.9%) 2.012s (~) 0.728s 15 1.07x
💻 Local Express 1.486s (-3.3%) 2.005s (~) 0.519s 15 1.24x
💻 Local Nitro 1.542s (-1.1%) 2.005s (~) 0.463s 15 1.29x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.197s (+3.4%) 3.731s (+1.0%) 1.533s 9 1.00x
▲ Vercel Next.js (Turbopack) 2.202s (-15.4% 🟢) 3.705s (-7.6% 🟢) 1.503s 9 1.00x
▲ Vercel Express 2.468s (+11.7% 🔺) 3.896s (+0.7%) 1.428s 8 1.12x

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

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.337s (-4.9%) 3.012s (~) 0.674s 10 1.00x
🐘 Postgres Express 2.475s (+1.0%) 3.013s (~) 0.538s 10 1.06x
💻 Local Express 2.861s (-5.7% 🟢) 3.108s (-17.3% 🟢) 0.247s 10 1.22x
💻 Local Nitro 3.013s (-0.5%) 3.565s (-3.0%) 0.552s 9 1.29x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.501s (-6.6% 🟢) 4.089s (-11.3% 🟢) 1.588s 8 1.00x
▲ Vercel Next.js (Turbopack) 3.610s (+51.7% 🔺) 4.889s (+33.5% 🔺) 1.279s 7 1.44x
▲ Vercel Nitro 3.623s (+40.0% 🔺) 4.920s (+27.4% 🔺) 1.297s 8 1.45x

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

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 3.498s (-3.3%) 4.014s (~) 0.517s 8 1.00x
🐘 Postgres Express 3.645s (+1.2%) 4.014s (~) 0.369s 8 1.04x
💻 Local Express 8.022s (-11.4% 🟢) 8.517s (-12.9% 🟢) 0.495s 4 2.29x
💻 Local Nitro 9.052s (+4.7%) 9.522s (+5.5% 🔺) 0.470s 4 2.59x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.716s (-11.5% 🟢) 4.133s (-9.0% 🟢) 1.417s 8 1.00x
▲ Vercel Nitro 2.721s (-3.8%) 3.843s (-13.3% 🟢) 1.123s 8 1.00x
▲ Vercel Next.js (Turbopack) 3.388s (-13.6% 🟢) 5.100s (-7.1% 🟢) 1.712s 6 1.25x

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

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.635s (-29.2% 🟢) 1.009s (-3.3%) 0.373s 60 1.00x
💻 Local Express 0.686s (-30.2% 🟢) 1.004s (-8.3% 🟢) 0.318s 60 1.08x
🐘 Postgres Express 0.893s (+3.1%) 1.009s (-1.6%) 0.117s 60 1.41x
💻 Local Nitro 0.973s (+1.8%) 1.057s (+3.5%) 0.084s 57 1.53x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 9.396s (-10.5% 🟢) 11.078s (-10.1% 🟢) 1.681s 6 1.00x
▲ Vercel Express 9.581s (-3.6%) 11.201s (-4.2%) 1.620s 6 1.02x
▲ Vercel Nitro 9.611s (+0.7%) 10.983s (~) 1.372s 6 1.02x

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

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.528s (-29.3% 🟢) 2.010s (-32.5% 🟢) 0.481s 45 1.00x
🐘 Postgres Express 2.201s (+4.0%) 3.012s (~) 0.811s 30 1.44x
💻 Local Express 2.230s (-26.0% 🟢) 3.008s (-15.2% 🟢) 0.778s 30 1.46x
💻 Local Nitro 2.987s (+1.1%) 3.416s (+9.8% 🔺) 0.428s 27 1.95x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 30.454s (-1.8%) 31.741s (-2.3%) 1.286s 3 1.00x
▲ Vercel Express 31.916s (-4.0%) 33.599s (-4.4%) 1.684s 3 1.05x
▲ Vercel Next.js (Turbopack) 34.619s (+6.0% 🔺) 36.433s (+6.6% 🔺) 1.814s 3 1.14x

🔍 Observability: Nitro | Express | 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.126s (-27.9% 🟢) 4.013s (-20.0% 🟢) 0.887s 30 1.00x
🐘 Postgres Express 4.477s (+5.0% 🔺) 5.015s (+0.8%) 0.537s 24 1.43x
💻 Local Express 7.371s (-19.5% 🟢) 8.014s (-18.1% 🟢) 0.644s 15 2.36x
💻 Local Nitro 9.132s (+1.1%) 9.710s (+2.4%) 0.577s 13 2.92x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 85.171s (-7.7% 🟢) 86.895s (-7.8% 🟢) 1.724s 2 1.00x
▲ Vercel Express 88.698s (+0.7%) 89.966s (~) 1.268s 2 1.04x
▲ Vercel Next.js (Turbopack) 89.689s (-7.9% 🟢) 91.485s (-7.9% 🟢) 1.796s 2 1.05x

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

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.234s (-20.4% 🟢) 1.009s (~) 0.774s 60 1.00x
🐘 Postgres Express 0.301s (~) 1.009s (~) 0.708s 60 1.29x
💻 Local Express 0.575s (-4.6%) 1.004s (~) 0.429s 60 2.45x
💻 Local Nitro 0.583s (-2.4%) 1.004s (~) 0.421s 60 2.49x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.884s (-0.8%) 3.617s (+1.1%) 1.732s 17 1.00x
▲ Vercel Nitro 1.886s (-18.7% 🟢) 3.649s (-5.6% 🟢) 1.763s 17 1.00x
▲ Vercel Express 2.782s (+10.8% 🔺) 4.419s (+9.7% 🔺) 1.638s 14 1.48x

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

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.414s (-23.1% 🟢) 1.008s (~) 0.594s 90 1.00x
🐘 Postgres Express 0.561s (+6.4% 🔺) 1.010s (~) 0.449s 90 1.35x
💻 Local Express 2.452s (-5.0% 🟢) 3.007s (~) 0.556s 30 5.92x
💻 Local Nitro 2.564s (+3.6%) 3.009s (~) 0.445s 30 6.19x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.685s (+36.2% 🔺) 6.113s (+27.1% 🔺) 1.428s 15 1.00x
▲ Vercel Nitro 5.262s (+81.7% 🔺) 6.642s (+57.5% 🔺) 1.380s 14 1.12x
▲ Vercel Express 5.757s (+87.0% 🔺) 7.278s (+61.8% 🔺) 1.521s 13 1.23x

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

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.598s (-36.3% 🟢) 1.009s (-15.4% 🟢) 0.411s 119 1.00x
🐘 Postgres Express 0.933s (+4.0%) 1.147s (+10.2% 🔺) 0.214s 105 1.56x
💻 Local Express 10.708s (-5.1% 🟢) 11.116s (-7.6% 🟢) 0.409s 11 17.91x
💻 Local Nitro 11.259s (+1.3%) 12.028s (+3.1%) 0.769s 10 18.83x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 8.368s (+6.7% 🔺) 10.131s (+8.1% 🔺) 1.764s 13 1.00x
▲ Vercel Nitro 8.480s (+1.4%) 10.167s (+4.3%) 1.687s 12 1.01x
▲ Vercel Express 9.656s (+32.8% 🔺) 11.542s (+29.0% 🔺) 1.886s 11 1.15x

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

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.143s (-30.1% 🟢) 1.003s (~) 0.010s (-14.3% 🟢) 1.016s (~) 0.873s 10 1.00x
🐘 Postgres Nitro 0.163s (-32.0% 🟢) 1.000s (+0.8%) 0.001s (-21.4% 🟢) 1.012s (~) 0.850s 10 1.14x
💻 Local Nitro 0.199s (+1.5%) 1.003s (~) 0.011s (-5.1% 🟢) 1.017s (~) 0.818s 10 1.40x
🐘 Postgres Express 0.215s (-0.8%) 0.996s (~) 0.001s (+8.3% 🔺) 1.013s (~) 0.798s 10 1.50x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 1.534s (-7.1% 🟢) 1.918s (-28.5% 🟢) 0.005s (-16.7% 🟢) 2.515s (-23.2% 🟢) 0.981s 10 1.00x
▲ Vercel Express 1.633s (-3.2%) 2.664s (+0.9%) 0.004s (-23.6% 🟢) 3.251s (~) 1.617s 10 1.06x
▲ Vercel Next.js (Turbopack) 1.760s (+1.8%) 2.692s (-9.9% 🟢) 0.006s (-6.6% 🟢) 3.283s (-8.7% 🟢) 1.524s 10 1.15x

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

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.581s (-25.1% 🟢) 1.009s (-1.8%) 0.008s (-14.5% 🟢) 1.021s (-1.9%) 0.440s 59 1.00x
🐘 Postgres Nitro 0.590s (-18.3% 🟢) 1.006s (~) 0.003s (-11.7% 🟢) 1.024s (~) 0.434s 59 1.02x
🐘 Postgres Express 0.703s (+1.3%) 1.005s (~) 0.004s (~) 1.027s (~) 0.324s 59 1.21x
💻 Local Nitro 0.733s (+0.9%) 1.009s (~) 0.010s (+9.8% 🔺) 1.024s (~) 0.291s 59 1.26x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 4.074s (-5.9% 🟢) 5.333s (-3.2%) 0.432s (+65.2% 🔺) 6.290s (~) 2.216s 10 1.00x
▲ Vercel Express 4.365s (~) 5.441s (-2.4%) 0.249s (-6.7% 🟢) 6.407s (~) 2.042s 10 1.07x
▲ Vercel Next.js (Turbopack) 4.544s (~) 5.611s (-3.0%) 0.250s (+24.6% 🔺) 6.490s (-0.6%) 1.946s 10 1.12x

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

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.804s (-12.7% 🟢) 1.050s (-1.2%) 0.000s (-100.0% 🟢) 1.070s (-1.0%) 0.266s 57 1.00x
🐘 Postgres Express 0.918s (+2.8%) 1.147s (+5.9% 🔺) 0.000s (-100.0% 🟢) 1.164s (+5.4% 🔺) 0.245s 52 1.14x
💻 Local Express 1.220s (-3.1%) 2.018s (~) 0.000s (-41.2% 🟢) 2.021s (~) 0.801s 30 1.52x
💻 Local Nitro 1.248s (+1.9%) 2.019s (~) 0.001s (+46.2% 🔺) 2.023s (~) 0.775s 30 1.55x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.630s (+10.0% 🔺) 3.676s (+11.8% 🔺) 0.000s (+Infinity% 🔺) 4.333s (+9.9% 🔺) 1.704s 14 1.00x
▲ Vercel Nitro 2.644s (+4.1%) 3.504s (+9.0% 🔺) 0.000s (-100.0% 🟢) 4.069s (+8.2% 🔺) 1.425s 15 1.01x
▲ Vercel Next.js (Turbopack) 2.828s (-3.5%) 3.780s (-5.2% 🟢) 0.000s (-75.0% 🟢) 4.337s (-5.5% 🟢) 1.509s 14 1.08x

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

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

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.650s (-12.8% 🟢) 2.105s (-5.2% 🟢) 0.000s (-100.0% 🟢) 2.123s (-5.2% 🟢) 0.474s 29 1.00x
🐘 Postgres Express 1.888s (+4.6%) 2.172s (+3.5%) 0.000s (+Infinity% 🔺) 2.210s (+4.2%) 0.322s 28 1.14x
💻 Local Nitro 3.430s (+3.0%) 3.967s (~) 0.001s (+120.0% 🔺) 3.974s (~) 0.544s 16 2.08x
💻 Local Express 3.597s (+4.5%) 4.099s (~) 0.001s (~) 4.103s (~) 0.506s 15 2.18x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -
🐘 Postgres Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.390s (~) 4.349s (-4.2%) 0.000s (-53.8% 🟢) 4.922s (-3.2%) 1.532s 13 1.00x
▲ Vercel Express 3.712s (+12.9% 🔺) 4.694s (+2.7%) 0.001s (+166.7% 🔺) 5.335s (+3.8%) 1.623s 12 1.09x
▲ Vercel Next.js (Turbopack) 4.217s (-3.4%) 5.113s (-5.3% 🟢) 0.000s (-62.5% 🟢) 5.681s (-4.7%) 1.463s 11 1.24x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Express 20/21
🐘 Postgres Nitro 21/21
▲ Vercel Nitro 9/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 12/21
Next.js (Turbopack) ▲ Vercel 21/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

@vercel
Copy link
Contributor

vercel bot commented Mar 20, 2026

…itecture-d-2026-02-27T14-07-52-1.png from the repo root, workbench/fastify/public/index.html (a Nitro example mistakenly placed in the fastify workbench by a ploop checkpoint), all .claude/worktrees/* submodule references, and all 15 string-presence audit guard tests in packages/docs-typecheck/src/__tests__/ (they only assert keyword presence, not semantic correctness). None of these belong in the docs audit PR.
Copy link
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.

LGTM! Very helpful. I left a bunch of small feedback to address and then it's good to go


## Machine-Readable Surfaces

For automations, custom dashboards, and agentic tooling, prefer stable fields and lifecycle events over parsing human-readable UI labels.
Copy link
Member

Choose a reason for hiding this comment

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

Was someone trying to parse UI labels? I don't feel like I know what this is about as someone just reading the observability docs

npx workflow inspect runs
```

## Machine-Readable Surfaces
Copy link
Member

Choose a reason for hiding this comment

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

This section seems misplaced, move it to bottom of this file?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah this whole section seems unnecessary imo

Copy link
Collaborator

Choose a reason for hiding this comment

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

it belongs in the world docs I think and it's already documented there

```

<Callout type="info">
This is the same mechanism that [`withWorkflow()`](/docs/api-reference/workflow-next/with-workflow#monorepos-and-workspace-imports) uses in Next.js — it reads `outputFileTracingRoot` from the Next.js config and passes it as `projectRoot` to the builder. Without this, workflow transforms may fail to resolve workspace module specifiers.
Copy link
Member

Choose a reason for hiding this comment

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

This might not be relevant for someone reading this section?

- `correlationId` links together events for the same entity lifecycle
- `requestId` tells you which inbound platform request created or updated the event

On Vercel, `requestId` comes from the platform request ID when available.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
On Vercel, `requestId` comes from the platform request ID when available.
On Vercel, `requestId` is the platform request ID when available.

Copy link
Collaborator

@pranaygp pranaygp Mar 20, 2026

Choose a reason for hiding this comment

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

we should also mention this is optional for metrics since only vercel implements this. Other worlds are not expected to have a requestId

Unlike other entities, hooks don't have a `status` field—the states above are conceptual. An "active" hook is one that exists in storage, while "disposed" means the hook has been deleted. When a `hook_disposed` event is created, the hook record is removed rather than updated.

While a hook is active, its token is reserved and cannot be used by other workflows. If a workflow attempts to create a hook with a token that is already in use by another active hook, a `hook_conflict` event is recorded instead of `hook_created`. This causes the hook's promise to reject with a `WorkflowRuntimeError`, failing the workflow gracefully. See the [hook-conflict error](/docs/errors/hook-conflict) documentation for more details.
While a hook is active, its token is reserved and cannot be used by other workflows. If a workflow attempts to create a hook with a token that is already in use by another active hook, a `hook_conflict` event is recorded instead of `hook_created`. This causes the hook's promise to reject with a `HookConflictError`, which you can detect with `HookConflictError.is(error)`. See the [hook-conflict error](/docs/errors/hook-conflict) documentation for more details.
Copy link
Member

Choose a reason for hiding this comment

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

The part we're cutting out here is that this would bubble up as a WorkflowRuntimeError.

Suggested change
While a hook is active, its token is reserved and cannot be used by other workflows. If a workflow attempts to create a hook with a token that is already in use by another active hook, a `hook_conflict` event is recorded instead of `hook_created`. This causes the hook's promise to reject with a `HookConflictError`, which you can detect with `HookConflictError.is(error)`. See the [hook-conflict error](/docs/errors/hook-conflict) documentation for more details.
While a hook is active, its token is reserved and cannot be used by other workflows. If a workflow attempts to create a hook with a token that is already in use by another active hook, a `hook_conflict` event is recorded instead of `hook_created`. This causes the hook's promise to reject with a `HookConflictError`, which you can detect with `HookConflictError.is(error)`. When unhandled, this fails the workflow with a `WorkflowRuntimeError`. See the [hook-conflict error](/docs/errors/hook-conflict) documentation for more details.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@VaguelySerious I don't think this becomes a WorkflowRuntimeError. It should just fail the workflow run if HookConflictError is unhandled in the workflow context.

WorkflowRuntimeError only happens when something is broken in the world/workflow/queue internals like a corrupted event log, etc.

Copy link
Collaborator

Choose a reason for hiding this comment

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

change was made here: #1448


Tool calls can be implemented as workflow steps for automatic retries, or as regular workflow-level logic utilizing core library features such as [`sleep()`](/docs/api-reference/workflow/sleep) and [Hooks](/docs/foundations/hooks).

{/* @skip-typecheck - uses DurableAgentOptions properties not yet in published dist types */}
Copy link
Member

Choose a reason for hiding this comment

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

I feel like CI was previously passing. Why are we adding all these typecheck skips? If they're not needed, we should do without

Copy link
Collaborator

Choose a reason for hiding this comment

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

+1 - it was already passing on latest main

- The agent processes tool calls iteratively until completion or `maxSteps` is reached
- **Default `maxSteps` is unlimited** - set a value to limit the number of LLM calls
- The `stream()` method returns `{ messages, steps, experimental_output, uiMessages }` containing the full conversation history, step details, optional structured output, and optionally accumulated UI messages
- The `stream()` method returns `{ messages, steps, toolCalls, toolResults, experimental_output, uiMessages }` containing the full conversation history, per-step details, machine-readable tool activity, optional structured output, and optionally accumulated UI messages
Copy link
Member

Choose a reason for hiding this comment

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

machine-readable tool activity

What is this? Did you mean tool call information?

Suggested change
- The `stream()` method returns `{ messages, steps, toolCalls, toolResults, experimental_output, uiMessages }` containing the full conversation history, per-step details, machine-readable tool activity, optional structured output, and optionally accumulated UI messages
- The `stream()` method returns `{ messages, steps, toolCalls, toolResults, experimental_output, uiMessages }` containing the full conversation history, step details, tool call details, optional structured output, and optionally accumulated UI messages


### List Workflow Runs (Display Names)

List workflow runs and derive user-friendly names from the machine-readable `workflowName` field:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
List workflow runs and derive user-friendly names from the machine-readable `workflowName` field:
List workflow runs and derive human-readable names from the `workflowName` field:

import { sendReminder } from "./workflows/send-reminder";

export async function POST() {
await start(async () => sendReminder("hello@example.com"));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
await start(async () => sendReminder("hello@example.com"));
// Does NOT work
await start(async () => sendReminder("hello@example.com"));


### Aborting Long-Running Streams

Use `timeout` to abort a stream automatically after a fixed duration. If both `timeout` and `abortSignal` are provided, whichever triggers first will abort the operation:
Copy link
Collaborator

Choose a reason for hiding this comment

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

AbortSignal won't actually work till we implement #1301

I haven't tried timeout. might need to have dig in and validate that it actually works too (and should add an e2e test maybe also if possible)?

import { withWorkflow } from "workflow/next";

const nextConfig: NextConfig = {
outputFileTracingRoot: resolve(process.cwd(), "../.."),
Copy link
Collaborator

Choose a reason for hiding this comment

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

@ijjk can you validate is this is right? 🤔

Copy link
Member

Choose a reason for hiding this comment

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

by default Next.js should detect the right value we should only mention this as a workaround if things are detecting correctly


| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `workflows.lazyDiscovery` | `boolean` | `false` | When `true`, defers workflow discovery until files are requested instead of scanning eagerly at startup. Useful for large projects where startup time matters. |
Copy link
Collaborator

Choose a reason for hiding this comment

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

lazyDiscovery is actually something that @ijjk wants to ship as the default for GA


## Options

`withWorkflow` accepts an optional second argument to control local development behavior.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
`withWorkflow` accepts an optional second argument to control local development behavior.
`withWorkflow` accepts an optional second argument to configure the Next.js integration.

it's not development behavior specific

### Configure NestJS for ESM
### Choose Your Module Format

NestJS with SWC uses ES modules. Add `"type": "module"` to your `package.json`:
Copy link
Collaborator

Choose a reason for hiding this comment

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

@VaguelySerious can you validate the NestJS changes here?

Copy link
Member

@VaguelySerious VaguelySerious Mar 20, 2026

Choose a reason for hiding this comment

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

Yes this looks right. A community PR added these attributes at some point

Comment on lines +51 to +53
<Callout type="info">
If your Next.js app lives inside a monorepo and your workflows import code from sibling workspace packages, set `outputFileTracingRoot` to the workspace root. See [`withWorkflow()`](/docs/api-reference/workflow-next/with-workflow#monorepos-and-workspace-imports) for the full example.
</Callout>
Copy link
Collaborator

Choose a reason for hiding this comment

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

fyi @ijjk

Comment on lines +51 to +57
<Callout type="info">
If your Next.js app lives inside a monorepo and your workflows import code from sibling workspace packages, set `outputFileTracingRoot` to the workspace root. See [`withWorkflow()`](/docs/api-reference/workflow-next/with-workflow#monorepos-and-workspace-imports) for the full example.
</Callout>

<Callout type="info">
You can pass a second argument to `withWorkflow()` to control local development behavior, such as enabling lazy workflow discovery or overriding the local port. See [`withWorkflow()` Options](/docs/api-reference/workflow-next/with-workflow#options) for details.
</Callout>
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's drop this callout honestly. let's not make agents use this unless they actually are trying to debug something so let's not load it into context so early

Comment on lines +292 to 307
### `start()` says it received an invalid workflow function

If you see this error:

```
'start' received an invalid workflow function. Ensure the Workflow Development Kit is configured correctly and the function includes a 'use workflow' directive.
```

Check both of these first:

1. The workflow function includes `"use workflow"`.
2. Your `next.config.ts` is wrapped with [`withWorkflow()`](/docs/api-reference/workflow-next/with-workflow).

See [start-invalid-workflow-function](/docs/errors/start-invalid-workflow-function) for full examples and fixes.

## Next Steps
Copy link
Collaborator

Choose a reason for hiding this comment

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

should a version of this go into every getting started guide then? not just next.js only

let server: ChildProcess | null = null;
const PORT = "4000";

function emitSetupLog(event: string, fields: Record<string, unknown> = {}) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

hmm why are we changing this? not sure I follow

@VaguelySerious your call on this if this seems right

Copy link
Member

Choose a reason for hiding this comment

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

It looks like this is just a log wrapper that helps agents debug the setup, which I don't oppose. It is a lot of stuff to write into the testing docs, but we don't recommend server-based test anyway since we have the vitest setup, so fine IMO

Comment on lines +161 to 162
{/* @skip-typecheck - uses getRun().wakeUp() not yet in published dist types */}
```typescript title="workflows/calculate.server.test.ts" lineNumbers
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's drop the skip type checks please. we need confidence that the docs are actually type checked.

if .wakeUp is not published, that's actually a real bug

I think something's wrong with your local cache @johnlindquist and you just need to build the monorepo so that the docs typecheck works

To add import inference for new symbols, edit `src/import-inference.ts`:
The docs test suite includes:
- `docs/content/docs/**/*.mdx`
- `packages/*/README.md`
Copy link
Collaborator

Choose a reason for hiding this comment

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

we should also do this for the SKILL.md actually @johnlindquist

do you wanna extend the docs type checking to validate the skills so that the skills are accurate too? 🙏🏼

Copy link
Collaborator

@pranaygp pranaygp left a comment

Choose a reason for hiding this comment

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

thank you so much! this is great. lett manual comments of things we need to fix. excited to ship this after

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