Repository navigation
Add separate release and nightly docs - #7871
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughDocumentation now supports release and nightly channels through channel-specific Vercel deployments, runtime URL routing, version switching, search assets, SEO metadata, and nightly indexing controls. ChangesDocumentation channels
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant DocsLayout
participant DocsSidebar
participant DocsVersionPicker
participant Browser
DocsLayout->>DocsSidebar: pass active documentation channel
DocsSidebar->>DocsVersionPicker: pass channel and localized labels
DocsVersionPicker->>Browser: navigate to selected channel URL
Possibly related PRs
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (4 errors, 2 warnings)
✅ Passed checks (19 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This comment has been minimized.
This comment has been minimized.
Greptile SummaryThis PR adds separate release and nightly documentation channels. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (10): Last reviewed commit: "Fix localized docs channel navigation" | Re-trigger Greptile |
| aria-label={`${releaseLabel} / ${nightlyLabel}`} | ||
| className="w-full rounded-md border border-border bg-background px-2 py-1.5 text-foreground" | ||
| value={channel} | ||
| onChange={(event) => { |
There was a problem hiding this comment.
When a reader is on a localized docs page like /ja/docs/..., the localized usePathname() value is the locale-stripped docs path. Building the cross-origin URL from that value sends the user to /docs/... on the other channel, so switching release/nightly loses the current locale instead of preserving the same documentation page.
Rule Used: Flag production user-facing text that is not fully... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e958e84. Configure here.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/docs-deploy-reusable.yml:
- Around line 25-28: Replace the inline GitHub Actions expression `${{
inputs.channel }}` in the deployment command within the run step with a
step-level environment variable assigned from `inputs.channel`, then reference
that shell environment variable in the `--build-env` argument to prevent
template injection.
- Line 19: Replace the bare ubuntu-latest value in the workflow’s runs-on
configuration with the repository variable vars.LINUX_RUNNER, ensuring the
workflow-guard-tests requirement is satisfied and runner selection remains
controlled by a single variable.
- Around line 17-21: Harden the deploy job by setting persist-credentials: false
on the actions/checkout step, and add an explicit top-level permissions block
granting only the minimal access required (or none if no repository permissions
are needed). Locate the deploy job and workflow-level configuration in
docs-deploy-reusable.yml.
In `@web/app/`[locale]/components/docs-version-picker.tsx:
- Around line 18-30: Fix the channel-switch navigation in the select onChange
handler by constructing the URL from the current pathname, search, and hash
rather than an absolute href, so the selected releaseOrigin or nightlyOrigin is
applied. Update the logic around pathname and window.location.assign while
preserving the existing channel selection behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: fe08584c-e7a3-4759-b83c-d0780bbcbf68
📒 Files selected for processing (9)
.github/workflows/docs-channels.yml.github/workflows/docs-deploy-reusable.ymlweb/app/[locale]/(landing)/docs/docs-nav.tsxweb/app/[locale]/(landing)/docs/layout.tsxweb/app/[locale]/components/docs-sidebar.tsxweb/app/[locale]/components/docs-version-picker.tsxweb/app/lib/docs-channel.tsweb/i18n/seo.tsweb/tests/docs-channel.test.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d17ad3087
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| mkdir -p web/.vercel | ||
| printf '{"orgId":"%s","projectId":"%s"}' "$VERCEL_ORG_ID" "$VERCEL_PROJECT_ID" > web/.vercel/project.json | ||
| bunx vercel deploy --prod --yes --token "$VERCEL_TOKEN" --build-env "CMUX_DOCS_CHANNEL=${{ inputs.channel }}" --build-env "CMUX_RELEASE_DOCS_ORIGIN=https://docs.cmux.com" --build-env "CMUX_NIGHTLY_DOCS_ORIGIN=https://nightly-docs.cmux.com" | ||
| working-directory: web |
There was a problem hiding this comment.
Deploy from a source tree that includes the changelog
In this workflow step the Vercel command runs with working-directory: web; vercel deploy --help describes deploying the current project directory, so the uploaded source tree will not include the repository-root CHANGELOG.md. The docs build reads that file from web/app/[locale]/(landing)/docs/changelog/page.tsx:241 and web/tools/build-docs-search.mjs:575, so either channel deployment can fail when the changelog page/search index is generated. Run Vercel from the repo root or explicitly copy/include CHANGELOG.md before deploying.
Useful? React with 👍 / 👎.
| on: | ||
| push: | ||
| branches: [main] | ||
| paths: ["web/**"] |
There was a problem hiding this comment.
Include non-web docs sources in the nightly trigger
For pushes to main, GitHub applies the branch and path filters together, so this nightly deploy only runs when a changed file matches web/**. The rendered docs changelog and docs search content are sourced from the repository-root CHANGELOG.md (web/app/[locale]/(landing)/docs/changelog/page.tsx:241 and web/tools/build-docs-search.mjs:575), so a changelog-only release/update on main will leave nightly docs stale until some unrelated web/ file changes. Add CHANGELOG.md (and any other non-web docs inputs) to this path filter.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 181685f. CHANGELOG.md now triggers nightly docs deployment.
— Claude Code
| value={channel} | ||
| onChange={(event) => { | ||
| const origin = event.target.value === "release" ? releaseOrigin : nightlyOrigin; | ||
| window.location.assign(new URL(pathname, origin).toString()); |
There was a problem hiding this comment.
Preserve the locale when switching docs channels
On non-English docs pages, usePathname() from the next-intl navigation returns the internal unprefixed route (the sidebar relies on this by comparing it to /docs/... hrefs), so building the destination from pathname sends a user on /ja/docs/ssh to /docs/ssh on the other docs origin. That drops localized readers back to English instead of preserving the current documentation path; reconstruct the URL with the active locale or use the browser pathname before assigning.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/docs-deploy-reusable.yml (1)
6-8: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winRestrict
channeltoreleaseornightly.This input accepts arbitrary strings, while
web/app/lib/docs-channel.tstreats every value other thannightlyasrelease. An invalid caller value can therefore deploy with mismatched channel and SEO behavior. Validate the value and fail before deployment.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/docs-deploy-reusable.yml around lines 6 - 8, Update the channel input definition in the reusable workflow to accept only the release or nightly values, using the workflow’s supported validation mechanism. Ensure invalid caller values fail before any deployment steps run, while preserving the existing required string input behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/app/`[locale]/(landing)/docs/base/page.tsx:
- Around line 3-7: Update the imports in the documentation page to use the
repository’s `@/` path aliases instead of deeply nested relative paths, including
buildAlternates and the referenced documentation components. Preserve the
existing imported symbols and behavior.
In `@web/app/`[locale]/components/docs-link.tsx:
- Around line 10-12: Parse string URLs into pathname, search, and hash
components before passing them to docsChannelUrl in
web/app/[locale]/components/docs-link.tsx lines 10-12 and
web/app/[locale]/components/docs-search.tsx line 193; preserve non-string href
handling in docs-link and pass each parsed component to the corresponding
docsChannelUrl arguments.
In `@web/app/`[locale]/components/docs-search.tsx:
- Around line 35-48: Update loadPagefind and the related module-scoped cache
state to track the channel associated with the cached promises; when the
requested channel differs from the cached channel, clear both pagefindPromise
and pagefindConfigurePromise before loading the new channel, then store the new
channel for subsequent calls.
In `@web/app/env.ts`:
- Around line 16-29: Ensure the docs-zone detection in isDocsZone works in the
browser by using a client-exposed channel value: either expose CMUX_DOCS_CHANNEL
through next.config.ts or add and consistently configure a public
NEXT_PUBLIC_CMUX_DOCS_CHANNEL fallback. Preserve the existing release/nightly
checks so skipEnvValidation and allowPreviewStackPlaceholders remain enabled for
credential-free docs deployments.
---
Outside diff comments:
In @.github/workflows/docs-deploy-reusable.yml:
- Around line 6-8: Update the channel input definition in the reusable workflow
to accept only the release or nightly values, using the workflow’s supported
validation mechanism. Ensure invalid caller values fail before any deployment
steps run, while preserving the existing required string input behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b429ad31-df42-4b17-b269-58d54b5b97c0
📒 Files selected for processing (25)
.github/workflows/docs-deploy-reusable.ymlweb/app/[locale]/(landing)/docs/base/page.tsxweb/app/[locale]/(landing)/docs/concepts/page.tsxweb/app/[locale]/(landing)/docs/configuration/page.tsxweb/app/[locale]/(landing)/docs/docs-nav.tsxweb/app/[locale]/(landing)/docs/getting-started/page.tsxweb/app/[locale]/(landing)/docs/ios/page.tsxweb/app/[locale]/(landing)/docs/keyboard-shortcuts/page.tsxweb/app/[locale]/(landing)/docs/layout.tsxweb/app/[locale]/(landing)/docs/session-restore/page.tsxweb/app/[locale]/(landing)/docs/skills/page.tsxweb/app/[locale]/(landing)/docs/task-manager/page.tsxweb/app/[locale]/(landing)/docs/vault/page.tsxweb/app/[locale]/(landing)/docs/workspace-groups/page.tsxweb/app/[locale]/components/docs-channel-context.tsxweb/app/[locale]/components/docs-link.tsxweb/app/[locale]/components/docs-pager.tsxweb/app/[locale]/components/docs-search.tsxweb/app/[locale]/components/docs-sidebar.tsxweb/app/[locale]/components/docs-version-picker.tsxweb/app/env.tsweb/app/lib/docs-channel.tsweb/next.config.tsweb/tests/client-config-env.test.tsweb/tests/docs-channel.test.ts
| import { buildAlternates } from "../../../../../i18n/seo"; | ||
| import { Callout } from "../../../components/callout"; | ||
| import { CodeBlock } from "../../../components/code-block"; | ||
| import { DocsHeading } from "../../../components/docs-heading"; | ||
| import { baseDocsLocales } from "../../../components/docs-nav-items"; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Use path aliases for consistency.
Other documentation pages in the codebase use the @/ path alias for absolute imports. Consider updating these deeply nested relative imports to match the repository convention.
♻️ Proposed refactor
-import { buildAlternates } from "../../../../../i18n/seo";
-import { Callout } from "../../../components/callout";
-import { CodeBlock } from "../../../components/code-block";
-import { DocsHeading } from "../../../components/docs-heading";
-import { baseDocsLocales } from "../../../components/docs-nav-items";
+import { buildAlternates } from "`@/i18n/seo`";
+import { Callout } from "`@/app/`[locale]/components/callout";
+import { CodeBlock } from "`@/app/`[locale]/components/code-block";
+import { DocsHeading } from "`@/app/`[locale]/components/docs-heading";
+import { baseDocsLocales } from "`@/app/`[locale]/components/docs-nav-items";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { buildAlternates } from "../../../../../i18n/seo"; | |
| import { Callout } from "../../../components/callout"; | |
| import { CodeBlock } from "../../../components/code-block"; | |
| import { DocsHeading } from "../../../components/docs-heading"; | |
| import { baseDocsLocales } from "../../../components/docs-nav-items"; | |
| import { buildAlternates } from "`@/i18n/seo`"; | |
| import { Callout } from "`@/app/`[locale]/components/callout"; | |
| import { CodeBlock } from "`@/app/`[locale]/components/code-block"; | |
| import { DocsHeading } from "`@/app/`[locale]/components/docs-heading"; | |
| import { baseDocsLocales } from "`@/app/`[locale]/components/docs-nav-items"; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/app/`[locale]/(landing)/docs/base/page.tsx around lines 3 - 7, Update the
imports in the documentation page to use the repository’s `@/` path aliases
instead of deeply nested relative paths, including buildAlternates and the
referenced documentation components. Preserve the existing imported symbols and
behavior.
| const href = typeof props.href === "string" | ||
| ? docsChannelUrl(channel, props.href) | ||
| : props.href; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Parse URLs before passing them to docsChannelUrl.
docsChannelUrl expects the pathname, search, and hash to be passed as separate arguments. Passing a full URL string (e.g., /docs#schema-reference) into the pathname parameter causes the internal rewrite regex /\/docs(?=\/|$)/ to fail, silently breaking the channel switch.
web/app/[locale]/components/docs-link.tsx#L10-L12: parseprops.hrefto separate the pathname from the query and hash components before callingdocsChannelUrl.web/app/[locale]/components/docs-search.tsx#L193-L193: parseresult.hrefto separate the pathname from the anchor hash before callingdocsChannelUrl.
🛠️ Proposed fixes
web/app/[locale]/components/docs-link.tsx
- const href = typeof props.href === "string"
- ? docsChannelUrl(channel, props.href)
- : props.href;
+ let href = props.href;
+ if (typeof href === "string") {
+ const splitIdx = href.search(/[?#]/);
+ if (splitIdx >= 0) {
+ href = docsChannelUrl(channel, href.slice(0, splitIdx), href.slice(splitIdx));
+ } else {
+ href = docsChannelUrl(channel, href);
+ }
+ }web/app/[locale]/components/docs-search.tsx
- router.push(docsChannelUrl(channel, result.href));
+ const splitIdx = result.href.search(/[?#]/);
+ const pathname = splitIdx >= 0 ? result.href.slice(0, splitIdx) : result.href;
+ const rest = splitIdx >= 0 ? result.href.slice(splitIdx) : "";
+ router.push(docsChannelUrl(channel, pathname, rest));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const href = typeof props.href === "string" | |
| ? docsChannelUrl(channel, props.href) | |
| : props.href; | |
| let href = props.href; | |
| if (typeof href === "string") { | |
| const splitIdx = href.search(/[?#]/); | |
| if (splitIdx >= 0) { | |
| href = docsChannelUrl(channel, href.slice(0, splitIdx), href.slice(splitIdx)); | |
| } else { | |
| href = docsChannelUrl(channel, href); | |
| } | |
| } |
| const href = typeof props.href === "string" | |
| ? docsChannelUrl(channel, props.href) | |
| : props.href; | |
| const splitIdx = result.href.search(/[?#]/); | |
| const pathname = splitIdx >= 0 ? result.href.slice(0, splitIdx) : result.href; | |
| const rest = splitIdx >= 0 ? result.href.slice(splitIdx) : ""; | |
| router.push(docsChannelUrl(channel, pathname, rest)); |
📍 Affects 2 files
web/app/[locale]/components/docs-link.tsx#L10-L12(this comment)web/app/[locale]/components/docs-search.tsx#L193-L193
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/app/`[locale]/components/docs-link.tsx around lines 10 - 12, Parse string
URLs into pathname, search, and hash components before passing them to
docsChannelUrl in web/app/[locale]/components/docs-link.tsx lines 10-12 and
web/app/[locale]/components/docs-search.tsx line 193; preserve non-string href
handling in docs-link and pass each parsed component to the corresponding
docsChannelUrl arguments.
| let pagefindPromise: Promise<PagefindModule> | null = null; | ||
| let pagefindConfigurePromise: Promise<PagefindModule> | null = null; | ||
|
|
||
| function importPagefind() { | ||
| function importPagefind(channel: "release" | "nightly") { | ||
| const pagefindBundlePath = `/_docs-search/${channel}/pagefind.js`; | ||
| return import( | ||
| /* webpackIgnore: true */ | ||
| pagefindBundlePath | ||
| ) as Promise<PagefindModule>; | ||
| } | ||
|
|
||
| async function loadPagefind() { | ||
| async function loadPagefind(channel: "release" | "nightly") { | ||
| if (!pagefindPromise) { | ||
| pagefindPromise = importPagefind().catch((error) => { | ||
| pagefindPromise = importPagefind(channel).catch((error) => { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Invalidate the cached Pagefind instance when the channel changes.
The loadPagefind function caches its initialization promises in module-scoped variables (pagefindPromise, pagefindConfigurePromise) without factoring in the active channel. If a user navigates client-side to a different channel, the search will continue querying the previously loaded channel's index.
🛠️ Proposed fix
Track the currently loaded channel and clear the promises if it changes:
let pagefindPromise: Promise<PagefindModule> | null = null;
let pagefindConfigurePromise: Promise<PagefindModule> | null = null;
+let loadedChannel: "release" | "nightly" | null = null;
async function loadPagefind(channel: "release" | "nightly") {
+ if (loadedChannel !== channel) {
+ pagefindPromise = null;
+ pagefindConfigurePromise = null;
+ loadedChannel = channel;
+ }
+
if (!pagefindPromise) {
pagefindPromise = importPagefind(channel).catch((error) => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let pagefindPromise: Promise<PagefindModule> | null = null; | |
| let pagefindConfigurePromise: Promise<PagefindModule> | null = null; | |
| function importPagefind() { | |
| function importPagefind(channel: "release" | "nightly") { | |
| const pagefindBundlePath = `/_docs-search/${channel}/pagefind.js`; | |
| return import( | |
| /* webpackIgnore: true */ | |
| pagefindBundlePath | |
| ) as Promise<PagefindModule>; | |
| } | |
| async function loadPagefind() { | |
| async function loadPagefind(channel: "release" | "nightly") { | |
| if (!pagefindPromise) { | |
| pagefindPromise = importPagefind().catch((error) => { | |
| pagefindPromise = importPagefind(channel).catch((error) => { | |
| let pagefindPromise: Promise<PagefindModule> | null = null; | |
| let pagefindConfigurePromise: Promise<PagefindModule> | null = null; | |
| let loadedChannel: "release" | "nightly" | null = null; | |
| function importPagefind(channel: "release" | "nightly") { | |
| const pagefindBundlePath = `/_docs-search/${channel}/pagefind.js`; | |
| return import( | |
| /* webpackIgnore: true */ | |
| pagefindBundlePath | |
| ) as Promise<PagefindModule>; | |
| } | |
| async function loadPagefind(channel: "release" | "nightly") { | |
| if (loadedChannel !== channel) { | |
| pagefindPromise = null; | |
| pagefindConfigurePromise = null; | |
| loadedChannel = channel; | |
| } | |
| if (!pagefindPromise) { | |
| pagefindPromise = importPagefind(channel).catch((error) => { |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/app/`[locale]/components/docs-search.tsx around lines 35 - 48, Update
loadPagefind and the related module-scoped cache state to track the channel
associated with the cached promises; when the requested channel differs from the
cached channel, clear both pagefindPromise and pagefindConfigurePromise before
loading the new channel, then store the new channel for subsequent calls.
# Conflicts: # web/app/[locale]/(landing)/docs/base/page.tsx # web/app/[locale]/(landing)/docs/ios/page.tsx # web/app/[locale]/components/docs-pager.tsx # web/app/[locale]/components/docs-sidebar.tsx # web/app/env.ts # web/i18n/seo.ts # web/tests/client-config-env.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/next.config.ts (1)
75-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAdd an exact-match rewrite for
/:locale/docsto prevent a trailing-slash redirect loop.Just as
/docsrequires an explicit exact match (Line 70) to prevent the:path*parameter from appending a trailing slash to the destination (${releaseDocsOrigin}/docs/), the localized equivalent/:locale/docsalso needs an exact match.Without it, requesting
/en/docsmatches/:locale/docs/:path*with an empty path, rewriting to${releaseDocsOrigin}/en/docs/. The upstream docs zone will respond with a 308 redirect to remove the trailing slash (Location: /en/docs), which the main site passes to the browser, causing an infinite redirect loop.🐛 Proposed fix
{ source: "/docs", destination: `${releaseDocsOrigin}/docs` }, { source: "/docs/:path*", destination: `${releaseDocsOrigin}/docs/:path*`, }, + { + source: "/:locale/docs", + destination: `${releaseDocsOrigin}/:locale/docs`, + }, { source: "/:locale/docs/:path*", destination: `${releaseDocsOrigin}/:locale/docs/:path*`, },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/next.config.ts` around lines 75 - 78, Add an exact-match rewrite for the localized docs root `/:locale/docs` alongside the existing `/:locale/docs/:path*` rule, targeting `${releaseDocsOrigin}/:locale/docs` without a trailing slash. Keep the wildcard rewrite for localized nested documentation paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/next.config.ts`:
- Around line 33-35: Update async rewrites() in next.config.ts so the isDocsZone
branch returns a self-rewrite mapping /_docs-assets/${docsChannel}/_next/:path*
to /_next/:path*, allowing assetPrefix requests to resolve when the docs zone is
visited directly while preserving existing non-docs rewrite behavior.
---
Outside diff comments:
In `@web/next.config.ts`:
- Around line 75-78: Add an exact-match rewrite for the localized docs root
`/:locale/docs` alongside the existing `/:locale/docs/:path*` rule, targeting
`${releaseDocsOrigin}/:locale/docs` without a trailing slash. Keep the wildcard
rewrite for localized nested documentation paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 17fb3cc3-2f13-4906-a023-9a83ae8095a4
📒 Files selected for processing (3)
.github/workflows/docs-channels.yml.github/workflows/docs-deploy-reusable.ymlweb/next.config.ts
| assetPrefix: isDocsZone ? `/_docs-assets/${docsChannel}` : undefined, | ||
| async rewrites() { | ||
| if (isDocsZone) return []; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Add a rewrite for assetPrefix in the docs zone to prevent broken assets on direct visits.
If the docs zone (docs.cmux.com or nightly-docs.cmux.com) is accessed directly (e.g., from a canonical search result), it will serve HTML requesting assets at /_docs-assets/${docsChannel}/_next/.... Since Next.js does not automatically handle serving assets at a path-based assetPrefix without a reverse proxy or rewrite, and isDocsZone currently returns [] for rewrites, these asset requests will 404 and break the page.
To ensure the docs zone works both when proxied through the main site and when visited directly, add a self-rewrite for the assetPrefix inside the docs zone.
🐛 Proposed fix
assetPrefix: isDocsZone ? `/_docs-assets/${docsChannel}` : undefined,
async rewrites() {
- if (isDocsZone) return [];
+ if (isDocsZone) {
+ return {
+ beforeFiles: [
+ {
+ source: `/_docs-assets/${docsChannel}/_next/:path*`,
+ destination: "/_next/:path*",
+ },
+ ],
+ };
+ }
return {
beforeFiles: [📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| assetPrefix: isDocsZone ? `/_docs-assets/${docsChannel}` : undefined, | |
| async rewrites() { | |
| if (isDocsZone) return []; | |
| assetPrefix: isDocsZone ? `/_docs-assets/${docsChannel}` : undefined, | |
| async rewrites() { | |
| if (isDocsZone) { | |
| return { | |
| beforeFiles: [ | |
| { | |
| source: `/_docs-assets/${docsChannel}/_next/:path*`, | |
| destination: "/_next/:path*", | |
| }, | |
| ], | |
| }; | |
| } | |
| return { | |
| beforeFiles: [ |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/next.config.ts` around lines 33 - 35, Update async rewrites() in
next.config.ts so the isDocsZone branch returns a self-rewrite mapping
/_docs-assets/${docsChannel}/_next/:path* to /_next/:path*, allowing assetPrefix
requests to resolve when the docs zone is visited directly while preserving
existing non-docs rewrite behavior.
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
| const releasePathname = docsChannelUrl("release", pathname); | ||
| const index = flat.findIndex((item) => item.href === releasePathname); |
There was a problem hiding this comment.
On localized docs pages, usePathname() can include the locale prefix, such as /ja/docs/configuration, while the nav items still use locale-neutral hrefs like /docs/configuration. docsChannelUrl("release", pathname) preserves that /ja prefix, so findIndex returns -1 and the previous/next pager disappears for localized docs pages. Normalize the pathname to the locale-neutral docs path before comparing it with nav item hrefs.

Adds distinct release and nightly documentation under one public hostname.
https://cmux.com/docs/..., built from the latest version tag, canonical and indexable.https://cmux.com/docs/nightly/..., built frommain, withnoindex, followand release canonicals./docs/basenow uses the normal docs sidebar and layout.mainto nightly and version tags to release.Verification:
cd web && bun run typecheck && bun test tests/docs-channel.test.ts tests/client-config-env.test.ts tests/docs-search-utils.test.ts tests/docs-search-index.test.tsLive preview: https://cmux-git-feat-docs-channels-manaflow.vercel.app
Summary by CodeRabbit
New Features
SEO