feat(security): make the npm cooldown real — Node 24 in CI plus gate-84 - #469
Merged
Conversation
The fleet's .npmrc files have carried a comment describing a 24h supply-chain cooldown. It has never been in effect anywhere, for two independent reasons. 1. `min-release-age` DOES NOT EXIST IN NPM 10. Not rejected, not warned about: `npm config get min-release-age` answers `undefined`. Every Node 22 release bundles npm 10 — 22.23.2, the latest, ships 10.9.8 — so no value of the node-version input in the 22 line could ever deliver it. Node 24.19 bundles npm 11.17. That is why the default moves to 24; it is an npm change, not a Node one, and the apps' own engines.node already declared ^24. 2. 13 of 19 apps set `min-release-age=0`, which disables it outright. gate-84 asserts the three settings that only work together: the window (>= 2 days), the `@conduction/*` exclusion, and an engines.npm that admits only npm 11+. Any one alone looks like protection and is not. The exclusion is not optional. Without it the cooldown does NOT fail loudly — it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. A green install of months-old code is worse than a red one. Gate is full-tree, not diff-scoped: conformance is a property of the repo, not of a change, and diff-scoped it would be silent on almost every PR. Verified against the real fleet: 19 of 19 apps FAIL at their current committed state, 19 of 19 PASS with the accompanying per-app change. The 15-assertion suite is auto-discovered by run-helper-suites.sh and asserts each setting is individually load-bearing — including engines.npm '>=10', a range a person would write meaning 'modern npm' that silently admits the one npm where the feature does not exist.
This was referenced Aug 15, 2026
Closed
Closed
Merged
Closed
Merged
…fix SC2015 Two failures from the package's own CI, both real. 1. On a repo with no package.json, gate-84's `if [ -f package.json ]` guard made it emit NOTHING — and a silence is indistinguishable from a pass, which is precisely what --require-full-coverage exists to catch. It reported 'GATES THAT DID NOT RUN: 84' and exited 98 on a repository with nothing wrong with it. Adds the applicability declaration so the absence is stated by name instead of being a gap. 2. ShellCheck SC2015: the suite's last assertion used `A && ok || no`, which is not if-then-else — the failure branch also runs when the check passes and the reporter fails, so one assertion could print both PASS and FAIL. Rewritten as a real if/then/else. Verified: tests/test-hydra-gates-bin.sh passes locally (70 assertions, rc 0) and the gate-84 suite is still 15 of 15.
…-arming errexit Two more findings from the package's own invariants, both correct. 1. The acceptance ratchet: 'gate-84 is DECLARED by the runner but has neither a planted/clean fixture nor a reasoned row in UNCOVERED.md. A gate can be added to the runner and never tested; this is that moment.' It was exactly that moment. Adds the bundle rather than an excuse — gate-84 is full-tree and needs no base ref, so nothing blocks fixturing it. The arms differ ONLY in the three cooldown settings. planted/ reproduces the pre-2026-08-16 fleet state (min-release-age=0 with engines.npm ^10.0.0); clean/ is conformant and deliberately keeps an unrelated legacy-peer-deps line, so a gate grading .npmrc as a whole file rather than per-setting turns the CLEAN arm red instead of leaving the planted one green. 2. errexit discipline: the gate-84 block restored with 'set -e'. Errexit off is the state this script actually runs in, and re-arming it would abort the run on the first non-zero anything downstream. Restore sites say 'set +e'. Verified locally: acceptance matrix rc 0 (64 of 69 gates fixtured, gate-84 FAILs on planted and NAMES min-release-age, silent on clean), errexit discipline rc 0, package invariants rc 0.
rubenvdlinde
added a commit
to ConductionNL/nextcloud-app-template
that referenced
this pull request
Aug 15, 2026
`gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/decidesk
that referenced
this pull request
Aug 15, 2026
`gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/docudesk
that referenced
this pull request
Aug 15, 2026
`gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/doriath
that referenced
this pull request
Aug 15, 2026
`gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/hermiq
that referenced
this pull request
Aug 15, 2026
`gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/zaakafhandelapp
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469), but this repo's OWN workflows did not, and they run `npm ci` too. Node 20 and 22 both bundle npm 10, which cannot install from an npm 11 lockfile — it exits EUSAGE with 'Missing: <pkg> from lock file'. Measured here: Lint Check and Spec Validation went red on exactly that while every shared-workflow job passed. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice: nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: api-test-coverage.yml stays on Node 20 where it exists — it runs `npm install -g newman`, a global tool install with no lockfile, so the npm major is irrelevant there. l10n.yml likewise: its only 'npm ci' is inside a comment saying it needs none.
rubenvdlinde
added a commit
to ConductionNL/doriath
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469), but this repo's OWN pull-request-lint-check.yaml did not, and it runs `npm ci` too. Node 20 and 22 both bundle npm 10, which cannot install from an npm 11 lockfile — it exits EUSAGE with 'Missing: <pkg> from lock file'. That workflow had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice: nothing in the file named a Node version, so nothing looked wrong.
rubenvdlinde
added a commit
to ConductionNL/nldesign
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/opencatalogi
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/softwarecatalog
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/docudesk
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/launchpad
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger.
rubenvdlinde
added a commit
to ConductionNL/scholiq
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger. * ci: run every npm job on Node 24 The shared quality.yml moved to Node 24 (ConductionNL/.github#469), but this repo's OWN workflows did not, and they run `npm ci` too. Node 20 and 22 both bundle npm 10, which cannot install from an npm 11 lockfile — it exits EUSAGE with 'Missing: <pkg> from lock file'. Measured here: Lint Check and Spec Validation went red on exactly that while every shared-workflow job passed. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice: nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: api-test-coverage.yml stays on Node 20 where it exists — it runs `npm install -g newman`, a global tool install with no lockfile, so the npm major is irrelevant there. l10n.yml likewise: its only 'npm ci' is inside a comment saying it needs none.
rubenvdlinde
added a commit
to ConductionNL/nextcloud-app-template
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger. * ci: run every npm job on Node 24 The shared quality.yml moved to Node 24 (ConductionNL/.github#469), but this repo's OWN workflows did not, and they run `npm ci` too. Node 20 and 22 both bundle npm 10, which cannot install from an npm 11 lockfile — it exits EUSAGE with 'Missing: <pkg> from lock file'. Measured here: Lint Check and Spec Validation went red on exactly that while every shared-workflow job passed. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice: nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: api-test-coverage.yml stays on Node 20 where it exists — it runs `npm install -g newman`, a global tool install with no lockfile, so the npm major is irrelevant there. l10n.yml likewise: its only 'npm ci' is inside a comment saying it needs none.
rubenvdlinde
added a commit
to ConductionNL/decidesk
that referenced
this pull request
Aug 16, 2026
* chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger. * ci: run every npm job on Node 24 The shared quality.yml moved to Node 24 (ConductionNL/.github#469), but this repo's OWN workflows did not, and they run `npm ci` too. Node 20 and 22 both bundle npm 10, which cannot install from an npm 11 lockfile — it exits EUSAGE with 'Missing: <pkg> from lock file'. Measured here: Lint Check and Spec Validation went red on exactly that while every shared-workflow job passed. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice: nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: api-test-coverage.yml stays on Node 20 where it exists — it runs `npm install -g newman`, a global tool install with no lockfile, so the npm major is irrelevant there. l10n.yml likewise: its only 'npm ci' is inside a comment saying it needs none.
This was referenced Aug 16, 2026
rubenvdlinde
added a commit
to ConductionNL/portaliq
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/hermiq
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/larpingapp
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/openregister
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/zaakafhandelapp
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/doriath
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/openconnector
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/openbuild
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/shillinq
that referenced
this pull request
Aug 16, 2026
The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile).
rubenvdlinde
added a commit
to ConductionNL/launchpad
that referenced
this pull request
Aug 16, 2026
…er a file-wide no-undef (#125) * chore(security): enable the npm supply-chain cooldown on npm 11 Sets `min-release-age=2` and `min-release-age-exclude[]=@conduction/*`, raises `engines.npm` to ^11.0.0, and regenerates the lockfile under npm 11. The .npmrc comment here has described a cooldown for months and it has never been in effect. `min-release-age` does not exist in npm 10 — `npm config get min-release-age` answers `undefined` — and every Node 22 release bundles npm 10, so the setting was read by nothing. Most repos also had it at 0, which disables it outright. @conduction/* is exempt because without the exemption the cooldown does not fail loudly, it silently resolves backwards: measured 2026-08-15, an install of @conduction/nextcloud-vue on release day picked 2.0.7 instead of 2.3.0 and exited 0. The lock is regenerated under npm 11 and iterated to a fixed point. Where the tree changed rather than its metadata, that is npm 10 -> 11 reconciling a lock shaped by the older resolver, not the cooldown — verified by regenerating with the cooldown enabled and disabled and getting identical trees. Verified: npm ci exit 0 under npm 11.19.0, @conduction/nextcloud-vue resolves to 2.3.0, gate-84 conformance passes. * ci: re-run against the merged shared workflow `gh run rerun` replays the workflow version resolved when the run was created, so a reusable workflow referenced as @main is NOT re-resolved — every re-run after ConductionNL/.github#469 merged still executed Node 22 with npm 10.9.8, where `min-release-age` does not exist and `npm ci` cannot read an npm-11 lockfile. Only a new run picks up the merged workflow. This empty commit is that trigger. * fix(lint): clear the last tranche-A suppression — require.context under a file-wide no-undef `require.context()` is a WEBPACK build-time API the bundler rewrites at compile time, so eslint is right that no runtime `require` exists and the code is right too. The file-wide `no-undef` suppression that recorded this also switched the rule off for every OTHER identifier in the file, so a genuine typo there would have been silent. Scoped to `/* global require */`. Same shape found in 9 apps across the fleet; all cleared the same way. Verified: eslint 0 errors, build exit 0, tests pass.
rubenvdlinde
added a commit
to ConductionNL/openbuild
that referenced
this pull request
Aug 16, 2026
…er a file-wide no-undef (#217) * ci: run every npm job on Node 24 The shared quality.yml moved to Node 24 (ConductionNL/.github#469) because npm 10 cannot install from an npm 11 lockfile — EUSAGE, 'Missing: <pkg> from lock file'. This repo's OWN workflows did not move with it and they run npm ci too: measured on the cooldown PR, every shared-workflow job passed while Lint Check and Spec Validation went red on exactly that error. pull-request-lint-check.yaml had no setup-node AT ALL, so it silently inherited the runner default. That is the harder half to notice — nothing in the file named a Node version, so nothing looked wrong. Left alone deliberately: l10n.yml (its only 'npm ci' is in a comment saying it needs none) and api-test-coverage.yml (npm install -g newman, no lockfile). * fix(lint): clear the last tranche-A suppression — require.context under a file-wide no-undef `require.context()` is a WEBPACK build-time API the bundler rewrites at compile time, so eslint is right that no runtime `require` exists and the code is right too. The file-wide `no-undef` suppression that recorded this also switched the rule off for every OTHER identifier in the file, so a genuine typo there would have been silent. Scoped to `/* global require */`. Same shape found in 9 apps across the fleet; all cleared the same way. Verified: eslint 0 errors, build exit 0, tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two changes that only make sense together:
node-versiondefault 22 → 24 in the sharedquality.yml.npm-supply-chain-config— asserts the three settings that make the cooldown real.Companion per-app PRs set
min-release-age=2,min-release-age-exclude[]=@conduction/*andengines.npm: ^11.0.0across all 19 repos.Why the Node bump is really an npm bump
The fleet's
.npmrcfiles have carried a comment describing a 24-hour supply-chain cooldown. It has never been in effect anywhere, for two independent reasons — either alone is enough.min-release-agedoes not exist in npm 10. Not rejected, not warned about:And every Node 22 release bundles npm 10 — 22.23.2, the latest, ships 10.9.8. So no value of
node-versionin the 22 line could ever deliver the feature. Node 24.19 bundles npm 11.17.This supersedes the previous "Node 22 is what Nextcloud's own apps target" rationale. That remains true about upstream, but a cooldown that is silently inert is worse than no cooldown: it reads as protection nobody has. The apps' own
engines.nodealready declared^22.14 || ^24 || >=26, so 24 was always an allowed runtime — this makes it the one CI uses.A caller that must stay on 22 can still pass
node-version: "22". It gets npm 10, and gate-84 reports that rather than letting it pass quietly.Why the gate needs all three settings
min-release-age=2min-release-age-exclude[]=@conduction/*engines.npm: ^11.0.0The exclusion is not optional. Without it the cooldown does not fail loudly — it silently resolves backwards. Measured on 2026-08-15, an install of
@conduction/nextcloud-vueon release day:min-release-age=1, no exclusionmin-release-age=1+@conduction/*excludedA green install of months-old code is a worse outcome than a red one, and nothing in the output distinguishes it from a correct install.
Full-tree, not diff-scoped
Conformance is a property of the repository, not of a change. Diff-scoped, this gate would report nothing on every PR that does not touch
.npmrc— nearly all of them — and a gate silent on 99% of PRs cannot establish fleet-wide conformance, which is its whole purpose.Verification
Against the real fleet, before and after the companion per-app change:
Suite (
scripts/lib/test_check_npm_supply_chain_config.sh, auto-discovered) — 15 assertions. Each setting is asserted individually load-bearing: drop any one from an otherwise conformant repo and the gate still fails. That matters because 4 of the 19 apps already had a correctmin-release-age=1while sitting on a toolchain that cannot read it — a gate firing only when everything is missing would have passed them.The
engines.npm: ">=10"case is the one most likely to rot: a range a person would write meaning "modern npm", which silently admits the one npm where the feature does not exist.Also covered: unparseable
package.jsonfails rather than reading as clean, commented-out settings do not count, and the terminal summary the runner greps for is asserted so a crashed checker cannot be mistaken for a clean one.