fix(agents): anchor active-turn timer to authoritative start time#2033
Open
wpfleger96 wants to merge 1 commit into
Open
fix(agents): anchor active-turn timer to authoritative start time#2033wpfleger96 wants to merge 1 commit into
wpfleger96 wants to merge 1 commit into
Conversation
The desktop active-turn badge reset to 0s repeatedly while agents were still working. Liveness pings previously started only after session setup and lacked a true start timestamp, so reconnect/resurrection re-anchored the badge to the recovery frame instead of the turn's actual start. turn_error also carried no turn_id, leaving terminal events unable to close out the right turn. - Start turn liveness at turn_started (covers session setup, not just the final prompt call) and carry the authoritative started_at on every observer frame for the turn. - Thread turn_id through PromptResult into emit_turn_error so error terminal events target the correct turn. - Resurrect uses the frame's startedAt when present and not later than the frame itself, so recovered turns keep their true elapsed time. - Prune-pause is now per-agent (not global) with a 3-minute bounded cap, so a genuinely dead turn still prunes instead of pausing indefinitely. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96
force-pushed
the
duncan/turn-timer-liveness-anchor
branch
from
July 17, 2026 18:16
fc402ab to
74071e6
Compare
atishpatel
approved these changes
Jul 17, 2026
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.
The desktop active-turn badge next to the channel name was resetting to
0srepeatedly while agents were still actively working, even though the observer feed showed continuous thinking/tool-call activity.Root causes:
turn_started. Session creation and context fetches can themselves take longer than the desktop's bounded prune pause, so the badge appeared to reset during that window.turn_errorterminal events carried noturn_id, so they couldn't reliably close out the turn they belonged to.Changes:
run_prompt_tasknow starts the liveness background task atturn_started(via aLivenessGuardthat aborts on drop) and stamps every observer frame for the turn with an authoritativestarted_at.PromptResultcarriesturn_id, threaded intoemit_turn_errorso error terminal events target the correct turn.resurrectTurnuses the frame'sstartedAtwhen present and not later than the frame itself, preserving true elapsed time across recovery; falls back to the recovery timestamp otherwise.shouldPausePruneis now scoped per agent instead of globally, with a bounded 3-minute cap so a genuinely dead turn still prunes.Liveness-spawn deviation from plan: liveness now runs as a spawned background task (not a
selectarm) so it can cover pre-prompt session setup, not just the final prompt call. That introduced a cross-thread race — a liveness tick could be mid-emit when the guard'sabort()lands, letting a staleturn_livenessframe reach the wire afterturn_completed. Closed (not just narrowed) via a shared mutex:LivenessGuard::droptakes the same lock the emitting tick holds across its check-then-emit, so drop either sees the tick hasn't started (and suppresses it) or blocks until the in-flight emit has fully landed before aborting — no interleaving can emit a liveness frame after the guard has dropped. Pinned by two tests that assert the mechanism itself rather than timing:test_liveness_emits_nothing_once_closed_flag_is_set(pre-set flag suppresses the emit) andtest_liveness_guard_drop_blocks_while_emit_lock_is_held(real OS threads, no cooperative scheduling — proves drop can't return while the emit lock is held).Same deviation also meant the spawned task captured its context once, before session resolution, so liveness frames carried
session_id: Nonefor the whole turn (a wire regression vs. the prior code, which built liveness after session resolution). Fixed by backfilling the session ID onto the shared liveness state once known (LivenessGuard::set_session_id), so ticks after resolution carry the real session; covered bytest_liveness_backfills_session_id_after_resolution.