refactor(adk): improve turn loop structure#1134
Merged
Merged
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## alpha/10 #1134 +/- ##
===========================================
Coverage ? 81.82%
===========================================
Files ? 193
Lines ? 30599
Branches ? 0
===========================================
Hits ? 25038
Misses ? 3764
Partials ? 1797 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
663a748 to
b96ffa6
Compare
Change-Id: I49b6c3194e0cb6de1dbffa8e3a00cef61f5ceab2
Change-Id: I4196fdde757e9f954978f3eb563a16a0aeae8634
b96ffa6 to
853299e
Compare
Change-Id: I28981523402d5962d8500c0df0d749afdc810482
N3kox
approved these changes
Jul 6, 2026
Change-Id: Icaca8726d10128529b496b3565292e5acfc192c6
N3kox
approved these changes
Jul 6, 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.
TurnLoop Teardown and Structure
Problem
TurnLoophad three quality risks in the same lifecycle area:OnAgentEventsreturned. That made captured framework state such asCancelErrorand interrupt contexts vulnerable to unsafe concurrent access during Stop or preempt teardown.TurnBufferbefore preventing later writes. A concurrentPushthat had already passed the stopped check could write to the buffer afterTakeAll, making an accepted item disappear from bothUnhandledItemsandTakeLateItems.Solution
The teardown path now treats the proxy goroutine as part of the turn lifecycle. The loop cancels the agent, drains the proxy iterator, and waits for
proxyDonebefore reading framework-captured state.Cleanup now closes
TurnBufferbefore the finalTakeAll. This makes the exit boundary explicit: pushes that arrive before the close are drained as unhandled items, while pushes that race with or arrive after the close failTrySendand are recoverable as late items.Per-turn mutable state now lives in
turnExecution, so theTurnLoopobject no longer carries transient cancel, interrupt, and runner checkpoint fields across turns.The large implementation file is split along existing lifecycle boundaries:
turn_loop_preempt.go: preempt options, Push preempt routing, andpreemptController.turn_loop_stop.go: Stop options, Stop method behavior, andstopController.turn_loop_checkpoint.go: checkpoint persistence, resume state, bridge store setup, and cleanup checkpoint decisions.turn_loop_run.go: item collection, turn planning, event forwarding, proxy teardown, and framework error handling.turn_loop.go: public TurnLoop types, configuration, constructor, Run, and Wait.Key Insight
For
TurnLoop,doneonly means the user callback has returned. It does not imply the framework proxy has finished observing the underlying agent iterator. Correct teardown needs a second boundary: callback completion first, proxy completion second, then exit-state/checkpoint decisions.For cleanup,
stoppedis only an admission check. The durable recovery boundary is the buffer close: after cleanup starts, every pushed item must be classified exactly once as either unhandled or late.Summary
CancelErrorcould be captured after callback returnPushitems could be written after cleanup's final drainTurnBufferbeforeTakeAll, routing post-close pushes to late itemsturnExecutionTurnLoop 退出流程与结构整理
问题
TurnLoop在同一生命周期区域存在三个质量风险:OnAgentEvents返回后,代理 goroutine 仍可能继续转发 framework event。这会让CancelError、interrupt contexts 等 framework 捕获状态在 Stop 或 preempt teardown 期间存在并发访问风险。TurnBuffer,之后才阻止后续写入。并发Push如果已经通过 stopped 检查,就可能在TakeAll之后写入 buffer,导致一个已被接受的 item 既不在UnhandledItems中,也不在TakeLateItems中。方案
teardown 路径现在把代理 goroutine 视为 turn 生命周期的一部分。循环会先 cancel agent,再 drain proxy iterator,并等待
proxyDone,之后才读取 framework 捕获状态。cleanup 现在会在最终
TakeAll前关闭TurnBuffer。这让退出边界更明确:close 前到达的 push 会作为 unhandled item 被 drain;与 close 竞争或 close 后到达的 push 会因为TrySend失败而作为 late item 可恢复。每个 turn 的临时状态现在保存在
turnExecution中,TurnLoop本身不再跨 turn 持有 transient cancel、interrupt、runner checkpoint 字段。大文件按已有生命周期边界拆分:
turn_loop_preempt.go:preempt options、Push preempt routing 和preemptController。turn_loop_stop.go:Stop options、Stop method behavior 和stopController。turn_loop_checkpoint.go:checkpoint persistence、resume state、bridge store setup 和 cleanup checkpoint decisions。turn_loop_run.go:item collection、turn planning、event forwarding、proxy teardown 和 framework error handling。turn_loop.go:public TurnLoop types、configuration、constructor、Run 和 Wait。关键认知
对
TurnLoop来说,done只表示用户回调已经返回,不表示 framework proxy 已经完成对底层 agent iterator 的观察。正确的退出流程需要第二个边界:先等待 callback 完成,再等待 proxy 完成,之后才能做 exit-state 和 checkpoint 决策。对 cleanup 来说,
stopped只是 admission check。真正的可恢复边界是 buffer close:cleanup 开始后,每个 pushed item 都必须且只能被归类为 unhandled 或 late。总结
CancelError可能在 callback 返回后才被捕获Pushitem 可能在 cleanup 最终 drain 后写入TakeAll前关闭TurnBuffer,将 close 后 push 路由到 late itemsturnExecution