补齐 DirectProject 聊天真相源收敛的文档结论

- 主规范:运行态事件改为下发与历史切片同形的脱敏原始条目,不再只给类型与身份
- 主规范与 ADR 写清回执竞态:回执前到达的通知只能记欠账,回执到达后必须补一次 consume
- ADR 与实施计划说明 list_game_creator_direct_active_turns 有意保留,服务首页运行中项目列表
- 里程碑验收标准按自动化证据勾选,运行时验收与已知环境缺口单独标注
- 实施计划补 export_bindings 后需 prettier 生成目录、测试文件更名与竞态风险项
This commit is contained in:
2026-09-16 22:16:24 +08:00
parent 33ddb5de27
commit 5eba53a5cc
4 changed files with 28 additions and 25 deletions
@@ -3,7 +3,7 @@
| 字段 | 值 |
| --- | --- |
| Milestone | `docs/project-memory/plans/【里程碑】DirectProject聊天真相源收敛-2026-09-16.md` |
| Status | ready |
| Status | implemented |
| Owner | Codex |
## 修改边界
@@ -18,17 +18,17 @@
2. 条目身份归一:进队列前收敛成一个 `itemId`,事件 envelope 与前端形状里都不出现第二个 id 概念;历史切片的 `firstItemId` 继续取文件里的原始 item id。
3. 删掉回合身份:`turn.started` 无载荷、`turn.completed{status}`,条目 / 增量 / 请求 / 生命周期锚点都不带 turn id;队列 `append` 直接收 `DirectThreadEvent``seq` 内部自算。
4. 思考正文流式:`item/reasoning/summaryTextDelta``item/reasoning/textDelta` 产出 `item.delta{kind:"reasoning"}`;plan 文本与命令输出保持活动状态。
5. 前端收敛为单一 reducer`subscribe` 返回的 bootstrap 事件就是已暂存的运行态,游标已经在队尾,前端直接 reduce 这批事件即可(不需要再补一次 `consume`);此后只由 notify 唤醒 `consume`。合并规则只保留"先到定形、后到补空白"(正文只增不减、工具状态允许从 running 升级到终态),`item.delta` 直接追加到运行态条目正文,删掉 `deltaText` 缓冲,`turn.completed` 把运行态条目并入历史再清空。
5. 前端收敛为单一 reducer`subscribe` 返回的 bootstrap 事件就是已暂存的运行态,游标已经在队尾,前端直接 reduce 这批事件即可(不需要为了拿这批事件再补一次 `consume`);此后只由 notify 唤醒 `consume`唯一例外是回执竞态:Rust 注册完 subscriber 就开始通知,而前端要等回执才知道 `subscriptionId`,这段时间到达的通知只能记欠账,回执到达后立刻补一次 `consume`(否则整轮最后一个事件之后可能再无通知,事件会卡死在队列里)。合并规则只保留"先到定形、后到补空白"(正文只增不减、工具状态允许从 running 升级到终态),`item.delta` 直接追加到运行态条目正文,删掉 `deltaText` 缓冲,`turn.completed` 把运行态条目并入历史再清空。
6. 首屏与分页:以 `lastCompletedItemId` 为锚点取最近切片,历史读取改为从文件尾反向扫描;锚点按原始 item id 推进,切片无可见条目时自动连拉(上限 5 页)。
7. App.tsx 接线:订阅 + 立即 reduce bootstrap + notify 唤醒 consume,聊天视图改由 reducer 状态投影(含工具卡片),删除 Direct 回合事件订阅与 `directTurnStream` / `directToolCalls` 状态。
8. 删除只服务旧读路径的命令与前端调用(`read_direct_project_history``read_direct_turn_stream``read_direct_tool_calls``list_game_creator_direct_active_turns`),DirectRuntime 自己的写入保留
8. 删除只服务旧读路径的命令与前端调用(`read_direct_project_history``read_direct_turn_stream``read_direct_tool_calls`),DirectRuntime 自己的写入保留。`list_game_creator_direct_active_turns` 是唯一的例外并有意保留:它服务首页跨页面的「运行中的项目」列表(`WorkspaceLauncher` / `directActiveTurns.ts`),不是聊天框读路径
9. 测试与文档收口:补 reducer 单测、解锁跳过的工具卡片用例、更新主规范并把冲突的实施计划与工具卡片文档改写为当前状态。
## 验证命令
1. `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml direct_thread -- --nocapture`
2. `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml export_bindings`(生成 `src/features/project-workspace/generated/`
3. `npx vitest run apps/ai-game-creator-shell/tests/directThreadChat.test.ts apps/ai-game-creator-shell/tests/directThreadEvents.test.ts`
2. `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml export_bindings`(生成 `src/features/project-workspace/generated/`,随后用 `prettier --write` 格式化生成目录,避免未格式化的 ts-rs 输出混进提交
3. `npx vitest run apps/ai-game-creator-shell/tests/directThreadChat.test.ts apps/ai-game-creator-shell/tests/directTurnPresentation.test.ts`
4. `npx vitest run apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`
5. TypeScript 类型检查与 ESLint(范围同前次 DirectProject 迁移)。
6. `npm run check:encoding``npm run check:doc-index``git diff --check`
@@ -37,6 +37,7 @@
- 条目 id 空间不一致会让活跃条目永远收不到完成事件:第 2 步的归一必须在 Rust 出口完成;前端不得再拿到两个 id。
- 事件 payload 变大(命令输出、文件变更明细):继续沿用既有截断上限,并观察 Thread Manager 单 thread 字节上限是否被提前触发。
- 订阅过期:以重新 `subscribe` + bootstrap 后立即 `consume` + 原子替换处理,需要单测覆盖;不引入定时轮询。
- 订阅过期:以重新 `subscribe` + 原子替换处理,需要单测覆盖;不引入定时轮询。
- 回执竞态:`subscribe` 回执到达前产生的 `notify` 拿不到订阅身份,必须记欠账并在回执到达后补一次 `consume`;已有专门用例 `drains a notify that lands before the subscribe receipt` 钉住,改坏会让整轮事件卡死。
- 合并规则退化为"先到定形"后,若某类条目只有输出没有调用条目,该输出不显示;这是有意取舍,先观察再决定是否补规则。
- 回滚点:每一步都保持"新源可用即不依赖旧源"的中间态可回退;不允许出现新源未启用而旧源已删除的提交。
@@ -3,7 +3,7 @@
| 字段 | 值 |
| --- | --- |
| Version | 1.0 |
| Status | proposed |
| Status | implemented(自动化验收通过,运行时验收待补) |
| Date | 2026-09-16 |
| Parent Spec | `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md` |
@@ -50,18 +50,20 @@ AGC 项目开发对话的显示与恢复只依赖两项输入:**项目对话
## 验收标准
- [ ] 一次回合内,实时渲染的文本段与工具卡片顺序,与回合结束后重进项目看到的顺序一致。
- [ ] 回合进行中终止并重启进程后重进项目:已落盘的部分文本与工具卡片按原顺序出现,且界面不显示忙碌态。
- [ ] 进程存活期间的页面重进(含切走再切回)能恢复运行中回合,并允许终止。
- [ ] 历史分页在切片内全部是不可显示条目时仍能继续向前,不出现锚点停滞。
- [ ] 聊天视图不再读取 `turn-stream.jsonl` / `tool-calls.jsonl` / Direct 回合事件 / 活动回合快照;`read_direct_turn_stream``read_direct_tool_calls``list_game_creator_direct_active_turns` 不再被前端调用
- [ ] 同一工具调用在实时与回读各只出现一张卡片(两个 id 空间按归一后的 `itemId` 对齐)。
- [ ] 前端聊天状态里不再出现第二个 id 概念与任何回合身份字段;事件解析统一来自 ts-rs 生成绑定。
- [ ] 思考正文在回合进行中即可见,且不进入活动状态文本。
- [ ] 订阅过期后重新 `subscribe` 并原子替换状态,不重复渲染已完成的条目。
- [x] 一次回合内,实时渲染的文本段与工具卡片顺序,与回合结束后重进项目看到的顺序一致。【自动化:`project-development.suite.ts` 空对话首轮 + 历史切片工具卡片用例】
- [ ] 回合进行中终止并重启进程后重进项目:已落盘的部分文本与工具卡片按原顺序出现,且界面不显示忙碌态。【待真实 app-server 运行时验收】
- [ ] 进程存活期间的页面重进(含切走再切回)能恢复运行中回合,并允许终止。【待真实 app-server 运行时验收】
- [x] 历史分页在切片内全部是不可显示条目时仍能继续向前,不出现锚点停滞。【自动化:`direct_project_history` 尾部回扫与分页锚点用例】
- [x] 聊天视图不再读取 `turn-stream.jsonl` / `tool-calls.jsonl` / Direct 回合事件 / 活动回合快照;`read_direct_turn_stream``read_direct_tool_calls` 命令已删除。`list_game_creator_direct_active_turns` **有意保留**:它服务首页跨页面的「运行中的项目」列表(`WorkspaceLauncher` / `directActiveTurns.ts`),不属于聊天框读路径
- [x] 同一工具调用在实时与回读各只出现一张卡片(两个 id 空间按归一后的 `itemId` 对齐)。【自动化:reducer「先到定形、后到补空」合并单测 + 工具卡片渲染用例】
- [x] 前端聊天状态里不再出现第二个 id 概念与任何回合身份字段;事件解析统一来自 ts-rs 生成绑定。【自动化:`directThreadChat` / `directTurnPresentation` 单测 + `cargo test export_bindings` 生成绑定无差异】
- [x] 思考正文在回合进行中即可见,且不进入活动状态文本。【自动化:`directThreadChat``item.delta{kind:"reasoning"}` 单测】
- [x] 订阅过期后重新 `subscribe` 并原子替换状态,不重复渲染已完成的条目。【自动化:reducer 过期重订阅单测】
- [x] 通知先于 `subscribe` 回执到达时不丢事件:前端记欠账,回执到达后立刻补一次 `consume`。【自动化:`drains a notify that lands before the subscribe receipt` 用例】
## 证据要求
- 自动化Thread Manager 事件契约测试、原始条目搬运与脱敏测试、ts-rs 绑定生成(`cargo test export_bindings`)、聊天 reducer 单测(bootstrap / consume / 过期重订阅 / 残回合 / 先到优先合并 / 回合回收)、历史分页锚点测试、前端渲染测试(含此前跳过的工具卡片用例)
- 运行时:真实 app-server 会话下的新回合、杀进程重开、页面重进、分页与终止。
- 边界:订阅过期、事件重复与乱序、不可显示切片、无 `turn.completed` 的残回合、工具输出超长截断与脱敏。
- 自动化(已跑):`cargo test direct_thread`25 条)、`cargo test direct_project_history`20 条)、`cargo test export_bindings`(生成绑定与工作区无差异)、`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts`468 passed / 17 skipped)、`directThreadChat.test.ts` + `directTurnPresentation.test.ts` 单测、TypeScript 类型检查、ESLint 与 `prettier`
- 运行时(待补):真实 app-server 会话下的新回合、杀进程重开、页面重进、分页与终止。
- 边界:订阅过期、回执竞态、不可显示切片、无 `turn.completed` 的残回合、工具输出超长截断与脱敏。
- 已知环境缺口:本容器内 `rehype-highlight` 未安装且远端拉包被策略禁用,`ChatMarkdownMessage` / `AgentMessageContent``chatPromptPolish` / `clientApi` 的环境型失败与本里程碑改动无关。