Repository navigation
Hatch rollback and remove refuse with "configuration drifted" after the project version is bumped or a dependency is added, in both hosted and vendored mode #385
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:hatchHatchHatch
on Sep 30, 2026 mikolalysenko commented
on Sep 30, 2026 CollaboratorAuthorMore actions[agent] Triaged:
priority:p1(Hatch / PyPI family). Not a duplicate, and no open or merged PR fixes it.Cause confirmed on main: the Hatch
pyproject.tomlrevert (vendoredvendor/pypi_hatch.rsand hostedpatch/redirect/replay.rs/utils/hatch.rs) goes throughvendor::pypi_lock::restore_document. That three-way merge was written for lock[[package]]tables, so it callssame_identity(textualname+versionequality) on every table-like it descends into, including[project], and it treats any array length change as drift. It's related to #379 and #382 (hosted rollback stuck after an unrelated edit), but those fail on the uv/PDM ledger fragment and re-scan rebase, which is a different code path, so they aren't clustered.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triage on main
2463257(#277, the v5 workflow), Linux, Hatch 1.18.1, same mock patch API. Half fixed:Edit after patching hosted vendored [project].versionbumpfixed: rollback success,six==1.16.0restored, version edit kept,[tool.hatch.metadata]droppedstill fails add "idna==3.7"toproject.dependenciesfixed: restored ["idna==3.7", "six==1.16.0"]still fails comment appended to name = "app"fixed untested In v5 hosted rollback restores the upstream pin from the live file, so it no longer goes through the snapshot merge. The vendored path still does:
socket-patch scan --mode vendored --json --yes … # pyproject wired to {root:uri}/.socket/vendor/pypi/<uuid>/six-1.16.0-…whl sed -i 's/0.1.0/0.3.0/' pyproject.toml # or add a dependency to the same array socket-patch rollback --json … # -> exit 1, partial_failure, vendoredFailed: [{"purl":"pkg:pypi/six@1.16.0","error":"pyproject.toml changed since patching"}] socket-patch remove pkg:pypi/six@1.16.0 --json --yes … # -> error vendor_revert_failed "could not revert vendoring for pkg:pypi/six@1.16.0: pyproject.toml changed since patching"
Reproduced 3 times (version bump ×2, added dependency ×1). Suspect code on current main:
crates/socket-patch-core/src/vendor/pypi_hatch.rs:258→vendor/pypi_lock.rs:534restore_document, withsame_identityat:408and the array-length checks at:478/:513.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Shares root cause with #474: the vendored revert's three-way TOML merge (
restore_documentincrates/socket-patch-core/src/vendor/pypi_lock.rs) matches array elements by position and treats any array-length change as drift, and applies the lock-packagename/versionidentity check to every table (including pyproject[project]). Will be fixed together.
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Claiming this issue (with #474; shared root cause: positional/length-strict array merge and table-level identity check in
vendor::pypi_lock::restore_document). Branch: agent/fix-pypi-toml-merge-identity. Claim-ID: 2026-10-01T16:20:43Z-576d58
Generated by Claude Code
mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions- added 2 commits that reference this issue
on Oct 1, 2026 mikolalysenko commented
on Oct 1, 2026 CollaboratorAuthorMore actions[agent] Re-triage on main
61cfb9b(Linux, real Hatch 1.18.1, mock patch API):- Hosted: passes now. After a
[project].versionbump, and after adding a dependency to the array that holds the pin,rollback --jsongivessuccesswithhosted.reverted: [pkg:pypi/six@1.16.0].six==1.16.0comes back and the unrelated edit is kept. - Vendored: still fails. After the version bump,
rollback --jsongivespartial_failurewithvendoredFailed: [{purl: pkg:pypi/six@1.16.0, error: "pyproject.toml changed since patching"}], and the{root:uri}wiring stays. Reproduced 2×.
So what's left of this issue is the vendored ledger (
vendor/pypi_hatch.rsrevert /ledger_snapshots).
Generated by Claude Code
- Hosted: passes now. After a
[agent] Found by the scheduled Hatch bug-hunt routine (ledger #314).
Summary
For a Hatch project, socket-patch records whole
pyproject.tomlsnapshots (hatch_documentwiring and theHatchDocumenthosted ledger edit) and reverts them with the three-way TOML mergevendor::pypi_lock::restore_document. That merge was written for lock[[package]]tables, and it treats two ordinary pyproject edits as drift:[project].version(orname) changed.restore_item/restore_valuecallsame_identity, which requires the live table'snameandversionto equal the recorded ones. Applied to the[project]table, that fails as soon as the project releases a new version. Even a trailing comment onname = "app"trips it, because the comparison is textual.project.dependencies).restore_valuereturns drift whenlive.len() != new.len().After either edit,
socket-patch rollbackandsocket-patch removerefuse, and the patch stays wired. A re-scan (scan --mode hostedor--mode vendored) exits 0 but doesn't re-baseline the ledger, so the next rollback fails the same way. Edits in other tables, such as a new env dependency or a new[tool.ruff]table, merge fine.Impact
Bumping
[project].versionis part of every release, and adding a dependency is routine. After either, a Hatch project can't unpatch through socket-patch in either mode.removeexits withhosted_revert_failed/vendor_revert_failed, and the only way out is to editpyproject.tomlby hand. That includes removingallow-direct-referencescorrectly, which socket-patch otherwise tracks ownership of. It fails closed (nothing is corrupted), but it blocks the documented rollback and remove workflow.Repro (Linux, Hatch 1.18.1 and 1.7.0, main
f6b7fb9)Mock patch API serving a patched six 1.16.0 wheel: the same mock as the #335 probe (https://github.com/SocketDev/socket-patch/actions/runs/36740025279), modeled on
tests/vex_pypi_real_common.With
--mode vendored --vendor-source build(Hatch on PATH), the same steps givevendoredFailed: "pyproject.toml changed since patching", andremovegivesvendor_revert_failed.Control: without the edit, rollback restores
pyproject.tomlbyte for byte in both modes (this passed on the previous run for 13 hosted and 12 vendored shapes).Expected vs actual
allow-direct-referencesafter the last reference is unwired. The only refusals it documents are drifted sources, ledgerless direct references, concurrent edits and symlinks. An unrelated key in[project], or a sibling entry in the dependency array, is none of those, so rollback should putsix==1.16.0back, keep the user's edits, and drop the permission.Matrix (Linux)
[project].versionbump"idna==3.7"toproject.dependenciesname = "app"[tool.hatch.envs.default][tool.ruff]tableThe failure comes from socket-patch's TOML merge, not from Hatch, so it's the same on every Hatch version and OS. No macOS or Windows probe was run for that reason.
First bad
Hatch support landed in #244 (649d457), which is after v4.0.0, so no release is affected yet. It has been present since Hatch support landed.
Suspect code
crates/socket-patch-core/src/vendor/pypi_lock.rs:400same_identity(called at:462and:495) applies the lock-package identity check to pyproject tables.crates/socket-patch-core/src/vendor/pypi_lock.rs:470/:505treat any array-length change as drift.crates/socket-patch-core/src/patch/redirect/replay.rs:652(hostedHatchDocument) andcrates/socket-patch-core/src/vendor/pypi_hatch.rs:253(vendored revert).Related, but a different code path: #379 (uv,
ReplaceFragmentwhole-document fragments) and #382 (PDM re-scan never normalizes the ledger).