[FEATURE] Stop notifications for synchronously-launched descendant agents flood the top-level session — no suppress/filter/batch option
What's Wrong?
In nested agent orchestration (main session → Agent(run_in_background: true) orchestrator → synchronously-launched descendants), every descendant agent's stop injects a <task-notification> into the top-level session, even when that agent was launched with run_in_background: false and its result was already delivered inline to its direct caller as a tool_result.
Verified via local transcript analysis: every Agent call below the background root used run_in_background: false (confirmed by grepping the launch flags in the subagent JSONL transcripts), the callers received all results inline and the pipeline progressed normally — yet each grandchild stop was additionally injected into the top-level conversation as a multi-KB payload (the full result block). The top-level copy has no consumer by construction: the sync caller already has the result, and nothing in the top-level session acts on it.
Why It Hurts (cost structure)
- Each injected notification triggers a full model turn in the top-level session. Output can be kept minimal, but the input side re-reads the entire conversation context.
- Delivery timing is worst-case for prompt caching. Notifications are delivered when the session is idle — typically past the 5-minute prompt-cache TTL — so each turn is a full-price context re-read, not a cache hit.
- Permanent context pollution. Each payload (3–5 KB of another agent's full findings) is appended to the conversation forever, inflating every subsequent turn and accelerating compaction.
- The sessions most affected are exactly the ones practicing cost hygiene: an orchestration/manager session deliberately kept idle gets taxed per descendant stop. In one pipeline run we observed 8+ such injections (a review fan-out of 3 sync agents alone produced 3).
This compounds with #76021 (duplicate [queued] + delivered injection) but is a different mechanism: that issue is about two copies of one notification; this one is about any copy at all being unnecessary for synchronously-launched descendants.
What Should Happen?
Any of the following would resolve it (in order of preference):
- Don't inject stop notifications for synchronously-launched descendants. Their results are already delivered inline to the caller; reserve
<task-notification>forrun_in_background: truelaunches, where the notification is the only delivery channel (and where the resumability affordance in the notification note actually applies). - A settings.json filter, e.g.
"taskNotifications": "direct-background-only" | "all" | "none"— scoping injection by launch mode and/or tree depth. - Batch/coalesce: deliver queued notifications as a single combined injection instead of one full-context turn each.
Steps to Reproduce
- In a main session, launch an orchestrator:
Agent(prompt: ..., run_in_background: true). - Inside it, have the orchestrator launch several workers synchronously (
run_in_background: false, multiple calls in one turn for parallelism). - Observe: each worker's completion injects a
<task-notification>(with the full result payload) into the main session, triggering a model turn there — despite the orchestrator having received each result inline and continuing normally.
Environment
- Claude Code 2.1.198, macOS (darwin 25.2.0), npm install (fnm-managed Node)
Related (not duplicates — different mechanisms)
- #76021 — duplicate
[queued]+ delivered injection of the same notification - #76174 — background-task notification routed to a wrong/unrelated session