Skip to content

ci(docs): publish Fern docs from staging and releases - #6157

Merged
miyoungc merged 8 commits into
mainfrom
agent/fern-doc-workflows
Jul 2, 2026
Merged

miyoungc merged 8 commits into
mainfrom
agent/fern-doc-workflows

Conversation

@miyoungc

@miyoungc miyoungc commented Jul 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR adds Fern publishing automation for NemoClaw docs so merged docs changes publish to staging and release tags publish to the public instance.
It points PR previews at the staging Fern instance, keeps the Fern CLI version sourced from fern/fern.config.json, and removes PR preview deployments after staging publish succeeds.

Changes

  • Add a staging Fern instance in fern/docs.yml.
  • Add Docs / Publish Staging for main pushes that touch docs, Fern config, or docs build inputs.
  • Add Docs / Publish Public for v*.*.* release tag pushes.
  • Run npm run docs:sync-agent-variants and npm run docs before both Fern publish commands.
  • Point PR preview generation at the staging instance with --instance, disable checkout credential persistence, and validate the extracted Fern preview URL before posting it.
  • Delete merged PR Fern previews after staging publish succeeds with fern docs preview delete --id pr-<number>.
  • Guard public publishing with the docs-public environment and an ancestry check that runs immediately after checkout and requires release tag commits to be reachable from origin/main before any npm/Fern commands.
  • Disable checkout credential persistence for staging and public publish jobs; disable setup-node package-manager caching for the public publish job.
  • Keep workflow validation lightweight through existing YAML checks, hooks, and CI rather than adding a bespoke static checker.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification: CI/Fern publishing configuration only; no runtime product code paths changed. The workflows are covered by existing YAML validation, pre-commit/pre-push hooks, and CI execution on this PR.
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: This changes repository publishing automation, not user-facing product behavior or docs content.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification:
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
  • Full npm test passes (broad runtime changes only)
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Additional verification:

  • npm ci --ignore-scripts
  • npm run docs:sync-agent-variants
  • npm run docs passed with 0 Fern errors; existing Fern warnings remain for the published-redirects network check and light-mode accent contrast.
  • FERN_VERSION=$(node -p "require('./fern/fern.config.json').version") && cd fern && npx --yes "fern-api@${FERN_VERSION}" check --warnings
  • npm run typecheck:cli
  • npm run build:cli
  • Pre-commit hooks passed during commits, including YAML checks, repository checks, and secret scanning.
  • Pre-push hooks passed after pushes.
  • git diff --check
  • ReadLints reported no issues for changed workflow/config files.
  • Manual staging publish succeeded: https://nvidia-nemoclaw-staging.docs.buildwithfern.com/nemoclaw, deployment 019f2071-e6b7-72d2-be92-21bf324c96c1.

Reviewer notes:

  • fern/fern.config.json is the intended single source of truth for the Fern CLI version, so this PR intentionally does not duplicate that value in workflow YAML.
  • A bespoke static workflow checker was removed to avoid adding maintenance-heavy validation for a small publishing workflow change.
  • Checkout v7 updates are left to the existing dependency/update PR rather than mixing action-version churn into this PR.

Signed-off-by: Miyoung Choi miyoungc@nvidia.com

@miyoungc miyoungc self-assigned this Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

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

This PR updates preview generation to target a configured staging Fern instance, adds a main-branch staging publish workflow with preview cleanup, adds a tag-triggered public publish workflow, and registers an additional staging docs instance.

Changes

Docs Publishing and Preview Configuration

Layer / File(s) Summary
Preview and Fern config updates
.github/workflows/docs-preview-pr.yaml, fern/docs.yml
Adds a staging instance env value, uses it for preview generation and URL validation, and registers a staging Fern docs instance.
Staging publish workflow
.github/workflows/docs-publish-staging.yaml
Adds the main-branch staging publish workflow, doc-path filters, staging publish steps, and preview deletion job.
Public publish workflow
.github/workflows/docs-publish-public.yaml
Adds the tag-triggered public publish workflow, release-tag guard, and public docs publish step.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
    participant GitHubActions as GitHub Actions
    participant NpmScripts as npm scripts
    participant FernCLI as fern-api CLI
    participant FernDocs as Fern Docs Instance
    participant GitHubAPI as GitHub API

    GitHubActions->>NpmScripts: npm run docs:sync-agent-variants
    GitHubActions->>NpmScripts: npm run docs
    GitHubActions->>FernCLI: generate --docs --instance staging
    FernCLI->>FernDocs: publish staging docs
    GitHubActions->>GitHubAPI: find PRs for commit SHA
    GitHubActions->>FernCLI: docs preview delete --id pr-<number>
    FernCLI->>FernDocs: delete preview

    GitHubActions->>GitHubActions: verify tag commit is reachable from origin/main
    GitHubActions->>FernCLI: generate --docs --instance public
    FernCLI->>FernDocs: publish public docs
Loading

Suggested reviewers: ericksoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: automated Fern docs publishing from staging and release tags.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/fern-doc-workflows

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

@github-code-quality

github-code-quality Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the agent/fern-doc-workf... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main agent/fern-doc-workf... 0679c8e +/-
nemoclaw/src/se...cret-scanner.ts — 100% —
nemoclaw/src/commands/slash.ts — 100% —
nemoclaw/src/li...bprocess-env.ts — 100% —
nemoclaw/src/bl...eprint/state.ts — 98% —
nemoclaw/src/onboard/config.ts — 98% —
nemoclaw/src/bl...int/snapshot.ts — 97% —
nemoclaw/src/bl...print/runner.ts — 95% —
nemoclaw/src/co...ration-state.ts — 94% —
nemoclaw/src/bl...ate-networks.ts — 94% —
nemoclaw/src/index.ts — 94% —

TypeScript / code-coverage/cli

The overall coverage in the agent/fern-doc-workf... branch is 69%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main agent/fern-doc-workf... 0679c8e +/-
src/lib/actions...dbox/rebuild.ts — 82% —
src/lib/actions...all/run-plan.ts — 80% —
src/lib/state/o...oard-session.ts — 80% —
src/lib/shields/index.ts — 75% —
src/lib/state/sandbox.ts — 73% —
src/lib/onboard/preflight.ts — 69% —
src/lib/onboard...er-gpu-patch.ts — 59% —
src/lib/actions...licy-channel.ts — 58% —
src/lib/policy/index.ts — 56% —
src/lib/onboard.ts — 20% —

Updated July 02, 2026 02:07 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: None
Optional E2E: None

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • None. No NemoClaw E2E jobs are required because this PR only changes documentation publishing/preview workflows and Fern docs configuration. These changes cannot affect runtime assistant behavior, onboarding/resume state machines, sandbox lifecycle, credentials, network policy, inference routing, or real user flows.

Optional E2E

  • None.

New E2E recommendations

  • None.

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: None
Optional E2E targets: None

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • None. Changes are limited to docs/Fern configuration and docs publishing/preview workflows, with no changes to .github/workflows/e2e.yaml, test/e2e/, target registry/runtime support, fixtures, or E2E-relevant product paths.

Optional E2E targets

  • None.

Relevant changed files

  • None.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

PR Review Advisor — Changes requested

Merge posture: Do not merge yet
Primary next action: Resolve or justify PRA-1: Source-of-truth review needed: Fern preview URL extraction from CLI stdout in `.github/workflows/docs-preview-pr.yaml`.
Open items: 0 required · 5 warnings · 0 suggestions · 4 test follow-ups
Since last review: 0 prior items resolved · 5 still apply · 0 new items found

Action checklist

  • PRA-1 Resolve or justify: Source-of-truth review needed: Fern preview URL extraction from CLI stdout in `.github/workflows/docs-preview-pr.yaml`
  • PRA-2 Resolve or justify: Keep FERN_TOKEN out of PR-controlled preview code in .github/workflows/docs-preview-pr.yaml:69
  • PRA-3 Resolve or justify: Do not post a preview URL after Fern exits nonzero in .github/workflows/docs-preview-pr.yaml:94
  • PRA-4 Resolve or justify: Document or isolate the Fern stdout preview URL contract in .github/workflows/docs-preview-pr.yaml:76
  • PRA-5 Resolve or justify: Add local regression checks for docs workflow security invariants in scripts/checks/run.ts:14
  • PRA-T1 Add or justify test follow-up: Add local regression checks for docs workflow security invariants
  • PRA-T2 Add or justify test follow-up: Acceptance clause
  • PRA-T3 Add or justify test follow-up: Acceptance clause
  • PRA-T4 Add or justify test follow-up: Fern preview URL extraction from CLI stdout in `.github/workflows/docs-preview-pr.yaml`

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify security .github/workflows/docs-preview-pr.yaml:69 Remove repository-controlled validation from the token-bearing step and rely on the prior non-secret validation. Keep the Fern CLI version, organization, and expected preview host in trusted workflow constants or a small allowlist, rather than reading them from PR-controlled checkout content while `FERN_TOKEN` is set.
PRA-3 Resolve/justify correctness .github/workflows/docs-preview-pr.yaml:94 Check `STATUS` before writing `preview_url` or posting the comment, and fail immediately on nonzero status unless Fern documents a nonzero-with-valid-preview contract and the workflow handles that case explicitly.
PRA-4 Resolve/justify architecture .github/workflows/docs-preview-pr.yaml:76 Document the Fern CLI boundary in the workflow or move the parsing into a small checked helper with explicit valid, empty, wrong-host, and nonzero-status cases. Prefer a source-level structured output or API if Fern supports one; otherwise define the removal condition for this parsing.
PRA-5 Resolve/justify tests scripts/checks/run.ts:14 Add a small focused checker under `scripts/checks` and register it in `scripts/checks/run.ts`. Keep it concrete to these workflows rather than adding a generalized workflow validation framework.
Review findings by urgency: 0 required fixes, 5 items to resolve/justify, 0 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: Fern preview URL extraction from CLI stdout in `.github/workflows/docs-preview-pr.yaml`

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing; add shell-unit or static coverage for valid output, empty output, wrong-host output, and nonzero-status-with-valid-URL output.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: The workflow extracts `Published docs to ...` with `sed`, validates the URL, and writes `preview_url` without a documented source contract or status guard.

PRA-2 Resolve/justify — Keep FERN_TOKEN out of PR-controlled preview code

  • Location: .github/workflows/docs-preview-pr.yaml:69
  • Category: security
  • Problem: The secret-bearing `Generate preview URL` step still runs checkout-controlled logic before and during the Fern invocation: `npm run docs:check-agent-variants`, `node -p "require('./fern/fern.config.json').version"`, `node -p "require('./fern/fern.config.json').organization"`, and `npx --yes "fern-api@${FERN_VERSION}"`. The earlier non-secret `Validate docs` step already runs `npm run docs`, which maps to `docs:strict` and starts with `docs:check-agent-variants`, so the duplicate repository script unnecessarily expands the trusted-code boundary while `FERN_TOKEN` is present.
  • Impact: For same-repository PR branches where repository secrets are available, a compromised branch can alter docs scripts or `fern/fern.config.json` to execute attacker-controlled package or repository code while `FERN_TOKEN` is in the environment, or select an unexpected `fern-api` package version for the secret-bearing command.
  • Recommended action: Remove repository-controlled validation from the token-bearing step and rely on the prior non-secret validation. Keep the Fern CLI version, organization, and expected preview host in trusted workflow constants or a small allowlist, rather than reading them from PR-controlled checkout content while `FERN_TOKEN` is set.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 62-94 and `package.json` scripts; confirm the step with `env.FERN_TOKEN` no longer runs `npm run` and no longer chooses the `npx fern-api` version or preview organization from PR-controlled config without a trusted pin or allowlist.
  • Missing regression test: Add a focused static workflow check that fails if any `.github/workflows/docs-preview-pr.yaml` step with `FERN_TOKEN` runs `npm run` or other repository scripts, and fails if the preview Fern CLI version or organization is sourced from checkout-controlled config in that step.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 62-94 and `package.json` scripts; confirm the step with `env.FERN_TOKEN` no longer runs `npm run` and no longer chooses the `npx fern-api` version or preview organization from PR-controlled config without a trusted pin or allowlist.
  • Evidence: `Generate preview URL` sets `FERN_TOKEN`, runs `npm run docs:check-agent-variants`, reads `./fern/fern.config.json`, then runs `npx --yes "fern-api@${FERN_VERSION}"`; `package.json` already runs `docs:check-agent-variants` through the earlier non-secret `npm run docs` validation.

PRA-3 Resolve/justify — Do not post a preview URL after Fern exits nonzero

  • Location: .github/workflows/docs-preview-pr.yaml:94
  • Category: correctness
  • Problem: The preview script captures `STATUS=$?`, but after parsing and validating a Fern-looking URL it writes `preview_url=$URL` without checking whether `STATUS` was nonzero. A failed Fern command that still prints a valid-looking preview URL can therefore allow the later PR comment step to post or update the preview link.
  • Impact: Reviewers can be sent to a stale, partial, or failed preview deployment, masking a Fern generation failure and weakening the preview comment as a trustworthy signal.
  • Recommended action: Check `STATUS` before writing `preview_url` or posting the comment, and fail immediately on nonzero status unless Fern documents a nonzero-with-valid-preview contract and the workflow handles that case explicitly.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect `.github/workflows/docs-preview-pr.yaml` around `OUTPUT=...`, `STATUS=$?`, URL validation, and `echo "preview_url=$URL" >> "$GITHUB_OUTPUT"`; the shortest confirmation is that an `if [ "$STATUS" -ne 0 ]` guard appears before the output write.
  • Missing regression test: Add a shell-unit or static workflow check named `docs preview does not write preview_url when Fern exits nonzero even if stdout contains a valid expected URL`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect `.github/workflows/docs-preview-pr.yaml` around `OUTPUT=...`, `STATUS=$?`, URL validation, and `echo "preview_url=$URL" >> "$GITHUB_OUTPUT"`; the shortest confirmation is that an `if [ "$STATUS" -ne 0 ]` guard appears before the output write.
  • Evidence: The script exits for an empty URL or wrong URL, but there is no nonzero-status guard before `echo "preview_url=$URL" >> "$GITHUB_OUTPUT"`.

PRA-4 Resolve/justify — Document or isolate the Fern stdout preview URL contract

  • Location: .github/workflows/docs-preview-pr.yaml:76
  • Category: architecture
  • Problem: The workflow parses human-oriented Fern CLI stdout with `sed` and validates the extracted URL. The stricter expected URL check is an improvement, but the localized parsing behavior still does not document the invalid state being worked around, the Fern source boundary, why a source fix or structured Fern output is unavailable, or when this shell parsing can be removed.
  • Impact: A localized workaround can preserve or hide an unstable external CLI contract; future Fern output changes can silently break preview comments or interact badly with the missing nonzero-status guard.
  • Recommended action: Document the Fern CLI boundary in the workflow or move the parsing into a small checked helper with explicit valid, empty, wrong-host, and nonzero-status cases. Prefer a source-level structured output or API if Fern supports one; otherwise define the removal condition for this parsing.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 72-94 and confirm there is a code comment or helper-level contract that identifies the Fern stdout format and covers failure modes, not just a best-effort `sed` extraction.
  • Missing regression test: Add a static or shell-unit check named `docs preview URL parsing accepts only the expected nvidia-preview-pr-N.docs.buildwithfern.com/nemoclaw base or subpaths and rejects empty, wrong-host, and nonzero-status outputs`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 72-94 and confirm there is a code comment or helper-level contract that identifies the Fern stdout format and covers failure modes, not just a best-effort `sed` extraction.
  • Evidence: The workflow uses `sed -nE 's/.*Published docs to (https?:\/\/[^[:space:]]+).*/\1/p' | head -n1`, builds an expected URL from config and `PREVIEW_ID`, and writes the result to `GITHUB_OUTPUT` without a documented Fern structured-output source.

PRA-5 Resolve/justify — Add local regression checks for docs workflow security invariants

  • Location: scripts/checks/run.ts:14
  • Category: tests
  • Problem: This PR changes secret-bearing workflow behavior but adds no changed tests or local static checks for the invariants that keep `FERN_TOKEN`, GitHub write permissions, and preview URL posting safe. Existing `scripts/checks/run.ts` registers several repository checks, but none cover the changed docs workflows.
  • Impact: Future workflow edits can reintroduce secret exposure, unpinned action or CLI execution, missing checkout credential hardening, or failed-preview comments without any local deterministic check catching the regression before CI.
  • Recommended action: Add a small focused checker under `scripts/checks` and register it in `scripts/checks/run.ts`. Keep it concrete to these workflows rather than adding a generalized workflow validation framework.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect `scripts/checks/run.ts` and `scripts/checks/`; confirm a new focused docs workflow checker is registered and covers the token-bearing preview step, preview status guard, URL validation, pinned actions, `persist-credentials: false`, and the public publish ancestry check.
  • Missing regression test: Add behavior-specific checks named `docs preview token-bearing step does not run repository scripts`, `docs preview does not write preview_url when Fern exits nonzero`, and `docs publish workflows keep checkout credentials disabled and public publish checks main ancestry before npm or Fern commands`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect `scripts/checks/run.ts` and `scripts/checks/`; confirm a new focused docs workflow checker is registered and covers the token-bearing preview step, preview status guard, URL validation, pinned actions, `persist-credentials: false`, and the public publish ancestry check.
  • Evidence: The changed file inventory includes no test files; `scripts/checks/run.ts` currently registers `direct-credential-env`, `no-coverage-ignore`, `layer-import-boundaries`, `no-test-dist-imports`, `vitest-project-overlap`, and `test-title-style`, but no docs workflow checker.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

  • None.
Simplification opportunities: 1 possible cut

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-2 shrink (.github/workflows/docs-preview-pr.yaml:69): Cut the duplicate `npm run docs:check-agent-variants` from the `FERN_TOKEN` step and stop rereading PR-controlled Fern config under that secret.
    • Replacement: Rely on the existing non-secret `Validate docs` step for repository validation, and use trusted workflow constants or an allowlist for the Fern CLI version and preview host used with `FERN_TOKEN`.
    • Safety boundary: Do not remove the non-secret docs validation, URL allowlist validation, action pinning, or credential redaction boundaries.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Add local regression checks for docs workflow security invariants — Add a small focused checker under `scripts/checks` and register it in `scripts/checks/run.ts`. Keep it concrete to these workflows rather than adding a generalized workflow validation framework.
  • PRA-T2 Acceptance clause — Point PR preview generation at the staging instance with `--instance`, disable checkout credential persistence, and validate the extracted Fern preview URL before posting it. — add test evidence or identify existing coverage. The preview workflow adds `--instance "$FERN_STAGING_INSTANCE"`, sets `persist-credentials: false`, and validates the extracted URL. However, it can still write `preview_url` when Fern exits nonzero, and part of the expected host is derived from PR-controlled `fern.config.json` while `FERN_TOKEN` is present.
  • PRA-T3 Acceptance clause — Keep workflow validation lightweight through existing YAML checks, hooks, and CI rather than adding a bespoke static checker. — add test evidence or identify existing coverage. No generalized validator was added, but changed secret-bearing workflow logic lacks focused local regression checks for the security and correctness invariants introduced by this PR.
  • PRA-T4 Fern preview URL extraction from CLI stdout in `.github/workflows/docs-preview-pr.yaml` — Missing; add shell-unit or static coverage for valid output, empty output, wrong-host output, and nonzero-status-with-valid-URL output.. The workflow extracts `Published docs to ...` with `sed`, validates the URL, and writes `preview_url` without a documented source contract or status guard.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: Fern preview URL extraction from CLI stdout in `.github/workflows/docs-preview-pr.yaml`

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Missing; add shell-unit or static coverage for valid output, empty output, wrong-host output, and nonzero-status-with-valid-URL output.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: The workflow extracts `Published docs to ...` with `sed`, validates the URL, and writes `preview_url` without a documented source contract or status guard.

PRA-2 Resolve/justify — Keep FERN_TOKEN out of PR-controlled preview code

  • Location: .github/workflows/docs-preview-pr.yaml:69
  • Category: security
  • Problem: The secret-bearing `Generate preview URL` step still runs checkout-controlled logic before and during the Fern invocation: `npm run docs:check-agent-variants`, `node -p "require('./fern/fern.config.json').version"`, `node -p "require('./fern/fern.config.json').organization"`, and `npx --yes "fern-api@${FERN_VERSION}"`. The earlier non-secret `Validate docs` step already runs `npm run docs`, which maps to `docs:strict` and starts with `docs:check-agent-variants`, so the duplicate repository script unnecessarily expands the trusted-code boundary while `FERN_TOKEN` is present.
  • Impact: For same-repository PR branches where repository secrets are available, a compromised branch can alter docs scripts or `fern/fern.config.json` to execute attacker-controlled package or repository code while `FERN_TOKEN` is in the environment, or select an unexpected `fern-api` package version for the secret-bearing command.
  • Recommended action: Remove repository-controlled validation from the token-bearing step and rely on the prior non-secret validation. Keep the Fern CLI version, organization, and expected preview host in trusted workflow constants or a small allowlist, rather than reading them from PR-controlled checkout content while `FERN_TOKEN` is set.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 62-94 and `package.json` scripts; confirm the step with `env.FERN_TOKEN` no longer runs `npm run` and no longer chooses the `npx fern-api` version or preview organization from PR-controlled config without a trusted pin or allowlist.
  • Missing regression test: Add a focused static workflow check that fails if any `.github/workflows/docs-preview-pr.yaml` step with `FERN_TOKEN` runs `npm run` or other repository scripts, and fails if the preview Fern CLI version or organization is sourced from checkout-controlled config in that step.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 62-94 and `package.json` scripts; confirm the step with `env.FERN_TOKEN` no longer runs `npm run` and no longer chooses the `npx fern-api` version or preview organization from PR-controlled config without a trusted pin or allowlist.
  • Evidence: `Generate preview URL` sets `FERN_TOKEN`, runs `npm run docs:check-agent-variants`, reads `./fern/fern.config.json`, then runs `npx --yes "fern-api@${FERN_VERSION}"`; `package.json` already runs `docs:check-agent-variants` through the earlier non-secret `npm run docs` validation.

PRA-3 Resolve/justify — Do not post a preview URL after Fern exits nonzero

  • Location: .github/workflows/docs-preview-pr.yaml:94
  • Category: correctness
  • Problem: The preview script captures `STATUS=$?`, but after parsing and validating a Fern-looking URL it writes `preview_url=$URL` without checking whether `STATUS` was nonzero. A failed Fern command that still prints a valid-looking preview URL can therefore allow the later PR comment step to post or update the preview link.
  • Impact: Reviewers can be sent to a stale, partial, or failed preview deployment, masking a Fern generation failure and weakening the preview comment as a trustworthy signal.
  • Recommended action: Check `STATUS` before writing `preview_url` or posting the comment, and fail immediately on nonzero status unless Fern documents a nonzero-with-valid-preview contract and the workflow handles that case explicitly.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect `.github/workflows/docs-preview-pr.yaml` around `OUTPUT=...`, `STATUS=$?`, URL validation, and `echo "preview_url=$URL" >> "$GITHUB_OUTPUT"`; the shortest confirmation is that an `if [ "$STATUS" -ne 0 ]` guard appears before the output write.
  • Missing regression test: Add a shell-unit or static workflow check named `docs preview does not write preview_url when Fern exits nonzero even if stdout contains a valid expected URL`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect `.github/workflows/docs-preview-pr.yaml` around `OUTPUT=...`, `STATUS=$?`, URL validation, and `echo "preview_url=$URL" >> "$GITHUB_OUTPUT"`; the shortest confirmation is that an `if [ "$STATUS" -ne 0 ]` guard appears before the output write.
  • Evidence: The script exits for an empty URL or wrong URL, but there is no nonzero-status guard before `echo "preview_url=$URL" >> "$GITHUB_OUTPUT"`.

PRA-4 Resolve/justify — Document or isolate the Fern stdout preview URL contract

  • Location: .github/workflows/docs-preview-pr.yaml:76
  • Category: architecture
  • Problem: The workflow parses human-oriented Fern CLI stdout with `sed` and validates the extracted URL. The stricter expected URL check is an improvement, but the localized parsing behavior still does not document the invalid state being worked around, the Fern source boundary, why a source fix or structured Fern output is unavailable, or when this shell parsing can be removed.
  • Impact: A localized workaround can preserve or hide an unstable external CLI contract; future Fern output changes can silently break preview comments or interact badly with the missing nonzero-status guard.
  • Recommended action: Document the Fern CLI boundary in the workflow or move the parsing into a small checked helper with explicit valid, empty, wrong-host, and nonzero-status cases. Prefer a source-level structured output or API if Fern supports one; otherwise define the removal condition for this parsing.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 72-94 and confirm there is a code comment or helper-level contract that identifies the Fern stdout format and covers failure modes, not just a best-effort `sed` extraction.
  • Missing regression test: Add a static or shell-unit check named `docs preview URL parsing accepts only the expected nvidia-preview-pr-N.docs.buildwithfern.com/nemoclaw base or subpaths and rejects empty, wrong-host, and nonzero-status outputs`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `.github/workflows/docs-preview-pr.yaml` lines 72-94 and confirm there is a code comment or helper-level contract that identifies the Fern stdout format and covers failure modes, not just a best-effort `sed` extraction.
  • Evidence: The workflow uses `sed -nE 's/.*Published docs to (https?:\/\/[^[:space:]]+).*/\1/p' | head -n1`, builds an expected URL from config and `PREVIEW_ID`, and writes the result to `GITHUB_OUTPUT` without a documented Fern structured-output source.

PRA-5 Resolve/justify — Add local regression checks for docs workflow security invariants

  • Location: scripts/checks/run.ts:14
  • Category: tests
  • Problem: This PR changes secret-bearing workflow behavior but adds no changed tests or local static checks for the invariants that keep `FERN_TOKEN`, GitHub write permissions, and preview URL posting safe. Existing `scripts/checks/run.ts` registers several repository checks, but none cover the changed docs workflows.
  • Impact: Future workflow edits can reintroduce secret exposure, unpinned action or CLI execution, missing checkout credential hardening, or failed-preview comments without any local deterministic check catching the regression before CI.
  • Recommended action: Add a small focused checker under `scripts/checks` and register it in `scripts/checks/run.ts`. Keep it concrete to these workflows rather than adding a generalized workflow validation framework.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect `scripts/checks/run.ts` and `scripts/checks/`; confirm a new focused docs workflow checker is registered and covers the token-bearing preview step, preview status guard, URL validation, pinned actions, `persist-credentials: false`, and the public publish ancestry check.
  • Missing regression test: Add behavior-specific checks named `docs preview token-bearing step does not run repository scripts`, `docs preview does not write preview_url when Fern exits nonzero`, and `docs publish workflows keep checkout credentials disabled and public publish checks main ancestry before npm or Fern commands`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect `scripts/checks/run.ts` and `scripts/checks/`; confirm a new focused docs workflow checker is registered and covers the token-bearing preview step, preview status guard, URL validation, pinned actions, `persist-credentials: false`, and the public publish ancestry check.
  • Evidence: The changed file inventory includes no test files; `scripts/checks/run.ts` currently registers `direct-credential-env`, `no-coverage-ignore`, `layer-import-boundaries`, `no-test-dist-imports`, `vitest-project-overlap`, and `test-title-style`, but no docs workflow checker.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Blocked

Merge posture: Do not merge until addressed
Primary next action: Fix PRA-4: Preview workflow reads Fern version from mutable repo file at runtime — supply chain risk; then add or justify PRA-T1.
Open items: 9 required · 8 warnings · 3 suggestions · 8 test follow-ups
Since last review: 0 prior items resolved · 9 still apply · 5 new items found

Action checklist

  • PRA-4 Fix: Preview workflow reads Fern version from mutable repo file at runtime — supply chain risk in .github/workflows/docs-preview-pr.yaml:56
  • PRA-5 Fix: Staging publish workflow (publish job) reads Fern version from mutable repo file at runtime — supply chain risk in .github/workflows/docs-publish-staging.yaml:47
  • PRA-6 Fix: Staging publish workflow (delete-preview job) reads Fern version from mutable repo file at runtime — supply chain risk in .github/workflows/docs-publish-staging.yaml:109
  • PRA-7 Fix: Public publish workflow reads Fern version from mutable repo file at runtime — supply chain risk in .github/workflows/docs-publish-public.yaml:59
  • PRA-8 Fix: actions/checkout v6.0.3 has known vulnerabilities fixed in v7.0.0 in .github/workflows/docs-preview-pr.yaml:35
  • PRA-9 Fix: PR acceptance clause 'Tests not applicable' is false — workflows execute third-party binaries in .github/workflows/docs-preview-pr.yaml:1
  • PRA-10 Fix: PR acceptance clause 'Sensitive paths changed' unchecked but workflows are sensitive in .github/workflows/docs-preview-pr.yaml:1
  • PRA-11 Fix: Preview URL validation uses FERN_ORG from mutable config — validation can be bypassed in .github/workflows/docs-preview-pr.yaml:64
  • PRA-12 Fix: delete-preview job lacks validation of preview IDs before deletion in .github/workflows/docs-publish-staging.yaml:85
  • PRA-1 Resolve or justify: Source-of-truth review needed: FERN_VERSION in workflows
  • PRA-2 Resolve or justify: Source-of-truth review needed: FERN_ORG in preview workflow
  • PRA-3 Resolve or justify: Source-of-truth review needed: FERN_STAGING_INSTANCE duplication
  • PRA-13 Resolve or justify: Merge-base guard has TOCTOU window if main is force-pushed after tag creation in .github/workflows/docs-publish-public.yaml:33
  • PRA-14 Resolve or justify: FERN_STAGING_INSTANCE duplicated across fern/docs.yml and two workflow env blocks in fern/docs.yml:3
  • PRA-15 Resolve or justify: Public instance URL only in workflow env, not in fern/docs.yml in .github/workflows/docs-publish-public.yaml:17
  • PRA-16 Resolve or justify: Concurrency group for staging serializes all main pushes unnecessarily in .github/workflows/docs-publish-staging.yaml:15
  • PRA-17 Resolve or justify: Zero workflow security tests — previous review required 8 test follow-ups, none added in .github/workflows/docs-preview-pr.yaml:1
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Zero workflow security tests — previous review required 8 test follow-ups, none added
  • PRA-T7 Add or justify test follow-up: Acceptance clause
  • PRA-T8 Add or justify test follow-up: Acceptance clause
  • PRA-18 In-scope improvement: Simplify: consolidate FERN_STAGING_INSTANCE to single canonical source in fern/docs.yml in fern/docs.yml:3
  • PRA-19 In-scope improvement: Simplify: consolidate FERN_PUBLIC_INSTANCE to single canonical source in fern/docs.yml in .github/workflows/docs-publish-public.yaml:17
  • PRA-20 In-scope improvement: Simplify: preview URL validation shell logic is complex; consider Node.js script in .github/workflows/docs-preview-pr.yaml:64

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-3 Resolve/justify architecture — Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-4 Required security .github/workflows/docs-preview-pr.yaml:56 Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 56 with `FERN_VERSION=${FERN_VERSION}`.
PRA-5 Required security .github/workflows/docs-publish-staging.yaml:47 Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 47 with `FERN_VERSION=${FERN_VERSION}`.
PRA-6 Required security .github/workflows/docs-publish-staging.yaml:109 Add `FERN_VERSION: "5.59.0"` to workflow `env:` block (shared with publish job). Replace line 109 with `FERN_VERSION=${FERN_VERSION}`.
PRA-7 Required security .github/workflows/docs-publish-public.yaml:59 Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 59 with `FERN_VERSION=${FERN_VERSION}`.
PRA-8 Required security .github/workflows/docs-preview-pr.yaml:35 Update to v7.0.0 SHA across all three workflows. Coordinate with PR #5755 author to avoid conflicts.
PRA-9 Required acceptance .github/workflows/docs-preview-pr.yaml:1 Uncheck 'Tests not applicable'. Add workflow security tests per PRA-T1 through T5, or update justification with evidence that existing tests cover supply chain validation (they do not).
PRA-10 Required acceptance .github/workflows/docs-preview-pr.yaml:1 Check 'Sensitive paths changed' in PR body and complete sensitive-path review with maintainer-approved waiver or reviewer sign-off.
PRA-11 Required security .github/workflows/docs-preview-pr.yaml:64 Pin FERN_ORG in workflow env as well (`FERN_ORG: "nvidia"`), or validate against hardcoded expected org "nvidia".
PRA-12 Required security .github/workflows/docs-publish-staging.yaml:85 Add validation that preview IDs match expected pattern `pr-<number>` before deletion. Consider dry-run mode or manual approval for preview deletion.
PRA-13 Resolve/justify architecture .github/workflows/docs-publish-public.yaml:33 Consider fetching main with explicit ref or adding tag protection rules. At minimum, document the assumption in a comment.
PRA-14 Resolve/justify workflow fern/docs.yml:3 Keep staging instance in fern/docs.yml as canonical source. Remove FERN_STAGING_INSTANCE from workflow env blocks; add comment referencing fern/docs.yml. Workflows can read from config if needed, but since version is pinned, instance can also be pinned in workflow env with comment.
PRA-15 Resolve/justify workflow .github/workflows/docs-publish-public.yaml:17 Remove FERN_PUBLIC_INSTANCE from workflow env; add comment referencing fern/docs.yml instances[1].url. Or keep in workflow env with comment cross-referencing fern/docs.yml.
PRA-16 Resolve/justify workflow .github/workflows/docs-publish-staging.yaml:15 Consider `concurrency.group: docs-publish-staging-${{ github.sha }}` or similar to allow parallel publishes for different commits, or document why serialization is desired.
PRA-17 Resolve/justify tests .github/workflows/docs-preview-pr.yaml:1 Add workflow validation tests (YAML parsing + static analysis) in repo test suite. At minimum: test pinned version in env, no mutable config reads, validation uses pinned values, delete-preview ID pattern check.
PRA-18 Improvement workflow fern/docs.yml:3 Remove FERN_STAGING_INSTANCE from workflow env blocks (docs-preview-pr.yaml line 14, docs-publish-staging.yaml line 14). Add comment in workflows: `# Staging instance defined in fern/docs.yml instances[0].url`.
PRA-19 Improvement workflow .github/workflows/docs-publish-public.yaml:17 Remove FERN_PUBLIC_INSTANCE from workflow env (line 17). Add comment: `# Public instance defined in fern/docs.yml instances[1].url`.
PRA-20 Improvement correctness .github/workflows/docs-preview-pr.yaml:64 Extract validation to a small Node.js script (e.g., `scripts/validate-preview-url.js`) that reads pinned FERN_ORG, FERN_STAGING_INSTANCE, PREVIEW_URL from env and exits 0/1. Call from workflow.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-4 Required — Preview workflow reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-preview-pr.yaml:56
  • Category: security
  • Problem: Line 56 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}"`. A malicious fork PR can modify fern/fern.config.json to execute arbitrary fern-api versions. The FERN_TOKEN gate is a single defense; previous review required pinning FERN_VERSION in workflow env as defense-in-depth.
  • Impact: Supply chain compromise: attacker with fork PR write access or compromised FERN_TOKEN could run malicious fern-api version during preview generation, potentially exfiltrating secrets or poisoning published docs.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 56 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-preview-pr.yaml line 56: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-preview-pr.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert no step contains "node -p require.*fern.config.json". Add fork PR simulation test: mock missing FERN_TOKEN secret; assert preview generation step is skipped.
  • Done when: The required change is committed and verification passes: Check docs-preview-pr.yaml line 56: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 56: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-4 unfixed.

PRA-5 Required — Staging publish workflow (publish job) reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-publish-staging.yaml:47
  • Category: security
  • Problem: Line 47 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}"`. Runs on pushes to main (protected branch) but risk remains if main is compromised or FERN_TOKEN leaked.
  • Impact: Supply chain compromise: if main branch is compromised or FERN_TOKEN leaked, attacker controls fern-api version for staging publishes.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 47 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-staging.yaml line 47: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-publish-staging.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert no step contains "node -p require.*fern.config.json".
  • Done when: The required change is committed and verification passes: Check docs-publish-staging.yaml line 47: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 47: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-5 unfixed.

PRA-6 Required — Staging publish workflow (delete-preview job) reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-publish-staging.yaml:109
  • Category: security
  • Problem: Line 109 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}" docs preview delete`. Same supply chain risk as publish job.
  • Impact: Supply chain compromise: if main branch is compromised or FERN_TOKEN leaked, attacker controls fern-api version for preview deletion, potentially deleting arbitrary previews or executing malicious code.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block (shared with publish job). Replace line 109 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-staging.yaml line 109: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-publish-staging.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert delete-preview job references env var not config read.
  • Done when: The required change is committed and verification passes: Check docs-publish-staging.yaml line 109: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 109: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-6 unfixed.

PRA-7 Required — Public publish workflow reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-publish-public.yaml:59
  • Category: security
  • Problem: Line 59 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}" generate --docs --instance`. Runs on release tags with environment gate, but mutable config read remains single point of failure.
  • Impact: Supply chain compromise: if release tag workflow is triggered (even legitimately), attacker controlling fern/fern.config.json via compromised main or token leak controls fern-api version for public docs publish.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 59 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-public.yaml line 59: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-publish-public.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert no step contains "node -p require.*fern.config.json".
  • Done when: The required change is committed and verification passes: Check docs-publish-public.yaml line 59: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 59: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-7 unfixed.

PRA-8 Required — actions/checkout v6.0.3 has known vulnerabilities fixed in v7.0.0

  • Location: .github/workflows/docs-preview-pr.yaml:35
  • Category: security
  • Problem: All three workflows use `actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10` (v6.0.3). Vulnerabilities fixed in v7.0.0. PR chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 #5755 overlaps but not merged.
  • Impact: Known vulnerabilities in checkout action could allow credential leakage or workflow compromise. Coordination with PR chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 #5755 needed to avoid conflicts.
  • Required action: Update to v7.0.0 SHA across all three workflows. Coordinate with PR chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 #5755 author to avoid conflicts.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check all three workflow files: checkout action should reference v7.0.0 SHA (e.g., `actions/checkout@<v7-sha>`).
  • Missing regression test: Add dependency scanning test: parse workflow YAMLs; assert actions/checkout version >= 7.0.0.
  • Done when: The required change is committed and verification passes: Check all three workflow files: checkout action should reference v7.0.0 SHA (e.g., `actions/checkout@<v7-sha>`).
  • Evidence: All three workflows use `actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10` (v6.0.3). Previous review PRA-8 unfixed.

PRA-9 Required — PR acceptance clause 'Tests not applicable' is false — workflows execute third-party binaries

  • Location: .github/workflows/docs-preview-pr.yaml:1
  • Category: acceptance
  • Problem: PR body checks 'Tests not applicable' with justification 'CI/Fern publishing configuration only; no runtime product code paths changed'. This is false: workflows execute `npx --yes fern-api@${VERSION}` which downloads and runs third-party code from npm registry. Supply chain risk makes tests applicable.
  • Impact: False acceptance waiver hides missing security test coverage for supply chain validation. Previous review required tests (PRA-T1 through T5).
  • Required action: Uncheck 'Tests not applicable'. Add workflow security tests per PRA-T1 through T5, or update justification with evidence that existing tests cover supply chain validation (they do not).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check PR body Quality Gates section: 'Tests not applicable' should be unchecked or justification should reference actual supply chain test coverage.
  • Missing regression test: Add workflow validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling.
  • Done when: The required change is committed and verification passes: Check PR body Quality Gates section: 'Tests not applicable' should be unchecked or justification should reference actual supply chain test coverage.
  • Evidence: PR body line: '- [x] Tests not applicable — justification: CI/Fern publishing configuration only; no runtime product code paths changed.'

PRA-10 Required — PR acceptance clause 'Sensitive paths changed' unchecked but workflows are sensitive

  • Location: .github/workflows/docs-preview-pr.yaml:1
  • Category: acceptance
  • Problem: PR body leaves 'Sensitive paths changed' unchecked. Workflows handle FERN_TOKEN (org-level Fern credential), publish to public/staging Fern instances, and execute third-party binaries. These are sensitive paths per NemoClaw definition (credentials, publishing, supply chain).
  • Impact: Missing sensitive-path review means no security specialist has validated credential handling, publishing permissions, or supply chain defenses.
  • Required action: Check 'Sensitive paths changed' in PR body and complete sensitive-path review with maintainer-approved waiver or reviewer sign-off.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check PR body Quality Gates section: 'Sensitive paths changed' should be checked with reviewer/approval link recorded.
  • Missing regression test: N/A — process gate, not code test.
  • Done when: The required change is committed and verification passes: Check PR body Quality Gates section: 'Sensitive paths changed' should be checked with reviewer/approval link recorded.
  • Evidence: PR body line: '- [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)' — unchecked.

PRA-11 Required — Preview URL validation uses FERN_ORG from mutable config — validation can be bypassed

  • Location: .github/workflows/docs-preview-pr.yaml:64
  • Category: security
  • Problem: Lines 64-73 validate preview URL against expected pattern using `FERN_ORG=$(node -p "require('./fern/fern.config.json').organization")`. Attacker modifying config can set arbitrary org, making validation accept malicious URLs.
  • Impact: Validation bypass: attacker controls expected URL pattern via mutable config, rendering URL validation ineffective. Could redirect preview comments to phishing sites or exfiltrate tokens via malicious preview URLs.
  • Required action: Pin FERN_ORG in workflow env as well (`FERN_ORG: "nvidia"`), or validate against hardcoded expected org "nvidia".
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-preview-pr.yaml lines 64-73: FERN_ORG should come from workflow env or hardcoded string, not from `node -p require('./fern/fern.config.json').organization`.
  • Missing regression test: Add test: inject config with `organization: "attacker"`; assert URL validation rejects preview URLs not matching `nvidia-preview-pr-<num>.docs.buildwithfern.com`.
  • Done when: The required change is committed and verification passes: Check docs-preview-pr.yaml lines 64-73: FERN_ORG should come from workflow env or hardcoded string, not from `node -p require('./fern/fern.config.json').organization`.
  • Evidence: Line 64: `FERN_ORG=$(node -p "require('./fern/fern.config.json').organization")`; validation uses this value. Previous review PRA-16 unfixed.

PRA-12 Required — delete-preview job lacks validation of preview IDs before deletion

  • Location: .github/workflows/docs-publish-staging.yaml:85
  • Category: security
  • Problem: Lines 85-124 accept preview IDs from GitHub API (`gh api .../commits/${SHA}/pulls`) without pattern validation. Malicious/compromised GH API response could inject arbitrary preview IDs (e.g., `pr-1; rm -rf /` or `pr-999999`) for deletion.
  • Impact: Arbitrary preview deletion or command injection via shell interpolation. While `set -euo pipefail` and quoted variables mitigate injection, pattern validation is defense-in-depth.
  • Required action: Add validation that preview IDs match expected pattern `pr-<number>` before deletion. Consider dry-run mode or manual approval for preview deletion.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-staging.yaml lines 109-124: loop should validate each `preview_id` against regex `^pr-[0-9]+$` before passing to fern-api.
  • Missing regression test: Add test: feed `preview_ids` with `pr-1; malicious` and `pr-999999`; assert only valid `pr-\d+` pattern processed and malicious entries rejected.
  • Done when: The required change is committed and verification passes: Check docs-publish-staging.yaml lines 109-124: loop should validate each `preview_id` against regex `^pr-[0-9]+$` before passing to fern-api.
  • Evidence: Lines 119-124: `while IFS= read -r preview_id; do ... npx --yes "fern-api@${FERN_VERSION}" docs preview delete --id "$preview_id"` with no validation. Previous review PRA-17 unfixed.
Review findings by urgency: 9 required fixes, 8 items to resolve/justify, 3 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: FERN_VERSION in workflows

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Parse all 3 workflow YAMLs; assert env.FERN_VERSION == "5.59.0" and no step contains "node -p require.*fern.config.json"
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Lines 56, 47, 109, 59 in respective workflows read version from config; no FERN_VERSION in any workflow env block

PRA-2 Resolve/justify — Source-of-truth review needed: FERN_ORG in preview workflow

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Inject config with organization: "attacker"; assert URL validation rejects preview URLs not matching nvidia-preview-pr-<num>.docs.buildwithfern.com
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Line 64: FERN_ORG=$(node -p "require('./fern/fern.config.json').organization") used in validation logic

PRA-3 Resolve/justify — Source-of-truth review needed: FERN_STAGING_INSTANCE duplication

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Assert all 3 locations match exactly, or assert workflows have comment referencing canonical source in fern/docs.yml
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Three identical URLs; fern/docs.yml now has staging instance (good) but workflows still duplicate

PRA-13 Resolve/justify — Merge-base guard has TOCTOU window if main is force-pushed after tag creation

  • Location: .github/workflows/docs-publish-public.yaml:33
  • Category: architecture
  • Problem: Line 33: `git merge-base --is-ancestor "$GITHUB_SHA" origin/main` checks if tag commit is ancestor of origin/main at workflow runtime. If main is force-pushed between tag creation and workflow execution, check passes for commit no longer on main.
  • Impact: TOCTOU: release tag on diverged history could publish docs from unapproved commit. Low probability but violates defense-in-depth.
  • Recommended action: Consider fetching main with explicit ref or adding tag protection rules. At minimum, document the assumption in a comment.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check docs-publish-public.yaml line 33: comment should acknowledge TOCTOU window or guard should fetch main at specific ref.
  • Missing regression test: Add test: create tag on commit not ancestor of main (simulate force-push); assert workflow fails or document known limitation.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check docs-publish-public.yaml line 33: comment should acknowledge TOCTOU window or guard should fetch main at specific ref.
  • Evidence: Line 33: `if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then` with no explicit ref fetch. Previous review PRA-12 unfixed.

PRA-14 Resolve/justify — FERN_STAGING_INSTANCE duplicated across fern/docs.yml and two workflow env blocks

  • Location: fern/docs.yml:3
  • Category: workflow
  • Problem: Staging instance URL appears in: fern/docs.yml instances[0].url, docs-preview-pr.yaml env.FERN_STAGING_INSTANCE, docs-publish-staging.yaml env.FERN_STAGING_INSTANCE. Drift risk if one updated without others.
  • Impact: Configuration drift: staging instance URL could differ between Fern config and workflows, causing publish failures or wrong instance targeting.
  • Recommended action: Keep staging instance in fern/docs.yml as canonical source. Remove FERN_STAGING_INSTANCE from workflow env blocks; add comment referencing fern/docs.yml. Workflows can read from config if needed, but since version is pinned, instance can also be pinned in workflow env with comment.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check all three locations: fern/docs.yml instances[0].url, docs-preview-pr.yaml env.FERN_STAGING_INSTANCE, docs-publish-staging.yaml env.FERN_STAGING_INSTANCE — should match exactly or workflows should reference canonical source.
  • Missing regression test: Add test: assert FERN_STAGING_INSTANCE in workflows matches fern/docs.yml instances[0].url, or assert workflows have comment referencing canonical source.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check all three locations: fern/docs.yml instances[0].url, docs-preview-pr.yaml env.FERN_STAGING_INSTANCE, docs-publish-staging.yaml env.FERN_STAGING_INSTANCE — should match exactly or workflows should reference canonical source.
  • Evidence: fern/docs.yml line 3: `url: nvidia-nemoclaw-staging.docs.buildwithfern.com/nemoclaw`; docs-preview-pr.yaml line 14: `FERN_STAGING_INSTANCE: nvidia-nemoclaw-staging.docs.buildwithfern.com/nemoclaw`; docs-publish-staging.yaml line 14: same. Previous review PRA-3/11/18 unfixed.

PRA-15 Resolve/justify — Public instance URL only in workflow env, not in fern/docs.yml

  • Location: .github/workflows/docs-publish-public.yaml:17
  • Category: workflow
  • Problem: FERN_PUBLIC_INSTANCE only in workflow env (line 17). fern/docs.yml now has both instances (staging + public) — good, but workflow still duplicates public instance.
  • Impact: Minor drift risk: if public instance URL changes, must update in two places.
  • Recommended action: Remove FERN_PUBLIC_INSTANCE from workflow env; add comment referencing fern/docs.yml instances[1].url. Or keep in workflow env with comment cross-referencing fern/docs.yml.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check docs-publish-public.yaml line 17: should either be removed with comment, or have comment referencing fern/docs.yml.
  • Missing regression test: Add test: assert FERN_PUBLIC_INSTANCE in workflow matches fern/docs.yml instances[1].url, or assert cross-reference comment exists.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check docs-publish-public.yaml line 17: should either be removed with comment, or have comment referencing fern/docs.yml.
  • Evidence: docs-publish-public.yaml line 17: `FERN_PUBLIC_INSTANCE: nvidia-nemoclaw.docs.buildwithfern.com/nemoclaw`; fern/docs.yml instances[1] has same URL. Previous review PRA-15 partially addressed (now in fern/docs.yml) but workflow still duplicates.

PRA-16 Resolve/justify — Concurrency group for staging serializes all main pushes unnecessarily

  • Location: .github/workflows/docs-publish-staging.yaml:15
  • Category: workflow
  • Problem: Concurrency group `docs-publish-staging` (no ref) serializes all main pushes touching docs paths. Concurrent doc changes on main queue instead of running in parallel.
  • Impact: Reduced throughput for doc publishes on main. Not a security issue but operational concern.
  • Recommended action: Consider `concurrency.group: docs-publish-staging-${{ github.sha }}` or similar to allow parallel publishes for different commits, or document why serialization is desired.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check docs-publish-staging.yaml line 15: concurrency group should include ref or SHA for parallelism, or have comment justifying serialization.
  • Missing regression test: N/A — operational concern.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check docs-publish-staging.yaml line 15: concurrency group should include ref or SHA for parallelism, or have comment justifying serialization.
  • Evidence: Line 15: `group: docs-publish-staging` (no ref). Preview workflow correctly uses `group: fern-preview-${{ github.ref }}`.

PRA-17 Resolve/justify — Zero workflow security tests — previous review required 8 test follow-ups, none added

  • Location: .github/workflows/docs-preview-pr.yaml:1
  • Category: tests
  • Problem: Previous review mandated tests for: runtime version pinning validation (T1-T4), fork PR simulation (T1), tag ancestry guard (T2), preview URL validation (T3), delete-preview ID validation (T4), missing FERN_TOKEN (T5), path filtering (T6), instance consistency (T7). No test files changed in this PR.
  • Impact: No automated regression coverage for supply chain defenses, validation bypasses, or deletion safety. Changes to workflows cannot be validated without manual execution.
  • Recommended action: Add workflow validation tests (YAML parsing + static analysis) in repo test suite. At minimum: test pinned version in env, no mutable config reads, validation uses pinned values, delete-preview ID pattern check.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search repo for test files validating workflow YAML (e.g., `.github/workflows/**/*.test.ts`, `tests/workflows/`). Should find tests for all three workflow files.
  • Missing regression test: Add 8 behavior-specific tests: (1) pinned FERN_VERSION in all workflows, (2) fork PR with malicious config uses pinned version, (3) tag not on main fails ancestry check, (4) preview URL validation rejects attacker-controlled org, (5) delete-preview validates pr-<number> pattern, (6) missing FERN_TOKEN skips preview, (7) staging path filter excludes non-docs changes, (8) staging instance URL consistent across configs.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search repo for test files validating workflow YAML (e.g., `.github/workflows/**/*.test.ts`, `tests/workflows/`). Should find tests for all three workflow files.
  • Evidence: staticTestInventory: 'No changed test files were detected for changed source files'. Previous review PRA-T1 through T8 unaddressed.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-18 Improvement — Simplify: consolidate FERN_STAGING_INSTANCE to single canonical source in fern/docs.yml

  • Location: fern/docs.yml:3
  • Category: workflow
  • Problem: Staging instance URL duplicated in 3 places. fern/docs.yml is the Fern-native config; workflows should reference it or pin with comment.
  • Impact: Maintenance burden: three locations must be kept in sync. Risk of drift causes publish failures or wrong instance targeting. Extra lines in workflow env blocks with no added value since fern/docs.yml is authoritative.
  • Suggested action: Remove FERN_STAGING_INSTANCE from workflow env blocks (docs-preview-pr.yaml line 14, docs-publish-staging.yaml line 14). Add comment in workflows: `# Staging instance defined in fern/docs.yml instances[0].url`.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check workflow env blocks: FERN_STAGING_INSTANCE should be removed; comment referencing fern/docs.yml should exist.
  • Missing regression test: Add test: assert workflow env does not contain FERN_STAGING_INSTANCE; assert comment references fern/docs.yml.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Three locations with identical URL. Previous review PRA-18 recommended this simplification.

PRA-19 Improvement — Simplify: consolidate FERN_PUBLIC_INSTANCE to single canonical source in fern/docs.yml

  • Location: .github/workflows/docs-publish-public.yaml:17
  • Category: workflow
  • Problem: Public instance URL in workflow env and fern/docs.yml instances[1].url. Workflow should reference canonical source.
  • Impact: Maintenance burden: two locations must be kept in sync. Risk of drift if public instance URL changes.
  • Suggested action: Remove FERN_PUBLIC_INSTANCE from workflow env (line 17). Add comment: `# Public instance defined in fern/docs.yml instances[1].url`.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check docs-publish-public.yaml line 17: FERN_PUBLIC_INSTANCE removed; comment referencing fern/docs.yml exists.
  • Missing regression test: Add test: assert workflow env does not contain FERN_PUBLIC_INSTANCE; assert comment references fern/docs.yml.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: docs-publish-public.yaml line 17 duplicates fern/docs.yml instances[1].url. Previous review PRA-15 noted this.

PRA-20 Improvement — Simplify: preview URL validation shell logic is complex; consider Node.js script

  • Location: .github/workflows/docs-preview-pr.yaml:64
  • Category: correctness
  • Problem: Lines 64-73 use complex bash string manipulation for URL validation. Hard to maintain and audit.
  • Impact: Increased cognitive load for reviewers; bash parameter expansion and conditional logic is error-prone compared to Node.js. Harder to unit test in isolation.
  • Suggested action: Extract validation to a small Node.js script (e.g., `scripts/validate-preview-url.js`) that reads pinned FERN_ORG, FERN_STAGING_INSTANCE, PREVIEW_URL from env and exits 0/1. Call from workflow.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check if validation logic moved to script; workflow step calls `node scripts/validate-preview-url.js`.
  • Missing regression test: Add unit test for validation script with various URL/org/instance combinations.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Lines 64-73: 10 lines of bash parameter expansion and conditional logic for URL pattern matching.
Simplification opportunities: 3 possible cuts

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-18 shrink (fern/docs.yml:3): env.FERN_STAGING_INSTANCE from .github/workflows/docs-preview-pr.yaml:14 and .github/workflows/docs-publish-staging.yaml:14
    • Replacement: Comment: `# Staging instance defined in fern/docs.yml instances[0].url`
    • Net: -2 lines
    • Safety boundary: fern/docs.yml remains source of truth; workflows only read at build time if needed (but version pinning makes runtime read unnecessary)
  • PRA-19 shrink (.github/workflows/docs-publish-public.yaml:17): env.FERN_PUBLIC_INSTANCE from .github/workflows/docs-publish-public.yaml:17
    • Replacement: Comment: `# Public instance defined in fern/docs.yml instances[1].url`
    • Net: -1 lines
    • Safety boundary: fern/docs.yml remains source of truth; workflow only reads at build time if needed
  • PRA-20 stdlib (.github/workflows/docs-preview-pr.yaml:64): bash URL validation logic (lines 64-73)
    • Replacement: Node.js script `scripts/validate-preview-url.js` called via `node scripts/validate-preview-url.js`
    • Net: 5 lines
    • Safety boundary: Validation logic must still use pinned FERN_ORG and FERN_STAGING_INSTANCE from workflow env, not mutable config
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — test:workflow:pin-fern-version — parse all 3 workflow YAMLs; assert env.FERN_VERSION == "5.59.0" and no runtime config reads. Workflows execute third-party binaries with supply chain risk; static YAML validation insufficient. Need runtime validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling, path filtering, instance consistency.
  • PRA-T2 Runtime validation — test:workflow:fork-pr-malicious-config — simulate fork PR with modified fern.config.json; assert preview uses pinned version. Workflows execute third-party binaries with supply chain risk; static YAML validation insufficient. Need runtime validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling, path filtering, instance consistency.
  • PRA-T3 Runtime validation — test:workflow:public-publish-tag-not-on-main — create tag on commit not ancestor of main; assert merge-base guard fails. Workflows execute third-party binaries with supply chain risk; static YAML validation insufficient. Need runtime validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling, path filtering, instance consistency.
  • PRA-T4 Runtime validation — test:workflow:preview-url-validation-bypass — inject organization: "evil" in config; assert URL validation rejects. Workflows execute third-party binaries with supply chain risk; static YAML validation insufficient. Need runtime validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling, path filtering, instance consistency.
  • PRA-T5 Runtime validation — test:workflow:delete-preview-id-validation — feed preview_ids with pr-1;malicious; assert only pr-\d+ pattern processed. Workflows execute third-party binaries with supply chain risk; static YAML validation insufficient. Need runtime validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling, path filtering, instance consistency.
  • PRA-T6 Zero workflow security tests — previous review required 8 test follow-ups, none added — Add workflow validation tests (YAML parsing + static analysis) in repo test suite. At minimum: test pinned version in env, no mutable config reads, validation uses pinned values, delete-preview ID pattern check.
  • PRA-T7 Acceptance clause — Tests added or updated for changed behavior — add test evidence or identify existing coverage. No test files changed; staticTestInventory confirms zero coverage for workflow files
  • PRA-T8 Acceptance clause — Tests not applicable — justification: CI/Fern publishing configuration only; no runtime product code paths changed — add test evidence or identify existing coverage. False — workflows execute `npx --yes fern-api@${VERSION}` downloading third-party binaries; supply chain risk makes tests applicable
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: FERN_VERSION in workflows

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Parse all 3 workflow YAMLs; assert env.FERN_VERSION == "5.59.0" and no step contains "node -p require.*fern.config.json"
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Lines 56, 47, 109, 59 in respective workflows read version from config; no FERN_VERSION in any workflow env block

PRA-2 Resolve/justify — Source-of-truth review needed: FERN_ORG in preview workflow

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Inject config with organization: "attacker"; assert URL validation rejects preview URLs not matching nvidia-preview-pr-<num>.docs.buildwithfern.com
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Line 64: FERN_ORG=$(node -p "require('./fern/fern.config.json').organization") used in validation logic

PRA-3 Resolve/justify — Source-of-truth review needed: FERN_STAGING_INSTANCE duplication

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Assert all 3 locations match exactly, or assert workflows have comment referencing canonical source in fern/docs.yml
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: Three identical URLs; fern/docs.yml now has staging instance (good) but workflows still duplicate

PRA-4 Required — Preview workflow reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-preview-pr.yaml:56
  • Category: security
  • Problem: Line 56 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}"`. A malicious fork PR can modify fern/fern.config.json to execute arbitrary fern-api versions. The FERN_TOKEN gate is a single defense; previous review required pinning FERN_VERSION in workflow env as defense-in-depth.
  • Impact: Supply chain compromise: attacker with fork PR write access or compromised FERN_TOKEN could run malicious fern-api version during preview generation, potentially exfiltrating secrets or poisoning published docs.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 56 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-preview-pr.yaml line 56: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-preview-pr.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert no step contains "node -p require.*fern.config.json". Add fork PR simulation test: mock missing FERN_TOKEN secret; assert preview generation step is skipped.
  • Done when: The required change is committed and verification passes: Check docs-preview-pr.yaml line 56: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 56: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-4 unfixed.

PRA-5 Required — Staging publish workflow (publish job) reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-publish-staging.yaml:47
  • Category: security
  • Problem: Line 47 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}"`. Runs on pushes to main (protected branch) but risk remains if main is compromised or FERN_TOKEN leaked.
  • Impact: Supply chain compromise: if main branch is compromised or FERN_TOKEN leaked, attacker controls fern-api version for staging publishes.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 47 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-staging.yaml line 47: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-publish-staging.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert no step contains "node -p require.*fern.config.json".
  • Done when: The required change is committed and verification passes: Check docs-publish-staging.yaml line 47: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 47: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-5 unfixed.

PRA-6 Required — Staging publish workflow (delete-preview job) reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-publish-staging.yaml:109
  • Category: security
  • Problem: Line 109 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}" docs preview delete`. Same supply chain risk as publish job.
  • Impact: Supply chain compromise: if main branch is compromised or FERN_TOKEN leaked, attacker controls fern-api version for preview deletion, potentially deleting arbitrary previews or executing malicious code.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block (shared with publish job). Replace line 109 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-staging.yaml line 109: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-publish-staging.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert delete-preview job references env var not config read.
  • Done when: The required change is committed and verification passes: Check docs-publish-staging.yaml line 109: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 109: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-6 unfixed.

PRA-7 Required — Public publish workflow reads Fern version from mutable repo file at runtime — supply chain risk

  • Location: .github/workflows/docs-publish-public.yaml:59
  • Category: security
  • Problem: Line 59 executes `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")` then `npx --yes "fern-api@${FERN_VERSION}" generate --docs --instance`. Runs on release tags with environment gate, but mutable config read remains single point of failure.
  • Impact: Supply chain compromise: if release tag workflow is triggered (even legitimately), attacker controlling fern/fern.config.json via compromised main or token leak controls fern-api version for public docs publish.
  • Required action: Add `FERN_VERSION: "5.59.0"` to workflow `env:` block. Replace line 59 with `FERN_VERSION=${FERN_VERSION}`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-public.yaml line 59: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Missing regression test: Add workflow validation test: parse docs-publish-public.yaml; assert env.FERN_VERSION exists and equals "5.59.0"; assert no step contains "node -p require.*fern.config.json".
  • Done when: The required change is committed and verification passes: Check docs-publish-public.yaml line 59: should read `FERN_VERSION=${FERN_VERSION}` not `node -p "require('./fern/fern.config.json').version"`; verify env block contains `FERN_VERSION: "5.59.0"`.
  • Evidence: Line 59: `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")`; no FERN_VERSION in workflow env block. Previous review PRA-7 unfixed.

PRA-8 Required — actions/checkout v6.0.3 has known vulnerabilities fixed in v7.0.0

  • Location: .github/workflows/docs-preview-pr.yaml:35
  • Category: security
  • Problem: All three workflows use `actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10` (v6.0.3). Vulnerabilities fixed in v7.0.0. PR chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 #5755 overlaps but not merged.
  • Impact: Known vulnerabilities in checkout action could allow credential leakage or workflow compromise. Coordination with PR chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 #5755 needed to avoid conflicts.
  • Required action: Update to v7.0.0 SHA across all three workflows. Coordinate with PR chore(deps): bump actions/checkout from 6.0.3 to 7.0.0 #5755 author to avoid conflicts.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check all three workflow files: checkout action should reference v7.0.0 SHA (e.g., `actions/checkout@<v7-sha>`).
  • Missing regression test: Add dependency scanning test: parse workflow YAMLs; assert actions/checkout version >= 7.0.0.
  • Done when: The required change is committed and verification passes: Check all three workflow files: checkout action should reference v7.0.0 SHA (e.g., `actions/checkout@<v7-sha>`).
  • Evidence: All three workflows use `actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10` (v6.0.3). Previous review PRA-8 unfixed.

PRA-9 Required — PR acceptance clause 'Tests not applicable' is false — workflows execute third-party binaries

  • Location: .github/workflows/docs-preview-pr.yaml:1
  • Category: acceptance
  • Problem: PR body checks 'Tests not applicable' with justification 'CI/Fern publishing configuration only; no runtime product code paths changed'. This is false: workflows execute `npx --yes fern-api@${VERSION}` which downloads and runs third-party code from npm registry. Supply chain risk makes tests applicable.
  • Impact: False acceptance waiver hides missing security test coverage for supply chain validation. Previous review required tests (PRA-T1 through T5).
  • Required action: Uncheck 'Tests not applicable'. Add workflow security tests per PRA-T1 through T5, or update justification with evidence that existing tests cover supply chain validation (they do not).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check PR body Quality Gates section: 'Tests not applicable' should be unchecked or justification should reference actual supply chain test coverage.
  • Missing regression test: Add workflow validation tests for: pinned version enforcement, fork PR malicious config, tag ancestry guard, preview URL validation, delete-preview ID validation, missing FERN_TOKEN handling.
  • Done when: The required change is committed and verification passes: Check PR body Quality Gates section: 'Tests not applicable' should be unchecked or justification should reference actual supply chain test coverage.
  • Evidence: PR body line: '- [x] Tests not applicable — justification: CI/Fern publishing configuration only; no runtime product code paths changed.'

PRA-10 Required — PR acceptance clause 'Sensitive paths changed' unchecked but workflows are sensitive

  • Location: .github/workflows/docs-preview-pr.yaml:1
  • Category: acceptance
  • Problem: PR body leaves 'Sensitive paths changed' unchecked. Workflows handle FERN_TOKEN (org-level Fern credential), publish to public/staging Fern instances, and execute third-party binaries. These are sensitive paths per NemoClaw definition (credentials, publishing, supply chain).
  • Impact: Missing sensitive-path review means no security specialist has validated credential handling, publishing permissions, or supply chain defenses.
  • Required action: Check 'Sensitive paths changed' in PR body and complete sensitive-path review with maintainer-approved waiver or reviewer sign-off.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check PR body Quality Gates section: 'Sensitive paths changed' should be checked with reviewer/approval link recorded.
  • Missing regression test: N/A — process gate, not code test.
  • Done when: The required change is committed and verification passes: Check PR body Quality Gates section: 'Sensitive paths changed' should be checked with reviewer/approval link recorded.
  • Evidence: PR body line: '- [ ] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)' — unchecked.

PRA-11 Required — Preview URL validation uses FERN_ORG from mutable config — validation can be bypassed

  • Location: .github/workflows/docs-preview-pr.yaml:64
  • Category: security
  • Problem: Lines 64-73 validate preview URL against expected pattern using `FERN_ORG=$(node -p "require('./fern/fern.config.json').organization")`. Attacker modifying config can set arbitrary org, making validation accept malicious URLs.
  • Impact: Validation bypass: attacker controls expected URL pattern via mutable config, rendering URL validation ineffective. Could redirect preview comments to phishing sites or exfiltrate tokens via malicious preview URLs.
  • Required action: Pin FERN_ORG in workflow env as well (`FERN_ORG: "nvidia"`), or validate against hardcoded expected org "nvidia".
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-preview-pr.yaml lines 64-73: FERN_ORG should come from workflow env or hardcoded string, not from `node -p require('./fern/fern.config.json').organization`.
  • Missing regression test: Add test: inject config with `organization: "attacker"`; assert URL validation rejects preview URLs not matching `nvidia-preview-pr-<num>.docs.buildwithfern.com`.
  • Done when: The required change is committed and verification passes: Check docs-preview-pr.yaml lines 64-73: FERN_ORG should come from workflow env or hardcoded string, not from `node -p require('./fern/fern.config.json').organization`.
  • Evidence: Line 64: `FERN_ORG=$(node -p "require('./fern/fern.config.json').organization")`; validation uses this value. Previous review PRA-16 unfixed.

PRA-12 Required — delete-preview job lacks validation of preview IDs before deletion

  • Location: .github/workflows/docs-publish-staging.yaml:85
  • Category: security
  • Problem: Lines 85-124 accept preview IDs from GitHub API (`gh api .../commits/${SHA}/pulls`) without pattern validation. Malicious/compromised GH API response could inject arbitrary preview IDs (e.g., `pr-1; rm -rf /` or `pr-999999`) for deletion.
  • Impact: Arbitrary preview deletion or command injection via shell interpolation. While `set -euo pipefail` and quoted variables mitigate injection, pattern validation is defense-in-depth.
  • Required action: Add validation that preview IDs match expected pattern `pr-<number>` before deletion. Consider dry-run mode or manual approval for preview deletion.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Check docs-publish-staging.yaml lines 109-124: loop should validate each `preview_id` against regex `^pr-[0-9]+$` before passing to fern-api.
  • Missing regression test: Add test: feed `preview_ids` with `pr-1; malicious` and `pr-999999`; assert only valid `pr-\d+` pattern processed and malicious entries rejected.
  • Done when: The required change is committed and verification passes: Check docs-publish-staging.yaml lines 109-124: loop should validate each `preview_id` against regex `^pr-[0-9]+$` before passing to fern-api.
  • Evidence: Lines 119-124: `while IFS= read -r preview_id; do ... npx --yes "fern-api@${FERN_VERSION}" docs preview delete --id "$preview_id"` with no validation. Previous review PRA-17 unfixed.

PRA-13 Resolve/justify — Merge-base guard has TOCTOU window if main is force-pushed after tag creation

  • Location: .github/workflows/docs-publish-public.yaml:33
  • Category: architecture
  • Problem: Line 33: `git merge-base --is-ancestor "$GITHUB_SHA" origin/main` checks if tag commit is ancestor of origin/main at workflow runtime. If main is force-pushed between tag creation and workflow execution, check passes for commit no longer on main.
  • Impact: TOCTOU: release tag on diverged history could publish docs from unapproved commit. Low probability but violates defense-in-depth.
  • Recommended action: Consider fetching main with explicit ref or adding tag protection rules. At minimum, document the assumption in a comment.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check docs-publish-public.yaml line 33: comment should acknowledge TOCTOU window or guard should fetch main at specific ref.
  • Missing regression test: Add test: create tag on commit not ancestor of main (simulate force-push); assert workflow fails or document known limitation.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check docs-publish-public.yaml line 33: comment should acknowledge TOCTOU window or guard should fetch main at specific ref.
  • Evidence: Line 33: `if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then` with no explicit ref fetch. Previous review PRA-12 unfixed.

PRA-14 Resolve/justify — FERN_STAGING_INSTANCE duplicated across fern/docs.yml and two workflow env blocks

  • Location: fern/docs.yml:3
  • Category: workflow
  • Problem: Staging instance URL appears in: fern/docs.yml instances[0].url, docs-preview-pr.yaml env.FERN_STAGING_INSTANCE, docs-publish-staging.yaml env.FERN_STAGING_INSTANCE. Drift risk if one updated without others.
  • Impact: Configuration drift: staging instance URL could differ between Fern config and workflows, causing publish failures or wrong instance targeting.
  • Recommended action: Keep staging instance in fern/docs.yml as canonical source. Remove FERN_STAGING_INSTANCE from workflow env blocks; add comment referencing fern/docs.yml. Workflows can read from config if needed, but since version is pinned, instance can also be pinned in workflow env with comment.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check all three locations: fern/docs.yml instances[0].url, docs-preview-pr.yaml env.FERN_STAGING_INSTANCE, docs-publish-staging.yaml env.FERN_STAGING_INSTANCE — should match exactly or workflows should reference canonical source.
  • Missing regression test: Add test: assert FERN_STAGING_INSTANCE in workflows matches fern/docs.yml instances[0].url, or assert workflows have comment referencing canonical source.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check all three locations: fern/docs.yml instances[0].url, docs-preview-pr.yaml env.FERN_STAGING_INSTANCE, docs-publish-staging.yaml env.FERN_STAGING_INSTANCE — should match exactly or workflows should reference canonical source.
  • Evidence: fern/docs.yml line 3: `url: nvidia-nemoclaw-staging.docs.buildwithfern.com/nemoclaw`; docs-preview-pr.yaml line 14: `FERN_STAGING_INSTANCE: nvidia-nemoclaw-staging.docs.buildwithfern.com/nemoclaw`; docs-publish-staging.yaml line 14: same. Previous review PRA-3/11/18 unfixed.

PRA-15 Resolve/justify — Public instance URL only in workflow env, not in fern/docs.yml

  • Location: .github/workflows/docs-publish-public.yaml:17
  • Category: workflow
  • Problem: FERN_PUBLIC_INSTANCE only in workflow env (line 17). fern/docs.yml now has both instances (staging + public) — good, but workflow still duplicates public instance.
  • Impact: Minor drift risk: if public instance URL changes, must update in two places.
  • Recommended action: Remove FERN_PUBLIC_INSTANCE from workflow env; add comment referencing fern/docs.yml instances[1].url. Or keep in workflow env with comment cross-referencing fern/docs.yml.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check docs-publish-public.yaml line 17: should either be removed with comment, or have comment referencing fern/docs.yml.
  • Missing regression test: Add test: assert FERN_PUBLIC_INSTANCE in workflow matches fern/docs.yml instances[1].url, or assert cross-reference comment exists.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check docs-publish-public.yaml line 17: should either be removed with comment, or have comment referencing fern/docs.yml.
  • Evidence: docs-publish-public.yaml line 17: `FERN_PUBLIC_INSTANCE: nvidia-nemoclaw.docs.buildwithfern.com/nemoclaw`; fern/docs.yml instances[1] has same URL. Previous review PRA-15 partially addressed (now in fern/docs.yml) but workflow still duplicates.

PRA-16 Resolve/justify — Concurrency group for staging serializes all main pushes unnecessarily

  • Location: .github/workflows/docs-publish-staging.yaml:15
  • Category: workflow
  • Problem: Concurrency group `docs-publish-staging` (no ref) serializes all main pushes touching docs paths. Concurrent doc changes on main queue instead of running in parallel.
  • Impact: Reduced throughput for doc publishes on main. Not a security issue but operational concern.
  • Recommended action: Consider `concurrency.group: docs-publish-staging-${{ github.sha }}` or similar to allow parallel publishes for different commits, or document why serialization is desired.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check docs-publish-staging.yaml line 15: concurrency group should include ref or SHA for parallelism, or have comment justifying serialization.
  • Missing regression test: N/A — operational concern.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check docs-publish-staging.yaml line 15: concurrency group should include ref or SHA for parallelism, or have comment justifying serialization.
  • Evidence: Line 15: `group: docs-publish-staging` (no ref). Preview workflow correctly uses `group: fern-preview-${{ github.ref }}`.

PRA-17 Resolve/justify — Zero workflow security tests — previous review required 8 test follow-ups, none added

  • Location: .github/workflows/docs-preview-pr.yaml:1
  • Category: tests
  • Problem: Previous review mandated tests for: runtime version pinning validation (T1-T4), fork PR simulation (T1), tag ancestry guard (T2), preview URL validation (T3), delete-preview ID validation (T4), missing FERN_TOKEN (T5), path filtering (T6), instance consistency (T7). No test files changed in this PR.
  • Impact: No automated regression coverage for supply chain defenses, validation bypasses, or deletion safety. Changes to workflows cannot be validated without manual execution.
  • Recommended action: Add workflow validation tests (YAML parsing + static analysis) in repo test suite. At minimum: test pinned version in env, no mutable config reads, validation uses pinned values, delete-preview ID pattern check.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search repo for test files validating workflow YAML (e.g., `.github/workflows/**/*.test.ts`, `tests/workflows/`). Should find tests for all three workflow files.
  • Missing regression test: Add 8 behavior-specific tests: (1) pinned FERN_VERSION in all workflows, (2) fork PR with malicious config uses pinned version, (3) tag not on main fails ancestry check, (4) preview URL validation rejects attacker-controlled org, (5) delete-preview validates pr-<number> pattern, (6) missing FERN_TOKEN skips preview, (7) staging path filter excludes non-docs changes, (8) staging instance URL consistent across configs.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search repo for test files validating workflow YAML (e.g., `.github/workflows/**/*.test.ts`, `tests/workflows/`). Should find tests for all three workflow files.
  • Evidence: staticTestInventory: 'No changed test files were detected for changed source files'. Previous review PRA-T1 through T8 unaddressed.

PRA-18 Improvement — Simplify: consolidate FERN_STAGING_INSTANCE to single canonical source in fern/docs.yml

  • Location: fern/docs.yml:3
  • Category: workflow
  • Problem: Staging instance URL duplicated in 3 places. fern/docs.yml is the Fern-native config; workflows should reference it or pin with comment.
  • Impact: Maintenance burden: three locations must be kept in sync. Risk of drift causes publish failures or wrong instance targeting. Extra lines in workflow env blocks with no added value since fern/docs.yml is authoritative.
  • Suggested action: Remove FERN_STAGING_INSTANCE from workflow env blocks (docs-preview-pr.yaml line 14, docs-publish-staging.yaml line 14). Add comment in workflows: `# Staging instance defined in fern/docs.yml instances[0].url`.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check workflow env blocks: FERN_STAGING_INSTANCE should be removed; comment referencing fern/docs.yml should exist.
  • Missing regression test: Add test: assert workflow env does not contain FERN_STAGING_INSTANCE; assert comment references fern/docs.yml.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Three locations with identical URL. Previous review PRA-18 recommended this simplification.

PRA-19 Improvement — Simplify: consolidate FERN_PUBLIC_INSTANCE to single canonical source in fern/docs.yml

  • Location: .github/workflows/docs-publish-public.yaml:17
  • Category: workflow
  • Problem: Public instance URL in workflow env and fern/docs.yml instances[1].url. Workflow should reference canonical source.
  • Impact: Maintenance burden: two locations must be kept in sync. Risk of drift if public instance URL changes.
  • Suggested action: Remove FERN_PUBLIC_INSTANCE from workflow env (line 17). Add comment: `# Public instance defined in fern/docs.yml instances[1].url`.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check docs-publish-public.yaml line 17: FERN_PUBLIC_INSTANCE removed; comment referencing fern/docs.yml exists.
  • Missing regression test: Add test: assert workflow env does not contain FERN_PUBLIC_INSTANCE; assert comment references fern/docs.yml.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: docs-publish-public.yaml line 17 duplicates fern/docs.yml instances[1].url. Previous review PRA-15 noted this.

PRA-20 Improvement — Simplify: preview URL validation shell logic is complex; consider Node.js script

  • Location: .github/workflows/docs-preview-pr.yaml:64
  • Category: correctness
  • Problem: Lines 64-73 use complex bash string manipulation for URL validation. Hard to maintain and audit.
  • Impact: Increased cognitive load for reviewers; bash parameter expansion and conditional logic is error-prone compared to Node.js. Harder to unit test in isolation.
  • Suggested action: Extract validation to a small Node.js script (e.g., `scripts/validate-preview-url.js`) that reads pinned FERN_ORG, FERN_STAGING_INSTANCE, PREVIEW_URL from env and exits 0/1. Call from workflow.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check if validation logic moved to script; workflow step calls `node scripts/validate-preview-url.js`.
  • Missing regression test: Add unit test for validation script with various URL/org/instance combinations.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Lines 64-73: 10 lines of bash parameter expansion and conditional logic for URL pattern matching.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@miyoungc miyoungc added area: ci CI workflows, checks, release automation, or GitHub Actions area: docs Documentation, examples, guides, or docs build v0.0.72 and removed v0.0.72 labels Jul 2, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🤖 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-publish-public.yaml:
- Around line 26-27: The checkout step in the docs publish workflow is still
persisting the GitHub token by default, which is unnecessary here. Update the
existing actions/checkout usage in the checkout step to explicitly disable
credential persistence, matching the staging workflow behavior, so later steps
cannot read the token. Keep the change scoped to the Checkout repository step in
the workflow and preserve the current checkout action reference.
- Around line 29-32: The Setup Node.js step in the docs-publish-public workflow
is relying on actions/setup-node@v6 default package-manager caching, which
should be turned off for this privileged publish job. Update the Setup Node.js
configuration to explicitly disable automatic npm cache detection by setting
package-manager-cache to false in the workflow step. Keep the change scoped to
the Setup Node.js action in docs-publish-public so the release-publish flow no
longer restores potentially poisoned cache state while using FERN_TOKEN.

In @.github/workflows/docs-publish-staging.yaml:
- Around line 32-33: Disable token persistence in the Checkout repository step
by updating the actions/checkout usage in the docs-publish-staging workflow so
the repository is checked out without writing credentials to local git config.
Keep the existing checkout step, but set the checkout action’s
credential-persistence option to false so later npm ci and npm run steps cannot
read the token from the job workspace.
🪄 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: 0b0ea518-1552-4ef9-8189-234395880f24

📥 Commits

Reviewing files that changed from the base of the PR and between 0d9fc20 and 2128394.

📒 Files selected for processing (4)
  • .github/workflows/docs-preview-pr.yaml
  • .github/workflows/docs-publish-public.yaml
  • .github/workflows/docs-publish-staging.yaml
  • fern/docs.yml

Comment thread .github/workflows/docs-publish-public.yaml
Comment thread .github/workflows/docs-publish-public.yaml
Comment thread .github/workflows/docs-publish-staging.yaml

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/checks/docs-publish-workflows.ts (1)

1-246: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add focused tests for this guardrail.

No test coverage is provided for docs-publish-workflows.ts. The pure helper functions (assertPinnedActions, assertFernVersionSource, assertPublishSteps, etc.) are straightforward to unit test with small in-memory YAML fixtures covering both a passing case and known-bad cases (missing pin, wrong instance, guard-after-publish ordering).

Do you want me to draft a test suite covering detection and false-positive scenarios for these assertions?

As per path instructions, "Require focused tests for both detection and false-positive behavior."

🤖 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 `@scripts/checks/docs-publish-workflows.ts` around lines 1 - 246, Add focused
unit tests for docs-publish-workflows.ts, covering the pure helpers like
assertPinnedActions, assertFernVersionSource, assertPublishSteps,
assertPreviewWorkflow, assertStagingWorkflow, and assertPublicWorkflow with
small in-memory YAML fixtures. Include both passing cases and negative cases
such as unpinned actions, wrong Fern instance/env, missing config-based
FERN_VERSION usage, and guard-after-publish ordering, and verify false-positive
scenarios where valid fixtures produce no errors.

Source: Path instructions

🧹 Nitpick comments (1)
scripts/checks/docs-publish-workflows.ts (1)

163-168: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider enforcing persist-credentials: false here too.

This check already inspects the checkout step's with block for fetch-depth. Since the workflow it validates is missing persist-credentials: false (flagged separately by zizmor), extending this assertion would deterministically prevent that regression going forward instead of relying on a linter catching it each time.

♻️ Proposed addition
   const checkout = workflowSteps(workflow, "publish").find((step) =>
     stringValue(step.uses).startsWith("actions/checkout@"),
   );
   if (asRecord(checkout?.with)["fetch-depth"] !== 0) {
     errors.push(`${workflow.path}: public publish checkout must use fetch-depth: 0`);
   }
+  if (asRecord(checkout?.with)["persist-credentials"] !== false) {
+    errors.push(`${workflow.path}: public publish checkout must disable persist-credentials`);
+  }

As per path instructions, "Enforce objective invariants with deterministic code."

🤖 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 `@scripts/checks/docs-publish-workflows.ts` around lines 163 - 168, The
publish-workflow checkout validation in docs-publish-workflows.ts only enforces
fetch-depth, so extend the existing checkout step check in the publish workflow
to also require persist-credentials to be false. Update the logic around
workflowSteps(workflow, "publish") and the checkout step’s with block so both
invariants are asserted together, and keep reporting through the same
errors.push path when either setting is missing or incorrect.

Source: Path instructions

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

Outside diff comments:
In `@scripts/checks/docs-publish-workflows.ts`:
- Around line 1-246: Add focused unit tests for docs-publish-workflows.ts,
covering the pure helpers like assertPinnedActions, assertFernVersionSource,
assertPublishSteps, assertPreviewWorkflow, assertStagingWorkflow, and
assertPublicWorkflow with small in-memory YAML fixtures. Include both passing
cases and negative cases such as unpinned actions, wrong Fern instance/env,
missing config-based FERN_VERSION usage, and guard-after-publish ordering, and
verify false-positive scenarios where valid fixtures produce no errors.

---

Nitpick comments:
In `@scripts/checks/docs-publish-workflows.ts`:
- Around line 163-168: The publish-workflow checkout validation in
docs-publish-workflows.ts only enforces fetch-depth, so extend the existing
checkout step check in the publish workflow to also require persist-credentials
to be false. Update the logic around workflowSteps(workflow, "publish") and the
checkout step’s with block so both invariants are asserted together, and keep
reporting through the same errors.push path when either setting is missing or
incorrect.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1a89a133-95ea-4284-85a7-9e3b3c53d081

📥 Commits

Reviewing files that changed from the base of the PR and between 2128394 and ef93556.

📒 Files selected for processing (4)
  • .github/workflows/docs-publish-public.yaml
  • .github/workflows/docs-publish-staging.yaml
  • scripts/checks/docs-publish-workflows.ts
  • scripts/checks/run.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/docs-publish-staging.yaml

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
.github/workflows/docs-publish-staging.yaml (1)

67-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate the two identical gh api calls.

The step calls gh api repos/${GH_REPO}/commits/${COMMIT_SHA}/pulls twice — once to build preview_ids (lines 78-81), once to build pr_numbers (lines 84-88) — doubling API usage for the same data.

♻️ Proposed consolidation
-          {
-            echo "preview_ids<<EOF"
-            gh api "repos/${GH_REPO}/commits/${COMMIT_SHA}/pulls" \
-              -H "Accept: application/vnd.github+json" \
-              --jq '.[] | "pr-\(.number)"'
-            echo "EOF"
-          } >> "$GITHUB_OUTPUT"
-
-          mapfile -t pr_numbers < <(
-            gh api "repos/${GH_REPO}/commits/${COMMIT_SHA}/pulls" \
-              -H "Accept: application/vnd.github+json" \
-              --jq '.[].number'
-          )
+          mapfile -t pr_numbers < <(
+            gh api "repos/${GH_REPO}/commits/${COMMIT_SHA}/pulls" \
+              -H "Accept: application/vnd.github+json" \
+              --jq '.[].number'
+          )
+          {
+            echo "preview_ids<<EOF"
+            printf 'pr-%s\n' "${pr_numbers[@]}"
+            echo "EOF"
+          } >> "$GITHUB_OUTPUT"
🤖 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-publish-staging.yaml around lines 67 - 98, The “Find
merged PR previews” step is calling the same gh api endpoint twice to fetch
commit-associated PRs. Consolidate this in the workflow by using a single gh api
invocation in the run block, storing its output once, and reusing it to derive
both preview_ids and pr_numbers in the same step. Keep the logic in the existing
“Find merged PR previews” step and preserve the has_previews guard and output
formatting.
🤖 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.

Nitpick comments:
In @.github/workflows/docs-publish-staging.yaml:
- Around line 67-98: The “Find merged PR previews” step is calling the same gh
api endpoint twice to fetch commit-associated PRs. Consolidate this in the
workflow by using a single gh api invocation in the run block, storing its
output once, and reusing it to derive both preview_ids and pr_numbers in the
same step. Keep the logic in the existing “Find merged PR previews” step and
preserve the has_previews guard and output formatting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6efe04a5-0a2e-4f47-b385-b9134c6a9964

📥 Commits

Reviewing files that changed from the base of the PR and between 70ddfd2 and 8cdd5c9.

📒 Files selected for processing (1)
  • .github/workflows/docs-publish-staging.yaml

@miyoungc

miyoungc commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the automated review. I am not planning to incorporate the remaining PR Advisor recommendations unless a human reviewer feels strongly otherwise.

Rationale:

  • FERN_VERSION intentionally stays sourced from fern/fern.config.json. That file is the repo's existing source of truth for Fern CLI versioning, and duplicating the version in workflow YAML would create drift.
  • I removed the bespoke static workflow checker. For this small workflow change, adding a custom parser/test harness felt like more maintenance surface than value. The workflows still go through existing YAML validation, hooks, CI, and the PR itself exercises the preview workflow.
  • I am leaving the actions/checkout v7 bump to the existing dependency/update work rather than mixing action-version churn into this docs publishing PR.
  • I did take the targeted hardening that seemed directly useful here: checkout credential persistence is disabled, the public tag ancestry guard runs before npm/Fern commands, public setup-node package-manager caching is disabled, and preview URLs are validated before posting.

If reviewers think any of the remaining recommendations should be required for this PR, please say so and I will adjust.

@miyoungc
miyoungc merged commit bdaecbc into main Jul 2, 2026
40 checks passed
@miyoungc
miyoungc deleted the agent/fern-doc-workflows branch July 2, 2026 02:22
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary
This PR adds Fern publishing automation for NemoClaw docs so merged docs
changes publish to staging and release tags publish to the public
instance.
It points PR previews at the staging Fern instance, keeps the Fern CLI
version sourced from `fern/fern.config.json`, and removes PR preview
deployments after staging publish succeeds.

## Changes
- Add a staging Fern instance in `fern/docs.yml`.
- Add `Docs / Publish Staging` for `main` pushes that touch docs, Fern
config, or docs build inputs.
- Add `Docs / Publish Public` for `v*.*.*` release tag pushes.
- Run `npm run docs:sync-agent-variants` and `npm run docs` before both
Fern publish commands.
- Point PR preview generation at the staging instance with `--instance`,
disable checkout credential persistence, and validate the extracted Fern
preview URL before posting it.
- Delete merged PR Fern previews after staging publish succeeds with
`fern docs preview delete --id pr-<number>`.
- Guard public publishing with the `docs-public` environment and an
ancestry check that runs immediately after checkout and requires release
tag commits to be reachable from `origin/main` before any npm/Fern
commands.
- Disable checkout credential persistence for staging and public publish
jobs; disable setup-node package-manager caching for the public publish
job.
- Keep workflow validation lightweight through existing YAML checks,
hooks, and CI rather than adding a bespoke static checker.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check all that apply. For any "covered by existing tests", "not
applicable", or waiver entry, add a brief justification on the same line
or in the Changes section. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: CI/Fern publishing
configuration only; no runtime product code paths changed. The workflows
are covered by existing YAML validation, pre-commit/pre-push hooks, and
CI execution on this PR.
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: This changes repository
publishing automation, not user-facing product behavior or docs content.
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each item you ran and confirmed. Leave unchecked items you
skipped. Doc-only changes do not require npm test unless you ran it. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Git hooks passed during commit and push, or `npx prek run
--from-ref main --to-ref HEAD` passes
- [ ] Targeted tests pass for changed behavior
- [ ] Full `npm test` passes (broad runtime changes only)
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Additional verification:
- `npm ci --ignore-scripts`
- `npm run docs:sync-agent-variants`
- `npm run docs` passed with 0 Fern errors; existing Fern warnings
remain for the published-redirects network check and light-mode accent
contrast.
- `FERN_VERSION=$(node -p "require('./fern/fern.config.json').version")
&& cd fern && npx --yes "fern-api@${FERN_VERSION}" check --warnings`
- `npm run typecheck:cli`
- `npm run build:cli`
- Pre-commit hooks passed during commits, including YAML checks,
repository checks, and secret scanning.
- Pre-push hooks passed after pushes.
- `git diff --check`
- ReadLints reported no issues for changed workflow/config files.
- Manual staging publish succeeded:
https://nvidia-nemoclaw-staging.docs.buildwithfern.com/nemoclaw,
deployment `019f2071-e6b7-72d2-be92-21bf324c96c1`.

Reviewer notes:
- `fern/fern.config.json` is the intended single source of truth for the
Fern CLI version, so this PR intentionally does not duplicate that value
in workflow YAML.
- A bespoke static workflow checker was removed to avoid adding
maintenance-heavy validation for a small publishing workflow change.
- Checkout v7 updates are left to the existing dependency/update PR
rather than mixing action-version churn into this PR.

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci CI workflows, checks, release automation, or GitHub Actions area: docs Documentation, examples, guides, or docs build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants