Skip to content

[docs] v5 stable docs - #3100

Open
VaguelySerious wants to merge 19 commits into
mainfrom
peter/docs-5-ga
Open

[docs] v5 stable docs#3100
VaguelySerious wants to merge 19 commits into
mainfrom
peter/docs-5-ga

Conversation

@VaguelySerious

@VaguelySerious VaguelySerious commented Jul 24, 2026

Copy link
Copy Markdown
Member

Makes v5 the default documentation version for the 5.0.0 GA. Companion to #3090 (exit changeset pre mode) and #3091 (pin 4.x publishes to previous).

Version flip and routing

content/docs/v5 and content/worlds/v5 now serve unprefixed — /docs, /worlds, /cookbook — and v4 moves under a /v4 prefix. The versioned source config drives both route trees (the root tree always renders versionedSources.current), so this is a routePrefix move plus source re-binding, not a restructure. The only file moves are app/[lang]/v5/**app/[lang]/v4/**.

  • Version switcher: labels are v5 (Latest) / v4 (Maintenance); current: 'v5'.
  • Banner: pre-release-banner.tsxmaintenance-banner.tsx. v4 pages carry an amber notice (with dark-mode styling) whose "Go to Workflow 5 (Latest)" link deep-links to the same page on the current version; v4-only pages land on the nearest section index via the fallback redirects. v4 pages keep robots: noindex, follow.
  • Redirects (docs/next.config.ts): /v5/* → unprefixed equivalent (permanent; bare /v5 gets its own rule because :path* expands to an empty Location otherwise). The world-docs and api-reference restructure rules are mirrored onto /v4/docs/*, and every page that exists in only one tree has a version-switcher fallback (verified by diffing the two content trees).
  • Worlds routes: both the unprefixed and /v4 worlds routes pass an explicit version into the shared page components, whose semantics flipped with the switch — smoke checks (assertWorldVersionMarkers, assertServesDirectly('/worlds/turso')) now guard the pairing so the canonical /worlds/* pages can't silently serve the maintenance version or self-redirect-loop again.
  • Link lint (docs/scripts/lint.ts): the two-space model swaps with the URL space; redirect destinations resolve against the real HTTP space since redirects are matched before render-time href rewriting.
  • Intentional oddity: rewriteHrefForVersion pulls unprefixed hrefs on v4 pages into the /v4 view, so a /v5/... href is the only way for v4 content to point at a current page. Three such links are kept deliberately; the lint models this.
  • Sidebar: both docs layouts moved into their [[...slug]] segment so params.slug is available server-side, and the getting-started section no longer drills in on /docs/getting-started (where every bare /docs link lands). The per-navigation layout remount this causes is fine in practice — verified in a browser that geistdocs re-scrolls the active sidebar item into view after each navigation.

What's new page and migration skill

  • /docs/whats-new: new top-level page, first in the v5 sidebar. Leads with faster/cheaper runs (single-pass invocations, skipped persistence round trips, payload compression — up to 80% lower compute and 70% lower storage cost on Vercel Workflows), then multi-region, in-flight cancellation, attributes GA, richer serialization, and the redesigned trace viewer. A <CopyPrompt> after the install snippet gives a paste-ready agent prompt for the upgrade, and the Custom Worlds section opens with a World-upgrade prompt that diffs packages/world* between stable and main.
  • Breaking-change tables (application code vs. custom Worlds/build integrations) were built from the package diff against stable, then re-swept twice: once against the 5.0.0 draft changelog and once against post-branch main — picking up the late-landing GA breakers (createLocalWorld/createVercelWorld removal (Remove obsolete world factory aliases #3112), deterministic-sandbox hardening (feat(core): deterministic sandbox hardening #3045), step-queue-topic retirement).
  • skills/migrating-workflow-v4-to-v5 (v0.2.3): mechanical rewrite rules, behavior changes the agent must report rather than silently "fix", World contract changes, a fixed output shape, and a fail-the-migration checklist.
  • Env vars documented: WORKFLOW_MAX_EVENTS_OVERRIDE (Runtime Tuning › Limits), WORKFLOW_MAX_EVENTS (Worlds › Local), WORKFLOW_DISABLE_ANALYTICS_READS (Worlds › Vercel).

Full-tree staleness sweep

The whole v5 content tree (165 docs pages + worlds + cookbook) was audited against the actual v4→v5 delta, with every finding verified against SDK source. 30 files fixed in 44a53f0be:

  • Removed client SWC mode: code-transform.mdx restructured around the real v5 modes (step/workflow/detect) with a migration callout; framework-integrations.mdx and the NestJS guide now pass/describe mode: "step".
  • v4 execution model: passages describing one-step-per-queue-message ("enqueues the step for background execution", "the step function's HTTP request", "step execution requests") now describe inline execution with queue fallback — across foundations, how-it-works, tracing, and testing pages.
  • Building a World: the World interface listing gains the now-required specVersion, the v5 capabilities (preconditionGuard, maxConcurrency — with the fail-closed warning), analytics, createRunId (the multi-region hook), describeRun, experimentalSetAttributes, a required steps.get runId, and queue guidance for delaySeconds (a World that ignores it busy-loops every sleep()) and region.
  • Inverted/impossible claims: the precondition guard is default-on (not opt-in via =1); set-world/create-world no longer describe runtime WORKFLOW_TARGET_WORLD switching, which build-time World injection makes impossible.
  • Comparisons: child runs use start() directly from a workflow (six step-wrapped-start() rows across four pages), Temporal search attributes map to setAttributes(), multi-region is shipped.
  • Additions: compression-before-encryption in the encryption internals, AbortController/AbortSignal in workflow globals, region on start() (API reference + foundations), all eight run error codes including MAX_EVENTS_EXCEEDED, stream flush-window note.
  • Adjacent bugs: 14 samples awaited the synchronous createWebhook() (whose thenable handle resolves the received request), an unused import, a missing sidebar entry, and an undeclared class that failed snippet typecheck.

One deliberate soft-fix: the Python guide's "each step compiles into an isolated route" was made topology-neutral rather than corrected, pending confirmation of the Python SDK's route model.

Verification

  • pnpm test:docs: 972 documentation snippet typechecks pass (the snippet type-checker now covers @workflow/world-local, so the set-world example and the code-transform input samples are machine-checked instead of skipped).
  • bun ./scripts/lint.ts: 0 errored files, 0 errors — every link/anchor resolves in both URL spaces.
  • pnpm build: exit 0; key fixes confirmed in prerendered HTML (version markers, Detect Mode, error-code table, set-world example).
  • Docs smoke suite (including the new worlds-version and community-world guards) green against pnpm start; curl matrix over redirects and worlds routes (/worlds/vercel 200 v5-titled, /worlds/turso 200, /v4/worlds/turso single 307, /v5/* → unprefixed 308s, markdown + sitemap routes 200).
  • All Vercel toolbar review threads addressed and resolved; all code-review findings fixed.
  • Branch is merged with origin/main (factory-alias removal Remove obsolete world factory aliases #3112, sandbox hardening feat(core): deterministic sandbox hardening #3045, migration-guides redirects docs: redirect retired migration-guides URLs to comparisons #3127), so the GA-forward claims in the breaking-change tables are true in-tree and docs: redirect retired migration-guides URLs to comparisons #3127's redirect destinations pass this branch's lint.

Empty changeset — docs and skills only, no published package changes.

Docs Preview

Base: https://workflow-docs-git-peter-docs-5-ga.vercel.sh (Vercel SSO). One row per changed page.

New page

Page What changed
/docs/whats-new New top-level page: highlights, breaking-change tables, migration + World-upgrade prompts; first in the v5 sidebar

Foundations & observability

Page What changed
/docs/foundations/workflows-and-steps Suspension bullet describes inline step execution with queue fallback
/docs/foundations/errors-and-retries#error-codes Full run-error-code table, including the v5 MAX_EVENTS_EXCEEDED
/docs/foundations/streaming#best-practices Flush-window note added; stuck-lock warning reattributed from "step HTTP request" to the invocation
/docs/foundations/starting-workflows region option bullet with multi-region link
/docs/foundations/cancellation Cookbook link de-prefixed from /v5
/docs/observability/tracing#trace-shape-one-trace-per-invocation Queue-delivery list no longer claims every step completion is a delivery
/docs/observability Notes the Nitro /_workflow dev route

How it works

Page What changed
/docs/how-it-works/code-transform#the-three-transformation-modes Restructured around the v5 modes (step/workflow/detect); client-mode migration callout; step-mode tab shows the absorbed workflow-throw behavior
/docs/how-it-works/framework-integrations App-code transform section passes mode: "step"
/docs/how-it-works/understanding-directives Step-execution wording matches the inline model
/docs/how-it-works/encryption#compression New compress-before-encrypt section

API reference

Page What changed
/docs/api-reference/workflow-api/start#good-to-know region and attributes bullets
/docs/api-reference/workflow-errors/precondition-failed-error Guard documented as default-on (=0 opts out), not opt-in
/docs/api-reference/workflow-globals#web-platform-apis AbortController / AbortSignal added to the globals list
/docs/api-reference/workflow-runtime/create-world Build-time World injection framing (env var is a build-time selector)
/docs/api-reference/workflow-runtime/set-world Impossible runtime env-switch example replaced with explicit createWorld() injection
/docs/api-reference/workflow-runtime/world/queue QueueOptions lists region and specVersion
/docs/api-reference/workflow-runtime/world/storage steps.get runId param typed as required

Comparisons

Page What changed
/docs/comparisons/workflow-sdk-vs-temporal Child Workflow row uses direct in-workflow start(); search attributes now map to setAttributes()
/docs/comparisons/workflow-sdk-vs-inngest step.invoke() row uses direct start()
/docs/comparisons/workflow-sdk-vs-trigger-dev triggerAndWait() row uses direct start() + awaited Run
/docs/comparisons/workflow-sdk-vs-aws-step-functions Three step-wrapped start() rows (Map, child state machine, Distributed Map) use direct start()
/docs/comparisons/workflow-sdk-vs-cloudflare-workflows Multi-region described as shipped, with link

Getting started & testing

Page What changed
/docs/getting-started/nestjs .swcrc described as step-mode (client mode removed)
/docs/getting-started/python Per-step-route claim made topology-neutral
/docs/getting-started/next Troubleshooting version reference: beta → latest release
/docs/getting-started/react-router/v7 5.0.0-beta.335.0.0
/docs/getting-started/react-router/v8 5.0.0-beta.335.0.0
/docs/testing/server-based "Step execution requests" → flow-route requests

Configuration, deploying, errors, AI

Page What changed
/docs/configuration/runtime-tuning#limits New Limits section: WORKFLOW_MAX_EVENTS_OVERRIDE; /v5 link de-prefixed
/docs/configuration/worlds#workflow_max_events WORKFLOW_MAX_EVENTS (Local) and WORKFLOW_DISABLE_ANALYTICS_READS (Vercel) documented
/docs/deploying Multi-region beta version gate dropped
/docs/errors/webhook-response-not-sent createWebhook() samples no longer await the thenable handle
/docs/errors/webhook-invalid-respond-with-value Same createWebhook() fix
/docs/ai/defining-tools Unused import removed
/docs/ai Sidebar gains the existing message-queueing page (meta.json)
/docs/changelog/step-message-ownership /v5 links de-prefixed
/docs/internal + /docs/internal/nitro-web-ui Release wording (preview-only pages)

Worlds

Page What changed
/worlds/building-a-world#analytics-interface-optional v5 World interface (specVersion, capabilities, createRunId, describeRun, …), required steps.get runId, delaySeconds/region queue guidance, new Analytics section
/worlds/vercel#multi-region GA version references; serves v5 content at the canonical URL (version-flip fix)
/worlds/postgres Version-pairing callout rewritten for GA
/v4/worlds/vercel 5.0.0-beta.335.0.0 references; · v4 title + noindex

v4 (maintenance) docs

Page What changed
/v4/docs/deploying Multi-region callout version → 5.0.0; intentional /v5 cross-version link
/v4/docs/getting-started/next Troubleshooting version → workflow@4.0.1

Routing & chrome spot-checks

Check Link
/v5/* permanent redirect /v5/docs/getting-started
Bare /v5 redirect /v5
Community world serves directly (no redirect loop) /worlds/turso
Maintenance banner: dark mode + deep-link to same page /v4/docs/foundations
Sidebar: no drill-in on docs home /docs/getting-started
Sidebar: still drills in on section pages /docs/getting-started/next
Version-switcher fallback (v5-only page on v4) /v4/docs/whats-new

🤖 Generated with Claude Code

v5 goes stable, so the docs swap places: content/docs/v5 and
content/worlds/v5 now serve unprefixed (/docs, /worlds, /cookbook) and
v4 moves under a /v4 prefix. The versioned source config drives both
route trees, so the swap is a routePrefix + source re-binding plus the
app/[lang]/v5 -> app/[lang]/v4 tree rename.

- Switcher labels: "v5 (Latest)" / "v4 (Maintenance)".
- The v5 pre-release banner becomes an amber maintenance notice on v4
  pages, pointing at the current version.
- /v5/* redirects onto its unprefixed equivalent (bare /v5 needs its own
  rule, since the wildcard expands to an empty destination). v4 content
  keeps its /v5/... hrefs on purpose: render-time href rewriting would
  pull an unprefixed /docs link into the /v4 view.
- Version-switcher fallbacks are inverted: pages that exist only in v5
  now get /v4 fallbacks, and the two v4-only pages fall back to the
  root tree. /docs/configuration/* is a real page again.
- The link lint's two-space model swaps with the URL space: unprefixed
  is v5, /v4-prefixed is v4, and redirect destinations resolve against
  the HTTP space (they are matched before rendering).
- Content sweep: drop pre-release version gates and beta dist-tag
  references from v5 content, and de-prefix self-referential /v5 links.
@VaguelySerious
VaguelySerious requested review from a team and ijjk as code owners July 24, 2026 21:49
@changeset-bot

changeset-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9abe275

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

This PR includes changesets to release 0 packages

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

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

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

@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview Aug 3, 2026 9:18pm
example-nextjs-workflow-webpack Ready Ready Preview Aug 3, 2026 9:18pm
example-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-astro-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-express-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-fastify-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-hono-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-nestjs-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-nitro-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-nuxt-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-sveltekit-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-tanstack-start-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workbench-vite-workflow Ready Ready Preview Aug 3, 2026 9:18pm
workflow-docs Ready Ready Preview, v0 Aug 3, 2026 9:18pm
workflow-swc-playground Ready Ready Preview Aug 3, 2026 9:18pm
workflow-tarballs Ready Ready Preview Aug 3, 2026 9:18pm
workflow-web Ready Ready Preview Aug 3, 2026 9:18pm

- New top-level `/docs/whats-new` page above Getting Started: highlights,
  verified breaking-change table, and a "Migrating from V4" section.
- New `skills/migrating-workflow-v4-to-v5` agent skill covering the async
  `getWorld()`, removed `runStep`/`stepEntrypoint`/private subpaths, the
  `world.streams.*` argument reorder, `steps.get(runId, ...)`, the
  `getConflict()` -> `Run` shape, and the Nest builder subpath.
- Landing on `/docs/getting-started` (where bare `/docs` links redirect) no
  longer drills the sidebar into the framework list; the root menu stays
  visible. Both docs layouts moved into their `[[...slug]]` segment so the
  active slug is available server-side.
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

📦 Local Production (2 failed)

hono-stable (1 failed):

  • webhookWorkflow | wrun_41KYJVKJ5J0GMV92MDCGDT3YMW

nextjs-webpack-stable (1 failed):

  • webhookWorkflow | wrun_41KYJVKJ5J0GMV92MDCGDT3YMW

E2E Test Summary

Summary
Passed Failed Skipped Total
✅ ▲ Vercel Production 1455 0 239 1694
✅ 💻 Local Development 1621 0 227 1848
❌ 📦 Local Production 1619 2 227 1848
✅ 🐘 Local Postgres 1621 0 227 1848
✅ 🪟 Windows 154 0 0 154
✅ 📋 Other 1020 0 212 1232
✅ vercel-multi-region 27 0 0 27
Total 7517 2 1132 8651
Details by Category

✅ ▲ Vercel Production

App Passed Failed Skipped
✅ astro 126 0 28
✅ example 126 0 28
✅ express 126 0 28
✅ fastify 126 0 28
✅ hono 126 0 28
✅ nextjs-turbopack 151 0 3
✅ nextjs-webpack 151 0 3
✅ nitro 126 0 28
✅ nuxt 126 0 28
✅ sveltekit 145 0 9
✅ vite 126 0 28

✅ 💻 Local Development

App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26

❌ 📦 Local Production

App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
❌ hono-stable 127 1 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
❌ nextjs-webpack-stable 153 1 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26

✅ 🐘 Local Postgres

App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26

✅ 🪟 Windows

App Passed Failed Skipped
✅ nextjs-turbopack 154 0 0

✅ 📋 Other

App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 128 0 26
✅ e2e-local-dev-tanstack-start- 128 0 26
✅ e2e-local-postgres-nest-stable 128 0 26
✅ e2e-local-postgres-tanstack-start- 128 0 26
✅ e2e-local-prod-nest-stable 128 0 26
✅ e2e-local-prod-tanstack-start- 128 0 26
✅ e2e-vercel-prod-nest 126 0 28
✅ e2e-vercel-prod-tanstack-start 126 0 28

✅ vercel-multi-region

App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit 2f1c101 · Mon, 27 Jul 2026 22:56:03 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1255 (+70%) 🔻 1388 🔴 (+23%) 🔻 1402 🔴 (+16%) 🔻 1720 🔴 (+26%) 🔻 30
TTFS stream 372 (+18%) 🔻 1355 🔴 (+23%) 🔻 1376 🔴 (+20%) 🔻 1457 🔴 (+24%) 🔻 30
TTFS hook + stream 1342 (+5.8%) 1663 🔴 (+19%) 🔻 1712 🔴 (+17%) 🔻 1767 🔴 (+8.1%) 30
STSO 1020 steps (1-20) 165 (-2.9%) 333 🔴 (+10%) 377 🔴 (+7.1%) 451 🔴 (+19%) 🔻 19
STSO 1020 steps (101-120) 173 (-12%) 271 🔴 (-20%) 💚 359 🔴 (-12%) 478 🔴 (-4.4%) 19
STSO 1020 steps (1001-1020) 478 (-1.0%) 561 🔴 (-2.3%) 569 🔴 (-12%) 700 🔴 (+6.5%) 19
WO 1020 steps 406393 (-7.4%) 406393 (-7.4%) 406393 (-7.4%) 406393 (-7.4%) 1
SL stream latency 99 (+6.5%) 164 🔴 (-2.4%) 183 🔴 (-22%) 💚 370 🔴 (+46%) 🔻 30
SO stream overhead (text) 104 (-21%) 💚 168 (-42%) 💚 170 (-52%) 💚 589 (+40%) 🔻 30
SO stream overhead (structured) 103 (-24%) 💚 189 (-27%) 💚 215 (-23%) 💚 332 (-20%) 💚 30
📜 Previous results (7)

eb4659d

Mon, 27 Jul 2026 22:29:19 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1282 (+423%) 🔻 1419 🔴 (+4.9%) 1466 🔴 (+6.6%) 1521 🔴 (+5.1%) 30
TTFS stream 1253 (+27%) 🔻 1385 🔴 (+3.1%) 1431 🔴 (+5.4%) 1489 🔴 (+3.3%) 30
TTFS hook + stream 1475 (+162%) 🔻 1601 🔴 (+23%) 🔻 1645 🔴 (+19%) 🔻 1801 🔴 (+20%) 🔻 30
STSO 1020 steps (1-20) 167 (-5.1%) 264 🔴 (-5.0%) 319 🔴 (-5.3%) 396 🔴 (+11%) 19
STSO 1020 steps (101-120) 185 (-6.6%) 243 🔴 (-24%) 💚 377 🔴 (-23%) 💚 433 🔴 (-17%) 💚 19
STSO 1020 steps (1001-1020) 480 (+9.1%) 537 🔴 (-5.6%) 656 🔴 (-15%) 958 🔴 (+6.3%) 19
WO 1020 steps 401096 (-4.2%) 401096 (-4.2%) 401096 (-4.2%) 401096 (-4.2%) 1
SL stream latency 94 (+4.4%) 147 🔴 (-28%) 💚 224 🔴 (-4.3%) 518 🔴 (+22%) 🔻 30
SO stream overhead (text) 118 (-20%) 💚 170 (-42%) 💚 206 (-57%) 💚 287 (-86%) 💚 30
SO stream overhead (structured) 102 (-19%) 💚 150 (-37%) 💚 163 (-39%) 💚 183 (-47%) 💚 30

48b5b7a

Mon, 27 Jul 2026 21:34:44 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1249 (+91%) 🔻 1282 🔴 (+8.0%) 1326 🔴 (-11%) 1401 🔴 (-10.0%) 30
TTFS stream 1216 (+466%) 🔻 1283 🔴 (+12%) 1312 🔴 (+10%) 1414 🔴 (+1.8%) 30
TTFS hook + stream 475 (-64%) 💚 1524 🔴 (+4.9%) 1539 🔴 (+4.4%) 1775 🔴 (-1.0%) 30
STSO 1020 steps (1-20) 176 (-16%) 💚 272 🔴 (-6.2%) 354 🔴 (-17%) 💚 365 🔴 (-34%) 💚 19
STSO 1020 steps (101-120) 179 (-14%) 302 🔴 (+2.4%) 338 🔴 (±0%) 339 🔴 (-45%) 💚 19
STSO 1020 steps (1001-1020) 468 (-1.9%) 528 🔴 (-9.9%) 621 🔴 (-11%) 861 🔴 (-1.1%) 19
WO 1020 steps 381356 (-14%) 381356 (-14%) 381356 (-14%) 381356 (-14%) 1
SL stream latency 94 (-16%) 💚 147 🔴 (-10%) 180 🔴 (+1.1%) 296 🔴 (+42%) 🔻 30
SO stream overhead (text) 107 (-33%) 💚 154 (-44%) 💚 178 (-45%) 💚 228 (-74%) 💚 30
SO stream overhead (structured) 95 (-35%) 💚 160 (-60%) 💚 292 (-53%) 💚 429 (-85%) 💚 30

44a53f0

Mon, 27 Jul 2026 20:52:48 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 210 (-70%) 💚 1394 🔴 (+35%) 🔻 1419 🔴 (+30%) 🔻 1454 🔴 (+2.5%) 30
TTFS stream 287 (-67%) 💚 1315 🔴 (+38%) 🔻 1404 🔴 (+44%) 🔻 1450 🔴 (+46%) 🔻 30
TTFS hook + stream 395 (-64%) 💚 1636 🔴 (+33%) 🔻 1704 🔴 (+35%) 🔻 1961 🔴 (+33%) 🔻 30
STSO 1020 steps (1-20) 183 (+1.1%) 276 🔴 (±0%) 652 🔴 (+114%) 🔻 687 🔴 (+74%) 🔻 19
STSO 1020 steps (101-120) 198 (+6.5%) 276 🔴 (+3.4%) 344 🔴 (+14%) 401 🔴 (-83%) 💚 19
STSO 1020 steps (1001-1020) 480 (+7.4%) 570 🔴 (+10%) 633 🔴 (+17%) 🔻 636 🔴 (-3.2%) 19
WO 1020 steps 410023 (+6.6%) 410023 (+6.6%) 410023 (+6.6%) 410023 (+6.6%) 1
SL stream latency 92 (+19%) 🔻 196 🔴 (+58%) 🔻 243 🔴 (+69%) 🔻 286 🔴 (+12%) 30
SO stream overhead (text) 143 (+47%) 🔻 273 🔴 (+76%) 🔻 393 (+125%) 🔻 883 (+346%) 🔻 30
SO stream overhead (structured) 106 (+5.0%) 220 (+31%) 🔻 271 (+53%) 🔻 2708 🔴 (+77%) 🔻 30

f6861c4

Mon, 27 Jul 2026 20:27:40 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 228 (-68%) 💚 1341 🔴 (+30%) 🔻 1418 🔴 (+30%) 🔻 1496 🔴 (+5.4%) 30
TTFS stream 254 (-70%) 💚 1452 🔴 (+52%) 🔻 1471 🔴 (+51%) 🔻 1600 🔴 (+61%) 🔻 30
TTFS hook + stream 408 (-63%) 💚 1539 🔴 (+25%) 🔻 1631 🔴 (+29%) 🔻 1915 🔴 (+30%) 🔻 30
STSO 1020 steps (1-20) 171 (-5.5%) 268 🔴 (-2.9%) 309 🔴 (+1.6%) 361 🔴 (-8.4%) 19
STSO 1020 steps (101-120) 165 (-11%) 272 🔴 (+1.9%) 296 🔴 (-1.7%) 299 🔴 (-88%) 💚 19
STSO 1020 steps (1001-1020) 507 (+13%) 602 🔴 (+17%) 🔻 706 🔴 (+31%) 🔻 813 🔴 (+24%) 🔻 19
WO 1020 steps 418712 (+8.9%) 418712 (+8.9%) 418712 (+8.9%) 418712 (+8.9%) 1
SL stream latency 111 (+44%) 🔻 168 🔴 (+35%) 🔻 197 🔴 (+37%) 🔻 355 🔴 (+39%) 🔻 30
SO stream overhead (text) 125 (+29%) 🔻 240 (+55%) 🔻 314 (+79%) 🔻 554 (+180%) 🔻 30

ccbc70a

Mon, 27 Jul 2026 20:03:01 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1309 (+84%) 🔻 1413 🔴 (+37%) 🔻 1437 🔴 (+32%) 🔻 1502 🔴 (+5.8%) 30
TTFS stream 202 (-76%) 💚 1372 🔴 (+44%) 🔻 1390 🔴 (+43%) 🔻 1703 🔴 (+71%) 🔻 30
TTFS hook + stream 1540 (+41%) 🔻 1690 🔴 (+38%) 🔻 1723 🔴 (+37%) 🔻 1978 🔴 (+35%) 🔻 30
STSO 1020 steps (1-20) 162 (-10%) 291 🔴 (+5.4%) 343 🔴 (+13%) 398 🔴 (+1.0%) 19
STSO 1020 steps (101-120) 205 (+10%) 283 🔴 (+6.0%) 355 🔴 (+18%) 🔻 359 🔴 (-85%) 💚 19
STSO 1020 steps (1001-1020) 502 (+12%) 595 🔴 (+15%) 🔻 637 🔴 (+18%) 🔻 666 🔴 (+1.4%) 19
WO 1020 steps 408331 (+6.2%) 408331 (+6.2%) 408331 (+6.2%) 408331 (+6.2%) 1
SL stream latency 142 (+84%) 🔻 171 🔴 (+38%) 🔻 184 🔴 (+28%) 🔻 310 🔴 (+22%) 🔻 30
SO stream overhead (structured) 143 (+42%) 🔻 226 (+35%) 🔻 279 (+58%) 🔻 636 (-58%) 💚 30

b36c1a1

Sat, 25 Jul 2026 00:25:52 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 214 (-70%) 💚 1342 🔴 (+29%) 🔻 1460 🔴 (+32%) 🔻 1507 🔴 (+31%) 🔻 30
TTFS stream 270 (-73%) 💚 1380 🔴 (+28%) 🔻 1416 🔴 (+22%) 🔻 1492 🔴 (+16%) 🔻 30
TTFS hook + stream 438 (-66%) 💚 1559 🔴 (+11%) 1635 🔴 (+13%) 1755 🔴 (+4.8%) 30
STSO 1020 steps (1-20) 132 (-34%) 💚 247 🔴 (-11%) 307 🔴 (-11%) 313 🔴 (-19%) 💚 19
STSO 1020 steps (101-120) 179 (-17%) 💚 258 🔴 (-7.9%) 352 🔴 (+18%) 🔻 382 🔴 (+1.3%) 19
STSO 1020 steps (1001-1020) 490 (±0%) 597 🔴 (+6.6%) 668 🔴 (+12%) 839 🔴 (+39%) 🔻 19
WO 1020 steps 400022 (-3.0%) 400022 (-3.0%) 400022 (-3.0%) 400022 (-3.0%) 1
SL stream latency 88 (-11%) 145 🔴 (+11%) 172 🔴 (+16%) 🔻 200 🔴 (-3.8%) 30
SO stream overhead (text) 124 (-4.6%) 178 (-18%) 💚 228 (-7.3%) 275 (+1.9%) 30
SO stream overhead (structured) 111 (-21%) 💚 260 🔴 (+18%) 🔻 326 (+31%) 🔻 597 (-14%) 30

4a6fabd

Fri, 24 Jul 2026 23:38:35 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1279 (+30%) 🔻 1401 🔴 (+29%) 🔻 1421 🔴 (+23%) 🔻 1511 🔴 (+25%) 🔻 30
TTFS stream 1204 (+421%) 🔻 1379 🔴 (+27%) 🔻 1432 🔴 (+27%) 🔻 1545 🔴 (+8.6%) 30
TTFS hook + stream 1505 (+53%) 🔻 1667 🔴 (+15%) 🔻 1711 🔴 (+13%) 1779 🔴 (+7.6%) 30
STSO 1020 steps (1-20) 177 (-13%) 259 🔴 (-6.8%) 348 🔴 (-7.9%) 466 🔴 (+2.6%) 19
STSO 1020 steps (101-120) 186 (-19%) 💚 283 🔴 (-15%) 370 🔴 (-8.6%) 1186 🔴 (±0%) 19
STSO 1020 steps (1001-1020) 465 (-8.1%) 588 🔴 (+0.9%) 710 🔴 (+13%) 824 🔴 (+20%) 🔻 19
WO 1020 steps 405651 (-4.4%) 405651 (-4.4%) 405651 (-4.4%) 405651 (-4.4%) 1
SL stream latency 96 (-11%) 159 🔴 (-1.2%) 194 🔴 (+4.3%) 253 🔴 (+1.2%) 30
SO stream overhead (text) 112 (-25%) 💚 181 (-25%) 💚 199 (-24%) 💚 233 (-37%) 💚 30
SO stream overhead (structured) 105 (-32%) 💚 175 (-23%) 💚 196 (-19%) 💚 314 (-1.3%) 30
ℹ️ Metric definitions & methodology

Best/P75/P90/P99 deltas compare against the most recent benchmark run on main at the time of this run. 🔻 flags a delta worse than +15%, 💚 one better than −15%.

Metrics — TTFS: time to first step body (in-deployment start() → first step body, deployment clocks) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) · SL: stream latency (in-deployment write → read propagation, readAt - writtenAt) · SO: stream overhead (end-to-end write+consume time beyond the modelled generation window)

Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · stream latency: parallel reader/writer steps on a dedicated stream; SL is the in-deployment write->read propagation (readAt - writtenAt) · stream overhead (text): writer streams 300 variable-length text token deltas paced at 100/s for 3s (a haiku-size LLM's token throughput) while a parallel reader drains the whole stream; SO is the end-to-end write+consume time beyond the 3s generation window (overhead/backpressure) · stream overhead (structured): same workload as stream overhead (text), but each delta is an AI-SDK-style structured object ({ type: 'text-delta', id, text }) instead of a raw string, so the SO gap vs the text scenario is the added serialization cost

🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 · SL 50/60/125 · SO 250/500/1000 · STSO (1-20) 20/30/60 · STSO (101-120) 30/45/90 · STSO (1001-1020) 40/60/120

All metrics are measured from deployment-side timestamps only. Runs are triggered by an in-deployment route that stamps the anchor (clientStart) right before start(), so the CI runner’s request and its path through api.vercel.com sit outside every measured window. TTFS = in-deployment start() → first step body (turbo uses the in-process fast path, non-turbo the dispatch path), and includes the VQS dispatch hop plus any /flow cold start. STSO/WO are measured between step bodies on the deployment. SL is measured inside the workflow (parallel reader/writer steps), so it no longer includes the api.vercel.com read path.

Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the /flow invocation for a large fraction of runs, inflating P75+; the Best column shows the fastest (warm-start) sample for comparison.

Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
Comment thread docs/content/docs/v5/whats-new.mdx Outdated
VaguelySerious and others added 3 commits July 24, 2026 16:55
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>

@pranaygp pranaygp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thorough pass over all 50 files: the route/source/lint re-binding is careful and internally consistent — I diffed both content trees and every one-sided page has a matching version-switcher fallback, the redirect model in lint.ts (destinations resolved in the HTTP space) is right, and I verified the what's-new/skill claims against the actual v4 (origin/stable) and v5 interfaces: stream signatures, steps.get, getConflict, setAttributes alias, start() attributes/region options, seed derivation, env vars (WORKFLOW_MAX_EVENTS[_OVERRIDE], WORKFLOW_TURBO, WORKFLOW_TRACE_MODE, WORKFLOW_PRECONDITION_GUARD, WORKFLOW_STREAM_FLUSH_INTERVAL_MS), and the removed exports all check out.

Two real issues, both detailed inline:

  1. Blocker — the unprefixed worlds routes still pass version="v4" (app/[lang]/worlds/[id]/page.tsx and app/[lang]/worlds/building-a-world/page.tsx, untouched by this PR). After the semantics flip they serve v4 content with noindex at the canonical /worlds/* URLs, and all 12 community world pages become self-redirect loops.
  2. Stale lazyDiscovery claim in whats-new.mdx and the migration skill — the option was removed in @workflow/next@5.0.0-beta.21 (#2545); neither v4 nor v5 GA has it.

Plus three minor/UX notes inline (banner dark mode, banner deep-linking, and a question about sidebar state now that the docs layouts live inside [[...slug]]).

// Community worlds have no versioned content — their canonical page lives
// at /worlds/<id> only.
if (version !== 'v4' && !isOfficial) {
if (version !== 'v5' && !isOfficial) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocker: this semantics flip ('v4' now = maintenance) has two consumers that weren't updated: docs/app/[lang]/worlds/[id]/page.tsx and docs/app/[lang]/worlds/building-a-world/page.tsx (not in this diff — they were last touched in #2934) still pass version="v4" / generateWorldMetadata(id, 'v4') for the unprefixed routes.

Consequences with this PR as-is:

  • /worlds/local, /worlds/postgres, /worlds/vercel render v4 content at the canonical URLs, get robots: noindex, a · v4 title suffix, a text/markdown alternate pointing at /v4/worlds/<id>.md, and WorldVersionSelect current="v4" — while /worlds/<id>.md (served from the swapped worldsSourceBundle) returns v5 content. The v5 worlds pages become unreachable in the HTML UI: both /worlds/* and /v4/worlds/* serve v4.
  • Every community world (turso, mongodb, redis, platformatic, … — 12 ids in worlds-manifest.json) now takes this redirect branch on the unprefixed route itself, redirecting to its own URL — an infinite redirect loop. /v4/worlds/<community-id> redirects into the same loop.

Fix: flip the four call sites in those two page files to 'v5'. Might also be worth adding /worlds/vercel (title or a v5-only content marker) and one community world to check-docs-smoke.mjs, since the existing og-meta assertions pass either way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in a0adc9e — all four call sites now pass 'v5'. Verified against the built server: /worlds/vercel serves the v5 content with a clean title and no noindex, /worlds/turso and /worlds/platformatic return 200, and /v4/worlds/turso is a single 307 to /worlds/turso. Also added smoke checks (assertWorldVersionMarkers for /worlds/vercel and /v4/worlds/vercel, plus assertServesDirectly('/worlds/turso')) so this can't silently regress.


const versionPrefix = VERSION_PREFIXES[version];
const isPreRelease = version === 'v5';
const isMaintenance = version === 'v4';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same issue as world-detail-page.tsx: the unprefixed docs/app/[lang]/worlds/building-a-world/page.tsx still passes 'v4', so /worlds/building-a-world now serves the v4 content with noindex and a · v4 title. Needs the 'v5' flip in that page file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in a0adc9e — the unprefixed building-a-world page now passes 'v5'; verified it serves with the current-version title and no noindex.

Comment thread docs/content/docs/v5/whats-new.mdx Outdated
| `workflow/internal/private` and `@workflow/core/private` removed | These were never public API. The compiler no longer emits imports from them, so regenerate build output rather than importing them yourself. |
| `@workflow/world-local` stream chunks moved | Chunks live at `streams/chunks/<streamName>/`. Files written in the old flat layout are not read back; local development state from 4.x can be deleted. |

On Next.js 16.2.0-canary.48 and newer, `withWorkflow` now defaults to `lazyDiscovery: true`; older versions fall back to eager discovery automatically.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is stale: the lazyDiscovery option — and the whole deferred-discovery builder path — was removed in @workflow/next@5.0.0-beta.21 (#2545); discovery at GA is always eager and workflows.lazyDiscovery no longer exists (the only remaining references in the repo are CHANGELOG entries). The default-true behavior described here only existed between beta.8 (#1805) and beta.20, and v4 never had the option either, so there's no net v4 → 5.0.0 change to document. Suggest dropping this paragraph.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in a0adc9e — dropped the paragraph. (Same commit also folds in Pranay's Vercel toolbar feedback on this page: intro paragraph replaced by a post-install callout, a CopyPrompt for the agent migration, Python dropped from the frameworks bullet, multi-region cross-links, child-workflows-first wording on the event-limit row, and the duplicate-ID row rephrased.)

- **Errors keep their type.** `WorkflowRunFailedError.cause` now preserves the original class identity and cause chain. Code that pattern-matched on `error.message` because the class was flattened in 4.x can use `instanceof` — but flag it rather than rewriting error handling unprompted.
- **A per-run event limit is enforced.** The World supplies the ceiling (25,000 events on the Local and Vercel Worlds) and a run that reaches it fails with `MAX_EVENTS_EXCEEDED`. Flag any workflow with an unbounded loop; the fix is a child run per batch, which is a design change, not a migration edit.
- **Stream writes flush the leading chunk immediately.** The flush window default went from 10ms to 0. An app that relied on the window to coalesce a burst of tiny chunks can set `streamFlushIntervalMs` or `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`.
- **`lazyDiscovery: true` is the default** for `withWorkflow` on Next.js ≥ 16.2.0-canary.48. Older Next.js versions fall back to eager discovery automatically, so this needs no edit — mention it only if the build's discovery behavior changes visibly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same staleness as whats-new.mdx:129: lazyDiscovery was removed in @workflow/next@5.0.0-beta.21 (#2545). Neither v4 nor v5 GA has the option, so this bullet has the agent report a behavior change that doesn't exist — and its implied remedy (workflows: { lazyDiscovery: false }) would introduce an unknown option into next.config. Drop the bullet (and the corresponding row is absent from the what's-new table, so the two docs will agree once both are fixed).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in a0adc9e — bullet removed, skill version bumped to 0.2.1.

export const MaintenanceBanner = ({ pathname }: MaintenanceBannerProps) => {
const latestHref = buildVersionUrl(pathname, LATEST_VERSION);
return (
<div className="border-b bg-amber-100 px-4 py-2 text-center text-sm">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor: the pre-release banner this replaces carried dark: variants (dark:bg-blue-950/40, dark:text-blue-900, …); this one has none, so in dark mode it renders as a bright light-amber band across an otherwise dark page (bg-amber-100 doesn't adapt). Consider matching the old pattern, e.g. dark:bg-amber-950/40 on the container plus dark:text-amber-200-style variants on the text/link/decoration classes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in a0adc9e — added dark:bg-amber-950/40 on the container and dark:text-amber-200 / decoration variants on the text and link, mirroring the old banner's pattern.

const { lang, slug } = await params;
return (
<div className="bg-background-100">
<MaintenanceBanner pathname={`/${lang}/v4/docs`} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Optional, but now cheap: slug is available in this layout, so the banner could deep-link instead of always landing on /docs → getting-started:

<MaintenanceBanner
  pathname={`/${lang}/v4/docs${slug?.length ? `/${slug.join('/')}` : ''}`}
/>

buildVersionUrl strips the /v4 prefix, and the v4-only pages are already covered by the version-switcher fallback redirects, so "Go to Workflow 5 (Latest)" — the banner's main CTA — would keep the reader on the equivalent page. (The hardcoded pathname was a pre-existing limitation from the pre-release banner; the layout move is what makes fixing it possible.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done in a0adc9e — the banner now receives the full slug path. Verified in prerendered output (e.g. the v4 foundations page links to /en/docs/foundations) and confirmed against the built server that locale-prefixed URLs go through the i18n strip and then the version-switcher fallback redirects for v4-only pages.


const Layout = async ({ children, params }: LayoutProps<'/[lang]/docs'>) => {
const { lang } = await params;
// This layout lives inside `[[...slug]]` rather than next to it so that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Question on the tradeoff: with the layout inside [[...slug]], it belongs to the dynamic segment, so client-side navigation between two docs pages re-renders it with new params — App Router only preserves layout state when the segment (including its param value) is unchanged. The practical symptom would be sidebar scroll position / drill-in state resetting on every page click, which the previous static docs/layout.tsx avoided. The PR verification covered prerendered output; did you also check client-side navigation in the browser? If geistdocs re-scrolls the active item into view this may be a fine trade for the hydration-flash-free activeSlug, but it affects every docs page, so worth confirming before GA.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Answered this empirically against the built server with a browser: the sidebar's scroll container IS remounted on every client-side navigation (a DOM marker set before navigating does not survive), but geistdocs re-scrolls the active item into view after each mount — verified in the long API-reference section (sidebar overflow ~2300px, scrollTop lands at the active link, active item visible after both direct load and client-side nav). So the practical UX is the standard docs behavior of following the current page, and the tradeoff for server-rendered activeSlug with no hydration flash holds. No change needed — resolving.

…s review feedback

- Pass 'v5' on the unprefixed worlds routes: after the version-semantics
  flip they still passed 'v4', serving v4 content with noindex at the
  canonical /worlds/* URLs and turning every community world page into a
  self-redirect loop. Add smoke checks for the version markers and for a
  community world serving directly.
- Remove the lazyDiscovery claim from whats-new and the migration skill:
  the option was removed in @workflow/next@5.0.0-beta.21 (#2545); neither
  v4 nor 5.0.0 GA has it.
- whats-new (toolbar feedback): replace the intro paragraph with a
  post-install callout, add a CopyPrompt for the agent migration, drop
  Python from the frameworks bullet, add multi-region cross-links,
  recommend child workflows over the event-limit tuning knob, and rephrase
  the duplicate-ID row around build failures after upgrading.
- Maintenance banner: restore dark-mode styling and deep-link "Go to
  latest" to the same page (version-switcher fallbacks catch v4-only
  pages).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp

Copy link
Copy Markdown
Contributor

Pushed a0adc9e addressing the review findings and Pranay's Vercel toolbar comments on /docs/whats-new:

Review fixes

  • Unprefixed worlds routes now pass 'v5' — fixes v4 content + noindex at canonical /worlds/* URLs and the community-world self-redirect loops. New smoke checks guard the version markers and community-world serving.
  • Removed the stale lazyDiscovery claim from whats-new and the migration skill (option removed in @workflow/next@5.0.0-beta.21, Remove lazy discovery from workflow/next #2545); skill bumped to 0.2.1.
  • Maintenance banner: dark-mode styling restored; "Go to latest" now deep-links to the same page (v4-only pages caught by the fallback redirects).
  • Sidebar-in-[[...slug]] question resolved empirically (browser test): the sidebar does remount per navigation but geistdocs re-scrolls the active item into view, so no change needed.

Toolbar feedback on /docs/whats-new (threads replied + resolved in the Vercel dashboard)

  • Intro paragraph replaced with a post-install "v4 remains installable" callout.
  • Added a <CopyPrompt> with a paste-ready agent prompt for the migration.
  • Frameworks bullet: Python dropped; React Router + NestJS "now supported".
  • Multi-region section links to /worlds/vercel#multi-region, explicit region selection, and Building a World.
  • Event-limit row recommends child workflows first (cookbook recipe), with Runtime Tuning › Limits as the fallback.
  • Duplicate-ID row rephrased around "if you start encountering build failures after upgrading".

Verified: bun ./scripts/lint.ts 0 errors, pnpm build exit 0, full smoke suite (incl. the 3 new checks) green against pnpm start, and a curl matrix over the worlds routes (/worlds/vercel 200 v5-titled, /worlds/turso + /worlds/platformatic 200, /v4/worlds/turso single 307 → /worlds/turso).

- Custom Worlds section gains an intro (spec changed, upgrade your World,
  compatibility reporting returning to the Worlds page) and a CopyPrompt
  that walks an agent through diffing packages/world* between stable and
  main to port the spec changes into a custom World.
- Drop the deterministic-seed breaking-change row (runs are pinned to the
  deployment that created them, so cross-version replay cannot occur) and
  the trailing reference to it; trim the matching seed rationale and
  checklist item from the migration skill (0.2.2).
- Event-limit row: drop 'one run per batch'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@pranaygp pranaygp left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm taking over this PR. need to update a bunch of other docs too that are out dated and will get everything up into shape before approving and getting ready for v5 stable

…cs-reads env var

Breaking changes (verified against origin/main, which moved past this
branch's last merge):
- createLocalWorld()/createVercelWorld() removed (#3112) — new Worlds-table
  row and a mechanical rewrite rule in the migration skill, replacing the
  skill's now-wrong 'not a breaking change' note.
- Deterministic sandbox hardening (#3045) — application-table row (WeakRef,
  FinalizationRegistry, Atomics.waitAsync, async WebAssembly gone;
  crypto.subtle.digest synchronous) and a step-3 skill item.
- Step queue topics retired (pending retire-step-* changesets) — Worlds-table
  row and third World contract change in the skill; stepEntrypoint row
  reworded since standalone step routes no longer exist.

Missed v5-only features:
- Lineage attributes ($parentRunId/$rootRunId) noted in the Run attributes
  section; run.cancel({ cancelReason }) in the cancellation section.
- 'Errors keep their type' bullet expanded into a Richer serialization
  highlight with an instanceof example, serializable workflow-function
  references and Run handles, and the new SerializationError/context-violation
  errors.
- Trace-viewer section gains the local-tooling additions: Nitro /_workflow dev
  route, inspect --since/--until, past-24h name lookups, World.describeRun
  region output. /_workflow also added to the Observability page.

Env vars: WORKFLOW_DISABLE_ANALYTICS_READS documented under the Vercel World.
WORKFLOW_SAFE_MODE was not documented on purpose — it no longer exists in
source (only WORKFLOW_SEQUENTIAL_REPLAYS survived).

Skill bumped to 0.2.3 (new rewrite rule, sandbox item, queue contract change,
createLocalWorld/createVercelWorld intake + fail checks).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pranaygp

Copy link
Copy Markdown
Contributor

Handoff for whoever ships next from peter/docs-5-ga: commit f6861c412 on origin/pgp/docs-5-ga-v5-gaps is ready to fast-forward onto this branch (parented on ccbc70abb, the current tip):

git fetch origin pgp/docs-5-ga-v5-gaps
git merge --ff-only FETCH_HEAD   # or cherry-pick f6861c412 if the branch has moved
git push origin peter/docs-5-ga

It covers the v5-vs-v4 gap analysis (4 files):

  • Breaking changes that landed on main after this branch's last merge (verified against origin/main): createLocalWorld()/createVercelWorld() removed (Remove obsolete world factory aliases #3112, new Worlds row + skill rewrite rule replacing the now-wrong "not a breaking change" note); deterministic sandbox hardening (feat(core): deterministic sandbox hardening #3045, new application row — WeakRef/FinalizationRegistry/Atomics.waitAsync/async WebAssembly gone, crypto.subtle.digest synchronous); step queue topics retired (new Worlds row + skill contract change; stepEntrypoint row reworded since standalone step routes no longer exist).
  • Missed v5-only features: lineage attributes ($parentRunId/$rootRunId) in the attributes section, run.cancel({ cancelReason }) in cancellation, "Richer serialization" highlight (error instanceof example, serializable workflow-function refs and Run handles, SerializationError), and the local-tooling additions (Nitro /_workflow dev route, inspect --since/--until, past-24h name lookups, per-World describeRun fields) in the trace-viewer section. /_workflow also added to the Observability page.
  • Env vars: WORKFLOW_DISABLE_ANALYTICS_READS documented under Configuration › Worlds › Vercel World. WORKFLOW_SAFE_MODE deliberately not documented — it no longer exists in source (only WORKFLOW_SEQUENTIAL_REPLAYS survived, already documented).
  • Skill bumped to 0.2.3.

Verified on an isolated worktree at ccbc70abb: link lint 0 errors, pnpm build exit 0, all additions confirmed in prerendered HTML. Commit is signed.

pranaygp and others added 2 commits July 27, 2026 13:31
Full audit of the v5 content tree against the actual v4 (stable) -> v5
(main) delta. Fixes, by theme:

Wrong facts:
- building-a-world: World interface gains the now-required specVersion,
  the v5 capabilities (preconditionGuard, maxConcurrency), analytics,
  createRunId, describeRun, processExitTriggersQueueRedelivery, and
  runs.experimentalSetAttributes; steps.get runId is required; queue
  considerations cover delaySeconds (waits are delayed continuations)
  and region.
- precondition-failed-error: the guard is on by default (=0 opts out),
  not opt-in via =1.
- set-world/create-world: Worlds are statically injected at build time;
  the runtime env-mutation example could never switch Worlds - replaced
  with an explicit createWorld() injection example.
- world/storage: steps.get runId param typed as required.
- comparisons: child runs use start() directly from a workflow (six
  step-wrapped-start rows across four pages), Temporal search
  attributes now map to setAttributes()/attribute filtering, and
  multi-region has shipped.
- errors-and-retries: errorCode table now lists all eight run error
  codes including the v5 MAX_EVENTS_EXCEEDED.

Removed 'client' SWC mode (merged into step in 5.0):
- code-transform: restructured around the real v5 modes
  (step/workflow/detect) with a migration callout; step mode now shows
  the workflow-function throw + workflowId behavior it absorbed.
- framework-integrations: app-code transform passes mode 'step'.
- nestjs getting started: .swcrc described as step-mode.

v4 execution-model prose (steps run inline in v5; no step route):
- workflows-and-steps, understanding-directives, code-transform,
  streaming (lock warning), tracing (queue-delivery list),
  testing/server-based (flow-route requests), python (drop per-step
  route claim pending confirmation of the Python topology).

Additions: compression-before-encryption in the encryption internals,
AbortController/AbortSignal in workflow globals, region option on
start() (api-reference + foundations), QueueOptions region/specVersion,
stream flush-window note in streaming.

Adjacent fixes: createWebhook() is synchronous (14 samples awaited the
thenable handle, which resolves the received request instead), unused
import in ai/defining-tools, ai/meta.json missing message-queueing,
undeclared class in the whats-new serialization sample.

Verified: pnpm test:docs 968 snippet typechecks pass, docs link lint 0
errors, pnpm build exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v5 World interface listing referenced analytics with a one-line
bullet only. Add a full 'Analytics Interface (Optional)' section:
the interface shape (runs/attributes/steps/events/hooks/waits, matching
packages/world/src/analytics.ts), the implementer contract (metadata-only
rows, latest-write-wins attribute filtering, paired time windows,
optional pageInfo, eventual consistency), feature-detection semantics
(tooling prefers analytics when present, falls back to Storage), and
which first-party Worlds implement it (Vercel only). Cross-linked from
the World-members list and to the Analytics API reference, which was
audited against source (pageInfo shape, attribute-key fields, 24h
default window, hooks.get signature) and is already accurate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@karthikscale3 karthikscale3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The Analytics section is implementor-facing, so I think it should define portable interface semantics and keep Vercel observability-pipeline behavior in the Vercel World documentation. I left two concrete wording suggestions below.


## Analytics Interface (Optional)

The optional `analytics` namespace serves observability surfaces — dashboards, `workflow inspect`, the local web UI — with **metadata-only** listings that can scan large numbers of runs without touching payload data. Tooling feature-detects it: when `world.analytics` is present it is preferred for listings and attribute search, and when it is absent, tooling falls back to the Storage APIs. Of the first-party Worlds, only the [Vercel World](/worlds/vercel) implements it (backed by the observability data pipeline); the Local and Postgres Worlds leave it `undefined`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Separate the portable purpose and feature-detection behavior from the first-party implementation note, without tying the interface to Vercel's observability pipeline.

Suggested change
The optional `analytics` namespace serves observability surfaces — dashboards, `workflow inspect`, the local web UI — with **metadata-only** listings that can scan large numbers of runs without touching payload data. Tooling feature-detects it: when `world.analytics` is present it is preferred for listings and attribute search, and when it is absent, tooling falls back to the Storage APIs. Of the first-party Worlds, only the [Vercel World](/worlds/vercel) implements it (backed by the observability data pipeline); the Local and Postgres Worlds leave it `undefined`.
The optional `analytics` namespace provides **metadata-only** access to runs and their related records. It is intended for observability and discovery surfaces such as dashboards, `workflow inspect`, and the local web UI. Implementations can optimize these queries independently of payload storage.
Tooling feature-detects this namespace. When `world.analytics` is available, tooling prefers it for listings and attribute search; otherwise, it uses the Storage APIs. Among the first-party Worlds, only the [Vercel World](/worlds/vercel) currently implements it. The Local and Postgres Worlds leave it `undefined`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Applied in 9abe275 — the intro now leads with the portable purpose (metadata-only access, implementations free to optimize independently of payload storage) and puts feature detection in its own paragraph, with the pipeline-backing note dropped from the interface docs. That Vercel-specific behavior now lives in the Vercel World page's Observability section instead.

Comment on lines +278 to +283
If you implement it, honor the contract that makes the namespace safe to expose broadly:

- **Metadata only.** Rows must never include run inputs/outputs, step data, or hook tokens — the `Analytics*` row schemas in `@workflow/world` define exactly which fields are allowed. There is no `resolveData` escape hatch here; payload access stays on the Storage APIs.
- **Attribute filtering is latest-write-wins.** `runs.list({ attributes })` matches a run's most recent value for each key (up to 8 pairs), and reserved `$`-prefixed keys are valid in filters even though user writes to them are rejected.
- **Time windows are the query contract.** `startTime`/`endTime` must be provided together; a bounded window lets the backend prune its scan. Responses may carry an optional `pageInfo` describing the scannable window (lookback days, window starts, upgrade availability) — return it if your backend has retention limits so tools can size date pickers.
- **Eventual consistency is acceptable.** Analytics data may trail live state by a few seconds; tools treat it as a listing surface, not a source of truth.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These are useful interface guarantees, but they can be phrased without prescribing Vercel/ClickHouse query behavior. This also makes the paired-boundary rule explicit when both values are omitted.

Suggested change
If you implement it, honor the contract that makes the namespace safe to expose broadly:
- **Metadata only.** Rows must never include run inputs/outputs, step data, or hook tokens — the `Analytics*` row schemas in `@workflow/world` define exactly which fields are allowed. There is no `resolveData` escape hatch here; payload access stays on the Storage APIs.
- **Attribute filtering is latest-write-wins.** `runs.list({ attributes })` matches a run's most recent value for each key (up to 8 pairs), and reserved `$`-prefixed keys are valid in filters even though user writes to them are rejected.
- **Time windows are the query contract.** `startTime`/`endTime` must be provided together; a bounded window lets the backend prune its scan. Responses may carry an optional `pageInfo` describing the scannable window (lookback days, window starts, upgrade availability) — return it if your backend has retention limits so tools can size date pickers.
- **Eventual consistency is acceptable.** Analytics data may trail live state by a few seconds; tools treat it as a listing surface, not a source of truth.
If you implement this namespace, observe the following requirements:
- **Metadata only.** Analytics responses must not include run inputs or outputs, step data, hook tokens, or other payload data. The `Analytics*` schemas exported by `@workflow/world` define the complete set of permitted fields. Payload retrieval remains exclusively available through the Storage APIs.
- **Attribute filters use the latest value.** `runs.list({ attributes })` evaluates each filter against the run's most recently written value for that key. A request may contain up to eight key-value pairs. Reserved `$`-prefixed attributes are valid filters, although users cannot write them directly.
- **Time boundaries must be paired.** `startTime` and `endTime` may either both be omitted or both be supplied. Responses may include `pageInfo` describing retention and the available query window. Implementations with retention limits should return this information so tooling can present valid date ranges.
- **Results may be eventually consistent.** Analytics records may lag live workflow state. Consumers use this namespace for discovery and listing; Storage remains the authoritative interface for current workflow state and payload access.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Applied in 9abe275, verbatim — including the explicit both-omitted-or-both-supplied phrasing, which matches the AnalyticsListRunsParams doc comments in @workflow/world. The Vercel/ClickHouse specifics that were dropped here (bounded-window scan speed, plan lookback surfacing as observability-upgrade-required + pageInfo) moved to the Vercel World page's Observability section so they're still documented, just in the right place.

pranaygp and others added 4 commits July 27, 2026 15:06
… attributes across comparisons

Toolbar feedback on the Temporal comparison:
- Search attributes move out of the 'no direct equivalent' table into the
  migration mapping — setAttributes()/attributes-on-start() is the direct
  equivalent. trigger.dev gains the matching row (run tags / metadata.set());
  the other platforms have no run-tag concept to map.
- The retry-policy row no longer reads as a limitation: declarative Temporal
  policies (backoffCoefficient, nonRetryableErrorTypes, maximumAttempts) map
  to idiomatic code — maxRetries, FatalError, and backoff derived from
  getStepMetadata().attempt via RetryableError's retryAfter — with links to
  the errors-and-retries examples. Same treatment for the Cloudflare,
  Inngest, and Step Functions retry rows.
- code-transform: 'Changed in 5.0' callout on __step_registrations.js
  explaining the v4 step route + step queue topic merger, linking the v4
  version of the page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* origin/main:
  Prewarm only appended replay payloads (#3131)
  feat: publish each run's X25519 public key on the run entity (#3095)
  feat(core): route sealed envelopes through the serialization layer (#3094)
  docs: redirect retired migration-guides URLs to comparisons (#3127)
  feat(core): add `encp` sealed-box encryption primitive (#3093)
  chore(core): clarify runtime comments (#3111)
  Remove obsolete world factory aliases (#3112)
  feat(core): deterministic sandbox hardening (#3045)
- Map @workflow/world-local in the docs snippet type-checker and drop
  the @skip-typecheck escape on the set-world example; also unskip the
  three complete input samples on code-transform. Four more snippets are
  now machine-checked (972 passing, was 968).
- World-upgrade prompt diffs against the release tag being upgraded to
  (workflow@5.x.y) instead of a moving main, falling back to main.
- World SDK index points World authors at the top-level interface
  members (specVersion, capabilities, createRunId, describeRun, ...)
  documented in Building a World.

The preceding merge of origin/main makes the GA-forward docs claims true
in-tree (factory aliases actually removed by #3112, sandbox hardening
from #3045 including the workflow-globals digest row this branch had
stale) and picks up the migration-guides redirects (#3127), whose
destinations now pass this branch's redirect-destination lint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A correlation id names a step, hook or wait within its run, so the
lookup is now scoped to one run. Adds it to the World migration table
and to the v4→v5 migration skill.
… to Vercel World page

Apply karthikscale3's review suggestions on Building a World: the analytics
namespace intro and implementation requirements now describe portable
interface semantics (paired-or-omitted time bounds, latest-value attribute
filters, metadata-only schemas) without prescribing Vercel/ClickHouse query
behavior. The Vercel-specific details (observability pipeline backing,
bounded-window scan speed, plan lookback via observability-upgrade-required
and pageInfo) move to the Vercel World Observability section.

Co-Authored-By: Claude Fable 5 <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.

4 participants