Skip to content

Fix berry hosted pin beside a later registry entry (#1082) - #1397

Merged
Mikola Lysenko (mikolalysenko) merged 7 commits into
mainfrom
agent/fix-berry-hosted-pin-sibling-entry
Oct 11, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 7 commits into
mainfrom
agent/fix-berry-hosted-pin-sibling-entry

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Fixes #1082

Summary

A yarn berry hosted pin routes each descriptor that existed at scan time to the Socket tarball through a root resolutions selector. Sometimes a range of the same package is added later, such as a workspace member's left-pad: 1.3.0, or an npm: alias of it. Yarn then locks that descriptor as a separate registry entry, and that copy installs unpatched.

Before this PR:

  • A re-scan refused with redirect_yarn_berry_ambiguous_entry and suggested "run yarn install to dedupe". That can't work, because the pin is what keeps the two entries apart.
  • rollback / remove exited 0 but left two entries with the same resolution:. Yarn merges such entries, so every fresh yarn install --immutable failed with YN0028.
  • The vendored counterpart had the same two-entry result: vendor --revert, rollback or remove after an alias was added post-vendoring.

After this PR:

  • The re-scan adds a selector for the new range and folds its registry entry into the pinned one.
  • The hosted restore and the vendored revert merge the restored entry into the entry yarn wrote for the same resolution. The result is one entry, keyed by every descriptor in sorted order, with yarn's own body.

Root cause

The berry hosted rewriter and both unwind paths treated the hosted (or vendored) entry and a later registry entry of the same name@version as unrelated blocks:

  • patch/redirect/mod.rs (berry rewriter): when targets.len() > 1, it refused, even when one target was our own URL-keyed entry and the others were plain npm: registry entries.
  • patch/redirect/upstream/npm.rs (restore_berry) and vendor/yarn_berry_lock.rs (revert_yarn_berry_opts): both restored the original entry next to an existing entry with the same resolution.

The bisect in the issue points at #1008. Its Discovery::shadowed change hands these refs to rollback; before it, they were refused.

Changes

  • Re-scan fold (redirect/mod.rs): when exactly one target is URL-keyed, the other npm: targets are folded in. BerryResolutionsPin::add_ranges routes their ranges (plus any matching catalog selectors) and removes their entries (edit kind redirect_yarn_berry_entry_folded). Two registry entries with no pin of ours still get ambiguous_entry.
  • Shared merge (formats/yarn/stanzas.rs, merge_same_resolution): folds each restored stanza into a sibling with the same resolution:. The merged stanza keeps the sibling's (yarn-written) body and takes the sorted union of both keys. The merged key goes to render, which places it in yarn's order.
  • Hosted restore (upstream/npm.rs): runs the merge after re-keying. The yarn_berry_node_gyp_unresolved advisory is dropped for an entry that was merged into yarn's own.
  • Vendored revert (yarn_berry_lock.rs): runs the merge over the restored lock and pruned entries.
  • docs/ecosystems.md: documents the fold and the merge.

Tests (red on main, green here)

Case Test Without fix
Re-scan folds a later range, with the same and with a superseding uuid. The folded pin is stable, and two plain registry entries stay ambiguous core redirect::tests::yarn_berry_rerun_folds_a_descriptor_added_after_pinning redirect_yarn_berry_ambiguous_entry
Hosted rollback and remove merge into a later plain entry and into an alias entry cli in_process_redirect::yarn_berry_unwind_merges_into_a_registry_entry_added_after_pinning two entries with one resolution
Vendored revert merges into an alias entry, byte-exact core yarn_berry_lock::tests::revert_merges_into_an_alias_entry_added_after_vendoring two entries
Merge helper: sorted keys, yarn's body, moved bookkeeping core stanzas::tests::restored_entries_merge_into_a_sibling_with_their_resolution n/a (new helper)
Real yarn 4.12 workspace with a member added after the pin. Steps: (1) roll back the drifted lock; (2) re-scan, then a fresh offline --immutable install gets only the patched bytes; (3) roll back, then a fresh --immutable install passes cli e2e_yarn4_workspaces_build::yarn4_hosted_pin_folds_a_member_added_later_and_rolls_back With restore_berry from main, rollback leaves two entries. With the rewriter from main, the re-scan refuses (ambiguous_entry, applied 0)

Per-issue checklist:

Local runs:

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test --workspace --all-features --no-fail-fast (with corepack yarn available): 13905 passed, 15 failed. None of the failures is in code this PR changes, and all come from the sandbox:
    • 12 rely on chmod-based write failures, which the sandbox's root user bypasses (*_write_failure_*, *unremovable*, relax_loop_must_not_traverse_symlinked_root, and similar).
    • 1 is e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed, which needs patches-api.socket.dev.
    • 2 are mode_migration_npm::berry_{hosted_then_vendored,vendored_then_hosted}_takeover_*. They panic in their own fixture setup (mount_hosted_mocks, mode_migration_npm.rs:329), where the test's reqwest client can't verify the sandbox proxy's certificate for registry.npmjs.org. That happens before any socket-patch code runs. Earlier agent runs had no corepack yarn, so these skipped there.
  • cargo test -p socket-patch-cli --test e2e_yarn4_workspaces_build with real yarn 4.12.0 (SOCKET_PATCH_YARN_E2E_REQUIRED=1): passed

/code-review high

Fixed:

  • pruned entries now merge too
  • the node-gyp advisory is no longer kept for a merged-away entry
  • the catalog lookup is skipped when nothing is folded
  • stale moved keys are dropped

Not changed:

🤖 Generated with Claude Code

https://claude.ai/code/session_01TY1qeZG6YhbTSVfdgf4hhG


Note

Medium Risk
Changes hosted redirect, rollback, and vendored revert paths for yarn.lock and root resolutions; mistakes could break installs or leave packages unpatched, but behavior is heavily tested including real Yarn e2e.

Overview
Fixes #1082: when Yarn Berry adds a second yarn.lock entry for the same package after a hosted pin (e.g. a new workspace member range or npm: alias with no matching resolutions selector), re-scan no longer bails with redirect_yarn_berry_ambiguous_entry, and rollback/remove/revert no longer leave two stanzas sharing one resolution: (which breaks yarn install --immutable with YN0028).

Re-scan (hosted redirect) treats the URL-keyed pin plus one extra plain registry entry as foldable: it adds selectors for the new ranges, drops the duplicate stanza, and records redirect_yarn_berry_entry_folded edits.

Shared merge_same_resolution in yarn stanza handling combines restored stanzas into an existing sibling with the same resolution: (sorted union of keys, Yarn’s body kept). Hosted restore and vendored revert call this after unwinding.

Tests cover unit merge/rewrite behavior, in-process rollback/remove, vendored revert with a post-vendor alias, and a real Yarn 4 workspace e2e (late member, re-scan fold, rollback). Docs in ecosystems.md describe the fold/merge behavior.

Reviewed by Cursor Bugbot for commit f3d85e3. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
A hosted yarn berry pin routes each descriptor that existed at scan
time to the Socket tarball. A descriptor added later (a workspace
member's exact range, an npm: alias) gets its own registry entry, so
that copy installs unpatched.

A re-scan used to refuse with a "run yarn install to dedupe" remedy
that never works, since the pin keeps the entries apart. It now adds a
selector for the new descriptor and drops its registry entry, which is
the lock yarn writes once that selector exists.

rollback and remove restored the hosted entry next to that registry
entry with the same resolution. Yarn merges such entries, so every
fresh yarn install --immutable failed with YN0028 while the unwind
reported success. The restored entry is now merged into it.

Fixes #1082

Assisted-by: Claude Code:claude-opus-5-5
Adds a real-yarn workspace e2e: a member added after the hosted pin
gets its own registry entry; rollback of that lock, and a re-scan
followed by rollback, each leave a lock a fresh yarn install
--immutable accepts. The hosted API mocks move into a helper shared
with the existing capstone.

Assisted-by: Claude Code:claude-opus-5-5
vendor --revert (and rollback, remove, the hosted takeover) put the
pre-vendor yarn.lock entry back verbatim. When an npm: alias or a
member range of the same package was added after vendoring, yarn had
locked it as its own registry entry with the same resolution, so the
reverted lock held two entries yarn merges and every fresh yarn
install --immutable failed with YN0028. The restored entry is now
merged into that entry, as yarn writes it.

The merge helper moves to formats::yarn::stanzas, shared with the
hosted restore.

Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
The vendored berry revert also merges a restored pruned entry into a
sibling with its resolution. The hosted restore drops its node-gyp
advisory for an entry merged into yarn's own, and skips the catalog
lookup when nothing is folded.

Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 11, 2026 20:28
@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.

✅ 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 f3d85e3. Configure here.

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

Copy link
Copy Markdown
Collaborator Author

Ready for review at f3d85e3.

  • CI: 35/35 non-skipped checks green on the head (26 skipped by path filters); no main-wide failures.
  • Bugbot: reviewed f3d85e3, no findings.
  • Mergeable: yes, no conflicts; no CHANGELOG.md change.
  • Worth a look: the new shared merge_same_resolution in formats/yarn/stanzas.rs, which both the hosted restore and the vendored revert call. It unions keys and keeps yarn's body, so check that it never merges two stanzas that differ in more than their key list.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[final reviewer] Review brief for f3d85e3

What it does. Fixes #1082. When a new range of an already-pinned yarn berry package shows up later (for example a workspace member's left-pad: 1.3.0), yarn locks it as a second, unpatched registry entry. A re-scan now folds that entry into the hosted pin by adding one more resolutions selector. rollback/remove and vendor --revert now merge the restored entry into any entry with the same resolution:, which keeps yarn install --immutable from failing with YN0028.

Risk: medium. It changes how lockfiles are rewritten on the hosted pin, hosted restore and vendored revert paths. The fold is narrow: it only applies when exactly one target is our own URL-keyed entry. Two plain registry entries still get ambiguous_entry, and alias entries are still skipped before targeting. Coverage includes a real yarn 4.12 e2e.

Look here

  • stanzas.rs:95: merge_same_resolution (sorted union of keys, keeps yarn's body)
  • redirect/mod.rs:4710: the fold gate in the berry rewriter, plus add_ranges at 5063
  • upstream/npm.rs:1017:`` hosted restore merge. The node-gyp advisory is suppressed for an entry that gets merged away
  • yarn_berry_lock.rs:795: vendored revert merge. If the restored stanza text doesn't match exactly, it falls back to the old behavior, so nothing gets worse

Verified

  • Read the full diff. Folded-block removal runs from the highest index down, after the target is re-keyed in place. The fail-closed manifest path also drops the new entry_folded edits. CRLF goes through BerryStanzas LF normalization.
  • Ran the 5 new and changed core tests locally on f3d85e3: 5/5 pass.
  • CI: ci-ok and clippy green, all non-skipped checks green. Bugbot reviewed f3d85e3 and found nothing.
  • CHANGELOG.md is untouched. Mergeable, no unresolved threads.

Changes I made: none.
Open questions: none.

Auto-merge (squash) is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

Merged via the queue into main with commit 1fb8901 Oct 11, 2026
86 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-berry-hosted-pin-sibling-entry branch October 11, 2026 22:55
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