Skip to content

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 into
mainfrom
fix/bundle-package-install-inference
Sep 3, 2026
Merged

fix(activator): install a bundle root's package only when a declared module lives there; fail by name#352
Brian Krabach (bkrabach) merged 1 commit into
mainfrom
fix/bundle-package-install-inference

Conversation

@bkrabach

Copy link
Copy Markdown
Collaborator

Defect

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 (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 --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 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 remove was the only cure. Related to the #326-era _distribution_installed skip, which mitigated a sibling manifestation of the same heuristic.

Fix

  • activate_bundle_package(bundle_path, progress_callback=None, *, module_sources=None) — when module_sources is 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 by prepare() time), or a git+ 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/x matches and any other repo does not. None preserves the historical rule for callers that cannot supply the list.
  • Bundle.prepare() collects modules_to_activate first and passes the declared sources to every root-package install. The bundle's own root failing 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 it fail on its own, by name, in activate_all().
  • BundlePackageInstallError(BundleError) 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, not a declared dependency — skipped if absent).

Verification

  • 20 new tests (tests/test_activate_bundle_package.py): inference in isolation (same-repo git / other repo / different ref / garbage), install decision, attribution, and the prepare() call site including the exact field shape under strict=True. Full suite 1716 passed, 1 skipped; zero new ruff findings.
  • Clean-environment A/B (two DTU containers, one differing input): stock foundation → adding the app-repo behavior breaks every session (Error mounting tools … Python>=3.13); with this branch resolved as the dependency by a fresh uv tool install amplifier (direct_url.json commit d2a7bd9) → sessions work, the three shipped skills load, and the positive control still holds: adding amplifier-bundle-recipes still editable-installs amplifier-recipe-runner from 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

…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.
@bkrabach
Brian Krabach (bkrabach) merged commit 5ebf1da into main Sep 3, 2026
@bkrabach
Brian Krabach (bkrabach) deleted the fix/bundle-package-install-inference branch September 3, 2026 04:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants