Repository navigation
[quality] no test pins the concurrency/cancel-in-progress contract on PR-triggered workflows #1001
Description
Finding
Both pull_request-triggered workflows cancel their own superseded runs, and
nothing in the test suite requires them to.
.github/workflows/ci.yml:14-18:
concurrency:
group:
ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref
}}
cancel-in-progress: true.github/workflows/codeql.yml:39-41 does the same, keyed on
github.event.pull_request.number || github.ref. Both properties are load-bearing
and both are unasserted:
- Without a
concurrencyblock, pushing three times to a branch leaves
three full runs ofci.ymlalive. Each holds its runners for the whole
matrix, and the two already-obsolete runs report onto the same commit status
as the one that matters. - Without a per-PR
group,concurrency: ciis one constant key for the
whole repository, socancel-in-progress: truestops cancelling stale runs
of this PR and starts cancelling other people's PRs. The keying is what
makes the cancellation safe.
Nothing in the suite covers this
tests/ci-supply-chain.test.mjscovers action pinning,permissions,
pull_request_target, Node versions,persist-credentials,
timeout-minutesand runner pinning. Concurrency appears only in a prose
comment explaining why a hung job is not superseded — no assertion.tests/workflow-scripts.test.mjsasserts which commands a workflow runs.tests/automation-workflow-ordering.test.mjscovers step ordering in the
generator workflows, none of which are PR-triggered.- Open PR test: guard ci.yml gating jobs against continue-on-error #1000 pins
continue-on-erroron ci.yml's gating jobs — a different
property of a different key.
A new PR-triggered workflow can be added today with no concurrency block at
all, or cancel-in-progress can be flipped to false, and the suite stays
green.
Evidence
This is not a coverage gap — there is no uncovered line to report.
Unit, at main 6ccdaac, npm run test:unit:coverage:check (TZ=UTC) run
locally 2026-10-03, exit 0:
src files | 100.00 | 100.00 | 8351/8351 lines | 2395/2395 regions
all files | 99.37 | 95.02 | 42722/42995 lines | 7205/7583 regions
Every file under scripts/ and scripts/lib/ also reports 100.00 | 100.00.
The gap is a missing contract guard over YAML, which no coverage number can
show.
End-to-end evidence is not relevant to this finding: the contract is over
workflow definitions, not over code any browser executes.
Recommendation
Add tests/ci-concurrency.test.mjs asserting, over the parsed workflows:
- every
pull_request(orpull_request_target) workflow declares a
concurrencygroup; - that group sets
cancel-in-progress: true; - that group's key varies per pull request — it must reference
github.event.pull_request.number,github.head_reforgithub.ref
inside a${{ ... }}expression; - any workflow using a constant group is recorded in a
SERIALISED_WORKFLOWS
allowlist with the reason, following the retiring-baseline convention
already used byKNOWN_PERSISTING_CHECKOUTSandKNOWN_UNBOUNDED_JOBSin
tests/ci-supply-chain.test.mjs.deploy-gh-pages.ymlis the one entry:
it uses the constantpagesgroup withcancel-in-progress: falsebecause
cancelling a deploy halfway can leave Pages serving a partial artifact. - a companion staleness test deletes an allowlist entry once its workflow
satisfies both halves of the contract, so the exemption cannot outlive the
reason for it.
The guards must be exercised against fixtures as well as the repository's own
workflows — a guard whose failure branch never runs is a guard nobody has
checked.
This is entirely within tests/. It reads .github/workflows/*.yml but
writes nothing there, so it is pushable at contributor tier and needs no
workflow permission.
Completion criteria
- a test fails when a
pull_requestworkflow declares noconcurrencygroup - a test fails when such a group omits
cancel-in-progress: true - a test fails when such a group is keyed on a constant
-
deploy-gh-pages.yml's constant group is recorded with a reason, and the entry retires itself - each guard is exercised against a fixture that drives its failure branch
Priority
- Impact: medium — no current defect; protects PR-lane throughput and the
integrity of commit statuses against a one-line YAML regression - Effort: low
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5n31 | SHA: 6ccdaac
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Activity
test: pin the concurrency and cancel-in-progress contract on the PR lane