feat(bundle): add routing field for bundle-declared matrix defaults - #301
Open
Sam Schillace (ramparte) wants to merge 1 commit into
Open
feat(bundle): add routing field for bundle-declared matrix defaults#301Sam Schillace (ramparte) wants to merge 1 commit into
Sam Schillace (ramparte) wants to merge 1 commit into
Conversation
Adds a `routing: dict[str, Any]` field to the Bundle dataclass so a
bundle can declare a default routing matrix (e.g. {matrix, overrides}).
Foundation treats it as opaque passthrough -- it stores and deep-merges
the dict during compose() but interprets nothing about its contents.
A separate app-cli PR consumes it, applying user/project settings'
routing: block as the higher-priority override.
Changes in amplifier_foundation/bundle/_dataclass.py:
- New `routing` field on Bundle, defaulting to {} via default_factory
- __post_init__ None-guard, matching existing collection field pattern
- from_dict() reads routing, coercing non-dict values to {} rather than
raising -- foundation does not validate routing semantics
- compose() carries routing into the result and deep-merges it in the
merge loop, so an overlay declaring only `overrides` preserves the
base's `matrix`
- to_mount_plan() deliberately does NOT include routing -- it's app-layer
policy, not a kernel-facing mount-plan concern. Added a comment so a
future reader doesn't "fix" this omission.
Purely additive: a bundle with no `routing:` key behaves byte-identically
to today (routing == {}, absent from mount plan).
Tests: tests/test_bundle_routing_field.py covers from_dict (absent key,
present key, malformed value) and compose (overlay wins, deep merge
preserves base matrix, base survives overlay without routing) and
to_mount_plan omission.
Docs: docs/BUNDLE_GUIDE.md frontmatter reference and merge-rules list
now document routing: as a default that user/project settings override.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Sam Schillace (ramparte)
requested review from
Brian Krabach (bkrabach) and
Salil Das (sadlilas)
August 12, 2026 02:49
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.
Problem
An Amplifier bundle and the active routing matrix are independent settings with no linkage, so they silently disagree. Real incident: a stale project-level
.amplifier/settings.local.yamlpinnedrouting: matrix: anthropicwhile the session ran on OpenAI. Everymodel_roledelegation resolved to zero candidates —model_role 'fast' resolved to no candidates— with no error, no attribution, and no hint that a one-line file three directories away was the cause.What this does
Adds an opaque
routing: dict[str, Any]field to theBundledataclass so a bundle can declarerouting:\n matrix: openai. Foundation stores and merges it and interprets nothing (same posture asspawn); the host app decides what it means. Read infrom_dictwith non-dict values coerced to{}; deep-merged incompose()beside the existingspawnmerge so later/overlay wins and an overlay declaring onlyoverrides:keeps the base'smatrix:. Typed as a dict rather than a narrowrouting_matrix: str | Noneso it mirrors the settingsrouting:shape and the app layer uses one merge path. Deliberately NOT added toto_mount_plan()— the mount plan is the kernel-facing surface and routing is app-layer policy; a comment marks this so nobody "fixes" it later.Precedence
Bundle value is the WEAKEST source. Precedence, weakest to strongest:
routing.matrix~/.amplifier/settings.yaml.amplifier/settings.yaml.amplifier/settings.local.yamlA bundle with no
routing:key behaves exactly as today.Backward compatibility
Purely additive; no
routing:key means byte-identical behavior, pinned bytest_to_mount_plan_omits_routingand the default-empty-dict tests.Testing
1558 -> 1565 passing, zero regressions, 7 new tests, pyright clean.
Note:
tests/test_grpc_adapter_main.py::TestVerifyModuleType::test_non_isinstance_object_with_mount_passesfails on this branch AND on unmodifiedorigin/main— a pre-existing@runtime_checkableissue unrelated to this change.Non-goals
--matrixCLI flagsession:starthook)Companion PR
microsoft/amplifier-app-cli#262