Context
This package pins pnpm@10.34.5. Migrating to pnpm 11 adopts its supply-chain hardening defaults and consolidates onto the single global pnpm store.
pnpm 11 turns on five defaults that were previously opt-in or absent:
| Setting |
Default |
Purpose |
minimumReleaseAge |
1440 (24h) |
Refuse versions younger than the cooldown window — blocks the publish-malware-then-wait-for-CI attack |
blockExoticSubdeps |
true |
Transitive deps cannot come from git URLs or arbitrary tarballs |
strictDepBuilds |
true |
Unapproved postinstall scripts fail the install |
verifyDepsBeforeRun |
install |
Re-check lockfile before pnpm run |
optimisticRepeatInstall |
true |
Skip work when the lockfile is unchanged |
The Node 20 decision
pnpm 11 is pure ESM and requires Node >=22. This repo's CI matrix currently tests 20.x, 22.x, 24.x, so the Node 20 leg would fail at pnpm install as soon as the packageManager field moves.
Decision: drop Node 20 and raise engines.node to >=22.
Rationale:
- Node 20 reached end-of-life in April 2026 and no longer receives security updates.
- Leaving
engines.node: ">=20" while CI no longer tests Node 20 would advertise support that isn't verified.
This is a support-contract change on a published package. Consumers still on Node 20 (polydoc, TeamWiki) will see an engines warning on install. The package is pre-1.0 (0.1.1), so this is a reasonable point to make the change.
release.yml already uses Node 22.x and needs no change.
Scope notes
- Lockfile is already
lockfileVersion: '9.0', identical between pnpm 10 and 11 — no regeneration expected.
- No
.npmrc, no package.json#pnpm field, no Dockerfile or devcontainer — nothing to relocate into the new config home.
- Both workflows use
pnpm/action-setup@v6 with no version: pin, so they read packageManager from package.json automatically.
strictDepBuilds means the first install is expected to fail with ERR_PNPM_IGNORED_BUILDS until each dependency with a postinstall script is audited and allowlisted.
Tasks
Verification note
check:package (attw + publint) is the critical gate here — packaging breakage is invisible to lint, typecheck, and the test suite, and only surfaces for a consumer after publish.
Follow-up (not in this change)
@types/node is pinned at ^20.19.43, which is inconsistent with an engines.node: ">=22" contract. Bumping it can surface new type errors, so it is left for a separate PR.
Context
This package pins
pnpm@10.34.5. Migrating to pnpm 11 adopts its supply-chain hardening defaults and consolidates onto the single global pnpm store.pnpm 11 turns on five defaults that were previously opt-in or absent:
minimumReleaseAge1440(24h)blockExoticSubdepstruestrictDepBuildstrueverifyDepsBeforeRuninstallpnpm runoptimisticRepeatInstalltrueThe Node 20 decision
pnpm 11 is pure ESM and requires Node >=22. This repo's CI matrix currently tests
20.x,22.x,24.x, so the Node 20 leg would fail atpnpm installas soon as thepackageManagerfield moves.Decision: drop Node 20 and raise
engines.nodeto>=22.Rationale:
engines.node: ">=20"while CI no longer tests Node 20 would advertise support that isn't verified.This is a support-contract change on a published package. Consumers still on Node 20 (
polydoc,TeamWiki) will see an engines warning on install. The package is pre-1.0 (0.1.1), so this is a reasonable point to make the change.release.ymlalready uses Node 22.x and needs no change.Scope notes
lockfileVersion: '9.0', identical between pnpm 10 and 11 — no regeneration expected..npmrc, nopackage.json#pnpmfield, no Dockerfile or devcontainer — nothing to relocate into the new config home.pnpm/action-setup@v6with noversion:pin, so they readpackageManagerfrompackage.jsonautomatically.strictDepBuildsmeans the first install is expected to fail withERR_PNPM_IGNORED_BUILDSuntil each dependency with a postinstall script is audited and allowlisted.Tasks
pnpm@11.18.0and raiseengines.nodeto>=2220.xfrom thechecksmatrix inci.ymlpnpm-workspace.yaml--frozen-lockfileinstall,format:check,lint,typecheck,test,build,check:package,audit signaturesVerification note
check:package(attw + publint) is the critical gate here — packaging breakage is invisible to lint, typecheck, and the test suite, and only surfaces for a consumer after publish.Follow-up (not in this change)
@types/nodeis pinned at^20.19.43, which is inconsistent with anengines.node: ">=22"contract. Bumping it can surface new type errors, so it is left for a separate PR.