Skip to content

build: pnpm 12.14.0 - #1442

Merged
Stvad merged 3 commits into
masterfrom
claude/laughing-williams-13ef78
Oct 24, 2026
Merged

Stvad merged 3 commits into
masterfrom
claude/laughing-williams-13ef78

Conversation

@Stvad

@Stvad Stvad commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Moves the repo from pnpm 10.34.5 to pnpm 12.14.0. 12.14.0 is 8 days old, so it has cleared the repo's 7-day cooldown; 12.15.0 clears it on 2026-10-28. This is dev tooling only: no dependency changes, and nothing in the app or user data is affected.

What changed

  • package.json: packageManager: pnpm@12.14.0.
  • pnpm-workspace.yaml:
    • onlyBuiltDependencies (removed in pnpm 11) becomes allowBuilds: {better-sqlite3: true, '@journeyapps/wa-sqlite': true}.
      • Builds are strict now (strictDepBuilds defaults to true): a dependency with an install script that isn't listed fails the install. These two are the only ones in the tree, and the pnpm 12 install raised nothing else.
      • pnpm silently ignores the old onlyBuiltDependencies key: it gives no error or warning, and its list isn't applied. The comment says so, so it doesn't creep back in.
    • verifyDepsBeforeRun: warn. See below.
    • Every other key is a valid pnpm 12 setting: linkWorkspacePackages, minimumReleaseAge, overrides. When the pinned version is the one running, pnpm 12 fails the command on an unknown key (ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS); I checked that with a misspelt key.
  • agent-extensions/social-media-publisher (installed on its own, outside the workspace):
    • It gets allowBuilds: {core-js: false}. core-js's postinstall only prints a funding banner, and the global pnpm 11 already failed the install without this entry.
    • It also gets its own packageManager: pnpm@12.14.0. Before, it had no pin, so it ran whatever pnpm was global: 11.17.0 here, the root's pin in CI. Its pnpm-workspace.yaml says to bump the two pins together.
  • pnpm-lock.yaml (both):
    • pnpm 11+ starts the lockfile with a new YAML document recording the resolved package manager: packageManagerDependencies, with pnpm and @pnpm/exe 12.14.0 and per-platform integrity.
    • Each diff is that document only: one hunk at the top, 183 lines added, 0 removed. No dependency version moved, and pnpm skipped resolution ("Lockfile is up to date").
  • .github/workflows/*: pnpm/action-setup@v4 → @v6.1.0 in all 9 places.
  • scripts/ensure-deps.mjs: fixes a real break, and drops a flag pnpm 12 no longer needs. Both are described below.

The SessionStart hook would have reinstalled at every session start

ensure-deps.mjs treats node_modules/.pnpm/lock.yaml as the install record and compares it byte for byte with pnpm-lock.yaml. Under pnpm 12, the record is the lockfile minus its new leading package-manager document; tail -n +184 pnpm-lock.yaml is byte-identical to it.

  • Unfixed, every checkout would read as mismatch. Every session would then reinstall and rebuild the agent packages.
  • The hook's old advice for a lockfile it can't match was to copy the record over the lockfile. That would now delete the leading document from the committed lockfile.

The fix:

  • The hook strips a leading ---…--- document before comparing. Two new readDepsState cases cover it.
  • The advice is now pnpm install --lockfile-only --force. Tested on three kinds of malformed lockfile: it rewrites each one to the exact bytes pnpm writes, without re-resolving. A plain install, or --lockfile-only without --force, says "Already up to date" and leaves the file alone.
  • The comparison is the same in both directions across the 10/12 boundary. On a pnpm-10 branch, the record still equals the lockfile.

--config.confirmModulesPurge=false is gone from the shared install (FROZEN_INSTALL_ARGS) and from AGENTS.md:

  • pnpm 10 needed it, because an install that had to recreate node_modules aborted without a TTY.
  • Under 12.14.0, frozen and plain installs alike recreate the tree with no prompt and exit 0 with no TTY. I tried three triggers: a pnpm-10 node_modules (store v10, layout from 10.34.5), a publicHoistPattern change and a virtualStoreDir change.
  • Branches from before this PR keep their own copy of the hook, with the flag and pnpm 10.

verifyDepsBeforeRun: warn, not the new default install

In pnpm 11+, pnpm run, pnpm exec and implicit bins (pnpm vitest) first check whether node_modules matches the lockfile. By default, when it doesn't, they install. Outside CI, that install is not frozen.

  • I removed a dependency from package.json, then ran pnpm run dev:who. pnpm first rewrote pnpm-lock.yaml, pruned node_modules and took about 4s. Its output went to stderr.
  • Under CI=true the same install is frozen: it fails on the outdated lockfile and leaves it untouched.

That clashes with how the repo reconciles node_modules:

  • Two owners already do it, and both are frozen: the SessionStart hook and the gate's first task. The default would add a third, implicit owner that is not frozen.
  • pnpm run check would run that install before the gate's own frozen install. A branch whose lockfile was never updated would get it rewritten silently. Today, the gate's frozen install fails on it.
  • Several agents can share one checkout, and any of them could trigger an install there.

warn keeps pnpm 10's rule of never installing implicitly. It adds one stderr line when node_modules really is out of sync: [WARN] Your node_modules are out of sync with your lockfile. <reason>.

  • An mtime-only touch of package.json and the lockfile produced no warning, and the lockfile stayed untouched.
  • Its cost compared with false was lost in measurement noise on a loaded machine, so treat it as unmeasured.
  • It covers the case in Nothing surfaces a node_modules stale relative to pnpm-lock.yaml #663, where a merge moves the lockfile and a single pnpm vitest run quietly uses the stale install. With vitest 4.1.12 installed and 5.0.5 locked, pnpm vitest, pnpm exec vitest and pnpm run test each warned (pnpm vitest warns twice), then ran 4.1.12. The hook then reinstalled, and the checkout read fresh. Calling the binary directly (npx vitest, node_modules/.bin/vitest) still gives no warning.
  • AGENTS.md now says the warning means pnpm install --frozen-lockfile, with two exceptions:
    • A warning whose reason says a manifest changed needs pnpm install, because the frozen install fails on it.
    • A worktree whose node_modules is a symlink to another checkout's always warns: its packages/*/node_modules are missing. Installing there writes through the symlink, so never do it.

Behaviour changes agents will notice

  • Script header. pnpm run x prints $ <command> on stderr, not > pkg@version x /path plus > <command>. Stdout carries only the script's output. Nothing in scripts/ parses that header. The publish read-back verifier matches URLs, and its pr:reply fixture now uses the new form.
  • The drift warning on existing worktrees. After this lands, an existing worktree keeps its pnpm-10 node_modules: the hook still reads it as fresh, because the dependency document is unchanged. Until something reinstalls it, every pnpm run warns The value of the allowBuilds setting has changed. The first pnpm run check, or pnpm install --frozen-lockfile, fixes that.
  • Store v11. That first reinstall on each machine moves to ~/Library/pnpm/store/v11. It downloads whatever the new store lacks: 647 of 713 packages, about 16s, the first time here. The old store/v10 can be pruned by hand later.
  • Strict builds. A new dependency with an install script fails pnpm install until it is listed under allowBuilds, as true or false.
    • Under pnpm 12 without a terminal, which is the agent case, the install just fails with ERR_PNPM_IGNORED_BUILDS. An interactive install also writes a placeholder entry into pnpm-workspace.yaml, and so does the global pnpm 11 even without a terminal (seen in the publisher before it had a pin). Replace the placeholder; don't commit it.
    • pnpm still writes its install record when the install fails this way. The hook therefore reports the failure once and reads fresh afterwards, while the gate's install keeps failing. That's noted at readDepsState. CI's frozen install fails on such a commit too.
  • The global pnpm writes the lockfile. Any pnpm command run in a checkout makes the global pnpm (11.17.0 here) switch to the pinned 12.14.0. It records that switch in the lockfile's package-manager document. A branch pinned to 10 gets no such document.
  • --frozen-lockfile false is rejected in pnpm 12; use --no-frozen-lockfile. Nothing in the repo uses it.
  • One-time reshaping of ajv-formats in the lockfile. pnpm install --fix-lockfile changes the ajv-formats@3.0.1 entry, and probably so will the first install that really re-resolves, such as a pnpm add (not tested). No version changes:
    • its recorded optional ajv peer (peerDependencies/peerDependenciesMeta) disappears;
    • the snapshot loses its (ajv@8.20.0) suffix;
    • ajv moves from optionalDependencies to dependencies.
      If an unrelated ajv-formats hunk shows up in a later dependency PR, this is where it comes from.

Verified

  • Lockfiles: under 12.14.0, pnpm install regenerated both with no dependency changes (diffs above). The root lockfile has 0 git or tarball resolutions, so the new blockExoticSubdeps default has nothing to block.
  • pnpm pack still rewrites workspace:^ to ^0.8.0 in agent-dispatch's tarball. For both packages, the file lists and manifests match pnpm 10.34.5's, apart from key order. So the publish job's pinned npm 11.18.0, whose comment says to re-check it when pnpm changes, is unaffected.
  • minimumReleaseAge: 10080 stays strict. minimumReleaseAgeStrict defaults to true when the age is set explicitly; tested with no global config, an immature version fails the install.
  • The repo doesn't use any of these pnpm 11/12 removals: npm_config_* reads, a tracked .npmrc, a pnpm field in package.json, script names that collide with pnpm's new built-ins, hidden scripts. INIT_CWD and npm_lifecycle_event, which scripts/pr-reply.mjs reads, are still set inside scripts.
  • pnpm run check: check: PASS, 834 files and 12,293 tests (master's 12,291 plus the 2 new hook cases). Run on the final head.
  • pnpm run build passes, including check-dist-exports.
  • The extensions, in CI's order (after pnpm agent types --force), both pass:
    • pnpm -C agent-extensions/social-media-publisher run check: 6 tests.
    • pnpm -C agent-extensions/strength-tracker run check: 264 + 150 tests.

Not covered

  • Workflows that didn't run on this PR: deploy-pages, publish-packages, fuzz-nightly, supabase-health, attachments-ciphertext-audit, approved-pr-preview. They use only the same pnpm commands (install --frozen-lockfile, run …, pack).
  • A frozen install with no network when pnpm's new lockfile supply-chain check misses its cache.
  • pnpm 12's fallback that puts the store in node_modules/.pnpm-store when no parent directory accepts hard links (a sandbox case).
  • The 11.x and 12.x minor release notes: I searched them by keyword rather than reading each one.

🤖 Generated with Claude Code

Stvad and others added 2 commits October 23, 2026 23:44
…document

pnpm 11+ leads pnpm-lock.yaml with a YAML document recording the
resolved package manager (packageManagerDependencies) and leaves that
document out of node_modules/.pnpm/lock.yaml. Byte-comparing the two
read every pnpm 12 checkout as a mismatch, so the SessionStart hook
would reinstall and rebuild the agent packages at every session start.
The comparison now drops a leading `---` document first; a lockfile
without one (pnpm 10) compares as before.

The advice for a lockfile no install can match was to copy the record
over it, which would now delete that document. `pnpm install
--lockfile-only --force` rewrites it in pnpm's own form instead
(checked under 12.14.0; a plain install leaves it as committed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- packageManager 10.34.5 -> 12.14.0, in the root and (new) in the
  standalone social-media-publisher, which had no pin and ran whatever
  pnpm was global.
- onlyBuiltDependencies (removed in pnpm 11) -> allowBuilds. Builds
  are strict now: an unlisted install script fails the install. The
  publisher denies core-js's (a funding banner).
- verifyDepsBeforeRun: warn. The pnpm 11+ default, `install`, runs a
  non-frozen install before any `pnpm run`/`exec` that rewrites
  pnpm-lock.yaml from package.json; node_modules already has two frozen
  owners (the SessionStart hook and the gate's first task).
- lockfiles: only the new leading package-manager document is added;
  no dependency version changes.
- pnpm/action-setup v4 -> v6.1.0, the first release supporting pnpm 12
  (the floating v6 tag predates it).
- The pr:reply fixture uses pnpm 12's `$ command` script header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T17:21:32.796518Z b3c1f2f New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

- FROZEN_INSTALL_ARGS loses --config.confirmModulesPurge=false. Under
  pnpm 12.14.0 an install that has to recreate node_modules (a pnpm-10
  layout, a hoist or virtualStoreDir change) no longer prompts or aborts
  without a TTY, frozen or plain. Branches before the pnpm 12 move keep
  their own copy of the hook with the flag.
- AGENTS.md: the out-of-sync warning means reinstall frozen, except when
  its reason is a changed manifest (the frozen install fails there;
  `pnpm install` updates the lockfile) and in a worktree whose
  node_modules is a symlink, which always warns and must never install.
- pnpm-workspace.yaml: the default `install` mode is non-frozen only
  outside CI; the removed onlyBuiltDependencies key is silently ignored.
- readDepsState: note that pnpm writes the install record even when the
  install fails on an unlisted build script, so the hook reports that
  failure once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Stvad
Stvad merged commit 9624bf0 into master Oct 24, 2026
3 checks passed
@Stvad
Stvad deleted the claude/laughing-williams-13ef78 branch October 24, 2026 18:16
Stvad added a commit that referenced this pull request Oct 25, 2026
#1444 replaced the esm.sh importmap with same-origin facades in
`src/vendor/`, so the "hold React while reactImportMapMode.ts exists"
step is gone. A React bump is now taken like any other. If it changes
React's exports or browser subpaths, `src/vendor/vendorEntries.test.ts`
fails, and the step points at updating the facades to the set it names.

Also from master: @journeyapps/wa-sqlite is no longer a direct
dependency (PowerSync 2), so it leaves the hold list. The standalone
extension pins its own pnpm (#1442), so a pnpm bump updates both pins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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