Skip to content

Fix uv sources unwind on dotted/sub-table spellings (#544, #524) - #545

Merged
Mikola Lysenko (mikolalysenko) merged 6 commits into
mainfrom
agent/fix-uv-sources-table-spelling
Oct 2, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 6 commits into
mainfrom
agent/fix-uv-sources-table-spelling

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

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

Fixes #544
Fixes #524

Summary

After vendoring or a hosted scan, uv projects whose existing [tool.uv] sources aren't written as a plain [tool.uv.sources] table now unwind cleanly:

Root cause

The uv wiring assumed sources live under an explicit [tool.uv.sources] header as name = {…} lines. toml_edit, though, writes a new key in the spelling its parent already has:

  • Vendored mode recorded a hard-coded six = { path = … } in the ledger, while the file held sources.six = { … }. Revert's exact-line splice missed it, and the fragment was treated as user drift.
  • A parent that exists only implicitly (implied by [tool.uv.sources.<pkg>] sub-tables) prints its header once a key is added. Vendored mode decided "we didn't create the table" because the table existed, and the hosted restore left it explicit.

Changes

Known trade-off: a user who wrote an explicit, key-less [tool.uv.sources] header with only sub-tables beneath it gets that header dropped by a hosted rollback. The hosted path has no ledger, so it can't tell that header apart from one the scan made explicit. The result still parses identically, and that shape is unusual.

An earlier cargo fmt --all had also reformatted 124 unrelated files (main isn't rustfmt-clean, and CI has no fmt check). Those are reverted at f1a45b0, so the diff is just the 4 files above.

No wrapper changes are needed (npm/, pypi/, gem/ only dispatch to the binary).

Test evidence

Each regression test was run red on the pre-fix code and green with the fix:

Issue Test Without fix With fix
#544 vendor::pypi_uv::tests::revert_round_trips_dotted_sources_under_tool_uv drift warning ok
#544 …revert_round_trips_dotted_sources_path_key drift ok
#544 …revert_round_trips_dotted_uv_sources_under_tool (follow-up comment's [tool] + uv.sources) drift ok
#544 …revert_round_trips_root_dotted_tool_uv_sources drift ok
#544 …revert_round_trips_dotted_sources_crlf drift ok
#544 …revert_round_trips_dotted_override_under_tool (transitive) drift ×2 ok
#544 e2e e2e_vendor_pypi_build::uv_vendor_revert_dotted_sources_key, …_dotted_sources_url_key (real uv 0.8.17, plus uv lock --check after revert) vendor_lock_entry_drifted ok
#524 …revert_drops_header_made_explicit_over_sub_tables (+ _override, _crlf) header residue ok
#524 patch::redirect::upstream::uv::tests::restore_drops_sources_header_made_explicit_over_sub_tables (+ _transitive, _crlf) header residue ok
#524 e2e e2e_vendor_pypi_build::uv_vendor_revert_sub_table_sources (real uv 0.8.17) [tool.uv.sources]\n\n residue ok
control revert_keeps_user_authored_sources_header, restore_keeps_user_sources_spellings – ok

Commands run locally on bec2311:

  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test --workspace --all-features --no-fail-fast: all pass except 12 permission-injection tests (chmod/set_permissions read-only fixtures in covgap_commands_vendor, in_process_redirect, repair, and core copy_tree/vlt_heal/pypi_poetry/pypi_requirements). Those can't fail as intended because the sandbox runs as root. None of them is in a file this PR touches, and CI runs as non-root.
  • cargo test -p socket-patch-cli --all-features --test e2e_vendor_pypi_build -- --include-ignored (uv 0.8.17): every uv test passes, including the 3 new ones. Re-run on f1a45b0: 23/23 pass.
  • cargo test -p socket-patch-cli --all-features --test e2e_redirect_uv_build -- --include-ignored (uv 0.8.17): 16/16 pass (also on f1a45b0).

🤖 Generated with Claude Code

https://claude.ai/code/session_013Lodu4CMs7Cfqq8pEzPXpb


Note

Medium Risk
Changes pyproject/uv.lock revert and hosted rollback logic for dependency wiring; mistakes could leave projects pointed at vendored wheels or corrupt TOML layout, though heavily regression-tested.

Overview
Fixes vendor revert and hosted metadata restore for uv projects that express [tool.uv] sources with dotted keys or sub-table-only parents (#544, #524).

Vendored path: wiring now records each added pyproject.toml line exactly as toml_edit rendered it (e.g. sources.six = … vs six = …), so revert splices match and no longer flag socket-patch’s own line as drift. Implicit parents that only existed via [tool.uv.sources.<pkg>] sub-tables are treated as “ours,” so revert also drops headers the wire step introduced.

Hosted restore: after removing hosted source entries, [tool.uv.sources] / [tool.uv] are made implicit again when they only contain sub-tables, restoring the original sub-table-only spelling.

TOML surgery: remove_table_if_empty no longer strips blank lines that belong to the next section header (fixes spacing when removing an empty [tool.uv.sources] above sub-tables).

Coverage adds unit tests across pypi_uv, upstream uv restore, and toml_surgery, plus three real-uv e2e round-trip tests (dotted keys, dotted url key, sub-table sources).

Reviewed by Cursor Bugbot for commit f1a45b0. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Vendored uv revert misreads its own sources line as drift when the
project spells sources as dotted keys (#544), and leaves behind the
[tool.uv.sources] header it made explicit over sub-tables (#524).

Assisted-by: Claude Code:claude-opus-5-5
Vendored revert and remove now find the sources and override lines
they wrote even when the project spells [tool.uv] sources as dotted
keys, so a revert no longer reports its own line as drift and leaves
pyproject.toml routed to the vendored wheel while uv.lock is restored
(which broke uv sync --locked). (#544)

When a project only has [tool.uv.sources.<pkg>] sub-tables, the scan
has to print an explicit [tool.uv.sources] header. Vendored revert and
hosted rollback/remove now drop that header again, so an unwind leaves
pyproject.toml byte-identical. (#524)

Assisted-by: Claude Code:claude-opus-5-5
Vendors and reverts six on real uv projects whose existing sources use
a dotted key, a dotted .url key, or [tool.uv.sources.<pkg>] sub-tables,
and checks the unwind is byte-identical and passes uv lock --check.
(#544, #524)

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

Copy link
Copy Markdown
Collaborator Author

[agent] Ready for review at bec2311. CI is green (484 checks passed, 6 skipped by design) and Bugbot found no issues. Two legs failed on first run for reasons outside this change, and each passed when re-run once: native (macos-latest, 1.1.15) (Poetry populated/hosted appliedExactlyOne; the same leg is green on #543's run off the same main) and e2e (macos-latest, e2e_vex_build, pipenv::) (a DNS failure fetching pipenv from files.pythonhosted.org before any test body ran).


Generated by Claude Code

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

Copy link
Copy Markdown
Collaborator Author

[agent] Labeled Ready for review at bec2311. I re-checked it: 484/484 non-skipped check runs green, Bugbot success on this head, no unresolved threads, 0 commits behind main. Slack announcement pending: this run has no Slack send tool.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Reviewed bec23111c24f551adde5087890f9c7d67c97409f. Recommendation: ready to merge as-is from code review.

No actionable correctness/security regressions found in the rendered-line ledger recording or implicit-header cleanup. Normalizing both sides with rustfmt confirms the surrounding 124-file churn is formatting-only; reviewed the four semantic files.

Validation: core uv tests — 130 passed; toml_surgery tests — 8 passed; all 3 new real-uv revert tests passed with uv 0.11.19 on macOS (SOCKET_PATCH_UV_E2E_REQUIRED=1, no fixture skips). Full workspace and historical uv matrix not rerun.

An earlier cargo fmt --all run reformatted 124 files this fix doesn't
touch. Restore them to main so the PR only carries the uv sources
change and its tests.

Assisted-by: Claude Code:claude-opus-5-5
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] Thanks for the review. The 124 files of formatting-only churn it flagged came from my cargo fmt --all run (main isn't rustfmt-clean). They're reverted to main at f1a45b0, which also merges the latest main. The PR now changes only the 4 semantic files. On f1a45b0 I re-ran clippy, the uv/toml_surgery unit tests (106 passed), e2e_vendor_pypi_build --include-ignored (23/23) and e2e_redirect_uv_build --include-ignored (16/16) against real uv 0.8.17.


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

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final incremental review of f1a45b01ff173e5a26494f3dbbd753f57f9e45ab: code review remains clear. The changes since my original review remove formatting churn and merge the already-reviewed #543; comparing against that expected merge after Rust formatting normalization found no additional semantic changes.

Validation carried forward: 130 uv unit tests, 8 TOML surgery tests, 3 real-uv tests, plus #543’s 55 discovery tests and 33 vendor rescan tests passed on the reviewed component heads. These were not rerun for the final formatting/merge-only update.

The failed PDM CI job is blocked by an external patch API HTTP 504, confirmed in its uploaded scan output. Recommendation: merge once the failed CI check has been rerun successfully.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] native (ubuntu-latest, 2.29.2) failed one case (PDM direct hosted, appliedExactlyOne). The cause was an HTTP 504 from the external patch API during the hosted scan, as the review above confirmed from the job's scan output. It isn't this PR's: no PDM code is changed here, and the same leg was green on bec2311. No fix is needed in the PR. I've queued the one re-run of that job.


Generated by Claude Code

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

3 participants