feat(orchestrator): drain the reveal sweep before finalize restructures

finalize's ReplaceSubtree allocates ids the reveal overlay never saw,
so a mid-animation section snapped in at once and the agent cursor
lost its target. Wait (worker thread, abort-aware, capped) for the
scheduled sweep to finish first.
This commit is contained in:
Fini 2026-07-04 02:15:58 +08:00
parent a9845e6239
commit eb7724d48f
2 changed files with 34 additions and 0 deletions

View file

@ -542,6 +542,14 @@ impl Orchestrator {
});
}
// Let the reveal sweep FINISH before cleanup restructures the tree:
// `finalize_design`'s ReplaceSubtree allocates fresh ids that were
// never registered with the reveal overlay, so a section still
// mid-animation snaps in all at once and the agent cursor loses its
// target (measured: the tail of a run popped in "一口气" while
// earlier sections streamed). Worker-thread wait, abort-aware.
crate::subagent::wait_for_reveal_drain(self.agent_indicator_epoch, abort);
// -- 阶段 4:清理 --(有内容才跑;空 root 无可清理)
// Append mode (skip_root_insertion): scope cleanup to ONLY the roots
// this run inserted (post-remap ids from each outcome) so pre-existing

View file

@ -332,6 +332,32 @@ pub(crate) async fn run_subtask_with_reveal_at(
}
}
/// Block (worker thread, abort-aware) until the reveal overlay's scheduled
/// sweep for `epoch` has finished playing. The finalize passes swap subtrees
/// via `ReplaceSubtree`, whose fresh ids were never registered with the
/// overlay — restructuring mid-sweep snaps the still-animating tail of the
/// design in at once and orphans the agent cursor. Capped so a stuck clock
/// can't hang the run.
pub(crate) fn wait_for_reveal_drain(epoch: Option<u64>, abort: &crate::types::AbortFlag) {
let Some(epoch) = epoch else {
return;
};
let cap = reveal_now_millis().saturating_add(15_000);
loop {
if abort.is_set() {
return;
}
let now = reveal_now_millis();
let Some(end) = op_editor_core::agent_indicators::latest_reveal_end_ms(epoch) else {
return;
};
if now >= end || now >= cap {
return;
}
std::thread::sleep(std::time::Duration::from_millis((end - now).min(120)));
}
}
pub(crate) fn reveal_now_millis() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)