[docs] Make v5 the default docs version#3100
Conversation
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.
🦋 Changeset detectedLatest commit: b36c1a1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 0 packagesWhen 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 |
- 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.
🧪 E2E Test Results✅ All tests passed E2E Test SummarySummary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
✅ vercel-multi-region
|
📊 Workflow Benchmarkscommit Backend:
📜 Previous results (1)4a6fabdFri, 24 Jul 2026 23:38:35 GMT · run logs
ℹ️ Metric definitions & methodologyBest/P75/P90/P99 deltas compare against the most recent benchmark run on 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 ( Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the |
TooTallNate
left a comment
There was a problem hiding this comment.
Reviewed the version inversion end to end — source binding, routing, SEO metadata, and the agent-facing surfaces — and ran the verification suite locally (link lint, full docs build, all 21 smoke checks). Approving, with one pre-merge housekeeping step and one cross-PR gap worth closing before GA.
The AGENTS.md-critical sitemap behavior survives: both sitemap.md routes and the llms.mdx route are untouched, and the source lists stay symmetric — allSources still carries six bundles (current docs + cookbook + worlds, plus the prefixed set), with only the version identity swapped. Nothing falls out of search/llms.txt/sitemap coverage, which is the failure mode a version flip could easily introduce.
Routing and SEO checks:
- The bare
/v5rule is a real edge, not defensive noise::path*matches zero segments and the expanded destination collapses to an emptyLocation. Good catch, and worth the extra rule. - The noindex/canonical inversion is complete and correct —
isMaintenancenow drivesrobots: { index: false, follow: true }on v4 pages with the canonical pointing at the unprefixed URL, exactly mirroring what v5 pages had when they were the pre-release side. - The banner swap (pre-release → maintenance, with a one-click same-page link to latest via
buildVersionUrl) is the right treatment for readers arriving on v4 from old links. - The switcher-fallback inversion is honest about its own limitation (the switcher adds/drops a prefix without checking the target has the page) and correctly uses temporary redirects so they can be dropped wholesale at v4 retirement rather than lingering as permanents.
Verified locally: link lint 0 errors, full docs build passes, 21/21 smoke checks including the new /v4/worlds/vercel assertion. The new migration skill is a new file, so version: '0.1.0' is correct (the AGENTS.md bump rule applies to modifications).
Pre-merge: merge main to clear the CI failure. Unit Tests (ubuntu-latest) fails on world-testing's inline-batches-debug timing out — which is precisely the shared-data-dir concurrency flake fixed by #3055 (merged to main at 22:46Z). This branch's head is from 22:56Z but branched before that merge, so it doesn't carry the fix. A docs-only change can't cause a world-testing timeout; a main merge should turn it green.
Cross-PR gap that should not ship with GA (flagged on #3091 too): once 5.0.0 takes latest, the v4 docs' install instructions become actively wrong. 15 v4 pages carry bare npm i workflow, and docs/v4/errors/corrupted-event-log.mdx + docs/v4/errors/runtime-decryption-failed.mdx both say npm install workflow@latest — a v4 troubleshooting page telling someone mid-incident to run a command that now performs a major upgrade to 5.x. This PR is the one that makes v4 the maintenance line, so it's the natural place to sweep those to workflow@previous (or a pinned workflow@4), but anywhere before GA is fine.
Co-authored-by: Peter Wielander <mittgfu@gmail.com> Signed-off-by: Peter Wielander <mittgfu@gmail.com>
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).content/docs/v5andcontent/worlds/v5now serve unprefixed —/docs,/worlds,/cookbook— and v4 moves under a/v4prefix. The versioned source config already drives both route trees (the root tree always rendersversionedSources.current), so this is aroutePrefixmove plus source re-binding, not a restructure. The only file moves areapp/[lang]/v5/**→app/[lang]/v4/**.What changed
v5 (Latest)andv4 (Maintenance);current: 'v5'.pre-release-banner.tsx→maintenance-banner.tsx. Instead of warning that v5 is a pre-release, v4 pages carry an amber notice — "Viewing Workflow 4.x documentation. This version only receives stability fixes." — linking to the current version. v5 pages have no banner. v4 pages keeprobots: noindex, follow.docs/next.config.ts):/v5/*→ unprefixed equivalent (permanent). Bare/v5needs its own rule::path*matches zero segments, but the expanded destination is then the empty string, which Next.js emits as an emptyLocationheader. The world-docs and api-reference restructure rules are mirrored onto/v4/docs/*./v4/docs/...fallbacks (set-attributes, precondition-failed-error, world/analytics, three changelog entries, configuration, cancellation, react-router, …); the two v4-only pages (workflow-runtime/step-entrypoint,cookbook/advanced/distributed-abort-controller) fall back into the root tree. All temporary redirects — they get revisited on backport and can be dropped wholesale when v4 retires. Side effect:/docs/configurationand/docs/configuration/runtime-tuningare real pages again instead of being shadowed by the old fallback.docs/scripts/lint.ts): the two-space model swaps with the URL space. v5Space is the real HTTP space (unprefixed = v5,/v4-prefixed = v4); v4Space overlays the render-time href rewrite. Redirect destinations now resolve against the HTTP space, since redirects are matched server-side before any rendering — resolving them in the source space made the/v5/:path*expansion inherit v4 metadata.requires workflow 5.0.0-beta.33 or later→5.0.0,@betadist-tag install instructions → a concrete pre-release version), and de-prefixed 5 self-referential/v5/...links in v5 pages.Intentional oddity: v4 content links to
/v5/...rewriteHrefForVersionprefixes/docsand/worldshrefs at render time, so an unprefixed/docs/...href on a v4 page is pulled into the/v4view. A/v5/...href is therefore the only way for v4 content to point at the current version's page, and it resolves through the permanent redirect above. Three such links incontent/docs/v4andcontent/worlds/v4are kept deliberately; the lint models this.Verification
pnpm build: exit 0, 916 static pages./[lang]/docs/[[...slug]]renders 165 v5 pages,/[lang]/v4/docs/[[...slug]]141 v4 pages; cookbook 20 unprefixed / 21 under/v4(the v4-only recipe appears only there).bun ./scripts/lint.ts: 0 errored files, 0 errors.pnpm start:/docs→ 308/docs/getting-started;/v4/docs→ 307/v4/docs/getting-started;/v5/docs/getting-started→ 308 unprefixed;/v5→ 308/; markdown routes (/docs/deploying.md,/v4/docs/deploying.md,/v4/cookbook/advanced/child-workflows.md) 200;/sitemap.md200 with 166/v4/entries;/llms.txt200 with none (current sources only).Empty changeset — docs only, no published package changes.
Docs Preview
Base: https://workflow-docs-git-peter-docs-5-ga.vercel.sh (Vercel SSO)
Follow-up commit: What's new page, sidebar drill-in, migration skill
/docs/whats-newNew top-level page, first in the v5 sidebar above Getting Started. Highlights (turbo/time-to-first-step, cancellation, attributes,
start()in workflow context,getConflict()→Run, typed error serialization,linkedtracing +world.analytics, the configuration reference, React Router / NestJS / Python), then a breaking-change table, then a## Migrating from V4section pointing at the new skill.The table was built from
git diff origin/stable HEADon the package surface, not from the changelogs — the changelogs carry the whole 4.x history and over-report. Two claims I had drafted and dropped after checking the code:createLocalWorld()already took a config object in 4.x (args?: Partial<Config>on both branches, onlyrecoverActiveRuns's env fallback is new), andworld.steps.get's first parameter went fromstring | undefinedtostringrather than being newly added.getStreamChunks→streams.getChunkswas missing and is now listed./v4/docs/whats-newgets a temporary version-switcher fallback to/v4/docs, matching the other v5-only pages.Sidebar: no drill-in on
/docs/getting-started/docsredirects to/docs/getting-started, so that page is where every bare docs link lands — and geistdocs'findActiveRootSectionwas drilling the sidebar into the section pane there, replacing the top-level menu with the framework list. Fix is indocs-layout.tsx, the one place both trees are already transformed: on that exact slug, empty the section'schildren.findActiveRootSectiononly matches folders with children, so the root menu stays visible with the row highlighted as current. Nothing is lost — the page body is a card grid of exactly those children — and every other page in the section still drills in normally.Both docs layouts moved into their
[[...slug]]segment soparams.slugis available server-side; no client component, no hydration flash.defaultOpen: trueongetting-started/meta.jsonwas left alone after checking: it only feedsSidebarTree(mobile sheet + section pane), not the desktop root-pane drill-in, and it is a shared convention across root folders.Also fixed in passing:
editPathon the two current-version routes still pointed atdocs/content/docs/v4/{path}.skills/migrating-workflow-v4-to-v5Agent skill for the upgrade. Mechanical rules (async
getWorld()/createWorld()/getWorldHandlers(), removedrunStepandstepEntrypoint, removedworkflow/internal/privateand@workflow/core/private, theworld.streams.*rename and argument reorder,steps.get(runId, …),getConflict()→Run,NestLocalBuilder→workflow/nest/builder), a separate section for behavior changes it must report rather than silently "fix" (linkedtracing, precondition guard, turbo, typed errors, pre-upgradeworld-postgreserror: undefinedrows, in-flight runs not migrating), a fixed output shape, and a fail-the-migration checklist. The stream reorder gets called out explicitly because a rename alone type-checks while passing a stream name where a run ID belongs.Verification
bun ./scripts/lint.ts: 0 errored files, 0 errors — so every link on the new page resolves in both spaces.pnpm build: exit 0, 919 static pages (916 + the new page and itsog/ markdown variants).grep -c ">Overview"distinguishes the two states.en/docs/getting-started.html→ 0,en/docs/foundations.html→ 1,en/docs/getting-started/next.html→ 1. Same on v4:en/v4/docs/getting-started.html→ 0,en/v4/docs/foundations.html→ 1.foundations.html:docs/whats-newat 28378 precedesdocs/getting-startedat 28816.Docs Preview
Follow-up commit: highlights rewrite + full breaking-change sweep
Highlights rewritten around the headline items
The first pass listed changes in roughly the order I found them. This one leads with the story: faster and cheaper runs, broken into the three mechanisms that compound (single-pass invocations that only return to the queue at a wait/hook/timeout; persistence-layer work skipped where the runtime can determine it is safe; payload compression), then the measured outcome — time to first step roughly halved vs v4, and on Vercel Workflows up to 80% lower compute cost for many-small-step workflows and up to 70% lower storage cost.
Cost and performance are framed mechanically (fewer invocations, fewer queue messages, fewer storage round trips, fewer stored bytes) so they read as true on any World, with the percentages explicitly attributed to Vercel Workflows rather than implied as SDK-wide.
Then: regional run placement (
World.createRunId()+regionon queue dispatch, with world-vercel as the implementation), in-flight cancellation, attributes GA, and the redesigned trace viewer — which had been Vercel-Observability-only and now ships to open-source users vianpx workflow inspect runs --web. The five published Vercel changelogs are linked from the relevant sections. Everything else that was a###before (start()in a workflow, hook coordination, typed errors, tracing/analytics, the configuration reference, new frameworks) collapsed into a single "Also new in 5.0" bullet list.Breaking changes: second pass against
CHANGELOG-5.0.0-draft.mdThe first table was built from the package diff against
stable, which misses build-time and contract-level changes. Reading the draft changelog end to end surfaced ten more. The table is now split into Application code and Custom Worlds and build integrations, and gained:runId:workflowName:deploymentId— so a pre-upgrade run must not be replayed post-upgrade, which is a stronger statement than "in-flight runs don't migrate"MAX_EVENTS_EXCEEDED)experimental_setAttributes→setAttributesclientSWC transform mode{ timeoutSeconds }wait return)createWorld()factoryworld-localchunk layout move tostreams/chunks/<streamName>/Plus a note that
lazyDiscovery: trueis now thewithWorkflowdefault on Next.js ≥ 16.2.0-canary.48.The skill got the same items (bumped to
0.2.0): two new intake greps, rewrite rules forsetAttributesandmode: 'client', six new entries under "behavior changes that need a decision", the two World contract changes in step 4, and two new fail-the-migration checks.Two undocumented env vars
The event-limit row needed something to link to, and neither variable was documented. Verified the numbers in code rather than from the changelog:
DEFAULT_MAX_EVENTS_PER_RUN = 25_000inworld-local, and the Vercel backend's own default is also 25,000, both overridable.WORKFLOW_MAX_EVENTS_OVERRIDE→ Runtime Tuning, new "Limits" section (clamp-down only; never raises the World's limit)WORKFLOW_MAX_EVENTS→ Configuration › Worlds, under Local WorldVerification
bun ./scripts/lint.ts: 0 errored files, 0 errors.pnpm build: exit 0.id="workflow_max_events_override"inruntime-tuning.html,id="workflow_max_events"inworlds.html.__vercel_draftparam.Docs Preview