From f1298c8b35b9a2e1a0d5352a6fb687129bcb56eb Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Wed, 6 May 2026 22:14:53 +0300 Subject: chore(claude-md): update for post-flip MyST-as-primary state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CLAUDE.md scaffolded by /init reflected the pre-MyST repo and was never updated as the migration landed. Replaces it with a current description of how the repo actually works: - MD is the canonical source for migrated pages on all three release branches (current/circinus/sagitta) as of 2026-05-06. RST is the canonical source for pages that haven't been migrated yet. - Documents the rare RST override mechanism: rst-.rst plus docs/_rst_overrides.txt, scripts/swap_sources.py CLI flags. The override list is empty by default; the mechanism is a no-op. - Editing rules: edit the .md for migrated pages, the .rst for non-migrated pages, write new pages as .md from the start. The md- prefix used during the migration is gone. Also adds previously-missing sections: tests command, vale lint command, branches/versions table with constellation→version mapping, PR review flow with Copilot+CodeRabbit, worktree convention. Generated by robots https://vyos.io --- CLAUDE.md | 193 +++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 128 insertions(+), 65 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 60f7d13f..2d553780 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,81 +1,147 @@ -# VyOS Documentation +# CLAUDE.md -RST documentation for VyOS, built with Sphinx and hosted on Read the Docs. +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project + +VyOS user documentation, built with Sphinx and hosted on Read the Docs at https://docs.vyos.io. Sources are MyST Markdown (`.md`) for migrated pages and RST (`.rst`) for the rest. Both formats are first-class to Sphinx; MD is the canonical source for any page that has been migrated, and RST is the canonical source for pages that haven't. + +A small swap mechanism exists for the rare case where a maintainer needs to render the legacy RST version of a page that already has an MD primary — see `RST override mechanism` below. The override list is empty by default. ## Build ```bash -# Docker (recommended) +# Docker (recommended — bundles all deps incl. vale) docker build -t vyos/vyos-documentation docker docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \ -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \ vyos/vyos-documentation make html -# Local +# Live-reload server on port 8000 +docker run --rm -it -p 8000:8000 -v "$(pwd)":/vyos -w /vyos/docs \ + -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \ + vyos/vyos-documentation make livehtml + +# Local (Python 3, see requirements.txt for pinned versions) pip install -r requirements.txt cd docs && make html ``` -Output goes to `docs/_build/html/`. +Output: `docs/_build/html/`. -## RST Conventions +## Tests -### Heading Hierarchy +```bash +pytest tests/ # all tests +pytest tests/test_swap_sources.py # single test file +``` -```rst -##### -Title -##### +## Lint (vale) -******** -Chapters -******** +```bash +docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \ + -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \ + vyos/vyos-documentation vale . # all files +docker run ... vale path/to/page.rst # single file +``` + +## Branches and versions -Sections -======== +One long-lived branch per VyOS release line. Branch names are constellations sorted by area: -Subsections ------------ +| Branch | VyOS version | +|--------|--------------| +| `current` | rolling / 1.5+ (default branch — all new docs target this) | +| `circinus` | 1.5.x | +| `sagitta` | 1.4.x | +| `equuleus` | 1.3.x (legacy) | +| `crux` | 1.2.x (legacy) | -Subsubsections -^^^^^^^^^^^^^^ +PRs target `current`. After merge, request backports via Mergify comments on the PR: -Paragraphs -"""""""""" ``` +@Mergifyio backport circinus +@Mergifyio backport sagitta +``` + +Mergify is configured at the org level (no `.mergify.yml` in the repo). The PR template has a `## Backport` section to declare intent. + +## Architecture + +### Sphinx config (`docs/conf.py`) -The first heading in every RST file must use `#` overline+underline. Files may have field lists (e.g., `:lastproofread:`) or labels before the heading. +- `source_suffix = ['.rst', '.md']` — both formats build into the same site. +- MyST extensions: `colon_fence`, `deflist`, `fieldlist`, `substitution`. +- `myst_fence_as_directive = ["cfgcmd", "opcmd", "cmdincludemd"]` — MyST fences with these names get parsed as if they were RST directives. This is how command pages stay format-portable. +- Custom Sphinx extensions live in `docs/_ext/`: + - `vyos.py` — defines the `cfgcmd`, `opcmd`, `cmdinclude` directives that drive command coverage tracking. + - `testcoverage.py` — reads VyOS XML command definitions and exposes coverage stats. + - `releasenotes.py`, `autosectionlabel.py` — release-notes builder and label-prefix tweak. -### Formatting Rules +### RST override mechanism -- 80 character line limit (except inside `.. code-block::`) -- American English -- Indent with 2 spaces -- Leave a blank line before and after headers -- Use double backticks for inline code: ``` ``command`` ``` -- Use `.. code-block:: none` for command/output blocks +For pages that have been migrated from RST to MyST: -### Address Space +- `docs/.md` — canonical MD source (primary). +- `docs/rst-.rst` — preserved RST sibling kept around as an override option, prefixed `rst-` so it doesn't collide with the MD. Excluded from the build by `exclude_patterns` in `conf.py`. +- `docs/_rst_overrides.txt` — list of page stems that should render from `rst-.rst` instead of `.md`. Empty by default; pages listed here have their RST temporarily activated for the build. +- `scripts/swap_sources.py` — CLI for `--swap` (apply overrides), `--restore`, `--dry-run`, `--status`. Build-time state lands in `docs/_build/_rst_override_state.json` and `docs/_build/_md_exclude.txt` (gitignored). -See `docs/documentation.rst` for canonical rules. Per RFC 5737, RFC 3849, RFC 5389, and RFC 7042: +For pages that have NOT been migrated: -The linter enforces documentation-reserved addresses. Use only: +- `docs/.rst` — original RST, no `rst-` prefix, no MD sibling. -- IPv4: `192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24` -- IPv6: `2001:db8::/32` -- ASN: `64496-64511` (16-bit), `65536-65551` (32-bit) -- MAC: `00-53-00` to `00-53-FF` (unicast), `90-10-00` to `90-10-FF` (multicast) +**Editing rules:** +- Migrated page (has `.md`): edit the `.md`. Don't touch `rst-.rst` unless you're maintaining a parallel RST version that someone has flagged in `_rst_overrides.txt`. +- Non-migrated page (RST-only): edit the `.rst`. +- New page: write it as `.md` from the start. The `md-` prefix that earlier MyST migration commits used is gone — never add it. -**Allowed without suppression:** +Running `make html` runs the swap automatically (it's a no-op when the override list is empty, which is the default state). + +### Command directives + +`.. cfgcmd::`, `.. opcmd::`, and `.. cmdinclude::` are the VyOS-specific Sphinx directives. They are tracked for command coverage — do **not** convert them to plain `.. code-block::`. In MyST, the same directives are written as fenced blocks: ` ```{cfgcmd} set system ... ` (the `myst_fence_as_directive` config makes this work). + +## Source conventions + +### RST heading hierarchy + +``` +##### Title (overline+underline, one per file) +***** Chapters +===== Sections +----- Subsections +^^^^^ Subsubsections +""""" Paragraphs +``` + +The first heading in every RST file uses `#` overline+underline. Field lists (e.g., `:lastproofread:`) or labels may precede it. + +### Formatting + +- 80-character line limit (exception: inside `.. code-block::` / fenced code blocks — `
` preserves source verbatim).
+- American English.
+- Indent with 2 spaces.
+- Blank lines around headings.
+- Inline code: `` ``command`` ``.
+
+### IP addresses (linter-enforced)
+
+Allowed without suppression:
+- RFC 5737 IPv4 docs: `192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`
+- RFC 3849 IPv6 docs: `2001:db8::/32`
 - RFC 1918 private ranges: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`
-- Link-local, loopback, and other non-public ranges
+- Loopback (`127.0.0.0/8`), link-local (`169.254.0.0/16`), `0.0.0.0/0`
 
-**Requires `stop/start_vyoslinter`:**
-- Real public IPs when needed for authenticity (e.g., `8.8.8.8` in DNS examples)
-- NAT64 well-known prefix `64:ff9b::/96`
-- Long URLs or certificate fingerprints that exceed 80 chars
+Allowed ASN: `64496-64511` (16-bit), `65536-65551` (32-bit).
+Allowed MAC ranges: `00-53-00`–`00-53-FF` (unicast), `90-10-00`–`90-10-FF` (multicast).
 
-### Linter Suppression
+**Requires `stop/start_vyoslinter` suppression:**
+- Real public IPs (e.g., `8.8.8.8` for DNS examples).
+- NAT64 well-known prefix `64:ff9b::/96`.
+- Lines over 80 chars (URLs, certificate fingerprints).
+
+### Linter suppression markers
 
 ```rst
 .. stop_vyoslinter
@@ -87,32 +153,29 @@ The linter enforces documentation-reserved addresses. Use only:
 .. start_vyoslinter
 ```
 
-Place markers immediately before/after the block they suppress. Always re-enable with `start_vyoslinter`.
-
-### VyOS-Specific Directives
+In MyST `.md` files use the comment form: `% stop_vyoslinter` / `% start_vyoslinter`. In `.txt` template files (included via `{include}` or `cmdincludemd`) keep the RST form: `.. stop_vyoslinter` / `.. start_vyoslinter`.
 
-- `.. cfgcmd::` — configuration mode commands
-- `.. opcmd::` — operational mode commands
-- `.. cmdinclude::` — include command definitions from XML
+Markers must always come in pairs. Indentation may match the surrounding directive (indented inside a block) or sit at column 0 (top-level) — both are valid.
 
-### Page Structure
+### Configuration page structure
 
-Each configuration page should contain:
+1. **Theory** — what it is, when to use it, relevant RFCs.
+2. **Configuration** — all CLI options as `.. cfgcmd::` (RST) or ` ```{cfgcmd} ` (MD).
+3. **Examples** — practical configurations with topology diagrams.
+4. **Known issues** — problems and workarounds.
+5. **Debugging** — log collection, `show` commands, state indicators.
 
-1. **Theory** — what it is, when to use it, relevant RFCs
-2. **Configuration** — all CLI options with `.. cfgcmd::` directives
-3. **Examples** — practical configurations with topology diagrams
-4. **Known issues** — problems and workarounds
-5. **Debugging** — log collection, `show` commands, state indicators
+### `.. TODO::` markers
 
-## CI
+Two valid uses:
+1. **Tracking** marker on pages that still need `cfgcmd`/`opcmd` conversion — intentional.
+2. **Stale** marker on pages that already have full content — should be removed.
 
-- **Linter** (`doc-linter.py` from `vyos/.github`): checks line length and IP addresses on changed files only
-- **Sphinx build**: runs on Read the Docs for each PR
-- **CLA check**: contributors must sign the CLA
+A PR that both adds and removes TODOs is not contradictory; intent matters.
 
-## Git Workflow
+## CI
 
-- Base branch: `current`
-- Branch naming: `fix/docs-*`, `feat/docs-*`
-- PRs target `current` branch
+- **vyoslinter** (`doc-linter.py` from the `vyos/.github` repo, run via `lint-doc.yml`) — line length and IP rules, on changed files only.
+- **Sphinx build** — runs on Read the Docs for every PR; preview URL appears as a check.
+- **CLA check** — contributors must sign the VyOS CLA before merge.
+- **Conflict check** — fails the PR if it doesn't merge cleanly into base.
-- 
cgit v1.2.3


From 36e66015a27cca0eabb1fdb904b47ae7c1de6101 Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Wed, 6 May 2026 22:52:55 +0300
Subject: chore(claude-md): address Copilot review feedback
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Tests: replace bare `pytest tests/` with the working invocation
  (`cd tests && PYTHONPATH=../scripts python -m pytest ...`) since the
  test imports from `scripts/` which is not on PYTHONPATH by default.
- Vale: expand the single-file invocation into a copy-pasteable
  `docker run` command instead of the placeholder `docker run ...`.
- Sphinx extensions bullet: add the missing `cmdincludemd`,
  `cfgcmdlist`, `opcmdlist` directives (and `cfgcmd`/`opcmd` roles)
  registered by `docs/_ext/vyos.py`.
- Command directives section: clarify that the MyST include directive is
  `cmdincludemd` (not `cmdinclude`) — RST pages use `cmdinclude`,
  MD pages use `cmdincludemd`.
- Linter suppression markers: clarify that `{eval-rst}` blocks inside
  Markdown pages keep the `..` RST marker form, while top-level Markdown
  uses the `%` MyST comment form.

🤖 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 2d553780..f0ed81f5 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -31,18 +31,27 @@ Output: `docs/_build/html/`.
 
 ## Tests
 
+`tests/test_swap_sources.py` imports `swap_sources` from `scripts/`, which is
+not on `PYTHONPATH` by default. Run from the `tests/` directory:
+
 ```bash
-pytest tests/                      # all tests
-pytest tests/test_swap_sources.py  # single test file
+cd tests
+PYTHONPATH=../scripts python -m pytest                    # all tests
+PYTHONPATH=../scripts python -m pytest test_swap_sources.py  # single file
 ```
 
 ## Lint (vale)
 
 ```bash
+# Lint everything
+docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \
+  -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \
+  vyos/vyos-documentation vale .
+
+# Lint a single file (substitute the real path)
 docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \
   -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \
-  vyos/vyos-documentation vale .              # all files
-docker run ... vale path/to/page.rst          # single file
+  vyos/vyos-documentation vale path/to/page.rst
 ```
 
 ## Branches and versions
@@ -74,7 +83,7 @@ Mergify is configured at the org level (no `.mergify.yml` in the repo). The PR t
 - MyST extensions: `colon_fence`, `deflist`, `fieldlist`, `substitution`.
 - `myst_fence_as_directive = ["cfgcmd", "opcmd", "cmdincludemd"]` — MyST fences with these names get parsed as if they were RST directives. This is how command pages stay format-portable.
 - Custom Sphinx extensions live in `docs/_ext/`:
-  - `vyos.py` — defines the `cfgcmd`, `opcmd`, `cmdinclude` directives that drive command coverage tracking.
+  - `vyos.py` — defines the `cfgcmd`, `opcmd`, `cmdinclude`, `cmdincludemd`, `cfgcmdlist`, and `opcmdlist` directives plus the `cfgcmd`/`opcmd` roles that drive command coverage tracking.
   - `testcoverage.py` — reads VyOS XML command definitions and exposes coverage stats.
   - `releasenotes.py`, `autosectionlabel.py` — release-notes builder and label-prefix tweak.
 
@@ -100,7 +109,7 @@ Running `make html` runs the swap automatically (it's a no-op when the override
 
 ### Command directives
 
-`.. cfgcmd::`, `.. opcmd::`, and `.. cmdinclude::` are the VyOS-specific Sphinx directives. They are tracked for command coverage — do **not** convert them to plain `.. code-block::`. In MyST, the same directives are written as fenced blocks: ` ```{cfgcmd} set system ... ` (the `myst_fence_as_directive` config makes this work).
+`.. cfgcmd::`, `.. opcmd::`, and `.. cmdinclude::` are the VyOS-specific Sphinx directives in RST. They are tracked for command coverage — do **not** convert them to plain `.. code-block::`. In MyST the same directives are written as fenced blocks: ` ```{cfgcmd} set system ... ` (enabled by `myst_fence_as_directive`). The MyST include directive is named `cmdincludemd` (not `cmdinclude`) so that template parsing follows MyST rules in MD pages and RST rules in RST pages — pick `cmdinclude` in `.rst`, `cmdincludemd` in `.md`.
 
 ## Source conventions
 
@@ -153,7 +162,7 @@ Allowed MAC ranges: `00-53-00`–`00-53-FF` (unicast), `90-10-00`–`90-10-FF` (
 .. start_vyoslinter
 ```
 
-In MyST `.md` files use the comment form: `% stop_vyoslinter` / `% start_vyoslinter`. In `.txt` template files (included via `{include}` or `cmdincludemd`) keep the RST form: `.. stop_vyoslinter` / `.. start_vyoslinter`.
+In MyST `.md` files use the comment form `% stop_vyoslinter` / `% start_vyoslinter` for top-level Markdown content. Inside `{eval-rst}` blocks (where the embedded content is parsed as RST) keep the RST form `.. stop_vyoslinter` / `.. start_vyoslinter` — the linter scans the source line literally and only the form that matches the surrounding parser is recognized. Likewise, `.txt` template files (included via `{include}` or `cmdincludemd`) keep the RST form.
 
 Markers must always come in pairs. Indentation may match the surrounding directive (indented inside a block) or sit at column 0 (top-level) — both are valid.
 
-- 
cgit v1.2.3


From 815179aab30dd3155a7591af75684620a4d47916 Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Wed, 6 May 2026 23:13:40 +0300
Subject: chore(claude-md): correct vale claim and Sphinx extension list
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Three follow-up Copilot findings on the post-flip CLAUDE.md:

1. Docker image doesn't bundle vale — the repo has no `.vale.ini`,
   `docker/Dockerfile` doesn't install vale, and there's no Vale CI
   workflow. Replace the entire "Lint (vale)" section with an honest
   description of how linting actually happens (`vyoslinter` /
   `doc-linter.py` in `vyos/.github`, run by `lint-doc.yml` on
   changed files only). The Docker build header is updated too —
   it says "bundles Sphinx and the MyST/RTD plugin set" instead of
   the inaccurate "incl. vale".

2. `docs/_ext/releasenotes.py` and `testcoverage.py` are not Sphinx
   extensions — neither has a `setup()` function and neither is in
   `extensions = [...]` in `conf.py`. Reword the bullet so the only
   files described as Sphinx extensions are `vyos.py` and
   `autosectionlabel.py` (both registered in `extensions`); the other
   two are described as standalone helper scripts.

Addresses Copilot review feedback on PR #1902.

\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index f0ed81f5..5b5f6225 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -11,7 +11,7 @@ A small swap mechanism exists for the rare case where a maintainer needs to rend
 ## Build
 
 ```bash
-# Docker (recommended — bundles all deps incl. vale)
+# Docker (recommended — bundles Sphinx and the MyST/RTD plugin set)
 docker build -t vyos/vyos-documentation docker
 docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \
   -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \
@@ -40,19 +40,14 @@ PYTHONPATH=../scripts python -m pytest                    # all tests
 PYTHONPATH=../scripts python -m pytest test_swap_sources.py  # single file
 ```
 
-## Lint (vale)
+## Lint
 
-```bash
-# Lint everything
-docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \
-  -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \
-  vyos/vyos-documentation vale .
-
-# Lint a single file (substitute the real path)
-docker run --rm -it -v "$(pwd)":/vyos -w /vyos/docs \
-  -e GOSU_UID=$(id -u) -e GOSU_GID=$(id -g) \
-  vyos/vyos-documentation vale path/to/page.rst
-```
+The repo doesn't ship a local lint config or pin a linter binary. CI runs
+`vyoslinter` (`doc-linter.py` from the `vyos/.github` repo, via the
+`lint-doc.yml` workflow) on changed files only — see the CI section
+below. For local checks, manually grep for the rules in
+[Source conventions](#source-conventions) (line length, address space,
+suppression markers).
 
 ## Branches and versions
 
@@ -82,10 +77,11 @@ Mergify is configured at the org level (no `.mergify.yml` in the repo). The PR t
 - `source_suffix = ['.rst', '.md']` — both formats build into the same site.
 - MyST extensions: `colon_fence`, `deflist`, `fieldlist`, `substitution`.
 - `myst_fence_as_directive = ["cfgcmd", "opcmd", "cmdincludemd"]` — MyST fences with these names get parsed as if they were RST directives. This is how command pages stay format-portable.
-- Custom Sphinx extensions live in `docs/_ext/`:
-  - `vyos.py` — defines the `cfgcmd`, `opcmd`, `cmdinclude`, `cmdincludemd`, `cfgcmdlist`, and `opcmdlist` directives plus the `cfgcmd`/`opcmd` roles that drive command coverage tracking.
-  - `testcoverage.py` — reads VyOS XML command definitions and exposes coverage stats.
-  - `releasenotes.py`, `autosectionlabel.py` — release-notes builder and label-prefix tweak.
+- Custom modules live in `docs/_ext/` (only files listed in `extensions = [...]` in `conf.py` are actual Sphinx extensions; the others are support scripts loaded ad hoc):
+  - `vyos.py` (Sphinx extension, registered as `vyos`) — defines the `cfgcmd`, `opcmd`, `cmdinclude`, `cmdincludemd`, `cfgcmdlist`, `opcmdlist` directives and `cfgcmd`/`opcmd` roles that drive command coverage tracking.
+  - `autosectionlabel.py` (Sphinx extension, registered as `autosectionlabel`) — connects to `doctree-read` to register sections as labels.
+  - `testcoverage.py` — standalone helper that reads VyOS XML command definitions and exposes coverage stats; not a Sphinx extension.
+  - `releasenotes.py` — standalone release-notes/changelog generator script; not a Sphinx extension.
 
 ### RST override mechanism
 
-- 
cgit v1.2.3


From 09056fc015ebd7e0fc797f29f9510c43591e85da Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Thu, 7 May 2026 10:30:26 +0300
Subject: docs(claude-md): document PR review bot workflow
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a "PR review workflow" section after CI describing how Copilot
and CodeRabbit are used on this repo:

- Copilot is opt-in (`@copilot review`) and works on drafts.
- CodeRabbit auto-runs when a PR flips to ready-for-review and does
  not review drafts.
- Convention: draft → iterate with Copilot → flip ready → iterate
  CodeRabbit → human review.
- Every review thread needs an explicit reply before resolving.

This matches the cross-repo workflow in `vyos-github` org rules and
makes the convention discoverable for new contributors landing on the
repo without prior context.

\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/CLAUDE.md b/CLAUDE.md
index 5b5f6225..4225b39b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -184,3 +184,20 @@ A PR that both adds and removes TODOs is not contradictory; intent matters.
 - **Sphinx build** — runs on Read the Docs for every PR; preview URL appears as a check.
 - **CLA check** — contributors must sign the VyOS CLA before merge.
 - **Conflict check** — fails the PR if it doesn't merge cleanly into base.
+
+## PR review workflow
+
+Two automated reviewers run on PRs in this repo:
+
+- **Copilot** (`@copilot`) — opt-in, works on draft PRs. Trigger by commenting `@copilot review` on the PR.
+- **CodeRabbit** (`@coderabbitai`) — auto-runs when a PR flips to ready-for-review. Does **not** review drafts. Re-trigger with `@coderabbitai review` after each round of fixes.
+
+Convention:
+
+1. Open the PR as a **draft** (`gh pr create --draft`).
+2. Iterate while draft: comment `@copilot review` per round of commits until Copilot is silent.
+3. Flip to ready (`gh pr ready `). CodeRabbit picks it up automatically.
+4. Address CodeRabbit threads, push commits, re-comment `@coderabbitai review` if you want another pass.
+5. When both bots are silent, the PR is ready for human review.
+
+Every review thread (Copilot or CodeRabbit) needs an **explicit reply** before resolving — fix in code with the commit SHA, or push back with technical reasoning. Never silently resolve.
-- 
cgit v1.2.3


From a42e5f6828a5b2f640b5e8b64510aa00d0e0e798 Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Thu, 7 May 2026 10:32:28 +0300
Subject: Revert "docs(claude-md): document PR review bot workflow"

The bot review workflow is a cross-repo convention that lives in
the org-level rule file (`vyos-github`-style global instructions),
not in per-repo CLAUDE.md. Documenting it here would duplicate the
canonical source and risk drift if the workflow changes.

This reverts the section added in the previous commit on this branch.

\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 4225b39b..5b5f6225 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -184,20 +184,3 @@ A PR that both adds and removes TODOs is not contradictory; intent matters.
 - **Sphinx build** — runs on Read the Docs for every PR; preview URL appears as a check.
 - **CLA check** — contributors must sign the VyOS CLA before merge.
 - **Conflict check** — fails the PR if it doesn't merge cleanly into base.
-
-## PR review workflow
-
-Two automated reviewers run on PRs in this repo:
-
-- **Copilot** (`@copilot`) — opt-in, works on draft PRs. Trigger by commenting `@copilot review` on the PR.
-- **CodeRabbit** (`@coderabbitai`) — auto-runs when a PR flips to ready-for-review. Does **not** review drafts. Re-trigger with `@coderabbitai review` after each round of fixes.
-
-Convention:
-
-1. Open the PR as a **draft** (`gh pr create --draft`).
-2. Iterate while draft: comment `@copilot review` per round of commits until Copilot is silent.
-3. Flip to ready (`gh pr ready `). CodeRabbit picks it up automatically.
-4. Address CodeRabbit threads, push commits, re-comment `@coderabbitai review` if you want another pass.
-5. When both bots are silent, the PR is ready for human review.
-
-Every review thread (Copilot or CodeRabbit) needs an **explicit reply** before resolving — fix in code with the commit SHA, or push back with technical reasoning. Never silently resolve.
-- 
cgit v1.2.3


From beb5f2c4f0c0565ca76c68f302fcd0217004bedf Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Thu, 7 May 2026 11:28:46 +0300
Subject: docs(claude-md): fix Markdown rendering + clarify rst- prefix wording
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Three rendering/clarity fixes flagged by Copilot review across the
post-flip CLAUDE.md trio (#1902/#1906/#1907):

1. **Triple-backtick examples in single-backtick code spans broke
   Markdown rendering.** The MyST fence example
   ```{cfgcmd} set system ...``` was wrapped in a single-backtick
   span, where the inner triple backticks confuse the parser. Switch
   to a four-backtick code span so the literal fence renders cleanly.
   Same fix applied to the "Configuration page structure" bullet.

2. **RST inline-literal example used confusing nested backticks.** The
   line "Inline code: \`\`\`\`command\`\`\`\`" parsed but
   was hard to read. Replace with a plain explanation:
   "Inline code: use double backticks (\`\`command\`\`)".

3. **Clarify that the `rst-` override prefix attaches to the basename,
   not the path stem.** The previous wording `docs/rst-.rst`
   could be misread as a top-level prefix. The actual behavior in
   `scripts/swap_sources.py` is: for a page at
   `docs/automation/cloud-init.md`, the override file lives at
   `docs/automation/rst-cloud-init.rst` (basename-prefixed sibling).
   Spell that out with a concrete example.

Same fixes applied symmetrically across [#1902](https://github.com/vyos/vyos-documentation/pull/1902) (current),
[#1906](https://github.com/vyos/vyos-documentation/pull/1906) (circinus), and [#1907](https://github.com/vyos/vyos-documentation/pull/1907) (sagitta) — all three worktrees
back to byte-identical (md5 `d1ceaddc...`).

\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 5b5f6225..0d897d51 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -88,7 +88,7 @@ Mergify is configured at the org level (no `.mergify.yml` in the repo). The PR t
 For pages that have been migrated from RST to MyST:
 
 - `docs/.md` — canonical MD source (primary).
-- `docs/rst-.rst` — preserved RST sibling kept around as an override option, prefixed `rst-` so it doesn't collide with the MD. Excluded from the build by `exclude_patterns` in `conf.py`.
+- `rst-.rst` — preserved RST sibling living next to `.md` (e.g. for `docs/automation/cloud-init.md`, the override sits at `docs/automation/rst-cloud-init.rst`). The `rst-` prefix attaches to the basename in the same directory, not to the path stem. Excluded from the build by `exclude_patterns` in `conf.py`.
 - `docs/_rst_overrides.txt` — list of page stems that should render from `rst-.rst` instead of `.md`. Empty by default; pages listed here have their RST temporarily activated for the build.
 - `scripts/swap_sources.py` — CLI for `--swap` (apply overrides), `--restore`, `--dry-run`, `--status`. Build-time state lands in `docs/_build/_rst_override_state.json` and `docs/_build/_md_exclude.txt` (gitignored).
 
@@ -97,7 +97,7 @@ For pages that have NOT been migrated:
 - `docs/.rst` — original RST, no `rst-` prefix, no MD sibling.
 
 **Editing rules:**
-- Migrated page (has `.md`): edit the `.md`. Don't touch `rst-.rst` unless you're maintaining a parallel RST version that someone has flagged in `_rst_overrides.txt`.
+- Migrated page (has `.md`): edit the `.md`. Don't touch the `rst-`-prefixed sibling unless you're maintaining a parallel RST version that someone has flagged in `_rst_overrides.txt`.
 - Non-migrated page (RST-only): edit the `.rst`.
 - New page: write it as `.md` from the start. The `md-` prefix that earlier MyST migration commits used is gone — never add it.
 
@@ -105,7 +105,7 @@ Running `make html` runs the swap automatically (it's a no-op when the override
 
 ### Command directives
 
-`.. cfgcmd::`, `.. opcmd::`, and `.. cmdinclude::` are the VyOS-specific Sphinx directives in RST. They are tracked for command coverage — do **not** convert them to plain `.. code-block::`. In MyST the same directives are written as fenced blocks: ` ```{cfgcmd} set system ... ` (enabled by `myst_fence_as_directive`). The MyST include directive is named `cmdincludemd` (not `cmdinclude`) so that template parsing follows MyST rules in MD pages and RST rules in RST pages — pick `cmdinclude` in `.rst`, `cmdincludemd` in `.md`.
+`.. cfgcmd::`, `.. opcmd::`, and `.. cmdinclude::` are the VyOS-specific Sphinx directives in RST. They are tracked for command coverage — do **not** convert them to plain `.. code-block::`. In MyST the same directives are written as fenced blocks: ```` ```{cfgcmd} set system ... ```` (enabled by `myst_fence_as_directive`). The MyST include directive is named `cmdincludemd` (not `cmdinclude`) so that template parsing follows MyST rules in MD pages and RST rules in RST pages — pick `cmdinclude` in `.rst`, `cmdincludemd` in `.md`.
 
 ## Source conventions
 
@@ -128,7 +128,7 @@ The first heading in every RST file uses `#` overline+underline. Field lists (e.
 - American English.
 - Indent with 2 spaces.
 - Blank lines around headings.
-- Inline code: `` ``command`` ``.
+- Inline code: use double backticks (`\`\`command\`\``).
 
 ### IP addresses (linter-enforced)
 
@@ -165,7 +165,7 @@ Markers must always come in pairs. Indentation may match the surrounding directi
 ### Configuration page structure
 
 1. **Theory** — what it is, when to use it, relevant RFCs.
-2. **Configuration** — all CLI options as `.. cfgcmd::` (RST) or ` ```{cfgcmd} ` (MD).
+2. **Configuration** — all CLI options as `.. cfgcmd::` (RST) or ```` ```{cfgcmd} ```` (MD).
 3. **Examples** — practical configurations with topology diagrams.
 4. **Known issues** — problems and workarounds.
 5. **Debugging** — log collection, `show` commands, state indicators.
-- 
cgit v1.2.3


From 0950184655410bf72fb38bcc93955080e3d62ee7 Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Thu, 7 May 2026 11:30:11 +0300
Subject: docs(claude-md): align rst- prefix wording with the canonical
 detailed version

Yuriy/Copilot SWE pushed a more detailed rst-prefix block to [#1906](https://github.com/vyos/vyos-documentation/pull/1906)
that gives an explicit subdirectory example and a counter-example
(`configuration/firewall/zone` maps to
`docs/configuration/firewall/rst-zone.rst`, NOT
`docs/rst-configuration/firewall/zone.rst`). Adopt that exact wording
on the other two PRs in the trio so all three CLAUDE.md files match
again (md5 `acd58911...`).

\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 0d897d51..c921033c 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -87,10 +87,19 @@ Mergify is configured at the org level (no `.mergify.yml` in the repo). The PR t
 
 For pages that have been migrated from RST to MyST:
 
-- `docs/.md` — canonical MD source (primary).
-- `rst-.rst` — preserved RST sibling living next to `.md` (e.g. for `docs/automation/cloud-init.md`, the override sits at `docs/automation/rst-cloud-init.rst`). The `rst-` prefix attaches to the basename in the same directory, not to the path stem. Excluded from the build by `exclude_patterns` in `conf.py`.
-- `docs/_rst_overrides.txt` — list of page stems that should render from `rst-.rst` instead of `.md`. Empty by default; pages listed here have their RST temporarily activated for the build.
-- `scripts/swap_sources.py` — CLI for `--swap` (apply overrides), `--restore`, `--dry-run`, `--status`. Build-time state lands in `docs/_build/_rst_override_state.json` and `docs/_build/_md_exclude.txt` (gitignored).
+- `docs//.md` — canonical MD source (primary).
+- `docs//rst-.rst` — preserved RST sibling kept around as an override
+  option. The `rst-` prefix applies only to the **filename**, not the directory, so
+  `configuration/firewall/zone` maps to `docs/configuration/firewall/rst-zone.rst`
+  (not `docs/rst-configuration/firewall/zone.rst`). Excluded from the build by
+  `exclude_patterns` in `conf.py`.
+- `docs/_rst_overrides.txt` — one stem per line, relative to `docs/` (e.g.
+  `configuration/firewall/zone`). Pages listed here render from
+  `rst-.rst` instead of `.md`. Empty by default.
+- `scripts/swap_sources.py` — CLI for `--swap` (apply overrides), `--restore`,
+  `--dry-run`, `--status`. Build-time state lands in
+  `docs/_build/_rst_override_state.json` and `docs/_build/_md_exclude.txt`
+  (gitignored).
 
 For pages that have NOT been migrated:
 
-- 
cgit v1.2.3


From 5e1d1ebc6c13bbf32327aa672d7bf51bb2a28556 Mon Sep 17 00:00:00 2001
From: Yuriy Andamasov 
Date: Thu, 7 May 2026 11:44:43 +0300
Subject: docs(claude-md): drop literal ``command`` example, link RST quickref
 instead
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The previous wording `Inline code: use double backticks (\`\`command\`\`)`
included literal backslashes intended to escape the inner backticks.
Markdown doesn't interpret backslashes inside code spans, so the
backslashes rendered verbatim — the rendered output read as
\`\`command\`\` instead of the intended ``command``.

Replace with a prose-only line that links the RST docutils quickref
(Inline markup section). No literal RST snippet to escape; rendering
is straightforward.

Same fix applied symmetrically across [#1902](https://github.com/vyos/vyos-documentation/pull/1902)/[#1906](https://github.com/vyos/vyos-documentation/pull/1906)/[#1907](https://github.com/vyos/vyos-documentation/pull/1907) — the
trio is byte-identical (md5 `bdea8e5c...`).

\xf0\x9f\xa4\x96 Generated by [robots](https://vyos.io)
---
 CLAUDE.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index c921033c..99320758 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -137,7 +137,7 @@ The first heading in every RST file uses `#` overline+underline. Field lists (e.
 - American English.
 - Indent with 2 spaces.
 - Blank lines around headings.
-- Inline code: use double backticks (`\`\`command\`\``).
+- Inline code: use double backticks per RST convention (the [Inline markup](https://docutils.sourceforge.io/docs/user/rst/quickref.html#inline-markup) section of the docutils quick reference).
 
 ### IP addresses (linter-enforced)
 
-- 
cgit v1.2.3