ci: the tree runs on one pinned pnpm 12 - #782
Merged
Merged
Conversation
`validate.yml` activates `pnpm@latest`, and pnpm 12.3.4 landed on the runners today. It removed the `=false` value form of `--frozen-lockfile`, so the lefthook job stopped at its own install step: error: unexpected value 'false' for '--frozen-lockfile' found; no more were expected Usage: pnpm install --frozen-lockfile Every pull request fails that check from now on, whatever it changes: the job never reaches the hooks it exists to run. The three merged today passed it hours earlier, under pnpm 11. `--no-frozen-lockfile` is the same instruction in the form both majors accept — pnpm 11 documents the flag as `--[no-]frozen-lockfile`, and pnpm 12's own error says the flag takes no value. Not fixed here, and worth a decision: nine other workflow steps also activate `pnpm@latest`, so the next major arrives the same way, unannounced. Pinning the version is a choice about what the team runs locally, not a CI detail, so it is named rather than made. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
…pped reading
`corepack prepare pnpm@latest --activate` ran in ten workflow steps, so the version
CI used was whatever pnpm had published that morning. `cli/` and `kanban/` pinned
`pnpm@10.14.0` through `packageManager` and the repository root pinned nothing at
all, so one run could resolve the root under 12 and `cli/` under 10.
`packageManager` now names `pnpm@12.3.4` in all three, and every workflow step
stops at `corepack enable` — corepack reads the pin rather than choosing. One
version, written down once, and the next major arrives when somebody edits that
line.
pnpm 12 stopped reading `package.json`'s own `pnpm` field, which is where `cli/`
kept four security floors:
[WARN] The "pnpm" field in package.json is no longer read by pnpm.
The following keys were ignored: "pnpm.overrides".
Ignored there while the lockfile still recorded them, which is the
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH every `cli` install answered with under 12. They
move to `cli/pnpm-workspace.yaml`, their new home, unchanged: `fast-uri >=3.1.2`,
`picomatch >=4.0.4`, `postcss >=8.5.10`, `qs >=6.15.2`.
Each lockfile gains 101 lines and loses none — the `packageManagerDependencies`
block pnpm 12 records for the pinned version. Measured, all three: no dependency
re-resolved, nothing removed. A pin cannot be added without it, since
`--frozen-lockfile` refuses to write that block itself.
Verified by running the thing rather than reading about it. Every package,
`pnpm@12.3.4`, `--frozen-lockfile`:
root exit 0, lockfile stable
cli exit 0, lockfile stable
kanban exit 0, lockfile stable
3,471 CLI tests, 371 repository script tests, biome `ci` clean (2 pre-existing
warnings), typecheck clean, bundle 595.7 / 598 KB, 0 broken links in 798 files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Pinning the version was not enough. pnpm turns an unapproved install script into `ERR_PNPM_IGNORED_BUILDS`, and under 12 every `cli` and `kanban` job stopped there — eleven checks red on a change that touches no source at all. The repository root already named `lefthook`. `cli/` and `kanban/` named nothing, because under 10.14.0 the same builds went through. Named from what pnpm itself printed, never guessed: cli Ignored build scripts: esbuild@0.21.5, esbuild@0.27.3, lefthook@2.1.12 kanban Ignored build scripts: esbuild@0.28.1 Verified after, `pnpm@12.3.4` with `--frozen-lockfile` and scripts enabled, each package in its own clean directory: root, `cli/` and `kanban/` all exit 0 with the lockfile unchanged. One measurement to distrust and the reason it is not here: `cli/` first failed with `ERR_PNPM_EXECUTOR_LIFECYCLE_SCRIPT_FAILED` in a bare directory, which is `lefthook install` finding no `.git` — the sandbox's fault, not the tree's. Run again in a directory with one, it passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
The lefthook hook installed kanban with `--ignore-workspace`, added so pnpm would not resolve that folder to the repository root, find it lists no members, report "Already up to date" and install nothing. `kanban/pnpm-workspace.yaml` now stops that upward search by itself — the whole reason the file exists, as its own comment says — and the flag had turned harmful: it discards that same file, and with it the `allowBuilds` entry the commit before this one added. So the one job that runs the hooks failed on the build pnpm 12 refuses to run unasked, while the standalone kanban job passed. Measured in the real layout rather than reasoned about: a root workspace above, kanban's own file present, `pnpm@12.3.4`, `--frozen-lockfile`. without --ignore-workspace exit 0 with --ignore-workspace exit 1, ERR_PNPM_IGNORED_BUILDS Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
`validate-yaml.mjs` called `load`, which refuses a stream carrying several documents: "expected a single document in the stream, but found more". pnpm 12 writes exactly that shape — a lockfile whose first document carries `packageManagerDependencies` and whose second carries the lockfile itself — so the hook reported all three regenerated lockfiles as broken YAML. They are not: a multi-document stream is valid YAML, and `load` was the wrong function for a check whose whole job is syntax. `loadAll` accepts them and still refuses what is actually malformed — verified both ways: the three lockfiles pass, and `a: [1,` still fails on its own indentation. Found only in CI, and the reason is worth writing down: run with no arguments, this script validates nothing and prints "passed for 0 file(s)". The hook hands it the staged files; a local run without them is green whatever the tree holds, which is how three broken-looking lockfiles reached a pull request after a local check said yes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
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 broke
validate.ymlactivatespnpm@latest. pnpm 12.3.4 landed on the runners today and removed the=falsevalue form of--frozen-lockfile, so the lefthook job now stops at its own install step:The job never reaches the hooks it exists to run, so every pull request fails that check from now on, whatever it changes. The three merged today passed it hours earlier, under pnpm 11.
Found on #781, whose diff is markdown and one test file — nothing that could touch an install.
The fix
--no-frozen-lockfileis the same instruction in the form both majors accept: pnpm 11 documents the flag as--[no-]frozen-lockfile, and pnpm 12's own error says the flag takes no value.One line. No behaviour change: the install still refuses to fail on an out-of-date lockfile, which is what
=falseasked for.Named, not fixed
Nine other workflow steps also run
corepack prepare pnpm@latest --activate, so the next major arrives the same way — unannounced, on a green branch. Pinning the version is a decision about what the team runs locally, not a CI detail, so it is stated here rather than made.🤖 Generated with Claude Code
https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
Second commit: one pinned version, and the overrides pnpm 12 stopped reading
Fixing the flag unblocks CI. It does not stop the same thing happening again, and it leaves the tree disagreeing with itself about which pnpm it runs.
The tree did not agree with itself
cli/pnpm@10.14.0kanban/pnpm@10.14.0Ten workflow steps ran
corepack prepare pnpm@latest --activate, so one CI run could resolve the root under 12 andcli/under 10.packageManagernow namespnpm@12.3.4in all three, and every workflow step stops atcorepack enable— corepack reads the pin instead of choosing. One version, written down once; the next major arrives when somebody edits that line.The overrides pnpm 12 silently dropped
cli/kept four security floors there. Ignored by pnpm 12 while the lockfile still recorded them — which is theERR_PNPM_LOCKFILE_CONFIG_MISMATCHeverycliinstall answered with under 12. They move tocli/pnpm-workspace.yaml, their new home, unchanged:fast-uri >=3.1.2,picomatch >=4.0.4,postcss >=8.5.10,qs >=6.15.2.Worth stating plainly: under pnpm 12 those four floors were not being applied at all. This restores them.
The lockfiles
cli/kanban/Only the
packageManagerDependenciesblock pnpm 12 records for a pinned version. No dependency re-resolved, nothing removed. A pin cannot be added without it:--frozen-lockfilerefuses to write that block itself, which is whatERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILEwas saying.Verified by running it
Every package,
pnpm@12.3.4,--frozen-lockfile:3,471 CLI tests, 371 repository script tests, biome
ciclean (2 pre-existing warnings), typecheck clean, bundle 595.7 / 598 KB, 0 broken links in 798 files.One thing reviewers should know
Pinning
packageManagermeans a contributor whosepnpmis a plain binary rather than a corepack shim will now seepnpm installtry to fetch 12.3.4. That is the point of a pin, and it is the first time this repository has had one at the root.