[Fix] Paired OpenCode idle events finalize runs while a drained follow-up turn is still running - #1157
Merged
Merged
Conversation
…follow-up OpenCode 1.17 emits session.status(idle) followed by a paired legacy session.idle for a single turn boundary. When the status-sourced turn completion drained a queued deliverable prompt (e.g. a hidden PR re-review follow-up), submitPrompt re-armed inFlight, so the trailing session.idle re-entered finishCurrentTurn, emitted a second taskCompleted against the now-empty queue, and HarnessManager finalized the run (onExit) while the drained turn was still running. The follow-up then ran in a post-finalization window kept alive only by the sleep heartbeat, and the drained turn's real completion later fired a duplicate onExit. Fix: arm an ignoreNextQueuedDrainSessionIdle guard when a status-sourced completion's drain submits a prompt, mirroring the existing paired-idle guards for stop-hook reminders, question replays, and provider recovery. Busy/retry status transitions and StartNewTask clear it like its siblings. The existing lifecycle test missed this because its mock emitted only a single session.idle; the new regression test completes turns with the real paired sequence and fails deterministically without the fix. Also include the deferral decision inputs (phase, queued prompt count, deferred settlement) in the manager's task-completed log line so future bypasses are diagnosable from live logs.
Contributor
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.
Problem
OpenCode 1.17 emits
session.status(idle)followed by a paired legacysession.idlefor a single turn boundary. When a turn ends with a deliverable prompt queued (for example the hidden re-review follow-up that active PR reviews receive when commits are pushed mid-review, #1150), the status-sourced completion correctly defers run finalization and drains the queued prompt. But draining submits the prompt and re-armsinFlight, so the trailing pairedsession.idlere-enteredfinishCurrentTurn, emitted a secondtaskCompletedagainst the now-empty queue, andHarnessManagerfinalized the run (onExit) while the drained turn was still running.Consequences:
onExit.The existing lifecycle integration test missed this because its mock emitted only a single
session.idle, never the paired sequence the real binary produces.Fix
Arm a new
ignoreNextQueuedDrainSessionIdleguard when a status-sourced turn completion's drain submits a queued prompt, mirroring the three existing paired-idle guards (stop-hook reminder, question replay, provider recovery). The trailingsession.idleis swallowed exactly once; busy/retry status transitions andStartNewTaskclear the flag like its siblings. Turns that end with onlysession.idle, empty-queue completions, and the other guards are unchanged.Also includes the deferral decision inputs (phase, queued prompt count, deferred settlement) in the manager's task-completed log line so future bypasses are diagnosable from live logs.
Testing
active-review-follow-up-paired-idle.test.tscompletes turns with the real paired sequence via the real harness + manager. It fails deterministically without the fix (prematureonExit) and passes with it.opencode serve1.17.8 binary with the real harness + manager, injecting a hidden follow-up mid-turn: pre-fix, the paired idle fired a secondtaskCompleted16ms after the drain andonExitlanded between the turns, followed by a thirdtaskCompletedand duplicateonExitwhen the follow-up turn actually finished. With the fix: exactly onetaskCompletedper turn and a singleonExitafter the follow-up turn settled.tsc --noEmitclean.