Skip to content

ci: the tree runs on one pinned pnpm 12 - #782

Merged
blafourcade merged 5 commits into
nextfrom
fix/ci-installs-under-pnpm-12
Sep 6, 2026
Merged

ci: the tree runs on one pinned pnpm 12#782
blafourcade merged 5 commits into
nextfrom
fix/ci-installs-under-pnpm-12

Conversation

@blafourcade

@blafourcade blafourcade commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

What broke

validate.yml activates pnpm@latest. pnpm 12.3.4 landed on the runners today and removed the =false value form of --frozen-lockfile, so the lefthook job now stops at its own install step:

error: unexpected value 'false' for '--frozen-lockfile' found; no more were expected
Usage: pnpm install --frozen-lockfile

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-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.

One line. No behaviour change: the install still refuses to fail on an out-of-date lockfile, which is what =false asked 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

pinned to
repository root nothing
cli/ pnpm@10.14.0
kanban/ pnpm@10.14.0

Ten workflow steps ran corepack prepare pnpm@latest --activate, so one CI 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 instead of choosing. One version, written down once; the next major arrives when somebody edits that line.

The overrides pnpm 12 silently dropped

[WARN] The "pnpm" field in package.json is no longer read by pnpm.
       The following keys were ignored: "pnpm.overrides".

cli/ kept four security floors there. Ignored by pnpm 12 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.

Worth stating plainly: under pnpm 12 those four floors were not being applied at all. This restores them.

The lockfiles

before after delta
root 746 847 +101 / −0
cli/ 5,306 5,407 +101 / −0
kanban/ 1,798 1,899 +101 / −0

Only the packageManagerDependencies block pnpm 12 records for a pinned version. No dependency re-resolved, nothing removed. A pin cannot be added without it: --frozen-lockfile refuses to write that block itself, which is what ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE was saying.

Verified by running 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.

One thing reviewers should know

Pinning packageManager means a contributor whose pnpm is a plain binary rather than a corepack shim will now see pnpm install try 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.

`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
@blafourcade
blafourcade requested a review from a team as a code owner September 5, 2026 18:58
…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
@blafourcade blafourcade changed the title ci: install under pnpm 12, which dropped --frozen-lockfile=false ci: the tree runs on one pinned pnpm 12 Sep 5, 2026
reference-week and others added 3 commits September 5, 2026 21:18
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
@blafourcade
blafourcade enabled auto-merge (squash) September 6, 2026 02:41
@blafourcade
blafourcade merged commit 1c309b8 into next Sep 6, 2026
17 checks passed
@blafourcade
blafourcade deleted the fix/ci-installs-under-pnpm-12 branch September 6, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant