Skip to content

Hosted uv rollback and remove delete a user-authored override-dependencies = ["<pkg>==<ver>"] pin that hosted mode never added #411

Description

[agent] Found by the scheduled uv bug-hunt routine (ledger #310).

Summary

On a uv project whose pyproject.toml already pins a transitive dependency with [tool.uv] override-dependencies = ["six==1.16.0"], a hosted scan leaves that override alone and only adds the [tool.uv.sources] url. rollback and remove pkg:pypi/[email protected] then delete the user's own override anyway. They also drop the now-empty [tool.uv] table and the lock's [manifest] overrides entry, and they warn upstream_uv_override_removed ("removed the six==1.16.0 override-dependencies entry the hosted run adds"), which isn't true here.

v5 hosted mode keeps no ledger, so the upstream restore guesses ownership: pushed_override treats any override-dependencies entry spelled exactly <name>==<version> for a transitive dependency as the one the rewrite added (crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:1086). A user who pinned that exact version themselves can't be told apart, and loses the pin.

Impact

This silently deletes user-authored resolver configuration. The lock still names 1.16.0 right after the rollback, so nothing fails at once. But the next uv lock --upgrade (or any re-resolve) moves six to 1.17.0, which the user had explicitly overridden away from. The warning tells them hosted mode added the line, so they have no reason to put it back. An exact-version override of a transitive dep is the usual way to hold back a problematic transitive release, and a project with such a pin is a natural candidate for scan --mode hosted.

Repro

Uses a local mock of the patch API serving a hosted six-1.16.0 wheel (the same mock as #379 / #381, now also returning integrity.sha512), plus --patch-server-url for the mock origin. The PyPI JSON API must be reachable (in the sandbox I used a local forwarder via SOCKET_PYPI_JSON_API; the probe runs hit pypi.org directly).

SP="socket-patch --api-url http://127.0.0.1:18080 --api-token t --org test-org --patch-server-url http://127.0.0.1:18080"
mkdir p && cd p
cat > pyproject.toml <<'TOML'
[project]
name = "uvp"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = ["python-dateutil==2.8.2"]

[tool.uv]
override-dependencies = ["six==1.16.0"]
TOML
uv lock
cp pyproject.toml pyproject.orig
$SP scan --mode hosted --json --yes     # redirected 1; vs pyproject.orig it adds ONLY [tool.uv.sources] six = { url = … }
$SP rollback --json --yes               # exit 0, success; warnings: reinstall_required, upstream_uv_override_removed
diff pyproject.orig pyproject.toml      # -[tool.uv]  -override-dependencies = ["six==1.16.0"]
grep -c '^overrides' uv.lock            # 0 (was 1)
uv lock --upgrade && grep -A1 'name = "six"' uv.lock   # version = "1.17.0"
# Control, same pyproject, no socket-patch: uv lock --upgrade keeps six 1.16.0.
# `remove pkg:pypi/[email protected] --json --yes` instead of rollback behaves the same.

Expected vs actual

  • Expected: CLI_CONTRACT.md, "Hosted unwind coverage" (pypi): "A transitive override-dependencies entry hosted mode added is removed (upstream_uv_override_removed)." An entry the user wrote before the scan should survive rollback / remove, as it does in vendored mode (whose ledger records the original). If ownership can't be decided without a ledger, the restore should keep the entry, or refuse with the git checkout remedy, rather than delete it.
  • Actual: the user's entry is deleted, along with [tool.uv] and the lock's [manifest] overrides. Exit 0 / success, and a warning that blames hosted mode.

OS × uv matrix (main 2463257)

Each cell covers both rollback and remove. "fail" = user override deleted and uv lock --upgrade → six 1.17.0. The no-socket-patch control keeps 1.16.0 in every cell.

OS uv 0.2.37 uv 0.5.31 uv 0.12.21
Linux (sandbox) fail fail (2 runs) fail (2 runs)
macOS arm64 (probe) – fail fail
Windows (probe) fail fail fail

First bad

2463257 (#277, the v5 upstream restore). Release 4.0.0 reverted from a recorded ledger fragment and has no upstream restore.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:1086 pushed_override: a spelling match on <name>==<version>, with no evidence the rewrite added it.
  • crates/socket-patch-core/src/patch/redirect/upstream/uv.rs:1103 restore_metadata (removes the entry, then the empty tables) and :947 / :996 (drops the [manifest] overrides entry on the same guess).

Probe run

https://github.com/SocketDev/socket-patch/actions/runs/36806727354 ("user override" groups in the Run probe step)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions