Repository navigation
[quality] package.json <-> scripts/ <-> workflows wiring contract is untested (tests/workflow-scripts.test.mjs) #277
Description
Finding
package.json scripts and .github/workflows/*.yml are full of references
that are strings in one file naming something real in another file, and not
one of them is verified by a test. Verified on main @ 00b44df:
- ~45 entries in
package.jsonscripts, containingnode scripts/<f>.mjs
targets,-c <dotfile>linter config paths, andnpm run <other>chains - 19
npm run <name>invocations across the five workflow files - zero tests read
package.jsonor any workflow file
The whole existing suite (tests/validate-*.test.mjs,
tests/validators-smoke.test.mjs) exercises the validators by spawning them
directly through tests/helpers.mjs. That deliberately bypasses the npm script
layer, which is correct for those tests but means the layer contributors and CI
actually invoke is the one nothing checks. A renamed script, a deleted
scripts/*.mjs, or an absent config dotfile produces a green unit suite and
fails only when a human or a workflow runs the command.
This has already happened here. Repository history records npm run check
failing on a fresh clone because .cspell.yml, .markdownlint.yaml and
.markdown-link-check.json were named by check:spelling, check:markdown
and _check:links-md but were not in the repo. All three are present again
today, and nothing stops them going missing again. Separately,
scripts/validate-button-contrast.mjs is reachable from package.json but
from no workflow — a gap the current suite has no way to express.
Recommendation
Add tests/workflow-scripts.test.mjs, a test-only file asserting the wiring
contract in both directions:
- Every workflow parses as YAML and declares at least one job.
- Every
npm run <name>in a workflow names a definedpackage.jsonscript. - Every
npm run <name>inside apackage.jsonscript names a defined
script (covers thecheck/fix/seq/precheck:linkschains). - Every
node scripts/...target inpackage.jsonexists on disk. - Every
-c/--configdotfile passed to a linter exists — the direct guard
against the fresh-clone regression above. - Every
scripts/*.mjsentry point is reachable from somepackage.json
script, so no entry point is orphaned. - Any workflow that runs a
validate:*script also runstest:unit.
All seven pass on main today. Each must be mutation-checked — an assertion
that cannot fail is not a test.
Completion criteria:
-
tests/workflow-scripts.test.mjsexists and is picked up bynpm run test:unit - each assertion is shown to fail against a deliberate mutation
- no production file, workflow, or dependency is modified
Scope note: this is the package.json ↔ scripts/ ↔ .github/workflows/
wiring contract only. It is disjoint from the data-file contract tests
(#235, #239, #241, #253, #257, #260, #270), from the navigation contract
(#275), and from the per-script unit tests (#185, #197, #208, #214, #216,
#221, #231, #263) — it adds one new test file and touches nothing they touch.
It is also distinct from the ci.yml validator gap filed alongside this
issue: that one asks for a change to a workflow (unpushable here), whereas
this one adds a test guarding the references that already exist.
Priority
- Impact: medium — silent breakage of the command layer contributors and CI use
- Effort: low — one test file, no dependencies (
yamlis already a devDependency)
Filed by quality agent (hold-gated mode)
— hive: agent=quality backend=copilot model=claude-opus-5
Activity
test: cover the package.json <-> scripts/ <-> workflows wiring contra…