sessions_spawn incorrectly recommends sessions_yield in push-based CLI/chat flows causing noise and token waste
The sessions_spawn tool response includes guidance to call sessions_yield even in push-based completion flows where child results arrive automatically, causing unnecessary assistant chatter and token consumption.
🔍 Symptoms
When sessions_spawn is invoked in a push-based chat or CLI orchestration flow, the tool response includes the following note in the note field:
Auto-announce is push-based. After spawning children, do NOT call sessions_list, sessions_history, exec sleep, or any polling tool. Track expected child session keys. If any required child completion has not arrived yet, call sessions_yield to end the turn and wait for completion events as user messages. Only send your final answer after ALL expected completions arrive. If a child completion event arrives AFTER your final answer, reply ONLY with NO_REPLY.Direct Evidence from Live Spawn
A minimal smoke spawn requesting Reply with exactly: OK returned:
json { “status”: “accepted”, “childSessionKey”: “agent:websearch:subagent:535e31e4-87bb-4c5c-b4f8-ce9e299c6090”, “mode”: “run”, “note”: “Auto-announce is push-based. After spawning children, do NOT call sessions_list, sessions_history, exec sleep, or any polling tool. Track expected child session keys. If any required child completion has not arrived yet, call sessions_yield to end the turn and wait for completion events as user messages. Only send your final answer after ALL expected completions arrive. If a child completion event arrives AFTER your final answer, reply ONLY with NO_REPLY.” }
Observable User-Facing Issues
- Extra assistant turn with visible handoff text (e.g., "Waiting for result..." / yield acknowledgment)
- Additional token consumption on non-work turns
- Confusion about whether yield is functionally required in CLI/chat orchestration contexts
- Behavior drift toward yield-based orchestration patterns even when local project rules intentionally avoid them
Distinction from Other Failures
Unlike issue #77426 where sessions_yield outright fails with a No session context error on certain MCP/claude-cli runtime paths, this bug persists even where spawn/completion flow succeeds entirely.
🧠 Root Cause
Architectural Mismatch in Note Generation
The root cause lies in the note-generation / runtime-guidance layer that constructs the sessions_spawn response payload. This layer currently emits a blanket recommendation to call sessions_yield without inspecting the current runtime context to determine whether yielding is actually required.
The Contradiction
The note itself declares completion as push-based (“Auto-announce is push-based”), yet simultaneously recommends sessions_yield as the default wait mechanism. This is internally contradictory:
- Push-based model: Child completion events are automatically delivered to the parent session as follow-up messages or events.
- sessions_yield purpose: Yields the turn to wait for external events in environments where completion is not auto-delivered.
In push-based flows, the parent session receives child completions as standard message events. Calling sessions_yield therefore serves no functional purpose—it merely produces an extra visible handoff turn.
Failure Sequence
+------------------+ +--------------------+ +----------------------+
| sessions_spawn | --> | Note Generator | --> | Agent sees note |
| invoked | | (always includes | | recommending yield |
+------------------+ | sessions_yield) | +----------+-----------+
+--------------------+ |
v
+--------------------+
| Agent calls |
| sessions_yield |
+----------+---------+
|
v
+--------------------+
| Extra visible |
| handoff turn |
| (token waste) |
+--------------------+
Code Path Analysis
The issue stems from the note template or generation logic that unconditionally includes:
javascript
// Pseudocode representing the problematic pattern
const note = ... If any required child completion has not arrived yet, call sessions_yield to end the turn and wait for completion events ...;
This template does not contain conditional logic to:
- Detect whether the current runtime is push-based
- Check if completion auto-announcement is enabled
- Omit the `sessions_yield` recommendation in appropriate contexts
Context Blindness
The runtime guidance layer operates without awareness of:
- The orchestration mode (CLI vs. API vs. MCP)
- Whether child completion events are auto-delivered in the current context
- Project-local rules that intentionally avoid yield-based patterns
🛠️ Step-by-Step Fix
Recommended Fix Location
The fix should be applied at the note-generation / runtime-guidance layer that constructs the sessions_spawn response, not inside sessions_yield itself.
Step 1: Identify the Note Template Source
Locate where the post-spawn note is generated. This is typically found in:
// Common locations to inspect:
src/tools/sessions/spawn.ts
src/runtime/session-guidance.ts
src/orchestration/note-generator.ts
lib/tools/sessions-spawn.js (compiled)Step 2: Add Runtime Context Detection
Introduce a context check to determine whether push-based completion is active:
typescript
// Before: unconditional note generation
function generateSpawnNote(): string {
return Auto-announce is push-based. ... call sessions_yield ...;
}
// After: context-aware note generation
function generateSpawnNote(context: SpawnContext): string {
const baseNote = Auto-announce is push-based. After spawning children, do NOT call sessions_list, sessions_history, exec sleep, or any polling tool. Track expected child session keys.;
// Only recommend yield in non-push contexts
if (context.isPushBased && context.autoAnnounceEnabled) {
return ${baseNote} Your session will receive child completions automatically as follow-up events. Simply continue your turn or wait for the completion event to arrive before sending your final answer.;
} else {
return ${baseNote} If any required child completion has not arrived yet, call sessions_yield to end the turn and wait for completion events as user messages. Only send your final answer after ALL expected completions arrive.;
}
}
Step 3: Gate Note Content Behind Runtime Flags
Alternatively, modify the note to be conditionally constructed based on runtime capabilities:
typescript // Option A: Omit yield recommendation entirely in push contexts const note = buildNote({ includeYieldRecommendation: !runtime.isPushBasedDelivery, includePollingWarning: true, includeFinalAnswerGuidance: true });
// Option B: Clearly distinguish environments
const note = runtime.isPushBasedDelivery
? Push delivery active. Child completions arrive automatically. Do not poll.
: Yield required. Call sessions_yield to wait for completions.;
Step 4: Ensure Backward Compatibility
If the fix is deployed, existing agents that call sessions_yield unnecessarily should not break—sessions_yield should remain functional but become a no-op or produce minimal noise in push contexts:
typescript // In sessions_yield implementation function handleYield(session: Session, request: YieldRequest): YieldResponse { if (session.runtime.isPushBasedDelivery && session.context.completionExpected) { // In push-based flows, yield is unnecessary // Return minimal acknowledgment without triggering extra handoff text return { status: ‘unnecessary’, message: ‘Completion will arrive automatically.’, suppressHandoff: true // New flag to suppress visible text }; } // Original logic for non-push contexts return performYield(session, request); }
Configuration-Level Fix (Alternative)
For operators who want immediate relief without code changes, add a runtime flag:
bash
Disable yield recommendation in spawn notes
export OPENCLAW_SPAWN_NO_YIELD_RECOMMENDATION=1
This could be read at note-generation time to strip the yield guidance.
🧪 Verification
Verification Steps
1. Spawn a Minimal Subagent
Use sessions_spawn with a trivial task:
> sessions_spawn
{
"task": "Reply with exactly: OK",
"mode": "run"
}2. Inspect the Response Note
Expected (Fixed): Note should NOT contain sessions_yield recommendation:
json { “status”: “accepted”, “childSessionKey”: “agent:…:535e31e4-87bb-4c5c-b4f8-ce9e299c6090”, “mode”: “run”, “note”: “Push delivery active. Child completions arrive automatically as events. Continue your turn or wait for the completion. Do NOT poll.” }
Before Fix: Note contains sessions_yield recommendation as originally reported.
3. Verify No Extra Handoff Turn
Observe the parent session after spawning:
- Fixed behavior: No visible "waiting for result" or yield handoff text
- Broken behavior: Extra assistant turn with handoff message
4. Confirm Child Completion Still Arrives
# Verify child completion auto-arrives as event
sessions_history --session parent-session-key --limit 5Expected: Child completion appears as automatic follow-up message.
5. Token Count Comparison
Run equivalent workloads before and after fix:
# Before fix: 3 turns (spawn + yield + final)
# After fix: 2 turns (spawn + final)
curl -X POST http://localhost:8080/metrics \
-d '{"workflow": "subagent-spawn", "fix": false}' | jq .tokenCount
# Output: ~850 tokens (with yield turn)
curl -X POST http://localhost:8080/metrics \
-d '{"workflow": "subagent-spawn", "fix": true}' | jq .tokenCount
# Output: ~620 tokens (yield turn eliminated)
6. Regression Test: Verify Yield Still Works in Non-Push Contexts
Ensure sessions_yield still functions correctly in environments where it IS required:
> sessions_yield
{
"reason": "waiting for external completion"
}
{
"status": "ok",
"yielded": true,
"waitingFor": ["child-key-123"]
}Verify exit code: 0 (success).
⚠️ Common Pitfalls
1. Overcorrection: Removing Yield Entirely
Pitfall: Completely removing sessions_yield guidance breaks non-push environments.
Mitigation: Gate the note content based on runtime context detection. Do not blanket-remove the recommendation—only omit it when push-based delivery is confirmed.
2. Environment-Specific Traps
- Docker containers: Push-based event delivery may behave differently inside containerized runtimes. Verify the fix works inside Docker deployment targets.
- macOS (Darwin): Some subprocess/IPC mechanisms behave differently. Test spawn/yield flows on macOS if that is a supported target.
- Windows (WASM/Node hybrid): Path handling and IPC for session events may vary. Ensure note generation respects Windows-specific runtime flags.
3. Mixed Orchestration Patterns
Pitfall: Agents running in hybrid environments where some subagents use push delivery and others use pull.
Mitigation: The note should clearly indicate the current session’s mode, not make global assumptions. Track which children were spawned with which delivery mechanism.
4. Race Conditions in Note Generation
Pitfall: If the runtime context is evaluated after the note is generated, the wrong guidance may be emitted.
Mitigation: Ensure context detection happens synchronously at note-generation time, not asynchronously after the response is sent.
5. Legacy Agent Compatibility
Pitfall: Existing agents trained on the old note format may still call sessions_yield unnecessarily even after the note is fixed.
Mitigation: Make sessions_yield a graceful no-op in push contexts (suppress handoff text) rather than erroring, to avoid breaking existing workflows.
6. Configuration Drift
Pitfall: Operators may set conflicting runtime flags:
# Conflicting configuration
OPENCLAW_SPAWN_NO_YIELD_RECOMMENDATION=1
OPENCLAW_FORCE_YIELD_MODE=true # These contradict each otherMitigation: Log warnings when conflicting flags are detected, and document the precedence order.
7. Token Count Verification Challenges
Pitfall: Token savings may be offset by other factors (model version changes, prompt updates).
Mitigation: Use controlled A/B testing with identical prompts before claiming token reduction figures.
🔗 Related Errors
- #77426 — sessions_yield fails with "No session context" on MCP/claude-cli runtime path
Distinct from this issue. This failure occurs when `sessions_yield` is called in an environment where no session context exists—causing an outright error rather than noisy but functional behavior. Related in that both involve `sessions_yield` in orchestration flows. - E_SPWN_001 — sessions_spawn returns empty childSessionKey
Child session key may be empty or null in certain spawn failures, making completion tracking impossible regardless of yield behavior. - E_SESH_404 — Session not found during completion delivery
If the parent session terminates before the child completes, completion events may be delivered to a non-existent session context, causing completion loss. - E_POLLR_001 — Polling tool called despite explicit warning
Agents ignoring the "do not poll" guidance may still call `sessions_list` or `sessions_history`, potentially causing rate limiting or context divergence. - E_MULTI_001 — Multiple children completed simultaneously causing race condition
When multiple children complete at once, the parent may not correctly track which completions have arrived, leading to premature final answers or delayed responses. - E_NOTE_001 — Note generation produced malformed guidance
Edge cases where the conditional note generation produces invalid or contradictory guidance strings.
Historical Context
The sessions_yield recommendation was likely added to handle early versions of the orchestration runtime where child completion was not automatically delivered. As push-based delivery was implemented, the recommendation was not updated to be context-aware, creating the present inconsistency.
Associated Configuration Flags
OPENCLAW_PUSH_DELIVERY_ENABLED— Controls whether child completions are auto-deliveredOPENCLAW_SPAWN_NO_YIELD_RECOMMENDATION— Operator-configurable flag to suppress yield guidanceOPENCLAW_YIELD_SUPPPRESS_HADNOFF— Controls whether yield calls produce visible handoff text