fix(activator): install a bundle root's package only when a declared module lives there; fail by name - #352
Merged
Brian Krabach (bkrabach) merged 1 commit intoSep 3, 2026
Conversation
…module lives there; fail by name Bundle.prepare() editable-installed the root Python package of the composed bundle AND of every included bundle whenever the root pyproject.toml carried a [project] table. The inference was wrong for a class of repos it never anticipated: a Python APPLICATION that ships a skills-only behavior. Its [project] table is the application, no declared module imports from it, and because --app bundles are an include of every session, one `amplifier bundle add` of such a repo made every session on the machine fail at preparation -- `uv pip install -e <app root>` refused (requires-python >= 3.13 vs the environment's 3.12.3), attributed to whichever bundle happened to be loading. Reproduced end-to-end in a clean container; `bundle remove` was the only cure. - activate_bundle_package() gains keyword-only `module_sources`. When given, the package is installed only if at least one declared module source resolves INSIDE the bundle root (bundle_root_declares_module): local paths (relative sources are already absolute by prepare() time), or git+ sources whose repo+ref hash to the same cache directory as the root -- the git handler's own placement computation, so a same-repo #subdirectory=modules/x matches and any other repo does not. None preserves the historical rule. - Bundle.prepare() now collects modules_to_activate FIRST and passes the declared sources to every package install. The bundle's own root failing still propagates; an INCLUDED root failing honors `strict` exactly as module activation does -- raise under strict, otherwise skip with a warning naming the include, and let any module that truly needed the package fail on its own, by name, in activate_all(). - Failures are attributed: BundlePackageInstallError names the owning bundle root and package, and points at `bundle remove`. A requires-python that excludes the running interpreter is reported in one sentence before uv is spawned (best-effort via `packaging`, which is not a declared dependency). Verified: 20 new tests (inference, install decision, attribution, and the production call site including the exact field shape under strict=True); full suite 1893 passed. Clean-environment before/after evidence accompanies the field report.
Brian Krabach (bkrabach)
deleted the
fix/bundle-package-install-inference
branch
September 3, 2026 04:47
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.
Defect
Bundle.prepare()editable-installed the root Python package of the composed bundle and of every included bundle whenever the rootpyproject.tomlcarried a[project]table (ModuleActivator.activate_bundle_package). That inference is wrong for a class of repos it never anticipated: a Python application that ships a skills-only behavior. Its[project]table is the application, no declared module imports from it — and because--appbundles are an include of every session, oneamplifier bundle addof such a repo made every session on the machine fail at preparation (uv pip install -e <app root>refused:requires-python >= 3.13vs the fresh-install venv's 3.12.3), attributed to whichever bundle happened to be loading (Failed to load bundle 'anchors'). On a 3.13 host it would instead silently install the whole application into the consumer's Amplifier environment. Reproduced end-to-end in a clean container;bundle removewas the only cure. Related to the #326-era_distribution_installedskip, which mitigated a sibling manifestation of the same heuristic.Fix
activate_bundle_package(bundle_path, progress_callback=None, *, module_sources=None)— whenmodule_sourcesis given, the package is installed only if at least one declared module source resolves inside the bundle root (bundle_root_declares_module): a local path (relative sources are already absolute byprepare()time), or agit+source whose repo+ref hash to the same cache directory as the root — the git handler's own placement computation, so a same-repo#subdirectory=modules/xmatches and any other repo does not.Nonepreserves the historical rule for callers that cannot supply the list.Bundle.prepare()collectsmodules_to_activatefirst and passes the declared sources to every root-package install. The bundle's own root failing propagates; an included root failing honorsstrictexactly as module activation does — raise under strict, otherwise skip with a warning naming the include and let any module that truly needed it fail on its own, by name, inactivate_all().BundlePackageInstallError(BundleError)names the owning bundle root and package (and points atbundle remove); arequires-pythonthat excludes the running interpreter is reported in one sentence before uv is spawned (best-effort viapackaging, not a declared dependency — skipped if absent).Verification
tests/test_activate_bundle_package.py): inference in isolation (same-repo git / other repo / different ref / garbage), install decision, attribution, and theprepare()call site including the exact field shape understrict=True. Full suite 1716 passed, 1 skipped; zero new ruff findings.Error mounting tools … Python>=3.13); with this branch resolved as the dependency by a freshuv tool install amplifier(direct_url.jsoncommitd2a7bd9) → sessions work, the three shipped skills load, and the positive control still holds: addingamplifier-bundle-recipesstill editable-installsamplifier-recipe-runnerfrom the bundle cache root (its module lives in its own repo).Not exercised live: a Python 3.13 host (unit-tested only).
🤖 Generated with Amplifier