Add task concurrency groups to pnpm v12. A task in pnpm-workspace.yaml
names a `concurrencyGroup`, and `concurrencyGroups` gives each group a
limit: at most that many tasks of the group run at once on the machine,
counted across every pnpm process. A task past the limit waits for a
running one to finish and prints who holds the slots.
A group's pool is N slot files under `<stateDir>/run-slots/<group>/`. A
task takes an exclusive advisory lock on one in `run_stages` and holds it
for every stage of its script, so the single-project run, the recursive
run, and the tasks a `pnpm pipeline` runs in-process are all counted,
each project's task separately. The operating system releases the lock
when the holder ends, however it ends, so a killed holder never leaves a
stale slot. `pnpm exec` names no task and is not gated.
A holder lists its group in `PNPM_HELD_CONCURRENCY_GROUPS` for the
scripts it spawns. A nested `pnpm run` of a task in the same group runs
under the slot its parent holds, which is what keeps a script that calls
`pnpm run` from waiting on itself under a limit of one.
`concurrencyGroups` merges entry by entry across the configuration
layers, so a machine can lower one group's limit through
`PNPM_CONFIG_CONCURRENCY_GROUPS` without restating the rest.
Every process that is meant to be counted has to start as
`pnpm <script>`, so the root manifest gains scripts that wrap the just
recipes and node scripts (`test:rust`, `test:rust-affected`,
`test:rust-smoke`, `check:rust`, `lint:rust`, `ready:rust`,
`pre-push:rust`), the pre-push hook runs its Rust checks through one,
and the agent guide, the testing-changes skill, and the contributing
guide name the pnpm scripts first.
This repository's own task declarations wait for a pnpm release that
knows the field: the pinned CLI rejects an unknown task setting, so
adding `concurrencyGroup` to pnpm-workspace.yaml now would break every
command for it.
Pin pnpm's rustfmt fork and use a cached wrapper for local formatting,
CI, pre-push checks, and editor integration. The formatter has its own
dated nightly runtime while the workspace keeps its existing compiler.
Choose chain layout by call count and argument complexity, with a
40-column allowance for short expressions. Attach the first method when
the complete first line would otherwise end within the continuation
indentation. Preserve simple receivers,
cap intermediate leading accesses at column 80 and final accesses at
100, and separate fields and
await after wrapped methods. Apply the same rules in conditions.
Keep Max heuristics and compact struct literals, with an independent
35-column destructuring limit. Reformat pnpm and pnpr, extract only the
helpers and test modules needed for existing size limits, and fix macro
commas without relaxing lint rules.
Related to pnpm/pnpm#14562 and pnpm/pnpm#14862.
Declare CI pipelines and root task scripts, and invoke them from the
TypeScript, Rust, reusable test, and pnpr build workflows. Let pipelines
perform frozen installs and order TypeScript compilation before linting.
Keep existing test runners responsible for affected-package selection,
file-level sharding, and summary generation. Read shard settings from the
workflow environment, with explicit command-line arguments taking precedence.
Run root tasks with --include-workspace-root --full: the scripts live in the
root manifest only, and the test runners already select their own scope. Pass
--no-cache so a task that later declares outputs cannot skip a check.
Keep the dependency install in its own step in the two jobs whose commands are
timed for Bencher, so the reported duration stays a test duration. Preserve
existing job gates and artifact handoffs.
Move pnpm's version banding and lockstep off the .meta-updater hacks and onto
the integrated versioning engine, then restore the PR-time drift guard the move
removed.
- versioning.epics bands every pnpm11/ library major to the pnpm CLI's major;
versioning.fixed keeps [pnpm, @pnpm/exe, ...artifacts] in exact-version
lockstep. Both live in pnpm-workspace.yaml. Removes the
smallestAllowedLibVersion rewrite, the @pnpm/exe/artifact version forcing,
the EXPERIMENTAL_PKGS set, and the semver dependency from .meta-updater.
- Adds `pnpm change check` (both stacks): a static counterpart to
`pnpm change status` that validates committed versions against the epic bands
and fixed-group lockstep, failing with every violation listed. Backed by
checkVersioningInvariants / check_versioning_invariants in the versioning
engine. Wired into CI via the check:versioning lint step so drift is caught at
PR time, the way meta-updater --test used to catch it.
* chore: adopt workspace task scheduler
Run package tests through the workspace scheduler with a concurrency limit matched to Jest workers.
Keep the one-shot compile artifact as the build barrier so test jobs do not repeat builds.
Use workspace-relative Jest cache paths so packages with the same basename cannot share output.
* fix: keep Jest caches inside workspace cache directory
Reject project directories outside the workspace before constructing Jest cache paths.
The dependency update (pnpm/pnpm#14205) broke TS CI / Compile & Lint in
two independent ways.
streamx 2.28.1 declares the 'data' event payload as unknown, so the five
tar-stream data handlers in the releasing commands that annotated the
chunk as Buffer no longer typechecked. They now take the chunk as
unknown and cast at the use site; tar-stream emits Buffers at runtime,
so the emitted JavaScript is unchanged.
The Node.js v26.8.0 binaries on nodejs.org were built with a bogus
version string and self-report as 26.8.0-alpha.0.0.0 (Node.js shipped
26.8.1 the next day to correct it). That prerelease-looking version made
cspell's engine guard reject the runtime during lint and made node-gyp
request a nonexistent v26.8.0-alpha.0.0.0 headers tarball for native
builds. The pinned runtime is bumped to 26.8.1 in the root devEngines,
the compile-only script, the CI test matrix, and the benchmark and
release workflows.
Switch the fix target from `cargo clippy --fix` to `cargo fixit --clippy`
and pin `cargo-fixit@0.1.13` as a dev prerequisite. `cargo fixit` is faster
than `cargo clippy --fix` on repeated runs because it skips the full re-check
compile between fix rounds, so iterating on a lint cleanup doesn't rebuild
the workspace each pass. No fallback to `cargo clippy --fix`.
- justfile: add a `fix` recipe running
`cargo fixit --clippy --workspace --all-targets --allow-dirty --allow-staged`
- justfile: install `cargo-fixit@0.1.13` from source (`cargo install ... --locked`)
in `just init`; cargo-fixit has no prebuilt binstall binaries
- package.json: `fix:rust` now delegates to `just fix` (single source of truth,
drops the `cargo clippy --fix` invocation)
- CONTRIBUTING: document cargo-fixit in the `just init` tool list and the
`just fix` workflow
Add a `fix:rust` npm script that applies clippy's machine-applicable
autofixes across the Rust workspace via `cargo clippy --fix`, matching
the existing `build:pnpm` root invocation. Run on a dirty tree is
intended (--allow-dirty --allow-staged); review the diff before
committing.
The first pnpm 12 beta release used pnpm 12.0.0-alpha.21 to publish a wrapper whose workspace name is `pacquet`. That version predates `publishConfig.name`, so npm trusted publishing was attempted for `pacquet` instead of `pnpm` and the root package failed after all native packages had already been published.
Use pnpm 11.18.0, the released TypeScript CLI that supports `publishConfig.name`, for release tooling. Update every `pnpm/setup` consumer to the revision that can install v11 from GitHub release archives. Make the Rust publishing loop query each effective published name and skip versions already on npm, while preserving hard failures for registry errors other than 404. This allows a moved beta tag to resume the partial release and reach the dependent GitHub release job.
* chore(release): drop the packing workarounds now that pacquet 12.0.0-alpha.19 is pinned
The pinned pacquet now carries the fs-packlist fix from
https://github.com/pnpm/pnpm/pull/13231, so a files allowlist wins over
workspace-inherited ignore rules and the release workflow no longer
needs to neutralize them: remove the empty pnpm11/pnpm/.npmignore
override, the temporary root .gitignore shadow, and the pre-publish
payload verification step (with its script) that guarded against the
now-fixed empty-tarball packing bug. The payload-verify step was also
what broke the v11.17.0 release: its filtered pn list fired a
verifyDepsBeforeRun auto-install that pruned the excluded pnpm
project's node_modules, so the darwin-arm64 artifact's prepublishOnly
could no longer resolve symlink-dir.
`@pnpm/prepare` and `@pnpm/prepare-temp-dir` were the only
publishable packages without a files allowlist; their lib/ payload
survived packing only through the .gitignore shadow. Give them files
allowlists so their tarballs no longer depend on workspace ignore
state.
* chore: record the pacquet 12.0.0-alpha.19 pin in the lockfile
* chore: drop the changeset - both files-allowlist fixes ship without a new version
The pending `@pnpm/prepare` 1100.0.22 is not on npm yet, so the rerun
of the v11.17.0 release publishes it with the files allowlist already
in place. `@pnpm/prepare-temp-dir` 1100.0.1 is already published with
correct tarball content (packed under the .gitignore shadow), so its
manifest-only change needs no republish. A changeset would only
schedule a patch release whose sole delta is the manifest field.
The only consumer of concurrently was the prepareFixtures script of
`@pnpm/installing.deps-restorer`, and that script has been dead since
the external `@pnpm/registry-mock` package (which shipped the
registry-mock bin the script starts with) was replaced by the in-repo
pnpr server: it fails at 'registry-mock prepare' before concurrently is
ever invoked. The fixture lockfiles it used to regenerate are committed
to git, and the jest with-registry preset manages the registry server
lifecycle itself (child_process spawn + tree-kill), so nothing needs
concurrently's --kill-others/--success=first orchestration anymore.
Remove the unused root devDependency, the deps-restorer devDependency
together with its dead prepareFixtures/runPrepareFixtures scripts, and
the concurrently catalog entry. The root devDependency was not
referenced by any root script at all.
* chore: update lockfile, Node.js, and pnpm versions
* ci: force a cold pnpm store to confirm the Windows cache corruption
The Windows Rust CI install fails with ERROR_DIRECTORY (os error 267)
creating symlinks over a store restored from actions/cache. With
enableGlobalVirtualStore on, the cached store holds the global-virtual-store
`links/` reparse points, which tar can't round-trip, so the restored store
is corrupt and a warm install fails on symlink creation. The failure moves
between symlinks run to run (parallel linking picks a different victim),
which is why the alpha.12 symlink-writer fix could not cover it.
Bump the cache key to force a cold store. If Windows goes green, the cache
is confirmed as the cause and the durable fix is to stop caching `links/`.
* fix(fs): heal a dangling junction parent when materializing a symlink
alpha.12 tried to recover from a dangling `node_modules` junction (e.g. a
store restored from a tar-based CI cache) by keying on ERROR_DIRECTORY
(os error 267), but the real path is different: `CreateSymbolicLinkW`
through a dangling junction returns NotFound, and the NotFound recovery's
`create_dir_all` then fails with AlreadyExists (os error 183) because the
junction still occupies the slot. So the 267 branch never fired and the
install aborted.
Handle it where it actually happens: when `create_dir_all` on the
symlink's parent fails and the parent is a reparse point, remove the
dangling junction (unlinking only the junction, never a live target),
rebuild a real directory, and retry. Drops the dead 267 branch.
* ci: exclude the global-virtual-store links/ tree from the pnpm store cache
The GVS `links/` tree is Windows directory junctions, which tar
(actions/cache) can't round-trip: a restored store came back with
dangling junctions and installs failed creating symlinks. Exclude
`links/` from the cached path so only the CAS content is cached; pnpm
regenerates `links/` fresh on every install. The `nolinks` key abandons
the old caches that still hold `links/`.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* chore: update lockfile, Node.js, and pnpm versions
* chore: pin packageManagerDependencies specifier to the resolved pnpm version
The update-lockfile workflow recorded the next-12 dist-tag as the
packageManagerDependencies specifier, but the manifest pins pnpm to
12.0.0-alpha.11. pacquet validates the specifier against the manifest, so a
later --frozen-lockfile install failed with
ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE. Record the resolved pin so the
specifier matches the wanted version.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
The Rust CLI and pnpr embed the versions their release builds report and
verify (`PNPM_VERSION` in pnpm/crates/config/src/defaults.rs, the crate
version in pnpr/crates/pnpr/Cargo.toml, and its Cargo.lock entry). These
were mirrored from the npm wrappers by `syncRustVersions` in bump.ts — a
step separate from the meta-updater, so running `pnpm version -r` without
the full bump flow bumped the wrappers (pacquet 12.0.0-alpha.10, pnpr
0.1.0-alpha.2) while the Rust sources stayed at the previous versions. The
release workflow's "Verify the committed version" step then failed, and
nothing caught the drift before the tag.
Move the sync into the meta-updater as a set of Rust-source file handlers,
so it is written by `pnpm update-manifests` and, crucially, validated by
`meta-updater --test` in pre-push and CI — a missed sync now fails locally
instead of at release time. bump.ts drops the redundant `syncRustVersions`
and runs `pnpm update-manifests` after `pnpm version -r`; the root `bump`
script no longer needs its own trailing `update-manifests`.
Regenerating brings the Rust sources up to the already-bumped wrapper
versions (alpha.10 / alpha.2), unblocking the release.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>