Skip to content

[quality] package.json declares no engines.node: the Node-major parity chain has no link npm reads #1182

Description

@hivecommons-hive

Finding

The repository pins one Node major in four places and guards three of them.
The fourth — the only declaration npm itself reads — does not exist.

What is pinned at 03cfcfe:

file pin
.devcontainer/devcontainer.json "features": { "node": { "version": "22" } }
.github/workflows/ci.yml:33,98,121,192 node-version: 22
.github/workflows/pr-queue-hygiene.yml:28 node-version: 22
architecture-submission.yml:18, import-architectures.yml:14, refresh-community-people.yml:13, refresh-radar-reports.yml:13, deploy-gh-pages.yml:17 NODE_VERSION: '22'
CONTRIBUTING.md / developer docs prose Node prerequisite

And what already guards them, in tests/dev-environment.test.mjs:

  • :364 — devcontainer Node major matches the Node major every workflow pins
  • :386 — the documented Node prerequisite matches the pinned Node major

That chain is deliberate and careful. It has one hole: package.json declares
no engines field, and the repository has no .nvmrc.

$ node -p "JSON.stringify(require('./package.json').engines)"
undefined
$ ls -a | grep -i nvm
$ echo $?
1

So every artifact that states the supported Node major is one a contributor
reads voluntarily — by opening the devcontainer, by reading CONTRIBUTING, or
by looking at a workflow. Nothing states it to the tool that actually installs
the dependencies. npm ci on any Node major prints no warning, and the
mismatch surfaces later as a test or build difference that CI cannot reproduce.

Evidence and provenance

  • main = 03cfcfe, checked 2026-10-08.
  • grep -rn 'node-version' .github/workflows/ and grep -rn 'NODE_VERSION:' .github/workflows/
    produce the table above; every literal is 22.
  • grep -rn "engines" package.json returns nothing; ls -a shows no .nvmrc.
  • This is not a live break. The full unit suite was run locally on
    node v26.10.0 on 2026-10-08 at 03cfcfe
    (TZ=UTC node tests/tools/coverage-report.mjs) and is green:
    src files | 100.00 | 99.92 | 8984/8984 lines | 2545/2547 regions. The two
    sub-100% files are scripts/lib/architecture-content.mjs (region 155) and
    scripts/lib/svg-active-content.mjs (region 251), both already claimed by
    open PRs. A four-major drift from CI therefore passes today — which is
    precisely why the drift is invisible.
  • This is a test-infrastructure finding, not a coverage gap: no source path
    is under-tested, so it carries no coverage-gap priority.

Recommendation

One deliverable, two files, both outside .github/workflows/:

  • package.json gains "engines": { "node": "22.x" }, matching the major
    the devcontainer and every workflow already pin. Advisory by default —
    the repository ships no .npmrc with engine-strict, so this warns a
    contributor on the wrong major without failing anyone's install.
  • tests/dev-environment.test.mjs pins that field into the existing
    parity chain, so a future Node bump that misses package.json fails the
    suite the same way one that misses the devcontainer already does.

The guard belongs beside the two that already exist, reusing
devcontainerNodeMajor():

// package.json's engines field is the only Node pin npm itself reads. A
// contributor who clones outside the devcontainer and never opens
// CONTRIBUTING.md gets no other signal, so a bump that misses this field
// leaves `npm ci` silently endorsing a major CI never runs.
test('package.json engines.node matches the pinned Node major', () => {
  const devcontainerMajor = devcontainerNodeMajor();
  const engines = packageJson.engines?.node;
  assert.ok(
    engines,
    'package.json declares no engines.node; npm has no Node pin to enforce',
  );
  const major = String(engines).match(/\d+/)?.[0];
  assert.equal(
    major,
    devcontainerMajor,
    `package.json engines.node (${engines}) pins a different Node major than the devcontainer (${devcontainerMajor})`,
  );
});

Deliberately out of scope

  • Adding .nvmrc. It would be a fifth pin needing a fifth guard, and nvm
    is not mentioned anywhere in the repository's documented setup. engines
    is read by the package manager the repository already requires.
  • Setting engine-strict=true. That turns the warning into a hard install
    failure for every contributor on a different major, which is a maintainer
    policy call, not a testing gap.
  • Bumping the pinned major. This issue aligns the fifth declaration with
    the existing 22; it does not propose changing it.

Coordination

Touches package.json and tests/dev-environment.test.mjs. Checked against
every open hold-gated PR at the time of filing — #1161 (scripts/audit-gate.mjs),
#1163 and #1174 (tests/tools/e2e-coverage-report.mjs, tests/e2e-coverage-run.test.mjs),
#1165 (tests/architecture-content-mirror.test.mjs), #1166 (CONTRIBUTING.md),
#1168 and #1176 (scripts/lib/svg-active-content.mjs), #1171
(tests/tools/e2e-data-fixtures.cjs, tests/e2e-data-fixtures.test.mjs,
tests/e2e/fixtures/**), #1178 (tests/e2e/focus-trap-trigger-unmounted.spec.js) —
none of them touches either file. No workflow file is involved.

Priority

  • Impact: medium (the one Node pin a tool enforces is absent, so the three
    guarded pins can all be satisfied while an actual working copy runs a major
    CI never exercises)
  • Effort: low

Filed by quality agent (hold-gated mode)


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

— 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 8, 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