Skip to content

feat: add CI guard requiring sidebar updates when adding docs#3089

Open
justin808 wants to merge 6 commits intomainfrom
jg/2889-sidebar-ci-guard
Open

feat: add CI guard requiring sidebar updates when adding docs#3089
justin808 wants to merge 6 commits intomainfrom
jg/2889-sidebar-ci-guard

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented Apr 9, 2026

Summary

  • Add script/check-docs-sidebar that detects newly added docs under docs/oss/ and docs/pro/ and verifies each has a corresponding entry in docs/sidebars.ts
  • Add docs/.sidebar-exclusions with existing intentional exclusions (legacy redirects, pages linked from intro instead of sidebar)
  • Wire check into GitHub Actions (.github/workflows/check-docs-sidebar.yml) and local CI (bin/ci-local)
  • Update AGENTS.md Boundaries/Always section with the new sidebar rule

Closes #2889

Test plan

  • Script correctly reports "no new docs" when no docs are added (exits 0)
  • Script correctly detects and fails when a new OSS doc is added without a sidebar entry
  • Script correctly detects and fails when a new Pro doc is added without a sidebar entry
  • Script correctly maps docs/oss/X.md → sidebar ID X and docs/pro/X.md → sidebar ID pro/X
  • Exclusion mechanism works — IDs in .sidebar-exclusions are skipped
  • Pre-commit hooks pass (trailing newlines, prettier, markdown links)
  • CI workflow runs on PR and passes

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new CI gate and local CI step that can fail PRs/commits based on git diff detection, so mis-detection of added docs or base-ref handling could block merges.

Overview
Introduces a new guard (script/check-docs-sidebar) that detects newly added published docs and fails if their derived doc IDs are not present in docs/sidebars.ts, with an opt-out list in docs/.sidebar-exclusions.

Wires the check into GitHub Actions via a new check-docs-sidebar.yml workflow and into bin/ci-local (runs always and is the only check run for docs-only change sets), and updates AGENTS.md to document the new requirement.

Reviewed by Cursor Bugbot for commit 8ec91d8. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Documentation

    • Enforces that new or renamed published docs must be registered in the sidebar; provides a configurable exclusions file and documents the format.
    • Adds guidance about sidebar registration and how to exclude docs.
  • Chores

    • Adds an automated sidebar validation that runs in CI and can be invoked during local CI-like runs to surface missing registrations.

Add script/check-docs-sidebar that detects newly added published docs
under docs/oss/ and docs/pro/ and verifies each has a corresponding
entry in docs/sidebars.ts. Supports an explicit exclusion list at
docs/.sidebar-exclusions for intentional omissions (legacy redirects,
pages linked from other docs instead of the sidebar).

Wire the check into:
- GitHub Actions via .github/workflows/check-docs-sidebar.yml
- Local CI via bin/ci-local (runs even for docs-only changes)

Update AGENTS.md Boundaries/Always section with the new rule.

Closes #2889

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

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

Adds a docs-sidebar guard: a script that detects newly added/renamed published docs under docs/oss/** and docs/pro/**, requires corresponding IDs in docs/sidebars.ts (or an explicit entry in docs/.sidebar-exclusions), a GitHub Actions workflow to run the check, and local bin/ci-local integration.

Changes

Cohort / File(s) Summary
CI Workflow
​.github/workflows/check-docs-sidebar.yml
New GitHub Actions workflow "Check Docs Sidebar" that runs script/check-docs-sidebar on pushes to main, pull requests, and manual dispatch for documentation paths; computes and passes base/current refs.
Validation Script
script/check-docs-sidebar
New Bash script that computes added/renamed docs (including staged/untracked locally), maps paths → doc IDs, applies docs/.sidebar-exclusions, checks docs/sidebars.ts for each ID, and fails with actionable output when IDs are missing.
Local CI Integration
bin/ci-local
Introduces argv-based job runner, moves early failure-state initialization, runs the sidebar check during docs-only early exits and unconditionally (failure recorded, flow continues via `
Documentation Rules
AGENTS.md
Adds rule: docs under docs/oss/ or docs/pro/ must be registered in docs/sidebars.ts or listed with a reason in docs/.sidebar-exclusions.
Sidebar Exclusions
docs/.sidebar-exclusions
New allowlist file listing doc IDs excluded from the sidebar with one-ID-per-line formatting and reason comments enforced by the CI script.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant GH as GitHub Actions
    participant Repo as Repository
    participant CI as Local CI (bin/ci-local)
    participant Check as script/check-docs-sidebar
    participant Sidebars as docs/sidebars.ts
    participant Exclusions as docs/.sidebar-exclusions

    Dev->>Repo: push / open PR (docs/*)
    GH->>Repo: fetch base ref if needed
    GH->>Check: run job with base/current refs
    CI->>Check: (local) invoke script with refs
    Check->>Repo: git diff → list added/renamed docs
    Check->>Exclusions: load excluded IDs
    Check->>Sidebars: check each doc ID presence
    Sidebars-->>Check: present / missing
    Check-->>GH: exit 0 if all present, exit 1 with missing IDs
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇
I hop the docs from root to tip,
Counting IDs on every slip.
If one’s not listed, I softly cry —
Mark it, exclude it, or add it nigh.
A tidy guide, then nap nearby. 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a CI guard that requires sidebar updates when docs are added.
Linked Issues check ✅ Passed The PR implements all coding requirements from #2889: script to detect new docs and verify sidebar entries, exclusion mechanism with docs/.sidebar-exclusions, GitHub Actions workflow, bin/ci-local integration, and AGENTS.md documentation.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the CI guard for sidebar updates; no out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/2889-sidebar-ci-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 9, 2026

Review: CI Guard for Docs Sidebar

Good addition — the guard closes a real gap where newly added docs could miss sidebar registration undetected. The structure is clean, the exclusion mechanism is well-documented, and wiring it into both GHA and bin/ci-local means contributors catch the failure early. A few issues worth addressing before merge:

Issues

Staged file detection gap (most impactful)
In local development, when a contributor does git add docs/oss/new-guide.md without committing first and then runs bin/ci-local, the check reports "No new published doc files detected" — a false negative. Staged files are neither in git diff ... HEAD nor in git ls-files --others. The fix is a one-liner: add git diff --diff-filter=A --cached to the local-dev file collection. See inline comment.

--depth=50 may be too shallow
For long-lived branches or repos with dense commit history, 50 commits may not reach the merge-base between a feature branch and origin/main. The git diff "$BASE_REF"..."$CURRENT_REF" call would produce wrong results silently. Inline comment has the suggestion.

Grep-based sidebar detection matches commented-out IDs
If a doc ID appears in a TypeScript comment in sidebars.ts (e.g., a removed entry left as a // comment), the script counts it as present. Filtering out comment lines before the grep is a cheap fix.

eval with single-quote wrapping of $BASE_REF in bin/ci-local
Minor: the invocation embeds $BASE_REF inside single quotes within an eval'd string. Unusual but possible ref names containing ' would break it. See inline.

Positive notes

  • set -euo pipefail with proper || echo "" fallbacks on grep/diff calls — correct handling.
  • Three-dot git diff A...B syntax is intentional and correct (compares against merge-base, not tip of main).
  • The declare -A EXCLUSION_MAP with O(1) lookup is a nice touch.
  • GHA workflow uses persist-credentials: false and minimal permissions (contents: read).
  • The three duplicate early-exit paths in bin/ci-local for docs-only detection are verbose but functionally correct.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 9, 2026

Greptile Summary

This PR adds a CI guard (script/check-docs-sidebar) that detects newly added docs under docs/oss/ and docs/pro/ and verifies each has a corresponding entry in docs/sidebars.ts. It wires the check into a new GitHub Actions workflow (path-filtered to doc-related files) and into all code paths of bin/ci-local, and seeds a docs/.sidebar-exclusions file for intentional omissions.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/quality suggestions that do not affect correctness.

The implementation correctly handles all documented edge cases (all-zeros SHA, untracked local files, remote fetch, three-dot diff semantics, early-exit doc-only paths). The three P2 comments are about a theoretical eval injection, a potential false-positive in the sidebar grep, and the path-filtered (vs. mandatory) nature of the workflow — none block merge.

script/check-docs-sidebar (sidebar presence grep could be tightened)

Vulnerabilities

No security concerns identified. The workflow uses permissions: contents: read and persist-credentials: false. The only elevated risk is the eval pattern in bin/ci-local's run_job, but this is a local developer script and the input (BASE_REF) is a git ref or SHA in all practical usage.

Important Files Changed

Filename Overview
script/check-docs-sidebar New bash script that detects newly added docs/oss and docs/pro files and checks they appear in sidebars.ts; logic is sound but the sidebar presence check uses a plain substring match that could produce false positives.
.github/workflows/check-docs-sidebar.yml New CI workflow that runs the sidebar check; BASE_REF derivation handles PR/push/dispatch correctly, but path-filtering means the workflow is optional rather than mandatory for all PRs.
bin/ci-local Moves FAILED_JOBS/run_job declarations before the early-exit paths and wires in the sidebar check for docs-only changes and the normal full-CI path; all code paths run the check exactly once.
docs/.sidebar-exclusions New exclusions file listing legacy redirect stubs and pages linked from intro rather than sidebar; well-commented with clear guidance for future contributors.
AGENTS.md Single-line addition documenting the sidebar requirement and exclusion mechanism; clear and actionable for AI agents and human contributors.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[CI trigger: push/PR/dispatch] --> B{Path filter\ndocs/oss/** or docs/pro/**?}
    B -- No --> Z[Workflow skipped]
    B -- Yes --> C[checkout with fetch-depth 0]
    C --> D[script/check-docs-sidebar BASE_REF]
    D --> E[git diff --diff-filter=A BASE_REF...HEAD]
    E --> F{Any new\ndocs/oss or docs/pro\n.md/.mdx files?}
    F -- No --> G[✓ Exit 0]
    F -- Yes --> H[For each new file\ncompute DOC_ID]
    H --> I{ID in\n.sidebar-exclusions?}
    I -- Yes --> J[⊘ Skip]
    I -- No --> K{ID appears\nin sidebars.ts?}
    K -- Yes --> L[✓ Pass]
    K -- No --> M[✗ Add to MISSING list]
    J & L & M --> N{All files\nprocessed?}
    N -- No --> H
    N -- Yes --> O{MISSING empty?}
    O -- Yes --> P[✓ Exit 0]
    O -- No --> Q[✗ Exit 1 — list missing IDs]
Loading

Reviews (1): Last reviewed commit: "feat: add CI guard requiring sidebar upd..." | Re-trigger Greptile

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ec91d8625

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@bin/ci-local`:
- Around line 45-50: The run_job function currently uses eval "$command", which
enables command injection when user-controlled values like BASE_REF are
embedded; change run_job to take the job name and command arguments (e.g.,
run_job job_name -- script/check-docs-sidebar arg1 arg2) and execute them with
an argv-based call (use "$@" for the command invocation) instead of eval; update
all call sites that currently build a single quoted string (the
script/check-docs-sidebar invocations at the spots mentioned) to pass the
program and its arguments as separate parameters so no user input is subject to
shell re-parsing.

In `@script/check-docs-sidebar`:
- Line 43: The assignment to ADDED_FILES currently swallows git errors by
falling back to an empty string; update the ADDED_FILES capture to fail the
script when `git diff --name-only --diff-filter=A "$BASE_REF"..."$CURRENT_REF"`
returns a non-zero exit (remove the `|| echo ""` fallback and/or check the
command's exit status), so any git diff failure causes the script to exit
non-zero and not silently proceed; locate the ADDED_FILES assignment and adjust
it and any subsequent logic that assumes a successful diff (references:
ADDED_FILES, BASE_REF, CURRENT_REF).
- Line 63: The script uses Bash associative arrays (declare -A EXCLUSION_MAP)
which require Bash 4+, so add a Bash version check at the top of the affected
scripts (script/check-docs-sidebar and bin/ci-rerun-failures; note bin/ci-local
invokes them) that verifies BASH_VERSINFO[0] is >= 4 and prints a clear error
instructing macOS users to upgrade Bash (or run with a newer shell), then exit
non‑zero; additionally add a short note to the README or CONTRIBUTING docs
stating the Bash 4+ requirement so contributors know to install/enable a newer
Bash before running local CI scripts.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c0ffdf9-89b0-4e8c-a23b-e0cc1a8e2695

📥 Commits

Reviewing files that changed from the base of the PR and between 16ac897 and 8ec91d8.

📒 Files selected for processing (5)
  • .github/workflows/check-docs-sidebar.yml
  • AGENTS.md
  • bin/ci-local
  • docs/.sidebar-exclusions
  • script/check-docs-sidebar

@ihabadham
Copy link
Copy Markdown
Collaborator

WIP — picking this up.

Address review feedback with empirically-reproduced fixes:

- script/check-docs-sidebar: exit hard on fetch/diff errors instead of
  silently reporting "no new docs" (matches ci-changes-detector). Add
  base-ref existence validation. Change --diff-filter=A to AR so pure
  renames are caught. Include uncommitted (staged + unstaged) files in
  local dev via `git diff HEAD`, not just untracked via ls-files.
- bin/ci-local: make run_job variadic so new callers can pass argv
  without eval. Legacy single-string callers unchanged. Update the four
  new sidebar-check call sites to pass BASE_REF as a separate argument
  (fixes a quoting brittleness when refs contain single quotes).
- docs/.sidebar-exclusions: add getting-started/comparison-with-alternatives
  and getting-started/nextjs-with-separate-rails-backend — intentionally
  removed from the sidebar in #3065 as deep-dive companions to the
  Decision Guide, kept published as supporting material.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
bin/ci-local (1)

44-73: ⚠️ Potential issue | 🟠 Major

run_job still keeps an eval execution path.

Line 56 preserves the same injection footgun that was previously flagged. Even with argv usage for docs checks, any future string-form caller can reintroduce command parsing risk. Prefer argv-only execution and migrate remaining string-form call sites.

#!/bin/bash
set -euo pipefail
# Verify eval path still exists and identify legacy string-form callers.
rg -n 'eval "\$1"' bin/ci-local
rg -nP 'run_job\s+"[^"]+"\s+"[^"]+"' bin/ci-local
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ci-local` around lines 44 - 73, The run_job function currently retains a
legacy eval-based execution path (the eval "$1" branch) which reintroduces shell
injection risk; remove the eval branch and enforce argv-only invocation in
run_job (accept job_name then command+args and invoke them as "$@"), update or
migrate any legacy string-form callers (calls matching run_job "Name" "command
string") to the argv form (run_job "Name" cmd arg1 ...), and optionally add a
defensive check inside run_job to error out if only a single non-empty argument
is passed so future callers cannot rely on a single-string eval path; target the
run_job function and all call sites that use the quoted single-string form.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/ci-local`:
- Around line 49-73: The script exits early because run_job failures trigger set
-euo pipefail; for any invocation of run_job that is followed by a later check
of FAILED_JOBS you must allow the command to fail without exiting the script —
change those calls to append "|| true" (e.g., replace calls like run_job "name"
... with run_job "name" ... || true) so FAILED_JOBS aggregation and subsequent
checks run; apply this change to every run_job invocation that precedes the
FAILED_JOBS post-check branches (the calls referenced around the three
post-check blocks) so the script continues to the aggregate failure logic.

---

Duplicate comments:
In `@bin/ci-local`:
- Around line 44-73: The run_job function currently retains a legacy eval-based
execution path (the eval "$1" branch) which reintroduces shell injection risk;
remove the eval branch and enforce argv-only invocation in run_job (accept
job_name then command+args and invoke them as "$@"), update or migrate any
legacy string-form callers (calls matching run_job "Name" "command string") to
the argv form (run_job "Name" cmd arg1 ...), and optionally add a defensive
check inside run_job to error out if only a single non-empty argument is passed
so future callers cannot rely on a single-string eval path; target the run_job
function and all call sites that use the quoted single-string form.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7af4ec37-d7de-4a40-96f3-9a4d7231320e

📥 Commits

Reviewing files that changed from the base of the PR and between 8ec91d8 and 6cd974a.

📒 Files selected for processing (3)
  • bin/ci-local
  • docs/.sidebar-exclusions
  • script/check-docs-sidebar
✅ Files skipped from review due to trivial changes (1)
  • docs/.sidebar-exclusions
🚧 Files skipped from review as they are similar to previous changes (1)
  • script/check-docs-sidebar

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 10, 2026

Review: CI guard for docs sidebar entries

Overall this is a clean, well-structured addition. The script is readable, the exclusion mechanism is a good escape hatch, and wiring it into both bin/ci-local and GitHub Actions is the right approach. A few issues to address before merge:

Bug (must fix)

Local dev mode checks modified docs, not just new ones (script/check-docs-sidebar line 58).

git diff --name-only HEAD returns every file that differs between HEAD and the working tree — including edits to docs that already exist. If a developer edits an existing doc that isn't in sidebars.ts (e.g. a pre-existing exclusion), they'd get a false failure locally even though CI would pass. The fix is one word: add --diff-filter=AR to match the committed-diff filter used on line 51.

Should fix

--depth=50 may exclude the merge-base (script/check-docs-sidebar line 37).

The three-dot git diff A...B needs the common ancestor in the local object store. On branches that diverged from main more than 50 commits ago this fetch depth may not include that ancestor, causing the diff to fail silently. CI is unaffected (fetch-depth: 0), but local runs on long-lived branches would be unreliable. Dropping the depth limit for local fetches is the safe choice.

Implicit eval/argv heuristic in run_job (bin/ci-local lines 50–56).

The $# -eq 1 → eval path is determined by argument count, not caller intent. A future contributor adding a single-token argv call (e.g. run_job "Fmt" gofmt) would silently get eval behaviour without any warning. At minimum the comment should say "a single-argument call routes through eval regardless of intent."

Minor / informational

  • Sidebar check uses substring match, not AST (script/check-docs-sidebar lines 115–118): a doc ID appearing in a sidebars.ts comment counts as "found." Unlikely to cause problems in practice, but worth a note in the script.

  • run_job calls in the non-docs-only path are bare statements under set -e: if the sidebar check fails at line 206 of bin/ci-local, the script exits immediately rather than accumulating the failure and continuing to linting/tests. This is a pre-existing pattern in the script (all run_job calls share this behaviour), so it doesn't block this PR, but it means the FAILED_JOBS accumulation only functions when everything passes.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6cd974a1a7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

- script/check-docs-sidebar: add --diff-filter=AR to the local uncommitted
  diff. Without it, editing any pre-existing doc absent from sidebars.ts
  (e.g. a release-notes file) would trigger a false local failure even
  though CI (which uses --diff-filter=AR on the committed diff) would pass.
  Verified empirically: editing docs/oss/upgrading/release-notes/16.4.0.md
  previously failed; now passes.
- bin/ci-local: split the variadic run_job helper into two explicit forms.
  run_job_argv takes argv and invokes directly (no shell re-parsing, safe
  with variables). _run_job_shell keeps the legacy single-string shell path
  for compound commands like "cd X && make" that cannot be expressed as
  argv. run_job is a back-compat wrapper that delegates to _run_job_shell,
  so the 18 existing legacy callers are untouched. The 4 new sidebar-check
  call sites use run_job_argv for unambiguous argv semantics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ihabadham
Copy link
Copy Markdown
Collaborator

Address-review summary

Scan scope: full PR history (no prior summary comment existed).

Mattered

Round 1 (pre-fix):

  • script/check-docs-sidebar — staged files missed → fixed in 6cd974a
  • script/check-docs-sidebar — silent pass on bad/unresolvable base ref → fixed in 6cd974a (now exits 1 with clear error + git rev-parse --verify)
  • script/check-docs-sidebar--diff-filter=A missed renames → fixed in 6cd974a (now AR)
  • bin/ci-localeval embedding of $BASE_REF brittle on single-quote refs → fixed in 6cd974a + refined in 1d83b3e (new run_job_argv helper, argv-based)
  • Pre-existing orphans getting-started/comparison-with-alternatives and getting-started/nextjs-with-separate-rails-backend — added to docs/.sidebar-exclusions in 6cd974a (intentionally removed from sidebar in Consolidate docs comparison pages into single evaluation entry #3065 as deep-dive companions)

Round 2 (post-fix, by claude + chatgpt-codex):

  • script/check-docs-sidebar:58regression introduced by round-1 fix: git diff --name-only HEAD caught plain modifications to existing docs as "added" → fixed in 1d83b3e (now --diff-filter=AR HEAD). Empirically verified: editing docs/oss/upgrading/release-notes/16.4.0.md no longer trips the script.
  • bin/ci-local — implicit $# -eq 1 dispatch in run_job was a footgun → fixed in 1d83b3e by splitting into explicit run_job_argv + _run_job_shell (with a run_job back-compat wrapper for the 18 legacy callers).

Deferred to follow-up issues:

Not addressed in this PR (out of scope):

  • bin/ci-localset -e + run_job failure breaks FAILED_JOBS aggregation (coderabbit). Pre-existing across all 22 run_job call sites in the script, not specific to the 4 new sidebar-check ones. Any fix is a behavior change (fail-fast vs collect-all) that affects the whole script's error-aggregation design.

Skipped

  • Grep false-positive on commented-out doc IDs (claude round 1, greptile round 1, claude round 2): theoretical only — verified that the current sidebars.ts:3-15 bare-bullet comment format is not matched by the quoted-string grep.
  • Workflow paths: filter excluding non-docs PRs (greptile): intentional, matches the existing repo convention for targeted checks (actionlint, bundle-size, check-markdown-links, trigger-docs-site all use the same pattern). Path-filtered workflows in this repo are not required status checks; verified against recent docs-only PR Add manual dev environment testing checklist for coding agents #3074 which merged cleanly with all path-filtered workflows skipped.
  • Bash 4+ requirement (coderabbit): pre-existing in the repo, bin/ci-rerun-failures:178,213,237 already uses declare -A. Not a new constraint.
  • Bot meta-summaries (coderabbit walkthrough, greptile summary, chatgpt-codex intro): no standalone action needed; addressed via their constituent inline comments.

Follow-up issues

Verification

Empirically re-tested after 1d83b3e: baseline (no changes), untracked new doc, staged new doc, editing existing doc (regression check), bad base ref, and rename detection — all five tests pass. Both run_job_argv and legacy run_job verified in isolation.

Fix commits

  • 6cd974a1a — first round of fixes
  • 1d83b3e7b — round 2 (regression fix + explicit run_job_argv)

All 17 review threads are resolved. Next default scan starts after this comment. Say check all reviews to rescan the full PR.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 11, 2026

Review: feat: add CI guard requiring sidebar updates when adding docs

Overall this is a solid addition — enforcing sidebar registration via CI is exactly the right approach to prevent orphaned docs, and the script is well-structured with good error handling and a clear exclusion mechanism.

A few issues worth addressing before merging:

Bugs / correctness

  1. declare -A requires bash 4.0+ (inline comment on line 77 of script/check-docs-sidebar) — macOS ships bash 3.2 as /bin/bash, and #!/usr/bin/env bash resolves there on stock setups. This will break the script for macOS developers who haven't installed a newer bash via Homebrew. Either add a bash version guard at the top or replace the associative array with a grep-based lookup that works on bash 3.x.

  2. Leading whitespace not stripped from exclusion entries (line 82) — the exclusion parser strips inline comments and trailing whitespace, but leaves leading whitespace intact. An entry written as doc-id (leading space) would be stored verbatim and never match doc-id. Silent non-exclusion.

  3. Dead code in bin/ci-local docs-only fast-paths (lines 113–118, and the two equivalent blocks) — bin/ci-local runs under set -euo pipefail. If run_job_argv returns non-zero, bash exits immediately at that line; the if [ ${#FAILED_JOBS[@]} -gt 0 ] check is unreachable. The end behavior is correct (exits 1) but via set -e, not the intended accumulator logic. Either drop the dead checks, or call run_job_argv ... || true and keep them.

Minor concerns

  1. --depth=50 fetch may be too shallow locally (line 37) — in CI the full history is already present (fetch-depth: 0). Locally on a long-lived branch the merge-base may not be within the 50-commit window. Consider removing --depth for the local fetch.

  2. Commented-out sidebar entries are not filtered (line 117) — the grep-based check would match a doc ID that only appears in a TypeScript comment (// 'some/doc'), giving a false green. Low probability but worth noting.

  3. workflow_dispatch BASE_REF (workflow line 37) — defaults to the literal origin/main string when triggered manually, causing a redundant git fetch inside the script (even though fetch-depth: 0 already has full history). A workflow_dispatch input would let callers be explicit.

What's good

  • Correct use of triple-dot (...) diff syntax for PR-relative comparisons
  • diff-filter=AR is the right filter (added + renamed, not modifications)
  • The local-dev UX (including untracked files, colored output, clear fix instructions) is excellent
  • Moving FAILED_JOBS / helper function definitions before the docs-only fast-exit paths fixes an ordering bug from the original script
  • The run_job_argvrun_job layering is clean and the reasoning in comments is clear
  • .sidebar-exclusions format with inline reason comments is developer-friendly

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
bin/ci-local (2)

112-117: De-duplicate the docs-only sidebar-check exit path.

The same block appears in three branches. A small helper would reduce drift risk and simplify future edits.

Also applies to: 128-132, 153-157

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ci-local` around lines 112 - 117, The docs-only sidebar-check block is
duplicated; refactor by adding a small helper function (e.g.,
run_docs_sidebar_check) that encapsulates running run_job_argv "Docs Sidebar
Check" script/check-docs-sidebar "$BASE_REF", printing the "${GREEN}No further
CI needed for documentation-only changes!${NC}" message, and checking the
FAILED_JOBS array to exit with status 1 if needed; then replace the three
duplicated blocks with calls to run_docs_sidebar_check to avoid drift and
centralize the logic.

217-219: Run the sidebar check before dependency installation for faster failure.

Line 217 says this check is fast/no-deps, but it currently runs after dependency install. Moving it above the install block would fail faster when sidebar coverage is missing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ci-local` around lines 217 - 219, The Docs Sidebar Check currently runs
after dependencies are installed; move the run_job_argv invocation for the
sidebar check (the line starting with run_job_argv "Docs Sidebar Check"
script/check-docs-sidebar "$BASE_REF") so it executes before the
dependency-install step (i.e. before the script/step that installs dependencies
or the job that performs dependency installation) so the fast/no-deps check
fails early.
script/check-docs-sidebar (1)

79-83: Trim leading whitespace in exclusion entries to prevent silent misses.

Line 82 trims trailing whitespace only. A leading space in docs/.sidebar-exclusions would create a non-matching key.

♻️ Proposed hardening
   while IFS= read -r line; do
     # Strip inline comments and trailing whitespace
     line="${line%%#*}"
+    line="${line#"${line%%[![:space:]]*}"}"
     line="${line%"${line##*[![:space:]]}"}"
     # Skip blank lines
     [ -z "$line" ] && continue
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@script/check-docs-sidebar` around lines 79 - 83, The loop that reads
exclusions (the while IFS= read -r line; do ... done) only strips inline
comments and trailing whitespace from the variable line, so entries in
docs/.sidebar-exclusions with leading spaces will not match; update the read
loop to also remove leading whitespace from line (using shell parameter
expansion to trim leading spaces) after stripping comments and trailing
whitespace so entries are normalized before any matching logic runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/ci-local`:
- Around line 112-117: The docs-only sidebar-check block is duplicated; refactor
by adding a small helper function (e.g., run_docs_sidebar_check) that
encapsulates running run_job_argv "Docs Sidebar Check" script/check-docs-sidebar
"$BASE_REF", printing the "${GREEN}No further CI needed for documentation-only
changes!${NC}" message, and checking the FAILED_JOBS array to exit with status 1
if needed; then replace the three duplicated blocks with calls to
run_docs_sidebar_check to avoid drift and centralize the logic.
- Around line 217-219: The Docs Sidebar Check currently runs after dependencies
are installed; move the run_job_argv invocation for the sidebar check (the line
starting with run_job_argv "Docs Sidebar Check" script/check-docs-sidebar
"$BASE_REF") so it executes before the dependency-install step (i.e. before the
script/step that installs dependencies or the job that performs dependency
installation) so the fast/no-deps check fails early.

In `@script/check-docs-sidebar`:
- Around line 79-83: The loop that reads exclusions (the while IFS= read -r
line; do ... done) only strips inline comments and trailing whitespace from the
variable line, so entries in docs/.sidebar-exclusions with leading spaces will
not match; update the read loop to also remove leading whitespace from line
(using shell parameter expansion to trim leading spaces) after stripping
comments and trailing whitespace so entries are normalized before any matching
logic runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eb406432-3a6f-440e-b65c-624ed07493b9

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd974a and 1d83b3e.

📒 Files selected for processing (2)
  • bin/ci-local
  • script/check-docs-sidebar

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d83b3e7bb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

- Strip leading whitespace in the exclusion parser. An entry written as
  ` doc-id` (with accidental leading space) was previously stored
  verbatim in EXCLUSION_MAP and failed to match a lookup for `doc-id`,
  producing a silent non-exclusion → false CI failure. Verified
  empirically. Trivial one-line bash parameter-expansion fix.
- Make the `git fetch origin` call best-effort and move the hard-fail
  gate to the subsequent `git rev-parse --verify` check. The real
  invariant is "base ref must be resolvable locally", not "the fetch
  call must succeed". The previous hard-fail broke offline local
  development even when a cached origin/main ref existed and would
  have produced a correct diff. Verified against four scenarios:
  online+valid, offline+cached, online+bad, offline+bad — all behave
  correctly. This is strictly better than the previous behavior:
  preserves the anti-silent-pass protection from round 1 while
  removing the offline-dev lockout introduced by round 2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ihabadham
Copy link
Copy Markdown
Collaborator

Address-review summary

Scan scope: activity after 2026-04-11T00:57:25Z (previous checkpoint).

Mattered

  • script/check-docs-sidebar:82 — leading whitespace in exclusion entries was not stripped, causing silent non-exclusion on accidentally space-prefixed IDs. Fixed in acc0fc4 by adding line="${line#"${line%%[![:space:]]*}"}". Verified empirically: doc-id and <TAB>doc-id now match doc-id lookups.
  • script/check-docs-sidebar:34-48 — round-2 hard-fail on git fetch broke offline local dev even when a cached origin/main ref was available. Fixed in acc0fc4 by making the fetch best-effort (|| true) and gating on git rev-parse --verify instead. The anti-silent-pass protection is preserved and semantically improved: "base ref must be resolvable locally" is the real invariant. Verified against four scenarios: online+valid, offline+cached, online+bad, offline+bad — all behave correctly.

Skipped

  • Bash 4+ requirement (claude[bot]) — duplicate of previously declined item. Pre-existing repo constraint via bin/ci-rerun-failures:178,213,237; this PR does not introduce new requirements.
  • --depth=50 too shallow (claude[bot]) — triplicate of previously declined item. Tracked in bin/ci-local and script/ci-changes-detector fail silently on shallow clones with long-lived branches #3108 for cross-script coordination with ci-changes-detector. The offline aspect of this concern is partially mitigated by acc0fc4 (the soft-fetch means cached refs can still be used).
  • set -e + run_job_argv makes FAILED_JOBS check unreachable (claude[bot]) — duplicate of round-2 coderabbit concern. Pre-existing across all 22 run_job callers; outcome is correct (script exits non-zero), the post-check is dead code but harmless. Any real fix is a behavior change affecting the whole script design, out of scope.
  • Grep false-positive on commented-out doc IDs (claude[bot]) — fourth report across three rounds. Verified against current sidebars.ts:3-15 bare-bullet comment format; quoted-string grep cannot false-positive. Reviewer acknowledged in the comment body that the precise-quote check is "fine" and raised only the theoretical edge case.
  • workflow_dispatch redundant fetch (claude[bot]) — cosmetic inefficiency on a rarely-used trigger. After acc0fc4 the fetch is best-effort, so worst case is a wasted ~1-3s network call, not a failure.
  • CodeRabbit round-3 nitpicks (dedup 3 docs-only blocks, move sidebar check before dep install) — minor refactoring suggestions, non-blocking.

Fix commit

  • acc0fc4ec — round 3 fixes (leading whitespace + offline-friendly fetch)

All 24 review threads are resolved. Next default scan starts after this comment. Say check all reviews to rescan the full PR.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 11, 2026

Review: CI guard for docs sidebar entries

The overall approach is sound — detect added/renamed docs, derive sidebar IDs, check against sidebars.ts, and allow explicit opt-outs in .sidebar-exclusions. The refactoring of bin/ci-local to introduce run_job_argv (safe argv invocation, no eval) is a genuine improvement. Three issues worth addressing before merging:

Bugs

1. Pre-existing untracked docs trigger false positives locally (script/check-docs-sidebar lines 63–66)

git ls-files --others --exclude-standard returns every untracked file, not just ones added since BASE_REF. A developer with any unrelated draft doc (e.g. docs/oss/wip.md) would see it flagged on every local run even though CI (which uses the committed diff) never would. See inline comment for a suggested fix.

2. Commented-out entries in sidebars.ts falsely satisfy the check (script/check-docs-sidebar lines 123–127)

grep -qF "'${DOC_ID}'" matches the raw file content including TypeScript comments. A stale // TODO: re-add 'getting-started/new-feature' line would cause the check to pass while the doc is actually absent from the rendered sidebar. Pre-filtering comment lines before grepping is sufficient mitigation for the common case.

3. set -euo pipefail makes the sidebar check abort all subsequent ci-local jobs on failure (bin/ci-local line 218)

run_job_argv returns 1 on failure. Since the call is a standalone statement (not in if/||), set -e exits the script before any linting or tests run. A developer with both a missing sidebar entry and a RuboCop failure needs two CI round trips to discover both. Adding || true (the FAILED_JOBS array is still populated inside the function) lets all checks run before the summary. See inline comment.

Minor

  • --depth=50 in the local fetch (line 41) is a heuristic that could leave git rev-parse --verify failing on a branch with a deep history — the error message could include a hint to run git fetch origin main manually.
  • The push: branches: [main] trigger in the GitHub Actions workflow means the check runs a second time when a PR is merged, which is harmless but redundant. Not worth changing unless workflow minute cost matters.

Otherwise this is clean work: good use of --diff-filter=AR, the whitespace-stripping in the exclusion parser is correct, the git rev-parse --verify hard gate is the right place for the base-ref check, and the AGENTS.md update closes the loop for future contributors.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: acc0fc4ec8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

- script/check-docs-sidebar: strip single-line TypeScript comments from
  SIDEBAR_CONTENT before the quoted-string grep. A comment like
  `// TODO: re-add 'getting-started/foo' later` could previously satisfy
  the presence check, causing a silent pass even though the doc ID was
  not in any real sidebar category. The current sidebars.ts uses bare
  bullet comments (`// - misc/doctrine`), so this was a latent concern
  rather than a reachable bug — but it has been reported across four
  review rounds and the minimal fix is one grep line. Block comments are
  not handled; the convention in this file is single-line comments only.
  Verified empirically: injected a fake ghost ID inside a TODO comment,
  confirmed the grep no longer false-matches it.
- bin/ci-local: add `|| true` to the always-run sidebar check call.
  This call is architecturally unique among the 22 run_job_* invocations
  — it is the only one at the top level of the main path that is not
  gated by an `if "$RUN_X" = true` block. Without `|| true`, a sidebar
  failure would trigger `set -euo pipefail` and abort all subsequent
  lint/test jobs, forcing contributors to fix sidebar first then
  discover lint/test failures in a second round trip. The FAILED_JOBS
  array is still populated inside run_job_argv, so the end-of-script
  summary correctly reports the sidebar failure along with any other
  failures. The 21 gated legacy callers are untouched; any broader
  fail-fast vs aggregate redesign belongs in a separate PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ihabadham
Copy link
Copy Markdown
Collaborator

Address-review summary

Scan scope: activity after 2026-04-11T01:17:03Z (previous checkpoint).

Mattered

  • script/check-docs-sidebar:97 — Strip single-line TypeScript comments from SIDEBAR_CONTENT before grep. Fixed in c48f494. Now built via grep -v '^[[:space:]]*//' "$SIDEBAR_FILE" instead of raw cat. A quoted doc ID inside a comment (e.g. // TODO: re-add 'foo') can no longer satisfy the presence check. Verified empirically by injecting // TODO: re-add 'getting-started/fake-ghost' later with a matching untracked doc and confirming the check correctly fails. Block comments are not handled; the convention in this file is single-line // comments only. This concern was raised across four review rounds — the round-4 reviewers finally proposed a minimal 2-line fix, which is what landed.
  • bin/ci-local:218 — Add || true to the always-run sidebar check. Fixed in c48f494. This call is architecturally unique: the only run_job_* invocation at the top level of the main path that is not gated by an if "$RUN_X" = true block. Without the guard, set -euo pipefail would kill the script on a sidebar failure before any lint/test jobs ran, forcing two round trips. FAILED_JOBS is still populated inside run_job_argv, so the end-of-script summary reports the sidebar failure alongside any other failures. The 21 gated legacy callers are untouched; the broader fail-fast vs aggregate redesign belongs in a separate PR.

Declined (with rationale)

  • script/check-docs-sidebar:66git ls-files --others flags stale untracked docs in docs/oss/ (claude[bot]). Verified empirically: a stale docs/oss/wip-draft.md does get flagged. Verified the proposed fix (drop the UNTRACKED block): it would silently regress the round-1 Workflow A fix because git diff --diff-filter=AR HEAD does not include untracked files. Trade-off evaluation: catching Workflow A (common: touch → edit → run bin/ci-local → git add) early is more valuable than avoiding the rare false positive on stale drafts inside the protected path docs/oss/. The "false positive" is arguably the correct signal — an unregistered doc file in docs/oss/ is exactly what this guard exists to catch. Workarounds for genuine scratch files (git stash -u, .gitignore, move to /tmp/) are trivial.

Skipped (not a bug in current state)

  • --depth=50 error message hint (claude[bot] minor): the error message already includes the suggested hint after acc0fc4. Reviewer hadn't seen the fixed version.
  • push: branches: [main] runs twice on PR merge (claude[bot] minor): reviewer explicitly marked this as harmless and not worth changing.

Fix commit

  • c48f49423 — round 4 fixes (comment-strip filter + || true on always-run sidebar check)

All 28 review threads are resolved. Next default scan starts after this comment. Say check all reviews to rescan the full PR.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 11, 2026

Review: feat: add CI guard requiring sidebar updates when adding docs

Overall this is a well-designed guard. The script is clearly structured, handles edge cases (shallow clones, offline dev, zero-SHA initial push, renamed files, inline comments in sidebars.ts), and the GitHub Actions workflow has tight permissions and correct path filters. The run_job_argv / _run_job_shell split in bin/ci-local is a clean upgrade from the eval-based helper.

One substantive issue

Dead code in the docs-only early-exit paths (bin/ci-local lines 113–118, 128–133, 153–158)

The if [ ${#FAILED_JOBS[@]} -gt 0 ]; then exit 1; fi pattern after a bare run_job_argv call is unreachable on the failure path. Because bin/ci-local runs under set -euo pipefail, when run_job_argv returns 1 bash exits immediately at that line — the FAILED_JOBS check is never evaluated. On the success path, FAILED_JOBS is always empty, so the check is trivially false there too.

The main-path call (line 224) correctly uses || true to suppress set -e propagation, populating FAILED_JOBS for the end-of-script summary. The three early-exit call sites don't need that full machinery (they exit immediately either way), but the current pattern implies the FAILED_JOBS check is the control-flow driver when it isn't. See inline comment for a suggested fix.

Minor observations

  • --depth=50 on the best-effort fetch in check-docs-sidebar could miss the common ancestor on very long-lived local branches. Not a correctness issue for CI (which uses fetch-depth: 0), but worth bumping or documenting for local dev. See inline note.
  • The SIDEBAR_CONTENT comment-stripping (grep -v '^[[:space:]]*//') correctly handles single-line // comments and leaves inline // trailing comments intact. The documented gap is /* block comments */; not a concern today but worth keeping in mind as sidebars.ts grows. See inline note.

Nits

  • The docs/.sidebar-exclusions format (comments with #, blank lines ignored, whitespace stripped) is clean and self-documented. Good choice.
  • The three-dot "$BASE_REF"..."$CURRENT_REF" diff syntax correctly computes the merge-base, so adding commits to main after a PR is opened won't cause phantom "new docs" in the diff. Well thought through.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c48f494239

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

…it paths

The three docs-only early-exit branches each had:

    run_job_argv "Docs Sidebar Check" script/check-docs-sidebar "$BASE_REF"
    echo -e "${GREEN}No further CI needed..."
    if [ ${#FAILED_JOBS[@]} -gt 0 ]; then
      exit 1
    fi
    exit 0

Under `set -euo pipefail`, this was confused control flow: a failing
run_job_argv immediately exited the script via set -e (before reaching
the FAILED_JOBS check), and on success the FAILED_JOBS array was
trivially empty. The `if FAILED_JOBS > 0` block was dead code that
made it look like FAILED_JOBS drove the exit code when it didn't.

Replace each block with a simple pattern: let set -e do its job on
failure, explicit exit 0 on success, with a comment making the
reliance on set -e obvious. No behavior change — both paths already
did the right thing — but the code is honest about it now.

Note: this is specifically *not* the same fix as the always-run call
at the main path (line 224), which uses `|| true` so that subsequent
lint/test jobs can still run. The early-exit paths have no subsequent
jobs, so set -e is the simpler and more correct choice there.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@ihabadham
Copy link
Copy Markdown
Collaborator

Address-review summary

Scan scope: activity after 2026-04-11T01:37:51Z (previous checkpoint).

Mattered

  • bin/ci-local:113-118, 128-133, 153-158 — Remove dead FAILED_JOBS checks from docs-only early-exit paths. Fixed in 0847202. Under set -euo pipefail, a failing run_job_argv killed the script at that line via set -e; the subsequent if [ ${#FAILED_JOBS[@]} -gt 0 ] check was unreachable on failure and trivially false on success. The dead code implied FAILED_JOBS drove control flow when set -e actually did. Chose removal over adding || true because the early-exit paths have an echo between run_job_argv and the dead check — || true would cause the "No further CI needed" success message to fire on failure too, which is misleading. Added comments at each call site making the reliance on set -e explicit. Intentionally not the same fix as the always-run call at line 224, which uses || true because subsequent lint/test jobs need to still run after a sidebar failure there. Verified success and failure paths behave correctly.

Skipped

  • script/check-docs-sidebar:41--depth=50 heuristic (claude[bot]). Sixth report of this concern, acknowledged softly by reviewer as "just flagging." Already tracked in bin/ci-local and script/ci-changes-detector fail silently on shallow clones with long-lived branches #3108 with the community iterative-deepen pattern; coordinated fix across both scripts belongs in a follow-up PR.
  • script/check-docs-sidebar:100Comment-strip filter review (claude[bot]). Positive confirmation, not a criticism — reviewer explicitly said "this is the right call." Block-comment gap is intentional and already documented in the code.
  • bin/ci-local:224Skip sidebar check in --all mode (chatgpt-codex-connector[bot]). Misunderstanding of the tool's usage model. Non-standard remote setups are handled by passing an explicit base ref: bin/ci-local --all upstream/main. --all is orthogonal to base-ref resolution; making it skip the sidebar would hide the check from the one mode where the developer explicitly asked for everything. After acc0fc4 the check is also offline-tolerant, so stale cached refs work.

Fix commit

  • 084720206 — round 5 fix (remove dead FAILED_JOBS early-exit checks)

All 32 review threads are resolved. Next default scan starts after this comment. Say check all reviews to rescan the full PR.

Copy link
Copy Markdown

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

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 084720206f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

# a `// comment` (e.g. `// TODO: re-add 'foo'`) cannot satisfy the grep below.
# Block comments `/* ... */` are not handled; the convention in this file is
# single-line comments only.
SIDEBAR_CONTENT=$(grep -v '^[[:space:]]*//' "$SIDEBAR_FILE")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Strip inline comments before matching doc IDs

SIDEBAR_CONTENT only removes lines that start with //, so quoted IDs that appear in inline comments (for example items: [...], // TODO add 'new/doc') are still searchable by the later grep -qF checks. In that case a newly added doc can pass this guard without an actual sidebar entry, which weakens the CI protection this script is intended to provide.

Useful? React with 👍 / 👎.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 11, 2026

This is solid work — the design is well-thought-out and hardened across several review rounds. Key choices are good: run_job_argv for safe argv invocation with run_job as a back-compat wrapper, best-effort fetch + hard gate via git rev-parse --verify for offline-safe local dev, persist-credentials: false and permissions: contents: read in the workflow, three-dot ... diff to isolate PR-branch additions, and the .sidebar-exclusions file with comment-based reason documentation.

Three observations (also posted inline). None are blockers — the logic is correct for the current sidebars.ts conventions — but worth noting for awareness.

1. Inline // comments not stripped from SIDEBAR_CONTENT (script/check-docs-sidebar line 100)

The script strips full-line // comments before the sidebar grep, preventing a ghost match from a dedicated comment line. But it does NOT strip inline comments on data lines. A line like:

'other-doc', // TODO: re-add 'getting-started/foo' when X ships

…would produce a false pass for getting-started/foo even though it is not in any real sidebar category. The code comment acknowledges block comments are out of scope, but inline comments on data lines are a reachable case with common TypeScript style. A sed 's|[[:space:]]//.*||' pipe after the grep would close it (with the caveat that // in a string literal would cause a false negative — unlikely for sidebar IDs).

2. Local dev path flags untracked WIP docs (script/check-docs-sidebar lines 63–65)

The local path includes ALL untracked .md/.mdx files under docs/oss/ and docs/pro/ via git ls-files --others. A developer with an unstaged draft doc will see a local CI failure asking them to add a sidebar entry — potentially confusing for contributors keeping work-in-progress docs in-tree. Consider limiting to staged files only (git diff --cached --diff-filter=AR --name-only), or at least add a note to the error output that untracked files are included in the local check.

3. Push-to-main event: BASE_REF is a bare SHA, fetch is silently skipped (workflow line 37)

For push events, BASE_REF = github.event.before is a raw 40-character SHA. The script only triggers git fetch when BASE_REF starts with origin/, so for push events the fetch is skipped entirely — the script relies on fetch-depth: 0 having already made the SHA available. This works correctly today but the coupling is implicit. A brief note in the script header or the workflow step would make it clear that bare SHAs skip the fetch and depend on the full checkout.

# a `// comment` (e.g. `// TODO: re-add 'foo'`) cannot satisfy the grep below.
# Block comments `/* ... */` are not handled; the convention in this file is
# single-line comments only.
SIDEBAR_CONTENT=$(grep -v '^[[:space:]]*//' "$SIDEBAR_FILE")
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.

This strips full-line // comments (where // is the first non-whitespace token), which correctly blocks a ghost match from:

  // TODO: re-add 'getting-started/foo' later

However, it does not strip inline // comments on data lines. A line like:

  'other-doc', // TODO: restore 'getting-started/foo' when X ships

…would cause 'getting-started/foo' to appear in SIDEBAR_CONTENT and satisfy the grep -qF check on line 127, producing a false pass even though getting-started/foo is not in any actual sidebar category.

The code comment acknowledges block comments are out of scope, but this inline-comment case is reachable with typical TypeScript style. A simple fix:

Suggested change
SIDEBAR_CONTENT=$(grep -v '^[[:space:]]*//' "$SIDEBAR_FILE")
SIDEBAR_CONTENT=$(grep -v '^[[:space:]]*//' "$SIDEBAR_FILE" | sed 's|[[:space:]]//.*||')

Caveat: if a sidebar ID ever contained // (it shouldn't — Docusaurus IDs are path segments), this would cause a false negative. The tradeoff is acceptable given how sidebar IDs are formed.

Comment on lines +63 to +66
if [ -z "${CI:-}" ] && [ "$CURRENT_REF" = "HEAD" ]; then
UNCOMMITTED=$(git diff --name-only --diff-filter=AR HEAD 2>/dev/null || echo "")
UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null || echo "")
ADDED_FILES=$(printf "%s\n%s\n%s" "$ADDED_FILES" "$UNCOMMITTED" "$UNTRACKED" | sort -u | grep -v '^$' || echo "")
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.

The git ls-files --others --exclude-standard on line 65 includes all untracked files — not just ones that are staged or intended for the current commit. This means a developer who has a work-in-progress draft at docs/oss/new-feature.md (not yet staged) will immediately see a local sidebar-check failure, even if they were not trying to land that file right now.

Two options:

Option A — staged only (mirrors what CI sees, no surprise failures from drafts):

Suggested change
if [ -z "${CI:-}" ] && [ "$CURRENT_REF" = "HEAD" ]; then
UNCOMMITTED=$(git diff --name-only --diff-filter=AR HEAD 2>/dev/null || echo "")
UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null || echo "")
ADDED_FILES=$(printf "%s\n%s\n%s" "$ADDED_FILES" "$UNCOMMITTED" "$UNTRACKED" | sort -u | grep -v '^$' || echo "")
if [ -z "${CI:-}" ] && [ "$CURRENT_REF" = "HEAD" ]; then
UNCOMMITTED=$(git diff --name-only --diff-filter=AR HEAD 2>/dev/null || echo "")
STAGED_NEW=$(git diff --cached --name-only --diff-filter=AR 2>/dev/null || echo "")
ADDED_FILES=$(printf "%s\n%s\n%s" "$ADDED_FILES" "$UNCOMMITTED" "$STAGED_NEW" | sort -u | grep -v '^$' || echo "")
fi

Option B — keep untracked, add a note to the error message so contributors understand why an unstaged draft triggered the failure.

Option A is safer for developer UX; Option B is more eager (catches the omission as early as possible).


- name: Check new docs have sidebar entries
env:
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
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.

For push events, github.event.before is a raw 40-character commit SHA (e.g. a1b2c3d...). The check-docs-sidebar script only triggers its git fetch when BASE_REF starts with origin/ (line 39 of the script), so for push events the fetch is entirely skipped. The script then relies on fetch-depth: 0 (line 30 above) having already made the SHA available locally.

This works correctly, but the coupling is silent — someone reading the script in isolation wouldn't know why a bare SHA is always resolvable in CI. A one-line comment in the workflow step would make it explicit:

Suggested change
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
env:
BASE_REF: ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }}
# fetch-depth: 0 above ensures bare SHAs (push events) are always resolvable;
# the script's git-fetch only fires for 'origin/*' refs (pull_request / workflow_dispatch).
run: script/check-docs-sidebar "$BASE_REF"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Follow-up: require sidebar updates when adding docs (agent docs + CI guard)

2 participants