MAF-20888: feat(website): serve the documentation site from docs.moreh.io - #179
Open
seongsu-dev wants to merge 10 commits into
Open
MAF-20888: feat(website): serve the documentation site from docs.moreh.io#179seongsu-dev wants to merge 10 commits into
seongsu-dev wants to merge 10 commits into
Conversation
Record the implementation plan for serving the MIF Docusaurus site at docs.moreh.io and retiring test-docs.moreh.io. The plan retargets the docs deployment at moreh-dev/docs.moreh.io-published, the repository that already owns the docs.moreh.io Pages custom domain, rather than moving the domain onto moreh-dev/test-docs.moreh.io. The domain carries HSTS with a one-year max-age, so releasing and re-binding it would open an outage window that visitors cannot click through while GitHub reissues the certificate. Retargeting leaves the binding untouched, makes removal of the previous site implicit, and reduces rollback to a Pages source branch switch. Client-side redirects cover the 15 Retype paths that have a Docusaurus equivalent. Migrating the pages that exist only on the Retype site is out of scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Steps 1 and 2 ran: gitgod-bot resolves to admin on docs.moreh.io-published, matching its permission on the repository the docs already deploy to, and MIF_DOCS_TOKEN is an organization secret exposed to moreh-dev/mif with cd-docs.yaml as its only consumer in the organization. Step 3 stays open because the token's type and repository allowlist are readable only by an organization secrets administrator. Executing the task showed the gate was drawn too wide. Task 6 Steps 1 to 3 push to the new target while its Pages source still points at publish-build, so a rejected push fails the workflow without touching the live site and answers the scope question for free. Move the gate to Task 6 Step 4, the Pages source switch, which is the first irreversible action. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…saurus routes docs.moreh.io is served today by a Retype site whose paths are snake_case and carry no /docs/ prefix. Once this site takes over the domain, every existing deep link would 404. GitHub Pages offers no server-side redirect, so emit the redirects as static pages at build time. Fifteen Retype paths have a Docusaurus equivalent and are mapped. Six have no counterpart and will 404: /features/auto_scaling, /features/context_length_aware_routing, /features/expert_parallelism, /features/load_aware_routing, /best_practices/resource_allocation, and /benchmarking/more_benchmarking_for_deepseek_r1_671b_on_amd_mi300x_gpus/ performance_with_prefix_cache_and_load_aware_routing. The plugin is pinned to the core version rather than a caret range. A range resolves to the newest 3.10.x while the lockfile holds the rest of the tree at 3.10.0, which makes npm nest a second copy of @docusaurus/core under the plugin instead of sharing the host instance. Verified: docusaurus build passes with onBrokenLinks set to throw, all 15 pages emit a refresh pointing at the mapped destination, every destination is built, and the redirect pages stay out of sitemap.xml. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The install command as written used a caret range. npm resolved it to 3.10.2 while the lockfile held the rest of the tree at 3.10.0 and nested a duplicate @docusaurus/core under the plugin, which breaks the shared-core contract a Docusaurus plugin relies on. Replace it with a command that reads the core version and installs that exact version, and add a step that fails on the duplicate by checking the dedupe output and the lockfile diff size. Replace the existence check in Step 5 with one that reads the target out of each emitted page, compares it against the map, confirms the destination is built, and asserts the entry count. Checking only that a file exists proves the plugin ran, not that any redirect goes where it should. Add a step confirming the redirect stubs stay out of sitemap.xml. Also list package-lock.json in the file tables, which the commit step already staged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…h.io static/CNAME is copied into the build output and is what GitHub Pages reads from the published branch to keep the custom domain bound, so it has to name the domain the site will actually answer on. url drives canonical links, Open Graph tags, and the sitemap. Verified: docusaurus build passes, build/CNAME reads docs.moreh.io, all 50 sitemap entries carry the new host, and no file in the build output still mentions test-docs.moreh.io. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lished docusaurus deploy pushes the build output to the repository named by PROJECT_NAME. Point it at moreh-dev/docs.moreh.io-published, which already holds docs.moreh.io as its GitHub Pages custom domain, so the domain binding and its certificate are never released during the cutover. DEPLOYMENT_BRANCH was already main, which becomes that repository's Pages source. Add workflow_dispatch so the deployment can be run and re-run on demand. The workflow otherwise fires only on pushes touching website/**, which would force an unrelated content commit to retry a failed cutover. The trigger becomes available once this file is on the default branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both skills point readers at the published documentation as a fallback when the local path is unavailable, so the host has to match where the site will actually be served. Realign the guide-odin reference table. Its columns are padded to a fixed width and the new host is five characters shorter, which left every data row short of the header rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Task 3 counted sitemap hits with grep -c, but sitemap.xml is emitted as a single line, so that reports 1 regardless of how many entries carry the host. Count occurrences and sweep the whole build output instead. Task 4 verified the workflow with grep alone, which cannot see an indentation slip in the on: block. Add a YAML load that prints the triggers and the deploy env. Note that workflow_dispatch only becomes selectable once the file is on the default branch, so the first deployment must come from the push trigger. Task 5 expected a repository-wide grep for the old host to return nothing, but this plan names that host throughout as its subject, so the check could never pass. Exclude docs/specs and say why. Add a step for the fixed-width table in guide-odin, which the five-character-shorter host leaves misaligned. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deploy to publish-build rather than main. GitHub Pages serves docs.moreh.io from docs.moreh.io-published on publish-build, so writing there makes the deployment itself the cutover and removes the settings change entirely. The reason is permission, not elegance. Repointing Pages at another branch needs PUT /repos/.../pages, which requires admin on that repository, and the docs maintainers hold push. Routing the cutover through a settings change would make it, and every future rollback, wait on one of four repository admins. Deploying onto publish-build keeps both within push permission. Two protections that the other placement gave for free are bought back beforehand: the Retype build that publish-build carried is archived to a retype-archive branch so rollback stays a single force-push, and the Retype publish workflow in moreh-dev/docs.moreh.io is disabled so it can no longer force-push over the live site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Record the choice between the two placements for the build inside the chosen approach, and why the second wins: repointing the Pages source needs admin on docs.moreh.io-published, which the docs maintainers do not hold, so it would gate both the cutover and every rollback on someone else's availability. Restructure the two tasks that change as a result. Task 6 becomes the preparation the new placement requires — archive the served branch, prove the archive holds the site and its CNAME, disable the Retype publish workflow — and its first three steps are done. Task 7 becomes the cutover, which is now the merge itself, and verifies that the Pages configuration and certificate were never touched. Rewrite the rollback table: every row is now executable with push permission and none waits on a certificate. Task 1 Step 3 loses its gate, since a token too narrow to write to the target now yields a red workflow over an unchanged site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Merging triggers an immediate production domain cutover via a cross-repository Pages publish, which warrants final human verification of the live deployment/redirect behavior and rollback readiness.
Pull request overview
This PR switches the MIF Docusaurus documentation site to be served from the production domain docs.moreh.io by retargeting the deployment to the existing Pages-served branch (publish-build) of moreh-dev/docs.moreh.io-published, and adds client-side redirects for key legacy Retype paths to preserve deep links.
Changes:
- Retarget docs deployment workflow to push to
moreh-dev/docs.moreh.io-publishedonpublish-build, and addworkflow_dispatchfor manual reruns. - Update the published site’s custom domain (
website/static/CNAME) and Docusaurusurltohttps://docs.moreh.io/. - Add
@docusaurus/plugin-client-redirectsand configure redirects for legacy Retype routes; update skill references and add a detailed cutover runbook/spec.
File summaries
| File | Description |
|---|---|
.github/workflows/cd-docs.yaml |
Deploys docs to docs.moreh.io-published / publish-build and adds manual trigger. |
website/static/CNAME |
Updates GitHub Pages custom domain to docs.moreh.io. |
website/docusaurus.config.ts |
Updates canonical site URL and adds Retype→Docusaurus redirect map via client-redirects plugin. |
website/package.json |
Adds @docusaurus/plugin-client-redirects dependency. |
website/package-lock.json |
Locks the added redirect plugin at the resolved version. |
skills/guide-heimdall/SKILL.md |
Updates documentation URLs from test-docs.moreh.io to docs.moreh.io. |
skills/guide-odin/SKILL.md |
Updates documentation URLs from test-docs.moreh.io to docs.moreh.io. |
docs/specs/2026-09-04-docs-domain-cutover.md |
Adds the cutover implementation plan, verification steps, and rollback procedure. |
Review details
Files not reviewed (1)
- website/package-lock.json: Generated file
- Files reviewed: 7/8 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
hhk7734
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Serve the MIF documentation site from its production domain,
docs.moreh.io.docs.moreh.iois served by thepublish-buildbranch ofmoreh-dev/docs.moreh.io-published, and its content is a separate Retype-built site last updated 2026-03-25. The MIF Docusaurus documentation has been sitting on a temporary host,test-docs.moreh.io.This PR points the MIF documentation deployment at
publish-builddirectly. Neither the custom-domain binding nor the Pages configuration is touched, so no certificate is reissued and no GitHub settings change is required.Warning
Merging this PR runs the deployment workflow immediately, which switches
docs.moreh.ioto the new site. The merge is the cutover.Why this approach
Why not move the domain onto the
test-docs.moreh.iorepository —docs.moreh.ioresponds withstrict-transport-security: max-age=31556952(one year). Moving the domain to another repository opens a window while GitHub reissues the certificate, and during it a browser that has seen the site shows an error the visitor cannot click through. GitHub does not guarantee how long that takes.Why not repoint the Pages source at
main—PUT /repos/.../pagesrequires admin on that repository, and the documentation maintainers holdpush. That would gate both the cutover and every future rollback on reaching one of four repository admins. Publishing ontopublish-buildkeeps both withinpushpermission.Changes
In this repository
.github/workflows/cd-docs.yamltest-docs.moreh.io→docs.moreh.io-published, branchmain→publish-build. Addsworkflow_dispatchfor re-runswebsite/static/CNAMEtest-docs.moreh.io→docs.moreh.iowebsite/docusaurus.config.tsurl, and adds@docusaurus/plugin-client-redirectswith 15 redirects from the retired Retype pathswebsite/package.json,package-lock.json3.10.0— a range resolves to a newer patch and nests a duplicate@docusaurus/coreunder the pluginskills/guide-heimdall/SKILL.md,skills/guide-odin/SKILL.mddocs/specs/2026-09-04-docs-domain-cutover.mdOf the 21 non-root Retype paths, the 15 with a Docusaurus counterpart are redirected. The six without one —
auto_scaling,context_length_aware_routing,expert_parallelism,load_aware_routing,resource_allocation, and the additional benchmarking page — will return 404. Migrating that content is out of scope.Outside this repository — two state changes the diff cannot show
Neither lands in any git history, so they are recorded here.
moreh-dev/docs.moreh.io-publishedretype-archiveat837780c8publish-build, so the site being served today is archived for rollbackmoreh-dev/docs.moreh.ioretype-action-publish.ymlworkflow (disabled_manually)publish-buildwhenever thepublishbranch is updated, which after the cutover would silently replace the live site with the Retype buildgh workflow enable "Publish the final version to GitHub Pages" --repo moreh-dev/docs.moreh.ioretype-archivewas checked for content, not just for the commit: it carries the Retype site and aCNAMEofdocs.moreh.io. Without that file a restore would bring the content back but drop the custom domain.The sibling workflow
retype-action-main.yml(the internal preview,main→main-build) is deliberately left enabled. The internal Retype site is not part of this cutover.maininmoreh-dev/docs.moreh.iois 6 commits ahead ofpublish. Those 6 were never published todocs.moreh.io, so nothing that was live is lost by this cutover.Manual steps remaining after merge
test-docs.moreh.ioCNAME record in Cloudflare.moreh-dev/test-docs.moreh.ioand archive the repository.test-docs.moreh.iolinks inmoreh-dev/offline_installer(README.md,tools/moai-deploy.sh) andmoreh-dev/moai-engine(.agents/skills/vllm-rocm-build/SKILL.md) — tracked separately.The procedures are in Tasks 7 through 9 of
docs/specs/2026-09-04-docs-domain-cutover.md.Rollback
retype-archiveback ontopublish-buildpublish-buildanddocs.moreh.ioare unchanged. Fix the token scope and re-runEvery row is executable with
pushpermission, and none waits on a certificate.Testing
cd website && npm run buildpasses.onBrokenLinksis"throw", so a redirect destination that fails to resolve breaks the build rather than production.build/CNAMEreadsdocs.moreh.io; all 50 sitemap entries carry the new host and none carries the old one.test-docs.moreh.io.sitemap.xml, which holds 50 entries.envblock asserted —grepalone cannot catch an indentation slip in theon:block.test-docs.moreh.ioreturns nothing outsidedocs/specs/, where the document names the host it is retiring.Whether
MIF_DOCS_TOKENcan write todocs.moreh.io-publishedcannot be checked in advance, since its value is an organization secret.gitgod-botholdsadminon that repository; if the token's scope turns out to be too narrow the push is rejected, the workflow fails, and the live site is untouched.