Skip to content

Add separate release and nightly docs - #7871

Merged
lawrencecchen merged 11 commits into
mainfrom
feat-docs-channels
Jul 15, 2026
Merged

lawrencecchen merged 11 commits into
mainfrom
feat-docs-channels

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Adds distinct release and nightly documentation under one public hostname.

  • Release docs: https://cmux.com/docs/..., built from the latest version tag, canonical and indexable.
  • Nightly docs: https://cmux.com/docs/nightly/..., built from main, with noindex, follow and release canonicals.
  • The version picker preserves the page path, query, and hash.
  • Navigation, pagination, search, and docs links remain in the selected channel.
  • /docs/base now uses the normal docs sidebar and layout.
  • Vercel origin projects and repository secrets are provisioned. GitHub Actions deploys main to 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.ts

Live preview: https://cmux-git-feat-docs-channels-manaflow.vercel.app

Summary by CodeRabbit

  • New Features

    • Added separate release and nightly documentation channels.
    • Added a documentation version picker for switching channels while preserving the current page, query, and anchor.
    • Updated documentation links, navigation, pagination, and search to remain within the selected channel.
    • Added automated deployments for nightly builds and tagged releases.
  • SEO

    • Nightly documentation is marked as noindex while remaining crawlable.
    • Improved canonical and alternate URLs for documentation pages.

@vercel

vercel Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jul 15, 2026 8:45pm
cmux-staging Building Building Preview, Comment Jul 15, 2026 8:45pm

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Documentation now supports release and nightly channels through channel-specific Vercel deployments, runtime URL routing, version switching, search assets, SEO metadata, and nightly indexing controls.

Changes

Documentation channels

Layer / File(s) Summary
Channel resolution and SEO origins
web/app/lib/docs-channel.ts, web/i18n/seo.ts, web/tests/docs-channel.test.ts
Adds typed channel and URL helpers, keeps documentation canonical URLs on https://cmux.com, and tests default, nightly, and path-rewriting behavior.
Documentation channel navigation
web/app/[locale]/(landing)/docs/..., web/app/[locale]/components/docs-*.tsx
Propagates the active channel through docs navigation, adds localized release/nightly selection, and applies channel-aware links, pager navigation, and search behavior.
Channel routing and search assets
web/next.config.ts, web/app/env.ts, web/tests/client-config-env.test.ts
Adds docs-zone rewrites, asset prefixes, nightly no-index headers, relaxed docs environment validation, and coverage for credential-free docs deployments.
Channel deployment workflows
.github/workflows/docs-*.yml
Adds reusable Vercel deployment and routes main pushes to nightly documentation and version tags to release documentation.

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
Loading

Possibly related PRs

  • manaflow-ai/cmux#7386: Related environment validation changes and client-config coverage for docs-channel deployments.
  • manaflow-ai/cmux#7561: Related canonical and alternate URL generation changes in web/i18n/seo.ts.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (4 errors, 2 warnings)

Check name Status Explanation Resolution
Cmux Swift Concurrency ❌ Error PR adds new Combine app-state models (WorkspaceTodoState, MobileWorkspaceListObserver publishers), which the rule flags as legacy async/state usage. Replace ObservableObject/@Published/AnyPublisher with Observation or async state; drive invalidation from async accessors/streams on the main actor.
Cmux Swift @Concurrent ❌ Error DeviceRegistryClient is @MainActor and awaits CmxCredentialedHTTPSession.data(for:) with no @concurrent, so network work stays on the UI actor. Mark the HTTP helper @concurrent or move the request off @MainActor before awaiting it.
Cmux Swiftui State Layout ❌ Error PR adds new WorkspaceTodoPanel/WorkspaceTodoState as ObservableObject with @Published fields, which the rule says should use @Observable-style state. Migrate the new todo models to @Observable/value snapshots, or justify why legacy ObservableObject is required; avoid adding fresh @Published ownership.
Cmux No Test Or Debug Seam In Production Source ❌ Error MobileShellComposite.swift adds *ForTesting accessors and widens private methods, creating a test seam in production Sources/. Move the observation into Tests/ via @testable import; keep production code seam-free, or isolate any real debug-only utility in a dedicated debug file/folder.
Description check ⚠️ Warning It covers summary and verification, but misses the required Testing, Demo Video, Review Trigger, and Checklist sections. Add the missing template sections with explicit testing steps, a demo video link, the review-trigger block, and the checklist.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: splitting docs into release and nightly channels.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed No actor-isolation regression: new MainActor protocols are implemented by MainActor runtimes/UI, and mutable Sendable helpers use locks or documented safety wrappers.
Cmux Swift Blocking Runtime ✅ Passed Merge-base diff touches only web/workflow/test files; no Swift files are changed, so the Swift blocking-runtime rule isn’t applicable.
Cmux Browser Automation Off-Main ✅ Passed Waiting browser.* methods stay in socketWorkerMethods/socketWorkerV2Response; the diff only removes stale main-switch cases and adds policy tests.
Cmux Expensive Synchronous Load ✅ Passed PASS: The PR diff against origin/main contains no .swift files, so it cannot add or move any Swift synchronous agent-history load on main/interactive paths.
Cmux Cache Substitution Correctness ✅ Passed The diff only adds docs-channel routing/deploy logic and UI helpers; it doesn’t replace authoritative persistence/history/undo/snapshot reads with stale caches.
Cmux No Hacky Sleeps ✅ Passed No new fixed sleeps, timers, polling, or wall-clock waits were added in the PR diff; changes are routing/metadata/test-only.
Cmux Algorithmic Complexity ✅ Passed PASS: New scans are over fixed docs nav/static locale lists or capped search results (8); no nested or unbounded rescans were introduced in touched runtime paths.
Cmux Swift Package Boundaries ✅ Passed The PR diff contains only web/workflow files; no .swift files or Swift source changes are present, so the Swift package-boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed No changed .gitignore ignores Package.resolved; IrohTransport and ios/cmuxPackage have matching local Package.resolved diffs, and the Xcode project has a root lockfile diff.
Cmux Swift Logging ✅ Passed Diff vs origin/main changes only .yml/.ts/.tsx files; no Swift production code was touched, so Swift logging rules don’t apply.
Cmux User-Facing Error Privacy ✅ Passed No changed user-facing copy exposes vendor names or secrets; the touched strings are internal env-validation messages/tests, and docs UI text stays generic.
Cmux Full Internationalization ✅ Passed New docs UI/metadata reads existing next-intl keys; release/nightly labels exist in all locales, and no message catalogs were edited.
Cmux Architecture Rethink ✅ Passed PR only changes web/workflow files; no Swift files or Swift architectural patterns are introduced, so the Swift rethink rule is not applicable.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR Swift changes are refactors/tests only; no production WindowGroup/NSWindow/NSPanel ownership or cmuxAuxiliaryWindowIdentifiers changes.
Cmux Source Artifacts ✅ Passed All changed paths are source/workflow/config/test files; no .vercel, .next, temp, cache, or other artifact paths appear in the diff.
Cmux No Ambient Global State ✅ Passed PR changes are web/workflow only; no Swift files were added or modified, so the Swift ambient-global-state rule doesn’t apply.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-docs-channels

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread .github/workflows/docs-deploy-reusable.yml Outdated
Comment thread web/app/[locale]/components/docs-version-picker.tsx Outdated
@blacksmith-sh

This comment has been minimized.

@greptile-apps

greptile-apps Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds separate release and nightly documentation channels. The main changes are:

  • New GitHub Actions workflows for release and nightly docs deployments.
  • Docs channel routing, rewrites, and channel-specific asset/search paths.
  • A docs version picker that preserves the current page, query, and hash.
  • Channel-aware docs links, sidebar, pager, navigation, metadata, and search indexing.
  • Tests covering channel URLs, search indexing, SEO middleware, and docs-only environment handling.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
web/app/[locale]/components/docs-version-picker.tsx Adds the release/nightly selector and preserves the current docs path, query, hash, and locale during channel switches.
web/app/[locale]/components/docs-pager.tsx Updates previous and next links to use channel-aware docs paths while matching localized routes against locale-neutral nav items.
web/app/lib/docs-channel.ts Adds shared helpers for docs channel detection, channel URL conversion, release availability, and nav path normalization.
web/next.config.ts Adds docs-channel rewrites, asset prefixes, search proxy paths, base-doc redirects, and nightly noindex headers.
web/proxy.ts Lets public docs requests pass through to the release or nightly docs origins before normal locale middleware runs.

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) => {

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.

P1 Locale Prefix Gets Dropped

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!

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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).

Fix All in Cursor

❌ 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.

Comment thread web/app/[locale]/components/docs-version-picker.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2b122c4 and 5d17ad3.

📒 Files selected for processing (9)
  • .github/workflows/docs-channels.yml
  • .github/workflows/docs-deploy-reusable.yml
  • web/app/[locale]/(landing)/docs/docs-nav.tsx
  • web/app/[locale]/(landing)/docs/layout.tsx
  • web/app/[locale]/components/docs-sidebar.tsx
  • web/app/[locale]/components/docs-version-picker.tsx
  • web/app/lib/docs-channel.ts
  • web/i18n/seo.ts
  • web/tests/docs-channel.test.ts

Comment thread .github/workflows/docs-deploy-reusable.yml
Comment thread .github/workflows/docs-deploy-reusable.yml Outdated
Comment thread .github/workflows/docs-deploy-reusable.yml Outdated
Comment thread web/app/[locale]/components/docs-version-picker.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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 👍 / 👎.

Comment thread .github/workflows/docs-channels.yml Outdated
on:
push:
branches: [main]
paths: ["web/**"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Restrict channel to release or nightly.

This input accepts arbitrary strings, while web/app/lib/docs-channel.ts treats every value other than nightly as release. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 82b60ba and 6c7581e.

📒 Files selected for processing (25)
  • .github/workflows/docs-deploy-reusable.yml
  • web/app/[locale]/(landing)/docs/base/page.tsx
  • web/app/[locale]/(landing)/docs/concepts/page.tsx
  • web/app/[locale]/(landing)/docs/configuration/page.tsx
  • web/app/[locale]/(landing)/docs/docs-nav.tsx
  • web/app/[locale]/(landing)/docs/getting-started/page.tsx
  • web/app/[locale]/(landing)/docs/ios/page.tsx
  • web/app/[locale]/(landing)/docs/keyboard-shortcuts/page.tsx
  • web/app/[locale]/(landing)/docs/layout.tsx
  • web/app/[locale]/(landing)/docs/session-restore/page.tsx
  • web/app/[locale]/(landing)/docs/skills/page.tsx
  • web/app/[locale]/(landing)/docs/task-manager/page.tsx
  • web/app/[locale]/(landing)/docs/vault/page.tsx
  • web/app/[locale]/(landing)/docs/workspace-groups/page.tsx
  • web/app/[locale]/components/docs-channel-context.tsx
  • web/app/[locale]/components/docs-link.tsx
  • web/app/[locale]/components/docs-pager.tsx
  • web/app/[locale]/components/docs-search.tsx
  • web/app/[locale]/components/docs-sidebar.tsx
  • web/app/[locale]/components/docs-version-picker.tsx
  • web/app/env.ts
  • web/app/lib/docs-channel.ts
  • web/next.config.ts
  • web/tests/client-config-env.test.ts
  • web/tests/docs-channel.test.ts

Comment on lines +3 to +7
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";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 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.

Suggested change
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.

Comment on lines +10 to +12
const href = typeof props.href === "string"
? docsChannelUrl(channel, props.href)
: props.href;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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: parse props.href to separate the pathname from the query and hash components before calling docsChannelUrl.
  • web/app/[locale]/components/docs-search.tsx#L193-L193: parse result.href to separate the pathname from the anchor hash before calling docsChannelUrl.
🛠️ 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.

Suggested change
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);
}
}
Suggested change
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.

Comment on lines 35 to +48
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) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
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.

Comment thread web/app/env.ts
# 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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Add an exact-match rewrite for /:locale/docs to prevent a trailing-slash redirect loop.

Just as /docs requires 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/docs also needs an exact match.

Without it, requesting /en/docs matches /: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

📥 Commits

Reviewing files that changed from the base of the PR and between ccc9815 and 181685f.

📒 Files selected for processing (3)
  • .github/workflows/docs-channels.yml
  • .github/workflows/docs-deploy-reusable.yml
  • web/next.config.ts

Comment thread web/next.config.ts Outdated
Comment on lines +33 to +35
assetPrefix: isDocsZone ? `/_docs-assets/${docsChannel}` : undefined,
async rewrites() {
if (isDocsZone) return [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
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.

@cursor

cursor Bot commented Jul 15, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot 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.

Comment on lines +19 to +20
const releasePathname = docsChannelUrl("release", pathname);
const index = flat.findIndex((item) => item.href === releasePathname);

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.

P1 Localized pager disappears

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.

@lawrencecchen
lawrencecchen merged commit 8d9c6fd into main Jul 15, 2026
7 of 9 checks passed

This branch was successfully deployed

1 active deployment
Preview – cmux — a4f425f6 Deployed Jul 15, 2026 by vercel[bot]
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.

1 participant