合并最新主分支并保留策划退役清理
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
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

合入 origin/master 的现役 Direct、Mac 随包资源、配置和测试变更
保留策划 Agent V1/V2 退役删除结果
仅在重叠文件保留当前分支的策划解耦代码
不额外处理 master 自身已有缺陷
This commit is contained in:
2026-09-18 08:04:10 +00:00
96 changed files with 5387 additions and 2039 deletions
@@ -1,26 +0,0 @@
# AGC 对话历史分页恢复实施计划
- Date: 2026-09-16
- Status: awaiting-runtime-acceptance
- Milestone: [AGC 对话历史分页恢复](./【里程碑】AGC对话历史分页恢复-2026-09-16.md)
## 实施
1. 原生历史读取复用逐行解析,增加消息模式,过滤后分页并返回已有原始消息 ID 游标;保持原始接口默认行为和路径权限。
2. 工作台首屏与更早消息读取显式请求消息模式,消费游标;加载代次隔离、单飞与 ID 去重。
3. 合成记录测试复现原始工具页卡住的形状,覆盖旧无 ID、坏行、时间、失败/重复/切项目;临时目录只读重放用户日志。
4. 前端/原生定向测试、类型、Lint、编码、文档和差异检查通过后,更新问题表及 PR 草稿并本地提交。
5. 在临时消息投影中标记历史来源,保留 Runtime 所有权语义;刷新合并时只保留非历史来源的待回读消息。补齐「先加载旧页,再 /history,再翻页」的顺序与去重回归,保持尚未落盘用户输入的保留逻辑。
## 边界与停止条件
不调整 Direct 消息呈现归属、不修未证明的写入丢失、不上传日志、不触碰用户项目。必要 API 变化仅为本地 IPC 可选参数和游标字段;无 OpenAPI、SpacetimeDB 或持久化迁移。远程推送/PR/WIP 操作仍待额外确认。
## 验收证据
- 历史消息模型、回合呈现及分页集成共 24 个前端测试通过;包含真实 App 的首屏/更早页、单飞、失败重试、重叠消息、同项目重新加载及离开再进入的迟到响应。
- 16 个历史原生测试通过;人工日志重放用例在 CI 默认忽略,已在本地单独执行通过。
- 使用用户提供的原始日志运行修复后的原生读取:44 条现存聊天消息(含 2 条用户消息)分 3 页取回,逐项内容与顺序一致,原文件字节未变;未在仓库保存原始日志。
- 工作台/Direct 恢复与画布导航的 8 个定向回归通过。真实客户端重新进入与向上翻页尚待用户验收;本次涉及 Rust IPC,需重新构建并启动原生端。
- 与前面画布/JSON 修复联合复验:119 个前端定向测试、30 个原生测试通过;AGC TypeScript、修改文件 ESLint、编码、文档索引和差异检查通过。
- 状态只覆盖日志中已经证明的分页卡页,不据此宣称其它可能的未落盘消息也已恢复。
@@ -32,7 +32,7 @@
| B02 | 初次进入双指平移无效,整理后恢复 | 已优化;用户在本轮反馈未再复现,按用户要求更新状态。隔离组件连续平移通过。 |
| B03 | 快速平移触发更新深度错误 | 已修复已确认的窗口 Context 反馈循环,回归验证收敛;真实操作继续观察。 |
| B04 | 资源选中后运行不可用提示消失 | 已修复,提示与选择解耦,自动化验证通过。 |
| B05 | 对话记录偶发丢失 | 已修复日志复现的历史分页卡点:消息模式过滤后分页、原生游标与读取代次隔离。原生只读重放分 3 页取回全部 44 条现存消息,原文件未变;真实客户端待验收,不扩大为其它未落盘记录已恢复。 |
| B05 | 对话记录偶发丢失 | 已定位历史分页卡点,尚未修改。只读复验用户提供的历史:558 条合法原始记录中有 44 条聊天消息;首屏原始 20 条只投影出一条助手消息,下一页原始 20 条没有聊天消息,按消息计算的游标不推进。已存用户提问和最终回答因此无法继续翻出;不能凭该文件排除其它未落盘记录。 |
| B06 | JSON 文档未正确识别展示 | 已按用户确认完成本地修复:合法 UI State 由原生完整校验,卡片显示 UI 设计并进入现有编辑器;普通 JSON 显示 JSON 并可代码预览。自动化验证通过,待重建原生客户端验收;详见 JSON 语义识别实施计划。 |
| C01 | 右键平移,保留左键框选 | 已实现,卡片左键拖动、框选、指针取消/失焦/捕获丢失及控件边界测试通过。 |
@@ -41,4 +41,4 @@
- 修复前新增回归测试能检出外壳重复发布、运行提示消失和右键无效;修复后窗口/画布定向测试通过,现有导航、框选、指针点击/取消、UI 编辑器返回平移和素材定位用例通过。
- AGC TypeScript、修改文件 ESLint、编码、文档索引及差异空白检查通过。
- 测试仍有既有 React 列表 key、旧用例 act/IPC 桩告警,未作为本批功能修复扩大范围。
- 用户反馈 B01/B02 本轮未再复现,记为已优化;右键手感与其它真实客户端细节继续观察。对话历史分页与 JSON 双路径均已本地修复并通过定向验证,待重建原生端后真实客户端验收。本计划保持开放。
- 用户反馈 B01/B02 本轮未再复现,记为已优化;右键手感与其它真实客户端细节继续观察。对话历史分页尚未修复;JSON 双路径已本地修复并通过自动化验证,待真实客户端验收。本计划保持开放。
@@ -0,0 +1,68 @@
# AGC 项目创建目录可选实施计划
Version: 1.0
Status: active
Date: 2026-09-17
Related Spec: `docs/technical/【技术方案】AGC模板库与模板建项-2026-09-17.md`、`docs/【技术方案】AGC异步操作可恢复闭环-2026-09-14.md`
## 目标
让用户在 AGC 里能自己选项目创建目录:首页「做游戏 / 做方案」自动建项与模板库「使用模板」建项都落在用户选定的目录下;不选时保持 AGC 管理的默认位置(`<app_data>/projects`),行为与现状一致。
## 交接结果
- `create_automatic_local_game_project`、`create_automatic_local_game_project_from_template` 新增可选参数 `projectsRoot`;为空时由 Rust 侧回落到默认目录。
- `pick_local_project_directory` 新增可选参数 `title`(只接受短标题,其余回退「选择游戏项目目录」)。
- 新增用户偏好「项目创建目录」:`localStorage` 键 `genarrative-ai-game-creator.project-creation-directory.v1`。
- 入口在客户端设置里:侧边栏「配置」→ 设置分类「工作区」;`RuntimeConfigDialog` 内用 `useProjectCreationDirectory` 展示与改选目录,不经过首页或模板库页头。
- 首页「做游戏 / 做方案」与模板库「使用模板」建项时读取同一份偏好,用户不需要在建项前再选一次。
## 行为契约
- 允许的来源只有一个:本机原生目录选择器(`pick_local_project_directory`)。它返回的目录已在选择时按 user-selected 范围做过一次加固,构成「用户显式选择」边界。
- Rust 侧 `validate_requested_game_project_creation_root` 对传入目录要求:非空、绝对路径、无控制字符、已存在的普通目录(符号链接 / Windows reparse point / 普通文件一律拒绝),并通过 `prepare_game_creator_project_root_for_read`(user-selected 范围的一次性修复)。目录不存在时不代为创建,直接失败。
- 建出的项目目录仍在所选目录下按 `gameagent-<8位短ID>` 命名,项目名、`.agent` 初始化、首轮投递与既有自动建项完全一致。
- 偏好只保存「用户意图」,不是授权凭据:存储被外部改动最坏只是回退默认位置或一次可见的建项失败,不会跳过 Rust 门禁。
- 该偏好是客户端本地设置,不写入 `read_game_creator_app_config` / 保存设置的那份运行时配置:在「工作区」里选择目录当场生效,与「保存设置」按钮无关。
- 「恢复默认位置」清空偏好即回到应用数据目录;既有项目不迁移。
## 步骤
1. **Rust 建项入口**
- `commands.rs`:新增 `validate_requested_game_project_creation_root` / `resolve_game_project_creation_root`,`create_automatic_local_game_project` 接受 `projects_root`;`pick_local_project_directory` 接受 `title`。
- `template_library.rs`:模板建项复用同一解析函数。
- 交付:两条定向单测(校验矩阵、在指定创建目录下建项)。
- 验收:`cargo test --bin genarrative-ai-game-creator-shell creation_root` 全绿。
2. **前端偏好与入口**
- `features/app-shell/model.ts`:`normalizeProjectCreationDirectory` / `readProjectCreationDirectory` / `writeProjectCreationDirectory` / `projectCreationDirectoryLabel`。
- `useProjectCreationDirectory`:选择目录、恢复默认、状态文案(失败在设置页字段内可见)。
- `RuntimeConfigDialog` 新增「工作区」设置分类,承载「项目创建目录」字段与「选择目录 / 恢复默认位置」动作。
- `useHomeProjectCreation` 与 `useTemplateLibrary` 在建项时带上 `projectsRoot`。
- 交付:偏好模型 4 项单测、设置页 1 项交互测试、appSurface 1 项「设置里选目录后建项」端到端场景。
- 验收:`npx vitest run apps/ai-game-creator-shell/tests/projectCreationDirectory.test.ts apps/ai-game-creator-shell/tests/appSurface.test.ts` 全绿。
3. **文档与共享记忆**
- 本实施计划;模板库技术方案的命令表补 `projectsRoot`;`decision-log.md` 记录偏好键与命令参数;`pitfalls.md` 说明用户自选目录与 AGC 管理目录的关系。
- 验收:`node scripts/check-doc-index.mjs` 通过。
## 验证命令
```bash
cargo check --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell
cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell creation_root
cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell template_library
cd apps/ai-game-creator-shell && npx tsc -p tsconfig.json --noEmit
npx vitest run apps/ai-game-creator-shell/tests
npm run check:encoding
node scripts/check-doc-index.mjs
git diff --check
```
手动验收(客户端内):侧边栏「配置」→「工作区」→「选择目录」选 `F:\Projects\我的游戏`,回首页建项后项目目录应为 `F:\Projects\我的游戏\gameagent-<8位>`;模板库「使用模板」建项同样落在该目录;点「恢复默认位置」后再建项回到应用数据目录。
## 风险与回退
- **自选目录无法加固**:`Documents` 等带受保护继承 ACL 的位置可能加固失败。失败发生在选择器或建项前置校验阶段,报错可见且不会留下半成品项目;用户改选其它目录或用默认位置即可。
- **偏好漂移**:偏好只是提示值,每次建项都会重新校验;存储被改坏不会绕过门禁。
- **回退**:删掉偏好键(或点「恢复默认位置」)即回到默认目录;需要彻底移除该能力时,去掉两个命令的可选参数与设置页字段即可,Rust 默认路径逻辑不变。
@@ -0,0 +1,43 @@
# 【实施计划】DirectProject 聊天真相源收敛
| 字段 | 值 |
| --- | --- |
| Milestone | `docs/project-memory/plans/【里程碑】DirectProject聊天真相源收敛-2026-09-16.md` |
| Status | implemented |
| Owner | Codex |
## 修改边界
- 允许修改:`agent/direct_thread_wire.rs`、`agent/direct_thread_manager.rs`、`agent/codex_app_server/`、`agent/direct_project_history.rs`、`main.rs` 命令注册、`src/features/project-workspace/generated/`(ts-rs 生成目录)、AGC 前端订阅与聊天投影、对应测试与 `docs/`。
- 明确不修改:SpacetimeDB schema 与绑定、HTTP/OpenAPI、DirectRuntime 自己的进度事件与 `turn-stream.jsonl` / `tool-calls.jsonl` 写入、Codex durable thread 行为、审批弹层现有状态来源。
- 保持 `.env` 未提交修改,不触碰个人配置。
## 实现顺序
1. Rust 只搬运:`agent/direct_thread_wire.rs` 把 Codex 原始条目挑字段、脱敏、截断后下发,事件载荷与历史切片同形,不生成卡片形状;线上模型是 ts-rs 导出的 tagged enum(`DirectThreadItem` / `DirectThreadEvent` / bootstrap / consume / history slice),`at` 标 `#[ts(as = "f64")]`,改完模型跑 `cargo test export_bindings` 生成前端绑定。
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`。唯一例外是回执竞态:Rust 注册完 subscriber 就开始通知,而前端要等回执才知道 `subscriptionId`,这段时间到达的通知只能记欠账,回执到达后立刻补一次 `consume`(否则整轮最后一个事件之后可能再无通知,事件会卡死在队列里)。合并规则只保留"先到定形、后到补空白"(正文只增不减、工具状态允许从 running 升级到终态),`item.delta` 直接追加到运行态条目正文,删掉 `deltaText` 缓冲,`turn.completed` 把运行态条目并入历史再清空。
6. 首屏与分页:以 `lastCompletedItemId` 为锚点取最近切片,历史读取改为从文件尾反向扫描;切片的新端边界由这个锚点给出(含该条,命令参数 `throughItemId`),首屏读取等订阅回执里的锚点,回执到达前不发请求、也不退化成「取文件尾」;之后锚点按原始 item id 推进(`beforeItemId`,不含锚点)。一次翻页操作在前端连拉,直到合并后聊天投影出现新回合(新的用户气泡)或 `hasMore=false`,每个操作上限 5 页;锚点未推进(`items` 为空 / `firstItemId` 为 null / 与请求锚点相同)时立即停止。可见性口径只在 `features/project-workspace/directHistoryPaging.ts` 实现一份,首屏与「显示更早」共用。
7. App.tsx 接线:订阅 + 立即 reduce bootstrap + notify 唤醒 consume,聊天视图改由 reducer 状态投影(含工具卡片),删除 Direct 回合事件订阅与 `directTurnStream` / `directToolCalls` 状态。
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/`),随后用 `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 apps/ai-game-creator-shell/tests/directHistoryPaging.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`
## 风险与回滚点
- 条目 id 空间不一致会让活跃条目永远收不到完成事件:第 2 步的归一必须在 Rust 出口完成;前端不得再拿到两个 id。
- 事件 payload 变大(命令输出、文件变更明细):继续沿用既有截断上限,并观察 Thread Manager 单 thread 字节上限是否被提前触发。
- 订阅过期:以重新 `subscribe` + 原子替换处理,需要单测覆盖;不引入定时轮询。
- 回执竞态:`subscribe` 回执到达前产生的 `notify` 拿不到订阅身份,必须记欠账并在回执到达后补一次 `consume`;已有专门用例 `drains a notify that lands before the subscribe receipt` 钉住,改坏会让整轮事件卡死。
- 合并规则退化为"先到定形"后,若某类条目只有输出没有调用条目,该输出不显示;这是有意取舍,先观察再决定是否补规则。
- 回滚点:每一步都保持"新源可用即不依赖旧源"的中间态可回退;不允许出现新源未启用而旧源已删除的提交。
@@ -0,0 +1,27 @@
# Mac 客户端随包运行依赖补齐实施计划
- Version: 2
- Status: awaiting-windows-acceptance
- Date: 2026-09-18
- Parent Spec: `【里程碑】Mac客户端随包运行依赖补齐-2026-09-18.md`
## 修改顺序
本轮评审修复顺序:统一 release context(覆盖 build/upload 两入口)→ 版本/端点/产物/清单的定向回归 → 宿主列表/启动/面板与前端自动启动能力门禁 → 同步单架构权威文档 → Node/Vitest/Rust/typecheck/编码/文档/diff 检查。用户随后授权同步 master、重打 0.1.67 并推送当前 PR 分支;不读取私钥、不上传安装包、不合并 PR。
1. 提取构建与运行共用的 Codex 平台布局;按 Cargo TARGET stage 锁定原生依赖并校验包元数据,保留可执行位。
2. 增加 macOS 专属 Tauri 资源映射、声明、产物忽略规则;复用插件 staging,不复制 Windows 原生 payload。
3. 修正 `.app/Contents/Resources` 定位与平台清单验证,保持外部安装回退。
4. 补目标布局、资源映射、缺失/篡改/平台身份、候选顺序与路径测试,运行定向门禁。
5. 构建 `.app`,在隔离目录做资源清点、原生二进制启动和 app-server 握手;生成 DMG 并校验。记录 GUI/真实 Provider 和 Cocos 未验收项。
## 命令与边界
- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml agent::codex_cli::tests:: -- --test-threads=1`
- `npm run ai-game-creator-shell:typecheck`
- 定向 Node 打包契约测试、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`
- 本地 Tauri 构建关闭 updater artifact;版本按用户要求统一为 0.1.67,不读取发布私钥,不运行 release upload。
## 风险与停止条件
磁盘不足、目标包不匹配或配置测试回归时停止打包,先修正原因,不跳过验证。用户已确认 Mac 本地可用并授权提交、推送当前分支和创建草稿 PR;产物放 gitignored target,不上传发布。回滚以本次路径 diff 为边界,不恢复无关文件。保留计划等待 Windows 回归验收,证据见对应里程碑规范。
@@ -1,20 +0,0 @@
# AGC 对话历史分页恢复
- Version: 1
- Status: reviewed
- Date: 2026-09-16
- Parent Spec: [AGC 实施计划:DirectProject 回合展示唯一归属](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)
## 范围与评审
用户已要求修复 B05。已用用户提供的原始日志只读复验:原始切片被工具/推理填满时,聊天投影为空,消息游标不推进。修复聊天读取与分页,不改历史写入、不删除记录、不修改模型上下文,不扩大到其它尚无证据的对话丢失原因。
采用已有切片接口的显式消息模式与原生游标。原始模式缺省行为保持不变,旧无 ID 消息保留;前端以项目和加载代次隔离结果。本轮不包含远程写入。
## 验收
1. 工具/推理密集、末尾无消息、纯工具历史均不产生空页死循环。
2. 消息正文、原始 ID、时间和顺序保持不变,翻页能到达早期用户提问及最终回答,不重复。
3. 连点、请求失败重试、项目切换和同项目重新加载不会污染消息或游标。
4. 原始切片默认模式回归通过,用户日志只读重放可以取回全部现存消息;不把真实日志或对话正文提交到仓库。
5. 加载更早消息后重新读取历史,旧页不出现在最新回复之后;再次翻页保持顺序且不重复。未落盘的实时用户输入不因历史刷新被丢弃。
@@ -0,0 +1,75 @@
# 【里程碑】DirectProject 聊天真相源收敛
| 字段 | 值 |
| --- | --- |
| Version | 1.0 |
| Status | implemented(自动化验收通过,运行时验收待补) |
| Date | 2026-09-16 |
| Parent Spec | `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md` |
## 目标
AGC 项目开发对话的显示与恢复只依赖两项输入:**项目对话历史**(`.agent/conversations/project.jsonl`)与 **运行态事件**(Thread Manager `subscribe` / `consume` / `notify`)。Direct 回合事件、`turn-stream.jsonl`、`tool-calls.jsonl` 与活动回合快照都不再是聊天视图的输入。
边界固定为:Thread Manager 只是**搬运层**——把 Codex 原始条目挑字段、脱敏、截断后下发;工具卡片的形状、可见性与合并全部由前端投影完成。线上模型是 ts-rs 导出的 tagged enum(`agent/direct_thread_wire.rs`),条目身份只有一个:Rust 在进队列前归一成一个 `itemId`,不再暴露第二个 id 概念,也不带任何回合身份。
## 范围
- 运行态事件自足化:`item.started` / `item.completed` 携带与历史切片同形的脱敏**原始条目**,前端用同一个投影函数处理实时与回读。
- 线上模型与提示词同源:条目与事件是 ts-rs 导出的 tagged enum,前端消费生成绑定(改 Rust 模型后跑 `cargo test export_bindings`);毫秒时间戳用 `#[ts(as = "f64")]` 对齐 Tauri JSON 通道的 `number`。
- 条目身份归一:进队列前收敛成一个 `itemId`(工具条目在 `project.jsonl` 里带调用 id 与 response item id 两个值);历史切片另给 `firstItemId` 作为分页锚点,锚点始终是文件里的原始 item id。
- 思考正文流式:`item/reasoning/summaryTextDelta` 与 `item/reasoning/textDelta` 以 `item.delta{kind:"reasoning"}` 下发正文;plan 文本与命令输出仍只降级为活动状态。
- 历史读取以 `subscribe` 返回的 `lastCompletedItemId` 为首屏锚点,切片从**文件尾反向扫描**;首屏切片的新端边界就是这个锚点(含该条,命令参数 `throughItemId`),比锚点更新的条目只从运行态事件来;向后翻页用切片返回的 `firstItemId`(`beforeItemId`,不含锚点)。后端切片只按原始条目切片,可见性判断留在前端聊天投影;一次翻页操作在前端连拉取页,直到合并后聊天投影出现新回合或 `hasMore=false`,每个操作上限 5 页。
- 前端收敛为单一事件 reducer 与单一聊天投影;活动回合只由 `turn.started` 与 `turn.completed` 判定(事件不带 turn id,前端只有一个 `turnRunning` 布尔);`item.delta` 直接追加到运行态条目正文,不保留增量缓冲;`turn.completed` 把该回合运行态条目并入历史再清空。
- 删除前端对 Direct 回合事件、`turn-stream.jsonl`、`tool-calls.jsonl`、活动回合快照的读取,以及只服务这些读取的命令与状态。
## 不在范围内
- 审批、提问与用户输入请求的状态机迁移;本次事件只作同一条流的 pass-through。
- 跨进程回合账本、按回合统计与持久 turn ledger。
- DirectRuntime 自己的进度事件与该运行时仍在使用的 `turn-stream.jsonl` / `tool-calls.jsonl` 写入:它们属于运行时的账本,本轮只切 DirectProject 聊天框的读路径。
- 旧项目磁盘上既有投影文件的清理、迁移或回填。
- 非 DirectProject 运行时、Codex durable thread 语义、SpacetimeDB 与 HTTP 契约。
## 已确认的决策
- 投影在前端:Rust 不生成 `kind` / 标题 / 折叠摘要,也不做合并。
- 合并只保留"先到定形、后到补空白":第一次见到的快照决定卡片形状,后续快照只补输出与状态;不做逐字段优先级表。
- 条目 id 只有一套:归一到 `itemId`;前端与 Thread Manager 都不再出现第二个 id。
- 事件不带回合身份:`turn.started` 无载荷、`turn.completed{status}`;Thread Manager 的生命周期锚点、条目、增量与请求都不带 turn id。
- 思考正文流式下发不放宽可见范围:被下发的就是此前已在 `item.completed` 展示、并已落进 `project.jsonl` 的同一段文本。
- 失败与中止说明只在运行期显示(不写 `project.jsonl`),页面重进后不再出现。
- 未知 item 类型由 Rust 原样透传(只有类型与身份,Rust 侧留 TODO),当前由前端投影丢弃。
- 前端聊天卡片的工具形状是 `Omit<GameCreatorDirectToolCall, 'turnId'>`;`tool-calls.jsonl` 的持久化形状与 DirectRuntime 的写入保持不变。
- 「可显示」的判据取**前端回合反馈**:一次翻页操作连拉到「合并后聊天投影的回合数增加」为止。工具卡片与思考文本虽然能通过 `projectDirectThreadItem`,但可能整页落进已渲染回合的折叠「执行过程」,不构成用户可见反馈;口径只在 `directHistoryPaging.ts` 里实现一份,首屏与「显示更早」共用。
- 首屏切片的**新端边界**只认 `subscribe` 回执里的 `lastCompletedItemId`(含该条):回执到达之前不读首屏,也不退化成「取文件尾」;锚点缺失(订阅不可用 / 失败 / 历史为空)时才按文件尾取尾屏,`/history` 手动重读保持按当前文件尾取尾屏的恢复语义。
## 依赖与前置条件
- Thread Manager 深模块、app-server 事件适配与 Tauri 桥接已存在。
- 主规范中的生命周期锚点、事件自足与首屏锚点条款已生效。
- id 空间已用源码核对:codex-rs `app-server-protocol/src/protocol/thread_history.rs` 中所有工具 item 都是 `id: payload.call_id.clone()`,而 `project.jsonl` 落盘的是原始 response item(`id` 与 `call_id` 不同)。真实 app-server 会话核对仍作为运行时验收项。
## 验收标准
- [x] 一次回合内,实时渲染的文本段与工具卡片顺序,与回合结束后重进项目看到的顺序一致。【自动化:`project-development.suite.ts` 空对话首轮 + 历史切片工具卡片用例】
- [ ] 回合进行中终止并重启进程后重进项目:已落盘的部分文本与工具卡片按原顺序出现,且界面不显示忙碌态。【待真实 app-server 运行时验收】
- [ ] 进程存活期间的页面重进(含切走再切回)能恢复运行中回合,并允许终止。【待真实 app-server 运行时验收】
- [x] 历史切片从文件尾反向回扫,锚点始终是文件里的原始 item id;切片内全部是不可显示条目时仍能继续向前,不出现锚点停滞。【自动化:`direct_project_history` 尾部回扫与分页锚点用例】
- [x] 首屏切片的新端边界是 `subscribe` 回执里的 `lastCompletedItemId`(含该条):比它更新的条目只从运行态事件来;锚点缺失时按文件尾取尾屏,订阅回执到达前不发首屏请求。【自动化:`cargo test agent::direct_project_history` 22 条(含 `through_item_id` 锚点用例)+ `directHistoryAnchorGate.test.ts` 10 条 + `project-development.suite.ts` 的 `anchors the first history page at the subscribe receipt instead of the file tail`(变异验证见下)】
- [x] 前端一次翻页操作自动连拉,直到合并后聊天投影出现新回合或 `hasMore=false`,每个操作上限 5 页;锚点不前进时立即停止,不空转。【自动化:`directHistoryPaging.test.ts` 8 条 + `project-development.suite.ts` 跨页同回合用例(变异验证见下)】
- [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` 用例】
## 证据要求
- 自动化(已跑):`cargo test direct_thread`(25 条)、`cargo test direct_project_history`(20 条)、`cargo test export_bindings`(生成绑定与工作区无差异)、`NODE_OPTIONS=--localstorage-file=… npx vitest run apps/ai-game-creator-shell/tests`(125 files / 1676 passed / 17 skipped)、`npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit`(exit 0)、ESLint 与 `prettier`。
- 自动化(分页连拉,已跑):`npx vitest run apps/ai-game-creator-shell/tests/directHistoryPaging.test.ts`(8 passed)、`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts`(470 tests / 453 passed / 17 skipped,含新增的 `keeps pulling earlier pages while the page only deepens the rendered turn`)、`npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit`(exit 0)。变异验证:把停止判据退化成「这一页有可渲染条目就停」后,跨页用例以 `Unable to find an element with the text: 更早的用户提问` 变红,即用户报的「点了没变化」。
- 自动化(首屏锚点,已跑):`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml agent::direct_project_history`(22 passed)、`npx vitest run apps/ai-game-creator-shell/tests/directHistoryAnchorGate.test.ts`(10 passed)、`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts`(475 tests / 457 passed / 17 skipped,含新增的 `anchors the first history page at the subscribe receipt instead of the file tail`)、`npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit`(exit 0)、ESLint / prettier / `npm run check:encoding` / `npm run check:doc-index` / `git diff --check`。同一次全量里唯一失败是既有的 `edits the published runtime config without leaking API keys into chat`(`appSurface/runtime-settings.suite.ts`),与本次改动无关:把本次前端改动 stash 掉后它同样变红。变异验证:① 锚点闸门忽略「已消费」后,`directHistoryAnchorGate.test.ts` 的「同项目已消费返回 null」变红;② 首屏不等闸门后,`anchors the first history page at the subscribe receipt instead of the file tail` 以 `throughItemId` 缺失变红。
- 运行时(待补):真实 app-server 会话下的新回合、杀进程重开、页面重进、分页与终止。
- 边界:订阅过期、回执竞态、不可显示切片、无 `turn.completed` 的残回合、工具输出超长截断与脱敏。
- 环境注意:`rehype-highlight` 已装齐后 `ChatMarkdownMessage` / `AgentMessageContent` 转绿;Node 26 下 vitest 的 jsdom 用例需要 `--localstorage-file` 才能拿到 `window.localStorage`(`clientApi.test.ts` / `chatPromptPolish.test.tsx`),已记入 `docs/project-memory/shared-memory/pitfalls.md`。
@@ -0,0 +1,42 @@
# Mac 客户端随包运行依赖补齐
- Version: 2
- Status: awaiting-windows-acceptance
- Date: 2026-09-18
- Parent Spec: `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` / Runtime 边界 / 安装包侧车
## 目标与范围
产出可交给用户本地测试的 Apple Silicon DMG;Codex、平台辅助组件、插件入口和面板来自安装包,不依赖仓库或全局 Codex。保持 Windows 现有路径、版本、回退和安全语义。
## 非目标与依赖
不修改 Agent Loop、公开 API、持久化数据、版本号或更新签名;远程推送与草稿 PR 已获用户确认,不发布安装包、不自动合并。Node/Cocos Creator/账号仍使用现有外部前提。Cocos macOS 原生桥接不是文件打包修复,不计为本里程碑完成能力;Intel 实机构建与 universal 不在本次交付验收内。
## 验收标准
1. 构建目标与锁定原生包匹配,缺组件或元数据错误失败关闭。
2. `.app` 内包含全部原生组件、SHA-256 清单、声明及插件清单/入口/面板;Unix 可执行位保留,不含 Windows DLL、私有配置与构建缓存。
3. 运行时先选随包文件,验证完整性和版本;覆盖缺失、篡改、平台不匹配及 macOS Resources 路径解析。
4. 脱离仓库且限制 PATH、隔离 HOME 后,真实内置 Codex app-server 完成初始化握手;不能借全局 Codex 掩盖缺包。
5. 定向 Rust 测试、配置/类型门禁、编码、文档索引和 diff 检查通过,DMG 校验通过;用户本地 GUI/登录/对话测试另行验收。
## 评审结论
2026-09-18 编码前自审:平台资源与运行时查找必须一起改;共享文件白名单避免双份清单漂移;不会复制 Windows 专属组件到 Mac;安装包结构证据和真实交互证据分开。以上范围可实施,不推进 Cocos 原生桥接下一里程碑。
## 验收现状与剩余门禁
### 评审反馈修订合同
2026-09-18 编码前自审:本轮仅修复同里程碑的目标平台传递、Cocos 能力门禁和文档冲突,不新增原生桥接或发布管线。CLI 显式目标必须覆盖环境默认,并在版本、构建、产物和清单全链路保持一致;不支持平台或渠道错配应在副作用前失败。Cocos 必须由已注册适配器决定可见/启动,覆盖无适配器、有适配器及非 Cocos 项目;前端不盲目启动隐藏插件。更新权威文档改为单架构策略,拒绝 universal,且不宣称现有发布脚本支持跨构建合并两种架构。新增回归通过后仍等待 Windows 验收;本地 0.1.67 版本修改保留,不与旧 0.1.47 安装包证据混淆。
评审修复验证:发布/feature/上传脚本测试 32 项通过,前端插件自动启动 5 项通过,Rust PluginHost 11 项与内置插件 10 项通过;Rust 使用 `TMPDIR=/private/tmp` 避免 macOS `/var` 系统链接触发既有路径安全断言。类型/配置、定向 ESLint、编码、文档索引和 diff 检查通过。测试覆盖显式目标覆盖环境、错误渠道提前拒绝、Tauri 实际注入配置、真实临时清单写入、Windows 默认 feature 保留、无 adapter 隐藏/启动拒绝、有 adapter RPC 回归。上述是自动化证据,不代表 Windows 真机、GUI 或带本次修复的新安装包已验收。
重打验证:重新 fetch/merge `origin/master` 确认当前分支已包含最新 master;按用户要求将 package、Tauri、Cargo 与锁文件版本同步到 `0.1.67`。包含上述修复的 Release `.app` 构建通过,Info.plist 实测版本 `0.1.67`、最低系统 `15.0`;隔离安装包脚本再次通过,DMG 用 hdiutil 生成并校验通过。此版本仍等待用户 GUI 与 Windows 回归,不做正式签名、公证、更新签名或产物上传。
- Mac 本地测试包已由用户确认“可以用了”;不外推为全部对话、工具和其它机器兼容性已覆盖。
- 定向 Rust 验证 14 项通过,1 项真实认证用例按原配置跳过;发布脚本测试 21 项通过;隔离 HOME/PATH 的安装包资源检查、正式 Codex 查找、app-server 握手及缺组件拒绝通过。
- 类型与配置、编码、文档索引、定向脚本 lint 和 diff 检查通过;139 MiB DMG 完整性通过。
- 随包 zsh 的原生最低系统为 macOS 15.0;macOS Tauri 最低版本声明随之对齐,不能仅按主程序的 macOS 11.0 下限宣称整包兼容性。
- Windows NSIS 构建、安装、随包 Codex 与 Cocos 原有能力回归尚待用户测试;其它 Mac、Intel、正式签名/公证和更新签名未验收。保持草稿,不推进合并或发布。
@@ -1,29 +1,32 @@
# 对话回合唯一投影
- Version: 2
- Status: implemented-awaiting-runtime-acceptance
- Version: 3
- Status: superseded
- Date: 2026-09-16
- Superseded by: `../【里程碑】DirectProject聊天真相源收敛-2026-09-16.md`
- Parent Spec: ../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md
## 范围与评审
## 结论
单里程碑修复回合展示和流写入的一致性;补充终态重进恢复、用户发送时间和完成后的过程折叠。评审确认:活动快照及 Direct 事件拥有生命周期,Provider 回放不创建 client 回合;JSONL 信封可选时间字段不污染原始 item,无须数据库或旧数据迁移;最终回复沿用 Runtime 的最后 assistant item 合同,失败提示不折叠。没有身份的旧记录不得做位置猜配。
本里程碑原先把「回合唯一投影」落在 Direct 回合事件 + `turn-stream.jsonl` + `tool-calls.jsonl` + 活动回合快照这条读路径上,方向已被推翻:聊天视图的输入只剩**项目对话历史**与**运行态事件**两项,Direct 回合事件、`turn-stream.jsonl`、`tool-calls.jsonl`、活动回合快照都不再是聊天视图的输入。后续实现与验收一律以 `../【里程碑】DirectProject聊天真相源收敛-2026-09-16.md` 与 `docs/adr/【ADR】DirectProject对话历史单一事实源-2026-09-16.md` 为准。
## 验收
## 仍然有效的部分
1. 一个 turn 只有一个呈现入口,用户消息不丢失。
2. item 增量、完成、持久化和回读保持相同身份与固定顺序。
3. 工具输入输出保留,重复快照不重复渲染。
4. TypeScript、最小 Cargo 检查、编码和 diff 检查通过;用户要求不运行测试,实机新回合/重开/分页验收待确认。
5. 已结束回合重进不显示提交中,真实运行回合可恢复;跨项目/新回合迟到快照无效。
6. 用户消息时间可刷新恢复,旧无时间记录不造值;完成后中间正文和工具统一折叠,最终回复及失败提示保持可见。
1. 一个回合只有一个呈现入口,用户消息不丢失;历史里带不了身份(没有 id)的旧记录不得做位置猜配。
2. 同一 item 的增量、完成、持久化与回读使用同一个身份、同一个顺序;重复快照不重复渲染。
3. 工具调用的输入与输出都保留,展开后仍显示「输入 / 输出」。
4. 已结束回合重进不显示提交中;真实运行回合可恢复。
5. 用户消息时间只来自条目自己的时间戳,旧无时间记录不造值;完成后中间正文与工具统一折叠,最终回复与失败提示保持可见。
6. 失败与中止说明只在运行期显示,不写进 `project.jsonl`;页面重进后不再出现。
依赖:既有项目历史和 v1 turn-stream / tool-calls DTO。未完成真实 UI 验收前不进入其它里程碑。
## 已作废的部分
- 由活动回合快照接管运行中回合:改为由 `subscribe` bootstrap 事件判定(事件序列里 `turn.started` 之后没有 `turn.completed` 即运行中)。
- 按 `turnId` 归并回合并把工具卡片挂在回合上:事件与条目都不再带回合身份,前端只有一个 `turnRunning` 布尔,卡片形状去掉 `turnId`。
- 用 `turn-stream.jsonl` 的 `seq` 决定文本与工具的交替顺序:改为按运行态事件顺序 + 历史文件顺序投影。
- 前端订阅 `game-creator-direct-turn-update`:改为 `subscribe` + `notify` 唤醒 `consume`(bootstrap 的事件直接 reduce,不再补一次 `consume`)。
## 当前证据
- 定向 TypeScript 类型检查、`cargo check --locked --bin genarrative-ai-game-creator-shell`、编码检查、文档索引检查、`git diff --check` 通过。
- 已补充回合归属、分页、重复快照、无流回退及 writer 完成/切段、持久快照单调性/跨回合裁剪用例;按用户要求未执行测试,不能作为已通过凭证。
- 静态自审确认视图只剩统一回合列表,不再存在 mapped/unmapped/live 三个回合流出口;失败提示使用稳定 failure 身份。
- 真实新回合、历史重开、分页、失败/中断、工具展开输入输出仍待重启原生客户端后验收;仅本地提交,不推送。
- 本次增量已完成生命周期来源收敛、信封发送时间和完成过程折叠;定向 TypeScript、ESLint、Cargo check、文档索引通过。新增时间幂等/旧记录、终态分类及内容分区用例但未运行;主页进入项目、重新发送/切项目竞态和自动折叠仍待原生实机验收,仅本地提交,不推送。
- 里程碑版本的静态检查结论(TypeScript、Cargo、编码、文档索引、`git diff --check`)仍然成立;该增量按当时授权未运行测试。
- 新路径的证据要求见 `../【里程碑】DirectProject聊天真相源收敛-2026-09-16.md` 的「验收标准」与「证据要求」,其中包含解锁此前跳过的工具卡片渲染用例。