Page-authoring rules

This page holds the rules a docs page is written to.

A page’s title: is its nav label and its H1 the label’s full form: the H1 names the label’s subject, as Install and Install and upgrade do. Living pages are governed prose under the anti-restatement doctrine (cite downward, never restate a SPEC’s invariant); dated docs/posts/ are immutable, temporal-exempt but still link/command-resolved (scripts/canon-config.knobs). A page off-nav by design joins scripts/docs-offnav.list — an embedded data fragment no link targets, or maintainer governance like this page reached only by citation.

Nav labels. Sibling labels are parallel in form: all nouns, or all a product’s names. A child’s label never repeats its parent’s, since the parent already frames it. An overview is the parent of its subpages, never their sibling. check-docs-nav-reachable holds the last rule (§Site chrome and the nav contract). The other two are review’s.

One statement, one home. A page states each fact, instruction and command once. It links each target once, at its first mention; a later mention is plain text or an in-page anchor. Across pages a fact has one home page, and every other page links it. A nav parent and its children divide a subject between them, so a child never restates its parent. The front door — README.md and docs/index.md — is the one summary tier. It may carry the product statement and the one-line install commands, and it links the page that explains them. check-docs-page-repeat holds the within-page half over the pages CANON_KIT_PAGE_REPEAT_PAGES names: a relative link target, or a sentence of eight words or more, stated twice on one page. The across-page half is review’s. A cross-page restatement is usually a paraphrase, which no scanner decides. The verbatim repeats across this site’s pages are sibling-page chrome, which the rule admits.

Page names. A page’s file name is its URL: the subject in lowercase hyphenated words, stable once published. Rename a page only when its name misleads, and move every inbound link with it. check-md-refs finds the in-tree links; an outside link to the old name breaks, and a misleading name is worth that price.

Section names. A heading names what its section answers, in the reader’s words — a noun phrase or a task, such as Choosing a profile — and uses the implementation’s name only where that name is what the reader types. It is sentence case, with no trailing punctuation and no link or § in it. A heading is an address as well as a label, since a citation links its anchor. So it is unique on its page, as check-spec-pointer’s one-title-per-file rule, which this repository turns on, holds over every page and README. A rename moves its inbound links in the same commit, which check-md-refs reds if missed.

Section citations are links. Cite a section with a link whose text is the citation and whose target carries the section’s anchor: [`installer/SPEC.md` §Profiles](/installer/SPEC.html#profiles). The target follows canon-kit/SPEC.md §The reference-link grammar. A kit’s SPEC or README is cited at its on-site mirror. Any other file off the site takes a self-repo blob link. A kit README cites any kit’s SPEC relatively, and the installer’s packer resolves each such link in the payload, which withholds every SPEC. A section of the same page takes a bare #anchor, with the heading alone as the link text. A later mention of a section the page already linked is plain text by the rule above; it begins with the linked text. check-citation-link holds the form over the pages CANON_KIT_CITATION_LINK_PAGES names, and check-md-refs resolves each target and anchor.

Collapsible regions. Collapse depth a reader may skip, such as the step-by-step form under a one-line command or a long reference table. Never collapse the primary path or a step every reader runs, and never hide a section a link targets. A region opens <details markdown="1"> followed by a <summary> naming what opens. It holds no heading. Without markdown="1" the Pages parser prints the region’s markdown raw. check-docs-collapsible holds that form on every page under the docs root. What collapses is review’s.

Code blocks. A reader copies a block whole, so a shell block is one paste: it runs to its end or stops at its first failure. It holds one command, or commands joined with &&. A sequence that cannot be joined is split into a block per step, and a later block may use what an earlier one set. An assignment that runs nothing may stand on its own line, since it cannot fail. A PowerShell block of more than one statement is wrapped as . { … }: Windows PowerShell 5.1 has no &&, and the dot-sourced block stops at a terminating error such as a throw while keeping the variables it sets. A block shown for reading rather than running — output, a transcript, a grammar sketch — takes text or console. Long lines wrap and the copy button takes the block as written. check-fence-paste-unit holds both forms over the pages CANON_KIT_FENCE_PASTE_PAGES names.

Page length. A page is short enough to read through. A page past CANON_KIT_PAGE_LENGTH_MAX_WORDS words is shortened, or split into a parent and sub-pages. The parent keeps the path every reader takes and links each sub-page once. A sub-page takes one subject a reader can skip: a reference table, a manual alternative, upkeep. A sub-page is a nav child of its parent where the parent is on the menu, and off-nav where the parent is. A generated page is left out, its length being its source’s. check-docs-page-length holds the bound over the pages CANON_KIT_PAGE_LENGTH_PAGES names. Which subject moves to which sub-page is review’s.

A page found breaking a rule is redone. The unit that finds it rebuilds the page to the rule, or files it with its cost; it is never annotated around.

Markdown constructs that render correctly on github.com but corrupt under the Pages parser are not listed here: site-kit/SPEC.md §check-docs-render-fidelity owns that roster (the consecutive-fence, promoted-heading, collapsed-table, and severed-inline-span classes) together with the gate that catches them and its honest limits, and site-kit/SPEC.md §check-docs-liquid-parse owns the Liquid-syntax class, where one token breaks the site’s whole build rather than one page. check-docs-collapsible holds the raw-region class beside them, a <details> body without markdown="1", which render-fidelity misses when the body is prose alone. Reach for them — not this page — when a well-formed construct renders wrong or reds either gate: the cause is routinely far upstream of the symptom in the same file, so the block the gate names is often the victim rather than the offender.

Which parser serves which file is what scopes that roster, and it is the first thing to establish before calling a rendering a defect. The Jekyll source is docs/, so the Pages parser (kramdown plus kramdown-parser-gfm) serves docs/ pages and nothing else. Jekyll runs Liquid over a docs/ page before kramdown renders it; the Liquid half is site-kit/SPEC.md §check-docs-liquid-parse’s. Every root manifest page (README.md, ROADMAP.md, CONTRIBUTING.md, RELEASING.md, CODE_OF_CONDUCT.md, SECURITY.md) sits outside that source and is not mirrored, since the mirror’s source set is kit SPEC/README/DOCTRINE, which the roster of generated projections holds. None of them has a site URL. A docs page reaching one links it through the self-repo blob grammar, into GitHub’s repository view, which is CommonMark-based (cmark-gfm) and markedly more forgiving; it joins a code span across a newline, which is the severed-span class’s whole premise. So a kramdown-only symptom found in a root page is not a defect, because no reader of that page is served by kramdown. Render a page through the parser that actually serves it before filing.