Skip to content

Fix unquoted scoped name in pnpm 7/8 vendored lock (#956) - #961

Open
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
mainfrom
agent/fix-pnpm-legacy-scoped-name-quote
Open

Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
mainfrom
agent/fix-pnpm-legacy-scoped-name-quote

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #956

Root cause

When the legacy pnpm vendored writer (vendor/pnpm_lock_legacy.rs, lock 5.4 / 6.0) re-emits a rewired packages: entry, it writes name: {ctx.name} with no YAML quoting. For a scoped package the result is name: @scope/pkg. @ is a reserved YAML indicator, so pnpm 7/8 rejects the whole lock with ERR_PNPM_BROKEN_LOCKFILE: every frozen install fails after a scan that reported success, and lock-only VEX keeps attesting not_affected from a lock pnpm can't read. pnpm's own writer emits name: '@scope/pkg'.

Fix

  • 62a0861: name: now goes through the shared formats::pnpm::lines::yaml_value scalar quoting. Every other line the legacy writer emits was already correct for scoped packages; the byte-exact oracle below matches pnpm on every line except name:.
  • 8b245d8: the pinned pnpm matrix skips the new scoped leg on pnpm 8.0.0–8.1.0 only. Those releases refuse their own lock for a scoped file: tarball override (ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY on the key they just wrote), so no vendored scoped lock can pass there. I measured this with real pnpm on Node 16: 8.0.0 and 8.1.0 fail on the lock they generate themselves, and 8.1.1, 8.2.0, 8.3.0, 8.6.0, 8.10.0 and 8.15.9 pass. The unscoped leg still runs on 8.0.0.
  • 35ffa60: cherry-pick of Route Gradle digests through utils::digest #878 (Route Gradle digests through utils::digest). main currently fails socket-patch-core --lib on utils::digest::tests::production_digests_go_through_the_helpers, which turned test and coverage red here on the empty start commit. The cherry-pick no-ops once Route Gradle digests through utils::digest #878 lands.
  • 5667ec9 (from Bugbot's review): the edit_packages in-sync check now also requires the canonical quoted name: line. Before this, a lock vendored by an older release (with the unquoted name) counted as already vendored on re-run, so the broken lock was never repaired.

Test evidence (local)

Test Without fix With fix
unit scoped_package_name_is_yaml_quoted_both_grammars (byte-exact oracles captured from real pnpm 7.33.7 / 8.15.9 for @isaacs/string-locale-compare@1.1.0; vendor, in-sync re-run, byte-identical revert) FAIL: name: @isaacs/string-locale-compare pass
unit revendor_heals_an_unquoted_scoped_name (5.4 and 6.0: re-vendor over a lock carrying the old unquoted name) FAIL: lock left unchanged pass
e2e pnpm7_real_lifecycle_scoped_package (real pnpm 7.33.5: frozen offline install, moved checkout, manifest-less VEX cells, re-vendor, revert) FAIL: ERR_PNPM_BROKEN_LOCKFILE … bad indentation of a mapping entry (16:11) pass
e2e pnpm8_real_lifecycle_scoped_package (real pnpm 8.15.9, same lifecycle) FAIL: ERR_PNPM_BROKEN_LOCKFILE … (19:11) pass
pnpm_pinned_matrix_vendored_lifecycle_and_manifestless_vex on 7.0.0 / 8.0.0 / 8.1.1 (Node 16) n/a pass (8.0.0 runs the unscoped leg only)
  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-cli --all-features --test e2e_vendor_pnpm_build: 26 passed.
  • cargo test -p socket-patch-core --lib pnpm_lock_legacy: 74 passed.
  • cargo test -p socket-patch-core --lib (on 35ffa60): 5247 passed. 4 failed, all pre-existing and unrelated: the sandbox runs as root, which ignores the read-only permissions those tests rely on (copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_…, pypi_requirements::wire_failure_rolls_back_…). CI runs them as a normal user.
  • I didn't run the full cargo test --workspace locally: the sandbox disk ran out at 29 GB of test binaries. CI runs it.
  • The npm/pypi/gem wrappers are untouched; this is core-only lock writing.
  • cargo fmt is not enforced in CI and main is not fmt-clean. I only rustfmt'ed the hunks I wrote.

Per-issue checklist

🤖 Generated with Claude Code

https://claude.ai/code/session_01UepoBazrbnBjy7HkD9YVJN


Note

Medium Risk
Changes lockfile emission and in-sync logic for pnpm 5.4/6.0 vendoring (install-breaking if wrong), but behavior is covered by new scoped unit and e2e tests; digest routing is a low-risk refactor.

Overview
Fixes #956: legacy pnpm 7/8 vendored locks for scoped packages now emit a YAML-valid name: line (quoted via yaml_value), matching pnpm’s serialization so frozen installs no longer hit ERR_PNPM_BROKEN_LOCKFILE. Re-vendor in-sync detection also requires that quoted name: line so older broken locks get repaired on the next run.

Tests add scoped-package unit oracles and e2e lifecycle coverage (run_legacy_capstone_for, matrix scoped leg), with a skip for pnpm 8.0.0–8.1.0 where scoped file: overrides fail under --frozen-lockfile even on pnpm’s own lock.

Refactor: Gradle cache, JVM jar, and Maven sidecar code now use shared utils::digest::{sha1_hex_of, sha256_hex_of} instead of inline sha1/sha2 + hex::encode (aligns with #878).

Reviewed by Cursor Bugbot for commit 5667ec9. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Vendoring a scoped package (@scope/pkg) into a pnpm 7 (lock 5.4) or
pnpm 8 (lock 6.0) project wrote `name: @scope/pkg` into the rekeyed
packages entry. A bare `@` cannot start a YAML scalar, so pnpm refused
the whole lock with ERR_PNPM_BROKEN_LOCKFILE: every frozen install
failed after a scan that reported success, and lock-only VEX kept
attesting not_affected from a lock pnpm could not read.

The name is now written through the shared YAML scalar quoting, which
gives `name: '@scope/pkg'`, byte-identical to what pnpm 7.33.7 and
8.15.9 serialize themselves for the same override.

Tests: a byte-exact unit oracle captured from real pnpm 7/8 for
@isaacs/string-locale-compare (vendor, in-sync re-run, revert), and
scoped real-pnpm lifecycle legs (frozen install, moved checkout,
manifest-less VEX, revert) in e2e_vendor_pnpm_build, also run in the
pinned pnpm 7/8 matrix. Fixes #956.

Assisted-by: Claude Code:claude-opus-5-5
pnpm 8.0.0 and 8.1.0 refuse their own lock for a scoped file: tarball
override under --frozen-lockfile (ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY
on the key they just wrote); 8.1.1 fixed it. Measured with real pnpm
on Node 16: the lock pnpm itself writes fails the same way, so no
vendored scoped lock can pass there. The pinned matrix keeps the
unscoped leg on those versions and runs the scoped leg everywhere else.

Assisted-by: Claude Code:claude-opus-5-5
main has failed socket-patch-core's lib tests since Gradle support
(#646) and the digest helpers (#865) both landed. The guard test
production_digests_go_through_the_helpers flags three files #646 added
that still hash inline: crawlers/gradle_cache.rs, patch/jvm_jar.rs and
patch/sidecars/maven.rs. That breaks test, test-release and coverage on
every open PR.

Each inline sha1/sha256 call now goes through sha1_hex_of or
sha256_hex_of, which compute the same lowercase hex. Behaviour is
unchanged.

(cherry picked from commit 659ac2c)

Ported from #878 so this PR's CI is green while main's digest guard
test is red; it no-ops once #878 lands.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 6, 2026 19:58
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vendor/pnpm_lock_legacy.rs Outdated
edit_packages treated a packages entry as in sync once its file: key
and resolution matched, without looking at name:. A lock vendored by a
release before the #956 fix still carries `name: @scope/pkg`, which
pnpm 7/8 can't load, so a later vendor reported the package already
vendored and left the lock broken.

The in-sync check now also requires the canonical quoted name: line, so
the old spelling is rewritten like any other stale wiring. The new test
revendor_heals_an_unquoted_scoped_name fails without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UepoBazrbnBjy7HkD9YVJN
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5667ec9. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 6, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] Ready for review at 5667ec9.

  • CI: every check suite on 5667ec9 is green.
  • Bugbot: one finding on 35ffa60 (re-vendor left a stale unquoted scoped name:), fixed in 5667ec9 with revendor_heals_an_unquoted_scoped_name; Bugbot re-reviewed 5667ec9 with no new issues. No open review threads.
  • Reviewer note: the branch also carries 35ffa60 (routes Gradle digests through utils::digest, the same change as Route Gradle digests through utils::digest #878) so the digest-guard test passes. Approved earlier on this same head.

Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

3 participants