CI Issue
Three hygiene gaps in the docs CI workflows. Nothing is currently red — all five workflows are green over the last 30 runs — but each gap is a latent breakage or waste:
1. Node version drift (latent breakage)
build.yml and vitest.yml pin Node 22 with explicit comments that the toolchain requires it ("ESLint 10 uses Node util.styleText, and jsdom 30 requires Node 22+"; "jsdom 30 depends on undici 8, which requires Node >= 22.19"). Yet:
typecheck.yml runs Node 20 and executes npm run lint (the very ESLint 10 the build.yml comment warns about)
check-internal-links.yml runs Node 20
meeting-recordings-refresh.yml hardcodes 20.11.1
package.json has no engines field
This currently passes only because "20" resolves to a late 20.x that happens to have util.styleText. The next dependency bump that actually needs 22 will break only the Node-20 workflows, in a confusing split-brain way.
2. Duplicated lint + test in build.yml (~2–3 min wasted per PR)
build.yml has no path filters and runs npm run lint and npm test on every PR/push — duplicating typecheck.yml (ESLint) and vitest.yml (tests) whenever their paths match, which is nearly every code PR. The duplication is understandable as a catch-all, but the lint/test steps can be dropped from build.yml now that typecheck and vitest carry the gate (vitest additionally enforces coverage thresholds, which npm test in build.yml does not).
3. Missing concurrency cancellation
Only build.yml has a concurrency block. vitest.yml, typecheck.yml, check-internal-links.yml, and markdownlint-cli2.yml let superseded runs on force-pushed PRs run to completion.
Evidence
Recommendation (exact replacement text)
typecheck.yml — change:
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
cache: "npm"
to:
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
# ESLint 10 uses Node util.styleText; align with build.yml/vitest.yml.
node-version: "22"
cache: "npm"
and add below the permissions: line:
concurrency:
group: typecheck-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
check-internal-links.yml — same node-version: "22" change, and add:
concurrency:
group: links-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
vitest.yml — add:
concurrency:
group: vitest-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
markdownlint-cli2.yml — add:
concurrency:
group: mdlint-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
meeting-recordings-refresh.yml — change node-version: '20.11.1' to node-version: '22' (scheduled job; nothing pins it to 20.11.1).
build.yml — delete the two steps:
- name: Lint site
run: npm run lint
- name: Test site
run: npm test
(typecheck.yml and vitest.yml own those gates; vitest.yml additionally enforces coverage thresholds.)
package.json (non-workflow, can be PRed separately if preferred) — add:
"engines": {
"node": ">=22.19"
},
Why no PR
Every change above except the package.json engines field lives under .github/workflows/**. This agent runs at the contributor tier without the workflows permission, so a push containing these files is rejected server-side. Landing this needs a human or an ISSUES_PRS_MERGE-tier agent — applying the replacement text above is mechanical. The engines change alone is not worth a standalone PR; it should ride along when a human applies the workflow edits.
Filed by ci-maintainer agent (ACMM L4/L5 — hold-gated mode)
🐝 Hive Agent: ci-maintainer | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown
— hive: agent=ci-maintainer backend=copilot model=claude-fable-5 copilot=1.0.88
CI Issue
Three hygiene gaps in the docs CI workflows. Nothing is currently red — all five workflows are green over the last 30 runs — but each gap is a latent breakage or waste:
1. Node version drift (latent breakage)
build.ymlandvitest.ymlpin Node 22 with explicit comments that the toolchain requires it ("ESLint 10 uses Node util.styleText, and jsdom 30 requires Node 22+"; "jsdom 30 depends on undici 8, which requires Node >= 22.19"). Yet:typecheck.ymlruns Node 20 and executesnpm run lint(the very ESLint 10 the build.yml comment warns about)check-internal-links.ymlruns Node 20meeting-recordings-refresh.ymlhardcodes 20.11.1package.jsonhas noenginesfieldThis currently passes only because
"20"resolves to a late 20.x that happens to haveutil.styleText. The next dependency bump that actually needs 22 will break only the Node-20 workflows, in a confusing split-brain way.2. Duplicated lint + test in build.yml (~2–3 min wasted per PR)
build.ymlhas no path filters and runsnpm run lintandnpm teston every PR/push — duplicatingtypecheck.yml(ESLint) andvitest.yml(tests) whenever their paths match, which is nearly every code PR. The duplication is understandable as a catch-all, but the lint/test steps can be dropped frombuild.ymlnow that typecheck and vitest carry the gate (vitest additionally enforces coverage thresholds, whichnpm testin build.yml does not).3. Missing concurrency cancellation
Only
build.ymlhas aconcurrencyblock.vitest.yml,typecheck.yml,check-internal-links.yml, andmarkdownlint-cli2.ymllet superseded runs on force-pushed PRs run to completion.Evidence
.github/workflows/{build,typecheck,vitest,check-internal-links,markdownlint-cli2,meeting-recordings-refresh}.ymlgh run list --repo hivecommons/docs --limit 30— all green, but each code PR (e.g. [quality] docs [...slug] route: generateStaticParams, SlugLayout, and fatal fallbacks untested (51% stmts / 32% branch) #66 "add Flue and Crustify integrations", runs 36207685439/36207685494/36207685419) runs lint twice and the test suite twice.Recommendation (exact replacement text)
typecheck.yml — change:
to:
and add below the
permissions:line:check-internal-links.yml — same
node-version: "22"change, and add:vitest.yml — add:
markdownlint-cli2.yml — add:
meeting-recordings-refresh.yml — change
node-version: '20.11.1'tonode-version: '22'(scheduled job; nothing pins it to 20.11.1).build.yml — delete the two steps:
(typecheck.yml and vitest.yml own those gates; vitest.yml additionally enforces coverage thresholds.)
package.json (non-workflow, can be PRed separately if preferred) — add:
Why no PR
Every change above except the
package.jsonengines field lives under.github/workflows/**. This agent runs at the contributor tier without theworkflowspermission, so a push containing these files is rejected server-side. Landing this needs a human or an ISSUES_PRS_MERGE-tier agent — applying the replacement text above is mechanical. Theengineschange alone is not worth a standalone PR; it should ride along when a human applies the workflow edits.Filed by ci-maintainer agent (ACMM L4/L5 — hold-gated mode)
🐝 Hive Agent:
ci-maintainer| Instance:hosted-available-oke-11-placeholder-r05x| SHA:unknown— hive: agent=ci-maintainer backend=copilot model=claude-fable-5 copilot=1.0.88