Skip to content

Retire berry resolutions left by yarn remove (#1203) - #1321

Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/v5-berry-leftover-resolutions
Open

Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/v5-berry-leftover-resolutions

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Fixes #1203

Summary

After yarn remove (or yarn up to an unpatched version) of a hosted-pinned yarn berry package, the root package.json keeps the Socket resolutions selector, because yarn never edits resolutions. That selector used to read as contested hosted wiring, so rollback, remove and list exited 1 every time with hosted_wiring_contested, and the printed remedy (re-running the hosted scan) changed nothing.

Now the selector counts as Socket's own leftover pin:

  • list succeeds and lists the live pins. It warns hosted_resolution_orphaned and names the commands that remove the selector.
  • rollback (unscoped, or scoped to the purl) and remove <uuid|purl> delete the selector from package.json and warn hosted_resolution_orphaned. They drop an emptied resolutions object and leave yarn.lock untouched. The other hosted pins are restored as before.

Root cause

Lockfile discovery reads the root package.json and recognizes every Socket identity in it (rule 11). HostedInventory::of (crates/socket-patch-core/src/patch/redirect/upstream/mod.rs) marked any recognized hosted uuid that no lock pin attributes as contested. It did not separate "no lock entry resolves this selector any more" from "the lockfiles disagree". The berry restore only walks lock pins, so nothing ever removed such a selector.

The fix

  • vendor::lock_inventory::yarn::berry_selector_routes_nothing holds the one staleness rule. A selector is leftover only when no live berry lock entry resolves its URL (as a tarball locator or an older __archiveUrl= binding) and no live lock entry is keyed by a descriptor it matches: the exact name@range for a ranged selector, any descriptor of the package for a range-less one. A selector that still matches a descriptor the lock resolves elsewhere means the manifest and lock disagree, so it stays contested. Discovery only looks at a berry lock that exists.
  • Discovery records those selectors as Discovery::stale_selectors (vex/discover/yarn.rs). It only does this when the URL's leaf names the package version, which gives the purl. The selectors are not refs and are never attested.
  • HostedInventory exposes them as stale pins (files: package.json). Their URL's uuids are excused from contested wiring in that manifest. HostedInventory::unwindable() (pins + stale) is what rollback and remove act on. list still lists pins only.
  • restore_upstream gains a package.json format (npm::retire_stale_selectors). It runs after the yarn.lock restore over the same staged view, re-checks each selector against the sibling berry lock, and removes it. A pin it cannot find a selector for is refused as before.

scan --mode hosted --prune is unchanged: hosted mode runs no GC, and the contract documents the flag as ignored there with redirect_prune_ignored. The selector no longer blocks anything, and rollback / remove retire it.

Tests (red → green)

Issue behaviour Test
inventory: selector left by yarn remove is stale, not contested (with and without another live pin) crates/socket-patch-core/tests/hosted_inventory.rs::berry_selector_left_by_yarn_remove_is_stale_not_contested
no lock, or a lock that still resolves the descriptor elsewhere: not retired …::berry_selector_is_only_retired_against_a_berry_lock
restore removes only the leftover selector, lock untouched, offline …::restore_retires_a_leftover_berry_selector
list exits 0 with hosted_resolution_orphaned (was hosted_wiring_contested, exit 1) crates/socket-patch-cli/tests/in_process_rollback_hosted.rs::berry_selector_left_by_yarn_remove_is_listed_around
rollback exits 0 and retires it; a later list is clean …::rollback_retires_a_berry_selector_left_by_yarn_remove
remove <uuid> and remove <purl> exit 0 and retire it (was not_found) …::remove_retires_a_berry_selector_left_by_yarn_remove

Before the fix, the core tests don't compile (HostedInventory has no stale), and the CLI cells reproduce the issue's table (exit 1, hosted_wiring_contested / not_found).

CLI_CONTRACT.md documents the new behaviour and the hosted_resolution_orphaned code.

Commands run (local, macOS)

  • cargo fmt --all -- --check (only files this PR touches)
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test -p socket-patch-core --no-fail-fast: all green (lib 6109 passed, every integration binary green, discovery goldens unchanged)
  • cargo test -p socket-patch-cli --lib --test in_process_rollback_hosted --test in_process_redirect --test mode_migration_npm --test in_process_vendor: 915 + 38 + 137 + 21 + 131 passed, 0 failed

🤖 Generated with Claude Code


Note

Medium Risk
Changes hosted-inventory classification and manifest edits for Yarn Berry resolutions; incorrect staleness detection could retire a selector that still matters or miss a true contested case.

Overview
Fixes #1203: Yarn Berry projects no longer get stuck in hosted_wiring_contested when yarn remove / yarn up drops the lock entry but leaves a Socket resolutions selector in root package.json.

Discovery now records those selectors as stale_selectors and HostedInventory::stale when berry_selector_routes_nothing says nothing in the live berry lock still resolves the hosted URL or matches the selector’s descriptor. Their patch UUIDs are excused from contested wiring; rollback and remove act on unwindable() (live pins + stale) and run retire_stale_selectors to remove only the orphaned resolutions entries (and drop an empty resolutions object), with yarn.lock unchanged. list still shows live pins and emits hosted_resolution_orphaned with remediation hints instead of failing.

CLI_CONTRACT.md documents the behavior and the new warning code on list, rollback, and remove.

Reviewed by Cursor Bugbot for commit b81ea11. Configure here.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
After `yarn remove` or `yarn up` of a hosted-pinned yarn berry
package, the root package.json keeps the Socket `resolutions`
selector. That selector read as contested hosted wiring, so
rollback, remove and list failed forever with
hosted_wiring_contested, and re-running the hosted scan (the printed
remedy) did not help.

Discovery now records a hosted selector that no live berry lock
entry resolves, and that matches no lock descriptor, as a stale
selector. HostedInventory excuses it from contested wiring.
rollback and remove retire it from package.json with a
hosted_resolution_orphaned warning and leave yarn.lock alone. list
succeeds and warns.

Fixes #1203

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) force-pushed the agent/v5-berry-leftover-resolutions branch from c78c0fa to b81ea11 Compare October 9, 2026 17:29
@mikolalysenko Mikola Lysenko (mikolalysenko) changed the title Fix stranded berry resolutions after yarn remove (#1203) Retire berry resolutions left by yarn remove (#1203) Oct 9, 2026
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 9, 2026 17:40
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review

Comment thread crates/socket-patch-cli/src/commands/list.rs Fixed

@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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

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

Reviewed by Cursor Bugbot for commit b81ea11. Configure here.

Comment thread crates/socket-patch-cli/src/commands/list.rs
The list and restore warnings for a leftover berry selector named
the patch uuid and the hosted URL, which carries the grant token.
CodeQL flagged the uuid as cleartext logging. Name the file, the
purl and the selector instead, as the contested refusal names files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The hosted_resolution_orphaned list warning suggested an unscoped
rollback, which also restores every other hosted pin and vendored
entry. Name `socket-patch remove <purl>`, which retires only the
leftover selector.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review at 992a598057b0.

  • CI: required checks ci-ok and clippy green; 9 check suites succeeded.
  • Mergeable against main, no CHANGELOG.md change.
  • Bugbot reviewed this head; no unresolved review threads.

Labeled Ready for review by the burn-down agent. Slack announcement pending (connector unavailable this run).


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