修复:封闭 game-chat 美术重试写入边界

在 successor durable 写入前类型化拒绝动态美术 child 通用 retry
将遗留 retry-source 美术 run 收敛为只读并阻断全部 mutation
补齐终态零副作用、工具矩阵及 DAG/非美术 retry 非回归测试
同步技术方案、决策记录与踩坑说明
This commit is contained in:
2026-08-11 10:40:12 +00:00
parent e613b07fb5
commit 4d0c8e40b3
7 changed files with 722 additions and 40 deletions
@@ -1,5 +1,16 @@
# 决策记录
## 2026-08-11 game-chat 动态美术 child 通用 retry 失败关闭
- 裁决:game-chat 动态美术 child`art-director` / `art-asset-plan`parent 为单主 `code-prototype`、root 绑定 `project-supervisor-game-chat` source)不允许走通用 Agent Runtime retry。retry 入口识别该类 child 后,在创建 successor run、delegationId、task journal 或其它 durable 副作用前直接返回类型化错误,引导回到 game-chat,由当前 `code-prototype` 先完成 `asset.list` 审计,再按真实缺口建立新的 durable 美术委派;新委派自然产生新的 delegationId 与 durable delivery,沿用现行严格 lineage 判定,不新增 delivery 换绑、续发或可变机制。
- 结构性依据:delivery 的 delegationId 由父动作 ID 派生,且 targetRunId 绑定原 child run;通用 retry 铸造的 `retry:{旧run}:{新run}:{纳秒}` 身份在结构上不可能匹配任何现有 delivery。即使只圈住 retry 的写边界,其产出也没有合法消费者。让 retry 继承 lineage 必须引入可变 delivery、换绑或放宽 exact binding,与不可变事实和失败关闭方向冲突,明确不采纳。
- 纵深防御:执行策略层把 `source=agent-delegate-retry` 的 game-chat 美术 run 视为“声称委派 lineage 但无法证明”,只允许现有诊断性只读白名单,全部可变工具 fail closed;不得因严格授权返回 false 而回落为普通美术 Agent 权限。首次 `agent-delegate` 与遗留/伪造 `agent-delegate-retry` 共用同一动态美术 lineage 分类入口,但严格 Canvas、route、delivery 授权 predicate 继续只接受现行可证明的首次委派,不为 retry 放宽。
- 错误 wire:现有 Tauri retry 命令继续保持 `Result<AgentRuntimeResult, String>`M0B-1 不修改命令 DTO。Rust 内部使用稳定的类型化 retry error kind,并在 String wire 中固定保留 `kind=game-chat-dynamic-art-retry-unsupported` 与公开恢复建议;测试同时断言 error kind、可见文本和零 successor durable 副作用。
- 范围限定:仅限上述 game-chat 动态美术 child。完整 16 任务 DAG 的 children 走文件交接、不依赖 delegationId delivery,其通用 retry 语义不变;非美术委派与顶层任务 retry 亦不受影响。前端隐藏或置灰该类 child 的 retry 控件归 M0B-2 source-aware 投影收敛,在此之前由后端类型化错误兜底。
- 实现边界:入口守卫落在 `runtime_driver/lifecycle_control.rs``retry_game_creator_agent_runtime_task_at`,必须使用不要求 child 仍为 running 的结构身份分类;`resolve_game_creator_agent_runtime_retry_configuration_at` 保持不变。纵深防御落在 `runtime_tools/file_ops.rs` 的动态美术分类入口;严格 lineage/Canvas 授权 predicate 本身不放宽。除这两处与对应测试外,不扩展 M0B-1 生产改动面。
- 验证方式:终态 failed/cancelled 动态美术 child 的 retry 返回类型化错误且不产生新 run;遗留或伪造的 `agent-delegate-retry` 美术 run 对 `file.write``project.patchset``canvas.asset_generate` 及其它全部可变工具阻断;完整 DAG child、非美术委派和顶层 retry 非回归;只有当前 `code-prototype` 重新审计并创建新委派、且新 delivery 全链一致时,新的首次 `agent-delegate` 才恢复 `assets/**` 授权。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md`
## 2026-08-11 固定 owner 产物验证与可玩验收分离
- 背景:真实新项目初始化后没有 `package.json`,默认 `game/index.html` 只是无 `<canvas>` 的占位页。`design-foundation``balance-seed``art-asset-plan``audio-asset-plan` 位于 `code-prototype` 上游,只负责策划、数值、美术清单和音频清单;若要求它们执行 `project.verify``game.static_smoke`,前者没有可执行合同,后者只能检查尚未生成的占位游戏并必然失败。曾在测试中预先写入 `fake_llm_game_draft()` 会把占位入口替换成可玩页面,从而掩盖这条真实新项目死锁。
@@ -14,6 +14,14 @@
- 关联:相关文件、文档、提交或 Issue
```
## 严格 delegated 授权失败不能把 retry 回落为普通美术权限
- 现象:game-chat 动态美术 child 以通用 retry 创建 `source=agent-delegate-retry` 的新 run 后,严格 Canvas/delivery 授权只接受首次 `agent-delegate`,返回 false;调用方却把 false 解释为“不是受限动态美术”,使 retry 回落到普通 autonomous 美术权限并可能写入 `game/**``.agent/**`,调用 preview 或继续委派。
- 原因:代码混用了“是否声称动态美术 lineage”和“是否已证明当前首次委派授权”两个事实。retry 使用新 run 和新 delegationId,但没有与之绑定的 durable delivery,结构上无法满足现行 exact lineage;授权失败应表示不可信候选,而不是普通 Agent。
- 处理:game-chat 动态美术 child 在通用 retry 入队前以终态可用的结构身份分类并返回 `kind=game-chat-dynamic-art-retry-unsupported`,引导当前 `code-prototype` 重新 `asset.list` 后建立新委派。对遗留/伪造的 retry source,美术分类入口只允许诊断性只读工具,全部 mutation fail closed;严格首次委派 predicate、delivery 和 Canvas 凭证不换绑、不续发、不放宽。完整 DAG、非美术委派和顶层 retry 不受影响。
- 验证:覆盖 failed/cancelled child 重试零 successor run、遗留 retry 的 file/patchset/Canvas/command/preview/再委派拒绝、新首次委派 delivery 全链一致后恢复 `assets/**`,并对完整 DAG 与非美术 retry 做非回归。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs``docs/project-memory/shared-memory/decision-log.md`
## 不能用可玩游戏 smoke 验证 code-prototype 上游的固定文档产物
- 现象:真实新项目的 `design-foundation``balance-seed``art-asset-plan``audio-asset-plan` 已写完自己的固定文件,却始终无法收束;`project.verify` 因项目没有 `package.json` 不可用,`game.static_smoke` 又报告缺少活动 `<canvas>`。测试若先调用 `fake_llm_game_draft()`,同一路径却会“通过”。