Skip to content

[quality] check:links is configured and guarded by a test, but no workflow ever runs it #1137

Closed as not planned

Description

@hivecommons-hive

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 onto opts and 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:105

No 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
    only ci.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
    of check:links, npm run check, or npm test.
  • What is not affected, measured rather than assumed:
    • Relative links in root *.md are covered offline by
      tests/root-docs-links.test.mjs, which runs under npm run test:unit in
      CI.
    • Relative links between ADR files are covered by
      tests/adr-contract.test.mjs:172.
    • Links inside docs/** and blog/** are gated by the production build.
      Verified empirically at 7ab301e: appending
      [Broken probe link](./no-such-page.md) to docs/community/index.md makes
      npm run build exit 1 with Docusaurus found broken links! ... Broken link on source page path = /community/. onBrokenMarkdownLinks is warn
      (docusaurus.config.js:125), but the unresolved link falls through to
      onBrokenLinks: 'throw' (docusaurus.config.js:124), and
      npm run build:production runs in CI at ci.yml:79. The probe edit was
      reverted.
  • The actual hole: external http(s) links. Nothing in the repository
    verifies them, in any file. tests/root-docs-links.test.mjs resolves paths
    on disk and never issues a request; Docusaurus does not check outbound URLs.
    check:links is 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:links

and 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:10 no longer states that check: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

  1. added
    qualityApproved by a Hive merger/owner for auto-merge on green CI
    testingApproved by a Hive merger/owner for auto-merge on green CI
    agent/qualityApproved by a Hive merger/owner for auto-merge on green CI
    hive/covered-by-prHive verified that an open PR references or claims this issue; still actionable until confirmed
    hive/likely-doneHive verified that a merged PR references or claims this issue; pending confirmation
    and removed
    hive/covered-by-prHive verified that an open PR references or claims this issue; still actionable until confirmed
    on Oct 7, 2026
  2. hivecommons-hive commented on Oct 7, 2026

    @hivecommons-hive
    ContributorAuthor

    task-list sweep: 0 of 2 items ticked. Not closing yet — outstanding boxes remain.

    Outstanding items:

    • 🔲 a workflow job runs npm run check:links, on a schedule rather than on
    • 🔲 tests/root-docs-links.test.mjs:10 no longer states that check:links

    Merged PRs referencing this issue so far:

    This comment is edited in place by the task-list sweep on every cycle; it is not duplicated.

  3. hivecommons-hive commented on Oct 7, 2026

    @hivecommons-hive
    ContributorAuthor

    Verification at 03cfcfe (2026-10-07)

    Re-checked both completion boxes after #1138 merged.

    • tests/root-docs-links.test.mjs no longer claims a missing Makefile. Done. The header now reads "npm run check:links does 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.
    • A workflow job runs npm run check:links. Still not done. grep -rn 'check:links\|npm test\|npm run check' .github/workflows/ at 03cfcfe returns only ci.yml:38 (check:audit), ci.yml:103 (check:format), ci.yml:105 (check:spelling), ci.yml:107 (check:markdown). Nothing reaches check:links, and no workflow runs the aggregate npm run check or npm 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.yml plus a schedule: trigger. That is now the wrong shape: ci.yml has no schedule trigger (on: pull_request / merge_group / workflow_dispatch, ci.yml:3-6), so adding one would also start running validate, lint, e2e and 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.yml with exactly:

    name: Link check
    
    # Scheduled and manual only: markdown-link-check issues live network requests,
    # so a third-party outage or rate limit must not turn unrelated pull requests
    # red. The config it reads (.markdown-link-check.json) is guarded by
    # tests/markdown-link-check-config.test.mjs, which runs under npm run test:unit
    # in the Validate repository workflow.
    on:
      schedule:
        - cron: '0 6 * * 1'
      workflow_dispatch:
    
    permissions:
      contents: read
    
    jobs:
      link-check:
        name: Link check
        runs-on: ubuntu-24.04
        timeout-minutes: 10
        steps:
          - name: Check out repository
            uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
            with:
              persist-credentials: false
          - name: Set up Node.js
            uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
            with:
              node-version: 22
              cache: npm
          - name: Install dependencies
            run: npm ci
          - name: Check markdown links
            run: npm run check:links

    The action SHAs, node-version: 22, cache: npm, persist-credentials: false, runs-on: ubuntu-24.04 and the timeout-minutes convention are copied verbatim from the existing lint job (.github/workflows/ci.yml:88-107), so this introduces no new pin and no new runner label. workflow_dispatch is 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 *.md only. Widening it to docs/** 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

  4. hivecommons-hive commented on Oct 7, 2026

    @hivecommons-hive
    ContributorAuthor

    Verified at 03cfcfe. The second box is done: tests/root-docs-links.test.mjs:10-16 no longer says check:links delegates 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 at 03cfcfe, so the job is still missing, and the fix edits .github/workflows/** — a path GitHub rejects for a push from a contributor-tier App token, which carries no workflows permission. 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

  5. hivecommons-hive commented on Oct 7, 2026

    @hivecommons-hive
    ContributorAuthor

    Verified at `main` = `03cfcfe`: the second box is landed, the first is not.

    • Box 2 — done. `tests/root-docs-links.test.mjs:11-13` no longer claims `check:links` "delegates to a Makefile that does not exist"; it now reads "npm run check:links does 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.
    • Box 1 — still open. `grep -rn 'check:links|npm run check\b|npm test' .github/workflows/` at `03cfcfe` returns only `ci.yml:38` (`check:audit`), `ci.yml:103` (`check:format`), `ci.yml:105` (`check:spelling`) and `ci.yml:107` (`check:markdown`). No workflow runs `check:links`, `npm run check`, or `npm test`, so external links remain unverified by anything.

    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 workflows permission"). 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

  6. hivecommons-hive commented on Oct 8, 2026

    @hivecommons-hive
    ContributorAuthor

    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:

    $ grep -rn 'check:links\|npm run check\b\|npm test' .github/workflows/
    .github/workflows/ci.yml:38:        run: npm run check:audit
    .github/workflows/ci.yml:103:        run: npm run check:format
    .github/workflows/ci.yml:105:        run: npm run check:spelling
    .github/workflows/ci.yml:107:        run: npm run check:markdown
    

    No workflow invokes check:links, npm run check, or npm test, so package.json:25 and its guard test tests/markdown-link-check-config.test.mjs still defend a command CI never executes. Labelling hive/verified-open rather than closing.

    Exact replacement text

    In .github/workflows/ci.yml, after the Check markdown step (currently lines 106–107), append:

          - name: Check links
            run: npm run check:links

    so the block reads:

          - name: Check formatting
            run: npm run check:format
          - name: Check spelling
            run: npm run check:spelling
          - name: Check markdown
            run: npm run check:markdown
          - name: Check links
            run: npm run check:links

    Nothing else changes: check:links is already defined (package.json:25 → _check:links → _check:links-md), .markdown-link-check.json is already checked in, and the scope stays root *.md exactly 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 the workflows permission, 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:links makes 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's aliveStatusCodes/ignorePatterns or 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

  7. removed
    hive/likely-doneHive verified that a merged PR references or claims this issue; pending confirmation
    on Oct 8, 2026
  8. mrbobbytables commented on Oct 8, 2026

    @mrbobbytables
    Member

    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.

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

    agent/qualityApproved by a Hive merger/owner for auto-merge on green CIhive/hosted-available-lke648397-260827-5n31Approved by a Hive merger/owner for auto-merge on green CIhive/verified-openqualityApproved by a Hive merger/owner for auto-merge on green CItestingApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions