Repository navigation
Fix vendored uv/Hatch re-vendor to a newer patch (#742, #650) - #943
Open
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Open
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
This was referenced Oct 6, 2026
A uv project, a uv script lock (or pylock) and a Hatch project vendored with one patch could not move to a newer patch for the same package: scan --mode vendored, vendor and get --mode vendored failed with pypi_uv_source_already_exists, pypi_lock_source_already_exists or pypi_hatch_unsupported, and the project kept installing the old patch. The guards treated socket-patch's own wiring as a user source. When that wiring belongs to an older patch uuid of the same release and the vendor ledger still records it, vendor now replays the older entry's revert, keeping its wheel, and wires the new wheel fresh over the restored files. The new entry records the user's real pre-vendor originals, so vendor --revert still restores them byte for byte. If the old wiring was edited since vendoring, or the restored files refuse the new wiring, every file is put back and the run fails as before. Without a ledger entry it still refuses before writing. (#742, #650) Assisted-by: Claude Code:claude-opus-5-5
Drive the real binary through vendor with patch A, then vendor with patch B for a uv project, a uv script lock and a Hatch project. Each must move to patch B, remove patch A's wheel, settle on a re-run and restore the user's files on vendor --revert. (#742, #650) Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
[agent] Generated by Claude Code |
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 6, 2026 15:13
Collaborator
Author
|
BugBot review Generated by Claude Code |
Hatch reports its uv-installer and Hatch-version guards under the same pypi_hatch_unsupported code as a foreign direct reference, so a superseding patch could unwind patch A's wiring only for the fresh plan to refuse on a guard unrelated to it. Run those guards (pypi_hatch:: preflight) once a superseded entry is found, before anything is touched. restore_snapshot now attempts every file and names any it could not write back in the reported failure, instead of dropping the error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SECNaPEKiAJVRwYMMVAcLx
Collaborator
Author
|
bugbot run Generated by Claude Code |
Tanmay Singla (Tanmay182003)
approved these changes
Oct 6, 2026
There was a problem hiding this comment.
✅ 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 efc6d91. Configure here.
Collaborator
Author
|
[burn-down agent] Ready for review at head
Generated by Claude Code |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #742
Fixes #650
Summary
A uv project, a uv PEP 723 script lock (or pylock) and a Hatch project that
were vendored with one patch now move to a newer patch for the same package on
the next
vendor/scan --mode vendored/get --mode vendored. Beforethis, the run failed with exit 1 (
pypi_uv_source_already_exists,pypi_lock_source_already_existsorpypi_hatch_unsupported) and told theuser to revert wiring socket-patch had written itself. The project kept
installing the old patch.
#743 fixed the hosted half of both issues. This PR fixes the vendored
half, which is what keeps both issues open. #766 did the same for
requirements.txt, and #825 does it for Pipenv.
Root cause
The three vendored backends' pre-flight guards (
check_target_guardsinvendor/pypi_uv.rs,load_python_locksinvendor/pypi_lock.rs, andload→hatch::replacementinvendor/pypi_hatch.rs) accept only wiringfor the current patch uuid. They treat socket-patch's own
.socket/vendor/pypi/<older uuid>/source as a foreign one. Nothing everunwound the older uuid's wiring, even though the CLI contract says "a package
the ledger holds at an older patch uuid is still re-vendored automatically".
Fix
All in
crates/socket-patch-core/src/vendor/pypi.rs, shared by the threeflavors:
supersede_or_refuse: when one of those guards refuses, check the vendorledger. If it holds exactly one entry for the same name and version under
another uuid, with this flavor and recorded wiring, and a project file still
references that uuid dir, the plan becomes
WiringPlan::Supersede.Otherwise the refusal stands unchanged.
pypi_hatch_unsupportedcode. For Hatch,pypi_hatch::preflightchecksthose guards before anything is unwound, so a refusal unrelated to the
old wiring never touches the project.
unwire_superseded, at wiring time after the new wheel is built:group-commit-aware
utils::fsreaders. Wiring paths from the tamper-ableledger must be plain relative paths outside
.socket/.keep_artifact. The CLI alreadysweeps the old uuid dir (
vendor_stale_artifact_removed) once the newledger entry lands.
to the old uuid dir.
(
fresh_pyproject_plan) and wires the new wheel through the normal path.The new entry therefore records the user's real pre-vendor originals, and
vendor --revertrestores them byte for byte.Any failure (drifted wiring, a fresh guard refusing, a wiring error) puts
every snapshotted file back and sweeps the new wheel, so the tree is left as
it was. In a
vendorrun those writes sit inside the group commit. Any filethat can't be restored is named in the reported error, never dropped. With
no ledger entry for the old uuid it still refuses before writing, because
there would be no recorded original to restore.
docs/testing/hatch.mdnow notes the vendored re-vendor.CHANGELOG.mdisuntouched (release-time only).
Also ported: 13f6eee is #878's Gradle digest routing. Main has been red on
production_digests_go_through_the_helperssince #865. The port becomes ano-op once #878 lands.
Tests (red → green)
vendor::pypi::tests::pyproject_flavors_revendor_to_a_superseding_uuid(each flavor: re-vendor to uuid B, originals carried, no uuid A left, revert byte-exact)tests/mode_migration_pypi.rs::pyproject_flavors_vendored_revendor_superseding_patch(real CLI:vendorA →vendorB → in-sync re-run →vendor --revert;vendor_stale_artifact_removed, ledger on B only)pypi_uv_source_already_exists(exit 1)pyproject_flavors_superseding_uuid_with_drifted_wiring_refuses(hand-edited wiring: refuses, files byte-identical, no uuid B dir)pyproject_flavors_superseding_uuid_without_ledger_refuses,uv_stale_uuid_vendor_refuses_through_orchestrator(no ledger entry: still refuses before writing)hatch_unrelated_guard_refuses_superseding_patch_before_unwinding(CLI, installer guard set: files, ledger, artifact A unchanged, no uuid B dir)restore_snapshot_reports_unrestored_files_and_restores_the_restLocal checks:
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast: 10822 passed, 12failed. All 12 are chmod 0o555 permission-denial tests that can't fail when
run as root, which this sandbox is (uid 0). The same 12 are noted on Fix uv/Hatch hosted re-pin to a newer patch (#742, #650) #743,
and CI runs them as non-root.
🤖 Generated with Claude Code
https://claude.ai/code/session_01SECNaPEKiAJVRwYMMVAcLx
Note
Medium Risk
Changes vendored wiring, ledger-driven revert, and atomic file restore on failure; behavior is heavily tested but mistakes could leave projects half-wired or restore incorrectly.
Overview
Vendored PyPI re-vendor when a newer patch supersedes the same release — uv projects, PEP 723 script locks, and Hatch no longer fail with
pypi_*_already_exists/pypi_hatch_unsupportedwhen the vendor ledger still records socket-patch’s own wiring at an older patch UUID.When those flavor guards would refuse “foreign” wiring that is actually the tool’s prior vendored path,
vendor/pypi.rsnow detects a single superseded ledger entry, plansWiringPlan::Supersede, and at wiring time snapshots project files, replays the old entry’s revert (keep_artifact), re-plans fresh wiring, and wires the new UUID. Failures restore the snapshot and sweep the new wheel; Hatch runspypi_hatch::preflightbefore any unwind so unrelated installer guards leave the tree untouched.Tests and docs add CLI and core coverage for happy path, drift, missing ledger, Hatch guard-before-unwind, and snapshot restore errors; Hatch testing docs note vendored supersede behavior.
Minor refactor: Gradle/JVM/Maven SHA-1/SHA-256 hashing routes through
utils::digesthelpers (aligned with #878).Reviewed by Cursor Bugbot for commit efc6d91. Configure here.
Generated by Claude Code