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

File Change Why it is not optional
package.json node: ">=22"">=22.11", pnpm: ">=9"">=10" The ruling.
QUICK_REFERENCE.md pnpm row ≥ 9≥ 10 The 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 floor Contributor 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.x There 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.sh reads both floors out of engines; compares full versions The automated setup README.md:465 points contributors at. It enforced Node ≥ 20 / pnpm ≥ 9 in hard-coded integers.
content/docs/guide/deployment.md corepack prepare pnpm@9pnpm@10; Netlify NODE_VERSION 20 → 22, PNPM_VERSION 9 → 10 This 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.md only — 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.

Gate Exit Its own verdict
pnpm install --frozen-lockfile (new floor) 0 Lockfile 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.mjs 0 ✅ No source of a released package changed in this range, so no changeset is owed.
pnpm quick-reference:check 0 ✅ QUICK_REFERENCE.md's "Current Release" block already states every anchor.
vitest run × 4 pin files 0 Test Files 4 passed (4) / Tests 139 passed (139)
pnpm check:control-bytes 0 ✅ check-control-bytes: OK (scanned 5195 tracked text file(s); skipped 85 binary).
pnpm check:shell-escape-residue 0 ✅ check-shell-escape-residue: OK (4/4 root(s) resolved …)
pnpm check:doc-fences 0 ✅ check:doc-fences — every TypeScript block in 223 document(s) is fenced ts/tsx/typescript …
pnpm docs:check-links 0 Links 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-actions github-actions Bot 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 queue Aug 25, 2026
Merged via the queue into main with commit b1c1e3b Aug 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configuration dependencies documentation Improvements or additions to documentation

Projects

None yet

2 participants