diff --git a/.claude/rules/tooling.md b/.claude/rules/tooling.md index c00df7ab..57386ecb 100644 --- a/.claude/rules/tooling.md +++ b/.claude/rules/tooling.md @@ -22,7 +22,7 @@ paths: ## Dependencies - Node comes from `engines`, pnpm from `packageManager` (`package.json`). `.npmrc` sets `save-exact=true` — pin exact versions. -- `pnpm-workspace.yaml` sets `minimumReleaseAge: 10080`, so pnpm **refuses versions published less than 7 days ago** — a failing `pnpm add` of a fresh release is expected, not a registry fault. To take a fresh release on purpose, list the package under `minimumReleaseAgeExclude` there and remove the entry once the release is a week old (`effect`, `@effect/vitest`, `@effect/openapi-generator` and the `@effect/platform-node*` peers pnpm auto-installs for the generator, all 4.0.1 published 2026-10-04/05, removable from 2026-10-12 — an auto-installed peer under the gate fails `pnpm install --frozen-lockfile` just like a direct dependency; check a publish date with `npm view time --json` and read the version key — `npm view @ time.` prints nothing). `allowBuilds` disables install scripts for esbuild, workerd, sharp and msgpackr-extract. +- `pnpm-workspace.yaml` sets `minimumReleaseAge: 10080`, so pnpm **refuses versions published less than 7 days ago** — a failing `pnpm add` of a fresh release is expected, not a registry fault. To take a fresh release on purpose, list the package under `minimumReleaseAgeExclude` there and remove the entry once the release is a week old (`effect`, `@effect/vitest`, `@effect/openapi-generator` and the `@effect/platform-node*` peers pnpm auto-installs for the generator, all 4.0.1 published 2026-10-04/05, removable from 2026-10-12 — an auto-installed peer under the gate fails `pnpm install --frozen-lockfile` just like a direct dependency; check a publish date with `npm view time --json` and read the version key — `npm view @ time.` prints nothing). `allowBuilds` disables install scripts for esbuild, workerd, sharp and msgpackr-extract. `pmOnFail: ignore` is for Dependabot (see the workflows rule), so pnpm won't switch itself to `packageManager`'s version: install it yourself (the devcontainer pins it in `pnpmVersion`, CI via `pnpm/action-setup`). - **Never bump `undici` across a major.** `setGlobalDispatcher()` reaches Node's built-in `fetch` only through a versioned global symbol (`Symbol.for('undici.globalDispatcher.1')` on Node 24). A different undici major can register under a different symbol — undici 8 did — and then `MockAgent` silently stops intercepting and tests hit the real network. Check `process.versions.undici` and raise the major only when Node's bundled one does (Dependabot ignores its majors). - Bump coupled packages together: `effect` + `@effect/vitest` + `@effect/openapi-generator` (both peer on `^`; run `pnpm run codegen:cloudflare` after, and `@effect/vitest` also pins one vitest major — `>=5 <6` since rc.113 — so an `effect` bump can force a vitest major; check with `npm view @effect/vitest@ peerDependencies` and `pnpm peers check`); `vitest` + `@vitest/coverage-v8` (it peers on the exact vitest version); `@effect/tsgo` + `oxlint` + `oxlint-tsgolint` (the patch is version-coupled — re-run `pnpm install` after). Dependabot's `effect`, `vitest` and `ox` groups do this. Release notes: `node_modules/effect` ships no CHANGELOG — read `repos/effect/packages//CHANGELOG.md` after `pnpm run sync:effect`; for `@effect/tsgo` there is no changelog either, so compare tags on GitHub (`gh api 'repos/Effect-TS/tsgo/compare/@effect/tsgo@...@effect/tsgo@'`). A bump of a runtime dependency (`effect`, `@actions/core`) changes `dist/`, so rebuild and commit it with the bump. - **TypeScript 7 only.** `typescript` is the Go-native 7.x: it ships the `tsc` binary (`tsc:check`, `tsc:ls`, the LSP, CI) but no `import ts from 'typescript'` compiler API (7.0 exports only `typescript/unstable/*`). Don't add a dev tool that needs that API — check its `peerDependencies` and that it runs — and don't reinstall TypeScript 5/6 beside it. `graphql-codegen` (loading `graphql.config.ts`), `knip` (oxc-parser), `tsx`, `@effect/openapi-generator` and `oxlint-tsgolint` run without it. `openapi-typescript` needed it and is discontinued (openapi-ts/openapi-typescript#2874, which also covers `openapi-fetch`); `@effect/openapi-generator` replaced both. diff --git a/.claude/rules/workflows.md b/.claude/rules/workflows.md index f5099496..4407d76f 100644 --- a/.claude/rules/workflows.md +++ b/.claude/rules/workflows.md @@ -35,5 +35,6 @@ paths: - The github-actions entry groups `actions/*` + `github/*` and `changesets/action*` at every level, and everything else at minor/patch; other third-party majors stay individual PRs. Sub-actions of one repo (`changesets/action/pr-status`, `github/codeql-action/init`) are separate dependencies to Dependabot, so a repo used that way needs a `/*` group or every sub-action gets its own PR (#893–#895). First matching group wins, so keep the `*` catch-all last. `.github/workflow-templates/` isn't scanned (only `.github/workflows/` and `action.yml`), so its pins are `pnpm run sync:readme`'s job. - `undici` majors are ignored on purpose — see the tooling rule. - A `wrangler` (cloudflare group) PR only bumps `package.json` and the lockfile, so its `test` check fails until the regenerated files are pushed to the Dependabot branch as a signed commit (#901). Follow `wrangler-upgrade.md` — its first step is that regeneration. +- pnpm 12 is supported by the updater since 2026-09-15 (dependabot-core#16169, #16170; GitHub changelog 2026-10-20), but GitHub's dependency graph still reads only the first document of a two-document `pnpm-lock.yaml` (#15904, open) — verified 2026-10-20 against a public pnpm 12 repo, whose lockfile the graph credits with pnpm's 15 own binaries and none of its 2016 project packages — which silences alerts and security PRs. `pmOnFail: ignore` in `pnpm-workspace.yaml` keeps the lockfile single-document (`warn` and `error` don't), so don't remove it until that issue closes; the cost is that pnpm no longer switches itself to `packageManager`'s version. And a security update of a transitive dependency locked on more than one major (`undici` is 6/7/8 here) fails with "resolved above the requested version" (#16434, open) — bump it by hand, as with `undici` already. - The npm `cooldown` must be at least `minimumReleaseAge` in `pnpm-workspace.yaml` (7 days). Dependabot picks the version with its own cooldown, then runs `pnpm install --lockfile-only`, and pnpm rejects anything younger than `minimumReleaseAge` with `ERR_PNPM_NO_MATURE_MATCHING_VERSION`, failing that dependency (2026-09-14, `skills` under a 3-day patch cooldown). - `exclude-paths: ['repos/**']` stops version updates for the vendored source. It doesn't affect alerts (the dependency graph still reads `repos/effect/pnpm-lock.yaml`) nor the security-update PRs they raise; the separate `/repos/*` npm entry with `ignore: dependency-name: '*'` stops those, because `ignore` applies to security updates and `exclude-paths` doesn't (options reference, 2026-09-13). The alerts themselves still show under Security unless a repo-level auto-triage rule dismisses them by manifest path (see the repos rule). diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d7026bc3..8e5ad504 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,7 @@ // Node 24 matches CI (.github/actions/setup-pnpm); pnpm matches packageManager. "ghcr.io/devcontainers/features/node:2.1.1": { "version": "24", - "pnpmVersion": "11.22.0" + "pnpmVersion": "12.14.0" }, // Extensions install at image build as the remote user. The option can't // pin a version, so each rebuild pulls the extension's latest release. diff --git a/package.json b/package.json index e0890025..f0510dd1 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ }, "engines": { "node": "^24.16.0", - "pnpm": "^11.22.0" + "pnpm": "^12.14.0" }, - "packageManager": "pnpm@11.22.0" + "packageManager": "pnpm@12.14.0" } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a5344c8d..c0c4e2d2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,3 +4,7 @@ allowBuilds: sharp: false workerd: false minimumReleaseAge: 10080 +# Keeps pnpm-lock.yaml a single YAML document: with the default `download`, +# pnpm 12 prepends a boot document that GitHub's dependency graph reads as an +# empty lockfile, so alerts and security PRs stop (dependabot-core#15904). +pmOnFail: ignore