Skip to content

docs-s1: tier restructure + Starlight scaffold + adapter + CI validation #345

Description

@JArmandoAnaya

Part of the Documentation Site workstream (see the epic) · decided in ADR-0009 · specified in the roadmap. First sprint; nothing else in the workstream can start until this lands.

Two independent things, done together because each is worthless without the other: split the authored user guide into a reference tier (dual-source: .qhp + site) and a guides tier (site only), and stand up the Astro Starlight project that consumes it.

As-built facts you must know before touching anything

Verified 2026-07-21. Read them before planning — several are counter-intuitive.

  1. The .qhp generator's file list is NOT a glob — docs/user-guide/index.md IS the manifest. helpc::build_toc() (editor/help_compiler/helpc/toc.cpp:102-137) reads index.md, extracts its markdown links in document order, and admits a link only if it is non-external, not an anchor, does not start with ../ or ./, ends in .md, exists on disk, and is not a duplicate. A page committed under docs/user-guide/ but not linked from index.md is invisible to the entire pipeline. TOC order is exactly index.md link order. Page titles come from each page's first # H1 (toc.cpp:46-56).
  2. A page's slug is its guide-relative path minus .md (toc.cpp:131), so moving create-road.md to reference/create-road.md changes the slug to reference/create-road, which changes its .qhp keyword id (helpc/qhp.cpp:76-82) and its runtime URL qthelp://ai.robomous.roadmaker/doc/<slug>.html (editor/src/help/help_viewer.cpp:52-55) in lockstep.
  3. The F1 map is a static side table, not a helpId() on tools: kToolPages (editor/src/help/help_registry.cpp:13-34, ToolId → slug) and kDockPages (:39-45, dock objectName → slug), resolved focused-dock → active-tool → index (:78-92).
  4. The coverage gate reads the repo from disk. EveryPageResolvesToACommittedGuidePage (editor/tests/test_help_registry.cpp:76-100) asserts, for every tool and dock slug, that RM_DOCS_DIR/user-guide/<slug>.md exists and that index.md contains the literal substring (<slug>.md). RM_DOCS_DIR is defined at editor/tests/CMakeLists.txt:115. Sibling gates in editor/tests/test_help_collection.cpp: EveryTocPageIsServed (:72-87), EveryReferencedImageIsServed (:93-129), EveryTocPageHasAKeyword (:131-145).
  5. shortcuts.md's path is hardcoded at editor/tests/test_shortcut_registry.cpp:129-130 (RM_DOCS_DIR / "user-guide" / "shortcuts.md"); the test slurps the file and asserts it contains the generated block verbatim. Moving that page requires editing that line in the same commit.
  6. Images: main.cpp:85-98 copies <guide>/img/* and <guide>/tutorials/img/* non-recursively; ../-prefixed images are copied on demand and rewritten to img/<basename> (helpc/render.cpp:55-62, 90-97). The .qhp <files> patterns are duplicated per subdirectory because qhelpgenerator wildcards do not recurse (qhp.cpp:87-97), and cover only png/gif/jpg. Any new tier folder needs its own <files> patterns and its own image-copy step — this is exactly the class of bug Help pipeline: register tutorials/img/* in the .qhp <files> so tutorial images render in-app #292 was.
  7. Renderer is md4c with MD_DIALECT_GITHUB (helpc/render.cpp:17-26): tables, strikethrough, task lists, autolinks, raw HTML pass through; no footnotes, no front-matter (renders as literal text), no admonition syntax, no code-fence transforms, and no heading id= anchors — so page.md#section links resolve to the page but land at its top.
  8. ../*.md links rewrite to https://github.com/Robomous/RoadMaker/blob/main/<path> (render.cpp:43-73); every reference page's trailing "## Reference" section relies on this.
  9. Theme tokens are C++ only. theme::graphite_amber() (editor/src/theme/theme.cpp:225-250) holds 19 named fields; help_style.cpp:9-66 substitutes 7 of them into a template, and the result is committed pre-substituted with flattened hex at editor/resources/help/help.css, gated byte-for-byte by editor/tests/test_help_style.cpp:17-31. QTextBrowser does not support CSS custom properties, so the in-app sheet must stay flattened.

Deliverables

1. Tier restructure of docs/user-guide/

Use git mv (preserve history). Move whole pages only — no content rewriting, no page splitting in this sprint. The proposed classification is in the workstream PR description; it is planning input, so re-verify before moving.

  • reference/ — the per-tool and per-panel pages.
  • tutorials/ — the four existing tutorials, kept where they are.
  • guides/ — created for the guides tier (may start empty or take whatever the classification puts there).
  • index.md stays at the guide root: it is the ordering manifest (fact 1) and the F1 fallback slug.

In the same commit, update every consumer: kToolPages/kDockPages slugs, index.md link targets (which the gate greps literally), the hardcoded shortcuts.md path, intra-guide relative links between moved pages, and image references. docs/README.md's user-guide links (lines ~24-29) point at several of these pages and must not rot.

2. Narrow the .qhp generator to the reference tier

The generator ingests reference/ (plus index.md) and ignores guides/ and tutorials/. Removing tutorials from the .qhp path is the point of the sprint — it is what lets guides use richer syntax later. Adjust the synthetic "Tutorials" TOC section (qhp.cpp:53-72) and the tutorial image handling accordingly, and delete what becomes dead rather than leaving it inert.

Keep the coverage philosophy intact: the tool/panel gate now means "every registered tool and panel resolves to an existing reference page".

3. docs-site/ scaffold

New top-level folder. Pinned Astro Starlight, .nvmrc + engines pinning the Node LTS major, committed package-lock.json, npm ci in CI. Every npm dependency MIT/BSD/Apache-2.0-compatible per docs/standards/dependencies.md; record them per that policy. A README.md stating plainly that adapted content is generated and must never be hand-edited.

Theme: reuse the graphite + amber palette via a single export path from theme::graphite_amber() to site CSS custom properties (a generator alongside help_style::css(), or a script parsing the initializer — your call, justify it). Do not transcribe hex values into a second place by hand, and do not change the committed help.css bytes.

4. Adapter script (Node)

Copies docs/user-guide/** into Starlight's content directory and:

  • synthesizes required title frontmatter from each page's first H1;
  • rewrites relative links and image references for Starlight routing;
  • derives reference-tier sidebar order from index.md (the same manifest the .qhp uses, so the two outputs cannot drift), and guides order from folder structure plus an optional _order manifest that the .qhp generator ignores;
  • fails loudly on a broken link — non-zero exit, naming the source page and target.

Output is gitignored.

5. CI

A new Linux-only workflow (Node), triggered on changes to docs/user-guide/** and docs-site/**: npm ci → adapter → astro build → link check. Extend the coverage philosophy with a check that every F1-required page also exists in the adapted Starlight set, so a page can never be reachable in-app but missing from the site.

The C++ jobs must not change at all. No CMake target invokes npm; .github/workflows/ci.yml C++ jobs stay byte-identical in behavior. Note that ci.yml:461-477 already runs a docs link check globbing docs/**/*.md — make sure the restructure keeps it green.

Acceptance

  • Both pipelines build green from the restructured source, with no dual-syntax hacks — no page contorted to satisfy both renderers.
  • The full editor test suite is green, specifically test_help_registry, test_help_collection, test_help_style, test_shortcut_registry.
  • F1 resolves correctly for every registered tool and panel after the move (verify in a running editor, not only via the gate).
  • Every F1-required page is present in the adapted Starlight set (enforced in CI).
  • astro build green on Linux CI; adapter fails the build on a deliberately broken link (prove it with a test or a documented manual check).
  • No C++/CMake job requires Node.

Out of scope — do not touch

  • The local reader build, packaging, ROADMAKER_BUNDLE_MANUAL, the Help-menu action, bridge links (docs-s2).
  • Any publishing workflow, Amplify, versioning (docs-s3).
  • The authoring guide, dependency-update cadence, IP sweep (docs-s4).
  • Help pipeline: tutorial pages' parent-relative links rewrite to wrong guide-root URLs #297 — do not fix it here, and do not close it. Removing tutorials from the .qhp path reduces its blast radius but the link rewriter is still wrong for reference-to-reference links.
  • Splitting objects-signals.md or normalizing <kbd> usage — whole-page moves only.
  • The ToolId-enum coverage hole noted in the epic (StopLine/JunctionSpan/JunctionSurface past the <= Corner loop bound). Tempting because you are in that file; it is a separate defect with its own fix.
  • MDX, JS components, or any Starlight feature beyond plain Markdown + asides.

Epic: #344

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    docs-siteDocumentation site workstream — tiered docs, Starlight site, versioned publishing (ADR-0009)pillar:P2Road to Parity pillar P2

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions