') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); fix(docs): version-switcher fallback redirects for pages missing in one version by molebox · Pull Request #3003 · vercel/workflow · GitHub
Skip to content

fix(docs): version-switcher fallback redirects for pages missing in one version - #3003

Merged
VaguelySerious merged 1 commit into
mainfrom
docs-version-switch-redirects
Jul 20, 2026
Merged

fix(docs): version-switcher fallback redirects for pages missing in one version#3003
VaguelySerious merged 1 commit into
mainfrom
docs-version-switch-redirects

Conversation

@molebox

Copy link
Copy Markdown
Contributor

Summary

The docs version switcher swaps the /v5 route prefix without checking that the page exists in the target version (resolveRouteHref in @vercel/geistdocs is a pure prefix swap), so any page that exists in only one docs tree 404s on switch. Example on production: https://workflow-sdk.dev/v5/docs/configuration → switch to v4 → 404 at /docs/configuration.

This adds fallback redirects in docs/next.config.ts for every page that currently exists in only one version, generated by diffing docs/content/docs/v4 against docs/content/docs/v5. Each lands on the nearest equivalent in the target version (usually the section index):

v5-only pages (v5 → v4 switch), /docs/...:

  • configuration + all subpages → /docs/deploying
  • api-reference/workflow/set-attributes/docs/api-reference/workflow
  • api-reference/workflow-errors/precondition-failed-error → section index
  • api-reference/workflow-runtime/world/analytics → section index
  • changelog/{attributes-mvp,eager-processing,step-message-ownership}/docs/changelog
  • errors/abort-signal-timeout-in-workflow/docs/errors
  • foundations/cancellation, how-it-works/cancellation/docs/foundations (v4 has no how-it-works index)
  • getting-started/react-router (+ v7/v8) → /docs/getting-started
  • internal/{nitro-native-build,nitro-web-ui,serializable-abort-controller}/docs/internal
  • observability/{attributes,tracing}/docs/observability

v4-only pages (v4 → v5 switch), /v5/...:

  • api-reference/workflow-runtime/step-entrypoint/v5/docs/api-reference/workflow-runtime
  • cookbook/advanced/distributed-abort-controller/v5/cookbook (no /v5/cookbook/advanced index)

All fallbacks are permanent: false — they need revisiting when content is backported or when v5 becomes the default version (which swaps the trees served at /docs).

Also fixes a related live 404: /docs/api-reference/workflow/experimental-set-attributes redirected to /docs/api-reference/workflow/set-attributes, which doesn't exist on v4. It now lands on the section index directly (no redirect chain through the new fallback).

The proper fix for the whole class is upstream in geistdocs (version switcher falling back to the nearest existing page in the target tree); these redirects fix production now.

Testing

  • pnpm --filter docs build
  • next start + curl: all 16 fallback sources return 307 to their documented destinations, every destination returns 200, and existing pages (e.g. /docs/changelog/resilient-start, /cookbook/advanced/distributed-abort-controller, /v5/docs/configuration) still return 200

No changeset needed: docs-app-only change (pnpm changeset status --since=origin/main passes).

@molebox
molebox requested review from a team and ijjk as code ownersJuly 20, 2026 12:33
@changeset-bot

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e61026e

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

@vercel

vercelBot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

@VaguelySerious
VaguelySerious merged commit e892e8b into mainJul 20, 2026
52 of 53 checks passed
@VaguelySerious
VaguelySerious deleted the docs-version-switch-redirects branch July 20, 2026 17:54
@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for e892e8b (AI decision).

The commit only modifies docs/next.config.ts, which is part of the docs app outside of docs/content/ — a path explicitly not maintained on stable (docs are deployed only from main). Backporting would have no effect on the stable branch's placeholder docs app.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

e892e8b3c5dfe896894d6beba4e832767aef5ba6

pranaygp added a commit that referenced this pull request Jul 21, 2026
* origin/main: (21 commits)
docs(agents): note lint/format/typecheck are advisory, not blocking (#2886)
Retry transient connection timeouts (#3013)
fix(world-vercel): append caller User-Agent products instead of discarding them (#2998)
[ci] Enable NestJS e2e-vercel-prod and add to docs as "experimental" (#3011)
[ci] Benchmark comment: Best column + best/p75/p99 deltas (drop Avg/P10) (#3005)
docs: fall back to first child page for sidebar folders without an index (#3009)
[nest] Fix NestJS Vercel build output (#2988)
Avoid resolving run data for background steps (#2993)
chore(docs): update @vercel/geistdocs to 1.14.0 (#3002)
fix(docs): add version-switcher fallback redirects for pages missing in one version (#3003)
ci: update opencode to 1.18.4 and switch backport AI model to claude-fable-5 (#3006)
fix(core): batch stream writes via writeMulti (#2995)
perf(core): cache port discovery in step invocations for self-hosted worlds (#2996)
feat(web-shared): Alt+hover span measurement in the new trace viewer (#2985)
fix(world-postgres): throw EntityConflictError on duplicate run_created (#2983)
[ci] Run benchmarks in-deployment to avoid proxy overhead (#2967)
Enable additional perf optimizations when correctness guarantees are met (#2970)
perf(core): prepare replay payloads concurrently (#2980)
Fix dotted tsconfig alias workflow discovery (#2963)
Adjust helper position on trace viewer (#2968)
...
pranaygp added a commit that referenced this pull request Jul 21, 2026
* origin/main:
test: regression coverage for hook.resume() from isolated route bundles (o2flow beta.26 incident) (#3001)
docs(agents): note lint/format/typecheck are advisory, not blocking (#2886)
Retry transient connection timeouts (#3013)
fix(world-vercel): append caller User-Agent products instead of discarding them (#2998)
[ci] Enable NestJS e2e-vercel-prod and add to docs as "experimental" (#3011)
[ci] Benchmark comment: Best column + best/p75/p99 deltas (drop Avg/P10) (#3005)
docs: fall back to first child page for sidebar folders without an index (#3009)
[nest] Fix NestJS Vercel build output (#2988)
Avoid resolving run data for background steps (#2993)
chore(docs): update @vercel/geistdocs to 1.14.0 (#3002)
fix(docs): add version-switcher fallback redirects for pages missing in one version (#3003)
ci: update opencode to 1.18.4 and switch backport AI model to claude-fable-5 (#3006)
fix(core): batch stream writes via writeMulti (#2995)
pranaygp added a commit that referenced this pull request Jul 22, 2026
* origin/main: (162 commits)
Implement `max_events` per run limit (#2986)
[core] Enforce maxRetries for steps that time out (#3035)
[world-vercel] Idempotent retry policy for stream close (5xx retriable) (#3038)
[world] Guard hook_received against a concurrent run termination (#2987)
docs: fix stale/incorrect v5 API reference details (#3017)
Default WORKFLOW_PRECONDITION_GUARD on (#2946)
docs: replace migration guides with a Comparisons section (#2676)
feat(core): add experimental Hook minimum retention (#2865)
test: regression coverage for hook.resume() from isolated route bundles (o2flow beta.26 incident) (#3001)
docs(agents): note lint/format/typecheck are advisory, not blocking (#2886)
Retry transient connection timeouts (#3013)
fix(world-vercel): append caller User-Agent products instead of discarding them (#2998)
[ci] Enable NestJS e2e-vercel-prod and add to docs as "experimental" (#3011)
[ci] Benchmark comment: Best column + best/p75/p99 deltas (drop Avg/P10) (#3005)
docs: fall back to first child page for sidebar folders without an index (#3009)
[nest] Fix NestJS Vercel build output (#2988)
Avoid resolving run data for background steps (#2993)
chore(docs): update @vercel/geistdocs to 1.14.0 (#3002)
fix(docs): add version-switcher fallback redirects for pages missing in one version (#3003)
ci: update opencode to 1.18.4 and switch backport AI model to claude-fable-5 (#3006)
...
# Conflicts:
#	docs/content/docs/v4/deploying/meta.json
#	docs/content/docs/v5/deploying/meta.json
#	packages/core/src/runtime.ts
#	packages/world-local/src/index.ts
#	packages/world-postgres/src/index.ts
#	packages/world/src/events.ts
#	packages/world/src/interfaces.ts
#	packages/world/src/recovery.ts
#	workbench/nest/src/main.ts
#	workbench/sveltekit/src/hooks.server.ts
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@molebox@VaguelySerious