修正 DirectProject 订阅语义:bootstrap 事件就是当前要处理的事件
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled

subscribe 已在同一边界把游标钉在队尾,因此前端直接 reduce bootstrap 事件,不再补一次 consume
主规范、ADR 与实施计划同步删除"竞态窗口"说法,notify 仍是唯一唤醒来源
This commit is contained in:
2026-09-16 19:06:05 +08:00
parent 42b702d362
commit 09ad0073fe
3 changed files with 3 additions and 3 deletions
@@ -21,7 +21,7 @@ AGC 项目开发聊天框当前同时从三处取数据:Direct 回合事件(
- 合并只在前端,规则只保留「先到定形、后到补空白」:第一次见到的快照决定卡片形状,后续快照只补输出与状态,不做逐字段优先级表。
- 活动回合的唯一判据是「出现过 `turn.started` 且未出现对应 `turn.completed`」;进程重启后队列消失,历史里的半截回合一律按已结束渲染。
- 分页锚点取原始条目 id;一次翻页操作在前端自动连拉,直到出现可显示条目或 `hasMore=false`,上限 5 页。
- `notify` 是唯一唤醒来源:前端在 bootstrap 之后立刻 `consume` 一次补齐竞态窗口,之后完全由 `notify` 驱动,不设低频 tick 或任何轮询兜底。
- `notify` 是唯一唤醒来源:`subscribe` 的 bootstrap 事件本身就是该 subscriber 此刻要处理的事件(游标已在队尾),前端直接 reduce 它们,不额外补一次 `consume`,之后完全由 `notify` 驱动,不设低频 tick 或任何轮询兜底。
- 迁移按一次干净切换落地:不做灰度、不做运行时开关、不双跑;允许提交序列里存在「新源已启用、旧代码尚未删除」的中间窗口,禁止反向的「新源未启用、旧源已删」。
- 思考过程与工具活动同样从运行态事件与历史条目推断,界面展示保持不变。
- 运行态事件必须自足:`item.started` / `item.completed` 携带与历史切片同形的完整**脱敏原始条目**,前端按归一后的 `itemId` 合并快照得到运行中与完成态;不提供按 `itemId` 单点取快照的接口。
@@ -17,7 +17,7 @@
1. Rust 只搬运:`agent/direct_thread_raw_item.rs` 把 Codex 原始条目挑字段、脱敏、截断后下发,事件载荷与历史切片同形,不生成卡片形状。
2. 条目身份归一:`itemId = call_id ?? id`,同时从事件 envelope 与前端形状里删掉第二个 id 概念;历史切片的 `firstItemId` 继续取文件里的原始 item id。
3. 思考正文流式:`item/reasoning/summaryTextDelta``item/reasoning/textDelta` 产出 `item.delta{kind:"reasoning"}`;plan 文本与命令输出保持活动状态。
4. 前端收敛为单一 reducerbootstrap 与 consume 走同一条事件流,bootstrap 后立刻 consume 一次补齐竞态窗口,此后只由 notify 唤醒合并规则只保留"先到定形、后到补空白",并删掉 `deltaText` 缓冲与回合结束后的运行态残留。
4. 前端收敛为单一 reducer`subscribe` 返回的 bootstrap 事件就是已暂存的运行态,游标已经在队尾,前端直接 reduce 这批事件即可(不需要再补一次 `consume`);此后只由 notify 唤醒 `consume`合并规则只保留"先到定形、后到补空白",并删掉 `deltaText` 缓冲与回合结束后的运行态残留。
5. 首屏与分页:以 `lastCompletedItemId` 为锚点取最近切片,历史读取改为从文件尾反向扫描;锚点按原始 item id 推进,切片无可见条目时自动连拉(上限 5 页)。
6. App.tsx 接线:订阅 + 立即 consume + notify 唤醒,聊天视图改由 reducer 状态投影(含工具卡片),删除 Direct 回合事件订阅与 `directTurnStream` / `directToolCalls` 状态。
7. 删除只服务旧读路径的命令与前端调用(`read_direct_project_history``read_direct_turn_stream``read_direct_tool_calls``list_game_creator_direct_active_turns`),DirectRuntime 自己的写入保留。
@@ -98,7 +98,7 @@ readHistory(threadId, { beforeItemId?, limit }) -> {
`notify` 只负责唤醒,不携带事件、cursor 或持久化状态。前端收到通知后调用 `consume`;通知可合并、重复或丢失,事件完整性由 `consume` 保证。
`subscribe` 把新 subscriber 的游标钉在当时的队尾,因此前端拿到 bootstrap 后必须**立刻 `consume` 一次**,补齐"订阅落定到首条事件"之间的竞态窗口;此后不再轮询,也不设任何定时 `consume`。用定时器兜底既自举不了(判断"有活动回合"本身依赖事件),也把唤醒机制变成两套。
`subscribe` 在同一个边界内先把新 subscriber 的游标钉在当时的队尾,再收集 bootstrap 的运行态事件,因此 bootstrap 返回的那批事件**就是**该 subscriber 此刻应处理的事件:前端直接 reduce 它们即可,不存在"再补一次 `consume` 才能补齐竞态窗口"的步骤。此后不再轮询,也不设任何定时 `consume`——唤醒只由 `notify` 负责。用定时器兜底既自举不了(判断"有活动回合"本身依赖事件),也把唤醒机制变成两套。
首屏历史不通过"读取整份对话"的命令获取:`subscribe` 返回的 `lastCompletedItemId` 就是首屏锚点,前端据此调用 `readHistory` 取最近的切片,再按滚动或按钮继续向前分页。系统不提供返回整份对话历史的命令。