合并 origin/master 到 AGC 渲染层下沉分支并完成三处对齐
- 活动回合事实源统一到 Direct 线程管理器:删除 direct_runtime 的第二份快照,接单、进度内容变化与收口各广播一次活动回合变更事件
- 平台维护态判定移入 Rust 并在渲染层只订阅单一事件:新增 platform_maintenance 模块与各平台 facade 错误分支的分类入口
- 封面生成请求补 generationInputs.source,保持队列回填后仍能拿到平台素材 ID
- 渲染层按 master 5398a53e6 退役诊断详情入口:删除 agentRuntimeErrorDetail 与「查看详情」交互及其专属用例
- 冲突收口:nginx SPA 白名单、.gitignore、mobile 检查脚本、capabilities 描述取上游,两个已退役计划随上游删除,文档保留双方条目
- 新增并回写本里程碑取证、decision-log 与 pitfalls 的 2026-09-28 记录
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# AGC 模型目录上游同步实施计划
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | in-progress |
|
||||
| Date | 2026-09-24 |
|
||||
| Parent Milestone | `docs/project-memory/plans/【里程碑】AGC模型目录上游同步-2026-09-24.md` |
|
||||
|
||||
## 修改边界与顺序
|
||||
|
||||
1. **领域模型(`module-runtime/src/agc_models.rs`)**:删除写死的 `Default` 实现(原 `quality → gpt-6-astra`、`fast → gpt-5.6-luna`),新增 `from_upstream_models`:按上游模型名排序去重后生成目录项(`modelId`/`alias` = 上游原名,`id` = 模型名 slug,`enabled = true`),默认项取排序后第一项;新增 `resolve_requested`(未选或 `platform-default` 用默认项)。字段、校验规则(32 项上限、id/alias/model_id 约束)与 `resolve` 保持原样。
|
||||
2. **procedure(`spacetime-module/src/agc_models.rs`)**:`read_agc_model_catalog` 缺行返回 `AGC_MODEL_CATALOG_NOT_INITIALIZED`,不再返回内置目录;`save_agc_model_catalog` 不变。无表结构变化,不改 `migration.rs`。
|
||||
3. **api-server 目录模块(`src/agc_models.rs`)**:新增启动期 `ensure_agc_model_catalog_initialized`(读 → 解析/校验 → 缺行或非法则 `GET {控制面}/api/pricing?group=taonier` → 生成目录 → 按存量 revision 写回;冲突后重读确认可用);上游请求 10s 超时、1 MiB 流式上限、禁止重定向、不带凭据;未初始化统一 `503` 文案;后台 PUT 增加未初始化门禁。
|
||||
4. **api-server 接线(`src/main.rs`、`src/external_api_keys.rs`)**:`try_restore_app_state_for_startup` 按 HTTP 角色调用初始化,失败只 `error!` 记录;抽出 `ensure_llm_router_url_allowed`(只校验地址/scheme,避免被已下线的固定模型哨兵挡住),`LLM_ROUTER_TOKEN_GROUP` / `router_control_origin` 供同步复用。
|
||||
5. **客户端与后台**:不改。`GET /api/llm/models` 形状、admin DTO、后台「AGC 模型」页、客户端 `select_game_creator_model` 校验全部保持原样。
|
||||
|
||||
## 不改的部分
|
||||
|
||||
目录字段语义、后台 DTO 与页面、公开 DTO 形状、客户端模型标识校验、`/api/external/v1` 与 OpenAPI、SpacetimeDB 表结构、Router provisioning/额度。
|
||||
|
||||
## 验证命令
|
||||
|
||||
- `cargo test --locked -p module-runtime --lib agc_models::`
|
||||
- `cargo test --locked -p api-server --bin api-server agc`、`... llm::`
|
||||
- `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bins configuration::`
|
||||
- `cargo fmt --all -- --check`(两套 workspace)、`npx vitest run apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx`、admin-web 页面定向 Vitest 与 typecheck
|
||||
- `npm run check:encoding`、`npm run check:doc-index`、`npm run check:spacetime-schema`、`git diff --check`
|
||||
- 运行时 smoke:本地 dev 栈清空 `agc_model_catalog` 后启动 api-server,确认日志 `已按上游模型列表初始化 AGC 模型目录`、库中 `catalog_json` 为「slug id + 上游原名 alias/modelId」、`GET /api/llm/models` 返回原名;再把上游地址指向不可达端口验证 `503` 与「无替代目录」。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- **上游端点与鉴权**:分组定价列表端点为实测确认的公开只读接口;若上游改版,同步失败只会让目录保持未初始化(接口 503 + 启动 error),不会写入错误模型。
|
||||
- **混合版本**:module 的缺行语义变化要求 module 与 api-server 同批发布/回滚;未升级的 api-server 会把自己的 AGC 接口打到 `503`(后台 DTO 未变,admin-web 可独立发布)。回滚点必须同时覆盖 module 与 api-server。
|
||||
- **存量目录**:结构合法的旧目录(含 `quality/fast`)不会自动重建,需要 owner 在后台修改或清空该行后重启。
|
||||
- **目录规模**:目录项上限仍是 32;上游在售模型超过 32 条时同步会失败并记录原因,需要 owner 在后台维护子集。
|
||||
@@ -3,7 +3,7 @@
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】AGC渲染层下沉与最新master对齐-2026-09-28.md` |
|
||||
| Status | ready |
|
||||
| Status | implemented-awaiting-runtime-acceptance |
|
||||
| Owner | Codex |
|
||||
|
||||
## 一句话交付结果
|
||||
@@ -34,3 +34,13 @@
|
||||
|
||||
本里程碑验收通过后,把仍然有效的长期边界同步回现行技术方案与项目记忆,再删除本里程碑与实施计划两份
|
||||
临时文档。
|
||||
|
||||
## 本轮取证(2026-09-28)
|
||||
|
||||
五步任务全部落地,逐条证据与「与 master 既定决策的分歧」见同名里程碑的《本轮取证》一节,这里只记范围事实:
|
||||
|
||||
1. 冲突收口:29 处冲突全部解决,机械取舍按「本分支退役渲染层传输」定向(nginx SPA 白名单、`.gitignore`、mobile 检查脚本、capabilities 描述、两个已退役计划随 master 删除、测试保留双方)。
|
||||
2. 活动回合事实源统一:删除 `direct_runtime` 的第二份快照与旧注册表的通知位点,改由 Direct 线程管理器三处变更点广播;Rust 定向用例 17 passed。
|
||||
3. 封面生成补 `generationInputs`:`tests/gameDistributionPublish.test.ts` 7 passed。
|
||||
4. 维护态:Rust 统一分类 + 单一事件,渲染层只订阅;Rust 1 passed、Web 2 passed。
|
||||
5. 文档收口:`decision-log` 与 `pitfalls` 各追加一条 2026-09-28 记录;两份冲突文档保留双方条目。
|
||||
@@ -34,6 +34,12 @@ Parent Milestone: `【里程碑】AGC统一错误诊断与验收反馈-2026-09-1
|
||||
- 若素材身份无法映射,继续失败关闭并记录明确 code,不回退为路径字符串通过。
|
||||
- 回滚可删除新事件写入和详情入口,保留旧 `failure.json` 读取兼容。
|
||||
|
||||
## 2026-09-23 维护态错误展示补充
|
||||
|
||||
- `apps/ai-game-creator-shell/src/services/clientApi.ts` 解析并保留维护响应的 `error.code`、HTTP 状态与 `meta.requestId`,识别网关 `MAINTENANCE` 后广播客户端维护事件。
|
||||
- `apps/ai-game-creator-shell/src/components/modal/MaintenanceNotice.tsx` 在客户端根部统一展示不可被局部业务兜底替代的大弹窗;发布、上传、资源换签等共用 `requestClientApi` 的请求均进入同一出口。
|
||||
- 普通 500、资源损坏和本地 `blob:` 图片预览失败不自动归类为维护;图片换签接口在维护期间失败时会触发统一弹窗,但本地刚选中的图片预览仍不依赖后端。
|
||||
- 验收补充:维护期间发布接口不能只显示“创建平台游戏失败”等局部文案;维护弹窗出现一次即可覆盖并发失败请求,关闭后业务页仍可重试。
|
||||
## 当前进度(2026-09-24)
|
||||
|
||||
- 已落地并验证:统一错误事件模型与脱敏(`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --offline -- --test-threads=1 runtime_error` → 7 passed)、DirectProject 失败安全投影与项目内 sidecar、下一轮诊断上下文、app-server 终态与超时分类、试玩 attempt 上限。
|
||||
@@ -49,3 +55,9 @@ Parent Milestone: `【里程碑】AGC统一错误诊断与验收反馈-2026-09-1
|
||||
- 钉住这条交互的用例:`tests/appSurface/chat-composer.suite.ts` 的「失败提示保留可执行原因,诊断正文只在「查看详情」时读取」(断言点击前没有 `read_agent_runtime_error_detail` 调用、正文不在提示或历史消息里,点击后按 `projectPath + detailRef` 读取并展示,再点收起)。
|
||||
- 自动化:`npx vitest run tests/agentRuntimeModel.test.ts tests/agentRuntimeErrorDetail.test.ts`(23 passed)、`npx vitest run tests/appSurface.test.ts`(191 passed / 9 skipped)、`npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit`、`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --offline -- --test-threads=1 runtime_error`(7 passed)。
|
||||
- 仍未验证(需要真实账号与 Provider):真实失败回合下从提示进入详情、以及下一轮追问从历史读取失败证据的端到端表现。
|
||||
|
||||
## 2026-09-28 上游取代:诊断详情入口按 master 收口
|
||||
|
||||
合并 `origin/master` 时发现上游提交 `5398a53e6 宿主与前端:用户可见文案不再带诊断引用,失败也进错误上报池` 已经明确退役本计划第 6 条的「查看详情」交互:失败文案不再带 `;详情:…` 引用,`read_agent_runtime_error_detail` 命令与渲染层 `agentRuntimeErrorDetail.ts` 一并删除,失败线索只留在 `.agent/runtime/errors`、应用日志与错误上报池。
|
||||
|
||||
本里程碑接受上游口径,主题的其余验收(统一错误事件、失败安全投影、下一轮诊断上下文、终态分类、试玩 attempt 上限、v1/v2 文案映射)保持不变;本次对齐删除了该交互的专属实现与用例,失败说明继续由 `turn.completed.failure` 事件投影成同一份安全文案。结论同时记入 `decision-log` 2026-09-28 条目。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 【实施计划】External v1 游戏场景生成路由
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】ExternalV1游戏场景生成路由-2026-09-24.md` |
|
||||
| Status | ready |
|
||||
| Owner | Agent |
|
||||
|
||||
## 修改边界
|
||||
|
||||
- 允许修改:
|
||||
- `server-rs/crates/api-server/src/editor_project.rs`(抽取场景组装/入队共享逻辑)
|
||||
- `server-rs/crates/api-server/src/external_editor_api.rs`(新 handler)
|
||||
- `server-rs/crates/api-server/src/modules/external_api.rs`(路由表与契约矩阵)
|
||||
- `docs/openapi/genarrative-external-v1.openapi.json` 与对应契约测试
|
||||
- AGC `src-tauri/src/agent/generation/canvas_generation.rs`、`src/agent/direct_runtime/mod.rs`、`src/agent/generation/external_generation_state.rs`、`src/agent/runtime_driver/recovery_scan.rs`、`src/project/manifest.rs`(背景阶段路由与身份口径)
|
||||
- `.codex/skills/genarrative-external-editor-api/references/`(外部 API 说明)
|
||||
- 明确不修改:站内场景生成规则、通用图片入口校验、SpacetimeDB schema、计费、动画/抠图链路(issue #495);站内入口补齐现有 AGC 账号模式的幂等与结果协议。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 后端:把 `generate_editor_scene` 的「payload → `EditorImageGenerationRequest`」段抽成 `pub(crate)` 共享函数(含 options 标准化、Prompt 组装、generationInputs 重建、assetLabel 规范化),站内 handler 改为调用它。
|
||||
2. 后端:`external_editor_api.rs` 新增 `generate_external_editor_scene`:`require_scope(SCOPE_EDITOR_IMAGE_GENERATE)` + `require_idempotency_key` + 共享函数 + `enqueue_editor_image_generation_for_owner` + `external_generation_accepted_response`。
|
||||
3. 后端:`modules/external_api.rs` 路由表与 `PROTECTED_ROUTES` 各加 `/api/external/v1/editor/scenes/generations`(POST)。
|
||||
4. 后端:补单测/契约测试(鉴权、参数 400、幂等重放、与站内同 Prompt);同步 OpenAPI JSON 与契约测试。
|
||||
5. 客户端:`canvas_generation.rs` 请求构造为 Scene 拆专属分支——新路由 + `EditorSceneGenerateRequest` 形状 body(`sceneContent` = 现有场景 prompt 文本,`stylePreset = "custom"`,`customStyle` = 现有风格描述文案,其余字段沿用);修 Scene 保留账本匹配器(新路由 + `sceneContent` 口径),统一 `game-background`/`scene` 不一致。
|
||||
6. 客户端:身份/对账常量迁移——`direct_runtime/mod.rs` 背景身份三元组路由与 kind、`manifest.rs` 期望路由、`recovery_scan.rs`、`external_generation_state.rs`;保持旧路由登记可读。
|
||||
7. 文档:外部 API Skill references 补新路由;运行编码与 diff 检查。
|
||||
8. 审查修复:站内场景 handler 读取并透传可选幂等键;共享组装只保留精确 AGC 来源标记。增加非法键路由测试与场景 payload 到队列可下载结果的回归测试,同步 OpenAPI 元数据说明。
|
||||
|
||||
## 验证命令
|
||||
|
||||
1. `cargo test -p api-server`(重点:external_editor_api、editor_project 场景相关)
|
||||
2. `cargo test -p shared-contracts`
|
||||
3. AGC:`cargo test` 于 `apps/ai-game-creator-shell/src-tauri`(定向:canvas_generation、direct_runtime、recovery_scan、manifest)
|
||||
4. `npm run check:encoding`、`git diff --check`、`npm run check:doc-index`
|
||||
5. smoke:`npm run dev:api-server` 后 `curl /healthz`,再用测试 Key POST 新路由验证受理与重放。
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- 共享逻辑抽取改变站内路由行为:以「相同输入 Prompt 逐字一致」单测为门禁;回滚点为抽取提交本身。
|
||||
- 旧路由登记的身份记录在新常量下读不到:保留旧路由 + `spec` 的读取分支,仅新增写入走新路由;回滚点为常量迁移提交。
|
||||
- 幂等重放依赖现有外部生成重放链路(issue #495 问题一不涉及图片生成入口,实测正常);若 smoke 暴露重放异常,停止合并并记录。
|
||||
@@ -0,0 +1,35 @@
|
||||
# 【实施计划】UI编辑器多树预览与独立显示开关
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】UI编辑器多树预览与独立显示开关-2026-09-14.md` |
|
||||
| Status | ready |
|
||||
| Owner | Codex |
|
||||
|
||||
## 修改边界
|
||||
|
||||
- 允许修改:AGC UI editor Rust Node/UITree DTO、前端 UI editor state/session、preview 组件、相关测试与当前专题文档。
|
||||
- 明确不修改:SpacetimeDB、External v1、代码生成 runtime 语义、用户 `.env`。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
1. 更新 Rust Node 与生成的 TS 类型;补齐所有 Node 构造器。
|
||||
2. 在状态层集中实现 createTree 与 root offset 更新。
|
||||
3. 将 session canvas projection 改为全树投影并提供按 treeId 的节点/根操作。
|
||||
4. 重构 preview 为联合画布、多树 wrapper、独立三个显示开关和树级拖动。
|
||||
5. 修正左侧选择与 Inspector 的 tree 反查,不让预览选择修改 activeImageId。
|
||||
6. 增加/更新定向测试与文档。
|
||||
|
||||
## 验证命令
|
||||
|
||||
1. `npm --prefix apps/ai-game-creator-shell test -- --run`
|
||||
2. `npm --prefix apps/ai-game-creator-shell run typecheck`
|
||||
3. `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`
|
||||
4. `npm run check:encoding`
|
||||
5. `git diff --check`
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- Node DTO 是生成绑定,Rust 与 TS 不一致会阻断编译;每次结构变更后立即生成并检查 diff。
|
||||
- 预览选择与 activeImageId 解耦可能暴露 Inspector 当前依赖;通过 treeId 反查补齐。
|
||||
- root 拖动需与画布平移和子节点拖动区分;若命中冲突,优先保留现有子节点手势。
|
||||
@@ -121,6 +121,12 @@
|
||||
|
||||
- 发布灰度改为**默认关闭**并修掉客户端“看得到点不动”:`is_game_distribution_publish_enabled_for_user` 现在要求 gate 行存在且 `enabled=true`(未登录、无行、`enabled=false` 一律 false),因此没配灰度时 `gameDistributionPublishEnabled=false`,AGC 不再渲染「发布到游戏广场」按钮、网页入口也不出现;AGC 侧新增 `announcePublishMessage`,把「已构建并打包试玩包」「先打开一个项目再发布」等提示通过 DirectProject 聊天容器的 `announce` 出口回话(普通项目不渲染工作台状态行,之前只写 workspaceStatus 才会表现为点击无反应)。后台「灰度发布配置」新增「可配置开关」列表:预设开关在未创建行时也可见并可一键配置(不再需要先猜 gate key)。
|
||||
|
||||
## 2026-09-23 口径更新:发行入口改为服务端派生
|
||||
|
||||
- 管理员不再填写 `entryUrl`:审核通过时 `api-server` 读版本取 gameId,按部署模板 `GENARRATIVE_GAME_DISTRIBUTION_RELEASE_ENTRY_TEMPLATE`(生产形如 `https://{gameId}.games.<发行域名>/`,必须含 `{gameId}` 占位符)派生每游戏独立来源地址,再走原有 HTTPS / 无凭据 / 无 query / 无 fragment 校验;后台审核 DTO 与页面已删除该输入框。
|
||||
- 上文「已完成证据」中描述「管理员填写 / 要求 HTTPS 发行入口」的条目是当时的交付事实,当前口径以主规范《平台入口与玩法链路》《本地开发验证与生产运维》与 `shared-memory/decision-log.md` 的 2026-09-23 条目为准。
|
||||
- 非生产环境未配置模板时仍回落到本地发行网关回环地址(用于免 TLS 验证内嵌游玩);生产未配置模板、模板缺 `{gameId}`、gameId 非主机安全字符或派生结果非法时,审核通过直接失败。
|
||||
|
||||
## 尚未完成
|
||||
|
||||
- 真实独立发行域名、通配 TLS 与 CDN 仍属部署侧:边缘模板与门禁已就绪,本地已用真实 nginx 验证按主机映射、Cookie 403 与命名空间隔离,但仍需在真实域名/证书下跑一次“审核通过 → 游玩 → 换版 → 下架”并确认 CDN TTL 不超过 60 秒窗口。
|
||||
|
||||
@@ -45,3 +45,7 @@ AGC 发布面板完成三项收敛:
|
||||
- 不做网页发布页的自动生成。
|
||||
- 不新增发布草稿持久化、生成历史、重试队列或 SpacetimeDB 表。
|
||||
- 不改变封面上传入口和手动选择封面的能力。
|
||||
|
||||
## 6. 本次缺陷修复记录
|
||||
|
||||
- 2026-09-23:AGC 生成游戏封面请求补充 `generationInputs.source = "ai-game-creator-client"`。队列 worker 依据该来源选择 `GameCreatorResourceEditor` 结果契约;未标记来源时会按 `Standard` 紧凑化并省略 `result`,导致生成完成后无法回传 `assetObjectId`。对应前端定向测试已锁定请求字段与平台素材 ID 回填链路。
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# 【实施计划】退役策划 Agent V1/V2 解耦清理
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Milestone | `docs/project-memory/plans/【里程碑】退役策划Agent V1V2解耦清理-2026-09-15.md` |
|
||||
| Status | implemented-awaiting-runtime-acceptance |
|
||||
| Owner | Codex |
|
||||
|
||||
## 一句话交付结果
|
||||
|
||||
删除退役策划 V1/V2 及其耦合的 Supervisor 产品残留,让当前 Design Agent 独立运行、做游戏走 DirectCodex 并恢复仓库编译;保留通用 Supervisor 和做游戏 16 Agent DAG。
|
||||
|
||||
## 验收判据
|
||||
|
||||
当前分支能够通过 AGC 前端 typecheck 和受影响定向测试;Design Agent 的新建、恢复、澄清、阶段审批和 reasoning 展示仍由当前 Design Agent 链路完成;旧 V2 IPC、旧 GDD 类型和旧前端测试契约不再存在。
|
||||
|
||||
## 修改边界
|
||||
|
||||
允许修改:
|
||||
|
||||
- `apps/ai-game-creator-shell/src/App.tsx` 中旧 V2 状态、helper、IPC 分支和旧 UI props。
|
||||
- `apps/ai-game-creator-shell/src/app/types.ts` 中旧 GDD 类型。
|
||||
- `apps/ai-game-creator-shell/src/features/project-workspace/planningSessionV2.ts` 及其直接调用方。
|
||||
- `apps/ai-game-creator-shell/tests/appSurface/harness.ts`、`home.suite.ts`、旧策划事件测试。
|
||||
- `apps/ai-game-creator-shell/src/styles.css` 中只属于旧 Plan GDD / planning lane 的样式。
|
||||
- 没有现役调用方的旧策划身份 fixture、注释和文档索引。
|
||||
- 为通过编译所需的最小共享残留删除或改名。
|
||||
|
||||
明确不修改:
|
||||
|
||||
- `directCodex` 主链路和当前 Design Agent。
|
||||
- 16 Agent DAG、`supervisor-swarm` 测试和通用 Runtime 编排;只处理删除策划耦合后直接造成的编译错误。
|
||||
- 当前 Design Agent Rust runtime、Design Agent 资源包和 Design Agent IPC 协议。
|
||||
- 公开 API、SpacetimeDB schema、迁移和历史持久化数据格式。
|
||||
|
||||
## 实现顺序与提交拆分
|
||||
|
||||
### 提交一:解耦 Design Agent 状态命名
|
||||
|
||||
- 将新版 Design Agent 实际使用的 `planningV2*` transient reply、reasoning、active ref 和相关 lane 控制改成 Design Agent 专属状态。
|
||||
- 保持行为不变,不删除旧 V2 会话代码。
|
||||
- 验证:AGC typecheck、`git diff --check`。
|
||||
|
||||
### 提交二:删除 App 旧 V2 会话控制流
|
||||
|
||||
- 删除仅服务旧策划的 Supervisor 产品入口、恢复、轮询和聊天提交分支。
|
||||
- 删除旧 V2 session/GDD 状态和 helper。
|
||||
- 删除项目打开、消息发送、问询回答、审批和旧流式事件分支。
|
||||
- 将“做方案”只连接到当前 Design Agent hydrate/continue/decide 路径,将“做游戏/做素材”只连接到 DirectCodex。
|
||||
- 保留 `planningStartMode` 作为入口路由字段,避免无关扩大重命名。
|
||||
- 验证:AGC typecheck;必要时运行 App 启动相关定向 suite。
|
||||
|
||||
### 提交三:删除旧 TypeScript 适配层和类型
|
||||
|
||||
- 删除 `planningSessionV2.ts`。
|
||||
- 删除 `PlanGddDecisionAction`、`PlanGddStateViewV1` 及所有直接导入。
|
||||
- 重新执行旧符号检索,确认没有残留调用方。
|
||||
- 验证:AGC typecheck、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
### 提交四:清理测试、事件契约和 CSS
|
||||
|
||||
- 精确删除 harness 中旧 V2/GDD 工厂、mock、调用记录和导出。
|
||||
- 精确删除首页 suite 中旧 V2 IPC 断言。
|
||||
- 删除旧 `planning-session-v2-stream` 事件测试。
|
||||
- 删除 Plan GDD、GDD 审批卡和 planning lane 专属 CSS 及过时说明。
|
||||
- 验证:appSurface 定向测试、事件订阅定向测试、AGC typecheck、编码和 diff 检查。
|
||||
|
||||
### 提交五:收口确定失效的身份残留和文档入口
|
||||
|
||||
- 只处理因 V1/V2 退役而确定失效的旧身份展示、测试 fixture、注释和文档索引。
|
||||
- 不扫描或重构做游戏 DAG;共享代码只在其旧策划用途已确定死且删除能直接解决编译/测试问题时处理。
|
||||
- 验证:旧策划符号定向检索、相关测试、编码和 diff 检查。
|
||||
|
||||
## 验证命令
|
||||
|
||||
1. `npm --prefix apps/ai-game-creator-shell run typecheck`
|
||||
2. `npm run check:encoding`
|
||||
3. `git diff --check`
|
||||
4. `npm --prefix apps/ai-game-creator-shell exec vitest run tests/appSurface.test.ts`
|
||||
5. 受影响 Rust 文件变化后运行 `cargo check --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`
|
||||
6. 完成全部提交后再次执行旧策划符号检索,并核对 `git status` 与提交边界
|
||||
|
||||
## 风险与回滚点
|
||||
|
||||
- 最大风险是新版 Design Agent 复用了旧变量名;必须先完成提交一,再删除旧 helper。
|
||||
- `ProjectSupervisorView` 已经移除旧 GDD props,App 传参残留会在提交二中一并删除。
|
||||
- 测试 harness 同时服务通用总控和 Design Agent,必须局部删除旧 mock,不能整段重写。
|
||||
- 若某次提交导致 Design Agent 测试失败,只回滚该独立提交,不恢复旧 V2 兼容层。
|
||||
|
||||
## 完成后的临时文档处理
|
||||
|
||||
全部里程碑验收通过后,删除本里程碑和实施计划两份临时文档;把仍然有效的长期边界同步回现行 Design Agent 技术方案和项目记忆,不保留阶段性提交步骤。
|
||||
|
||||
## 源码级验收(2026-09-24)
|
||||
|
||||
- `planningSessionV2.ts` 已不存在;`src` 内检索 `planning_[a-z0-9_]*_v2` / `planningV1` / `planningV2` 无命中,做方案入口只调用 `design_agent_session` / `continue_design_agent_session` / `hydrate_design_agent_session` 等当前 Design Agent IPC。
|
||||
- 旧 Plan GDD 类型与旧策划流工厂已清空:本轮删掉测试 harness 里无调用方、且引用了已不存在类型的 `createPlanGddStateView` 与 `planningResponseStream`(两者原本还导出给 suite);`PlanGdd` / `planning-session-v2-stream` 在 `src` 与 `tests` 已无命中。
|
||||
- 旧策划样式无残留:`plan-gdd` / `planning-lane` / `gdd-approval` 在 `src` 样式与组件内无命中。
|
||||
- Rust 侧同批退役见 decision-log 2026-09-21 条目:`planning_gdd_model.rs`、`planning_policy_v2.rs`、`planning_session_v2.rs`、`planning_*_v2` 命令与前端契约全部删除,不保留兼容别名或双跑路径。
|
||||
- 自动化:`npx vitest run tests/appSurface.test.ts tests/gamePublishFeedback.test.tsx` 195 passed / 9 skipped;`npm run ai-game-creator-shell:check:web` 185 files passed / 1 skipped、1806 tests passed;`npm run check:doc-index`、`npm run check:encoding`、`git diff --check` 通过。
|
||||
- 仍未取得:真实“做方案”新项目进入 Design Agent、已有会话恢复、澄清与阶段审批回合在真机 + 真实 Provider 下的连续验收。
|
||||
|
||||
因此状态从 `proposed` / `ready` 改为 `implemented-awaiting-runtime-acceptance`:源码级判据全部成立,缺真机运行时证据。
|
||||
@@ -0,0 +1,56 @@
|
||||
# AGC 模型目录初始值改为上游同步
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | in-progress(实现与本地真实上游验证完成;生产发布未执行) |
|
||||
| Date | 2026-09-24 |
|
||||
| Parent Spec | `docs/technical/【技术方案】AGC后台模型别名与对话选择-2026-09-05.md` |
|
||||
|
||||
## 背景与触发
|
||||
|
||||
`agc_model_catalog` 缺行时,`read_agc_model_catalog` 兜底返回写死的初始目录(`高质量 → gpt-6-astra`、`快速 → gpt-5.6-luna`)。这两个模型已从上游 Router 移除,于是从未配置过目录的环境(新库、清库、本地调试)会把两个不存在的模型下发给客户端,选中后上游 `model_not_found`,必须人工在后台保存一次目录才恢复。
|
||||
|
||||
## 目标
|
||||
|
||||
1. 初始目录不再写死:api-server 启动期从上游分组定价列表生成,`alias` 与 `modelId` 都用上游原始模型名(不再填“高质量/快速”这类人工别名)。
|
||||
2. 拉不到就报错、不写替代目录,并在下一次启动继续重试,直到目录里有数据。
|
||||
3. **保持既有格式与契约不变**:目录字段(`id`/`alias`/`modelId`/`defaultModelId`)、后台页面与 DTO、`GET /api/llm/models` 形状、客户端模型标识校验都不变,不引入不兼容变更。
|
||||
|
||||
## 不在本里程碑内
|
||||
|
||||
- 不改目录字段语义与后台维护方式,不删别名/稳定标识概念。
|
||||
- 不做上游变化的自动跟随同步(由 owner 在后台维护)。
|
||||
- 不改 Router provisioning、额度与计费链路。
|
||||
- 不改 `/api/external/v1` 与 OpenAPI,不改 SpacetimeDB 表结构。
|
||||
|
||||
## 合同要点
|
||||
|
||||
- **初始化**:api-server(API/All 角色)启动时目录缺失、结构与当前定义不符或校验不通过即视为未初始化;此时请求 `GET {Router 控制面}/api/pricing?group=taonier`(公开只读、不带凭据),取 `data[].model_name`,按模型名排序生成目录:`modelId` 与 `alias` 为上游原名、`id` 为模型名 slug(小写字母/数字/`-`/`_`,同名冲突追加 `-2`)、全部 enabled、默认项取排序后第一项,并以存量 revision 写回自增。
|
||||
- **失败关闭**:拉取失败、空列表、响应超过 1 MiB、缺可解析 revision、写回失败都只记录 error,不写替代目录;未初始化期间 AGC 目录/对话接口与后台目录接口返回 `503`“模型目录未初始化”。
|
||||
- **重试口径**:只启动期尝试一次;失败不阻塞启动,下次启动重试,直到目录里有数据。请求侧无法触发同步。
|
||||
- **幂等与并发**:目录只取决于模型集合(排序后生成),重复同步一致;多实例并发只有一个写入成功,冲突方接受既有目录并校验其可用性。
|
||||
- **存量目录**:结构合法的目录不会被自动重建(包括旧版写死的 `quality/fast`),需要 owner 在后台改掉或清空该行后重启。
|
||||
|
||||
## 依赖
|
||||
|
||||
- `module-runtime`:`AgcModelCatalog::from_upstream_models`(slug 生成 + 默认项 + 校验),删除写死的 `Default` 实现。
|
||||
- `spacetime-module`:`read_agc_model_catalog` 缺行返回 `AGC_MODEL_CATALOG_NOT_INITIALIZED`。
|
||||
- `api-server`:启动期 `ensure_agc_model_catalog_initialized`;上游请求硬化(10s 超时、1 MiB 流式上限、禁止重定向、不带凭据);`ensure_llm_router_url_allowed`(只校验地址,不绑定已下线的固定模型)。
|
||||
- 文档:主规范、后端数据契约、运维文档、decision-log。
|
||||
|
||||
## 验收标准
|
||||
|
||||
1. 空目录 + 上游可达:启动后目录自动生成(别名即上游原名),`revision` 自增一次,`GET /api/llm/models` 的 `displayName` 是上游原名,界面不出现内置模型名。
|
||||
2. 空目录 + 上游不可达/非 2xx/空列表:启动只记录 error、不写替代目录;AGC 与后台目录接口 `503`;上游恢复后重启即同步成功。
|
||||
3. 幂等:同一模型集合重复同步得到一致的目录与默认项。
|
||||
4. 目录领域校验(id/alias/model_id、32 项上限、默认项必须启用)与请求侧 `422`/`409` 行为与改动前一致。
|
||||
5. 回归:Rust 定向测试、AGC/admin-web 类型检查与定向测试、`npm run check:encoding`、`check:doc-index`、`check:spacetime-schema`、`git diff --check`。
|
||||
|
||||
## 已决与待决
|
||||
|
||||
- 已决:上游来源用分组定价列表(2026-09-24 实测:`/v1/models` 用管理 token 返回 401;管理面注册表会带出已下线、无路由绑定的模型)。
|
||||
- 已决:`id` 用模型名 slug,保持客户端标识校验契约不变。
|
||||
- 已决:初始化失败不阻塞启动,只在下次启动重试。
|
||||
- 已决:目录结构与既有 DTO/页面保持不变,本变更不引入不兼容改动。
|
||||
- 待决:是否需要“上游自动跟随同步”(当前不做)。
|
||||
@@ -1,7 +1,7 @@
|
||||
# 【里程碑】AGC 渲染层下沉与最新 master 对齐
|
||||
|
||||
Version: `v1`
|
||||
Status: `ready`
|
||||
Status: `implemented-awaiting-runtime-acceptance`
|
||||
Date: `2026-09-28`
|
||||
Parent Spec: `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
|
||||
|
||||
@@ -45,3 +45,21 @@ Direct 线程管理器持有、封面生成增加了队列回填契约、新增
|
||||
长期不消失,因此两类改动必须同一个提交落地。
|
||||
- 维护态分类依赖服务端 envelope 的 `code` 与 `message`;分类失败时按普通错误展示,不阻塞业务。
|
||||
- 回滚:本里程碑只在本地分支上进行,`git reset --hard 2b06394bc` 可退回对齐前的分支状态,master 不受影响。
|
||||
|
||||
## 本轮取证(2026-09-28)
|
||||
|
||||
- 验收 1/2(活动回合唯一事实源 + 三处变更通知):`direct_runtime` 不再导出快照函数,`list_direct_active_turns` 只保留 Thread Manager 一份;接单、进度内容变化、收口分别在 `accept_direct_thread_turn`、`update_direct_thread_active_turn`(仅内容变化时)、`complete_direct_thread_turn` 与 `complete_direct_thread_turn_if_reserved` 调用 `emit_direct_active_turns_changed()`。Rust 定向:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --offline -- direct_thread_manager --test-threads=1` → **17 passed**(含新增用例 `active_turn_changes_publish_one_notification_per_real_change`)。
|
||||
- 验收 3(先订阅再读快照、重复事件不换身份、卸载不写回):`npx vitest run tests/directActiveTurns.test.tsx --root apps/ai-game-creator-shell` → **11 passed**。
|
||||
- 验收 4(封面队列回填):Rust 封面生成请求体补 `generationInputs.source=ai-game-creator-client`,队列未完成时仍走既有 `resolve_cover_queue` 轮询;`tests/gameDistributionPublish.test.ts` → **7 passed**。
|
||||
- 验收 5(维护态唯一出口):平台请求的错误分支统一经 `platform_maintenance::watch_platform_response`(只有 503 且命中 `MAINTENANCE` / 「维护」才算)广播 `genarrative-client-maintenance-detected`;渲染层 `subscribeClientMaintenanceEvent` 订阅后打开唯一弹窗,同一批并发失败只弹一次。`cargo test ... -- platform_maintenance --test-threads=1` → **1 passed**;`npx vitest run tests/maintenanceNotice.test.tsx` → **2 passed**。
|
||||
- 验收 6(渲染层无 HTTP):`clientApi.ts` 已删除,`capabilities/main.json` 不含 `http:default`,全仓检索 `services/clientApi` 与 `fetchClientHttp` 无命中。
|
||||
- 验收 7:见合并提交说明里的 `lint` / `npm test` / `ai-game-creator-shell:check:web` / `check:encoding` / `check:doc-index` / `git diff --check` 结果。
|
||||
|
||||
### 与 master 既定决策的分歧:诊断详情入口跟随上游退役
|
||||
|
||||
master 提交 `5398a53e6 宿主与前端:用户可见文案不再带诊断引用,失败也进错误上报池` 明确退役了「查看详情」入口:失败文案不再带 `;详情:…` 引用,`read_agent_runtime_error_detail` 命令与渲染层 `agentRuntimeErrorDetail.ts` 一并删除,失败线索只留在 `.agent/runtime/errors`、应用日志与错误上报池。本分支《AGC统一错误诊断与验收反馈》里程碑的第 6 条(点击详情后才读取)被上游取代,本次对齐按 master 收口:删除该里程碑的专属实现与用例(含仅钉该后缀的 Rust 用例 `direct_codex_failure_text_keeps_the_detail_ref_marker_for_the_renderer`),失败说明仍由 `turn.completed.failure` 事件投影成同一份安全文案。
|
||||
|
||||
## 仍未取得(需要真实客户端)
|
||||
|
||||
- 真实维护窗口下并发失败只弹一次弹窗、关闭后业务页可重试的真机表现。
|
||||
- 真实 Provider 下长回合的活动回合事件时序与首页「运行中」列表的实时性。
|
||||
@@ -0,0 +1,46 @@
|
||||
# 【里程碑】External v1 游戏场景生成路由
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | proposed |
|
||||
| Date | 2026-09-24 |
|
||||
| Parent Spec | `docs/technical/【技术方案】ExternalV1游戏场景生成路由-2026-09-24.md` |
|
||||
|
||||
## 背景与触发
|
||||
|
||||
平台自 `72f268e08`(游戏场景需求 v1.0)起在通用图片生成入口拒绝 `kind = scene` / `assetKind = scene`,要求场景必须走结构化专用路由;但 external v1 没有场景路由,AGC 客户端陶泥儿美术包的背景阶段仍向 `/api/external/v1/editor/images/generations` 提交 `assetKind = scene`,被平台参数校验拒绝(HTTP 400),新项目美术包无法生成(实测产物 `gameagent-f91a64dd`)。同时发现背景阶段保留账本匹配器期望 `assetKind = "game-background"`,与实际请求线上的 `"scene"` 不一致,中断恢复匹配失效。
|
||||
|
||||
## 目标
|
||||
|
||||
1. external v1 新增 `POST /api/external/v1/editor/scenes/generations`,与站内场景路由共享同一场景 Prompt 组装与入队实现,鉴权、幂等、计费、持久化语义与现役外部生成入口一致。
|
||||
2. AGC 美术包背景阶段迁移到新路由,以结构化场景意图提交,`reuse-or-create` 与 `regenerate` 均恢复可用,出图风格与尺寸不变。
|
||||
3. 背景阶段的路由、manifest 身份登记、保留账本匹配与恢复扫描常量整圈对齐到新路由与实际线上值;历史登记保持可读。
|
||||
|
||||
## 不在本里程碑内
|
||||
|
||||
- 保持站内场景生成规则,补齐账号模式幂等与结果协议;不改通用图片入口的 scene 拒绝校验。
|
||||
- 不改 SpacetimeDB schema、队列类型、计费档位。
|
||||
- 不迁移历史背景登记数据。
|
||||
- 不处理 issue #495 的两个既有问题。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- `shared-contracts`:`EditorSceneGenerateRequest` 已存在,直接复用。
|
||||
- `api-server`:站内 `generate_editor_scene` 的组装与入队逻辑抽取为共享实现。
|
||||
- AGC `src-tauri`:背景阶段请求构造、保留账本与身份常量迁移。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [x] 账号场景入口透传可选幂等键、入队前拒绝非法键;场景配方保留 AGC 来源标记且队列完成结果包含可下载资源(路由校验及组装到结果序列化的定向测试)。
|
||||
- [ ] 新路由契约测试通过:路由矩阵与鉴权、参数 400、同键重放返回原任务、同键不同请求 409。
|
||||
- [ ] 外部场景路由与站内路由对相同输入产出相同的后端 Prompt 与入队 payload(共享实现单测对照)。
|
||||
- [ ] `docs/openapi/genarrative-external-v1.openapi.json` 与实现一致,相关契约检查通过。
|
||||
- [ ] 客户端定向测试:背景阶段请求命中新路由且为结构化字段;保留账本匹配器与实际请求口径一致;`reuse-or-create` 与 `regenerate` 路径回归通过。
|
||||
- [ ] 本地真实栈 smoke:`npm run dev:api-server` + 测试 Key 下美术包背景阶段受理成功(Provider 出图按环境可用性记录为已验证或未验证)。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 自动化:`cargo test -p api-server`(契约与场景相关单测)、AGC `src-tauri` 定向测试、`npm run check:encoding`、`git diff --check`。
|
||||
- 运行时:本地 api-server 受理 smoke。
|
||||
- 边界:鉴权 401/403、幂等重放、参数失败关闭。
|
||||
@@ -0,0 +1,47 @@
|
||||
# 【里程碑】UI编辑器多树预览与独立显示开关
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | approved |
|
||||
| Date | 2026-09-14 |
|
||||
| Parent Spec | `docs/technical/【前端架构】UI编辑会话模块边界-2026-08-19.md` |
|
||||
|
||||
## 目标
|
||||
|
||||
UI 编辑器预览同时展示全部界面树。每棵树使用 root.offset 的 min 作为树级画布位置,支持整树拖动持久化;显示原图、组件和普通框线拆成三个独立开关。
|
||||
|
||||
## 范围
|
||||
|
||||
- `Node` 增加包含 min/max 的 offset 数据,读取只使用 min,max 由 min 与源图 logical size 派生。
|
||||
- 所有新树统一由 createTree 入口创建,并按现有树最右边界加 padding 横向排列。
|
||||
- 首次进入预览和手动适配使用全部树联合边界。
|
||||
- 左侧图片选择继续维护 activeImageId;预览选择不改变它,不因选择或拖动自动 fit。
|
||||
- root 可拖动但不可 resize;子节点维持现有选择、拖动和 resize 行为。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- 旧数据 migration、offset fallback、自动 revision 修复。
|
||||
- 删除/排序后的自动重排。
|
||||
- 代码生成 runtime 的树级布局语义。
|
||||
- root/max 的独立编辑控件。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- 当前资源均符合最新 Node/UITree schema。
|
||||
- 对应界面图提供合法 pixel_size 与 pixels_per_unit。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ] 新树默认位于所有现有树右侧并与最小 top 对齐。
|
||||
- [ ] 原图与组件随 root 一起移动,拖动结束进入 undo/redo 并可保存恢复。
|
||||
- [ ] root 不显示 resize handles,子节点 resize 不回归。
|
||||
- [ ] 三个开关默认 showFrame=true、showOriginImage=true、showComponent=false,且互相独立。
|
||||
- [ ] 预览选择不修改 activeImageId;左侧图片选择和 Inspector 既有切换保持。
|
||||
- [ ] 仅首次进入和手动适配触发联合 fit。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 自动化:UI editor preview、state、transform 相关 Vitest;Tauri Rust 单元测试;类型检查。
|
||||
- 运行时:必要时执行 AGC UI 编辑器页面 smoke。
|
||||
- 边界:非法缺失 offset 不兜底;树重叠命中顺序;root 禁止 resize。
|
||||
@@ -1,70 +0,0 @@
|
||||
# 【里程碑】退役策划 Agent V1/V2 解耦清理
|
||||
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | implemented-awaiting-runtime-acceptance |
|
||||
| Date | 2026-09-15 |
|
||||
| Parent Spec | `docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md` |
|
||||
|
||||
## 目标
|
||||
|
||||
移除旧版策划 Agent V1、V2 的前端会话、审批、数据适配、测试契约和确定失效的展示残留;仅在删除策划链路时遇到已退役 Supervisor 功能耦合时一并删除该耦合,使“做方案”只使用当前 Design Agent、做游戏只使用 DirectCodex。
|
||||
|
||||
## 范围
|
||||
|
||||
- 解除当前 Design Agent 与旧 `planningV2` / `PlanGdd` 状态命名和控制流的耦合。
|
||||
- 删除仅服务旧策划的 Supervisor 产品入口、会话恢复、Runtime 轮询和聊天提交分支;保留通用 Supervisor 与做游戏 DAG。
|
||||
- 删除旧 V2 会话 hydrate、start、continue、审批和用户问询分支。
|
||||
- 删除旧 V2 TypeScript 会话适配层、旧 GDD 前端类型、测试 mock、旧事件契约和专属样式。
|
||||
- 清理确定没有现役调用方的旧策划身份说明、测试 fixture 和文档当前入口。
|
||||
- 保留当前 Design Agent 的会话、澄清、阶段审批、工作区浏览和 reasoning 展示行为。
|
||||
|
||||
## 不在范围内
|
||||
|
||||
- 不主动扫描、重构或整体删除做游戏 Agent 的 16 Agent DAG;只有策划删除直接造成编译或测试失败时才做最小修复。
|
||||
- 不删除 DirectCodex 或当前 Design Agent;必要时保留被两者复用的中性聊天表现组件。
|
||||
- 不为旧项目新增兼容层、迁移器、墓碑注释或退役行为测试。
|
||||
- 不修改 SpacetimeDB schema、公开 API、持久化迁移和现役 Design Agent 协议。
|
||||
|
||||
## 依赖与前置条件
|
||||
|
||||
- PR #159 的合并提交 `3d8e0211` 代表旧 Fast GDD / 策划 V1 的引入。
|
||||
- PR #305 的合并提交 `04128eb6` 同时包含 V1 大范围退役、策划 V2 会话链路和后续 Design Agent 迁移。
|
||||
- 当前分支已经删除 Rust V1/V2 Runtime 模块和旧审批组件,但前端仍残留旧 V2 调用方;实现前须保持工作树干净。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [x] “做方案”入口和已有 Design Agent 项目只调用当前 Design Agent IPC,不再调用旧 `planning_*_v2` IPC。
|
||||
- [x] 当前 Design Agent 的消息、reasoning、澄清、阶段审批和重试行为不依赖旧 V2 状态变量。
|
||||
- [x] 源码中不再存在旧 V2 TypeScript 会话适配层、旧 `PlanGdd` 类型和旧前端审批契约。
|
||||
- [x] 旧前端测试、事件测试和样式残留被删除或改为当前 Design Agent 契约。
|
||||
- [x] 不主动修改做游戏 Supervisor + 16 Agent DAG;因共享退役代码删除产生的编译错误得到最小修复。
|
||||
- [x] 前端 typecheck、相关定向测试、编码检查和 diff 检查通过;触及 Rust 时对应 cargo check 通过。
|
||||
|
||||
## 证据要求
|
||||
|
||||
- 自动化:`npm --prefix apps/ai-game-creator-shell run typecheck`、相关 appSurface 定向测试、`npm run check:encoding`、`git diff --check`。
|
||||
- 运行时:至少验证“做方案”新项目进入 Design Agent、已有 Design Agent 会话恢复、澄清/审批回合可继续。
|
||||
- 边界:确认 DirectCodex 和做游戏既有入口未被旧策划清理改动;确认旧 V2 IPC 字符串和旧事件契约不再进入现役前端。
|
||||
|
||||
## 源码级验收(2026-09-24)
|
||||
|
||||
- `planningSessionV2.ts` 已不存在;`src` 内检索 `planning_[a-z0-9_]*_v2` / `planningV1` / `planningV2` 无命中,做方案入口只调用 `design_agent_session` / `continue_design_agent_session` / `hydrate_design_agent_session` 等当前 Design Agent IPC。
|
||||
- 旧 Plan GDD 类型与旧策划流工厂已清空:本轮删掉测试 harness 里无调用方、且引用了已不存在类型的 `createPlanGddStateView` 与 `planningResponseStream`(两者原本还导出给 suite);`PlanGdd` / `planning-session-v2-stream` 在 `src` 与 `tests` 已无命中。
|
||||
- 旧策划样式无残留:`plan-gdd` / `planning-lane` / `gdd-approval` 在 `src` 样式与组件内无命中。
|
||||
- Rust 侧同批退役见 decision-log 2026-09-21 条目:`planning_gdd_model.rs`、`planning_policy_v2.rs`、`planning_session_v2.rs`、`planning_*_v2` 命令与前端契约全部删除,不保留兼容别名或双跑路径。
|
||||
- 自动化:`npx vitest run tests/appSurface.test.ts tests/gamePublishFeedback.test.tsx` 195 passed / 9 skipped;`npm run ai-game-creator-shell:check:web` 185 files passed / 1 skipped、1806 tests passed;`npm run check:doc-index`、`npm run check:encoding`、`git diff --check` 通过。
|
||||
- 仍未取得:真实“做方案”新项目进入 Design Agent、已有会话恢复、澄清与阶段审批回合在真机 + 真实 Provider 下的连续验收。
|
||||
|
||||
因此状态从 `proposed` / `ready` 改为 `implemented-awaiting-runtime-acceptance`:源码级判据全部成立,缺真机运行时证据。
|
||||
|
||||
## 逐条勾选依据(2026-09-24)
|
||||
|
||||
- 条目 1/2/3:`rg "planning_[a-z0-9_]*_v2|planningV1|planningV2" apps/ai-game-creator-shell/src` → 0 命中;`planningSessionV2.ts` 不存在;做方案入口只调用 `design_agent_session` / `continue_design_agent_session` / `hydrate_design_agent_session`。
|
||||
- 条目 4:本轮删掉测试 harness 里无调用方、且引用已删除类型的 `createPlanGddStateView` / `planningResponseStream`;`PlanGdd`、`planning-session-v2-stream`、`plan-gdd` 样式在 `src` 与 `tests` 内均无命中。
|
||||
- 条目 6:`npx vitest run tests/appSurface.test.ts tests/gamePublishFeedback.test.tsx` 195 passed;`npm run ai-game-creator-shell:check:web` 185 files / 1808 passed;`npm run check:encoding`、`npm run check:doc-index`、`git diff --check` 通过。
|
||||
- 条目 5:按「退役提交的文件清单 + 当前状态」两条线取证,判据成立。① 退役提交的删除面集中在 planning 专属对象:`876529e66` 删除 Project Supervisor 前端链路并把项目对话收敛到 DirectProject 与立项策划(改动落在 `src/App.tsx`、`src/features/**`、`src-tauri/capabilities/*`、`windows.rs`),`70271b408`(PR #355)删除 `planning_gdd_model.rs` / `planning_policy_v2.rs` / `planning_session_v2.rs` 三个 Rust 模块、对应命令、前端 `planningSessionV2.ts`、`GddApprovalCard.tsx` 与其测试和样式。
|
||||
- 条目 5(续):同一提交对做游戏 DAG 共享代码只有最小修复——`agent/runtime_protocol.rs` 去掉三处 `mod` / `pub use`;`agent/runtime_driver/main_loop.rs` 删掉引用 `PlanningStorageError` 的未用 helper;`project/write_lock.rs` 与 `delegation.rs` 只改文档注释(去掉已删模块名);`agent/runtime_tools/delegation.rs` 只删 planning 专属的 `PlanClarification` / `UserRevision` 两种跳转旁注及其测试,做游戏链路那支 `Repair` 旁注保留原文;`delegation.rs` 另有一处测试 fixture 文本从 `game/fast_gdd.md` 改为 `design.md`。
|
||||
- 条目 5(续):做游戏链路的授权信号仍在——`apps/ai-game-creator-shell/src-tauri/prompts/runtime/texts/runtime.json` 的 `delegation_repair`(「这是对已认领委派 … 的唯一返工轮。」)逐字未改,`agent/runtime_tools/delegation.rs` 的注释继续说明它是 `design-foundation` / `art-director` / `art-asset-plan` 的 `replaceExisting` 唯一许可。
|
||||
- 条目 5(续):当前状态直接跑做游戏 DAG 合同——`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --offline autonomous_game_build -- --test-threads=1` → lib 目标 **28 passed / 0 failed**(2909 filtered out),含 `new_autonomous_root_contract_resets_all_sixteen_seed_tasks`、`standard_profile_keeps_confirmation_policy_unchanged`、`autonomous_game_build_recovery_aborts_legacy_confirmation_batch_and_replans`。也就是说退役后做游戏 Supervisor 与 16 Agent DAG 既没有被改成别的行为,也没有留下编译或测试失败。
|
||||
@@ -1,5 +1,138 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-09-22 UI 编辑器预览画布补上右键拖拽平移,节点菜单改为右键抬起弹出
|
||||
|
||||
- 背景:预览画布此前只有中键与空格+左键平移,右键整段留给节点操作菜单(`UiTreeRenderer.onContextMenu` 直接弹 `UiNodeContextMenu`)。这次要补右键拖拽平移,并要求"拖拽过就不许再触发右键菜单"。实测(Linux Chromium 151 / Firefox 151,真实 X11 输入)确认 `contextmenu` 在**按下**瞬间触发,且原生菜单一旦弹出,页面之后收不到任何 `pointermove` / `pointerup` / `mouseup` / `auxclick`,所以"先让菜单弹、拖拽时再关"在浏览器层面不可行;headless 没有原生菜单,Playwright 复现不出该行为。macOS 的 `contextmenu` 在 mouseup 触发(本容器无法实测),但同一条实现路径对两种时序都成立。同一次实测:键盘菜单键触发的是 `button: -1`,所以"只认按钮 2"的拦截天然把键盘菜单留给原有节点菜单路径。
|
||||
- 决策:预览视口在捕获阶段拦截按钮 2 的 `contextmenu`(`preventDefault` + `stopPropagation`),右键手势改由预览自己裁决:按下时记录起点、`setPointerCapture` 并交焦点;移动越过与左键拖拽共用的 `DRAG_THRESHOLD_SCREEN_PX`(2px)后本次手势定死为平移,按"按下点全量 delta"更新视口(光标复用共享 `CanvasViewport` 的 `isPanning` → `cursor: grabbing`,三个平移绑定一起生效);未越阈值且在预览内抬起时,用 `[data-node-id]` 加树容器 `data-tree-id` 命中节点并打开 `UiNodeContextMenu`,保留"右键即选中该节点"的既有语义;空白处干净右键不做事(原生菜单已被抑制)。中键、空格+左键平移不变,macOS ctrl+左键与键盘菜单键继续走原有即时菜单路径;平移是纯视图操作,不写 State、不进历史、不受 `isLocked` 与空格按住态限制。
|
||||
- 原因:右键同时承载菜单与视图平移,只能等到手势结束再裁决;把判定放进预览,是因为树、节点命中、预览边界与拖动阈值都在预览手里,而 `useNodeTransformInteraction` 应保持左键变换的单一职责;`[data-node-id]` 命中也已是 `resolveHitNodeId` 的既有模式。
|
||||
- 代价与取舍:右键从"按下即弹菜单"变成"抬起才弹"(与 Windows 自身右键菜单一致);预览内空白处的浏览器原生菜单被永久抑制;右键平移与节点菜单互斥(越过阈值后抬起不再弹菜单);`grabbing` 光标在悬停到节点上时仍会被节点自身的 `cursor-move` 覆盖,与共享画布现状一致。本次只改 UI 编辑器预览:AGC 美术画布仍只有空格/中键平移,资源画布保留自己的右键平移实现,都不动,也不抽 `packages/shared`。
|
||||
- 验证方式:新增 `previewRightPanGesture` 纯状态机单测(阈值跨越、起点全量 delta、拖拽吞菜单 / 干净抬起开菜单、取消与失焦清理),并在 `previewRightPanDrag.test.tsx`、`previewRightPanGesture.test.ts` 补右键回归(含键盘菜单键 `button: -1` 不被拦截的用例);运行 `npx vitest run`(定向文件)、`apps/ai-game-creator-shell` `npm run typecheck`、`npm run lint:eslint`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-09-24 接单化 review 收口(第二轮):失败载荷分类、拒单身份与提示口径
|
||||
|
||||
- 决策(失败载荷的 `kind` 收成 typed 枚举):新增 `DirectTurnFailureKind`(`Serialize + Deserialize + TS`,
|
||||
`kebab-case`,7 个变体,含先前两份名单都漏登记的 `turn-interrupted`),`DirectTurnError::wire_kind`
|
||||
返回 `Option<DirectTurnFailureKind>`。线上仍是 `{kind, message}`、取值不变,只有 TS 侧从裸 `string`
|
||||
变成可穷尽收窄的联合类型;全仓没有按 `failure.kind` 分流的代码,它只给界面选语气。
|
||||
- 决策(并发拒单的两个身份是回合身份):`DirectThreadManager::accept_turn` 冲突时返回占用对象的
|
||||
`turn_id`,`DirectTurnReservation::accept` 把这一轮请求的 `clientTurnId` 传成
|
||||
`incoming_invocation_id`。改动前这两项是进程内 UUID,`TurnAlreadyRunning` 的"同一轮仍在处理中"
|
||||
分支永远命中不了,也与"回合身份由 `clientTurnId` 推导"的口径冲突。占用对象自己的 `token` 仍是
|
||||
UUID(`complete_direct_thread_turn_if_reserved` 靠它配对),只换错误载荷里的两项。
|
||||
- 决策(目录锚不定的拒单不再写诊断):`DirectTurnError::ProjectRootUnanchored` 从 `is_reportable()`
|
||||
拿掉,与 `ProjectRootUnusable` 同类——符号链接 / 权限 / 目录被删都是用户自己就能修的文件系统事实。
|
||||
改动前它被命令边界覆写成 `direct-codex-failure:v2` 收口文案,界面上那句"无法锚定 Direct 调用项目
|
||||
目录:{cause}"被内部诊断串顶掉;现在界面按 `Display` 显示,也不再进 `.agent/runtime/errors`。
|
||||
可留痕的拒单只剩 `environmentNotReady` / `hostStateUnavailable`。
|
||||
- 决策(认不出的拒单也要在聊天里有同级提示):`environmentNotReady` / `hostStateUnavailable` 除上报 +
|
||||
横幅外,再补一条与用户消息同级的提示——拒单没有接单、不产生 `turn.completed`,否则那条乐观用户
|
||||
气泡后面永远没有解释(改动前的注释"宿主已经把它放进了 `turn.completed.failure`"对拒单不成立)。
|
||||
文案走 `projectRuntimeVisibleRejectionError`:取宿主收口文案里已脱敏的摘要与建议,**不套阶段标签**
|
||||
(拒单这一轮没有开始,阶段只会是默认值);非结构化错误仍只走横幅(它可能发生在接单之后)。
|
||||
- 决策(失败说明的文案口径):`projectRuntimeVisibleError` 补上宿主 `Display` 事实句的模式
|
||||
(`执行通道已断开` / `等待模型回合结束达到硬上限` / `宿主任务提前结束` / `收尾历史失败` 一族),
|
||||
并给落盘那档补上不带"失败"二字的事实句;不回落宿主原文(`TransportClosed` 的原文带 `exitStatus=` /
|
||||
`stderrClass=`)。同时修掉收口文案的版本口径:解析只认 `v1`、宿主发的是多一段 `code=` 的 `v2`,
|
||||
脱敏摘要一直命中不了。口径定为"不加模式就只会看到通用文案",写在 `directTurnFailure.ts` 的注释里。
|
||||
- 明确不做:不改线上载荷形状与 `kind` 取值;不加新的失败阶段取值(拒单仍落默认阶段);不动
|
||||
`ProjectRootUnanchored` 之外的拒单分类。
|
||||
- 决策(连接死亡的失败事实先于看门狗可见):`CodexAppServerInner::closed` 的语义定为"这一段已经收束 /
|
||||
失败事实已经记下",看门狗就盯着它,所以它不能再兼作死亡收口的去重标志——去重改用私有的
|
||||
`connection_end_claimed`,`fail_game_creator_codex_app_server_connection` 不再置 `closed`,
|
||||
`closed` 只在 `shutdown_game_creator_codex_app_server_inner` 里、`record_execution_turn_failure` **之后**
|
||||
置位。改动前收口路径先置 `closed` 再做"两次加锁 + 一次日志写",200ms 看门狗可能在这一段里抢跑,把
|
||||
这一轮收束成 `Interrupted`,typed `TransportClosed` 记不进去,终态退化成"本轮已结束、没有原因"
|
||||
(失败事实是在模型终态那一刻被快照的,晚补记无用,所以只能保证"事实先于可见性")。代价是其它读
|
||||
`closed` 的地方会晚几十微秒看到"连接已死",两个并发的死亡观察者仍会各自走到幂等的收束函数。回归用例
|
||||
`connection_death_records_the_failure_fact_before_the_watchdog_seals_the_turn` 卡住 stderr 摘要锁把窗口
|
||||
拉成确定性,把看门狗真正跑起来钉这条(顺序反了就红)。
|
||||
- 决策(接单之后的失败不回命令返回值):`chat_with_game_creator_direct_codex_typed` 在接单后的历史追加
|
||||
写失败时仍然写 `turn.completed` 失败终态,但 `return Ok(())`——命令的 `Err` 只表示**拒单**。改动前同一
|
||||
个失败从"事件里的说明"和"命令 `Err` 的横幅"两条通道下发(且 `EnvironmentNotReady` 会写诊断 + 上报),
|
||||
前端又把 `Err` 当"这一轮没开始",于是忙态与出队同时被事件和返回值两条路推。**不继续起整轮**:
|
||||
`project.jsonl` 是这条对话的单一事实源,用户消息没落盘时继续跑只会得到一条没有开口用户消息的助手回复,
|
||||
失败还会被静默。用例:Rust `a_history_write_failure_after_accept_closes_the_turn_instead_of_rejecting`
|
||||
(恰好一条失败终态、不带拒单收口文案、占用释放)、前端 appSurface 的落盘失败用例(说明只来自事件且
|
||||
恰好一条、忙态放掉、下一条能发)。
|
||||
- 影响范围:Rust `apps/ai-game-creator-shell/src-tauri/src/agent/{codex_app_server/mod.rs,direct_turn_error.rs,direct_turn_failure.rs,direct_turn_accept.rs,direct_thread_manager.rs,direct_runtime/user_input.rs}`;
|
||||
前端 `src/features/agent-runtime/model.ts`、`src/view/project-development/chat/{conversation/directCodexConversation.ts,conversation/directTurnFailure.ts,controller/useDirectProjectChatController.ts}`、
|
||||
`src/view/project-development/chat/generated/DirectTurnFailureKind.ts` 与
|
||||
`tests/{agentRuntimeModel.test.ts,directThreadChat.test.ts,appSurface/chat-composer.suite.ts,appSurface/project-conversation.suite.ts}`;
|
||||
文档 `docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`、`docs/technical/【实施计划】DirectProject命令接单化-2026-09-23.md`。
|
||||
- 验证:Rust `cargo test --bins "agent::"`(902 passed / 5 ignored)、定向
|
||||
`cargo test --bins "agent::direct_turn_error"`(15 passed)、`cargo fmt`;前端
|
||||
`npx vitest run tests/{appSurface.test.ts,directRunAnalytics.test.ts,directProjectTurn.test.tsx,agentRuntimeModel.test.ts,directThreadChat.test.ts}`
|
||||
(277 passed / 9 skipped)、`npm --prefix apps/ai-game-creator-shell run typecheck`、`npm run check:encoding`、
|
||||
`git diff --check`。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-24 接单化 review 收口:终态写点、返修控制流、终止判据与失败投影
|
||||
|
||||
- 决策(终态的写点在整轮真正结束之后):Direct 回合先固定终态判定的上下文,`turn.completed` 的写出
|
||||
挪到执行结果收集、历史落盘、structured output 解析都定型之后,成功与失败共用一个写点。解析失败也是
|
||||
这一轮的失败,落进同一份失败载荷;改动前终态先写、再解析,解析失败时终态已是 `completed`,占用对象
|
||||
的兜底变成空操作,用户看到"本轮结束、没有回复、没有任何解释"。收尾结果因此拆成
|
||||
`DirectTurnReport`(报告正文 + 解析结果),占用解除与终态事件一起走 `DirectTurnTerminalContext::write`。
|
||||
- 决策(封口返修要求是控制流,不是失败):`HostOutcome::RepairRequired` 不再伪装成
|
||||
`LlmError::InvalidRequest("validation-source-changed: …")`,改为 typed 的
|
||||
`DirectTurnRunFailure::RepairRequired` → `DirectTurnError::RepairRequired`:不写终态、不进载荷、不上报,
|
||||
由 `direct_runtime` 的返修循环写回提示词继续跑(与 `ReviewRequired` 同一族,次数上限仍留在产生侧)。
|
||||
改动前它被判成 `failed` 终态、界面收到一条假失败,还会让同一个逻辑回合写出第二条终态。
|
||||
- 决策(用户按下的终止不算通道失败,判据收进 `fail_turn`):失败事实的判据是
|
||||
`!is_closed() && !host_stop_requested()`,不再由各调用点各写一遍 `!is_host_ending()`。用户点「终止」时
|
||||
标志先置位、阶段后变,原来的窗口里到达的 `TransportClosed` 会把用户自己的终止记成 `transport-failed`。
|
||||
- 决策(登录态失效的两条分类路径统一可重试):认证失败不再按"重跑整轮"处理,刷新失败与重试失败都按
|
||||
可重试的回合失败呈现(用户可见文案可能多一句"可直接重试",真实客户端观感未复核)。
|
||||
- 决策(失败载荷的健壮性):前端 reducer 对 `failure.message` 做运行时判据(缺字段 / `null` 不再抛错,
|
||||
与 `directTurnFailureNoticeText` 同口径);交付报告兜底只读一次 `terminal_report`(两次读取之间状态可能
|
||||
变化,`None` 不再被 `unwrap_or_default()` 变成空回复);失败说明条目在无身份无时间时会撞成同一条
|
||||
(已知边界,仅补注释)。
|
||||
- 明确不做:不改线上载荷形状(仍是 `{kind, message}`);不给 DirectProject 回合补端到端集成用例(缺轻型
|
||||
假 app-server 夹具),判据落在策略函数与适配器单测;不持久化"可见但不喂模型"的失败条目(TODO)。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/{codex_app_server/{mod.rs,execution.rs},direct_runtime/{mod.rs,user_input.rs},direct_turn_error.rs}`、前端
|
||||
`chat/{conversation/directThreadChat.ts,generated/DirectTurnError.ts}` 与 `tests/directThreadChat.test.ts`;
|
||||
文档 `docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`、`docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`。
|
||||
- 验证:`cargo test --bins "agent::"`(952 passed)、`cargo test --bins "direct_"`(475 passed)、定向
|
||||
`codex_app_server`(102 passed)、前端 `directThreadChat.test.ts`(36 passed)与
|
||||
`npm run ai-game-creator-shell:typecheck`、`npm run check:encoding`、`git diff --check` 通过。真实客户端观感未复核
|
||||
(终态写点与终止竞态落在真实宿主收尾上,单测盖不住)。
|
||||
|
||||
## 2026-09-23 Direct 回合错误改 typed:调用级拒绝与回合级失败分开
|
||||
|
||||
- 回合失败在宿主内部改成 typed 的 `DirectTurnError`(`apps/ai-game-creator-shell/src-tauri/src/agent/direct_turn_error.rs`):每个变体自带字段,调用级拒绝(并发复用同一 `clientTurnId`、另一条回合在跑、权限策略拒绝、目录锚不定、输入校验、环境/凭据未就绪)与回合级失败(模型调用失败、通道断开、等待超时、app-server 单方面中断、阶段失败)不共用判据,分流只认 `is_turn_failure()`。
|
||||
- 根因:改造前两层错误混在同一份字符串里,靠对原因文本做子串匹配决定"算不算失败""要不要反馈给模型""怎么给建议",任何文案改动都可能静默改变分流;并发拒绝还只靠一个前缀字面量给前端识别。
|
||||
- 分类不再做文本匹配:原生失败分类只解析 app-server 写下的 `codex-app-server-error:<kind>` 结构化前缀,转成 `DirectCodexNativeKind` 后再 `match`。
|
||||
- 明确不做:不改线上载荷(仍是 `{kind, message}`)、不改命令边界签名(仍是 `Result<String, String>`)、不改前端可见文案映射与 `wire_kind` 取值;Rust 侧不再解析那份字符串,字符串只在 `Display` 一处生成。不给深层尚未 typed 的事实补 typed 出口,只留一个显式的桥变体并在注释里写明新分类必须先加 typed 变体。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/{direct_turn_error.rs,direct_turn_failure.rs,direct_delivery.rs,direct_runtime/mod.rs,direct_runtime/user_input.rs,codex_app_server/mod.rs,codex_app_server/execution.rs}` 与 `apps/ai-game-creator-shell/src-tauri/src/cli.rs`;文档 `docs/adr/【ADR】DirectProject对话历史单一事实源-2026-09-16.md`。
|
||||
- 验证:`cargo test --bins -- direct_`(460 passed)、`cargo test --bins -- codex_app_server`(100 passed)、`cargo fmt --check`、`npm run check:encoding`、定向 `git diff --check`。整包 `cargo test --bins` 在本机被既有 `tests::provider` / `tests::project` 重型用例挂住(并发跑测试时另有一条锁竞争用例会假失败),非本次改动引入。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-23 ACL 提权修复按目标做 single-flight
|
||||
|
||||
- 背景:`windows_acl_repair_target` 对 Managed 作用域返回的是「第一个读取被拒的祖先」,同一祖先下的多个项目会解析到**同一个** repair target;而唯一的去重只是单次调用内的局部 `attempted_targets`。于是启动页一次挂载(≤8 个最近项目并发检查)会启动同样多次 `powershell -Verb RunAs`,用户看到叠在一起的 UAC 弹窗(issue #498)。
|
||||
- 决策:新增进程级闸门 `acl_repair_gate`,key = `(规范化 repair target, scope)`。并发调用只允许一次真实提权,其余等待并复用**同一结果**;结果在冷却窗口内直接复用(成功 30s / 失败 15s / 用户取消 120s),等待窗口 60s 超时按失败关闭。leader 异常退出由 RAII 兜底记为失败并唤醒全部等待者,避免等待者被永久挂住。
|
||||
- 决策补充(key 归一化):key 的路径半边经 `windows_acl_repair_gate_key` 归一化——去掉 `\\?\` / `\\?\UNC\` 前缀并统一小写。最近项目列表里同一项目实测同时存在 `\\?\C:\...` 与 `C:\...` 两种写法(客户端 localStorage 实测),不归一化就是两个 key,同一个目录仍会弹两次 UAC。这里刻意只做前缀与大小写归一而不 `canonicalize`:待修复目标恰恰是「读不动的目录」,解析不可靠。
|
||||
- 决策补充(冷却基准):冷却从**结果落库**时刻算起,不是 leader 起跑时刻。UAC 弹窗会被挂着几十秒到两分钟,用起跑时刻会让 120s 拒绝冷却在用户应答前就过期,前端 15s/45s/120s 的整表重查紧跟着再弹一次。
|
||||
- 决策补充(leader 失效接管):`leader_deadline`(默认 5 分钟)之后,新调用可以接管仍是 `running` 的 key;每个 leader 带令牌,被接管后旧 leader 迟到的结果直接丢弃,不会覆盖接管者的结果。真机上无人应答的 UAC 约 2 分钟自然超时,所以这个上限只兜「提权子进程真挂死」——否则该目标会永久按失败关闭(`clear_denials` 不清理 running,只能重启客户端)。
|
||||
- 错误类型化:用户取消 UAC 的错误统一带稳定标记 `AGC_ACL_ELEVATION_DENIED`,前端据此判定「不可自动重试」,不再依赖中文文案匹配。
|
||||
- 用户主动操作(打开/新建项目、文件选择器选择目录、重命名刷新)会调用 `clear_game_creator_acl_elevation_denials` 清除拒绝记忆,保证显式重试仍能再次请求提权。前端唯一入口是 `features/app-shell/aclElevation.ts` 的 `clearAclElevationDenials()`:最近项目 hook(`rememberRecentWorkspace` / `refreshRecentWorkspace`)与打开/新建链路(`useHomeProjectCreation.openProject`,覆盖行内打开与 picker)共用它;漏挂入口会让用户「点了打开立即失败、也不问授权」。
|
||||
- 未做:给提权子进程加有界等待(`Start-Process -Wait` 目前无超时)。理由:中断挂起的 UAC 流程比等待更糟,single-flight 已把并发弹窗收成一个,follower 的等待由 60s 窗口兜底。
|
||||
|
||||
## 2026-09-24 DirectProject 状态条口径翻转、几何约束与对话 Markdown 容错
|
||||
|
||||
- 背景:AGC DirectProject 对话区底部的「陶泥儿正在处理 / 已耗时 12.4秒」状态条同时退化三处:① 读秒 1 秒一跳(耗时文案不足一分钟显示一位小数,小数位却一秒才动一格);② 窗口压矮时被挤扁(300px 高压到 33px、240px 时 24px,文字被 `overflow: hidden` 裁掉);③ `turn.started` 之前(模型首 token 前,实测约十秒)整条卡片不出现,界面没有任何「正在处理」的交代。同批还修了对话 Markdown 的两处代码块问题(不换行把消息拉宽、粘在正文行里的围栏导致代码块解析错位)。
|
||||
- 决策(卡片口径翻转,**更正** 2026-09-22「卡片口径取保守」):卡片与已耗时起点改读 `displayBusy`(本地命令在飞 ∪ 原生已确认在跑)与「最新一个**未结束**回合的用户发送时间」。理由:`turn.started` 要等宿主应答返回才发出,只认原生真相会让首 token 之前那段没有交代;窗口期这一轮确实已经交给宿主(本地命令在飞),文案不虚报「宿主已在跑」之外的东西。(**再更正** 同日:本地乐观气泡已删,已耗时起点改读该轮的 `turn.started.at`——运行中读实时值、收口后读盖在条目上的值;接单窗口里还没有这一轮的条目,卡片只报「正在处理」、这一段不读秒。卡片口径本身不变:仍读 `displayBusy`。)
|
||||
- 决策(那条预言的处置):2026-09-22 那条写「若将来改成窗口期也显示卡片,`running` 在渲染层就没有消费者了,应把投影压成 `unfinished: boolean`」。本次改完后投影三态**仍有**消费者(**再更正** 同日:投影已压成两态 `running` / `finished`,`awaiting-start` 随本地乐观气泡一起删除;下面这两条消费者读的判据不变)——`DirectProjectTurn` 用 `state !== 'finished'` 做否定式判断、`state === 'running'` 挑流式正文,状态条也用 `state !== 'finished'` 定起点——所以不动 `DirectChatTurnState`,也不压缩成布尔。
|
||||
- 决策(状态条几何):卡片在 `.project-chat-conversation` 这条定高 flex 列里必须 `flex: 0 0 auto`。它带 `overflow: hidden`,按 flex 规范该项的自动最小尺寸归零,是这条链上唯一还能被压缩的项;压缩只能由消息列表吸收。同一选择器只保留一条规则(几何 + 不可压缩),不留两份。
|
||||
- 决策(对话 Markdown 对模型输出的容错):解析前先 `normalizeMarkdownFences` 再压缩空行;代码块 `pre` 与块内 `code` 各自都给 `whitespace-pre-wrap` + `break-words`。细则与判据见 `pitfalls.md` 同日两条。
|
||||
- 影响面:`apps/ai-game-creator-shell/src/{styles.css,components/ChatMarkdownMessage/index.tsx,view/project-development/chat/{DirectProjectChatView.tsx,components/DirectProjectConversation/DirectProjectConversation.tsx,controller/useDirectProjectTurnStatus.ts}}`;用例 `tests/{ChatMarkdownMessage.test.tsx,directProjectProcessStatus.test.tsx,appSurface/{chat-composer.suite.ts,project-development.suite.ts}}`。
|
||||
- 验证:`npx vitest run apps/ai-game-creator-shell/tests` 186 passed / 1 skipped(1874 条里 1860 passed / 14 skipped);真实 Chromium 夹具复核读秒 100ms、窗口 900→220px 高度下卡片恒为 36px 不被裁、代码块换行与两类粘住围栏;变异验证(读秒改回 1000ms、删 `flex: 0 0 auto`、卡片退回 `nativeRunning`、停掉围栏归一化、换行类名退回)逐条变红。
|
||||
|
||||
## 策划 V1/V2 退役的现行边界
|
||||
|
||||
- 旧策划 V1 和 Runtime V2 均已删除,当前策划入口统一使用独立 Design Agent。V1 被 V2 接替只描述历史过程,不表示 V2 仍在使用。
|
||||
- 下文旧策划版本的阶段审批、`plan.submit_gdd`、planning session binding、exact planning lifecycle v3、专属身份白名单、IPC 和测试约束均为历史记录,不能作为恢复代码或保留孤立实现的理由。不新增旧版本兼容别名、双跑或回退链路。
|
||||
- 通用项目锁、权限、持久化和当前 Design Agent 能力按实际调用保留;清理未用参数不扩大为删除调用方的持锁范围或锁归属校验。
|
||||
- 当前事实源:[策划 Agent 生产迁移与工作区浏览](../../technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md)。
|
||||
|
||||
## 2026-09-23 运行视窗:右下角全屏预览 + 没有内容就自动收起的信息栏
|
||||
|
||||
- 背景:运行页右下角缺一个把游戏画面放大到整屏的入口;运行视窗下方常驻「信息展示 / 数值微调」两张卡片,没有选中资源时就是两块空白,验收现场提出「没有功能就暂时隐藏」。
|
||||
@@ -924,7 +1057,7 @@ Godot 编辑器操控复用既有 AGC 插件宿主、EditorAdapter、Runner 和
|
||||
- 决策:Direct 过程卡顶部标题只由 `GameCreatorDirectTurnUpdateStatus` 决定(accepted=需求已接收 / running=任务执行中 / streaming=回复生成中 / finalizing=结果整理中 / completed=回复已生成 / failed=处理失败),小字只展示当前正在执行的具体内容并统一加“正在”前缀;真实回复增量(AccumulatedText)才标记 streaming,计划、推理、工具输出与 Activity 一律 running。生成中的累计回复直接作为 assistant 消息气泡在会话列表中原位更新,不再拼进过程卡;进入 finalizing / completed 时保留完整累计回复直到正式消息接管,失败时清除未完成正文。移除合成打字机回放;工具说明/中间文本不再触发 streaming。计划/推理通知收敛为 `preparing` 活动并在界面显示“正在思考中”,原始推理/计划正文不进入 UI,思考期的心跳按 1.2s 限流。命令/文件/工具执行细节与回复流解耦,`stream=false` 时仍展示在过程卡;MCP 工具按用户语义显示(例如 `agc_write_file` 为“正在写入文件:<项目相对路径>”、图片/素材/搜索/试玩分别显示生成、导入、搜索、试玩等动作),未知工具只显示“正在调用工具”不暴露内部工具名;命令显示“正在执行命令:<命令>”,验证类命令显示“正在验证游戏:<命令>”。同一活动后续无正文的心跳不得用通用文案覆盖已展示的具体工作。展开/收起是同一 `project + clientTurnId` 内的持久状态,内容更新不重置,切换新回合才收起;展开详情的滚动条轨道和角落保持透明。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs` 的 DirectProject observer、`apps/ai-game-creator-shell/src/App.tsx` 的事件投影、`ProjectSupervisorView` 过程卡渲染与对应 AppSurface 回归。
|
||||
- 验证方式:Rust 单测证明只有开启流式时的 AccumulatedText 是 streaming、preparing 通知只产生 thinking 活动词且不携带原始推理文本、执行细节在 `stream=false` 时仍保留,并覆盖全部 AGC MCP 工具语义、未知工具不泄漏、绝对路径 / 上跳路径不展示;AppSurface 覆盖接受态、preparing 显示“正在思考中”、running 长文本展开、command-exec 与写文件心跳不覆盖具体工作、streaming 正文进入 assistant 气泡且过程卡只显示阶段、同一回合后续 running 不覆盖正文也不收起、失败后清除未完成正文、正式消息接管不重复;样式核对确认展开详情的滚动条轨道与角落透明;AGC typecheck、全量 appSurface、rustfmt、`npm run check:encoding`、`git diff --check` 通过。
|
||||
- 关联文档:`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`、分支 `feat/agc-llm-router-official-chain`。
|
||||
- 当前行为依据:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`;旧 `docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md` 仅用于追溯,不承诺继续生成平行日志。
|
||||
|
||||
---
|
||||
|
||||
@@ -959,20 +1092,19 @@ Godot 编辑器操控复用既有 AGC 插件宿主、EditorAdapter、Runner 和
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-31 Direct 回合把 Codex item 落成有界行为账本
|
||||
## DirectProject 平行审计与请求分段计时退役边界(2026-09-23 核准)
|
||||
|
||||
- 背景:sidecar 已让模型看见本轮附件路径,但 native 读 / MCP / 写文件只存在于隔离 `CODEX_HOME` 的瞬时 stdout,回合结束即删。无法判断「没读附件」还是「读了仍走默认收集类」。
|
||||
- 决策:GUI DirectProject 每个 `clientTurnId` 追加 `.agent/runtime/direct-codex/turns/<id>.jsonl`,并在 `agent.db` 写一条 `direct.codex.turn` 摘要。记 sidecar 提供的路径与文件 hash、`item/completed` 的 Read/List/Search/MCP/写文件(不含 stdout、patch、MCP result),以及 `offeredRead` / `firstDesign`。审计 fail-open,不阻断做游戏。Home、CLI、Supervisor 收据模型不接。不灌附件正文,不强制读取,不为 GDD 开特例。
|
||||
- 影响范围:`direct_codex_audit.rs`、Direct GUI command 边界、Codex collect 循环;前端 / jsonl 气泡 / sidecar 文案不变。
|
||||
- 验证方式:Rust fixture 覆盖 turn_start hash、绝对路径相对化、stdout/diff 不落盘、art brief 保留、list/search 不算已读、firstDesign 顺序、256 条截断、写盘失败不 panic;sidecar 渲染与 Direct 活动词测试保持通过。
|
||||
- 关联文档:`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`、issue #212。
|
||||
- 当前合同:完整用户与 Codex 完成 item 保存在 `.agent/conversations/project.jsonl`;GUI 回合不再创建 `runtime/direct-codex/turns/<id>.jsonl` 或对应 `agent.db` 的 `direct.codex.turn` 摘要。旧 `offeredRead` / `firstDesign` 和请求分段计时不再属于生产保证,旧审计专题仅作历史追溯。
|
||||
- 实现边界:旧 `DirectCodexTurnAudit`、`DirectTurnMetrics`、可选审计 / 计时参数及专用批量 JSONL 追加包装已删除。Provider proxy 本体及独立 model-usage observer 仍有现役用途,不能随旧计时链退役;字节流透传和上游错误传递继续由现有测试验证。
|
||||
- 保留边界:运行中的界面对话 / 工具耗时、`.agent/model-usage.jsonl`、产品埋点及 Runtime Agent 审计保持各自合同。`project.jsonl` 的完成 item 写入时间不等于 turn 起止或请求阶段计时;不据此补造旧历史耗时。本次不清理或迁移用户项目内的旧审计文件。
|
||||
- 维护依据:AGC 实施计划“Direct 历史、审计与耗时的现行边界”和“DirectProject Codex 原始历史与异常恢复”。不能以原审计方案或已退役测试为由恢复旧 writer。
|
||||
|
||||
## 2026-08-31 Direct 本轮附件只映射路径,不灌正文、不区别 GDD
|
||||
|
||||
- 背景:issue #212。首页附件已经复制到 `assets/uploads/` 并登记,但 Direct 首轮只把用户原文发给 Codex,原文件名不是磁盘路径,模型会另起一套玩法。
|
||||
- 决策:Home 与 Project 共用 `DirectCodexTurnAttachment`。有项目路径或导入状态时,只在发给 Codex 的 user prompt 末尾附有界 sidecar(原名 → 项目相对路径、类型、大小、状态);无路径且无状态时保持首页元数据文案。不灌正文、不强制读取、不按 GDD 开特例。做成游戏固定 prompt 不改,同一条 Direct 首轮附件链自动吃到 sidecar。jsonl 与工作台气泡仍只写用户原文。
|
||||
- 影响范围:`direct_codex_attachments.rs`、Direct command 边界、首页建项 latch、工作台首轮 invoke;Supervisor / 做方案首轮忽略附件 sidecar。
|
||||
- 验证方式:Rust 渲染测试(Home 逐字兼容、Project 映射、非法路径);home.suite 附件 Direct invoke 含 `localPath`;无附件不出现 `attachments` 键;做方案首轮仍走 Supervisor 且无 sidecar;后续手打消息不带 attachments。
|
||||
- 当前决策(2026-09-23 更新):原 sidecar 已由 canonical `userItem.content` 中的 `agc_attachment_reference` 替代;每项保留名称、媒体类型、大小、项目相对路径和状态,经 validation/wire 校验投影。不灌全文、不强制读取、不按 GDD 开特例。未注册的 DirectHome 命令及其专属附件 DTO、渲染、prompt key 已清理,首页先创建项目再进入 DirectProject。
|
||||
- 影响范围:`direct_codex_attachments.rs` 只保留现役附件清洗与数量边界,canonical user-item 深模块、首页建项与项目工作台继续使用现行结构化输入;历史按主实施计划的完整 canonical 条目合同记录。
|
||||
- 验证边界:保留 canonical validation/wire、附件路径与状态投影及首页创建项目测试;退役 Home/sidecar 专属测试一并清理,不要求恢复旧独立 attachments 参数。
|
||||
- 关联文档:`docs/technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md`、issue #212。
|
||||
|
||||
## 2026-08-26 运行中自主扩图提案留在编排层
|
||||
@@ -2752,7 +2884,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
## 2026-06-22 编辑器生成扣费与新用户赠送收口
|
||||
|
||||
- 背景:画板多个生成按钮已经展示泥点消耗,但部分图片、图标、UI 提取、视频、角色动作或音频链路只校验 / 展示价格,没有统一进入钱包预扣;新用户注册送泥点也需要与当前生成价格匹配。
|
||||
- 决策:编辑器所有外部生成入口不再从前端请求接收 `priceMudPoints`,后端按运行时模型定价配置计算价格后统一进入 `execute_billable_asset_operation_with_cost` 或等价音频发布扣费链路;角色动作和视频使用真实登录用户作为扣费 owner。新用户注册赠送固定为 `100` 泥点。
|
||||
- 决策:编辑器所有外部生成入口不再从前端请求接收 `priceMudPoints`,后端按运行时模型定价配置计算价格后统一进入 `execute_billable_asset_operation_with_cost` 或等价音频发布扣费链路;角色动作和视频使用真实登录用户作为扣费 owner。~~新用户注册赠送固定为 `100` 泥点。~~(2026-09-24 更正:注册赠送金额不是固定值,由线上 `profile_wallet_config.initial_mud_points` 配置决定,后台通过 `/admin/api/profile/wallet-config` 与钱包配置页随时调整;代码内常量仅为未写入配置时的兜底默认,本地编译行为不代表线上实际赠送金额,线上数值以配置表当前值为准。契约说明见 [`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`](../../【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md)。)
|
||||
- 影响范围:编辑器图片 / 图片修改 / 图标 spritesheet / UI 提取 / 视频 / 角色动作 / 音频生成 BFF,前端画板生成提交模型,外部 OpenAPI,`module-runtime` 钱包注册奖励。
|
||||
- 验证方式:运行编辑器图片、图标、UI 提取、视频、角色动作、音频扣费结构性测试,前端生成提交和 API client 测试,`module-runtime` 注册奖励测试。
|
||||
- 关联文档:`docs/【编辑器】模型定价配置管理方案-2026-06-22.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
|
||||
@@ -8445,7 +8577,7 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 恢复入口:通用恢复扫描与 Direct 回合启动前置恢复都必须发现 `resetting`、`compensating` 和带替换锚点的 `in-progress`,并在专用执行锁内清阶段、补偿和中性化。补偿恢复旧文件并清除本地 replacement CAS 锚点,但保留已 `prepared / accepted` 的阶段账本、原 `Idempotency-Key / operationId`;同冻结意图续跑必须复用原请求身份,未知账本在文件 mutation 前失败关闭。冻结意图一致时,新进程 invocation 可接管未完成阶段;`completed` 以原始外层 `clientTurnId` 等值回放,不受模型 brief 重采样影响。App 在 Direct 调用前幂等持久化原始 User 消息与稳定回合 ID,Tauri 在成功返回及 `completed` 事件前以同一回合 ID 幂等持久化 assistant 终态,项目重开只续跑最近一条真正未回答的合法原始回合。
|
||||
- 资源投影:工具返回主包路径、已登记切片路径、安全 `resources` 身份,并分开保留普通 warning 与 slice warning。标准核心图集首次创建和重生成都必须严格提交恰好四张 canonical 切片;alpha、可见像素、规范像素唯一、Canvas resource/asset identity 唯一任一不满足即失败。旧项目补登记与已有完整登记都必须由客户端私有回执交叉验证,不能把可编辑公开清单或顶层 manifest 中的自述身份单独升级为权威源;部分登记要么按私有回执事务补全,要么明确 warning。规范图只作 reference,不再计为运行态平台素材。
|
||||
- 隐私投影:成功结果中的普通 warning 与 slice warning 也必须逐条经过宿主路径、凭据、URL 脱敏及长度限制,不能只保护错误分支。
|
||||
- 权限边界:开放的是 `regenerate / registered resources / playtest` 等产品语义,不是原始最高权限。`regenerate` 只由当前请求最新一条原始 User 消息授权并绑定客户端稳定 `clientTurnId`;模型参数、MCP 自动批准和缺失 clientTurnId 都失败关闭。授权输入先对完整原文做 Unicode NFKC 与撇号规范化,随后整串必须完整匹配审核过的独立立即执行指令,只允许句号/感叹号收尾;不得剥离引号、方括号或代码片段,动作前后也不得携带 brief、条件、否定、选择、确认、费用、延迟或其它文本。复杂风格需求先单独描述,再由下一条独立确认消息授权,不能用开放式 deny 词表推断付费同意。同一进程重复水合相同 stable turn 时,“回合仍在运行”只作为非终态占用提示,不得以该 turn 的稳定 assistant messageId 持久化并覆盖原执行结果。DirectProject 的 cwd、sandbox writable root 与文件批准根只允许 canonical 且非 symlink/reparse point 的真实 `game/`,canonical 项目根的原生 OS 路径字节和权威 manifest `projectId` 经域标签及独立长度前缀编码后共同绑定连接池与 thread 身份;项目根、`assets/`、`.agent/` 不可写,网络关闭,命令、MCP 扩权和额外权限批准全部拒绝。受控 `agc_tools` 只在客户端内部从同一真实 `game/` cwd 反查已校验的 canonical 项目根,不把项目根加入 Codex writable roots。Codex 不获得任意 Tauri invoke、Token/Key/Cookie;`resources` 也只投影稳定身份与相对路径,不返回 prompt、provider route、URL 或绝对路径。
|
||||
- 权限边界:开放的是 `regenerate / registered resources / playtest` 等产品语义,不是原始最高权限。`regenerate` 的旧文本授权规则已由 2026-09-03 MCP 决策替代:Codex 根据当前用户请求显式选择工具模式,客户端不再用关键词、否定词表或独立确认句式判断业务意图;保留活动客户端回合、稳定 `clientTurnId`、首次 brief 摘要、项目权限、计费、幂等、锁及未知结果恢复边界。2026-09-23 清理了旧文本判断残留;此处其余旧沙箱描述按主实施计划后续 DirectProject 完整访问合同覆盖。同一进程重复水合相同 stable turn 时,“回合仍在运行”只作为非终态占用提示,不得以该 turn 的稳定 assistant messageId 持久化并覆盖原执行结果。DirectProject 的 cwd 与 AGC 项目身份根使用 canonical 项目根及权威 manifest `projectId`,进程 sandbox 和批准规则按主实施计划“DirectProject Codex 完整访问覆盖”;不再沿用此旧决策中的 game/ 唯一可写根、关闭网络或拒绝全部命令的描述。Codex 不获得任意 Tauri invoke、Token/Key/Cookie;`resources` 也只投影稳定身份与相对路径,不返回 prompt、provider route、URL 或绝对路径。
|
||||
- Direct 恢复 claim:同一 App 实例重复水合相同 stable turn 并收到“仍在运行”时,必须释放该 `projectPath + clientTurnId` 的恢复 claim,且不得写稳定 assistant 终态。后续显式刷新对话可按原身份重新读取或续跑;不新增无界自动重试。
|
||||
- 严格图集崩溃收口:workflow 在严格图集调用前先持久化 `strictSpritesheetPending` 并冻结底层严格事务覆盖的九项旧合同身份;旧路径可精确冻结为缺失。Provider 完成结果先绑定原 retained stage ledger。恢复在同一项目锁内对账严格事务;只有新九项合同、规范图/背景图替换锚点与 retained spritesheet result 三者一致才补写 `completed`,旧九项合同才允许补偿。旧合同判定、写 `compensating`、恢复两项素材与登记、回读和清锚点必须在同一项目锁内,重启已有 `compensating` 也重新判定;第三种混合、漂移或 foreign result 状态进入 reconciliation。不能在主图集与四切片已整体提交后仍按两文件 rollback 制造混合包;若中断前阶段告警尚未进入 durable completed result,恢复结果追加“原阶段告警无法完整重放”的明确 warning,不静默清空。
|
||||
- Direct 对话恢复从新到旧扫描全部合法 User 回合,遇到较新已回答回合继续向前,不得丢失更早未回答回合。成功返回时 Rust 已先持久化 assistant,前端冗余 append 失败也不得重跑 Provider;普通错误终态的显式 append 失败后,恢复 claim 必须保持到 React fallback writer 对同一稳定 assistant messageId 的写入明确成功或失败,不能在 writer 尚在途时按旧会话快照重跑。fallback 成功后释放 claim;fallback 失败时跳过该 writer 的无界迟到重试并释放 claim,后续显式重新加载对话才可复用原稳定 `clientTurnId`。终态收敛后删除 claim,避免长会话无界增长。
|
||||
@@ -9244,6 +9376,51 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 影响面:`apps/ai-game-creator-shell/src/features/{project-workspace/resourceReferences.ts,project-workspace/ResourceReferenceInput.tsx,resource-canvas/ResourceCanvasAssetGenerationPanelView.tsx,resource-canvas/resourceCanvasAssetGenerationTaskModel.ts,resource-canvas/resourceCanvasAssetGenerationReferenceModel.ts}`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx` 与对应 6 个定向测试文件。
|
||||
- 验证:定向 `resourceCanvasAssetGenerationReferences` / `resourceCanvasAssetGenerationBackgroundClose` / `resourceCanvasBottomToolbar` / `resourceCanvasGenerationFloatingPanel(Chrome)` / `resourceReferenceInput` / `resourceReferences` / `resourceCanvasAssetGenerationTasksPanel` 全绿;全量 `npm run test -- apps/ai-game-creator-shell/tests` 只剩 `clientHttp` / `clientApi` / `clientAuthStorage` / `projectCreationDirectory` / `recentProjectsHook` 五个 jsdom `localStorage` 环境用例红(与本次改动无调用关系);TS typecheck、`check:encoding`、`git diff --check` 通过。未复核真实客户端观感。
|
||||
|
||||
## 2026-09-18 UI 编辑器深模块 seam 收敛
|
||||
|
||||
- 决策:UI 编辑器的语义状态写入通过 React-free `stateTransition` seam;页面 hook 继续负责 React/history/lock adapter。保存前增加 `stateInvariants` projection,Rust 持久化规则仍是最终权威。
|
||||
- 决策:节点几何新增 State 级 `findStateNodePageContext`,页面选择、预览和状态迁移共享同一坐标递归入口;四类异步操作的 running/status 由 `operationLifecycle` adapter 承接。
|
||||
- 决策:结构化 LLM action 共用 `commands::utils::required_tool_arguments`,仅统一必需 tool-call 定位与有界 JSON 解析,不合并 prompt、schema 或 materializer。
|
||||
- 验证:AGC typecheck、UI State 定向 Vitest、编码检查、doc-index、diff 检查和 Tauri Rust fmt 通过;Tauri 全量 cargo check 仍受现有 platform-llm API 漂移错误阻断,与本次 UI editor 改动无关。
|
||||
|
||||
## 2026-09-23 UI 编辑器退役界面图参考语义建议
|
||||
|
||||
- 背景:UI 编辑器的“分析参考图”步骤只用一次 LLM 调用给界面图补 `name` / `description` / `role` / `slave_to`,四个字段又反过来决定结构识别的上下文分组、合并的树优先级和 Inspector 的可选项;这条链路的价值不足以支撑它引入的跨层耦合。
|
||||
- 决策:`commands/ui_design_suggestion.rs`、`suggest_ui_design_semantic` 命令与 `UIDesignImage.metadata`(含 `UIDesignImageRole`、`UIDesignImageMetadata`)整体退役,`UIDesignImage` 只剩 `path` / `pixel_size` / `pixels_per_unit`;前端从三步工作流收敛为“识别界面结构 / 自动切分素材”两步,`model.ts`、`WorkflowActionCard`、`WorkflowChecks`、完成通知、`InputSidebar`、`InspectorSidebar` 与 `ImportOverview` 同步删减。不保留兼容字段、回退路径、旧文档迁移或写回。
|
||||
- 决策:界面图之间不再有持久化关系,结构识别按“每张界面图各自一棵树、各自一个上下文”执行(删掉 `recognition_root_image_ids` / `slave_image_ids`);界面图显示名统一取 `path` 的 basename(复用 `view/project-development/resourceAssetDisplayName.ts`)。
|
||||
- 决策:多树合并暂时没有优先级来源(原优先级由 `slave_to` 祖先链计数得出),`merge` 现在把所有输入树优先级恒置 0 并留 `TODO`,合并冲突取 `merged_from` 首位成员;`ui-workflow.*` 阶段与页面级工作流不在本次范围,后续整体重写。
|
||||
- 代价与取舍:删掉 `name` / `description` 后界面图在 UI 上只能用文件名标识;合并冲突的代表节点选择不再有“优先级”依据;`role` / `slave_to` 曾承担的“主页面 + 子界面”语义彻底消失。旧 `ui_design.json` 里的 `metadata` 由 serde 默认忽略、下次保存后自然消失(`State` / `UIDesignImage` 都没有 `deny_unknown_fields`),已生成的 `ui_trees` 不受影响。
|
||||
- 影响面:`apps/ai-game-creator-shell/src-tauri/src/{main.rs,ui_editor/**}`、`src/features/ui-editor/**`、`src/view/ui-editor/**`、`src/view/project-development/index.tsx`、`tests/{uiEditorPage,uiEditorState,previewWorkspaceZoom}.test.*`、`docs/technical/【技术方案】UI编辑器代码地图与模块职责-2026-09-23.md`、`docs/【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md`、`docs/technical/【设计】UI编辑器工作流完成通知弹窗-2026-09-04.md`、`docs/technical/【前端架构】UI编辑会话模块边界-2026-08-19.md`。
|
||||
- 验证:`cargo check` 与 `cargo test --bin genarrative-ai-game-creator-shell ui_editor`(160 passed)通过,ts-rs 重新导出 `types/UIDesignImage.ts` 并删除三个已退役类型;`npx vitest run` 定向 `uiEditorPage` / `uiEditorState` / `uiDesignStateStore` / `previewWorkspaceZoom` / `appSurface` 全绿;AGC `tsc --noEmit`、改动文件 eslint、`cargo fmt --check`、`check:encoding`、`git diff --check` 通过。整套 Rust 测试在本容器仍有 60 条环境性失败(`/sbin -> usr/bin` 让 `command.exec` 沙箱 merged-usr 预检失败),与本次改动无关。
|
||||
|
||||
## 2026-09-24 UI 设计文档三个工具的持久回执明细与入参摘要改为可读白名单
|
||||
|
||||
- 背景:动作回执的"安全明细"是一份白名单——只有认识的工具才把明细整理成安全字段。`ui.workflow.run` 退役时删掉了它那段分支,接管的 `ui-design-doc.*` 三个工具没补,于是统一落到兜底:明细整块变成 `detailUnavailable`,模型与审计都看不到;入参那一栏也只剩哈希。原始明细既不落盘也不给模型,所以**没有宿主路径泄露**,丢的是可用性(例如切分没登记上的素材清单)。
|
||||
- 决策:按既有白名单口径给三个工具各补一条分支。`from-images` 放 `assetId` / `relativePath` / `imageIds` / `revisionAdvanceCount`;`run-workflow` 再放文档 `revision`、恢复标记、识别与绑定计数、`backfillErrors` 与总数;`into-js` 只放相对路径与计数(导出名清单只用来核对数量,不逐项外传)。
|
||||
- 决策(校验口径):身份字段走 `agent_runtime_action_receipt_identity_text`(禁控制字符、限长、含 file URI 或绝对路径即失败关闭);相对路径必须归一化后落在 `ui/` 下;回填说明是自由文本,按既有口径把绝对路径脱敏成占位符,含控制字符或超长则整条明细失败关闭。
|
||||
- 决策(体积):回执明细总长仍受 `AGENT_RUNTIME_ACTION_RECEIPT_SAFE_DETAIL_MAX_CHARS`(500)约束。设计图身份与回填说明按长度上限能放多少放多少,放不下的部分用 `backfillErrorCount` 表达总数;必需字段本身就超限时整条明细不可用。
|
||||
- 决策(入参摘要):`agent_runtime_tool_action_input_summary` 为三个工具产出可读摘要(设计图逐张身份或目标文档 id),并把工具名加进 `agent_runtime_public_action_input_summary` 的可读名单,不再退化成只报哈希。
|
||||
- 验证方式:`tests::runtime_actions::action_execution::ui_design_doc_receipts_*` 两条用例(三个工具都能留下可用明细且不超长;宿主路径被脱敏;`ui/` 之外与宿主路径身份失败关闭)与 `action_audit::ui_design_doc_public_input_summary_tests` 两条用例;`cargo test --bin genarrative-ai-game-creator-shell receipt` 55 条全绿。
|
||||
|
||||
## 2026-09-24 UI 设计文档三个工具纳入项目变更门禁(成功返回即算改过项目)
|
||||
|
||||
- 背景:`ui.workflow.run` 退役时,项目变更门禁的两处工具名单(`agent/runtime_actions/project_gates.rs` 的 `is_agent_runtime_project_mutation_observation` 与 `agent_runtime_observation_advances_project_revision`)只删未补,新接管的 `ui-design-doc.from-images` / `ui-design-doc.run-workflow` / `ui-design-doc.into-js` 都没登记。后果:改完项目可能被判定"没改过",于是不要求验证就判完成、自动模式的 liveness 判据看不到进展;`agent_runtime_pending_expected_project_revision` 少算推进量又会误报 `pending_project_revision_drift`("并行项目变更使旧动作过期")。
|
||||
- 口径(产品确认):三个工具都在**成功返回时**改项目——`from-images` 登记设计图与文档、`run-workflow` 登记切图并保存文档、`into-js` 重写 `ui/generated-*.js`;调用中途不产生需要门禁额外追踪的中间态。
|
||||
- 决策:门禁按"工具名 + 成功返回"判定三者都算项目变更;其中 `into-js` 只重写派生产物、**不推进 revision**,所以不进 revision 推进名单——进去会虚报推进量,正好把要修的误报再造出来。
|
||||
- 决策(真实推进量):`from-images` / `run-workflow` 的观察明细带上 `revisionAdvanceCount`(沿用 `canvas.asset_import` 的既有字段),失败路径也带——切图素材先登记、后面步骤才失败时按约定不回滚,仍要如实计数,避免门禁把真实推进当成"别处改动"。
|
||||
- 代价与取舍:失败路径要多读一次项目 revision;`into-js` 属于"改了东西但项目 revision 没动"的少数派,与写文件类工具口径一致。
|
||||
- 验证方式:新增 `project_gates::ui_design_doc_project_mutation_gate_tests` 六条用例(成功即算变更、`into-js` 不推进 revision、登记类兜底推进量为 1、明细里的真实推进量优先、失败但已推进才算、无关工具不受影响)。
|
||||
|
||||
## 2026-09-23 UI 编辑器 Agent 工具化重写
|
||||
|
||||
- 背景:UI 编辑器的 Agent 链路原本只有一个 `ui.workflow.run`,把发现页面、桥接设计图、结构识别、多树合并、组件绑定、finalize 全塞进一个工具,工具参数本身就是工作流状态;识别与切分的产物由前端 `useUiEditorPage.ts` 落 State 再保存,Agent 侧没有任何恢复点,任一步失败只能整轮重来。
|
||||
- 决策:拆成三个各自只做一件事的工具——`ui-design-doc.from-images`(一至四张设计图新建并登记文档,返回 `assetId` 与 `relativePath`)、`ui-design-doc.run-workflow`(`recognize → separate → write-back`)、`ui-design-doc.into-js`(渲染 `ui/generated-<stem>-<digest>.js`,不推进 revision)。只有 `run-workflow` 带崩溃恢复,粒度到子步骤。
|
||||
- 决策(检查点):恢复判据只有「这一步有没有对应、且带着 State 快照的检查点行」。检查点是文档旁追加式 JSONL `ui/.<文档名>-workflow.jsonl`(不进 manifest、不推进 revision),行类型 `run` / `recognize` / `separate` / `write-back` / `outdated`;`run` 行带这一轮开始时的 State 快照,`recognize` / `separate` 行带该步应用完之后的 State 快照。恢复只读快照:逐级取回已完成步骤留下的 State,只把新完成那一步的改动应用到文档,已完成步骤整步跳过、绝不照 DTO 重跑(重跑会重复登记切图、把同一条回填出错原因重复报一遍)。只有 DTO、没有 State 快照的旧行按未完成处理,由主流程重跑该步。一轮以 `run` 开头、以首个 `write-back` 或 `outdated` 结束,只有最后一轮没有结束行时才恢复。追加前截断崩溃留下的半行;文档中途漂移(当前 State 既不是 `run` 行快照、也不是切分后那份快照)时追加 `outdated` 并返回错误,由下一次调用显式开新一轮,不在同一次调用里自动重启;写回按「切分后那份 State 快照与文档当前 State 相等」判幂等并补 `write-back` 行。
|
||||
- 决策(边界):文档内设计图身份直接采用该图在 manifest 里的 `assetId`,输入给相对路径时先登记再用它的 `assetId`;每次调用都新建文档,不做「原型 → 已存在文档」的幂等查找。切图资源失败不回滚,重放靠 by-path 复用接上;切分 op 内部更细粒度的恢复仍由 `SeparationState` sidecar 承担,日志不复制它的进度。前端 `useUiEditorPage.ts` 的人工链路保留同语义,Rust 只是第二份实现,不把编排搬进 Rust。
|
||||
- 退役:`ui.workflow.run`、`ui_editor/commands/{merge.rs,binding.rs}`、`ui_editor/workflow.rs`、`ensure_ui_design_resource_for_prototype`、`ui/ui-workflow-<sha256前24>.json` 命名与 `ui-workflow.*` manifest 阶段全部删除,不保留迁移、兼容与 fallback。
|
||||
- 代价与取舍:不接受跨语言 fixture 比对(四个 seam 都是简单变换,靠同语义实现与各自单测覆盖);`run-workflow` 每次调用都推进一轮,调用方重复调用会重新识别而不是被幂等短路(除「保存成功但缺 `write-back` 行」这一种重放)。工具名 `ui-design-doc.*` 含连字符,Function Calling 的函数名归一同时处理 `.` 与 `-`。
|
||||
- 验证方式:`cargo test --bin genarrative-ai-game-creator-shell agent_tools` 覆盖检查点、识别/切分镜像与切图登记;`ui_design_doc` 与 `native_ui_design_doc_tools` 用例覆盖工具入参和函数名;`npm run check:encoding`、`npm run check:doc-index`、`git diff --check` 通过。整套 Rust 用例在本容器仍有 9 条既有环境性失败(本地 HTTP 资源编辑器与 LLM 超时,改动前同样失败)。
|
||||
|
||||
## 2026-09-22 运行页收口:过程提示退出对话区,顶栏统一承载运行入口
|
||||
|
||||
- 背景:点播放(以及历史上 `/preview`、`/open-preview`、生成后自动启动预览)都会往对话区写一条 assistant 提示(`运行通过,已载入客户端运行视图:http://127.0.0.1:63155/` 这类)。它常驻对话底部遮挡运行画面,也让对话区混进非对话内容;运行页本身还有三处遮挡与两套皮:预览地址是一行常驻小字(不可点)、版本入口是绝对定位压在画面右上角的浮层、右上角还叠着「生成任务」开关。
|
||||
@@ -9273,14 +9450,52 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 边界(A 仍未修):`DirectProjectTurnUsage` 的 `Math.max(turn.endedAt, turn.startedAt)` 兜底没动,所以两类 `finished` 回合仍显示「耗时 0.0秒」——① 页面重进后读回来的历史回合(`turnEndedAt` 只是会话内展示缓存);② 发送后没有产生任何原生事件 / 发送失败的本地回合。为什么会有这两类、修法与要产品确认的口径都写在代码里(`DirectProjectTurn.tsx` 的 `DirectProjectTurnUsage` 注释与 `directTurnPresentation.ts` 的 `DirectChatTurnState` 注释),改完删掉那段注释。
|
||||
- 验证:`tests/directProjectTurn.test.tsx`(新增 3 条渲染契约:`awaiting-start` 与 `running` 不显示终态文案且不折叠、`finished` 有终态时显示结束时间与耗时);`tests/appSurface/chat-composer.suite.ts` 新增 `does not report a finished turn while the host has not acknowledged the send yet`(invoke 挂起、无任何原生事件时断言不出现「本轮结束于」);变异验证:把 `state !== 'finished'` 退回 `state === 'running'` 后渲染契约用例变红,恢复即绿。定向 vitest、`appSurface.test.ts`(203 passed / 13 skipped)、`tsc`、ESLint、Prettier、`check:encoding`、`check:doc-index`、`git diff --check` 通过。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-22 宿主崩掉不再留下永远开着的回合:本地命令失败时按身份兜底收口
|
||||
## 2026-09-22 失败回合的终态:`turn.completed` 带 `failure` 载荷 + 宿主 Drop 守卫兜底
|
||||
|
||||
- 背景:`turn.started` / `turn.completed` 是原生回合唯一的开闭配对,界面上的「正在处理」卡片与输入盒忙态都读 reducer 的 `turnRunning`。但 app-server 崩了、回合任务被中止或 panic 时没人补终态事件,事件流里就留一条永远开着的 `turn.started`:界面一直显示「陶泥儿正在处理」、输入盒一直排队(用户现场反馈)。
|
||||
- 决策(本地命令返回即这一轮在宿主那边收场):`chat_with_game_creator_direct_codex` 以真失败返回时,controller 按本轮身份调用 `stopDirectThreadTurn`,只放掉「是否在跑」,**不写终态时间**——命令返回不等于知道这一轮真正的结束时刻,编一个只会让耗时变成假数。用户主动终止与「正在跑的是另一轮」两条不适用:前者宿主必然补终态,后者不是这一轮(不能顺手抹掉别人的回合)。
|
||||
- 决策(身份作用域 + 不复活):`stopDirectThreadTurn` 只在 reducer 里的运行身份相同或为空时生效;收口记进 `commandClosedTurnUserItemId`,同身份迟到的 `turn.started` 不再把这一轮拉回运行态(迟到的 `turn.completed` 例外放行,仍要拿它补上真正的结束时间)。身份按 clientTurnId 唯一,所以这条记忆只挡它自己那一轮。
|
||||
- 影响面:`apps/ai-game-creator-shell/src/view/project-development/chat/{conversation/directThreadChat.ts,controller/useDirectThreadChatSubscription.ts,controller/useDirectProjectChatController.ts}` 与 `apps/ai-game-creator-shell/tests/{directThreadChat.test.ts,appSurface/chat-composer.suite.ts}`。
|
||||
- 验证:reducer 新增 2 条用例(兜底收口后同名 `turn.started` 不复活且真终态仍能补上结束时间;身份不同的回合不动),appSurface 新增 `stops claiming the turn is running when a failed send left turn.started open`;变异验证:拿掉 controller 里的兜底收口调用后该用例变红(界面仍显示「陶泥儿正在处理」),恢复即绿。
|
||||
- 边界(未做):根因仍在宿主侧——要在进程内保证开闭配对,应由 Rust 在回合函数退出(含 panic / 任务中止)时补一条终态事件(drop 守卫);本次只做到前端不再跟着说谎。另:兜底收口的回合没有终态时间,仍会落进「`finished` 但拿不到终态时间」那个已知缺口(终态文案要不要藏,见 `DirectProjectTurn.tsx` 与 `DirectChatTurnState` 注释里的 A 项)。
|
||||
- 背景:宿主崩在 `turn.started` 之后时没有任何终态事件,前端 `turnRunning` 永远为真,界面停在「陶泥儿正在处理」;同时失败在事件流里与正常结束同形(`turn.completed(status="failed")`,前端根本不读 `status`),失败文案只能从命令返回那条通道另造,同一次失败因此有两条通道、两份文案,而"这一轮结束了没有"只有事件说了算。
|
||||
- 决策(协议形状:复用,不新增事件类型):终态事件仍只有 `turn.completed`。`status !== "failed"` 表示正常结束 / 中断 / 终止,不带载荷;`status === "failed"` 是失败终态,**必须**带 `failure { kind, message }` —— `kind` 为稳定分类(`timeout` / `model-failed` / `transport-failed` / `request-rejected` / `host-dropped`,只给界面选语气;**2026-09-23 追加 `environment-not-ready`**),`message` 为宿主脱敏 + 截断后的可展示原因。"是不是失败"只看两件事:`collect_result` 是 Err 就用错误本身当原因;`collect_result` 是交付报告但状态已判成 `failed` 就用那份报告当原因。
|
||||
- 决策(兜底覆盖全部收场路径):`turn.started` 进入队列之后武装 Drop 守卫,正常写完终态即解除;panic、回合 future 被丢弃、终态之前的早退由守卫补一条 `host-dropped` 失败终态。Thread Manager 不改一行:`turn.completed` 本来就是 `lifecycle_anchor` 成员,失败终态天然顶替更早的 `turn.started`,重放不会把已收口的回合看成"还在跑"。(**已由 2026-09-23「DirectProject 命令接单化」取代**:守卫换成接单时登记的占用对象,接单前的早退改判为拒单、不再产生回合,`host-dropped` 只保留给"说不出原因"的一类。)
|
||||
- 决策(失败文案只有一条通道):聊天里那条失败说明仍落在原来的展示位(本轮最后一条助手气泡、只在运行期显示、不写进 `project.jsonl`),数据来源换成事件载荷;命令返回只保留运行错误横幅(含 `read_agent_runtime_error_detail` 的长 detail)与诊断留痕,不再写聊天气泡。可见文案映射仍走既有 `projectRuntimeVisibleError` 规则,只是执行点从 controller 移到 reducer。
|
||||
- 明确不做:不为 `turn.started` 之前的早退(`turn/start` 请求失败、响应缺 `turn.id`、缺稳定 `clientTurnId`、历史注入参数构建失败)补事件或兜底路径 —— 它们不产生回合、也不会留下永远开着的回合;不为进程被强杀(`kill -9`)补前端判据。(**已由 2026-09-23「DirectProject 命令接单化」取代**:接单前的失败改判为拒单、由命令边界返回 typed 错误;接单后的这类失败由占用对象收口成 `turn.completed`;`kill -9` 的界面表现在新 ADR §2。)
|
||||
- 同日被取代的还有本条目里的另一条:命令返回只保留"运行错误横幅 + 长 detail"——`详情:` 引用与 `read_agent_runtime_error_detail` 已删除,失败说明也不再落命令边界(改由宿主投影写事件载荷与诊断池)。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/{direct_thread_wire.rs,direct_turn_failure.rs,direct_thread_manager.rs,codex_app_server/mod.rs}`、`apps/ai-game-creator-shell/src/view/project-development/chat/{conversation/directThreadChat.ts,conversation/directTurnFailure.ts,controller/useDirectProjectChatController.ts}`、生成绑定与两侧用例;文档 `docs/adr/【ADR】DirectProject对话历史单一事实源-2026-09-16.md` 与 `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`。
|
||||
- 验证:见本条决策对应的提交记录(Rust 定向测试、reducer 与 appSurface 用例、`cargo test export_bindings` 后的生成绑定、`npm run check:encoding`、`git diff --check`)。
|
||||
|
||||
## 2026-09-22 执行通道断开也是失败终态:诊断记在执行适配器上,不与看门狗抢时序
|
||||
|
||||
- 背景:手工杀掉 codex app-server(`kill -9`)验证上一条修复时,回合确实收口了(界面不再停在"还在处理"),但**没有任何失败说明**:连接级故障走的是 `TransportClosed` 分支,那里用一句策略文案 `adapter.interrupt(...)` 收束成 `ExecutionPhase::Interrupted`,`lifecycle_status` 把 `Interrupted` 映射成 `status="interrupted"`,`direct_turn_failure` 因此返回 `None`,事件不带载荷、reducer 也就不落说明条目;真实诊断(`Codex app-server 已退出;exitStatus=signal: 9 (SIGKILL);stderrClass=...`)只进了 `app_log!`。
|
||||
- 决策(失败事实记在执行适配器上):新增 `ExecutionAdapter::transport_failed(diagnostic)` 与只读的 `transport_failure()`。连接级故障(`fail_game_creator_codex_app_server_connection`)与回合事件通道关闭(`TransportClosed` / 事件通道 `None`)都调它:先同步记下"本轮以传输失败收口"与原因,再把同一份原因补进宿主交付报告(`interrupt` 对已有终态不覆盖,报告只作旁证)。`lifecycle_status` 见到这条事实一律返回 `failed`,`direct_turn_failure` 因此产出 `kind="transport-failed"`、`message=诊断` 的载荷。原因不能存在调用点局部变量里:执行适配器的看门狗盯着同一个 `inner.closed` 标志,它可能先把回合收束成 `Interrupted`,而终态判定发生在收束之后。
|
||||
- 决策(区分"连接自己断了"与"宿主关的连接",判据收在适配器里):`transport_failed` 先看 `is_closed`——宿主自己收束(正常终态 / 用户主动停止 / 预算与交付收尾)时适配器先于连接置位 `closed`,那种情况下只按既有口径中断收口(原因照样写进报告),不记失败事实;调用点两条分支的判据保持原样(`!is_host_ending()`),不动它们的控制流。
|
||||
- 决策(载荷取诊断而不是交付报告):`direct_turn_failure` 增加第三来源且优先级最高——通道断开时原因用宿主诊断(含 `exitStatus` / stderr 摘要),不用 `collect_result` 里那份只说"收束到哪一步"的交付报告;报告与载荷同源的说法只对"原因写进报告"这一步成立,事件载荷才是失败原因的唯一权威。
|
||||
- 明确不做:不改前端可见文案映射(诊断命中不了专门规则,仍落到通用兜底文案);不给连接级故障补端到端集成用例(判据落在策略函数与适配器两层单测,DirectProject 回合路径缺轻型假 app-server 夹具);`kill -9` 掉宿主进程本身仍没有 `Drop`,不在本次范围。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/{mod.rs,execution.rs}`、`apps/ai-game-creator-shell/src-tauri/src/agent/direct_turn_failure.rs` 与其单测;文档 `docs/adr/【ADR】DirectProject对话历史单一事实源-2026-09-16.md` 与 `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`。
|
||||
- 验证:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml direct_`(439 passed)、定向 `transport_failure` / `direct_turn_failure::`(10 passed,含新增三条:适配器把诊断记成失败终态且只认第一份原因、宿主自己关的连接不算失败、失败载荷优先取宿主诊断)、`cargo fmt --check`、`npm run check:encoding`、`git diff --check`。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-23 终态由事实判定:模型自报失败的原生错误投影进既有错误通道
|
||||
|
||||
- 背景:app-server 老实报了 `turn/completed{status:"failed", error:{message, additionalDetails, codexErrorInfo}}`,界面却没有任何原因。两条通道同时哑:① 事件侧 `lifecycle_status` 拿收尾阶段当终态口径(执行适配器 `drain()` → `interrupt()` 把 ledger 推成 `Interrupted`),把模型报的 `failed` 改写成 `interrupted`,失败载荷因此永远产不出来;② 命令侧有执行许可时 `finish_model_attempt` 先返回交付报告,命令变成 Ok,运行错误横幅的前提也消失。原生 `turn.error`(带 `codexErrorInfo` 分类,前端 `projectRuntimeVisibleError` 有现成中文映射表)只在"没有执行许可"的 `Err` 分支里被读一次。
|
||||
- 决策(终态由事实判定,有载荷必 `failed`):`direct_turn_terminal` 去掉 `model_status` 入参,判定按「宿主当场记下的失败(通道断开 / 等待超时 / app-server 单方面中断)→ 本回合的错误结果是 Err → 只有收尾阶段账本读不出来时才用交付报告」取原因;**有载荷一定写 `status="failed"`**,没载荷才用收尾阶段推出来的 `status`。收尾阶段的中断不再有终态否决权——这是本次修复的根因。
|
||||
- 决策(原生失败用投影,不扩载荷、不加入参):`turn.error` 由既有的 `game_creator_codex_app_server_failed_turn_error` 投影成 `LlmError`,在 `"failed"` 分支里作为本回合的错误结果返回,于是走已有的错误槽位产出 `{kind, message}` 载荷;`RepairRequired`(返修请求)保持原语义优先。前端零改动——`projectRuntimeVisibleError` 现有映射直接命中 `codex-app-server-error:<kind>`;命令返回同时回到 Err,运行错误横幅恢复。
|
||||
- 明确不做:不新增事件类型、不给失败载荷加字段、不改前端可见文案映射;不给回合路径补轻型假 app-server 夹具(判据落在 `direct_turn_terminal` 与执行适配器两层单测)。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/{mod.rs,execution.rs}`、`apps/ai-game-creator-shell/src-tauri/src/agent/direct_turn_failure.rs` 与其单测;文档 `docs/adr/【ADR】DirectProject对话历史单一事实源-2026-09-16.md` 与 `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`。
|
||||
- 验证:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bins direct_`(442 passed)、`codex_app_server`(100 passed)、`cargo fmt --check`、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-23 DirectProject 命令接单化:命令只接单,逻辑回合归 Thread Manager
|
||||
|
||||
- 背景:`chat_with_game_creator_direct_codex` 一个命令调用覆盖整轮(校验 → 跑 → 交付验证),于是命令边界同时兼职"接单被拒"与"回合失败"两种回执:失败文案有事件载荷与命令 Err 两条来源,`withDirectCodexSessionRefresh` 的"刷新会话 + 重跑整轮"会重复落盘一条用户消息,认证重试只能挂在 Err 上;而回合边界又镜像 Codex 原生回合(`turn/start` 成功应答后才发 `turn.started`),"接单到 `turn/start` 之间"的失败(连不上 app-server、配置未就绪、历史注入失败、`turn/start` 被拒)没有任何事件可解释,命令一旦不 await 就会静默。
|
||||
- 决策(命令 = 接单 / 拒单):命令只做 `clientTurnId` 校验 → 占用调用身份(只挡并发,早于工程准备)→ 工作流恢复 → 用户条目校验 → 工程准备 → 接单成立 → 用户条目落盘 → 起 codex,成功后立刻返回、不等回合。命令返回类型改成结构化的 `DirectTurnError`(ts-rs 已导出到 `chat/generated/`,与 `DirectThreadEvent` 同一套 `cargo test export_bindings` 流程)。
|
||||
- 决策(分流判据从"错误种类"改成"发生位置"):**接单之前**的失败(目录、权限、输入、并发、工程准备未就绪、宿主状态取不到)是拒单——不产生回合事件、不写用户条目、不写失败诊断;**接单之后**的失败(连接、配置、历史注入、`turn/start` 被拒以及回合过程中的一切)是回合失败,只走 `turn.completed` 带 `failure` 载荷一条通道。`DirectTurnError::EnvironmentNotReady` 接单前后都可能出现,因此新增自己的失败分类 `environment-not-ready`(否则投影会写成 `model-failed`、界面语气就错了)。这条位置判据取代原先"调用级拒绝直通"的分支。
|
||||
- 决策(逻辑回合由 Thread Manager 拥有):新模块 `agent/direct_turn_accept.rs` 按 thread 维护占用登记,`accept(thread, user_item_id, client_turn_id)` 在同一个临界区里完成"拒绝并发 + 登记占用 + 追加逻辑回合开始事件";`DirectTurnReservation::finish(terminal)` 幂等写出 `turn.completed` 并释放占用,`Drop` 兜底补 `host-dropped` 终态。发点在接单时、不再镜像 Codex 原生回合(原生事件留在适配器内部,不再进事件队列),线上仍只有一对 `turn.started` / `turn.completed`。因此"接单成功 ⇔ 事件流里有开始且有结束"是结构性成立,不依赖实现者给每条早退路径补事件。并发锁与首页快照的口径:`DirectTaonierActiveInvocation` 退回纯单飞锁,首页"运行中的项目"改由 `list_direct_active_turns` 从 TM 的逻辑回合导出(`ActiveDirectTurn` 带快照字段,`DirectActiveTurnSnapshot` 移入 `direct_thread_manager.rs`),不再留两处事实。
|
||||
- 决策(回合身份由 `clientTurnId` 推导):`turn.started` / `turn.completed` 的 `userItemId` 按 `direct-codex:{clientTurnId}:user` 算出(与前端 `directCodexConversationMessageId` 同规则),**不读盘回填**——开始事件发生在用户条目落盘之前,落盘本身也可能失败。
|
||||
- 决策(界面:同级提示、删除 `详情:`):失败说明与接单被拒提示都与用户消息**同级**、按事件顺序排在它后面,不嵌在这条用户消息里;删掉 `详情:` 引用、它的正则解析与只服务详情展开的第二次 IPC `read_agent_runtime_error_detail`;顶部状态行只显示回合状态,不承载错误文本。前端按 typed 变体分流:认得的"前置条件不满足 / 用户参数无效"(`clientTurnIdMissing` / `clientTurnIdMalformed` / `turnAlreadyRunning` / `projectRootUnanchored` / `projectRootUnusable` / `permissionRejected` / `inputRejected` / `contentEmpty`)→ 出同级提示、不走 `captureAgentRuntimeError`;认不出的变体(`environmentNotReady` / `hostStateUnavailable`)以及非结构化错误 → 抛出,走既有捕获上报链路。
|
||||
- 决策(队列与埋点听回合终态,不听命令返回):前端发送队列的放行改由"回合完成(终态事件)或接单被拒"驱动,reducer 新增 `completedTurnCount` 作为唯一判据——不能用 `turnRunning` 的下降沿,一轮可能同批开始 + 结束。埋点结算同样挂到回合终态:不能在接单返回时结算,成绩是回合末才入 `pending_runs`,提前结算会变成空操作;`runTurn` 返回"是否接单",接单失败的路径只清句柄、不结算。**加 TODO:这条队列以后挪到 Rust 端,落点就是 Thread Manager 的接单动作。** 首页"运行中的项目"快照改由 TM 的逻辑回合导出,任务侧不再单独维护一张表。
|
||||
- 决策(认证失败不再重跑整轮):删掉 `withDirectCodexSessionRefresh` 的"刷新 + 重跑整轮"(重跑会重复落盘用户消息),登录态失效按普通回合失败呈现;用同一个包装的 `cancel_direct_codex_turn` 一并去掉。
|
||||
- 决策(失败原因本轮不落历史):失败原因只走事件载荷与宿主诊断(`.agent/runtime/errors` + 应用日志 + 错误上报池由宿主投影写出,进池责任从前端 catch 移到宿主),不写进 `project.jsonl`——重进项目只会看到那条没有回复的用户消息。**加 TODO(暂定做法见 ADR 备选方案第 3 条 (b)):以后要做"进历史但不喂模型"的失败条目,本轮明确不持久化。**
|
||||
- 决策(CLI 保持 await):CLI 入口(`cli.rs` 的 `direct-codex.chat`)继续 await 整轮,因为它要把回复文本打到终端、没有事件订阅可用;两个入口共用同一份接单前检查、同一个命令主体和同一份 `Display` 文案,不各写一套判据。
|
||||
- 明确不做:不给失败载荷加字段(不加 `detailRef`);不恢复 invoke 拒绝通道,也不为"接单后的前置失败"新增事件类型;本轮不做"失败条目进历史但不喂模型"(TODO)、不做 Rust 端发送队列(TODO)。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/{direct_turn_accept.rs,direct_thread_manager.rs,direct_thread_wire.rs,direct_turn_error.rs,direct_turn_failure.rs,direct_project_context.rs,direct_runtime/{mod.rs,user_input.rs},codex_app_server/mod.rs,runtime_driver/entrypoints.rs,cli.rs}`、前端 `chat/{controller/useDirectProjectChatController.ts,controller/useDirectProjectTurnStatus.ts,controller/useDirectThreadChatSubscription.ts,conversation/directCodexConversation.ts,conversation/directThreadChat.ts,conversation/directTurnPresentation.ts}`、`chat/generated/{DirectTurnError,DirectTurnRejection,DirectThreadEvent,...}.ts` 与 `tests/{directThreadChat.test.ts,appSurface/*.suite.ts}`;文档 `docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`、`docs/technical/【实施计划】DirectProject命令接单化-2026-09-23.md`、`docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md` 与 `docs/adr/【ADR】DirectProject对话历史单一事实源-2026-09-16.md`。
|
||||
- 已知坑:`cargo test export_bindings` 会重写全部 `chat/generated/`(引号风格漂移),跑完要 `git checkout --` 掉不是本次新增的文件;本机 rust 全量 `--bins` 测试会挂在 mock server 的 `inet_csk_accept` 上,用 `--bins "agent::"` 之类过滤跑。
|
||||
- 验证:Rust 定向 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bins "agent::"`(949 passed);前端 `NODE_OPTIONS=--localstorage-file=/tmp/ls-gen.json npm test`(4473 passed);`npm run ai-game-creator-shell:typecheck`、`cargo fmt --check`、`npm run check:encoding`、`git diff --check` 通过。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-23 后台 Dashboard「消耗泥点」改为对冲退还后的净消耗
|
||||
|
||||
@@ -9337,3 +9552,66 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 背景:`docs/project-memory/todos/【待办】引用输入区后续收口-2026-09-24.md` 第 2 条曾把「`resource-reference-*` 类名重命名」列为待做的机械提交,理由是「与 `RESOURCE_REFERENCE_OVERLAY_SELECTOR` 同批改名,避免选择器与类名短暂不一致」。
|
||||
- 审计:选择器常量正好是 `.resource-reference-picker, .resource-reference-menu`,指向的就是这两个组件渲染的类;其余类名也逐条与归属组件同名(`ResourceReferenceInput` / `ResourceReferencePicker` / `ResourceReferenceChip`)。规模为 80 条规则(`styles.css` 78 + `resourceCanvasChrome.css` 2)、17 个文件、32 处测试断言。
|
||||
- 决策:不改名。没有现役调用方、公开契约或样式隔离问题需要靠改名解决,纯改名只增加回归面;等出现新的命名口径时,再与选择器常量、样式表与断言一次性同批改。该条以「接受现状」关闭,其余两条(归一化撞名的候选菜单提示、真机手感验收)仍开放。
|
||||
## 2026-09-23 游戏发行入口改为服务端按模板派生:管理员不再手填地址
|
||||
|
||||
- 背景:游戏审核通过要求管理员手填绝对 HTTPS `entryUrl`,现场出现「不知道该填什么、随手填一个外部站点也能过校验」的风险;而每游戏独立来源本身完全能由 gameId 推出,人工输入没有增加任何判断。
|
||||
- 决策(唯一口径):部署侧用 `GENARRATIVE_GAME_DISTRIBUTION_RELEASE_ENTRY_TEMPLATE` 配置带 `{gameId}` 占位符的模板(生产形如 `https://{gameId}.games.<发行域名>/`);审核通过时 `api-server` 读版本取 gameId、替换模板、再走原有绝对 HTTPS / 无凭据 / 无 query / 无 fragment 校验后写入公开投影。后台审核请求 DTO 删除 `entryUrl`,页面不再渲染输入框,也不要求二次确认。
|
||||
- 决策(失败关闭):模板缺 `{gameId}`、生产未配置模板、gameId 含非主机安全字符或派生结果非法时,审核通过直接失败,不回落主站、内网或任意外部地址;非生产未配置模板时回落 `http://127.0.0.1:<bind_port>/api/game-distribution/releases/{gameId}/`,保持免 TLS 的本地内嵌游玩验证。
|
||||
- 边界:`entryUrl` 仍是公开投影字段,只是改由服务端写入;审核请求摘要不再包含它,表结构与版本回读不变;模板变更只影响之后新通过审核的版本,历史版本已冻结的 `entry_url` 不改写。
|
||||
- 影响面:`server-rs/crates/api-server/src/{config.rs,modules/game_distribution.rs}`、`apps/admin-web/src/{api/adminApiTypes.ts,api/adminApiClient.test.ts,pages/AdminGameDistributionReviewPage.tsx,pages/AdminGameDistributionReviewPage.test.tsx}`、`scripts/check-game-distribution-media-e2e.mjs`、`deploy/{nginx,env,container}`、平台与运维主规范、发行里程碑实施计划。
|
||||
- 验证:`cargo check -p api-server`、`cargo test -p api-server game_distribution`(31 passed)、admin-web 定向 Vitest(19 passed)与 `apps/admin-web` typecheck、`npm run check:release-origin-config`、`npm run check:doc-index`、`npm run check:encoding`、`git diff --check` 全部通过;真实栈端到端(真实 OSS + SpacetimeDB + 审核通过)未在本轮复跑。
|
||||
|
||||
## 2026-09-24 游戏发行入口改为平台同源路径:取消发行域名与部署模板变量
|
||||
|
||||
- 背景:每游戏独立来源要求 `*.games.<域名>` 通配 DNS 与通配 TLS,一直未在任何环境落地,dev / release 审核通过直接报「发行来源未配置」;同时线上 SPA 白名单缺少 `games` 系列路由,`/games`、`/games/detail`、`/games/play` 在真实域名上全部 404。运行隔离实际由 iframe `sandbox="allow-scripts"` 的不透明来源承担,不需要独立 origin 兜底。
|
||||
- 决策(唯一口径):发行入口固定为平台同源路径 `/games/{gameId}/`。审核通过时 `api-server` 按 gameId 派生该相对路径写入公开投影,不再读取 `GENARRATIVE_GAME_DISTRIBUTION_RELEASE_ENTRY_TEMPLATE`;`AppConfig` 字段与两份部署 env 示例一并删除。dev / release / 预览环境口径一致,不再需要发行域名、通配 DNS 或通配 TLS。
|
||||
- 决策(边缘):`deploy/nginx/genarrative.conf`、`deploy/nginx/genarrative-dev-http.conf`、`deploy/container/nginx.conf` 三份模板内联同一条同源发行入口 location,把 `/games/<gameId>/` 与 `/games/<gameId>/<asset>` 转发到发行网关,转发前清空 `Cookie`;正则整体必须加双引号,否则 `{32}` 会被 nginx 当块定界符。SPA allowlist 补齐 `components`、`design-system`、`games`、`games/detail`、`games/mine`、`games/play`、`games/publish`。
|
||||
- 决策(客户端):`normalizeGameEntryUrl` 接受相对路径与同源发行路径,按当前 origin 解析成绝对地址后交给 iframe;无尾斜杠会归一化补齐。同源非发行路径继续拒绝,非当前源的绝对 https 继续兼容历史数据。
|
||||
- 决策(退役):删除 `deploy/nginx/genarrative-release-origin.conf`、`scripts/check-release-origin-config.mjs` 与 `npm run check:release-origin-config`;独立来源不再作为上线门禁。
|
||||
- 影响面:`server-rs/crates/api-server/src/{config.rs,modules/game_distribution.rs}`、`server-rs/crates/shared-contracts/src/game_distribution.rs`、`packages/shared/src/contracts/gameDistribution.ts`、`src/components/game-distribution/gameDistributionGuards.ts`(含新增测试)、`deploy/{nginx,container,env}`、`scripts/check-game-distribution-media-e2e.mjs`、`package.json`、平台与运维主规范。
|
||||
- 边界:SpacetimeDB 表结构与公开契约字段不变(`entryUrl` 仍是 string),只是取值从绝对 URL 变为相对路径;历史版本已冻结的绝对值不改写,admin 页与详情页展示口径不变。线上 dev / release 的 nginx 已按同源路径改动并 reload,`/etc/genarrative/api-server.env` 已删除模板变量;api-server 未重启,新写入要等下次重启。
|
||||
- 验证:`cargo check -p api-server --tests`、`cargo test -p api-server game_distribution`(27 passed)、`cargo fmt --all --check`、`npx vitest run src/components/game-distribution`(57 passed)、`npm run check:nginx-spa-routes`、`npm run check:encoding`(5060 文件)、`npm run check:doc-index`、`git diff --check` 全部通过;三份 nginx 模板渲染后 `nginx -t` 语法通过;dev 线上实测 `/games/game_2dcd…4955/` 与 `./assets/index-2Ws3zHlS.js` 均 200。
|
||||
|
||||
## 2026-09-24 DirectProject 失败说明按回合身份归位:开口用户条目发点提前到接单之后
|
||||
|
||||
- 背景:用户在同一个项目里连发消息,每条都在**连接获取阶段**就失败(执行器版本未通过逐次审批协议验收),界面上"错误显示在用户消息上面",上一轮还顶替本轮显示耗时(现场 15.6 秒),本轮气泡自成一轮显示 0.0 秒;后面再发一条,说明落进更早的分区里,用户以为"这条没报错"。区分两个 `turn/start`:逻辑回合的 `turn.started` 由接单动作发出(成对、一定有);app-server 协议的 `turn/start` 请求在连接拿到之后才发。失败发生在后者之前。
|
||||
- 根因:本轮的**开口用户条目**(`item_completed`,身份 `direct-codex:{clientTurnId}:user`)原来在 app-server `turn/start` 应答之后才下发,于是"接单到 `turn/start` 之间"的失败没有用户条目可挂;前端 `buildDirectChatTurns` 按**条目顺序**分回合,失败说明只能落在上一轮末尾,而本轮的乐观气泡被排在所有正式条目之后 → 渲染成"错误在用户消息之上"。
|
||||
- 决策(宿主):开口用户条目的**发点**提前到"接单成立、用户条目落盘成功、起 codex 之前"(`emit_direct_thread_user_item`,调用点 `direct_runtime/user_input.rs` 的命令主体),删掉 `turn/start` 之后那一处;线上仍然只有一处下发,不变式变成 `接单 → 开口用户条目 → 整轮里其余一切`。
|
||||
- 决策(前端):回合归属只认**身份**——失败说明条目带 `turnUserItemId`(reducer 写),`buildDirectChatTurns` 按开口条目身份分组(同一身份的条目永远同一轮),本地乐观气泡按身份挂回自己的回合而不是另开一轮;reducer 的收口早退只挡重复终态,不再吞掉"订阅重建只回放生命周期锚点"时那条还没写进界面的失败说明(`direct_thread_manager.rs` 的 `lifecycle_anchor`)。
|
||||
- 影响面:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs`、`.../agent/direct_runtime/user_input.rs`、`.../chat/conversation/{directThreadChat.ts,directTurnPresentation.ts}`。
|
||||
- 边界:`project.jsonl` 里的用户条目依旧只在首屏 / 翻页时读进前端,本轮不改读取时机——开口条目的运行态下发 + 身份归位已经让"说明挂错回合"不成立。
|
||||
- 验证:宿主 `cargo test --bins "agent::"`、`the_opening_user_item_is_emitted_before_anything_that_can_fail_in_the_turn`、`direct_project_turn_does_not_forward_codex_user_echo_as_chat_items`(补上同一发点);前端 `directTurnPresentation.test.ts` 的"本轮用户条目没到时,失败说明按身份挂回自己那一轮,本地气泡不再自成假回合"、`directThreadChat.test.ts` 的两条(身份字段、收口早退不吞说明)。
|
||||
|
||||
## 2026-09-24 DirectProject 删掉本地乐观用户气泡:用户气泡只来自宿主条目
|
||||
|
||||
- 背景:接单化之后,"接单窗口期"只服务本地乐观气泡(`awaiting-start` 展示态 + `pendingUserItemId` 身份)。上一轮把开口用户条目的发点提前到接单之后,"说明挂错回合"已不再需要气泡兜底;用户确认按"这条消息就像从来没存在过"处理,直接删干净。
|
||||
- 决策(不造用户消息):删 `pendingUserItemId` / `beginTurnCommand` / `endTurnCommand`(忙态改由 `beginTurnBusy` / `endTurnBusy` 持有,宿主认领判据 = `turnRunning` 或收口计数变过)、权限确认重跑的 `messageAppended` 参数、`DirectProjectTurnInput.messageText`(含首轮 `directInitialTurnText`)、投影里的 `awaiting-start` 与本地用户气泡路径(展示态只剩 `running` / `finished`);controller 不再需要 `assets`。
|
||||
- 决策(时间口径):回合起点只认 `turn.started.at`、终点只认 `turn.completed.at`;用户气泡的时钟是宿主落盘 / 观测时间,不再有"本地更早的真实发送时刻"(`sameIdentitySentAt` 删除)。两边都拿不到(重进项目读回来的历史回合)时整条「本轮结束于 … 」隐藏,不再兜出 0.0 秒。
|
||||
- 决策(本地说明):拒单提示带自己的身份(`…:rejected`),投影据此在会话末尾自成一组,不挂进上一轮;壳层 `announce`(无身份)照旧挂当前回合末尾。带身份的本地说明不开运行态标记,避免把真正在跑的那一轮读成已结束。
|
||||
- 代价(已知并接受):接单窗口与订阅重建窗口里聊天区没有这一轮的显示,反馈只有 composer 忙态、状态行与「陶泥儿正在处理」卡片(卡片这一段还读不出「已耗时」——起点是宿主的 `turn.started.at`,开始事件到了才开始读秒);`project.jsonl` 用户条目依旧只在首屏 / 翻页读进前端。
|
||||
- 影响面:`apps/ai-game-creator-shell/src/view/project-development/chat/conversation/{directTurnPresentation.ts,directCodexConversation.ts}`、`.../chat/controller/{useDirectProjectChatController.ts,useDirectProjectTurnStatus.ts}`、`.../chat/DirectProjectChatView.tsx`、`.../chat/components/DirectProjectConversation/DirectProjectTurn.tsx`、`src-tauri/src/agent/codex_app_server/mod.rs`(用户条目时间的注释口径)、对应 ADR 与实施计划。
|
||||
- 验证:`npx vitest run tests/directTurnPresentation.test.ts tests/directProjectTurn.test.tsx tests/directProjectTurnStatus.test.ts tests/directThreadChat.test.ts tests/chatComposerAttachmentCap.test.tsx`、`tests/appSurface.test.ts`(213 passed / 9 skipped)、`npx tsc -p tsconfig.json --noEmit`、`eslint`、`prettier --check`、`npm run check:encoding`、`git diff --check` 全绿。真实客户端观感未复核。
|
||||
|
||||
## 2026-09-24 AGC 模型目录初始值改为上游同步:不再回退写死的 gpt-6-astra/gpt-5.6-luna
|
||||
|
||||
- 背景:`agc_model_catalog` 缺行时 procedure 兜底返回内置目录(`quality → gpt-6-astra`、`fast → gpt-5.6-luna`),两个模型都已从上游移除;从未配置过目录的环境(新库、清库、本地调试)会把不存在的模型下发给客户端,选中后上游 `model_not_found`。
|
||||
- 决策(范围):本次只改目录初始值的来源,保持既有格式与契约不变 —— 目录字段仍是 `id`/`alias`/`modelId`/`defaultModelId`,后台页面与 admin DTO、`GET /api/llm/models` 形状、客户端 `select_game_creator_model` 的标识校验都不动,因此没有不兼容变更。
|
||||
- 决策(初始化):api-server(API/All 角色)启动时目录缺失、结构与当前定义不符或校验不通过即视为未初始化;此时请求上游 Router 控制面的分组定价列表 `GET {控制面}/api/pricing?group=taonier`(公开只读、不带凭据),按 `data[].model_name` 排序生成目录:`modelId` 与 `alias` 用上游原名(不再填“高质量/快速”),`id` 用模型名 slug(小写字母/数字/`-`/`_`,同名冲突追加 `-2`,因此客户端标识校验无需放宽),全部 enabled,默认项取排序后第一项,并按存量 revision 写回自增。
|
||||
- 决策(来源选择,2026-09-24 实测后确定):不用管理面模型注册表 `/api/models/`(会带出已下线、没有路由绑定的 `gpt-6-astra`/`gpt-6-luna`),也不用 `/v1/models`(要求 Router 用户 Key,用管理 token 实测 401)。当日 `group=taonier` 在售 6 个:`deepseek-flash`、`deepseek-v4-pro`、`glm-5.3`、`glm-5.3-flash`、`qwen-image-3.0`、`qwen3.8-flash`。
|
||||
- 决策(失败关闭与重试):拉取失败、空列表、响应超 1 MiB、缺可解析 revision 或写回失败都只记录 error,不写替代目录;未初始化期间 `GET /api/llm/models`、`/api/llm/responses`、`/api/llm/chat/completions` 与后台 `GET/PUT /admin/api/agc-models` 返回 `503`“模型目录未初始化”;只在启动期尝试一次,下一次启动重试,直到目录里有数据。启动本身不因同步失败而失败,避免 Router 短时不可用放大成 api-server 起不来。
|
||||
- 决策(幂等与并发):目录只取决于模型集合(排序后生成),重复同步结果一致;多实例并发启动只有一个写入成功,冲突方重读并校验既有目录可用性。目录只在未初始化时重建,上游变化不自动跟随。
|
||||
- 决策(存量目录):结构合法的目录不会被自动重建,包括旧版写死的 `quality/fast` —— 需要 owner 在后台改掉,或清空该行后重启重新同步。
|
||||
- 影响范围:`module-runtime`(`from_upstream_models` + slug 生成,删除写死的 `Default`)、`spacetime-module`(缺行返回 `AGC_MODEL_CATALOG_NOT_INITIALIZED`)、`api-server`(启动期同步、上游请求硬化、只校验地址的目标校验、后台 PUT 未初始化门禁)、AGC 客户端(默认模型占位改为 `platform-default`)、AGC 模型弹层 CSS、主规范/后端契约/运维文档。
|
||||
- 验证:`cargo test -p module-runtime --lib agc_models::`(4 passed)、`cargo test -p api-server --bin api-server agc` 与 `llm::`、AGC 客户端 `configuration::`、admin-web 页面定向 Vitest 与 typecheck、两套 workspace 的 `cargo fmt -- --check`、`check:encoding`/`check:doc-index`/`check:spacetime-schema`/`git diff --check`。
|
||||
- 验证(真实上游 smoke,本地 dev DB):清空 `agc_model_catalog` 后启动 api-server → 日志 `已按上游模型列表初始化 AGC 模型目录 revision=1 model_count=6`;登录后 `GET /api/llm/models` 返回同一批模型、`displayName` 即上游原名、默认项为排序后第一项;上游不可达/非 2xx 时启动只记录 error、AGC 接口 `503` 且目录保持未初始化;目录已存在时重启不重写。
|
||||
- 边界(未验证/残留):上游在售模型超过 32 条时同步会失败(目录项上限未改);`qwen-image-3.0` 这类图像模型会一起进入目录,是否对 AGC 隐藏由 owner 在后台停用;混合版本期间未升级的 api-server 会把自己的 AGC 接口打到 `503`,module 与 api-server 必须同批发布/回滚。
|
||||
|
||||
## 2026-09-28 渲染层下沉分支与最新 master 对齐:活动回合事实源、维护态出口与诊断详情
|
||||
|
||||
- 背景:`codex/agc-renderer-io-downshift` 把 AGC 渲染层的网络与状态下沉 Rust 之后,要重新落到 master 已经演进出的新形状上。29 处冲突里 25 处是机械取舍,真正的分歧只有三处需要定口径。
|
||||
- 决策(活动回合事实源唯一):master 把首页「运行中的项目」快照改成 Thread Manager 的 `active_turn_snapshots`,本分支的活动回合事件挂在 `DIRECT_TAONIER_ACTIVE_INVOCATIONS` 上。对齐后只保留 Thread Manager 一份快照:`accept_direct_thread_turn`、`update_direct_thread_active_turn`(仅内容变化时)、`complete_direct_thread_turn(_if_reserved)` 各广播一次 `game-creator-direct-active-turns-changed`,`direct_runtime` 不再导出第二份快照。理由:通知必须与快照同源,否则首页会把「不刷新」或「永不消失」表现成 bug。
|
||||
- 决策(维护态判定归 Rust,渲染层只订阅):master 的维护弹窗建在渲染层 `clientApi` / `fetchClientHttp` 上,而本分支已删除该传输层。对齐后由 Rust 在平台请求的错误分支用 `platform_maintenance::watch_platform_response`(只有 503 且命中 `MAINTENANCE` / 「维护」)广播 `genarrative-client-maintenance-detected`,渲染层 `subscribeClientMaintenanceEvent` 只负责打开唯一弹窗;同一批并发失败只弹一次。分类复用调用方本来就要读的响应体,不发额外请求,也不改写任何业务错误文案。
|
||||
- 决策(跟随 master 退役诊断详情入口):master `5398a53e6` 明确把诊断引用移出用户可见文案并删除 `read_agent_runtime_error_detail` 与渲染层读取逻辑,本分支《AGC统一错误诊断与验收反馈》里程碑第 6 条因此被上游取代,实现与专属用例一并退役;失败说明继续由 `turn.completed.failure` 事件投影。
|
||||
- 影响范围:AGC 客户端(`direct_thread_manager`、`direct_runtime`、`platform_maintenance`、`llm_catalog`、`account_api`、`auth_session`、`platform_asset_upload`、`assets`、`game_distribution_publish`、`agent/generation/canvas_generation`、渲染层 chat 控制器与视图、`tests/`)、nginx SPA 白名单、`.gitignore`、mobile 检查脚本、capabilities 描述、文档与共享记忆。
|
||||
- 验证:`cargo check`(0 error)、`cargo fmt -- --check`、Rust 定向 `direct_thread_manager` 17 passed 与 `platform_maintenance` 1 passed、`npx vitest run tests/appSurface.test.ts`(201 passed / 9 skipped)与受影响套件、`npm run ai-game-creator-shell:check:web`、根 `npm test`、`npm run lint`、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check`。
|
||||
- 边界(未验证):真实维护窗口下的弹窗与并发失败表现、真实 Provider 下长回合的活动回合事件时序,都需要真机运行时证据;本轮只到源码级与定向用例级。
|
||||
@@ -53,6 +53,8 @@
|
||||
|
||||
## 验证路由
|
||||
|
||||
Windows 下的移动壳 smoke 通过 Node 启动从当前 workspace 包解析出的 Expo/EAS CLI,不直接 `spawnSync('npm.cmd')`;保留原配置与导出断言。具体入口和警告清理边界见本地开发运维文档。
|
||||
|
||||
提示词外置变更运行 `runtime_prompt_bundle_build` 与 `prompt_source_boundaries` 两个 Rust 集成测试,验证编译期文本、目录登记和源码边界;现有 `agc-rust-shard-1` 本地/CI 入口先执行这组检查,再运行分片单测。
|
||||
|
||||
提示词测试验证实际请求中的片段来源、动态参数和工具结构;措辞不作为逐字契约。已有行为测试覆盖的限制不再另设整段文案检查。Direct 回合测试复用生产的消息转换和文件投影函数,不维护仅供测试调用的回合编排副本。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 文档地图与阅读索引
|
||||
|
||||
更新时间:`2026-08-31`
|
||||
更新时间:`2026-09-23`
|
||||
|
||||
## 阅读顺序
|
||||
|
||||
@@ -21,19 +21,24 @@
|
||||
3. `server-rs/crates/api-server/src/app.rs`、`server-rs/crates/api-server/src/modules.rs` 与对应 crate README / 源码
|
||||
4. `docs/openapi/genarrative-external-v1.openapi.json`(涉及 External v1 时)
|
||||
|
||||
外部 MCP 语义工具设计:
|
||||
|
||||
1. [外部 OpenAPI 与 API Key 接入方案](../../【后端架构】外部OpenAPI与APIKey接入方案-2026-06-19.md):现役托管 MCP 与 External API 合同。
|
||||
2. [外部 MCP 语义工具说明与参数设计](../../technical/【技术方案】外部MCP语义工具说明与参数设计-2026-09-23.md):15 个语义工具与全部旧工具并存,复用现有 API 分派和 schema;多功能入口使用 action/input,结果不裁剪,可选幂等只扩展新入口。语义工具按 operation 显式声明 destructive 风险,并汇总各 action;上传确认包含已有元数据更新风险。instructions 与 resources 已同步当前工具选择、调用流程及结果说明;资源 URI 保留,Skill 文档源与下载包共用,线上状态按实际部署核对。
|
||||
|
||||
AI 游戏创作 / DirectProject / UI workflow:
|
||||
|
||||
1. `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
|
||||
2. `docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`(历史方案,仅供追溯)
|
||||
2. `docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md`(当前 Design Agent;旧策划 V1/V2 均已退役)
|
||||
3. `docs/technical/【技术方案】DirectProject客户端Skill与MCP扩展导入方案-2026-08-31.md`
|
||||
4. `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`
|
||||
5. `docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`(历史 V1 方案,仅供追溯)
|
||||
6. `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md`
|
||||
7. `docs/technical/【技术方案】DirectProject客户端Skill与MCP扩展导入方案-2026-08-31.md`
|
||||
8. `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`
|
||||
9. `docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`
|
||||
10. `docs/technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md`
|
||||
11. `docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`
|
||||
9. `docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`(历史 V2 方案,仅供追溯,不是当前实现依据)
|
||||
10. DirectProject 附件按 AGC 主实施计划的 canonical `userItem` 合同;`docs/technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md` 为旧 sidecar 历史方案,不作为实现入口。
|
||||
11. Direct 历史、审计与耗时按 AGC 实施计划及原始历史专题;`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md` 已归历史,不作为实现入口。
|
||||
12. `docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md`
|
||||
13. `docs/【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md`
|
||||
14. UI 编辑器、宿主壳和当前测试专题文档
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
# 踩坑与排障记录
|
||||
|
||||
## 美术包固定 PNG 路径与平台返回格式不一致
|
||||
|
||||
- 平台生成成功仍可能返回 JPEG/WebP;直接按 `assets/art-spec.png` 等固定路径保存会在扩展名校验时失败,图片尚未落盘但已付费结果账本仍存在,不能据 manifest 为空认定没有生成结果。
|
||||
- 美术包专用入口在本地提交前有界解码并归一化为 PNG,原 PNG 字节不变;扩展名、MIME、内容摘要和恢复比较必须使用同一份最终字节。普通图片工具和独立切片的格式合同不随之放宽,转码也不能代替真实透明度校验。
|
||||
- 失败后保留原账本并按冻结意图恢复,不能删除账本后重新生成。详情见 [AGC 美术包合同](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md#2026-08-23-direct-codex-美术包显式重生成与切片投影)。
|
||||
|
||||
## 2026-09-24 AGC 生成路由迁移必须核对账号队列契约
|
||||
|
||||
- AGC 普通账号会把 external editor 路由映射到站内入口;只验证 API Key 路由不足以证明客户端链路可用。
|
||||
- 站内入口必须读取客户端持久化的 `Idempotency-Key`,场景配方重建须保留精确的 `generationInputs.source = ai-game-creator-client` 标记。否则可能按请求 ID 重复入队,且普通队列 consumer 不保存客户端轮询所需的可下载 `result`。
|
||||
- 场景其余执行字段和引用身份仍由服务端重建,不整体信任调用方 `generationInputs`。验证应覆盖来源标记经过组装、队列清理和结果序列化的完整纯逻辑链路,以及账号路由的幂等键校验。
|
||||
|
||||
## 同一祖先下的多个项目会各自弹一次 UAC
|
||||
|
||||
- **现象**:AGC 启动页一次挂载出现多个叠在一起的 UAC 提权弹窗;用户点「否」后仍会被再问一次。
|
||||
- **原因**:`windows_acl_repair_target`(`src-tauri/src/config.rs`)对 Managed 作用域返回「第一个读取被拒的祖先」——同一祖先下的多个项目解析到**同一个** repair target;而唯一的去重是单次调用内的局部 `attempted_targets`,跨调用、跨线程都没有记忆。启动页一次并发检查 ≤8 个最近项目,就会并发启动同样多次 `powershell -Verb RunAs`。
|
||||
- **处理**:进程级 single-flight(key = `(规范化 repair target, scope)`)+ 结果冷却(成功 30s / 失败 15s / 用户取消 120s)+ 等待窗口 60s 超时按失败关闭;leader 异常退出由 RAII 兜底唤醒等待者。用户取消带稳定标记 `AGC_ACL_ELEVATION_DENIED`,前端据此不自动重试;用户主动操作会清除拒绝记忆。
|
||||
- **不要踩的坑**:① 闸门 key 必须归一化 `\\?\` / `\\?\UNC\` 前缀——最近项目列表里同一项目实测同时存在 `\\?\C:\...` 与 `C:\...` 两种写法,按原始字符串做 key 会让同一个目录弹两次 UAC(`windows_acl_repair_gate_key`);② 冷却必须从**结果落库**时刻算起,用 leader 起跑时刻会让 120s 拒绝冷却在 UAC 被挂着两分钟时提前过期,紧接着的自动重查立刻再弹一次;③ 复现「多个项目共用同一 target」时,DENY 要写在祖先的**父目录**上靠继承落入祖先——`icacls` 直接加在容器自身实测只影响子项(容器自身 `GetFileAttributes` 仍成功),target 会退化成每个项目自己,repro 不出并发弹窗;④ 夹具路径必须落在 `game_creator_private_path_allows_auto_elevation` 放行范围内(runtime config dir / `.config/genarrative` / 打包 AppData / 带 `.agent/manifest.json` 的项目根),因为提权子进程会按 **repair target** 再校验一次 `scope.allows_path`,否则失败关闭。
|
||||
- **验证**:`src-tauri/src/tests/acl_repair_gate.rs`(并发只执行一次、冷却复用、拒绝冷却、清除后可重试、follower 超时、leader panic 唤醒等待者、冷却基准、路径写法归一、leader 卡死接管与迟到结果丢弃)。真机复现(无需提权交互即可计数):在 Managed 放行范围内建 8 个带 `.agent/manifest.json` 的假项目 → 对共同祖先的**父目录** `icacls <父目录> /deny *<sid>:(OI)(CI)(RX)` → 挂载启动页,同时数 `powershell.exe` 里命令行带 `RunAs` 的进程数(`Start-Process -Wait` 会让它一直存活到用户应答)与 `consent.exe` 峰值:修复前 8 个并发请求,修复后 1 个;把同一目录的 `\\?\C:\...` 与 `C:\...` 两种写法一起塞进最近项目,还能验证 key 归一化是否生效(修复前 2 个、修复后 1 个)。
|
||||
- **leader 卡死的兜底**:闸门只有 follower 的有界等待(60s),若提权子进程真的挂死(`Start-Process -Wait` 无超时),`leader_deadline`(5 分钟)之前该 key 一直被占住,之后新调用会接管并按新 leader 执行;被接管后旧 leader 迟到的结果按令牌丢弃,不会覆盖接管者。`clear_game_creator_acl_elevation_denials` 只清「被拒绝」记忆,不清理 running。
|
||||
- **关联**:`src-tauri/src/acl_repair_gate.rs`、`src-tauri/src/config.rs`、issue #498。
|
||||
|
||||
> 策划历史条目边界:旧策划 V1/V2 已全部退役,当前入口仅使用 Design Agent。下文带日期的旧 Planning V2、Fast GDD、`plan.submit_gdd`、旧 IPC/模块记录仅用于追溯,不能作为恢复旧代码、身份门禁或专属测试的依据;共享问题需在现役调用上核查。现行合同见[策划 Agent 生产迁移与工作区浏览](../../technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md)。
|
||||
|
||||
## 2026-09-24 模型输出的围栏会粘在正文行里:聊天 Markdown 必须先归一化再解析
|
||||
|
||||
- **现象**:AGC 对话里代码块解析错位——引言行被当成代码渲染(`…实现细节(game.js):```js`),或者代码块收不住、把后面的正文一起吞进去(`… return centerOn(projection); }````)。文本本身「看起来没问题」,容易被当成渲染器坏了。
|
||||
- **原因**:CommonMark 只认**整行**的围栏(最多 3 个空格缩进)。模型经常把 ``` 直接粘在上一行末尾,那个 ``` 退化成行内文本:开场围栏不成立(后面的正文被当成代码)、收场围栏不生效(代码块不闭合,吞掉剩余内容)。`ChatMarkdownMessage` 原先只做空行压缩,没有这一步归一化。
|
||||
- **处理(现行口径)**:`normalizeMarkdownFences` 在解析前把「围栏前是非空白字符、围栏到行尾只剩语言标识(可空)」的行拆成两行。判据刻意收窄:整行 / 缩进围栏、行内代码(单个反引号)、代码里出现的 ``` (`const s = "```";`)、引用块 / 列表项开头的合法围栏(`> ```js`、`- ```js`)以及同一行里出现第二段围栏串的行内代码(``文本 ```x``` ``)都不动——后三者拆开只会把围栏从引用块 / 列表项里挪出来,或者凭空造出一个开场围栏。归一化对 `preserveBlankLines`(文件预览)同样生效。
|
||||
- **同时**:块级 `pre` 与块内 `code` 都要给 `whitespace-pre-wrap` + `break-words`(两处各写过 `white-space`,只改一处不换行),否则窄面板里长行会把消息拉宽、顶出横向滚动条。
|
||||
- **验证**:`npx vitest run apps/ai-game-creator-shell/tests/ChatMarkdownMessage.test.tsx`(去掉归一化或换行类名即红);真实 Chromium 夹具里长代码行 `horizontalOverflow: false`、粘住的开场 / 收场围栏都渲染成正确结构。
|
||||
|
||||
## 2026-09-24 对话过程卡的读秒退回 1 秒一跳:刷新粒度必须与显示精度同格
|
||||
|
||||
- **现象**:AGC DirectProject 对话区底部那条「陶泥儿正在处理 / 已耗时 12.4秒」的状态条,小数位一秒才动一格,看着像读数卡住;同一屏里工具卡片的耗时与资源生成侧栏的读秒都在正常走 0.1 秒,只有这一处不动。
|
||||
- **原因**:耗时文案不足一分钟保留一位小数(`formatElapsedDuration`),刷新就必须是 100ms。这次改动方向本身是对的——把 clock 从整个聊天视图下移到耗时那一行,但顺手在组件里另写了一份 `setInterval(..., 1000)`,绕过了对话侧唯一的 `useLiveNow`(`LIVE_TIMER_TICK_MS = 100`);`team-conventions.md` 里「运行时用 100ms 叶子时钟刷新一位小数、终态冻结」这条约定当时已经写好,改动没有对齐它。
|
||||
- **处理(现行口径)**:耗时文案的刷新一律走 `useLiveNow`,不在视图组件里另起 interval;tick 只订在显示耗时的那一行(叶子节点),不能落在整块面板或整份回合列表上。可机检的判据是「不足一分钟的耗时必须每 100ms 递增一次小数位」。
|
||||
- **验证**:`npx vitest run apps/ai-game-creator-shell/tests/directProjectProcessStatus.test.tsx`(把 `LIVE_TIMER_TICK_MS` 临时改回 1000 时第一步即红);真实浏览器里 600ms 内文案从 `18.0秒` 走到 `18.6秒`。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/DirectProjectConversation.tsx`、`apps/ai-game-creator-shell/src/features/project-workspace/useLiveNow.ts`、`apps/ai-game-creator-shell/src/styles.css`。
|
||||
|
||||
## 策划回复的重复终态不能重新启动伪流式
|
||||
|
||||
策划 Runtime 会通过状态事件与命令返回交付同一份最终视图。若前端清空临时正文后再拿“最后一条非用户历史消息”回填动画,就会出现正式回复旁又播放一遍、播放后消失的假重试。正文应按 `messageId` 保存显示进度,与正式消息共用一个气泡;请求完成不清动画,不延迟正式业务状态。Provider 自动重试复用消息 ID 并发送空文本,只允许重置未持久化的该条回复。正文、工具状态和 reasoning 分开;事件与异步命令收尾均检查项目及活动回合,旧请求不能覆盖新回合。详见 [AGC 实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)。
|
||||
@@ -290,6 +330,12 @@ Direct 工具桥会 canonicalize 项目根,事件中的路径可能带 `\\?\`
|
||||
- 成功 JSON 与错误响应体均复用 `readClientHttpResponseText` 的 15 秒上限;超时后保留最后一次有效目录并释放在途请求,手动重试重新发起请求。迟到的响应不得覆盖重试获得的新目录。
|
||||
- 排查时区分接口未挂载(404)、未授权(401)、网络或响应体超时以及刷新无变化但缺少反馈;不能仅凭客户端启动 IPC 回退警告判断刷新失败原因。
|
||||
|
||||
## 2026-09-14 未知 JS 异常必须继续进入 error report
|
||||
|
||||
- **原则**:任何 JS 边界只要无法确认异常属于已知、已解决且有契约的业务失败,就必须保留原始异常并继续抛出,由全局 error report 链路采集;范围不限于 UI 编辑器、生成路径、剪贴板,也包括文件系统、权限、网络、插件和其它宿主调用。用户界面的 fallback(例如显示“复制失败,请手动复制”)只是附加的可继续操作提示,不代表异常已经被处理。
|
||||
- **易错点**:不要在 `catch` 中只设置 UI 文案然后结束,也不要把未知异常替换成新的泛化错误。需要用户 fallback 时,先更新提示,再重新抛出原始对象;只有已知且契约化的业务失败才可以在边界处转换为稳定的用户文案。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/view/ui-editor/components/UiEditorCopyPathButton.tsx`、`docs/technical/【前端架构】UI编辑会话模块边界-2026-08-19.md`。
|
||||
|
||||
## 2026-09-14 AGC 壳 Rust 套件按「一片一 job」拆分,且分片必须自校验覆盖
|
||||
|
||||
- **现象**:`AI game creator shell Rust tests` 一直是客户端 CI 的关键路径。run 2097 实测 15 分 27 秒,其中 `apps/ai-game-creator-shell/src-tauri` 的 bin target 单测(2466 条)一条 `cargo test -- --test-threads=1` 串行占 507 秒。
|
||||
@@ -5306,7 +5352,10 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
|
||||
- 现象:用户明确要求重做美术或切换游戏主题,工具仍立即返回 `assets/art-spec.png`、`assets/direct-game-background.png`、`assets/art-spritesheet.png`;新需求没有 Provider operation,游戏继续使用旧图。切片虽然已经落盘,也可能不出现在资源管理或工具结果中。
|
||||
- 原因:旧 Direct 工具只有 `brief`,完整包校验成功后无条件短路;固定阶段账本恢复又未比较本次生成 prompt。切片只写文件和切片清单,未作为顶层 manifest asset 投影;工具桥只返回三条主路径并丢失切片与 warning。
|
||||
- 处理:显式重做使用 `mode=regenerate`,普通请求使用 `reuse-or-create`。重生成必须由当前最新 User 消息明确授权并绑定客户端稳定 `clientTurnId`。授权先对完整原文做 Unicode NFKC 与撇号规范化,随后整串必须完整匹配审核过的独立立即执行指令,只允许句号/感叹号收尾;不得剥离引号、方括号或代码片段,动作前后也不得携带 brief、条件、否定、选择、确认、费用、延迟或其它文本。风格需求先单独描述,再由下一条独立“请重新生成美术”消息确认;不要靠扩充 deny 同义词推断付费同意。同一调用完成回包丢失只从 `completed` 持久结果等值重放,不能因重试再次扣费。App 必须在 Direct 调用前落盘原始 User 消息和回合 ID,Tauri 必须在成功返回前幂等落盘同 ID assistant 终态;同进程重复水合若命中“回合仍在运行”,只能显示瞬时占用提示,不得以稳定 assistant messageId 写成终态并抢占原执行的成功回复。恢复扫描与启动前置恢复必须发现 `resetting / compensating / anchored in-progress` 并在专用锁内恢复,重开项目只续跑真正未回答的原身份。整条付费链必须持有专用跨进程执行锁;换新回合时先持久化 `resetting` 再清理旧阶段账本,不得通过删除 workflow 留出无主窗口。崩溃补偿只恢复旧文件并清 replacement CAS 锚点,已 `prepared / accepted` 阶段账本、原 `Idempotency-Key / operationId` 必须保留,同冻结意图续跑复用旧请求;未知账本在文件 mutation 前失败关闭。只有没有任何阶段账本和替换锚点的孤立 workflow 空壳可原子接管;旧 schema 和其余冲突失败关闭。遇到 prompt 或当前 art-spec 身份不一致的未决账本必须保留原 operation 并返回对账错误。Direct app-server 可写边界只限真实 canonical `game/`,canonical 项目根的原生 OS 路径字节与权威 manifest `projectId` 经域标签和独立长度前缀编码后共同绑定连接池和 thread 身份,不得写项目根、`assets/`、`.agent/`,也不得获得网络、命令、MCP 或权限扩权;受控工具如果需要项目级客户端状态,只能从同一真实 `game/` cwd 经相同校验内部反查项目根,不能扩大模型可写根。标准图集首次创建和重生成都要求四张透明、可见、像素及平台身份唯一的 canonical 切片;工具只回传通过私有回执、公开清单、源图和顶层登记交叉验证的 `slicePaths` 与安全 `resources`。部分/opaque/重复/缺回执切片必须告警,不能把公开清单或顶层自述身份当作 Canvas 权威。
|
||||
- 处理:显式重做使用 `mode=regenerate`,普通请求使用 `reuse-or-create`。模式由 Codex 根据当前用户请求通过审核工具显式选择;客户端不再使用 Unicode NFKC、关键词、否定词表或独立确认句式判断业务意图。旧文本授权规则已被 2026-09-03 MCP 决策替代,相关无调用实现于 2026-09-23 删除。工具桥绑定活动客户端回合与稳定 `clientTurnId`,冻结首次 `brief` 摘要;缺少活动回合或摘要冲突仍拒绝。项目权限、账号、计费、幂等、锁与未知结果恢复合同继续有效。
|
||||
- 幂等与恢复:同一调用完成回包丢失只从 `completed` 持久结果等值重放,不能因重试再次扣费。App 必须在 Direct 调用前落盘原始 User 消息和回合 ID,Tauri 必须在成功返回前幂等落盘同 ID assistant 终态;同进程重复水合若命中“回合仍在运行”,只能显示瞬时占用提示,不得以稳定 assistant messageId 写成终态并抢占原执行的成功回复。恢复扫描与启动前置恢复必须发现 `resetting / compensating / anchored in-progress` 并在专用锁内恢复,重开项目只续跑真正未回答的原身份。整条付费链必须持有专用跨进程执行锁;换新回合时先持久化 `resetting` 再清理旧阶段账本,不得通过删除 workflow 留出无主窗口。崩溃补偿只恢复旧文件并清 replacement CAS 锚点,已 `prepared / accepted` 阶段账本、原 `Idempotency-Key / operationId` 必须保留,同冻结意图续跑复用旧请求;未知账本在文件 mutation 前失败关闭。只有没有任何阶段账本和替换锚点的孤立 workflow 空壳可原子接管;旧 schema 和其余冲突失败关闭。遇到 prompt 或当前 art-spec 身份不一致的未决账本必须保留原 operation 并返回对账错误。
|
||||
- 执行边界(2026-09-24 校准):DirectProject 的 cwd 与 AGC 业务身份根是用户选择的 canonical 项目根;其原生 OS 路径字节与权威 manifest `projectId` 经域标签和独立长度前缀编码后绑定连接池和 thread 身份。旧的 `game/` 唯一可写根、禁止全部网络 / 命令 / MCP 的描述已失效;也不能把后来的“完整访问”描述理解为绕过当前宿主门禁。当前 thread 使用 `sandbox=read-only`、`approvalPolicy=untrusted`,turn 使用 `sandboxPolicy.type=readOnly`;原生命令按逐次审批与宿主执行许可处理,客户端 MCP 仍校验项目绑定、业务权限和副作用许可。具体边界以[主实施计划“宿主验收与执行许可合同”](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md#宿主验收与执行许可合同)及当前实现为准,Provider 凭据保持隔离。
|
||||
- 资源投影:标准图集首次创建和重生成都要求四张透明、可见、像素及平台身份唯一的 canonical 切片;工具只回传通过私有回执、公开清单、源图和顶层登记交叉验证的 `slicePaths` 与安全 `resources`。部分/opaque/重复/缺回执切片必须告警,不能把公开清单或顶层自述身份当作 Canvas 权威。
|
||||
- 同进程恢复补充:命中“同一 stable turn 仍在运行”后除禁止写 assistant 终态外,还必须删除当前 App 实例的恢复 claim。这样原调用随后成功时显式刷新能读取其终态,随后失败时也能按相同 `clientTurnId` 再次续跑;不要靠重载 WebView 清理进程内 claim,也不要用无界定时轮询制造并发调用。
|
||||
- 严格图集崩溃补充:规范图和背景图的两文件 rollback 不覆盖严格图集事务已经整体修改的 `.agent/manifest.json`、私有回执、公开清单、主图集、四切片和切片清单。必须在严格调用前持久化 pending 及九项旧合同身份;重启恢复先对账底层严格事务,完整新合同直接收口完成,完整旧合同才补偿前两阶段,混合或漂移状态失败关闭。不要在严格提交成功后局部恢复前两张图。
|
||||
- 部分旧包补充:rollback 的规范图/背景图必须保存旧字节与旧 manifest entry,不能把这两项缺失隐式当成空内容;显式 `regenerate` 因此只在这两项可信可回滚时开放。历史主图集、私有回执、公开清单或 canonical 切片可以缺失,但八个严格路径与受管顶层 asset identity 必须逐项冻结其真实 `Present/Some` 或 `Missing/None` 状态,补偿也必须恢复相同存在性。不要因为旧美术包缺切片而阻断重生成,也不要把本轮新建的严格文件误记成旧文件。
|
||||
@@ -5970,3 +6019,30 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **现象**:`npm test -- src/services/sseStream.test.ts`(SSE 传输层收口约定与静态预览 MVP 验收清单里都写着的验收命令)报 `No test files found, exiting with code 1`;同一时期 `apps/ai-game-creator-shell/tests/resourceBatchTagsIntegration.test.tsx`、`resourceTagStatsRefresh.test.tsx` 在 `--root apps/ai-game-creator-shell` 口径下整文件以 `ReferenceError: describe is not defined` 失败、0 个用例执行。
|
||||
- **原因**:`e9c3dc112 退役旧创作模板业务`(2026-07-17)把根 `vitest.config.ts` 的 `src/**/*.test.ts(x)` 通配收成显式白名单,只列了「当时确认存活」的目录;`src/services/sseStream.test.ts`、`src/services/frontendRuntimeConfigService.test.ts`、`src/persistence/**`、`src/routing/RouteImageReadyGate.test.ts`、`src/editor/shared/jsonClient.test.ts`、`src/components/platform-entry/PlatformProfilePrimitives.test.tsx`、`packages/shared/src/theme.test.ts`、AGC 的 `tf2css.test.tsx` 这些**现役**模块的用例被一起漏掉(用临时配置收进来跑:261 个文件全绿)。同时 194 个 AGC 测试文件里有 2 个只依赖 `describe/it` 全局、没有显式导入,换执行口径就整文件不收集。
|
||||
- **处理**:把上面这些现役用例补回 `vitest.config.ts` 的 include(不恢复 `src/**` 通配,避免把旧创作链路的退役测试一起收回来);那两个 AGC 文件按同目录套件惯例从 `./appSurface/harness` 显式导入 `describe` / `it`;同步修订两份文档里指向已删除模块的验收命令。
|
||||
## 2026-09-24 DirectProject 失败说明显示在用户消息之上、下一条消息看起来"没报错"
|
||||
|
||||
- **现象**:连发几条消息,每条都在连接阶段失败(执行器版本未通过验收)时,界面上"错误出现在自己消息的上面",上一轮底下显示"本轮结束于 <本轮结束时刻> · 耗时 15.6秒",自己这条底下显示"耗时 0.0秒";再发一条,失败说明落进更早的分区,用户以为这条没有报错。
|
||||
- **原因**:① 本轮的开口用户条目(`item_completed`)原来在 app-server `turn/start` 应答之后才下发,连接阶段失败走不到那一步 → 事件流里只有逻辑回合的一对事件,没有开口条目;② 前端 `buildDirectChatTurns` 按条目顺序分回合,失败说明(assistant 条目)只能挂在"当前回合"(上一轮)末尾;③ 本地乐观气泡被排在所有正式条目之后,于是自成一轮(无边界 → `Math.max(endedAt, startedAt)` 兜底出 0.0 秒),上一轮则借用了本轮的终点(15.6 秒)。另一条独立漏洞:reducer 的收口早退(`!turnRunning && live 为空`)会整条吞掉"订阅重建只回放生命周期锚点"时那条失败说明。
|
||||
- **处理(现行口径)**:开口用户条目的发点提前到"接单 + 落盘成功、起 codex 之前"(`emit_direct_thread_user_item`),线上仍只有一处下发;回合归属改成按身份(失败说明带 `turnUserItemId`,同一身份的条目永远同一轮),本地气泡按身份挂回自己的回合;收口早退改为"说明还没写进界面就不早退"(只补说明与终点,不重开回合、不抬高冻结终点)。
|
||||
- **排查提示**:先分清两层 —— 逻辑回合的 `turn.started` / `turn.completed`(Thread Manager,一定有、成对)vs app-server 协议的 `turn/start` 请求(连接拿到之后才发)。"失败说明挂错回合"永远先看这条顺序,不要先怀疑事件丢了。
|
||||
- **验证**:宿主 `the_opening_user_item_is_emitted_before_anything_that_can_fail_in_the_turn`、前端 `本轮用户条目没到时,失败说明按身份挂回自己那一轮,本地气泡不再自成假回合` 与 `收口早退不吞掉还没写进界面的失败说明(订阅重建只回放生命周期锚点)`。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server/mod.rs`、`.../agent/direct_runtime/user_input.rs`、`.../chat/conversation/{directThreadChat.ts,directTurnPresentation.ts}`、`docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`。
|
||||
|
||||
## Linux command.exec 的 leader 回收与后代退出存在时序差
|
||||
|
||||
- bwrap 主进程已经 `wait` 回收时,namespace 后代仍可能短暂处于退出过程;一次 `/proc` 扫描发现活成员后再读取 leader 身份,会把正常退出误报为需人工核对。容器 PID 1 未回收的 `Z / X` 成员也不能当作活进程。
|
||||
- 正常退出与取消/超时共用有界的组退出确认,组内无活成员立即返回;缺失或变化的 leader 身份不能授权补发信号,持续活成员或读取失败仍报错。启动身份在 ready 后、commit 前记录;terminal 协议错误也不能跳过清理与 reader 取消。
|
||||
- 回归使用独立 subreaper 夹具:先 poll 清理并确认仍在等待,再让同组后代退出,覆盖正常收尾和取消/超时的共同清理路径;保持现有 CI 分片与并行,不靠取消并行或失败重试消除竞态。
|
||||
|
||||
## 2026-09-24 DirectProject「接单窗口里看不到自己刚发的话」是设计,不是丢消息
|
||||
|
||||
- **现象**:按下发送后聊天区里不会立刻出现自己那句话;宿主还在接单 / 落盘的那段时间只能看到 composer 忙态、状态行与「陶泥儿正在处理」卡片(卡片这一段不读秒——起点要等宿主的 `turn.started.at`),滚动也停在原地。订阅重建的窗口同理。容易被读成"消息丢了 / 没发出去"。
|
||||
- **原因**:本地乐观用户气泡已删(ADR「DirectProject命令接单化」后续更新 2026-09-24)。用户气泡的唯一来源是宿主下发的开口条目(发点=接单成立 + 用户条目落盘成功 + 起 codex 之前)。删它的收益是"回合归属只认身份"不再需要给本地消息一份同名身份,投影也少一个展示态(`awaiting-start`)。
|
||||
- **排查提示**:窗口期不要拿"有没有本地气泡"当发送成功的证据;证据是 `invoke` 返回 `Ok`(接单成立)与随后到达的 `turn.started` / 开口条目。显示时间与耗时也全以宿主事件为准:起点 `turn.started.at`、终点 `turn.completed.at`;重进项目读回来的历史回合两边都空,整条「本轮结束于 … 」直接隐藏(不再出现 0.0 秒)。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/view/project-development/chat/conversation/directTurnPresentation.ts`、`.../chat/controller/useDirectProjectChatController.ts`、`.../chat/components/DirectProjectConversation/DirectProjectTurn.tsx`、`docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`。
|
||||
|
||||
## 2026-09-28 大跨度架构合并里,"语义冲突"不会以冲突标记的形式出现
|
||||
|
||||
- **现象**:把 237 个上游提交并进一条改了传输层的分支时,`git merge` 报 29 处冲突;剩下的**自动合并**里藏着三类静默错误:① 对冲突文件用 `git checkout --ours` 整文件取本分支,会连上游新增的 `#[tauri::command]`(本例 `clear_game_creator_acl_elevation_denials`)一起丢掉,表现为 `cannot find macro __tauri_command_name_*`;② 上游给别的 item 加的 `#[cfg(test)]` 会被插到本分支新增函数的前一行(本例 `configure_game_creator_manifest_invalidation_event_sink`),表现为非 test 构建 `unresolved import`;③ 上游删除的实现(本例 `DirectCodexTurnFailure` / `record_direct_codex_turn_failure`)只在 **test 构建**里才报错,`cargo check` 全绿。
|
||||
- **处理**:冲突文件一律用 `git checkout -m -- <path>` 取回"未解决但已自动合并"的内容,只手工解决冲突块,不要整文件取一边;`cargo check` 之后必须再跑 `cargo test --no-run` 把 test 构建的错误扫出来;合并后再跑一遍 `cargo fmt -- --check`,rustfmt 的「Incorrect newline style」正好能抓到被脚本重写过的文件。
|
||||
- **附注**:reqwest 0.12 的 `Response` 没有 `into_parts` / `from_parts`(0.13 才有),想在发送层统一改写响应体会直接编译失败。平台维护态因此改成在各 facade 的错误分支复用已经读到的 body 判断,而不是重建响应对象。
|
||||
@@ -67,7 +67,7 @@ SpacetimeDB crate、SDK、CLI / standalone 与生成 bindings 按 `2.8.3` 对齐
|
||||
- 2026-09-09 起,AGC 已新增遵循 OpenAI Agent Plugins 组合模型的通用 Plugin Host/SDK:Plugin、Skill 和 MCP 进入统一扩展 catalog;插件生命周期、行分隔 JSON-RPC、UI 面板、Capability Registry、权限和审计由 `plugin_host` 统一承接,Skill/MCP 仍分别交给各自现有 loader/transport;目标编辑器只通过通用 `EditorAdapter` 扩展点接入。详见 `docs/technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md`。
|
||||
- Unity 编辑器能力以 `plugins/agc-unity-editor` 内置插件提供,固定复用 Apache-2.0 的 DotCraft Attach 核心;Windows x64 / Unity Mono 接入不安装项目包。GUI、Runtime 与 DirectProject 通过现有 Runner 统一执行归属,跨进程回执与持久不确定阻断统一处理。首次打开 Unity 工程只初始化 AGC `.agent` 元数据,保留原引擎工程;详见 `docs/technical/【技术方案】AGC Unity编辑器插件接入-2026-09-18.md`。
|
||||
- DirectProject 的 Codex 原生文件、搜索、命令、图片查看和 Skill 仅在用户项目 cwd 与 `workspaceWrite(writableRoots=[project])` 内可用;原生命令允许联网以支持 npm 安装,npm 缓存位于项目内 `.npm-cache/`。多 Agent、Apps、插件、hooks、图片生成、Goals、Workspace Dependencies、Tool Suggestion 和原生浏览器/电脑控制保持关闭。app-server 使用隔离 `CODEX_HOME`,provider 凭据只由 AGC 客户端代理持有,不能进入模型上下文或 shell 环境。
|
||||
- `ui-prototype`(设计图片)与 UI 编辑器 `UI` JSON 是不同资源。白名单 `ui.workflow.run` 按页面执行 `prepare → recognize → status → finalize`,由 provider-backed 识别、合并和组件绑定持久化 State/revision,并把 `reference-ready → structure-ready → merge-ready → binding-ready → application-ready → completed` 投影到 manifest。Provider 缺失、请求失败、工具缺失、结果不匹配或仍有待审节点时保留真实阶段并返回 blocker,不得用 deterministic seed 伪造完成。
|
||||
- `ui-prototype`(设计图片)与 UI 编辑器 `ui-design-doc` JSON 是不同资源。Agent 只通过三个工具驱动:`ui-design-doc.from-images` 由一至四张已登记设计图新建并登记文档(文档内设计图身份即图片 assetId),`ui-design-doc.run-workflow` 在 Rust 内跑 `recognize → separate → write-back` 并写回 State/revision,`ui-design-doc.into-js` 产出 `ui/generated-*.js`(不推进 revision);三个工具都算项目变更观察(成功返回即算改过项目),其中只有前两个推进项目 revision;工具名与入参文案在 `prompts/runtime/texts/ui-design-doc.json`。Provider 缺失、请求失败、工具缺失或结果不匹配时保留真实 State 并返回错误,不得用 deterministic seed 伪造完成;旧 `ui.workflow.run`、多树合并、组件绑定与原型幂等桥接已退役,不保留兼容入口。
|
||||
- UI workflow 的资源桥接与 Runtime 边界以 `docs/【技术方案】UI工作流资源桥接与Runtime执行-2026-08-24.md` 和 AGC 实施计划的 2026-08-24 覆盖段为准;只生成图片、登记空 JSON 或进入普通图片画布都不构成 workflow 完成。
|
||||
|
||||
## 当前产品边界
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
## 开发中
|
||||
|
||||
- DirectProject 工具可并行调度,依赖由调用方等待,同资源事务与付费动作幂等不能放松。Web 创作先用客户端环境预检,分层验证共用持久的 `validation.maxRuns`,不改写 Provider 的 `llm.maxRetries`;成功证据按输入指纹复用,达标后交付。模型请求计时只保存安全元数据与可观测边界,未知不补零,写盘不能阻塞响应流。详见 AGC 主专题的“DirectProject 交付效率与可观测性”。
|
||||
- DirectProject 工具可并行调度,依赖由调用方等待,同资源事务与付费动作幂等不能放松。Web 创作先用客户端环境预检,分层验证共用持久的 `validation.maxRuns`,不改写 Provider 的 `llm.maxRetries`;成功证据按输入指纹复用,达标后交付。完整回合条目统一保存在 `project.jsonl`;旧平行审计和附属请求分段计时已退出生产入口,不能因残留实现或测试而恢复旧契约。界面生命周期耗时与独立模型使用记录继续有效,未知边界不补零。详见 AGC 主专题的“Direct 历史、审计与耗时的现行边界”。
|
||||
|
||||
- DirectProject 源码修改走 `agc_apply_patch`、进度走 `agc_update_plan`:SDK 原生的 `apply_patch` / `update_plan` 注册会被按回合移除(全局串行单例),不要恢复它们或用伪造工具注解换取并发。补丁只在当前项目内、受当前回合 Write 许可和受控进程约束,失败可能已部分写入,未知结果不自动重放;计划完成不构成验收证据。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user