fix(subprocess): register the agent system instruction in subprocess-spawned child sessions - #295
Open
Brian Krabach (bkrabach) wants to merge 1 commit into
Open
Conversation
…spawned child sessions
Subprocess-spawned agents were silently dropping their system instruction (persona).
The 'instruction' key crossed the process boundary intact inside 'config' (via
merge_configs() on the parent side), but nothing on the subprocess child path ever
read it. This left subprocess-spawned agents running as generic sessions with NO
persona at all.
Fix: in _run_child_session, resolve config.get('instruction') or config.get('system',
{}).get('instruction') with an isinstance dict guard, expand @mentions using a fresh
ContentDeduplicator (not the session's shared instance, which would cause mention
content to bleed through into the prompt's context block), then register via
set_system_prompt_factory (preferred) or add_message(role=system) (fallback).
Also fixed a regression defect: the first version shared the session's
ContentDeduplicator across persona expansion and prompt expansion. Since
ContentDeduplicator.get_unique_files() returns every file the instance has ever
seen and re-serializes the whole set on each call, this made the persona's resolved
@mention content leak into the prompt's context block, costing tokens on every
affected turn. Now uses a fresh deduplicator for persona expansion, matching the
already-correct pattern at bundle/_prepared.py:411-412.
Evidence:
- RED proven (before fix): config['instruction'] present, set_system_prompt_factory
awaited 0 times, subprocess child runs with NO persona
- GREEN proven (after fix, revert-only): 1556 tests pass (+7 new persona tests)
- Regression test added: test_persona_mentions_do_not_bleed_into_prompt, RED-proven
by reverting only the fresh-dedup line (persona content bleeds, test fails)
Test suite: baseline 1549 passed, this branch 1556 passed (all new tests pass).
Lint: ruff check and ruff format --check both clean.
Reviews:
- Independent code review: APPROVED, two non-blocking follow-ups
- Council (six lenses): 5/6 DO-NOT-MERGE-YET (regression test added, now passing),
1 FAIL (same issue, same fix applied)
Known remaining gaps:
- Three hand-synced implementations of 'install a persona' now exist (bundle,
app-cli, foundation). This bug's existence IS a sync failure. Should be ticketed.
- session_spawner.py (in-process) still shares one deduplicator across its persona
and instruction expansions. Pre-existing, out of scope here. Should be ticketed.
- Tests mocked at AmplifierSession boundary; no end-to-end subprocess run with
real session included.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
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
Subprocess-spawned agents were silently dropping their system instruction (persona). The
instructionkey crossed the process boundary intact insideconfig(via merge_configs() on the parent side), but nothing on the subprocess child path ever read it.Root cause: Two defects, now fixed:
Defect 1 (shipped bug): System instruction never registered in subprocess child
config['instruction']present but never consumedamplifier-bundle-dot-graphrecipes usespawn_mode: subprocessDefect 2 (found in review): @mention bleed-through via shared deduplicator
ContentDeduplicatoracross persona + prompt expansionContentDeduplicator.get_unique_files()returns EVERY file ever seen, re-serializes on each callFix
For Defect 1:
In
_run_child_session, resolveconfig.get('instruction')orconfig.get('system', {}).get('instruction')with an isinstance dict guard, expand @mentions, then register via:set_system_prompt_factory(...)(allows composable hooks)add_message({'role': 'system', ...})(for contexts without factory)Mirrors in-process path in
amplifier-app-cli/session_spawner.pyexactly.For Defect 2:
Persona expansion now uses a fresh
ContentDeduplicator()instead of sharing the session's instance, matching the already-correct pattern atamplifier_foundation/bundle/_prepared.py:411-412("Fresh deduplicator each call").Verification Evidence
RED proven twice, independently:
Pre-fix probe (persona present in config, nothing registered):
Revert-production-only (tests kept):
Bleed-through regression test, RED-proven by reverting only the dedup line:
Test suite results:
Lint:
ruff checkandruff format --checkboth clean.Review Trail
Known Remaining Gaps
bundle/_prepared.py,amplifier-app-cli/session_spawner.py, and this fix. Tied together only by comments. This bug's existence IS a sync failure. Should be ticketed.session_spawner.py(in-process) still shares one deduplicator across its persona and instruction expansions — the same bleed-through defect, pre-existing, out of scope. Should be ticketed against amplifier-app-cli.Files Changed
amplifier_foundation/subprocess_runner.py— Added persona registration block in_run_child_session(after capability registration, before prompt expansion)tests/test_subprocess_runner.py— AddedTestChildSystemInstructionclass with 7 tests (factory registration, fallback path, nested form, no-instruction, @mention expansion, bleed-through regression, context-is-None no-crash)