GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Fix uv vendored revert half-reverting after a relock (#806, #821) by mikolalysenko · Pull Request #822 · SocketDev/socket-patch

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 5, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

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

Fixes #806
Fixes #821

Summary

Before this change, a vendored uv project broke after an ordinary relock. Running uv add --dev zipp with six in a dev group (#821), or uv add idna beside a user-authored override-dependencies (#806), and then vendor --revert, remove, rollback or a vendored→hosted takeover:

  • reverted pyproject.toml but left uv.lock wired;
  • made uv sync --locked fail with "The lockfile at uv.lock needs to be updated";
  • still let vendor --revert and the takeover exit 0 with status: "success", and the takeover warned "left in place" after it had already written pyproject.toml.

Now:

  • Revert recognizes our element inside uv's re-serialized array and unwinds both files cleanly.
  • When something really has drifted, or can't be read, revert writes neither file, so the pair stays consistent.

Root cause (shared by both issues)

revert_uv (crates/socket-patch-core/src/vendor/pypi_uv.rs) had two defects:

  1. Whole-array text matching. The uv_lock_requires_dev record (Vendored uv package in a dependency group: after uv add --dev / uv remove --dev, vendor --revert, remove, rollback and the hosted takeover revert pyproject.toml but keep uv.lock's vendored requires-dev entry, so uv sync --locked fails (exit 0, "success") #821) and the Rewritten uv_lock_manifest_overrides record (Vendored uv with a user-authored override-dependencies: after any relock (uv add, uv lock --upgrade-package), vendor --revert / remove revert pyproject.toml but keep the vendored [manifest] overrides entry in uv.lock, so uv sync --locked fails (vendor --revert exits 0) #806) capture the entire <key> = [ … ] array, sibling elements included. Any relock that touches a sibling makes uv re-serialize the array:

    • uv add --dev x rewrites the group line;
    • uv add y sorts [manifest] overrides and splits it one element per line.

    Our element stays byte-identical, but the recorded text no longer matches, so revert called it drift. The convergence shortcut also searched the whole lock for the recorded array, so a byte-identical array under another key could fake convergence. That was found in review: see the resolved thread.

  2. No pair gate. A drift-kept uv.lock record only added a warning. The pyproject.toml records were still reverted and both files written. That contradicts docs/testing/uv-compatibility.md ("conflicting changes preserve both files … rather than restoring only one side").

Change

  • Key-anchored array revert (revert_array_elements). It handles uv_lock_requires_dev, uv_lock_manifest_constraints and the Rewritten uv_lock_manifest_overrides, none of which is text-matched across the whole lock any more. It finds the array under the record's own key, in the root unit's [package.metadata.requires-dev] or in [manifest]:
    • Untouched array: restored verbatim.
    • Re-serialized array: the recorded old and new arrays are compared to find our edits (an element rewritten in place, or one appended). Just that element is restored or removed, and the array is re-rendered the way uv writes it: one element inline, two or more one per line, in the lock's own line ending.
    • Our element edited and still routing through the artifact: drift.
    • Our element gone with nothing in the array routing through the artifact, or the key/section provably absent: silent convergence, per the LIVENESS CONTRACT.
    • Key present but unreadable (re-spelled, unbalanced, root unit missing): drift, fail-closed. This was found by Bugbot: see the resolved thread.
  • Pair gate: if any record is drift-kept, revert_uv writes neither file. The vendor_lock_entry_drifted warning still keeps the artifact and ledger entry, so a re-run finishes once the drift is undone. With this, the hosted takeover's redirect_vendored_revert_failed warning ("left in place") is accurate: revert_keeps_wiring in scan/hosted.rs keys on the same drift signal.
  • remove, rollback and the takeover all go through revert_pypi → revert_uv, so they get the same fix.
  • Docs: docs/testing/uv-compatibility.md now describes the relock behaviour.

Behaviour change to existing tests

Six existing unit tests asserted the half-revert that these issues report. #821 points at one of them, revert_warns_and_skips_on_drifted_lock_fragment ("The pyproject side (undrifted) was still reverted"). They now assert the pair gate: neither file is written on drift. One test, renamed to revert_removes_our_element_from_a_reshaped_manifest_overrides_array, covered a user reshaping the overrides array around our unchanged element. That is no longer drift: our element is removed and the user's reshaping is kept. No test was skipped or weakened. Every drift case still warns vendor_lock_entry_drifted and keeps the artifact.

Test evidence (red → green)

Test Covers Without fix With fix
pypi_uv::tests::revert_survives_uv_reserializing_the_dev_group_line #821 (uv add --dev) fail (drift warning, half-revert) pass
pypi_uv::tests::revert_survives_a_sibling_leaving_the_dev_group #821 (uv remove --dev) fail pass
pypi_uv::tests::revert_survives_uv_reserializing_manifest_overrides #806 fail pass
pypi_uv::tests::revert_removes_our_override_from_a_sorted_multi_line_array #806 (two user overrides) fail pass
pypi_uv::tests::revert_converges_on_the_overrides_key_not_a_lookalike_array #806 (review: lookalike array fakes convergence) fail (element stranded, artifact deleted) pass
pypi_uv::tests::revert_treats_an_unreadable_array_key_as_drift Bugbot: a locator miss is not convergence fail pass
pypi_uv::tests::revert_writes_neither_file_when_a_lock_record_is_drift_kept pair gate (#806, #821) fail (pyproject reverted alone) pass
e2e_vendor_pypi_build::uv_vendor_revert_after_dev_group_relock (real uv: vendor → uv add --dev zipp → vendor --revert → uv lock --check) #821 fail (uv.lock fragment … changed since vendoring; left untouched) pass
e2e_vendor_pypi_build::uv_vendor_revert_after_manifest_overrides_relock (real uv: user override-dependencies + transitive six → uv add idna==3.7 → vendor --revert → uv lock --check) #806 fail (same) pass

The e2e results were run locally against uv 0.8.17 with SOCKET_PATCH_UV_E2E_REQUIRED=1, so skips count as failures. The red runs used main's pypi_uv.rs with the new tests. CI runs this suite on its uv legs (0.2.37 through 0.12.17, Linux and macOS); releases without uv lock --check report n/a, as the existing capstones do.

Local gates (head 77686c4)

  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • rustfmt --check on both touched Rust files: clean (both were clean on main).
  • cargo test -p socket-patch-core --lib vendor::pypi_uv: 98/98 pass.
  • cargo test -p socket-patch-cli --test e2e_vendor_pypi_build -- --include-ignored: 25/25 pass.
  • cargo test --workspace --all-features --lib --bins (on c35bf96): every test passes except 4 write-permission tests in files this PR doesn't touch (copy_tree, vlt_heal, pypi_poetry, pypi_requirements). They expect a read-only path to refuse a write, which can't happen as root, and this sandbox runs as root. The integration suites are left to CI; a full local workspace build of every test binary didn't fit in the sandbox disk.
  • The npm, PyPI and gem wrappers only dispatch to the binary, so no change was needed there.

Per-issue checklist

  • Vendored uv package in a dependency group: after uv add --dev / uv remove --dev, vendor --revert, remove, rollback and the hosted takeover revert pyproject.toml but keep uv.lock's vendored requires-dev entry, so uv sync --locked fails (exit 0, "success") #821 vendor --revert after uv add --dev / uv remove --dev → the two dev-group unit tests and the real-uv e2e
  • Vendored uv package in a dependency group: after uv add --dev / uv remove --dev, vendor --revert, remove, rollback and the hosted takeover revert pyproject.toml but keep uv.lock's vendored requires-dev entry, so uv sync --locked fails (exit 0, "success") #821 remove / rollback / hosted takeover → the same revert_uv path; the pair gate makes the takeover's "left in place" true
  • Vendored uv with a user-authored override-dependencies: after any relock (uv add, uv lock --upgrade-package), vendor --revert / remove revert pyproject.toml but keep the vendored [manifest] overrides entry in uv.lock, so uv sync --locked fails (vendor --revert exits 0) #806 vendor --revert / remove after a relock re-serializes [manifest] overrides → the overrides unit tests (including the lookalike-array case) and the real-uv e2e
  • Both: genuine drift or an unreadable array keeps both files wired → revert_writes_neither_file_when_a_lock_record_is_drift_kept, revert_treats_an_unreadable_array_key_as_drift, plus the five updated drift tests

Follow-ups (out of scope)

  • When wiring, add_manifest_override still appends our element in socket-patch's own spelling rather than uv's sorted multi-line form. With key-anchored revert this no longer causes drift, so the wiring format is left as is.

🤖 Generated with Claude Code

https://claude.ai/code/session_016MCKtjJk6MF8zQWSFYWg7N


Generated by Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@claude
Start fix for #806, #821
ac0a657
Assisted-by: Claude Code:claude-opus-5-5
This was referenced Oct 5, 2026
Vendored uv with a user-authored override-dependencies: after any relock (uv add, uv lock --upgrade-package), vendor --revert / remove revert pyproject.toml but keep the vendored [manifest] overrides entry in uv.lock, so uv sync --locked fails (vendor --revert exits 0) #806
Closed
Vendored uv package in a dependency group: after uv add --dev / uv remove --dev, vendor --revert, remove, rollback and the hosted takeover revert pyproject.toml but keep uv.lock's vendored requires-dev entry, so uv sync --locked fails (exit 0, "success") #821
Closed
Claude (claude) added 3 commits October 5, 2026 03:47
@claude
Unwind uv vendoring after a relock
c35bf96
After vendoring, an ordinary uv relock (`uv add --dev x`, `uv add y`)
re-serializes the lock arrays that hold our element: the dev group's
requires-dev line and `[manifest] overrides`. Revert matched those
arrays by their exact recorded text, so it saw drift and kept
uv.lock wired, but still reverted pyproject.toml. The pair then
failed `uv sync --locked` while `vendor --revert` reported success.

Revert now finds our unchanged element inside the live array under
the same key and restores or removes just that element, rendering
the array the way uv writes it. A pair gate also writes neither
file when any record is genuinely drift-kept, so pyproject.toml and
uv.lock always stay consistent.

Fixes #806, #821.

Assisted-by: Claude Code:claude-opus-5-5
@claude
Test uv revert after a relock with real uv
bb174d2
Vendor six, run the uv command that re-serializes the lock array
around our element (`uv add --dev zipp` for a dev group, `uv add
idna` beside user overrides), then revert. Both files must be
unwired with no drift warning, and `uv lock --check` must pass.

Refs #806, #821.

Assisted-by: Claude Code:claude-opus-5-5
@claude
Document uv revert after a relock
f4e9b7b
Refs #806, #821.

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

Mikola Lysenko (mikolalysenko) commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Tanmay182003
Tanmay Singla (Tanmay182003) reviewed Oct 5, 2026
View reviewed changes
Comment thread crates/socket-patch-core/src/vendor/pypi_uv.rs Resolved

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Oct 5, 2026
View reviewed changes

@cursor cursor Bot left a comment •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

There was a problem hiding this comment.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread crates/socket-patch-core/src/vendor/pypi_uv.rs Resolved

Uh oh!

There was an error while loading. Please reload this page.

Claude (claude) added 2 commits October 5, 2026 04:10
@claude
Anchor uv array reverts on their key
f9b7cfa
A [manifest] overrides record holds the bare array, and the old
convergence shortcut searched the whole lock for it. When the root
requires-dist happened to match the user's overrides array, revert
treated our element as already gone, left it in uv.lock and deleted
the artifact it points at. Every whole-array record now reverts
through its own key: an untouched array is restored verbatim,
otherwise just our element is.

Refs #806.

Assisted-by: Claude Code:claude-opus-5-5
@claude
Fail closed when a uv lock array can't be read
77686c4
Revert treated any miss locating a whole-array record as convergence,
including a key spelled differently or an unbalanced array. A lock
that still routed through the vendored wheel could then lose the
wheel. Only a key or section that is provably absent now counts as
converged. Anything unreadable is drift, which keeps both files and
the artifact.

Refs #806, #821.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

cursor[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Oct 5, 2026
View reviewed changes

@cursor cursor Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

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 77686c4. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

[agent] One check is red on head 77686c4: PDM patch compatibility → native (ubuntu-latest, 2.22.4). One cell failed, 2.22.4 optional hosted (rescanIdempotent, 40.7s against the usual ~20s). The other 46 cells passed.

I don't think this PR caused it:

  • This PR only changes the uv backend (vendor/pypi_uv.rs), a uv e2e test and the uv docs. PDM hosted rewriting and PDM vendored wiring live in other modules. In scripts/backtest-pdm.py, uv is only used to bootstrap the PDM release under test.
  • The failing cell moves between runs. On f9b7cfa, a different leg failed in a different cell: native (macos-latest, 2.8.2), direct vendored (rescanAfterRelockApplies, rescanReusesWheel). That leg passes on 77686c4, and every cell passed on f4e9b7b.
  • Recent PDM runs on other PRs (Fix vendored requirements.txt re-vendoring removed pins (#786) #787, Fix gem crawler missing Bundler 4 standalone bundle (#796) #797, Fix npm VEX attesting a patch the twin lock lacks (#798) #799) needed a second attempt to go green.

No fix exists to port. Once this run finishes I'll re-run the failed job once. If it fails again, I'll treat it as real and dig into the case artifacts.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Mikola Lysenko (mikolalysenko) commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

[agent] Labeled Ready for review.

  • Head: 77686c45
  • CI: 482/482 check runs completed on head, 0 failing
  • Bugbot: reviewed 77686c45, no new issues; both earlier review threads resolved
  • Reviewer focus: the uv pyproject/uv.lock pair gate and the fail-closed path when a lock array can't be read (crates/socket-patch-core/src/vendor/pypi_uv.rs).

Slack announcement not sent this run (no Slack send tool available), so the next run will retry.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Tanmay182003
Tanmay Singla (Tanmay182003) approved these changes Oct 5, 2026
View reviewed changes
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 5, 2026
Vendored uv revert writes the pre-vendor specifier back into uv.lock after the user changes the vendored package's version spec, so uv sync --locked fails (vendor --revert / remove / rollback exit 0) #840
Closed
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
@claude
uv: run 19 ledger (#840; #806/#821 verified on #822)
033cb3f
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 5, 2026
Fix uv revert restoring a stale specifier (#840) #841
Merged
5 tasks done
@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator Author

[agent] Stacked follow-up: #841 fixes #840. That's a different cause in the same revert_uv function: the revert restores a stale specifier after the user edits the declaration. It builds on this branch.


Generated by Claude Code

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) merged commit b6d4a3a into main Oct 5, 2026
526 of 527 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-uv-revert-pair-gate branch October 5, 2026 11:44
@mikolalysenko Mikola Lysenko (mikolalysenko) mentioned this pull request Oct 5, 2026
Fix vendored gem rewrite breaking positional args (#847) #849
Merged
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
@claude
Merge main into agent/fix-uv-revert-stale-specifier
9983ac8
#822 landed on main as a squash commit. The conflicts with this
branch's copy of #822's commits are resolved by three-way merging
against #822's head (77686c4), so only this PR's own changes remain on
top of main.

Assisted-by: Claude Code:claude-opus-5-5
This was referenced Oct 5, 2026
Fix store-copy fold dropping copy writes (#756, #772) #774
Merged
Hosted uv rollback and remove delete a user-authored override-dependencies = ["<pkg>==<ver>"] pin that hosted mode never added #411
Closed
Vendored gem revert restores Gemfile.lock but leaves the Gemfile path: line when that line carries a trailing comment, and no re-run can finish it #988
Closed
Tracking: revert every vendored backend through one record-revert engine instead of nine hand-written mechanisms #989
Open
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cursor cursor[bot] cursor[bot] left review comments
@Tanmay182003 Tanmay Singla (Tanmay182003) Tanmay182003 approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

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

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Vendored uv package in a dependency group: after uv add --dev / uv remove --dev, vendor --revert, remove, rollback and the hosted takeover revert pyproject.toml but keep uv.lock's vendored requires-dev entry, so uv sync --locked fails (exit 0, "success") Vendored uv with a user-authored override-dependencies: after any relock (uv add, uv lock --upgrade-package), vendor --revert / remove revert pyproject.toml but keep the vendored [manifest] overrides entry in uv.lock, so uv sync --locked fails (vendor --revert exits 0)

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@mikolalysenko @Tanmay182003 @claude
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.