Alt text on all images, and a markdownlint pass over the repo - #3358
Conversation
|
Pull request environment is available at https://stoctodocspr3358.z22.web.core.windows.net. You can view the ephemeral environment status in Octopus Deploy. This environment will be automatically deprovisioned when the pull request is closed, or after 7 days of inactivity. |
5183fdb to
af0a291
Compare
25a0dd3 to
44d9459
Compare
Clears 647 of the 830 markdownlint errors across the docs pages: whitespace, emphasis style, list and heading spacing, table alignment, heading levels, and two broken link fragments. Also fixes a "therefor" typo flagged by cspell. Remaining: 131 images without alt text, plus duplicate headings, fenced blocks without a language, and emphasis used as headings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds alt text to 131 images, labels 15 code fences, promotes emphasis used as
headings to real headings, and corrects heading levels. Configures MD024 to
siblings_only, since API reference pages repeat "Parameters" and "Properties"
under each endpoint, and scopes MD051 past the legacy {#PascalCase} anchors it
cannot parse.
The numbered list in the variables getting-started guide is now one continuous
sequence; it previously restarted at 1 twice where figures interrupted it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Clears all 4,432 markdownlint errors reported across the 2,666 markdown
pages, and all 18 remaining cspell issues.
Alt text added to 364 images. 397 fenced code blocks given a language.
113 bold labels used as headings promoted to real headings, or inlined
with their value where they were repeated field labels. Table pipes
aligned, heading levels corrected, whitespace and list formatting fixed.
Real bugs fixed along the way:
- The Azure accounts page had two sections titled "Create a Service
Principal Client Secret with PowerShell" sharing one anchor. The second
documents federated credentials, and a link elsewhere on the page
pointed at the anchor it should have had.
- kubernetes-resources.md carried a duplicated "Command" section, and the
probe sub-types sat at the same heading level as the probes themselves.
- Several link fragments pointed at anchors that do not exist.
- Six bare "here" links replaced with descriptive text.
MD024 is set to siblings_only and MD051 ignores fragments containing an
uppercase letter, since the site uses legacy {#PascalCase} anchors that
markdownlint cannot parse.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous pass only covered src/pages, so the include files under src/shared-content were never linted even though they render into those pages. This clears the remaining 8,901 errors across 234 files. Alt text added to 45 images in shared-content includes, which is why several built pages still had empty alt attributes. 109 bold labels used as headings promoted to real headings, 22 code fences given a language, tables aligned, and two dead link fragments removed. MD010 is set to code_blocks: false. Nearly all 8,090 hard tabs are inside published script samples under src/shared-content/scripts, and retabbing code that users copy is worse than allowing tabs there. Tabs outside code blocks are still caught. Also fixes an "eksctrl" link whose text contradicted its own URL, and "Springboot" where the product is Spring Boot. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two style rules were rewriting authored structure rather than fixing faults, so both are now off and the damage is reverted. MD007 re-indented nested list items to its own 2-space default, flattening 589 lines across 57 files that were deliberately indented 4 spaces. The repo is mixed, so enforcing either width churns files for no benefit. MD005 is off for the same reason. MD029 was the worse one. Where a figure or code block sat at column 0 inside a numbered list, the parser ended the list, and MD029 "fixed" the follow-on items by restarting them at 1. Rendered pages lost their step numbering. Authored numbering is restored on 252 items, and the 536 lines of interrupting content are now indented into the item they belong to, so each list stays intact. Numbering that runs across headings renders as <ol start="N">, which MD029 cannot express, so the rule is off. Tab-indented list items needed care: markdownlint rewrites a leading tab as a single space, which is too shallow to nest. Those 19 lines are now 4 spaces, matching what the tab rendered as. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The linting section told authors to lint src/pages only, which is how the shared-content includes went unlinted and shipped images with no alt text. It now covers both trees. Adds the conventions this PR settled on: images need alt text, content inside a numbered list is indented into its step rather than left at column 0, and the two auto-fixes that break things here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Product screenshots example modelled an image with no alt text, which contradicts the alt text section above it and would fail the lint check for anyone who copied it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dictionary-octopus.txt has a code owner, so adding words to it makes this
formatting PR wait on a review it does not otherwise need. It is unchanged
here, and so is cspell.json.
None of the words cspell flagged were introduced by this PR. They are
pre-existing prose that only got checked because the spellcheck job runs
over every changed file, so touching a page for lint alone was enough to
surface them.
Rather than reword anything, each affected file carries a cspell:ignore
for just the word it uses: eksctl, mysqldump, authbind, Payara, SCCM,
octopussamples, licensekey, tasklog, Linkback, Doesnt, Dbup, mytentacle,
deregistering, gitversion and Springboot. The word is accepted where it
appears rather than repo-wide, the wording is left exactly as the authors
wrote it, and the pages stay linted.
Several of these are worth correcting properly (Dbup is DbUp, Springboot
is Spring Boot, mytentacle would read better as your-tentacle). That is
left for a follow-up once the dictionary owner can review it.
The MDX page uses a YAML comment in its frontmatter instead: MDX rejects
<!-- --> and markdownlint reads {/* */} as emphasis markers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eadings Three fixes from review. MD029 damage was still in the diff. The earlier restore skipped any item whose list was interrupted by a heading, but that is exactly the case the authors rely on: numbering that runs across headings renders as <ol start="N">. Every ordered-list marker now matches main, indent and number, verified by aligning each changed file against main. The same alignment turned up 49 bullet indents that MD007 had moved and that the first restore missed. MD036 was promoting bold labels to headings, and the level for each came from MD001 ratcheting rather than from meaning, so flat peers ended up nested one level deeper each until they ran out at h6. It also pushed 214 new headings into the on-page nav, and gave shared includes an h2 that popped the outline back up when the include sat under an h3. Promoting a label to a heading is an information architecture decision, not formatting, so the rule is now off and all 214 promotions are reverted, along with the blockquote and punctuation edits made to satisfy it. Also from review: four code blocks the classifier defaulted to text are really PowerShell or Bash, and MD051's ignored_pattern was unanchored, so it exempted a capital anywhere rather than only the legacy anchors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The block under "outputs the list of projects in parsable JSON format" was labelled text. The classifier fell back to text because the array carries a trailing comma after its last object, so a strict parse of it fails. The console table further down the same page stays as text: it is tabular command output, not a structured format. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
These two sat at two spaces on main. The earlier fix put back one, from reading a truncated terminal column rather than measuring the line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two trailing spaces are a <br>, and that is what the rule allows, but a line carrying three or more was trimmed to zero rather than back to two, so the break was deleted instead of normalised. Seven lines are restored to exactly two trailing spaces: the definition lines and hint in the deploy release step page, two paragraphs in the import certificate step page, and one in step conditions. Sixteen other lines in the same class keep the stripped whitespace. In those the break sat before a list item or a ::: block close, where it renders nothing. The README now lists this alongside the other two auto-fixes that leave a file passing the linter while changing the rendered page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
MD039 removed spaces from inside link text. Where the space sat against the neighbouring word, code span or link, that joined two words in the page: "CIDR format172.28.6.0/24", "projectsand", "likePuTTYgen", "Backup and restorepage". Five lines, and the space is now outside the brackets rather than deleted. MD034 wrapped a scheme-less URL in angle brackets. www.octopussamples.com is not a valid autolink, so the brackets rendered as literal characters. It is an explicit link now. The earlier MD036 revert matched on heading text, so in two files that held both a real heading and a bold label of the same words, it demoted the heading as well. Both are restored, and the sibling labels stay bold. Five fences carried a wrong language: two Terraform blocks marked xml and csharp in a file whose other blocks are correctly hcl, a CLI usage listing marked python, a javac command marked powershell, and CLI help output marked bash. Every non-text language this PR assigned has now been checked against the block it labels, not just the ones changed from text. The README gains the MD039 hazard, and its MD034 entry now says the rule is unsafe in .md too when the URL has no scheme. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
da457b7 to
a7aaebc
Compare
Two rows in api.md carry an unescaped pipe mid-cell, so they parse as three cells in a two column table. GFM drops the extra cell, which is why production never showed that text. The table formatter read the widest row instead and grew both tables to three columns, giving every other row a blank cell on the right. The pipe in the Deployments row is escaped so its sentence stays in the Description column, the empty third cell on the RecoveredFrom row is dropped, and both tables are back to two columns. The page renders 225 table cells again, matching production, down from 247. Also drops a stray semicolon from a bash fence in prompted-variables.md. It predates this PR but cost the block its highlighting when main changed markdown engine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
VerificationThis PR was reviewed in two passes: static analysis of the source diff, then a diff of the rendered HTML against production. The second pass is what mattered — it found regressions the first pass structurally could not see. Why the rendered diff was necessaryEvery regression found here left the file passing the linter. Method: fetch all 639 changed doc pages from the preview environment and from What it found — 12 regressions across 5 classes, all fixed
Plus: a scheme-less Final state — verified against
|
| Check | Result |
|---|---|
| Rendered ordered-list ordinals vs main | 3 files differ, all fixes |
| Table column counts | 0 changed |
| Hard line breaks | 0 lost, 0 spuriously added |
| Headings demoted to bold | 0 |
MD039 word joins |
0 |
| Anchor fragments | 0 newly broken |
Empty alt in src |
0 |
<url> autolinks in .mdx |
0 |
| Accidental indented code blocks | 0 |
| Fence languages | 0 suspects across 74 non-text assignments |
/docs/insights/api cells |
225, matching production table-for-table |
dictionary-octopus.txt and cspell.json are byte-identical to main and absent from the diff.
Note on the diff baseline
aee3fac10 (unified → satteri) is in this PR's base but not yet on production, so 777 of the 1,405 hunks are that engine change, not this PR. Confirmed by running the same comparison on a page this PR doesn't touch: production drops <, & and && inside code blocks (List<string> renders as Liststring>) and satteri fixes it.
Lesson worth keeping
A formatter that normalises presentation should not infer structure. MD009 trimmed to zero because its rule is about trailing spaces, not about <br>. MD039 removed a space inside brackets without asking what the space separated. The table formatter measured the widest row because that is how you align columns — not because that row was correct.
README.md now documents MD009, MD010, MD034 and MD039 as auto-fixes to check by hand, with the point that ties them together: none of them show up as a lint failure afterwards.
Known gap, out of scope
CI lints only changed files, so MD045 guards alt text on any page a PR touches, but nothing re-checks all 2,901 files as a set. The clean baseline holds for edited pages and can drift elsewhere. A scheduled full-repo lint would close that — separate PR.
Closes NES-235.
Why
NES-235 asks for alt text on our images, for accessibility and because it is a primary signal for search crawlers and AI search assistants. Every image in the docs now has it.
Adding alt text meant running markdownlint over the content, and once it was running, almost everything else it flagged was auto-fixable. So this does the whole repo in one go rather than leaving thousands of findings to surface a handful at a time in unrelated PRs. Worth doing once now, before the docs rewrites start, so the rewrites land on a clean baseline.
The safety net
NES-235 also asks for something to stop this regressing. That now exists without new tooling: CI already lints every
.mdand.mdxfile a PR touches, and the repo passes clean, soMD045fails any PR that adds an image without alt text.One caveat. WCAG says genuinely decorative images should have empty alt, but
MD045requires alt on every markdown image. There was one such case — an inline help icon — and rather than invent a description, the sentence was reworded so the icon is the thing being named. A future decorative image needs an HTML<img alt="">tag, whichMD045does not inspect.What changed
Alt text on 534 images. Measured directly: the files in this diff contained 535 empty-alt images on
mainand contain one now — the deliberate before/after example inREADME.md. 12 built pages were still renderingalt=""after the first pass, because the include files insrc/shared-contentlive outsidesrc/pagesand were never being linted. They are now.Everything else markdownlint reported, nearly all of it mechanical: whitespace, emphasis style, blank lines around headings and lists, hard tabs, trailing newlines, table pipe alignment. Beyond the auto-fixable set, every fenced code block now declares a language, and a handful of heading levels were corrected so they increment by one.
List and heading structure are unchanged. Every ordered-list marker in the diff matches
mainexactly, indent and number, and no bold label was promoted to a heading. See the config notes below for why.Wording is left alone apart from seven spelling corrections cspell flagged (
WhilsttoWhile,therefortotherefore,utilizedtoused, and similar), six bareherelinks given descriptive text, and the link and heading corrections listed under real bugs below.Roughly 355 code blocks are labelled
text, which gives them a visible "Text" chip in the block header. That is the existing convention —mainalready labels 275 blocks that way — but it is a visible change at this scale, so worth knowing before merge.README.mddocuments the conventions, including the two auto-fixes below that are unsafe here, and the fact thatsrc/shared-contentneeds linting too.Config decisions
Six rules needed scoping or disabling rather than fixing. Each is a judgement call worth reviewing.
Four were rewriting authored structure rather than fixing faults, and are off:
MD007andMD005(list indentation) → off.MD007re-indented nested list items to its own 2-space default, flattening lines that were deliberately indented 4 spaces. The repo is mixed, so enforcing either width just churns files. Every bullet indent in the diff is back to what the author wrote.MD029(ordered list prefix) → off. This one was breaking pages. Where a figure or code block sat at column 0 inside a numbered list, the parser ended the list, andMD029"fixed" the follow-on items by restarting them at 1 — so rendered pages lost their step numbering. Numbering that deliberately runs across headings renders as<ol start="N">, whichMD029cannot express. Every ordered-list marker is restored, verified by aligning each changed file againstmain.MD036(emphasis as heading) → off. It wanted 214 bold labels turned into headings. The level for each came fromMD001ratcheting rather than from meaning, so flat peers nested one level deeper each until they ran out at h6, and shared includes gained anh2that popped a page outline back up when the include sat under anh3. It also pushed 214 entries into the on-page nav. Promoting a label to a heading is an information architecture decision rather than formatting, so it is left for a deliberate pass.The other three are scoped rather than off:
MD010→code_blocks: false. 8,090 of the hard tabs are inside published script samples undersrc/shared-content/scripts. Retabbing code that users copy and run is worse than allowing tabs there. Tabs outside code blocks are still caught.MD024→siblings_only. API reference pages legitimately repeatParameters,Sample responseandPropertiesunder each endpoint. These are not siblings, and the rule still catches genuine duplicates within a section.MD051→ignored_pattern: "^[a-z0-9-]*[A-Z]". The rule only recognises lowercase-kebab custom anchors, so it flags the 188 legacy{#PascalCase}anchors this site uses. Those anchors render and resolve correctly — verified against the rendered HTML. The pattern is anchored so it exempts that shape rather than any fragment with a capital in it.The spelling dictionary is deliberately untouched
dictionary-octopus.txthas a code owner, and adding words to it would make this formatting PR wait on a review it does not otherwise need. It is unchanged here, and so iscspell.json.None of the words cspell flagged were introduced by this PR — they are pre-existing prose that only got checked because spellcheck runs on changed files and this PR changes a lot of them. Touching a page for lint alone is enough to pull it into that job, so these failed regardless of whether anything was respelled.
No wording is changed. Each affected file carries a scoped exception next to the usage instead:
<!-- cspell:ignore SCCM -->Fifteen words are handled this way:
eksctl,mysqldump,authbind,Payara,SCCM,octopussamples,licensekey,tasklog,Linkback,Doesnt,Dbup,mytentacle,deregistering,gitversionandSpringboot. The word is accepted only where it appears rather than repo-wide, and bothdictionary-octopus.txtandcspell.jsonare untouched.Several are genuinely worth correcting —
Dbupis DbUp,Springbootis Spring Boot,mytentaclewould read better asyour-tentacle— but that is prose, not formatting, and it belongs in a follow-up the dictionary owner can review. This PR deliberately leaves the wording exactly as the authors wrote it.One MDX page uses a YAML comment in its frontmatter instead of the HTML form, because MDX rejects
<!-- -->and markdownlint reads{/* */}as emphasis markers.Every other cspell finding was a genuine typo and was corrected.
Real bugs this surfaced
/docs/octopus-rest-apilinked to#api-clients, a section that does not exist on that page. Now points at the Octopus.Client page.kubernetes-resources.mdcarried a byte-identical duplicatedCommandsection, and the probe sub-types sat at the same heading level as the probes themselves.eksctrllink whose text contradicted its own URL,Springbootwhere the product is Spring Boot, and two dead link fragments in a shared include.herelinks replaced with descriptive text.Two auto-fixes to avoid on this repo
Both are now documented in
README.md:MD034on.mdx. It rewrites bare URLs and email addresses as<url>autolinks. Valid CommonMark, but MDX parses the angle brackets as JSX and fails to compile. It broke the build twice here before being caught. Both cases are now explicit markdown links, and every.mdxhas been swept for others.MD010on tab-indented lists. It rewrites a leading tab as a single space, which is too shallow to nest the item, so the bullet silently escapes its parent. The 19 affected lines are now four spaces, matching what the tab rendered as.Verification
Run locally against the whole repo, not just the changed files:
markdownlint-cli2over all 2,900.mdand.mdxfiles in the repo: 0 errorspnpm spellcheck: 0 issues, with no dictionary changesrc, and 0 built pages renderingalt=""Rebased onto
mainafter 20 commits landed, including the heading-icon removal (#3354). Four content pages conflicted; those tookmain's version and the lint pass was re-run over them, so nothing from the newer commits is left behind. The nine markdown files those commits touched were checked individually — all images have alt text, no unlabelled code fences, no<url>autolinks in.mdx, and no tab-indented list items.The indented figures were checked separately, since indenting a block can turn it into a code block: no
:::figureor:::divtext leaks into any built page, no image path ends up inside a<pre>, and rendered<figure>counts match the source image counts per page.main.cssdocuments figures-inside-list-items as the intended layout.