fix: filter spawned sub-sessions in session list and resume - #52
Merged
Brian Krabach (bkrabach) merged 1 commit intoJan 14, 2026
Merged
Conversation
Fixes an issue where 'amplifier resume' with partial session IDs would
fail with 'ambiguous session ID' errors when spawned sub-sessions
existed, and where 'amplifier session list' was showing spawned
sub-sessions.
Root cause: Spawned sub-sessions have IDs like {parent_id}_{agent_name}
(e.g., a5c32e0d-..._foundation:explorer). When searching for partial ID
'a5c32e0d', both the parent and spawned sessions matched.
Solution (DRY fix in SessionStore):
- Added is_top_level_session() helper to identify main sessions (those
without '_' in ID)
- Added top_level_only=True parameter to list_sessions() - filters out
spawned sessions by default
- Added top_level_only=True parameter to find_session() - only matches
top-level sessions by default
- Removed redundant manual filtering in _interactive_resume_impl()
Commands fixed:
- amplifier resume (interactive and with partial ID)
- amplifier session list
- amplifier session resume
- amplifier run --resume
- amplifier continue
Brian Krabach (bkrabach)
deleted the
fix/session-filter-spawned-subsessions
branch
January 14, 2026 15:05
Brian Krabach (bkrabach)
pushed a commit
that referenced
this pull request
Sep 3, 2026
…ing list`/`show` `amplifier routing list` showed a user matrix and a bundle matrix of the same name as peers. Only one is ever loaded: hooks-routing's mount() searches `[*custom_routing_dirs, bundle routing/]` and takes the first hit, so a file in ~/.amplifier/routing/ silently makes the shipped bundle matrix dead -- and nothing in the CLI said so. That is why a matrix change shipped in the bundle can be completely inert on a host. The precedence rule is NOT re-derived here. It is consumed from hooks-routing's own `resolve_matrix_source()` (routing-matrix PR #52), loaded by file path out of the same cached bundle directory the CLI already globs -- app-cli does not depend on hooks-routing as a distribution, and `routing list` never mounts a bundle, so neither an import nor the session-time `model_role_resolver` capability is reachable from this process. When the cached bundle predates PR #52 (no `resolve_matrix_source`), the CLI draws no marker at all rather than guessing the search order: a wrong shadowing claim is worse than none. Unshadowed output is byte-identical to before, in both text and JSON. - amplifier_app_cli/lib/routing_provenance.py: locate + load the bundle's matrix_loader, classify custom vs bundle routing dirs, resolve one MatrixSource per matrix name. - commands/routing.py: row marker (`⚠ shadows bundle`), a footer naming the file in use and each file it suppresses, the same note on `routing show`, and MatrixSource.to_dict() in `--format json`. - tests/test_routing_shadowing.py: 15 tests -- shadowed marks the winner, unshadowed output byte-identical, no user routing dir, and the old-bundle degradation path.
Brian Krabach (bkrabach)
added a commit
that referenced
this pull request
Sep 3, 2026
…ing list`/`show` (#293) * fix(routing): mark a matrix that shadows a same-named matrix in `routing list`/`show` `amplifier routing list` showed a user matrix and a bundle matrix of the same name as peers. Only one is ever loaded: hooks-routing's mount() searches `[*custom_routing_dirs, bundle routing/]` and takes the first hit, so a file in ~/.amplifier/routing/ silently makes the shipped bundle matrix dead -- and nothing in the CLI said so. That is why a matrix change shipped in the bundle can be completely inert on a host. The precedence rule is NOT re-derived here. It is consumed from hooks-routing's own `resolve_matrix_source()` (routing-matrix PR #52), loaded by file path out of the same cached bundle directory the CLI already globs -- app-cli does not depend on hooks-routing as a distribution, and `routing list` never mounts a bundle, so neither an import nor the session-time `model_role_resolver` capability is reachable from this process. When the cached bundle predates PR #52 (no `resolve_matrix_source`), the CLI draws no marker at all rather than guessing the search order: a wrong shadowing claim is worse than none. Unshadowed output is byte-identical to before, in both text and JSON. - amplifier_app_cli/lib/routing_provenance.py: locate + load the bundle's matrix_loader, classify custom vs bundle routing dirs, resolve one MatrixSource per matrix name. - commands/routing.py: row marker (`⚠ shadows bundle`), a footer naming the file in use and each file it suppresses, the same note on `routing show`, and MatrixSource.to_dict() in `--format json`. - tests/test_routing_shadowing.py: 15 tests -- shadowed marks the winner, unshadowed output byte-identical, no user routing dir, and the old-bundle degradation path. * docs(adq): lane DONE-NOTE — findings, seam rationale, honest test limitation --------- Co-authored-by: Amplifier Lane adq <lane-adq@localhost>
Brian Krabach (bkrabach)
added a commit
that referenced
this pull request
Sep 3, 2026
…-write-wins (#294) * fix(routing): mark a matrix that shadows a same-named matrix in `routing list`/`show` `amplifier routing list` showed a user matrix and a bundle matrix of the same name as peers. Only one is ever loaded: hooks-routing's mount() searches `[*custom_routing_dirs, bundle routing/]` and takes the first hit, so a file in ~/.amplifier/routing/ silently makes the shipped bundle matrix dead -- and nothing in the CLI said so. That is why a matrix change shipped in the bundle can be completely inert on a host. The precedence rule is NOT re-derived here. It is consumed from hooks-routing's own `resolve_matrix_source()` (routing-matrix PR #52), loaded by file path out of the same cached bundle directory the CLI already globs -- app-cli does not depend on hooks-routing as a distribution, and `routing list` never mounts a bundle, so neither an import nor the session-time `model_role_resolver` capability is reachable from this process. When the cached bundle predates PR #52 (no `resolve_matrix_source`), the CLI draws no marker at all rather than guessing the search order: a wrong shadowing claim is worse than none. Unshadowed output is byte-identical to before, in both text and JSON. - amplifier_app_cli/lib/routing_provenance.py: locate + load the bundle's matrix_loader, classify custom vs bundle routing dirs, resolve one MatrixSource per matrix name. - commands/routing.py: row marker (`⚠ shadows bundle`), a footer naming the file in use and each file it suppresses, the same note on `routing show`, and MatrixSource.to_dict() in `--format json`. - tests/test_routing_shadowing.py: 15 tests -- shadowed marks the winner, unshadowed output byte-identical, no user routing dir, and the old-bundle degradation path. * docs(adq): lane DONE-NOTE — findings, seam rationale, honest test limitation * fix(routing): select the listed matrix by the loader's rule, not last-write-wins `amplifier routing list` built its rows with matrices[data["name"]] = (data, path) # over sorted(discovered files) which diverges from hooks-routing in two independent ways at once: it keyed on the `name:` field INSIDE each YAML (the loader resolves by file STEM), and it let the LAST file in sort order win (the loader takes the FIRST hit in `[*custom_routing_dirs, bundle routing/]`). The two rules agreed only by alphabetical accident -- `~/.amplifier/cache/...` sorts before `~/.amplifier/routing/...` because "c" < "r". When they disagree the command asserts something false: it names a file as in use that the loader would never read. A user file `my-fast.yaml` declaring `name: balanced` overwrote the row for the real `balanced` matrix outright. Rows are now keyed by file stem, and the file behind each row is `MatrixSource.path` -- the value hooks-routing's own `resolve_matrix_source()` assigns to `matrix_path` and loads. Those fields are reachable from the CLI through the seam PR #293 already built, so precedence is not re-derived a third time. Only the winning file is parsed, so a shadowed file can no longer supply a row's description, `updated:` date or compatibility count. Also: `routing use` now writes the filename the loader resolves (it could write an unloadable internal name before), a `name:`/stem disagreement is surfaced rather than silently keyed on the internal name, and every JSON row carries `matrix_file`. When the cached bundle predates routing-matrix PR #52 there is no `resolve_matrix_source` to ask. The shadowing MARKER is still withheld (#293's rule: a wrong marker is worse than none), but a row must point at some file, so selection falls back to the first candidate in `[*custom_dirs, *bundle_dirs]` -- the same list hooks-routing builds as `search_dirs`, in one labelled function. Tests: `tests/test_routing_winner_selection.py` constructs both disagreement classes explicitly, each with a non-vacuity gate that re-runs the old algorithm inline and asserts it picks the other file. Full suite green (1605 passed); existing routing list/show/use tests unmodified. --------- Co-authored-by: Amplifier Lane adq <lane-adq@localhost> Co-authored-by: Amplifier <240397093+microsoft-amplifier@users.noreply.github.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.
Summary
Fixes an issue where
amplifier resumewith partial session IDs would fail with "ambiguous session ID" errors when spawned sub-sessions existed, and whereamplifier session listwas showing spawned sub-sessions.Root Cause
Spawned sub-sessions have IDs like
{parent_id}_{agent_name}(e.g.,a5c32e0d-..._foundation:explorer). When searching for partial IDa5c32e0d, both the parent and spawned sessions matched.Solution (DRY fix in SessionStore)
is_top_level_session()helper function to identify main sessions (those without_in ID)top_level_only=Trueparameter tolist_sessions()- filters out spawned sessions by defaulttop_level_only=Trueparameter tofind_session()- only matches top-level sessions by default_interactive_resume_impl()Commands Fixed
amplifier resume(interactive and with partial ID)amplifier session listamplifier session resumeamplifier run --resumeamplifier continueFiles Changed
amplifier_app_cli/session_store.py- Added helper function and parametersamplifier_app_cli/commands/session.py- Removed redundant filtering (now handled by SessionStore)