fix(bundler): bundle update uninstalls components dropped by new version#3353
Open
jawwad-ali wants to merge 1 commit into
Open
fix(bundler): bundle update uninstalls components dropped by new version#3353jawwad-ali wants to merge 1 commit into
jawwad-ali wants to merge 1 commit into
Conversation
On refresh (bundle update), install_bundle iterated only the new plan's components, so a component the previous version owned but the new one no longer ships was left installed on disk while being dropped from the rewritten record (contributed only holds plan.components). With no record referencing it, remove_bundle could never clean it up — permanently orphaned, violating the provenance invariant (FR-022). After the component loop, when refresh and a prior record exists, uninstall each previously-owned component absent from the new plan — unless another bundle still needs it (components_still_needed refcount, mirroring remove_bundle), in which case it stays installed and is simply de-attributed. Runs inside the existing try so a failed removal takes the same no-record-written rollback path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When
specify bundle updaterefreshes a bundle to a new version that drops a component, that component is permanently orphaned.install_bundleiterates onlyplan.components(the new version's set). A component the previous version owned but the new one no longer ships is:contributedonly holdsplan.components).With no record referencing it,
remove_bundlecan never clean it up — it's stranded forever, violating the provenance invariant that records capture exactly what a bundle contributed (FR-022).Reproduced on main @
bba473cwith the repo'sFakeInstaller: install v1 (ext-a + ext-b), update to v2 (ext-a only,refresh=True) →remove_callsis empty,ext-bstays ininstalled, and the record lists only ext-a.Fix
After the component loop, when
refreshis True and a prior record exists, uninstall each previously-owned component absent from the new plan — unless another bundle still needs it (components_still_neededrefcount, mirroringremove_bundle), in which case it stays installed and is simply de-attributed from this bundle. The removal runs inside the existingtry, so a failure takes the same bounded-rollback / no-record-written path as the rest of the install.Testing
New tests in
test_bundler_install_flow.py:test_update_uninstalls_components_dropped_by_new_version: v1(ext-a,ext-b) → v2(ext-a) →ext-bis inremove_callsandresult.uninstalled, gone frominstalled, and dropped from the record. Fails before (ext-borphaned — verified by source-stash), passes after.test_update_keeps_component_still_needed_by_sibling_bundle: a sibling bundle also ownsext-b→ the update does not remove it; it stays installed and is only de-attributed from the updated bundle.Full
test_bundler_install_flow.py: 13 passed.uvx ruff checkclean.AI Disclosure
Found and fixed with Claude Code (Claude Fable 5) under my direction. AI traced the orphaning to the refresh path skipping dropped components; I reproduced it with FakeInstaller, mirrored
remove_bundle's refcount logic, verified fail-before/pass-after including the shared-component case, and reviewed the diff.