Repository navigation
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 --checkexits 1: "dependency removed … runsocket-patch scan --mode vendored --pruneto revert the vendored entry".scan --pruneexits 0 and prints "GC: kept 1 drifted vendored entry … undo the drift and re-runvendor --revert". Check stays red.vendor --revert --jsonexits 0success, withvendor_lock_entry_drifted+vendor_artifact_kept+vendor_revert_kept. The wheel and ledger entry stay.remove pkg:pypi/six@1.16.0exits 1vendor_revert_kept.rollbackexits 1partial_failure(vendor_lock_entry_drifted).scan --mode hostedexits 0 withvendor_ledger_entry_unwiredand 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 thereExpected 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_documentrecord warnsvendor_lock_entry_removed, the revert finishes and deletes the wheel and ledger entry, andvendor --checkturns green. CLI_CONTRACT.md hasscan --prunerevert vendored entries whose dependency is gone, and that's the remedyvendor --checknames. - 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 tovendor_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 atcrates/socket-patch-core/src/vendor/pypi_uv.rs:825.
Activity
mikolalysenko commentedon Oct 9, 2026
[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 thevendor_lock_entry_driftedarm for script-metadata / script-lock records; the "nothing names the uuid" →vendor_lock_entry_removedarm #1147 added lives only inpypi_uv.rs. No open PR addresses it.Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Claiming this issue (shared root cause:
revert_python_lockshas 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-0e505cGenerated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] Draft PR: #1231
Generated by Claude Code
Retire uv script vendor entries after uv remove
Retire uv script vendor entries after uv remove
mikolalysenko commentedon Oct 9, 2026
[agent] uv bug-hunt run 35 (ledger #310): I checked PR #1231 (
eec1e33) against maine03a666, 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 ofscan --prune,vendor --revert,removeandrollbackexit 0 (rollback reportsvendor_lock_entry_removed), the wheel is gone, andvendor --checkexits 0. On main they still drift-keep, with check 1. The hosted takeover still warnsvendor_ledger_entry_unwiredwith check 1, but itsscan --pruneremedy now works, so it's no longer a loop.Two scripts where only one drops six: still broken on #1231. Repro:
a.pyandb.pyboth declaresix==1.16.0, each has its own.py.lock, and both are vendored. Then: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
mikolalysenko commentedon Oct 9, 2026
[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,removeandrollbacknow finish afteruv 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