Skip to content

[quality] npm run check decides its membership by regex, and a multi-segment check: gate silently falls out #1281

Description

@hivecommons-hive

Finding

npm run check does not name its members. package.json:

"check": "npm run seq -- $(npm run -s _list:check:*)",
"_list:check:*": "npm run --loglevel=warn | grep -Ee '^\\s*check:[^:]+$'",

Membership is decided by a regular expression over script names, at runtime,
in a subshell. The one thing that regex cannot match is a name with a second
colon.

That is a live trap rather than a theoretical one, because the repository
already names things that way: test:unit:coverage, validate:metrics,
_check:markdown:all, _check:format:delta. A gate added as
check:links:external would be a check:* gate by every other measure in the
suite — tests/workflow-scripts.test.mjs:423 counts it with
/^(?:validate|check):/ and demands that a workflow run it or that the
omission be recorded in GATES_NOT_RUN_BY_CI — while never running under
npm run check. Nothing fails. The aggregate just quietly stops being one.

The blast radius is the two things npm run check backs:

  • "test": "npm run check && npm run test:unit", the command a contributor
    runs before pushing;
  • CONTRIBUTING.md:208, which tells contributors to treat npm run check as a
    superset of the CI checks.

No test reads the expansion. tests/workflow-scripts.test.mjs:96-107
explicitly skips it (if (target.includes('*')) continue; — "a literal glob is
not a script name"), which is correct for that file's purpose and leaves the
aggregate unexamined by anything.

Evidence and provenance

  • Repository revision 365e11f (main), clean checkout, npm ci,
    node v26.10.0.
  • This is not a defect today, and the issue does not claim one. The real
    expansion, read from npm run -s _list:check:*, is check:audit,
    check:format, check:links, check:markdown, check:spelling,
    check:community-group-links — every declared check:* script, all
    single-segment. CONTRIBUTING.md:208-220 documents that membership
    deliberately, including that check:community-group-links rewrites
    data/community-groups.json. The gap is that nothing holds it there.
  • Unit. npm run test:unit:coverage (TZ=UTC) run locally 2026-10-10:
    src files | 100.00 | 100.00 | 9015/9015 lines | 2713/2713 regions,
    harness files | 100.00 | 99.19 | 3890/3890 lines | 1467/1479 regions.
    Every scripts/** and src/** file is at 100% lines and regions, so this is
    filed as test-infrastructure, not coverage-gap: nothing here is
    uncovered code, it is an unasserted contract over a shell expansion.
  • End-to-end. Not applicable and not claimed: collectE2ECoverage
    attributes only paths under src/, so the Playwright run never observes
    package.json.

Recommendation

One deliverable, one new test file: read the real expansion and hold it to the
declared gates, both directions.

  • Every check:* script package.json declares appears in the expansion,
    or is recorded in a GATES_NOT_IN_CHECK map with a reason — the same
    convention GATES_NOT_RUN_BY_CI already uses, with the same staleness
    guard so an entry cannot outlive its fault.
  • Everything the expansion lists is a declared check:* script, so a name
    that merely looks like a gate cannot be run by the aggregate while every
    gate guard in the suite ignores it.
  • A non-vacuity guard: the expansion must still be several gates including
    the lint trio, and scripts.check must still be the _list:check:*
    form this file reads.

The guards read the expansion out of npm rather than re-implementing the grep,
so they are evidence about what npm run check does rather than a second copy
that can agree with itself while disagreeing with the shell. Only the
enumerating helper runs — no gate, no network, no file written.

Green at 365e11f with no change to package.json, and mutation-checked:
adding a check:links:external script fails the membership rule by name.

Nothing here is under .github/workflows/. No open pull request touches
tests/workflow-scripts.test.mjs; #1250 touches package.json, which this
adds nothing to and only reads.

Priority

  • Impact: medium (the pre-push command every contributor is pointed at decides
    what it runs by a regex nothing tests, and the failure mode is silent
    omission rather than an error)
  • Effort: low

Filed by quality agent (hold-gated mode)


🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5n31 | SHA: f72490a

— 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
    on Oct 10, 2026
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/covered-by-prHive verified that an open PR references or claims this issue; still actionable until confirmedhive/hosted-available-lke648397-260827-5n31Approved by a Hive merger/owner for auto-merge on green CIqualityApproved 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