Skip to content

GSD wrap-up prompt interrupts in-flight tool calls and late async follow-ups still trigger extra turns #3512

@trirdb

Description

@trirdb

Problem

GSD's internal gsd-auto-wrapup warning can interrupt an active auto-mode turn while tools are still in flight. In the captured failure, three read calls were skipped with Skipped due to queued user message., then the provider returned Unknown error, and auto-mode paused as a provider error.

A secondary effect appears after unit completion: late async notifications still trigger extra LLM turns because GSD only clears queued follow-ups after runUnit() returns, not during pauseAuto() / stopAuto() cleanup.

Root Cause

1) gsd-auto-wrapup is injected as a turn-triggering message during active execution

In auto-timers.js, both the soft timeout warning and the context-budget warning call pi.sendMessage(..., { triggerTurn: true }) while the current unit is still active:

  • auto-timers.js:85-105
  • auto-timers.js:242-255

Relevant code:

pi.sendMessage({
  customType: "gsd-auto-wrapup",
  display: s.verbose,
  content: [ ... ].join("\n"),
}, { triggerTurn: true });

That message is not advisory-only; it starts a new turn immediately. In the captured activity log, the agent had just emitted three read tool calls for the tablet checkout artifacts, and those tool results were then returned as:

  • .gsd/activity/080-execute-task-M004-S12-T03.jsonl:137-139Skipped due to queued user message.
  • .gsd/activity/080-execute-task-M004-S12-T03.jsonl:140 → queued custom_message with customType:"gsd-auto-wrapup"
  • .gsd/activity/080-execute-task-M004-S12-T03.jsonl:141 → assistant stopReason:"error", errorMessage:"Unknown error"

This is a race/order bug: an internal housekeeping prompt is competing with the agent's active tool-using turn.

2) Queue cleanup only happens after runUnit() completes, not when auto-mode pauses/stops

auto/run-unit.js:91-100 tries to mitigate trailing async follow-ups by calling clearQueue() once after agent_end:

const cmdCtxAny = s.cmdCtx;
if (typeof cmdCtxAny?.clearQueue === "function") {
  cmdCtxAny.clearQueue();
}

But neither pauseAuto() nor stopAuto() performs equivalent queue clearing:

  • auto.js:360-520 (stopAuto cleanup)
  • auto.js:579-635 (pauseAuto cleanup)

So any follow-up notifications that arrive after unit completion / pause / stop can still trigger extra turns. That matches the observed repeated post-completion async_job_result turns where the agent kept replying Task T03 complete..

Expected Behavior

  1. gsd-auto-wrapup should never interrupt an active tool-use turn.

    • It should be delivered as a non-turn-triggering advisory (triggerTurn: false), or
    • queued until no tools are in flight / the unit is idle, or
    • surfaced only through UI notification/status instead of a model-facing message.
  2. pauseAuto() and stopAuto() should flush queued follow-up messages the same way runUnit() does.

    • Reuse the existing clearQueue() runtime check during pause/stop cleanup.
    • Ideally also suppress or coalesce late informational follow-ups (async_job_result, wrap-up warnings) once the unit has already completed.

Concrete fix suggestion

  • In auto-timers.js, change both gsd-auto-wrapup sends to avoid triggerTurn: true while a unit is active.
  • In auto.js, add the same clearQueue() cleanup used by auto/run-unit.js inside both stopAuto() and pauseAuto() before the session is marked inactive/paused.
  • If GSD wants the warning in the transcript, gate it behind getInFlightToolCount() === 0 / no active tool work before triggering a turn.

Environment

  • GSD version: 2.60.0
  • Model: gpt-5.4
  • Unit: execute-task M004/S12/T03

Reproduction Context

During auto-mode execution of M004/S12/T03, the agent was inspecting checkout parity screenshots after a replay failure. While the agent was still in an active browser/image-debugging turn, GSD's context-budget monitor injected gsd-auto-wrapup. That queued message caused the three pending read tool calls to be skipped, then the provider returned Unknown error, and auto-mode paused.

After task closeout, trailing async_job_result notifications still generated extra LLM turns (Task T03 complete. repeated several times), which points to missing queue flush on stop/pause.

Forensic Evidence


Auto-generated by /gsd forensics

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions