Skip to content

Fix uv hosted unwind declaration matching (#606, #473) - #625

Open
Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
mainfrom
agent/fix-uv-unwind-declaration-match
Open

Mikola Lysenko (mikolalysenko) wants to merge 8 commits into
mainfrom
agent/fix-uv-unwind-declaration-match

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Ready for review on cfae77e3: all 488 check runs pass and Bugbot reports no issues on this commit.

Fixes #606 and #473. Hosted uv rollback, removal and vendored takeover previously refused when a package had different specifiers across ordinary dependencies, extras or marker-split declarations, or when it reached a dependency group through a PEP 735 include.

The unwind now records which optional group each declaration came from, expands normalized include-group references with a cycle guard, and uses the lock entry's marker to find the matching declaration. Marker comparisons account for uv's supported python_version → python_full_version rewrites. When every candidate's version clauses agree, the unwind uses them without having to work out which declaration the entry came from.

Declarations whose own markers use extra need care. uv can give a direct dependency and an optional dependency identical lock markers even though their version constraints differ. A simple forward equality (extra == 'name') is still matched when that's unambiguous. Any other explicit-extra expression, including the reversed 'name' == extra, refuses when the clauses differ, so the unwind can't silently report success after writing the wrong requirement. The supported subset and the refusal behavior are documented in docs/testing/uv-compatibility.md.

Each package's lock edits are restored together or discarded together on refusal, and the new transaction regression confirms that a refusal leaves both uv.lock and pyproject.toml untouched.

Validation:

  • 87 focused tests pass: uv upstream unit tests, redirect unit tests, uv restore transaction, mode-migration, hosted-engine and VEX tests.
  • Real uv capstones (extras and include-group lanes of e2e_redirect_uv_build) cover the hosted rewrite, fresh and plain installs, VEX, online PyPI reconstruction and a byte-exact rollback of both project files.
  • The forward- and reversed-equality collision cases fail before the fix and pass after it, in LF and CRLF and in normal and dry-run modes. Native uv lock --check --offline accepts the pristine lock and rejects the wrongly collapsed one.
  • VEX discovery golden: upstream.json was added for the new native fixture (two registry packages, no patched references). The 17 existing snapshots are unchanged.
  • The Windows fixture checkout is protected with tests/fixtures/upstream/** -text, the same rule the other native lock fixtures use.
  • CI on cfae77e3: 488/488 green. Compatibility-lane jobs that failed on earlier commits (PDM, Poetry, Bun) were in code paths this PR doesn't reach, and they passed on re-run or on the next commit.

🤖 Generated with Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Regression tests for #606 and #473: the hosted uv unwind must put a
lock entry's specifier back when one package is declared with
different specifiers per extra or marker, or reaches a group through a
PEP 735 include-group. These fail on main.

Assisted-by: Claude Code:claude-opus-5-5
Rollback, remove and the hosted to vendored takeover refused to unwind
a hosted uv pin when the package was declared with different
specifiers in dependencies and an extra (or under different markers),
or reached a dependency group through a PEP 735 include-group. Each
lock entry is now matched to the declaration uv lowered it from: the
marker's extra terms pick the extra, the rest of the marker picks among
marker-split lines, and include-group members are expanded. An entry
no declaration matches is still refused.

Adds real-uv extras and include-group lanes to e2e_redirect_uv_build.

Fixes #606, #473.

Assisted-by: Claude Code:claude-opus-5-5
Lock an idna sibling in the extras and include-group lanes so the
hosted rollback actually re-derives the registry entry, and require it
to restore byte for byte.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 3, 2026 00:54
@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.

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

Copy link
Copy Markdown
Collaborator Author

Ready for review at head 193e195481c1f6d9e6ab95ed14772946d7ff4d13.

  • CI: 488/488 check runs green on this head (6 skipped, none failed).
  • Bugbot: reviewed 193e195 and found no new issues. There are no review threads open.
  • Reviewer focus: the hosted uv unwind now matches lock requirement entries against pyproject declarations with extras, markers and include-group taken into account, where before it matched by bare name over a flattened set.

Generated by Claude Code

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator Author

Review updated for cfae77e3e4a5a25c1d8bab28b123fc8c56808c45: Ready to merge as-is from this review. Final-head CI is complete: 482 successful checks, 7 skipped; no failures or pending checks. Bugbot passed, there are no unresolved review threads, and the PR is mergeable.

The unwind preserves unambiguous simple extra == 'name' declarations and the per-package rollback. Other declaration-owned extra expressions, including reversed equality, now refuse when their version clauses differ, preventing two native lock entries from being restored to one incorrect constraint. The supported subset is documented.

All 87 focused tests and both real uv 0.11.19 capstones passed on 7f46a948; the tested source and native-fixture files are byte-identical in cfae77e3. The capstones include fresh/plain installs, VEX, online PyPI reconstruction and byte-exact rollback for ordinary extras and include-groups. The new transaction regression uses native-generated fixtures and verifies both forward and reversed collision cases across LF/CRLF and normal/dry-run modes, preserving both files on refusal. The reversed case fails on parent 413ffb38 and passes after the correction; native uv independently rejects the previously corrupted lock.

The author added the required discovery golden in 48bd2391. It exactly matches the independently generated snapshot: the pristine fixture contains two registry packages and no patched references. All four golden tests passed again on cfae77e3 with updates disabled, all 17 existing snapshots are unchanged, and Bugbot passed.

Windows CI exposed a separate fixture checkout issue: Git converted LF to CRLF, then the regression test created invalid double-CRLF bytes. The author added the existing repo convention of -text protection for this native fixture family in cfae77e3. An isolated core.autocrlf=true Git checkout reproduced the exact failing bytes before the rule and preserved the committed fixture bytes after it. All 43 upstream restore fixture tests passed both locally and in Windows CI on this commit. No production or test-source change was needed, and no retry of the known-broken commit was requested.

Independent marker and rollback reviews are clear. The committed sources match the tested files; core clippy and diff checks pass, and new production/test blocks match rustfmt, with the existing unrelated macOS warning recorded. The commit merges cleanly with current main. No remaining actionable finding from this review.

The Ready label is restored after all checks completed on the corrected commit.

CI note: macOS Bun 1.3.10 initially hit a patch-service connection timeout before the expected workspace refusal; its other 52 cases passed and the harness recorded no project-file changes. One targeted job retry passed all 53 cases, including the correct refusal. No source change was needed for that network failure.

@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 3, 2026
A dependencies line can carry its own extra == 'x' marker, so its
lock entry looks exactly like one lowered from extra x. When the two
declare different specifiers, which entry mirrors which is not
derivable; the unwind now refuses instead of restoring both from the
optional declaration and silently dropping the direct requirement.

A refused hit also leaves uv.lock exactly as it was: its entry and
requirement arrays are restored together or not at all.

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

Copy link
Copy Markdown
Collaborator Author

[agent] Confirmed the P2 finding and fixed it in 413ffb3. A dependencies declaration whose own marker has extra == '<x>' now stays a candidate for lock entries whose marker names x. When it and extra x's member declare different specifiers, the unwind refuses ("different specifiers … not derivable") instead of restoring both from the optional declaration. A refused hit also now rolls uv.lock back to its state before that hit (doc snapshot in restore_lock), so a partial restore can't be written alongside another hit.

Tests (red without the narrowing fix, green with it): dependency_with_its_own_extra_marker_is_ambiguous is your case and must refuse; dependency_with_its_own_extra_marker is the unambiguous variant and still restores. All 19 upstream::uv tests pass, and clippy is clean.


Generated by Claude Code

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

Preserve simple forward extra equality and all-agree matching, but keep
other declaration-owned extra expressions ambiguous when clauses differ.
Reversed equality can produce the same native uv marker as an optional
group, so selecting only that group loses the original requirement.

Add native-fixture transaction coverage for both comparison directions,
LF/CRLF and dry runs, retaining all hosted bytes when unwind is refused.
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review

Please review the corrected commit 7f46a948ea4a79b0a2cb81f95060c2398783db11.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI: native (macos-latest, 2.1.4) (Poetry patch compatibility) failed on 7f46a94 with one cell, 2.1.4 direct hosted FAIL appliedExactlyOne. The other four cells passed. This PR changes only the uv hosted unwind (upstream/uv.rs), which the Poetry hosted apply never calls. It matches the earlier one-cell appliedExactlyOne failure in the PDM macOS hosted lane on 658bc04, which passed on the next head, and the transport blips that open PRs #596 and #610 target. I re-ran the failed job once.


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.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI: native (ubuntu-latest, 2.29.2) (PDM patch compatibility) failed on 7f46a94 with one cell, platform-windows vendored FAIL rescanIdempotent; the other 46 rows passed. That's PDM vendored mode, which never calls the uv hosted unwind this PR changes. The same job passed on 193e195, and between the two commits only the declaration matching in upstream/uv.rs changed. I re-ran the failed job once.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] Update on the PDM run for 7f46a94: 40 of its 43 jobs passed, and 3 failed with one cell each, all different: ubuntu 2.29.2 platform-windows vendored (rescanIdempotent), macos 2.29.2 static-urls vendored, ubuntu 2.22.4 optional agent. The failures are spread across vendored and agent modes, neither of which reaches the uv hosted unwind this PR changes, and the same workflow passed on 193e195. I re-ran the three failed jobs once now that the run has finished.


Generated by Claude Code

The uv explicit-extra fixture added under tests/fixtures/upstream joins
the VEX discovery golden corpus, which needs a golden for every
fixture directory. Regenerated with SOCKET_PATCH_UPDATE_GOLDEN=1: the
pristine lock has no hosted refs, only its two registry packages.

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

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI: test (ubuntu-latest) failed on 7f46a94 in vex::discover::testing::golden::tests::committed_fixture_corpus_matches_golden with upstream: no golden. The new tests/fixtures/upstream/uv-explicit-extra directory joined the VEX discovery golden corpus, which needs a golden for every fixture directory. Fixed in 48bd239 by regenerating with SOCKET_PATCH_UPDATE_GOLDEN=1. The new upstream.json records no hosted refs, only the pristine lock's idna/six registry entries. That golden test now passes locally.


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.

On Windows, autocrlf turned the uv explicit-extra fixture into CRLF,
so the restore test's CRLF variant became CR CR LF and failed. Mark
tests/fixtures/upstream as -text like the other native lock fixtures.

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

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI: test (windows-latest) failed on 48bd239 in -p socket-patch-core --test upstream_restore_golden. The Windows checkout applies core.autocrlf, so tests/fixtures/upstream/uv-explicit-extra/* arrived as CRLF. uv_explicit_extra_collision_refuses_without_writing then built its CRLF variant with .replace('\n', "\r\n") and got \r\r\n. Fixed in cfae77e: .gitattributes now marks tests/fixtures/upstream/** -text, the same rule the poetry/pipenv/pdm-native lock fixtures use. The files are LF in the repo, so nothing else changes.


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 cfae77e. Configure here.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI: native (macos-latest, 1.3.10) (Bun patch compatibility) failed on cfae77e. Bun's npm-family code paths aren't touched by this PR, which changes only the uv hosted unwind (upstream/uv.rs), a uv fixture, its VEX golden and .gitattributes. That job passed on 193e195. I'll re-run the failed job once when the run finishes.


Generated by Claude Code

This branch has not been deployed

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

Labels

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

Projects

None yet

2 participants