From 8a0f24e5b2d5de6a9549b8966c16f644da5d6d8a Mon Sep 17 00:00:00 2001 From: reference-week Date: Sat, 5 Sep 2026 20:57:59 +0200 Subject: [PATCH 1/5] ci: install under pnpm 12, which dropped --frozen-lockfile=false MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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) Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 6cc3c7f02..03a46e678 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -42,7 +42,7 @@ jobs: restore-keys: pnpm-${{ runner.os }}- - name: Install dependencies - run: pnpm install --frozen-lockfile=false --ignore-scripts + run: pnpm install --no-frozen-lockfile --ignore-scripts - name: Install cli dependencies # cli/ is a self-contained project (own lockfile, not a pnpm workspace From c187dc9941c20acc2dd02f9df986e503c2f79180 Mon Sep 17 00:00:00 2001 From: reference-week Date: Sat, 5 Sep 2026 21:14:04 +0200 Subject: [PATCH 2/5] ci: one pinned pnpm for the whole tree, and the overrides pnpm 12 stopped reading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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) Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp --- .github/workflows/ci.yml | 1 - .github/workflows/cli-ci.yml | 9 --- .github/workflows/validate.yml | 1 - cli/package.json | 10 +--- cli/pnpm-lock.yaml | 101 +++++++++++++++++++++++++++++++++ cli/pnpm-workspace.yaml | 10 ++++ kanban/package.json | 2 +- kanban/pnpm-lock.yaml | 101 +++++++++++++++++++++++++++++++++ package.json | 1 + pnpm-lock.yaml | 101 +++++++++++++++++++++++++++++++++ 10 files changed, 316 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd90271c3..6585d8b8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -252,7 +252,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" diff --git a/.github/workflows/cli-ci.yml b/.github/workflows/cli-ci.yml index da3961e8c..18584c91e 100644 --- a/.github/workflows/cli-ci.yml +++ b/.github/workflows/cli-ci.yml @@ -82,7 +82,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -98,7 +97,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -115,7 +113,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -131,7 +128,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -148,7 +144,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -164,7 +159,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -191,7 +185,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -206,7 +199,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" @@ -228,7 +220,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "22" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 03a46e678..6f3bd3566 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -32,7 +32,6 @@ jobs: - name: Install pnpm run: | corepack enable - corepack prepare pnpm@latest --activate - name: Cache pnpm store uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 diff --git a/cli/package.json b/cli/package.json index a9b00d0cd..be97ab781 100644 --- a/cli/package.json +++ b/cli/package.json @@ -35,15 +35,7 @@ "engines": { "node": ">=22.12" }, - "packageManager": "pnpm@10.14.0", - "pnpm": { - "overrides": { - "fast-uri": ">=3.1.2", - "picomatch": ">=4.0.4", - "postcss": ">=8.5.10", - "qs": ">=6.15.2" - } - }, + "packageManager": "pnpm@12.3.4", "bundleBudgetKB": 598, "scripts": { "build": "tsup && node scripts/check-bundle-size.mjs", diff --git a/cli/pnpm-lock.yaml b/cli/pnpm-lock.yaml index ad13873fc..1a4e96127 100644 --- a/cli/pnpm-lock.yaml +++ b/cli/pnpm-lock.yaml @@ -1,3 +1,104 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.3.4 + version: 12.3.4 + +packages: + + '@pnpm/exe.darwin-arm64@12.3.4': + resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.3.4': + resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.linux-arm64-musl@12.3.4': + resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.3.4': + resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.3.4': + resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.3.4': + resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.3.4': + resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.3.4': + resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==} + cpu: [x64] + os: [win32] + + pnpm@12.3.4: + resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.darwin-arm64@12.3.4': + optional: true + + '@pnpm/exe.darwin-x64@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64@12.3.4': + optional: true + + '@pnpm/exe.linux-x64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-x64@12.3.4': + optional: true + + '@pnpm/exe.win32-arm64@12.3.4': + optional: true + + '@pnpm/exe.win32-x64@12.3.4': + optional: true + + pnpm@12.3.4: + optionalDependencies: + '@pnpm/exe.darwin-arm64': 12.3.4 + '@pnpm/exe.darwin-x64': 12.3.4 + '@pnpm/exe.linux-arm64': 12.3.4 + '@pnpm/exe.linux-arm64-musl': 12.3.4 + '@pnpm/exe.linux-x64': 12.3.4 + '@pnpm/exe.linux-x64-musl': 12.3.4 + '@pnpm/exe.win32-arm64': 12.3.4 + '@pnpm/exe.win32-x64': 12.3.4 + +--- lockfileVersion: '9.0' settings: diff --git a/cli/pnpm-workspace.yaml b/cli/pnpm-workspace.yaml index 96039eab1..e2b7ab287 100644 --- a/cli/pnpm-workspace.yaml +++ b/cli/pnpm-workspace.yaml @@ -5,3 +5,13 @@ # # Empty on purpose - cli/ has its own lockfile and installs alone. packages: [] + +# Moved here from `package.json`'s own `pnpm` field, which pnpm 12 stopped reading: +# "The 'pnpm' field in package.json is no longer read by pnpm". Left there, these four +# security floors were silently ignored while the lockfile still recorded them, which is +# the ERR_PNPM_LOCKFILE_CONFIG_MISMATCH every `cli` install answered with. +overrides: + fast-uri: '>=3.1.2' + picomatch: '>=4.0.4' + postcss: '>=8.5.10' + qs: '>=6.15.2' diff --git a/kanban/package.json b/kanban/package.json index 9af64b776..76b8d5456 100644 --- a/kanban/package.json +++ b/kanban/package.json @@ -6,7 +6,7 @@ "engines": { "node": ">=22.12" }, - "packageManager": "pnpm@10.14.0", + "packageManager": "pnpm@12.3.4", "scripts": { "test": "vitest run", "test:watch": "vitest", diff --git a/kanban/pnpm-lock.yaml b/kanban/pnpm-lock.yaml index e3ec3e5e2..af74a64ac 100644 --- a/kanban/pnpm-lock.yaml +++ b/kanban/pnpm-lock.yaml @@ -1,3 +1,104 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.3.4 + version: 12.3.4 + +packages: + + '@pnpm/exe.darwin-arm64@12.3.4': + resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.3.4': + resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.linux-arm64-musl@12.3.4': + resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.3.4': + resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.3.4': + resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.3.4': + resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.3.4': + resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.3.4': + resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==} + cpu: [x64] + os: [win32] + + pnpm@12.3.4: + resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.darwin-arm64@12.3.4': + optional: true + + '@pnpm/exe.darwin-x64@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64@12.3.4': + optional: true + + '@pnpm/exe.linux-x64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-x64@12.3.4': + optional: true + + '@pnpm/exe.win32-arm64@12.3.4': + optional: true + + '@pnpm/exe.win32-x64@12.3.4': + optional: true + + pnpm@12.3.4: + optionalDependencies: + '@pnpm/exe.darwin-arm64': 12.3.4 + '@pnpm/exe.darwin-x64': 12.3.4 + '@pnpm/exe.linux-arm64': 12.3.4 + '@pnpm/exe.linux-arm64-musl': 12.3.4 + '@pnpm/exe.linux-x64': 12.3.4 + '@pnpm/exe.linux-x64-musl': 12.3.4 + '@pnpm/exe.win32-arm64': 12.3.4 + '@pnpm/exe.win32-x64': 12.3.4 + +--- lockfileVersion: '9.0' settings: diff --git a/package.json b/package.json index b8415249e..17eba7439 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "sdlc-automation", "prompt-engineering" ], + "packageManager": "pnpm@12.3.4", "engines": { "node": ">=22.12" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8002bb9d4..24ff1ec21 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,3 +1,104 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.3.4 + version: 12.3.4 + +packages: + + '@pnpm/exe.darwin-arm64@12.3.4': + resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.3.4': + resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.linux-arm64-musl@12.3.4': + resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.3.4': + resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.3.4': + resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.3.4': + resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.3.4': + resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.3.4': + resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==} + cpu: [x64] + os: [win32] + + pnpm@12.3.4: + resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.darwin-arm64@12.3.4': + optional: true + + '@pnpm/exe.darwin-x64@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-arm64@12.3.4': + optional: true + + '@pnpm/exe.linux-x64-musl@12.3.4': + optional: true + + '@pnpm/exe.linux-x64@12.3.4': + optional: true + + '@pnpm/exe.win32-arm64@12.3.4': + optional: true + + '@pnpm/exe.win32-x64@12.3.4': + optional: true + + pnpm@12.3.4: + optionalDependencies: + '@pnpm/exe.darwin-arm64': 12.3.4 + '@pnpm/exe.darwin-x64': 12.3.4 + '@pnpm/exe.linux-arm64': 12.3.4 + '@pnpm/exe.linux-arm64-musl': 12.3.4 + '@pnpm/exe.linux-x64': 12.3.4 + '@pnpm/exe.linux-x64-musl': 12.3.4 + '@pnpm/exe.win32-arm64': 12.3.4 + '@pnpm/exe.win32-x64': 12.3.4 + +--- lockfileVersion: '9.0' settings: From 4eac3ef11ed6d96b1c919dc0074373e9e934aa83 Mon Sep 17 00:00:00 2001 From: reference-week Date: Sat, 5 Sep 2026 21:18:49 +0200 Subject: [PATCH 3/5] fix(ci): name the builds pnpm 12 refuses to run unasked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp --- cli/pnpm-workspace.yaml | 7 +++++++ kanban/pnpm-workspace.yaml | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/cli/pnpm-workspace.yaml b/cli/pnpm-workspace.yaml index e2b7ab287..b03326216 100644 --- a/cli/pnpm-workspace.yaml +++ b/cli/pnpm-workspace.yaml @@ -15,3 +15,10 @@ overrides: picomatch: '>=4.0.4' postcss: '>=8.5.10' qs: '>=6.15.2' + +# pnpm refuses a dependency's install scripts unless it is named here, and pnpm 12 makes +# that refusal a hard error rather than a warning: `ERR_PNPM_IGNORED_BUILDS`. Measured by +# running the install, never guessed - these are the packages pnpm 12 named itself. +allowBuilds: + esbuild: true + lefthook: true diff --git a/kanban/pnpm-workspace.yaml b/kanban/pnpm-workspace.yaml index 58105b003..b962c9299 100644 --- a/kanban/pnpm-workspace.yaml +++ b/kanban/pnpm-workspace.yaml @@ -1,3 +1,9 @@ # See cli/pnpm-workspace.yaml. This stops pnpm searching upward and treating the # repository root as this project's workspace. packages: [] + +# pnpm refuses a dependency's install scripts unless it is named here, and pnpm 12 makes +# that refusal a hard error rather than a warning: `ERR_PNPM_IGNORED_BUILDS`. Measured by +# running the install, never guessed - these are the packages pnpm 12 named itself. +allowBuilds: + esbuild: true From 6ac695fd90f00b8a1b7dd63680aca5e1a3ddfa37 Mon Sep 17 00:00:00 2001 From: reference-week Date: Sat, 5 Sep 2026 21:29:11 +0200 Subject: [PATCH 4/5] fix(ci): stop discarding the file that allows kanban's builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp --- lefthook.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lefthook.yml b/lefthook.yml index 121da7528..f48509960 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -162,13 +162,18 @@ pre-commit: cli-typecheck: # The CLI type-checks `kanban/` too, so that folder's dependencies must be # resolvable. Install them only when they are missing, to keep the hook fast. - # `--ignore-workspace` because the repository root declares one (for pnpm's - # build allowlist); without the flag pnpm resolves kanban to that root, finds - # it lists no members, reports "Already up to date" and installs nothing — - # leaving this step to fail on a sibling package it was meant to repair. + # + # `--ignore-workspace` was here so pnpm would not resolve kanban 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 on + # its own — that is the whole reason the file exists — and the flag had turned + # harmful: it discards that same file, and with it the `allowBuilds` entry pnpm + # needs to run esbuild's install script. Measured in the real layout under + # pnpm 12.3.4, a root workspace above and kanban's own file present: without the + # flag the install exits 0, with it, ERR_PNPM_IGNORED_BUILDS. glob: "{cli,kanban}/**" run: | - [ -d kanban/node_modules ] || (cd kanban && pnpm install --frozen-lockfile --ignore-workspace) + [ -d kanban/node_modules ] || (cd kanban && pnpm install --frozen-lockfile) cd cli && pnpm typecheck pre-push: From 877c954d40c63d9830cb5b624bd58cf4c04761b6 Mon Sep 17 00:00:00 2001 From: reference-week Date: Sat, 5 Sep 2026 21:39:50 +0200 Subject: [PATCH 5/5] fix(scripts): a YAML file may hold more than one document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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) Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp --- scripts/validate-yaml.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/validate-yaml.mjs b/scripts/validate-yaml.mjs index 043bcc4a0..57aab56a5 100755 --- a/scripts/validate-yaml.mjs +++ b/scripts/validate-yaml.mjs @@ -2,14 +2,20 @@ // Validates YAML syntax using the repository's Node dependency, avoiding Python in hooks. import { readFile } from "node:fs/promises"; -import { load } from "js-yaml"; +import { loadAll } from "js-yaml"; const files = process.argv.slice(2).filter((file) => file !== "--"); const errors = []; for (const file of files) { try { - load(await readFile(file, "utf8"), { filename: file }); + // `loadAll`, not `load`: a YAML stream may hold several documents, and `load` + // rejects one that does with "expected a single document in the stream". pnpm 12 + // writes exactly that shape - a lockfile whose first document carries + // `packageManagerDependencies` and whose second carries the lockfile itself - so + // `load` reported three valid files as broken. What this checks is syntax; how + // many documents a file holds is not a syntax error. + loadAll(await readFile(file, "utf8"), null, { filename: file }); } catch (error) { errors.push(`${file}: ${error.message}`); }