Visitar URL original
After `uv remove --script` drops a vendored package from a PEP 723 script lock, `scan --prune`, `vendor --revert`, `remove`, `rollback` and the hosted takeover still drift-keep it, so `vendor --check` stays red and its prune remedy loops · Issue #1214 · SocketDev/socket-patch · GitHub
Skip to content

After uv remove --script drops a vendored package from a PEP 723 script lock, scan --prune, vendor --revert, remove, rollback and the hosted takeover still drift-keep it, so vendor --check stays red and its prune remedy loops #1214

Description

[agent] Found by the scheduled uv bug-hunt routine (ledger #310).

Summary

#1147 fixed #1140 for uv projects: after uv remove six, revert_uv now reports vendor_lock_entry_removed when neither pyproject.toml nor uv.lock names the entry's uuid, and the revert finishes. The same removal in a PEP 723 script lock isn't covered. Script locks go through revert_python_locks (pypi_lock.rs), which still has only the drift arm.

After uv remove --script s.py six, uv deletes six from the script block, the tool.uv.sources.six line and the s.py.lock package. No socket reference is left in either file. Then:

  • vendor --check exits 1: "dependency removed … run socket-patch scan --mode vendored --prune to revert the vendored entry".
  • scan --prune exits 0 and prints "GC: kept 1 drifted vendored entry … undo the drift and re-run vendor --revert". Check stays red.
  • vendor --revert --json exits 0 success, with vendor_lock_entry_drifted + vendor_artifact_kept + vendor_revert_kept. The wheel and ledger entry stay.
  • remove pkg:pypi/six@1.16.0 exits 1 vendor_revert_kept. rollback exits 1 partial_failure (vendor_lock_entry_drifted).
  • scan --mode hosted exits 0 with vendor_ledger_entry_unwired and leaves the entry too.

There's nothing to undo, so no command can ever clear the entry. Check's own remedy (scan --prune) is a no-op that exits 0. I reported this shape on #1140 before #1147 merged (#1140 (comment)). #1140 is now closed, and the project and upgrade shapes from that comment pass on b76d7ab, so I'm filing the script lane separately.

Impact

A CI gate on vendor --check stays red for good after a routine dependency removal from a uv script. Every remedy the tool names exits 0 or loops, and the dead wheel and ledger entry stay committed. Nothing installs unpatched (six is gone from the script), so this is a stuck state, not a silent unpatch.

Repro (Linux, uv 0.12.24 or 0.5.31, main b76d7ab)

Patch data came from a local mock of the patch API (six@1.16.0), the same one as earlier uv issues.

git init -q app && cd app
cat > s.py <<'EOF'
# /// script
# requires-python = ">=3.9"
# dependencies = ["six==1.16.0", "attrs>=20"]
# ///
import six
EOF
uv lock --script s.py
socket-patch scan --mode vendored --yes --json      # exit 0; s.py + s.py.lock wired
git add -A && git commit -qm vendored
uv remove --script s.py six
grep -c socket s.py s.py.lock                       # 0 and 0
socket-patch vendor --check; echo $?                # 1: "dependency removed … run scan --mode vendored --prune"
socket-patch scan --prune --yes; echo $?            # 0: "GC: kept 1 drifted vendored entry"
socket-patch vendor --check; echo $?                # still 1
socket-patch vendor --revert --json; echo $?        # 0, vendor_lock_entry_drifted / vendor_revert_kept
ls .socket/vendor/pypi                              # wheel dir still there

Expected vs actual

  • Expected: what Fix vendored revert reading a removed dependency as drift (#1132, #1140, #1142) #1147 now does for the project lane. When the script and its lock no longer name the entry's uuid, each python_script_metadata / python_lock_document record warns vendor_lock_entry_removed, the revert finishes and deletes the wheel and ledger entry, and vendor --check turns green. CLI_CONTRACT.md has scan --prune revert vendored entries whose dependency is gone, and that's the remedy vendor --check names.
  • Actual: every unwind keeps the entry as "drift", and check stays red.

Matrix (Linux, real uv lock --script / uv remove --script; fresh fixture per cell, all run twice)

uv unwind after uv remove --script exit entry / wheel vendor --check after
0.5.31 scan --prune 0 kept 1
0.5.31 vendor --revert 0 (vendor_lock_entry_drifted) kept 1
0.5.31 remove pkg:pypi/six@1.16.0 1 vendor_revert_kept kept 1
0.5.31 rollback 1 partial_failure kept 1
0.5.31 scan --mode hosted 0 vendor_ledger_entry_unwired kept 1
0.12.24 all five, same as above same kept 1
0.12.24 control: project uv remove six → scan --prune 0 reverted 0 (fixed by #1147)
0.12.24 control: project uv remove six && uv add six==1.17.0 → scan --prune 0 reverted 0

Not bisected: this is a gap left by #1147, which only touched pypi_uv.rs. No probe branch: there's no OS-specific logic involved.

Suspect code

  • crates/socket-patch-core/src/vendor/pypi_lock.rs:723 (revert_python_locks): the script-metadata and script-lock records only map a missing original to vendor_lock_entry_drifted (lines ~733 / 746 / 774). They need the same "nothing names the uuid any more → vendor_lock_entry_removed" arm that Fix vendored revert reading a removed dependency as drift (#1132, #1140, #1142) #1147 added at crates/socket-patch-core/src/vendor/pypi_uv.rs:825.

Activity

  1. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (PyPI/uv). Confirmed on main f3c6313: revert_python_locks (crates/socket-patch-core/src/vendor/pypi_lock.rs:723) only has the vendor_lock_entry_drifted arm for script-metadata / script-lock records; the "nothing names the uuid" → vendor_lock_entry_removed arm #1147 added lives only in pypi_uv.rs. No open PR addresses it.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: revert_python_locks has no "nothing names the uuid any more" arm for PEP 723 script records). Branch: agent/fix-uv-script-lock-removed-entry. Claim-ID: 2026-10-09T06:21:47Z-0e505c


    Generated by Claude Code

  3. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1231


    Generated by Claude Code

  4. added 2 commits that reference this issue on Oct 9, 2026
    95fcbe4
    eec1e33
  5. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] uv bug-hunt run 35 (ledger #310): I checked PR #1231 (eec1e33) against main e03a666, using uv 0.12.24 (and 0.5.31 for the two-script row).

    Single script (the shape in this issue): fixed. After uv remove --script a.py six, all of scan --prune, vendor --revert, remove and rollback exit 0 (rollback reports vendor_lock_entry_removed), the wheel is gone, and vendor --check exits 0. On main they still drift-keep, with check 1. The hosted takeover still warns vendor_ledger_entry_unwired with check 1, but its scan --prune remedy now works, so it's no longer a loop.

    Two scripts where only one drops six: still broken on #1231. Repro: a.py and b.py both declare six==1.16.0, each has its own .py.lock, and both are vendored. Then:

    uv remove --script b.py six
    socket-patch vendor --revert --json
    #   exit 0, status success, removed 0
    #   vendor_lock_entry_drifted "b.py.lock changed since vendoring; conflicting fields were preserved"
    #   + vendor_artifact_kept + vendor_revert_kept
    #   a.py is still wired to .socket/vendor, and `uv run --locked --script a.py` still runs the patched six
    socket-patch remove pkg:pypi/six@1.16.0 --yes   # exit 1 vendor_revert_kept
    socket-patch rollback --yes                     # exit 1 vendor_artifact_kept + vendor_lock_entry_drifted
    socket-patch scan --mode hosted --yes           # exit 0 redirect_vendored_revert_failed, a.py stays vendored
    

    So b.py's record is still classified as drift, not as removed, while another script keeps the uuid live. That blocks the whole unwind, including a.py, which didn't change. The results are the same on main and on #1231, with uv 0.5.31 and 0.12.24, and on repeated runs. A user-deleted # six = { path = … } source line in one script, followed by a relock, unwinds cleanly on both builds.


    Generated by Claude Code

  6. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] uv bug-hunt run 36 (ledger #310): I verified the fix on main e9be746 (with #1231), using uv 0.5.31 and 0.12.24. For the single-script shape in this issue, vendor --revert, scan --prune, remove and rollback now finish after uv remove --script.

    The two-script shape from my earlier comment, where only one script drops six, still reproduces on main. I filed it separately as #1285.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions