feat(routing): honor bundle-declared routing matrix as weakest-precedence default - #262
Open
Sam Schillace (ramparte) wants to merge 3 commits into
Open
feat(routing): honor bundle-declared routing matrix as weakest-precedence default#262Sam Schillace (ramparte) wants to merge 3 commits into
Sam Schillace (ramparte) wants to merge 3 commits into
Conversation
…ence default
Bundles can now declare a default routing matrix via a top-level
`routing:` section in their frontmatter (companion PR to
amplifier-foundation adds `routing: dict[str, Any]` to the Bundle
dataclass, deep-merged on compose). This is consumed as the WEAKEST
source in the precedence chain:
built-in default < bundle-declared routing.matrix
< ~/.amplifier/settings.yaml < .amplifier/settings.yaml
< .amplifier/settings.local.yaml
If a bundle declares no matrix, behavior is exactly as before this
change -- the bundle value is a default only.
Changes:
- lib/settings.py: add AppSettings.get_routing_config_with_source(),
returning (merged config, highest-precedence scope file that set
routing.matrix). get_routing_config() becomes a thin wrapper.
- lib/routing_matrices.py (new): extract matrix file discovery out of
commands/routing.py into discover_matrix_files(fetch=bool) and
known_matrix_names() (non-fetching, safe for the session-start hot
path). commands/routing.py delegates with fetch=True.
- lib/bundle_loader/prepare.py: add on_bundle_loaded callback to
load_and_prepare_bundle(), invoked right after the bundle (and its
includes) load but BEFORE any app-policy behavior is composed onto
it -- so an app-injected behavior can never masquerade as a bundle
default. Returned behavior URIs are appended to compose_behaviors
and required_behaviors.
- runtime/config.py: read the bundle's routing default via
getattr(bundle, "routing", {}) or {} for forward-compat with
foundation installs that lack the field. Merge bundle default under
user settings (user wins key-by-key), drop+warn on an unknown
bundle-declared matrix name (never bricks a session), and print
observability messages (bundle default won / user override conflict
/ unknown matrix) after the prepare spinner stops.
- commands/routing.py: `routing show` gains a Source: line.
commands/bundle.py: `bundle show` gains a Routing matrix: line.
- README.md: document the precedence chain and Source: line.
Tests: tests/test_bundle_routing_matrix.py (9),
tests/test_settings_routing_source.py (4). Updated
tests/test_routing_matrix_registration.py and
tests/test_general_config_overrides.py for the routing_matrices.py
extraction and get_routing_config_with_source() call site change.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
pyright flags `prepare.await_args.kwargs` as reportOptionalMemberAccess since unittest.mock stubs type `await_args` as `_Call | None`. Add an explicit `assert prepare.await_args is not None` before each access so the new routing-composition tests (introduced by the bundle-declared routing matrix feature) produce zero new pyright findings versus the origin/main baseline. Test behavior and assertions are unchanged. 🤖 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:50
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.The failure was silent and unattributable. These PRs close both gaps.
What this does
Consumes bundle-declared routing as weakest-precedence default. If a bundle declares
routing.matrix: anthropic, the session inherits it unless overridden by settings files.Adds routing attribution. A new function
get_routing_config_with_source()reports WHICH settings file (or bundle) set the active matrix, making failures diagnosable. Directly closes the incident's attribution gap.Extracts matrix discovery to non-fetching path. New
lib/routing_matrices.pywithdiscover_matrix_files(fetch=False)andknown_matrix_names()ensures the session path never hits the network.commands/routing.pyfetches withfetch=True. No more surprise network calls during session startup.Warns and falls back when bundle names uninstalled matrix. A bundle default must never brick a session, but must fail loudly. Falls back to system default with a clear warning.
Adds attribution output.
amplifier routing showandamplifier bundle shownow report which source provided the active matrix.Precedence
Bundle value is the WEAKEST source. Precedence, weakest to strongest:
routing.matrix~/.amplifier/settings.yaml.amplifier/settings.yaml.amplifier/settings.local.yamlThis PR carries three commits. The first two are the feature; the third is necessary. Read carefully:
Commit
34ea3cb:fix: prepare routing before config injectionThis commit introduces
required_behaviorsonload_and_prepare_bundleand moves routing-behavior composition beforeprepare(), which the feature builds on. Neither change exists onorigin/main.Scope change: This commit changes failure semantics: routing composition failures now propagate as hard errors instead of being swallowed as silent warnings. This may surface failures in environments that relied on the previous silent-warning behavior.
Reviewers should bless this deliberately. Also note: a sibling, never-reconciled implementation of the same ordering fix exists on branch
fix/compose-routing-before-prepare(c2b4de7);34ea3cbwas chosen because the feature was literally built on it, not because the two were evaluated on merit — worth confirming which is intended.Backward compatibility
No
routing:key in bundle means unchanged behavior (pinned bytest_bundle_without_routing_behaves_identically_to_today). Reads the field viagetattr(bundle, "routing", {}) or {}, verified against the currently installed foundation which lacks the field, so this can land before or after the companion PR.Testing
1301 -> 1321 passing (+20: 13 feature tests, 7 from
34ea3cb). Pyright identical to baseline after8e67424.Lint Note
lib/settings.py:607adds 2 ruff findings (S112/BLE001) from anexcept Exception: continueinget_routing_config_with_source(). This copies an existing convention used 3x elsewhere in the same file, all currently unflagged/unfixed. Left consistent rather than selectively suppressed — reviewer's call whether to fix all four together.Non-goals
--matrixCLI flagsession:starthook)Companion PR
Link to foundation PR will be inserted here.