You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pacquet's pnpm-lock.yaml map ordering is non-canonical: the importers, packages, snapshots, and per-importer dependencies/optionalDependencies/devDependencies maps are serialized in std::HashMap iteration order. Because std::HashMap uses a per-instance random seed, two installs that produce the same resolution can emit the maps in different orders, so the on-disk lockfile is not byte-stable.
This diverges from pnpm, whose pnpm-lock.yaml is deterministically ordered.
Root cause
The lockfile maps are std::collections::HashMap (pacquet/crates/lockfile/src/lib.rs: importers, packages, snapshots; project_snapshot.rs: ResolvedDependencyMap = HashMap<PkgName, _>).
pacquet/crates/lockfile/src/serialize_yaml.rs serializes the struct as-is with serde_saphyr and does not sort keys.
So the emitted order reflects per-HashMap random seeding, not a canonical sort.
Impact
Spurious lockfile churn: an unrelated re-install can reorder pnpm-lock.yaml, producing a large, meaningless git diff.
The pacquet test suite already works around this by parsing lockfiles for comparison rather than byte-comparing (e.g. pacquet/crates/cli/tests/inject_workspace_packages.rs).
It became user-visible while implementing lockfile-resolution reuse (perf(pacquet): reuse lockfile resolutions on re-resolution #12113): a reuse-built tree and a fresh-resolved tree are content-identical but byte-different purely due to map order, so the equivalence test there compares parsed Lockfile structs instead of bytes.
Suggested fix
Make lockfile serialization canonical, matching pnpm's exact per-section ordering (not merely an arbitrary sort):
Emit the lockfile maps sorted — either switch the lockfile maps to BTreeMap where the key types are Ord, or sort keys at serialization time in serialize_yaml.
Verify byte-parity against real pnpm pnpm-lock.yaml output for representative fixtures (pnpm has section-specific ordering rules, so confirm rather than assume pure lexicographic).
Regenerate the affected lockfile snapshot/insta tests deliberately and review the diffs.
Add a byte-stability test: install twice (and/or reuse vs. fresh) and assert the lockfile bytes are identical.
Summary
pacquet's
pnpm-lock.yamlmap ordering is non-canonical: theimporters,packages,snapshots, and per-importerdependencies/optionalDependencies/devDependenciesmaps are serialized instd::HashMapiteration order. Becausestd::HashMapuses a per-instance random seed, two installs that produce the same resolution can emit the maps in different orders, so the on-disk lockfile is not byte-stable.This diverges from pnpm, whose
pnpm-lock.yamlis deterministically ordered.Root cause
std::collections::HashMap(pacquet/crates/lockfile/src/lib.rs:importers,packages,snapshots;project_snapshot.rs:ResolvedDependencyMap = HashMap<PkgName, _>).pacquet/crates/lockfile/src/serialize_yaml.rsserializes the struct as-is withserde_saphyrand does not sort keys.So the emitted order reflects per-
HashMaprandom seeding, not a canonical sort.Impact
pnpm-lock.yaml, producing a large, meaningless git diff.pacquet/crates/cli/tests/inject_workspace_packages.rs).Lockfilestructs instead of bytes.Suggested fix
Make lockfile serialization canonical, matching pnpm's exact per-section ordering (not merely an arbitrary sort):
BTreeMapwhere the key types areOrd, or sort keys at serialization time inserialize_yaml.pnpm-lock.yamloutput for representative fixtures (pnpm has section-specific ordering rules, so confirm rather than assume pure lexicographic).instatests deliberately and review the diffs.References
pacquet/plans/LOCKFILE_RESOLUTION_REUSE.md("Known follow-ups")Written by an agent (Claude Code, claude-opus-4-8).