Skip to content

Add markdownlint and link-checking CI - #3

Merged
stxkxs merged 2 commits into
mainfrom
ci/markdown-and-link-checks
Aug 19, 2026
Merged

stxkxs merged 2 commits into
mainfrom
ci/markdown-and-link-checks

Conversation

@stxkxs

@stxkxs stxkxs commented Aug 19, 2026

Copy link
Copy Markdown
Member

Closes the testing gap from the 2026-08-18 sweep: the repo had no CI of any kind, while being a public surface with four external links and time-sensitive factual claims.

⚠️ Merge after #2

This PR will be red until #2 merges, and that is expected, not a fault. The markdownlint job fails on main's current profile/README.md — those failures are the a11y and line-length defects that #2 fixes:

profile/README.md:1   MD041/first-line-heading
profile/README.md:3   MD013/line-length  (155 > 100)
profile/README.md:49  MD013/line-length  (125 > 100)

Order: #2 → this → required_status_checks → #1.

What runs

Job Tool Catches
markdownlint markdownlint-cli2 structure/style across tracked markdown
link check lychee dead external + relative links

Triggers: pull_request, push to main, a weekly cron, and manual dispatch.

The cron matters as much as the PR trigger. Link rot happens with no commit attached to it — a check that only runs on change would never notice a dead upstream until someone happened to edit that file. The four links I verified by hand during the sweep are now verified continuously.

Supply-chain note

Actions are pinned to full commit SHAs, with the version in a trailing comment, resolved from the registry at authoring time rather than written from memory:

  • actions/checkout → 3d3c42e5… (v7.0.1)
  • DavidAnson/markdownlint-cli2-action → 21c1be1b… (v24.2.0)
  • lycheeverse/lychee-action → e7477775… (v2.9.0)

A mutable tag on a third-party action is a supply-chain hole. permissions: contents: read — the workflow needs nothing more.

On the relaxed lint rules

Three rules are relaxed, each with its reason recorded inline in the config rather than left as a bare number:

  • MD013 at 100 cols, off for tables/code.
  • MD033 allows only the elements the profile actually needs (div, img, sub, b, a, br, picture, source) — raw HTML is how GitHub centres a brand lockup and serves a theme-aware image.
  • MD024 siblings_only, so separate files may repeat a heading.

LICENSE is excluded from the glob — legal text, not prose to lint.

I did not relax MD041 here. The one file that can't satisfy it carries a scoped inline exception in #2 with its reason; the rule stays enforced for every other file.

stxkxs and others added 2 commits August 18, 2026 23:41
The repository had no CI of any kind. It is a public marketing surface
carrying four external links and time-sensitive factual claims, with
nothing verifying either — the gap behind the testing grade in the
2026-08-18 sweep.

──────────────────────── The workflow ────────────────────────

`.github/workflows/checks.yml` runs two independent jobs:

- **markdownlint** (markdownlint-cli2) — structure and style across every
  tracked markdown file.
- **link check** (lychee) — every external and relative link actually
  resolves. Accepts 200/206/301/302/403, retries three times, and times
  out at 20s so a slow upstream is not read as a broken link.

Triggers on pull_request and on push to main, plus a weekly cron and
manual dispatch. The schedule is the point of the job as much as the PR
trigger: external link rot happens with no commit attached to it, so a
check that only runs on change would never catch a dead upstream until
someone happened to edit the file.

`permissions: contents: read` — the workflow needs nothing else.
Concurrency cancels superseded runs per ref.

Actions are pinned to full commit SHAs with the version in a trailing
comment, resolved from the registry at authoring time rather than written
from memory: actions/checkout v7.0.1, markdownlint-cli2-action v24.2.0,
lychee-action v2.9.0. A mutable tag on a third-party action is a supply
chain hole; a SHA is not.

────────────────────── The lint config ──────────────────────

`.markdownlint-cli2.yaml` relaxes three rules deliberately, each with the
reason recorded inline:

- MD013 at 100 columns, off for tables and code blocks. Prose is wrapped
  by hand; tables and links cannot always honour a limit.
- MD033 allows only the block elements the org profile actually needs —
  div, img, sub, b, a, br, picture, source. Raw HTML is how a GitHub
  profile centres a brand lockup and how it serves a theme-aware image.
- MD024 siblings_only, so separate files may repeat a heading.

LICENSE is excluded from the glob; it is legal text, not prose to lint.

Co-authored-by: stxkxsbot <275011021+stxkxsbot@users.noreply.github.com>
profile/README.md carries a scoped MD041 disable because the brand lockup
has to precede the heading. The comment there claims the property MD041
protects is still satisfied — a top-level heading naming the document —
and that claim is true today.

But a suppression outlives the reason it was granted. Delete that h1 and
MD041 is off on the file, so nothing fails and the accessibility defect
returns silently. The disable would then be asserting something false.

Adds a two-line assertion to the markdown job: profile/README.md must
contain exactly one top-level heading. This pins the property rather than
trusting the comment, and converts the suppression from a promise into a
checked invariant.

Verified: the fixed README returns 1 and passes; main's current README
returns 0 and fails, which is the correct behaviour until the profile fix
lands ahead of this workflow.

Co-authored-by: stxkxsbot <275011021+stxkxsbot@users.noreply.github.com>
@stxkxs
stxkxs force-pushed the ci/markdown-and-link-checks branch from 533c90c to 19f5fc4 Compare August 19, 2026 06:41
@stxkxs
stxkxs merged commit 7aab9e0 into main Aug 19, 2026
2 checks passed
@stxkxs
stxkxs deleted the ci/markdown-and-link-checks branch August 19, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant