Skip to content

fix(repo): declare the Node/pnpm floors the toolchain actually requires - #6311

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-5306-engines-truthful-floor
Aug 25, 2026
Merged

fix(repo): declare the Node/pnpm floors the toolchain actually requires#6311
os-warren merged 1 commit into
mainfrom
claude/issue-5306-engines-truthful-floor

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#5306

Implements the maintainer ruling of 2026-08-25T05:50Z exactly, by the numbers rather than the option letters (the letters in the ruling do not line up with the letters in the analysis comment; the numbers govern):

"engines": { "node": ">=22.11", "pnpm": ">=10" }

⛔ The segmented range ^22.11 || ^24 || >=26 was ruled out as an ongoing maintenance tax, and the jsdom ^22.22.2 measurement is not re-litigated here — the maintainer had it in front of them when they chose the minimal correction. ⛔ engine-strict=true is out of scope by the same ruling; .npmrc is untouched and #6264 is not edited.

Verified before implementing: >=22.11 is not actively wrong. CI resolves node-version: '22.x' to 22.23.2 (PM's job-log read on this card), this container runs 22.22.2, and pnpm install --frozen-lockfile is clean under the new floor — quoted below. Nothing had to be excluded that CI or contributors actually use.

What moved, and why each one is load-bearing

FileChangeWhy it is not optional
package.jsonnode: ">=22"">=22.11", pnpm: ">=9"">=10"The ruling.
QUICK_REFERENCE.mdpnpm row ≥ 9≥ 10The second half the dispatch asked me to look for.scripts/__tests__/quick-reference-current-release-4143.test.ts pins this row to engines.pnpm; without it the change is red. Regenerated with pnpm quick-reference:sync, not by hand.
CONTRIBUTING.md:23-24"Node.js 18.0 or higher" → 22.11; pnpm line now states its floorContributor onboarding stated a floor four majors below the manifest.
CONTRIBUTING.md:378"Tests on Node.js 18.x and 20.x" → 4 shards on 22.xThere is no Node matrix in ci.yml at all — the matrix is shard: [1,2,3,4], all on node-version: '22.x'.
scripts/setup.shreads both floors out of engines; compares full versionsThe automated setup README.md:465 points contributors at. It enforced Node ≥ 20 / pnpm ≥ 9 in hard-coded integers.
content/docs/guide/deployment.mdcorepack prepare pnpm@9pnpm@10; Netlify NODE_VERSION 20 → 22, PNPM_VERSION 9 → 10This page builds this workspace (it copies pnpm-workspace.yaml, packages/, apps/ and publishes apps/console/dist), so its toolchain versions are claims about the repo, not about a reader's app.

⚠️The Node row in QUICK_REFERENCE.md deliberately does not move. Its pin derives the floor as the first integer of the range (engines.node.match(/(\d+)/)), so >=22.11 still reads ≥ 22. Changing that row to ≥ 22.11 would go red. Called out because "the Node floor changed but the Node row didn't" looks like an oversight and is not.

Three edits outside package.json + CONTRIBUTING.md, declared rather than slipped in

scripts/setup.sh, the deployment.md versions and the pnpm half of the CONTRIBUTING.md prerequisites are in-place fixes of the same defect this card exists to close (a stated Node/pnpm floor disagreeing with the toolchain), each with its correct form pinned by evidence already in the tree — the ruling's numbers, packageManager: pnpm@10.31.0, and the workflows' own node-version: '22.x'. None of them adds a verification surface. Naming them here because an unnamed drive-by fix is unreviewable sprawl.

setup.sh reads the floors instead of repeating them, which is the only version of this fix that cannot drift again:

NODE_FLOOR=$(node -p "require('$REPO_ROOT/package.json').engines.node.replace(/^[^0-9]*/, '')")if [ "$(printf '%s\n%s\n'"$NODE_FLOOR""$(node -v | cut -d'v' -f2)"| sort -V | head -n1)"!="$NODE_FLOOR" ];then

The old check compared majors (cut -d'.' -f1), so a 22.11 floor was not even expressible in it — -lt 22 would have accepted 22.0–22.10 while the message next to it claimed 22.11, re-creating this card's defect one order of magnitude smaller.

Two premises in the dispatch, measured

  • CONTRIBUTING.md does say "Node.js 18.0 or higher" — line 23, and it is fixed here. The PM's grep found only line 378 because the line reads - **Node.js** 18.0 or higher: the bold markers sit between the name and the number, so a contiguous Node.js 18 pattern misses it. Not "already fixed" — the pattern missed it.
  • ⚠️The content/docs sweep is NOT empty. Paired with a control (grep -rl pnpm content/docs → 19 files of 184, so the sweep reaches the tree), four pages carry Node/pnpm version prose. One is fixed here (deployment.md); one is already correct and self-anchoring (content/docs/utilities/cli.mdx:300 points at the engines field rather than restating it); two are deliberately not touched and filed instead — see below. My own first pattern also returned zero on QUICK_REFERENCE.md, for the same bold-marker reason, which is how a one-pattern zero reads as an empty sweep twice in a row on this card.

content/docs/releases/ untouched. content/docs/guide/release-notes.md's historical Node.js >= 18 compatibility matrix for the shipped v3.3.0 release untouched — rewriting a past release's stated compatibility misrepresents history.

Deliberately left alone, filed instead

Cross-lane

⚠️ This PR touches content/docs/guide/, where PR #6082 (card #5923) is in flight. Checked immediately before pushing: #6082's file list is content/docs/guide/layout.mdonly — no overlap with deployment.md. origin/main was re-fetched at push time and is still 090927f4f (0 commits since my base), so nothing landed under content/docs/guide/ or on root package.json while I worked.

Gates

All run on the final commit 12052c180, exit codes captured by redirect before any pipe, each quoted from the gate's own verdict line.

GateExitIts own verdict
pnpm install --frozen-lockfile (new floor)0Lockfile is up to date, resolution step is skipped / Already up to date / Done in 2.6s using pnpm v10.31.0 — no engine warning
node scripts/check-changeset-presence.mjs0✅ No source of a released package changed in this range, so no changeset is owed.
pnpm quick-reference:check0✅ QUICK_REFERENCE.md's "Current Release" block already states every anchor.
vitest run × 4 pin files0Test Files 4 passed (4) / Tests 139 passed (139)
pnpm check:control-bytes0✅ check-control-bytes: OK (scanned 5195 tracked text file(s); skipped 85 binary).
pnpm check:shell-escape-residue0✅ check-shell-escape-residue: OK (4/4 root(s) resolved …)
pnpm check:doc-fences0✅ check:doc-fences — every TypeScript block in 223 document(s) is fenced ts/tsx/typescript …
pnpm docs:check-links0Links are valid across 17 scan roots.

Pin files run: quick-reference-current-release-4143, sync-quick-reference-release, doc-version-claims, check-doc-links.

No changeset, on the gate's verdict rather than on my judgement — nothing published changed. ⛔ No skip-changeset label: #4912 records that no workflow reads it in this repo.

Repo-wide pnpm lint narrowed, and the narrowing is measured, not assumed.eslint --format json over all five changed paths returns five results, every one of them File ignored because no matching configuration was supplied. — eslint's own config, not my guess, says this diff contains zero files it judges (a .json, three .md, one .sh). No type-aware linting is configured, so no untouched file's verdict can move either. CI runs the full farm regardless.

Reverse verification — the pin is live, not vacuously green

Committed first, then mutated, then restored (never against an uncommitted edit):

  1. Put the pnpm row back to ≥ 9. Mutation proven on disk by blob hash, not by an editor's exit code: git hash-object moved from 0708d493d… (HEAD) to ef00feb7f…, and the old/new row spellings counted 1/0.
  2. vitest run scripts/__tests__/quick-reference-current-release-4143.test.tsexit 1, Tests 2 failed | 6 passed, on exactly the right assertion:
    AssertionError: QUICK_REFERENCE.md's pnpm row must state exactly the floor "≥ 10" and the pinned "pnpm@10.31.0": expected [ '10.31.0', '≥9' ] to deeply equal [ '10.31.0', '≥10' ]
  3. Restored with git checkout HEAD -- QUICK_REFERENCE.md (naming HEAD, not a bare checkout that would re-read the polluted index), under an EXIT INT TERM trap using an absolute repo root. Restore proven the same way as the mutation: blob hash back to 0708d493d…, git diff HEAD empty. Both legs re-run green afterwards.

No build/dist leg applies here: the pin reads QUICK_REFERENCE.md and package.json off disk, so there is no compiled artifact that could keep a mutation alive or hide it.


Generated by Claude Code

Root `engines` said `node: ">=22"` / `pnpm: ">=9"`, admitting Node 22.0-22.10
and pnpm 9 - versions `@changesets/cli@3` refuses outright. Nothing enforces the
declaration (no `engine-strict`, `packageManager` pinned to pnpm@10.31.0, every
workflow on `node-version: '22.x'`), so this was a false declaration rather than
an outage: a contributor picking a version by reading `engines` got a toolchain
combination that does not work, with no guard to say so.
Per the maintainer ruling of 2026-08-25, the minimal truthful correction:
"engines": { "node": ">=22.11", "pnpm": ">=10" }
The segmented range `^22.11 || ^24 || >=26` was considered and rejected as an
ongoing maintenance tax; `engine-strict=true` is ruled a separate follow-up card
and is deliberately NOT part of this change.
Second half - the surfaces that restate those floors:
- `QUICK_REFERENCE.md`'s pnpm row is pinned to `engines.pnpm` by
`scripts/__tests__/quick-reference-current-release-4143.test.ts`; regenerated
with `pnpm quick-reference:sync` (the Node row is derived as the first integer
of the range, so `>=22.11` still reads "≥ 22" and does not move).
- `CONTRIBUTING.md` claimed "Node.js 18.0 or higher" and a Node 18.x/20.x test
matrix; CI has no Node matrix at all - it shards the suite four ways on 22.x.
- `scripts/setup.sh`, the automated setup README points contributors at, checked
for Node >= 20 and pnpm >= 9. It now READS both floors out of `engines` rather
than repeating them, so the script cannot drift from the manifest again, and
it compares full versions (`sort -V`) instead of majors, which is what makes
a 22.11 floor expressible at all.
- `content/docs/guide/deployment.md` built this workspace with
`corepack prepare pnpm@9` and Netlify `NODE_VERSION = "20"` / `PNPM_VERSION = "9"`.
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation configuration dependencies labels Aug 25, 2026
@os-warren
os-warren marked this pull request as ready for review August 25, 2026 12:31
@os-warren
os-warren added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit b1c1e3bAug 25, 2026
26 checks passed
@os-warren
os-warren deleted the claude/issue-5306-engines-truthful-floor branch August 25, 2026 12:55
os-warren pushed a commit that referenced this pull request Aug 25, 2026
objectui#6313: the pin at scripts/__tests__/quick-reference-current-release-4143.test.ts
and the sync generator (scripts/sync-quick-reference-release.mjs) derived the
Node/pnpm version floors with `match(/(\d+)/)?.[1]`, keeping only the leading
integer group. Against root `engines.node: ">=22.11"` (objectui#5306 / PR #6311)
that produced a floor of `22`, silently discarding the `.11` — so a row reading
exactly `≥ 22` passed a pin whose entire premise is disagreeing with its own
cited anchor.
Both derivations now strip the comparator and keep the whole version string.
`pnpm quick-reference:sync` regenerated the Node.js row to `≥ 22.11`. Added
regression coverage pinning that a `≥ 22`-shaped row is now rejected and that a
`≥ 220`-shaped row still is (objectui#4913), and corrected the test file's
docblock sentence that had gone stale on the same anchor one decimal place up.
No published package source changed; changeset declares no release.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CZgmFFzqA9cX8tBMhvpFe
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configurationdependenciesdocumentationImprovements or additions to documentation

Projects

None yet

2 participants

@os-warren@claude