From d341a9be1339a981820ed5ae2c9f3e1121a010d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 30 Sep 2026 12:43:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E4=B8=AD=E9=80=94=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E8=80=85=E7=9A=84=E5=90=88=E5=B9=B6=E5=BF=AB=E7=85=A7=E7=94=A8?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 `late_subscriber_sees_the_running_turn_and_the_pending_queue_together` - 钉住:正在跑那一轮的 `turn.started` 锚点(带开口条目身份)与还排着的 `queue.enqueued` 同时进 bootstrap,顺序按事件序 - 原有用例各覆盖一半(一条只测队列、一条只测锚点),这条覆盖两者同时存在的中途加入场景 --- .../src/agent/direct_thread_manager.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_thread_manager.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_thread_manager.rs index 2e1fe0208..17375996e 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_thread_manager.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_thread_manager.rs @@ -1422,6 +1422,44 @@ mod tests { assert_eq!(enqueued_ids(&bootstrap.events), vec!["turn-2"]); } + /// 中途加入者拿到的是**合并快照**:正在跑那一轮的锚点 + 还没走的队列,顺序就是事件顺序。 + /// + /// 队列搬到宿主之后这件事只能由 live-set bootstrap 顶上:另一个窗口、离开工作台再回来, + /// 看到的状态必须和一直在场的那位一致——在跑的那一轮还在跑(锚点带开口条目身份,条目挂得回去), + /// 排着的那几条还在队,而不是只剩其中一个。 + #[test] + fn late_subscriber_sees_the_running_turn_and_the_pending_queue_together() { + let mut manager = DirectThreadManager::with_limits(100, 100_000); + manager + .enqueue_pending_turn("thread-1", pending_turn("turn-1")) + .expect("enqueue head"); + manager + .claim_pending_turn("thread-1", FIXED_AT_MS) + .expect("claim head"); + manager + .enqueue_pending_turn("thread-1", pending_turn("turn-2")) + .expect("enqueue tail"); + + let bootstrap = manager.subscribe("thread-1"); + assert!( + matches!( + bootstrap.events.as_slice(), + [ + DirectThreadEvent::TurnStarted { .. }, + DirectThreadEvent::QueueEnqueued { client_turn_id, .. } + ] if client_turn_id == "turn-2" + ), + "中途加入者要先看到在跑的那一轮、再看到还排着的那几条:{:?}", + bootstrap.events + ); + assert_eq!( + bootstrap.events[0].user_item_id(), + Some("direct-codex:turn-1:user"), + "锚点必须带着正在跑那一轮的开口条目身份" + ); + assert_eq!(enqueued_ids(&bootstrap.events), vec!["turn-2"]); + } + /// 入队幂等:判重范围是「在队 ∪ 正在跑的那一轮」,重复入队不排第二条、不发第二条事件。 #[test] fn enqueue_is_idempotent_per_client_turn_id() {