Skip to content

Fix vendored PyPI revert of entries wired across flavors (#1524) - #1526

Merged
Mikola Lysenko (mikolalysenko) merged 6 commits into
mainfrom
agent/fix-pypi-mixed-flavor-revert
Oct 14, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 6 commits into
mainfrom
agent/fix-pypi-mixed-flavor-revert

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Fixes #1524

What it does

Vendored PyPI entries whose wiring spans two backends can now be
reverted. The #1524 case: a project vendors requirements.txt, then a
pylock.toml appears and the next scan --mode vendored wires it too.
Before this fix, vendor --revert exited 0 with success and restored
nothing, remove exited 1, and the hosted takeover refused. Each printed
a remedy that couldn't be followed. Now the revert restores both files
and deletes the wheel.

Root cause

A re-run restamps the entry's flavor (requirements → python-lock),
but carry_forward_wiring keeps the earlier requirements_line
records. That is deliberate, because the revert still has to restore
that file. revert_pypi_opts then sent the whole entry to the backend
of that one flavor, and each lock backend calls a record it doesn't own
drift (vendor_lock_entry_drifted: unexpected Python wiring file or kind: requirements.txt (requirements_line)). So the CLI's own wiring
kept the entry, and the wheel, forever. Any mix fails the same way:
requirements then poetry/pdm, or a pylock record under a requirements
entry, which the requirements revert read as a vendor line in
pylock.toml.

Fix

  • Revert by record kind. revert_by_record_kind (vendor/pypi.rs)
    replaces the uv/Pipenv-only revert_with_sibling. It sends each
    record to the backend that owns its kind (record_owner), in this
    order: requirements lines, then pylock records, then other backends'
    records, then the entry's own flavor. If a later group fails or is
    drift-kept, the earlier groups' files are put back, the same rollback
    the uv and Pipenv export paths already had. A kind no backend owns
    stays with the entry's flavor, so its backend reports it as before.
    When one backend owns every record, that backend reverts the entry,
    even if it isn't the flavor the entry is stamped with (Bugbot finding,
    00abb90).

  • Takeover preflight by record kind (faf549a, review finding). Now
    that a mixed entry reverts fully, preflight_pypi_takeover checks
    reach the same way:

    • requirements_line records under any non-requirements flavor;
    • the uv check whenever the entry holds uv_ records;
    • the Poetry check whenever it holds poetry_ records.

    Without this, a poetry/pdm/pylock entry could have a requirements line
    in a -r include, or an appended transitive line, reverted with no
    hosted pin to replace it.

  • Routing note. The pypi_multiple_lockfiles note used to say an
    already vendored requirements.txt "retain[s] its existing sources".
    When requirements.txt holds this package's # socket-patch vendor:
    line, the note now says it "keeps its vendored wiring". The check uses
    the requirements backend's own vendor-line parser.

The npm, PyPI and gem wrappers only dispatch to the binary and have no
revert logic, so they need no change.

Tests (red before the fix, green here)

Issue / case Regression test Before the fix
#1524: requirements then pylock.toml (revert, dry-run, routing note) vendor::pypi::tests::requirements_then_pylock_entry_reverts_both_files main: vendor_lock_entry_drifted: unexpected Python wiring file or kind: requirements.txt (requirements_line)
#1524 variant: requirements then poetry.lock / pdm.lock vendor::pypi::tests::requirements_then_tool_lock_entry_reverts_both_files main: ignoring wiring record for unexpected file requirements.txt (only poetry.lock is poetry-owned), artifact kept
#1524 variant: requirements entry carrying a pylock record vendor::pypi::tests::requirements_entry_with_a_pylock_record_reverts_both_files main: pylock.toml: the vendor line ... changed since vendoring + residual reference
#1524 variant: all records owned by a backend other than the stamped flavor vendor::pypi::tests::entry_whose_records_all_belong_to_another_flavor_reverts 4bc92fa and main: drift-kept by the stamped flavor's backend
takeover of a mixed entry with an include or transitive requirements line patch::redirect::pypi_takeover::tests::requirements_lines_under_another_lock_flavor_are_checked, uv_records_under_another_flavor_are_checked 00abb90: preflight admitted it

Local runs:

  • cargo fmt --all -- --check: clean (faf549a).
  • cargo clippy --workspace --all-features -- -D warnings: clean (faf549a).
  • cargo test -p socket-patch-core --lib --all-features (4bc92fa): 6478
    passed, 4 failed. The 4 are chmod-based tests (copy_tree symlinked
    root, vlt_heal unremovable lock, poetry/requirements write failure).
    They fail because this sandbox runs as uid 0, which ignores permission
    bits; Fix hosted/vendored cargo floor reads (#653, #651) #1429 reports the same 4 on main. On faf549a,
    patch::redirect::{pypi_takeover,requirements} passed 32/32.
  • cargo test -p socket-patch-cli --all-features --test mode_migration_pypi -- --include-ignored
    (faf549a): 63 passed.
  • cargo test -p socket-patch-cli --all-features --test e2e_vendor_pypi_build -- --include-ignored
    (uv 0.11.32, 4bc92fa): 55 passed.
  • CI on faf549a: every check green (ci-ok success). Bugbot found no new
    issues.

/code-review high raised 9 findings on the first version. This version
addresses them:

  • per-kind dispatch instead of a single-pair special case;
  • the routing note uses the vendor-tag parser, which handles sdists,
    dotted names, comment lines and other packages' lines, and reads files
    with read_regular_to_string;
  • the note no longer claims a lock reverts with it;
  • tests for the other flavor mixes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GtbdfcgfARj65ipZ22JEDm

Assisted-by: Claude Code:claude-opus-5-5
Vendoring requirements.txt and then adding a pylock.toml left a ledger
entry that `vendor --revert`, `remove` and the hosted takeover could
never unwind: the re-run restamped the entry's flavor as python-lock
but kept the requirements.txt record, and the python-lock revert
called that record drift. Revert exited 0 and restored nothing.

Every lock flavor now reverts its requirements lines through the
requirements backend before its lock, and a requirements entry that
carries pylock records reverts them through the python-lock backend.
The routing note no longer says an already vendored requirements.txt
keeps its registry source.

Fixes #1524

Assisted-by: Claude Code:claude-opus-5-5
The routing note's "already vendored" check matched any line under
.socket/vendor/pypi/, so another package's vendored line would mark
requirements.txt as wired for this one. It now matches only a line
that pins this package's own wheel at this version.

Assisted-by: Claude Code:claude-opus-5-5
The first fix covered one pair of record kinds, but a re-run carries
any earlier wiring forward, so other mixes stayed stuck: requirements
lines under a poetry or pdm entry were kept as drift, and a pylock
record under a requirements entry was read as a requirements line.

The revert now sends each record to the backend that owns its kind:
requirements lines first, then pylocks, then other backends' records,
and the entry's own flavor last. If a later group fails or drifts, the
files reverted so far are put back, as the uv and Pipenv exports
already did.

The routing note now uses the requirements vendor tag to tell that
requirements.txt is already vendored for this package. It no longer
guesses from paths, and it doesn't claim lock files revert with it.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) changed the title Fix vendored PyPI revert of mixed requirements+pylock entries (#1524) Fix vendored PyPI revert of entries wired across flavors (#1524) Oct 14, 2026
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 14, 2026 09:02
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

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

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vendor/pypi.rs Outdated
When every record of an entry belongs to one backend that isn't the
flavor the entry was last stamped with, the revert still called the
stamped flavor's backend, which kept the wheel as drift. It now calls
the backend that owns the records.

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

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

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

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vendor/pypi.rs
Now that a mixed entry reverts fully, the hosted takeover could revert
a requirements.txt line from a -r include, or an appended transitive
line, under a pylock, poetry or pdm entry. Its preflight only checked
requirements lines for uv and Pipenv entries, so the package then went
unpinned in requirements.txt and installed the unpatched release.

The preflight now checks requirements lines under any non-requirements
flavor, and the uv and Poetry checks whenever the entry holds their
records, matching how the revert dispatches.

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

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

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

✅ 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 faf549a. Configure here.

Merged via the queue into main with commit cbfbd9a Oct 14, 2026
54 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-pypi-mixed-flavor-revert branch October 14, 2026 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants