前端收口:待发消息队列改由事件投影驱动,退役本地队列机器
- 新增 conversation/directPendingTurns.ts:入队/移除两个纯函数,不判上限、不排期、不排序 - reducer 新增 queue.enqueued / queue.removed 分支,折成 pendingTurns 投影 - chip 文案派生搬到 pendingTurnChipLabel.ts,删除 chatComposerQueue.ts 整套本地队列实现 - controller 退役 queuedTurns / queueSequenceRef / completionPendingRef / busyBaselineTurnCountRef 与看门 counts effect;取消 chip 改调 remove_direct_project_pending_turn 按 typed 结果说话 - 埋点句柄按 clientTurnId 存表,回合终态按本轮开口条目身份结算 - turnBusy 改名 commandInFlight 并收窄成 IPC 在飞;忙态 = 原生在跑 ∨ IPC 在飞 ∨ 待发消息非空 - 提交改成等命令的入队结果:用户自己能改的入队失败保留草稿,其余按已交出处理 - 测试:队列投影单测、入队只发一次 IPC、IPC 在飞挡住第二次提交、满队保留草稿、取消已放行条目 - 文档:实施计划标注第 0–3 步落地并补第 3 步细则,ADR 的埋点与退役面口径改成落地后的写法
This commit is contained in:
@@ -2,11 +2,22 @@
|
||||
|
||||
更新时间:`2026-09-24`
|
||||
|
||||
状态:**待实施**(设计已定稿,尚未开工)
|
||||
状态:**实施中**(第 0–3 步已落地;第 4、5 步待做)
|
||||
|
||||
设计口径见 [`【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24`](../adr/【ADR】DirectProject命令入队化与待发消息队列归宿主-2026-09-24.md)。
|
||||
本文件只排实施顺序、不变式与验收,不重复设计理由。
|
||||
|
||||
## 落地进度
|
||||
|
||||
| 步骤 | 状态 | 落地说明 |
|
||||
| --- | --- | --- |
|
||||
| 第 0 步 词表切换 | 已落地 | `rg "接单\|拒单"` 只剩 `direct_runtime/mod.rs` 对旧 ADR 文件名的引用(链接完整性,故意保留)与 `codex_app_server/mod.rs` 的一处假阳性 |
|
||||
| 第 1 步 命令 = 入队 | 已落地 | `enqueue_direct_codex_turn`(`+_typed`);队列条目落在 `agent/direct_thread_queue.rs`,带上入队时产出的 `prompt` / `creation_type` / `analytics_attempt_id` |
|
||||
| 第 2 步 队列归 Thread Manager | 已落地 | `StoredEvent.pending` 产物字段 + `enqueue_pending_turn` / `remove_pending_turn` / `claim_pending_turn`;放行在 `agent/direct_turn_dispatch.rs`(`kick_direct_queue_dispatch` + `DirectTurnReservation`) |
|
||||
| 第 3 步 前端收口 | 已落地 | 见下面「第 3 步的落地细则」 |
|
||||
| 第 4 步 CLI 与夹具退役 | 待做 | |
|
||||
| 第 5 步 守卫清理 | 待做 | |
|
||||
|
||||
## 第 0 步:词表切换(与代码同批,不单独提交)
|
||||
|
||||
「接单 / 拒单」退役,改成「入队 / 入队失败 / 放行」。逐处按角色改,**不做字面替换**:
|
||||
@@ -88,6 +99,26 @@ kick 幂等(并发两次只认领一次);队首在放行后被移除、rem
|
||||
|
||||
## 第 3 步:前端收口
|
||||
|
||||
### 第 3 步的落地细则
|
||||
|
||||
- 队列投影是新文件 `chat/conversation/directPendingTurns.ts`:只做 `enqueuePendingTurn` /
|
||||
`removePendingTurn` 两个纯函数,**不判上限、不排期、不排序**;上限与认领顺序只在宿主。
|
||||
reducer 的 `queue.enqueued` / `queue.removed` 两个分支是它唯一的调用方。
|
||||
- chip 文案派生搬到 `chat/components/DirectProjectComposer/pendingTurnChipLabel.ts`
|
||||
(`pendingTurnChipLabel`),与 `chatComposerQueue.ts` 一起把"队列在本地"的最后一份实现删掉。
|
||||
- **草稿清不清由命令的入队结果回答**:`onSubmit` 改成返回 `Promise<boolean>`,composer 只在
|
||||
`true` 时清草稿。返回 `false` 的口子是"用户自己就能改的入队失败"(队列已满、参数无效这类)——
|
||||
宿主已经给了同级提示,草稿再没了就等于让用户重打一遍。写权限门让路给确认流程时返回 `true`
|
||||
(内容已经在重跑的入参里),与入队化之前一致。
|
||||
- **埋点句柄按 `clientTurnId` 存成一张表**(`pendingRunAnalyticsRef`),回合终态按本轮开口条目的
|
||||
canonical 身份(`turn.completed.userItemId` ↔ `directCodexConversationMessageId(clientTurnId,'user')`)
|
||||
认领结算;身份缺失时退回结算最早的那一条(放行严格按队首顺序,收口顺序就是入队顺序)。
|
||||
入队失败的那一轮直接把句柄删掉,不结算。
|
||||
- `displayBusy` = 原生在跑 ∨ IPC 在飞 ∨ 待发消息非空;`commandInFlight` 收窄成"IPC 在飞"。
|
||||
- 取消 chip 调 `remove_direct_project_pending_turn`,按 typed 结果 `removed / alreadyDispatched /
|
||||
notFound` 说清楚;chip 的撤除仍然只认 `queue.removed` 事件(界面不改本地队列)。
|
||||
|
||||
|
||||
退役:
|
||||
|
||||
- `chatComposerQueue.ts` 的 `enqueueChatTurn` / `dequeueChatTurn` / `removeQueuedChatTurn` / `isChatTurnQueueFull` /
|
||||
@@ -104,8 +135,9 @@ kick 幂等(并发两次只认领一次);队首在放行后被移除、rem
|
||||
- 忙态 = 事件投影 + 「队列非空」指示;`directProjectTurnStatus` 的"命令在飞"分支收成 IPC 在飞。
|
||||
- 写权限门 `ensureConversationWriteAllowed` 留在入队之前(确认框必须在用户在场时弹)。
|
||||
|
||||
验收:`tests/directThreadChat.test.ts` 补队列事件投影用例;`tests/appSurface/chat-composer.suite.ts` 的排队 / 取消 / 满队 /
|
||||
放行三组用例改成新语义;`npm --workspace apps/ai-game-creator-shell run typecheck` 通过。
|
||||
验收:`tests/directThreadChat.test.ts` 补队列事件投影用例(顺序、幂等、按身份移除、bootstrap 带出在队条目、
|
||||
回合收口不清队列);`tests/appSurface/chat-composer.suite.ts` 的排队 / 取消 / 满队 / 放行四组用例改成新语义,
|
||||
并新增「入队只发一次 IPC」「IPC 在飞时挡住第二次提交」两条;`npm --workspace apps/ai-game-creator-shell run typecheck` 通过。
|
||||
|
||||
## 第 4 步:CLI 与夹具退役
|
||||
|
||||
|
||||
Reference in New Issue
Block a user