Skip to content

rfc: key snapshots by package and record peer bindings as properties - #72

Draft
zkochan wants to merge 20 commits into
mainfrom
rfc/lockfile-peer-records
Draft

zkochan wants to merge 20 commits into
mainfrom
rfc/lockfile-peer-records

Conversation

@zkochan

@zkochan zkochan commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Proposes the next lockfile format. packages has one entry per package, keyed by name@version, holding its metadata, its resolved dependencies, and its peer bindings as properties: peers for the package's own, transitivePeers for those bound below it. There is no snapshots section, and no key or reference carries a peer or patch group: the patch hash lives in patchedDependencies, and a reference the referrer's scope does not select names its snapshot by its peer bindings. peerDependenciesMeta is folded into peerDependencies: an optional peer is {specifier: '*', optional: true} on one line. An importer's dependency is its specifier alone when the version follows from it, and {specifier, version} on one line otherwise. A package installed with several sets of peers has one snapshot per set. The entry writes what the snapshots share once and lists what differs under snapshots. A reference names a version, and the referrer's bindings select the snapshot; where they can't, the reference keeps its peer groups.

The RFC also requires every directory package's entry to record specifiers, the field pnpm/pnpm#17477 adds to lockfile 9.0 as optional.

It fixes these problems:

A prototype in pnpm v12 (pnpm/pnpm#17886), behind a lockfile.format: next setting, reads and writes 170 of 173 real lockfiles exactly. A 5,623-snapshot lockfile goes from 4.40 MB to 2.20 MB and from 99,955 lines to 58,186, and a react bump changes 58 lines instead of 142. On a 237-project workspace, installs are within noise of today's format except regenerating a deleted pnpm-lock.yaml, which pays the prototype's write cost (+6 to +12 ms). The "Prototype results" section has the numbers and what the design had to change: references keep their peer groups where the referrer's scope doesn't predict the snapshot, and peers bound from anywhere in the graph go in missingPeers. The section also splits the prototype's time into the stages the format needs and the stages only its conversion needs, as an estimate for the native implementation planned for pnpm v13.

The earlier drafts, which kept the suffix keys, and the designs considered on the way (ordinal ids, grouping by suffix, keys by peer names) are listed as alternatives with the reasons they fall short. The lockfileVersion number is left open, given the decision recorded in #68.


Written by an agent (Claude Code, claude-fable-5-1).

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 6, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Covers the peer/child decision requested in pnpm/tasks#221 and lists its
additive alternative.
Covers the specifiers field that pnpm/pnpm#17477 adds to lockfile 9.0
as an optional field.
@zkochan zkochan changed the title rfc: record peer bindings explicitly in lockfile snapshots RFC: Record resolution inputs and peer bindings in the lockfile Nov 7, 2026
@zkochan zkochan changed the title RFC: Record resolution inputs and peer bindings in the lockfile rfc: record resolution inputs and peer bindings in the lockfile Nov 7, 2026
Replaces the suffix keys of the first draft. A reference names a version,
and the parent's context determines the variant. The earlier designs
stay as alternatives.
@zkochan zkochan changed the title rfc: record resolution inputs and peer bindings in the lockfile rfc: key snapshots by package and record peer bindings as properties Nov 8, 2026
A prototype of the format reads and writes 170 of 173 real lockfiles
exactly. It showed that the referrer's scope does not always select a
variant, that transitive peers bound from anywhere in the graph are not
part of a package's identity, and that rebuilding nested peer suffixes
needs the scope above a snapshot. The RFC now describes references that
keep peer groups, the missingPeerDependencies list, how providers are
found, the current lockfile, and the measured results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zkochan

zkochan commented Nov 8, 2026

Copy link
Copy Markdown
Member Author

The prototype described under "Prototype results" is open as a draft: pnpm/pnpm#17886. It is opt-in, behind lockfile.format: next.


Written by an agent (Claude Code, claude-opus-5-5).

zkochan and others added 15 commits November 8, 2026 15:22
One entry per package holds the metadata, the resolved dependencies,
and the peer bindings, named peers, transitivePeers, and missingPeers.
A package with several variants writes what they share once. The
prototype results are updated: the 5,623-snapshot lockfile is 61% of
today's bytes, and both formats parse in parallel chunks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Such an entry can only come from a hand-written or broken lockfile.
The one the prototype seemed to find was an artifact of an earlier
version of its merge code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… implementation for pnpm v13

`variants` is the name of the list inside a `type: variations` resolution,
where it means the platform builds of a package. Each item of the renamed
list is one snapshot of the package, which is what today's `snapshots`
section records for one set of peers.

The results section now shows where the prototype's time goes, split into
the stages the format needs and the stages only the conversion needs, and
says that the native numbers are a sum of those stages, not a measurement.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A peer's entry is its range, or a map of `specifier` and `optional` when
`package.json` marks the peer optional. The only thing
`peerDependenciesMeta` ever held was that flag.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…apshot

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n follows from it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The patch hash lives in `patchedDependencies` alone, and a reference the
referrer's scope does not select names its snapshot by its peer bindings.

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