microsoft / microsoft/amplifier
[amplifier-app-cli] `resume`/`continue` ignore settings `bundle.active` (hardcode "anchors") and persist self-perpetuating `"bundle": "unknown"`; delegate spawner never registers a system-prompt factory
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 3.1k
- Forks
- 261
- Avg merge
- 3h 28m
- Merged PRs (30d)
- 13
Description
Summary
Two related issues found while debugging why sessions launched by a third-party terminal app (freshell, which pre-creates session stubs and launches amplifier resume <uuid>) behaved differently from directly-launched sessions. Diagnosis was validated by a three-round adversarial review with file:line verification.
Line numbers below refer to the installed release as of 2026-08-01 (amplifier-app-cli 0.1.1 via uv tool install).
Bug 1: resume/continue bundle resolution ignores bundle.active
Behavior: amplifier run consults settings bundle.active before falling back to the default (commands/run.py:134-141). The resume path does not: commands/session.py:112-129 extracts the bundle from session metadata and, when missing, leaves bundle_name = None; runtime/config.py:971 then hardcodes default_bundle = "anchors" without consulting settings. A call-site audit shows resolve_config's else-branch is reachable only from resume — run.py:164 always passes a resolved name — so the fix is contained.
Self-perpetuation: the resume flow then sets active_bundle = "unknown" (commands/session.py:153), which gets persisted into session metadata (incremental_save.py:112, main.py:2895/3581/3617). session_store.py:67-73 maps "unknown" back to None on the next resume — its docstring even promises "allowing caller to fall back to configured default bundle", i.e. the intended behavior is documented but not implemented.
Impact: any session whose metadata lacks a bundle (or carries "unknown") silently runs anchors forever, even when the user's ~/.amplifier/settings.yaml says bundle.active: foundation. Third-party launchers that pre-create session stubs (freshell) hit this for every session; users get a different bundle depending on how the session was started, with no error or warning.
Suggested fix:
- In
resolve_config's no-bundle branch, fall back to merged-settingsbundle.activebefore the hardcoded"anchors"(mirrorsrun.py:134-141); update the "No bundle specified, using default" message to say when settings supplied the name. Placing it there also coversamplifier continue(same helper). - Persist the resolved bundle name after fallback instead of the
"unknown"display string (deterministic resumes; pins the session). - Keep
session_store.py's legacy"unknown"→Nonemapping permanently — it is the healing mechanism for already-affected sessions.
Note this is a (desirable) behavior change for existing "unknown" sessions: they will visibly switch from anchors to the user's configured bundle on next resume. Worth a changelog line.
Bug 2: delegate spawner never registers a system-prompt factory (defeats skills prefix placement; per-request token cost)
Behavior: session_spawner.py creates delegate children via AmplifierSession(...) directly (~line 486), bypassing foundation's PreparedBundle.create_session()/spawn(), and injects the agent persona as a static system message via context.add_message({"role": "system", ...}) (~line 741). There are zero calls to set_system_prompt_factory in the package.
Impact: the skills-visibility hook (amplifier-bundle-skills) defaults to placement='prefix', which wraps the context module's system-prompt factory so the skills index rides the provider's cached prefix. With no factory registered, every delegate child (and every recipe step — recipes use the same spawn path) logs a one-time WARNING to the shared terminal stderr and falls back to per-request injection: the multi-KB skills index is re-sent as fresh input tokens on every request of every child. In delegation-heavy sessions and long recipes this is a warning storm plus a real token cost.
Suggested fix (precedent: foundation's own PreparedBundle.spawn() already registers a factory for its children — amplifier_foundation/bundle/_prepared.py:869-881):
- Register a system-prompt factory for delegate children that replaces the static
add_messageinjection. Important:context-simple'sget_messages_for_requestfilters out storedrole=systemmessages when a factory is set (preserving onlymetadata.source == "hook"ones) — registering a factory alongside the static message would silently drop the persona. Replace, don't add. - Guard registration on a truthy persona instruction (mirror foundation's
if effective_bundle.instruction or effective_bundle.contextguard) — an unconditional empty-string factory would suppress stored system messages of personaless children for no benefit. - Re-register the factory in
resume_sub_session(the persona is recoverable from the persistedagent_overlaymetadata, ~line 817). Old transcripts containing the static persona dedupe automatically at request time (filtered while the factory supplies fresh content). - The
spawn_mode="subprocess"branch routes before the injection code and goes through foundation's prepare path — excluded.
A companion issue on amplifier-bundle-skills covers the misleading warning text/level itself.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with commands/session.py and runtime/config.py to trace bundle resolution and persistence through session_store.py and incremental_save.py. Then inspect session_spawner.py and the resume_sub_session path, using foundation's PreparedBundle.spawn() behavior as the reference. Done means configured bundles are honored and persisted, while delegate and resumed children retain persona prompts without repeated skills injection warnings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 56/100