Repository navigation
[quality] check:links is configured and guarded by a test, but no workflow ever runs it #1137
Description
Finding
npm run check:links is maintained with unusual care and executed by nothing.
The repository ships .markdown-link-check.json and a dedicated guard test
for it, tests/markdown-link-check-config.test.mjs, whose own header explains
why the guard is needed:
It fails silently by construction. markdown-link-check does not pass the
parsed config object through to the checker: its CLI copies a fixed list of
keys ontooptsand drops everything else, with no warning and no non-zero
exit.
—tests/markdown-link-check-config.test.mjs:4-8
That guard derives the accepted key set from the installed CLI source so a
devDependency bump cannot quietly disable a setting. It is real, careful work
defending a command that CI never runs.
.github/workflows/ci.yml runs the other three check:* targets one at a time
and omits this one:
run: npm run check:format # ci.yml:101
run: npm run check:spelling # ci.yml:103
run: npm run check:markdown # ci.yml:105No workflow runs the aggregate npm run check or npm test either, so nothing
reaches check:links indirectly.
Evidence and provenance
main=7ab301e, checked 2026-10-06.grep -rn 'check:links\|npm test\|npm run check\b' .github/workflows/returns
onlyci.yml:101,ci.yml:103,ci.yml:105— the format, spelling and
markdown targets. Across all nine workflow files
(architecture-submission,ci,codeql,create-milestones,
deploy-gh-pages,import-architectures,pr-queue-hygiene,
refresh-community-people,refresh-radar-reports) there is no invocation
ofcheck:links,npm run check, ornpm test.- What is not affected, measured rather than assumed:
- Relative links in root
*.mdare covered offline by
tests/root-docs-links.test.mjs, which runs undernpm run test:unitin
CI. - Relative links between ADR files are covered by
tests/adr-contract.test.mjs:172. - Links inside
docs/**andblog/**are gated by the production build.
Verified empirically at7ab301e: appending
[Broken probe link](./no-such-page.md)todocs/community/index.mdmakes
npm run buildexit 1 withDocusaurus found broken links! ... Broken link on source page path = /community/.onBrokenMarkdownLinksiswarn
(docusaurus.config.js:125), but the unresolved link falls through to
onBrokenLinks: 'throw'(docusaurus.config.js:124), and
npm run build:productionruns in CI atci.yml:79. The probe edit was
reverted.
- Relative links in root
- The actual hole: external
http(s)links. Nothing in the repository
verifies them, in any file.tests/root-docs-links.test.mjsresolves paths
on disk and never issues a request; Docusaurus does not check outbound URLs.
check:linksis the only mechanism that would, and it never runs.
This is a missing-workflow finding, not a coverage gap — no source path is
under-tested — so it carries no coverage-gap priority.
Recommendation
Run it on a schedule rather than on pull_request. markdown-link-check issues
live network requests, so a required per-PR check would make unrelated PRs red
when a third-party host rate-limits or 503s; the config already carries
retryOn429, retryCount: 3 and a 20s timeout for exactly that reason, which
mitigates but does not eliminate it.
Add to .github/workflows/ci.yml, as a new top-level job (it is additive; no
existing job or line changes):
link-check:
name: Link check
# Scheduled only: markdown-link-check issues live network requests, so a
# third-party outage must not turn unrelated pull requests red. The config
# it reads is guarded by tests/markdown-link-check-config.test.mjs.
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- name: Install dependencies
run: npm ci
- name: Check markdown links
run: npm run check:linksand give the workflow a schedule trigger if it has none:
on:
schedule:
- cron: '0 6 * * 1'Pin the action refs and the node-version-file to whatever the repository's
other jobs already use — match ci.yml's existing steps verbatim rather than
the sketch above where they differ.
- a workflow job runs
npm run check:links, on a schedule rather than on
pull_request -
tests/root-docs-links.test.mjs:10no longer states thatcheck:links
"delegates to a Makefile that does not exist"
Deliberately out of scope
Widening _check:links-md (package.json:10) beyond root *.md is not
part of this issue and should not be bundled into it. Measured at 7ab301e:
$ npx markdown-link-check --config .markdown-link-check.json -p -v docs/community/index.md
21 links checked.
ERROR: 3 dead links found!
[✖] /community/user-groups → Status: 400
[✖] /community/technical-community-groups → Status: 400
[✖] /architectures/ → Status: 400
Every one of those is a live site route that Docusaurus already resolves and
already gates; markdown-link-check reports them dead because it resolves a
leading / against the filesystem. Covering docs/** would therefore need
replacementPatterns mapping site-absolute routes onto the build output —
real design work, and redundant with the onBrokenLinks: 'throw' gate proved
above. Root *.md files are outside the route tree and carry no such links,
which is why they are the right and sufficient scope here.
Why only the second box has a PR
The first box edits .github/workflows/**, which is unreachable from this
agent's token tier: GitHub rejects a push whose diff touches that directory
without the workflows permission, which the contributor tier does not carry.
It needs a human or an agent with workflow-write access to land. That is a
token ceiling, not a judgement that the change is unready — the YAML above is
complete.
The second box is a correction this agent can push, and the PR below lands it.
Priority
- Impact: medium (every external link in the repository's contributor-facing
documentation is unverified; a maintained config and a bespoke guard test
defend a command nothing executes) - Effort: low
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5n31 | SHA: 7ab301e
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Activity
hivecommons-hive commentedon Oct 7, 2026
task-list sweep: 0 of 2 items ticked. Not closing yet — outstanding boxes remain.
Outstanding items:
npm run check:links, on a schedule rather than ontests/root-docs-links.test.mjs:10no longer states thatcheck:linksMerged PRs referencing this issue so far:
This comment is edited in place by the task-list sweep on every cycle; it is not duplicated.
hivecommons-hive commentedon Oct 7, 2026
Verification at
03cfcfe(2026-10-07)Re-checked both completion boxes after #1138 merged.
tests/root-docs-links.test.mjsno longer claims a missing Makefile. Done. The header now reads "npm run check:linksdoes work -- it shells out to markdown-link-check with .markdown-link-check.json, which tests/markdown-link-check-config.test.mjs guards -- but no workflow invokes it" (tests/root-docs-links.test.mjs:10-13). This box is closed by test: correct root-docs-links' account of what check:links does #1138.npm run check:links. Still not done.grep -rn 'check:links\|npm test\|npm run check' .github/workflows/at03cfcfereturns onlyci.yml:38(check:audit),ci.yml:103(check:format),ci.yml:105(check:spelling),ci.yml:107(check:markdown). Nothing reachescheck:links, and no workflow runs the aggregatenpm run checkornpm test.The issue therefore stays open on the first box only.
Revised exact text (supersedes the sketch in the original body)
The original recommendation added the job to
.github/workflows/ci.ymlplus aschedule:trigger. That is now the wrong shape:ci.ymlhas noscheduletrigger (on: pull_request / merge_group / workflow_dispatch,ci.yml:3-6), so adding one would also start runningvalidate,lint,e2eand every other job of that workflow on the cron — a behaviour change well outside this issue. A dedicated workflow is strictly additive and changes no existing line.Create
.github/workflows/link-check.ymlwith exactly:The action SHAs,
node-version: 22,cache: npm,persist-credentials: false,runs-on: ubuntu-24.04and thetimeout-minutesconvention are copied verbatim from the existinglintjob (.github/workflows/ci.yml:88-107), so this introduces no new pin and no new runner label.workflow_dispatchis included so a maintainer can prove it green on merge without waiting for Monday.Scope is unchanged:
_check:links-md(package.json:10) still covers root*.mdonly. Widening it todocs/**remains explicitly out of scope for the reasons measured in the original body.Why there is still no PR for this box
This change creates a file under
.github/workflows/. A push whose diff touches that directory is rejected server-side for a GitHub App token minted at the contributor tier — it does not carry the Workflows permission — so no PR this agent can open could contain the file. This needs a human maintainer, or an agent with workflow-write access, to land. That is a token ceiling, not an assessment that the change is unready: the YAML above is complete and can be committed as-is.🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:03cfcfe— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
hivecommons-hive commentedon Oct 7, 2026
Verified at
03cfcfe. The second box is done:tests/root-docs-links.test.mjs:10-16no longer sayscheck:linksdelegates to a Makefile that does not exist — it now states that the command works, shells out to markdown-link-check with.markdown-link-check.json, and that no workflow invokes it.The first box is still open and is not something this agent can land.
grep -rn 'check:links' .github/workflows/returns nothing at03cfcfe, so the job is still missing, and the fix edits.github/workflows/**— a path GitHub rejects for a push from acontributor-tier App token, which carries noworkflowspermission. The YAML in the issue body is complete; it needs a human or an agent with workflow-write access to apply. That is a token ceiling, not a judgement that the change is unready.Leaving the issue open for that reason.
🐝 Hive Agent:
quality| SHA:03cfcfe🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:03cfcfe— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
hivecommons-hive commentedon Oct 7, 2026
Verified at `main` = `03cfcfe`: the second box is landed, the first is not.
npm run check:linksdoes work -- it shells out to markdown-link-check with .markdown-link-check.json ... but no workflow invokes it". That correction merged in test: correct root-docs-links' account of what check:links does #1138.Keeping this open for box 1 only. No PR will be opened for it by this agent, and that is a token ceiling rather than a judgement that the change is unready: the fix edits `.github/workflows/ci.yml`, and GitHub rejects server-side any push from a contributor-tier App token whose diff touches `.github/workflows/**` ("refusing to allow a GitHub App to create or update workflow ... without
workflowspermission"). The replacement YAML in the issue body is complete and mechanical to apply — it needs a human or an agent with workflow-write access to land.🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:03cfcfe— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
hivecommons-hive commentedon Oct 8, 2026
Re-verified at
main=03cfcfe(2026-10-07). The second box landed via #1138 (merged,tests/root-docs-links.test.mjs). The first box is still open:No workflow invokes
check:links,npm run check, ornpm test, sopackage.json:25and its guard testtests/markdown-link-check-config.test.mjsstill defend a command CI never executes. Labellinghive/verified-openrather than closing.Exact replacement text
In
.github/workflows/ci.yml, after theCheck markdownstep (currently lines 106–107), append:so the block reads:
Nothing else changes:
check:linksis already defined (package.json:25→_check:links→_check:links-md),.markdown-link-check.jsonis already checked in, and the scope stays root*.mdexactly as the issue's "deliberately out of scope" section requires.Why no PR accompanies this
This change touches
.github/workflows/**. This agent runs at a token tier without theworkflowspermission, so GitHub rejects any push whose diff touches that directory — nothing this agent can push could contain the change. It needs a human, or an agent with workflow-write access, to land. That is a token ceiling, not a judgement that the change is unready: the YAML above is complete and can be applied mechanically.One caveat for whoever lands it:
npm run check:linksmakes live outbound HTTP requests, so it can fail on third-party rate limiting rather than on a repository defect. If that proves noisy, the usual remedies are.markdown-link-check.json'saliveStatusCodes/ignorePatternsor moving the step to a scheduled workflow — but it should be made to run somewhere before either is considered.🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5n31| SHA:03cfcfe— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
mrbobbytables commentedon Oct 8, 2026
Consolidated into #1188 together with #1179: both need workflow edits the Hive lane cannot push, so they should land as one human-authored CI PR.