合并最新主线并解决首页提示冲突
Project CI / Repository checks (pull_request) Successful in 3m21s
Project CI / Frontend tests (pull_request) Successful in 3m35s
Project CI / Backend tests (pull_request) Successful in 5m47s
Project CI / Native shell tests (pull_request) Successful in 16m21s

合并 master 最新主线变更
保留首页立项策划入口与运行视窗播放入口
隐藏首页状态行及最近项目副标题并更新回归断言
This commit is contained in:
2026-08-24 22:05:44 +08:00
173 changed files with 50506 additions and 1704 deletions
+6
View File
@@ -17,6 +17,12 @@
## 当前专题
### AI 游戏创作与 Agent Runtime
- [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)
- [立项策划 Agent(Fast GDD)技术方案](<./technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md>)
- [AI 游戏创作 Agent Runtime V1.1](<./technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md>)
### 图片编辑器与 Agent
- [客户端素材创作无限画布阶段一合同](./technical/【技术方案】客户端素材创作无限画布阶段一合同-2026-08-05.md)
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
# 文档地图与阅读索引
更新时间:`2026-07-16`
更新时间:`2026-08-10`
## 当前文档入口
@@ -14,7 +14,7 @@
| 创作入口、草稿架和玩法链路 | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md` |
| 创作流程统一阶段计划 | `docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md` |
| 宿主壳、移动 App、桌面 App 与 AI H5 沙箱边界 | `docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`、`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md` |
| AI 游戏创作独立 App、Agent Runtime、Runner、浏览器验证与动态隔离子 Agent | `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` |
| AI 游戏创作独立 App、立项策划、Agent Runtime、Runner、浏览器验证与动态隔离子 Agent | `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`、`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` |
| 本地启动、验证、部署、埋点和运营查询 | `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md` |
| 微信小程序虚拟支付 | `docs/【技术方案】微信虚拟支付接入-2026-05-26.md` |
| UI 像素资产与 9-slice 规范 | `UI_CODING_STANDARD.md` |
@@ -44,6 +44,13 @@
3. `docs/【项目基线】当前产品与工程约束-2026-05-15.md`
4. 相关前端组件、service、shared contract 和后端 module
AI 游戏创作独立 App / 立项策划 / Agent Runtime:
1. `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
2. 涉及立项策划、Fast GDD、审批或构建基线时,读取 `docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`
3. 涉及 Runtime 工具、持久化、恢复或 Profile 时,读取 `docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`
4. 涉及正式工作台 UI 时,读取 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`
生产部署 / 服务器 / Jenkins:
1. `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
+86 -4
View File
@@ -1,5 +1,68 @@
# 踩坑与排障记录
## 2026-08-15 把校验往链路前面挪,改的不是严格程度而是作用域
- 现象:CI 全量 5 条失败,看上去毫不相干(两条 Goal 续跑停在 `needs-reconciliation`、一条交接用例断言错误文案、一条恢复用例把不可读 state 的错误抛了出来、一条 Linux-only 用例错误码对不上),实际只有 3 个根因,且三者是**同一个形状**:新增或既有的检查被放在了链路更靠前的位置,于是它的语义作用域被悄悄放大或提前,而不是「变严」。
- 形状一(判据上提 → 作用域从同 loop 变成跨 loop):`tool_plan_handoff/identity_order_validation.rs` 的 `same_tool_plan_repair_chain` 含 steer cursor、goal revision/快照与 planning session binding 这些**本轮量**,只在同 loop 的 repair 之间才必须逐位相等。`M1B-2`(`27c3eb847`)把它提到 `loop_iteration` 分支之外后,`loop-N → loop-N+1` 的正常续跑必然被判成「身份冲突」,整个 run 进 `needs-reconciliation`。**同一次上提还把分支内那句同名检查变成了死代码**——编译器不报,测试拿到的是外层文案,于是表现成「断言的错误文案不对」这种看起来无关的症状。模块内本来就有反例可对照:`ledger.rs` 的 `is_later_repair_identity` 一直把这条判据显式限定在 `current_loop == candidate_loop`。
- 形状二(探测器的前置条件变成整条链路的 gate):`recovery_scan.rs` 的 `missing_plan_submit_anchor_candidate_at` 用 `?` 强读 runtime state,而它被挂在每个 Agent resume 循环的**最前面**。真正负责处置「state 不可读」的是它下游的 `resume_game_creator_agent_finalization_at`——那里会从 task record 重建并 fail-closed 到 `needs-reconciliation`。前面这一 `?` 把整轮 resume 打断,**恰好绕过了专门为这种情况写的兜底**。这个探测器甚至不适用于出事的 Agent(它只认 planning agent + `agent-delegate`),读失败纯属前置成本。
- 形状三(通用解析器排在专用校验之前,错误码被压平):`planning_storage.rs` 把 `resolve_local_project_path` 的失败整体映射成 `PLAN_INVALID_PATH`,但该解析器会先于 planning 自己的校验逐组件拒绝符号链接。于是「不可信路径」被报成「非法路径」,与同模块 `ensure_planning_parent`、`verify_regular_planning_file` 的分类自相矛盾。这条自 `M1B-1`(`f453c2ca2`)写下就没绿过——用例是 `#[cfg(unix)]`,Windows 上编译都不参与(`0 tests`),只有 Linux CI 能看见。
- 处理:形状一改回 loop 分支内,并补一条正向回归(跨 loop 且 steer cursor / goal revision 已前进必须被接受),把边界钉住而不是只钉拒绝;跨 loop 身份由 `same_durable_tool_plan_run` 守,binding 漂移另有 `provider_retry.rs` 的 per-request 判据兜底,去掉上提不留缺口。形状二把强读改成「读不到就 `Ok(None)`」,让处置权回到下游兜底。形状三新增 `resolve_planning_path`,先用模块自己的 `planning_metadata_is_link_or_reparse` 判链接/重解析点并返回 `PLAN_UNTRUSTED_PATH`,再交给通用解析器;模块内 13 处解析全部改走它,分类统一。
- 定位手法(比二分快得多):失败用例的临时项目目录在 panic 后不会被清理(`fs::remove_dir_all` 写在用例末尾),直接读里面的 `.agent/agent.db` 与 `.agent/runtime/agents/*.json`。本次两条 Goal 失败在 db 里留下 `failureKind=tool-plan-integrity`、`errorChars=55`、`errorSha256=0750f609…`,把候选错误文案逐条算 SHA-256 一比即命中,`requestSlot` 从 `loop-1-repair-0` 到 `loop-2-repair-0` 直接指出是跨 loop 那一跳。**错误只留指纹不留原文时,指纹就是可检索的**。
- 验证:4 条可在 Windows 复现的用例全部转绿(含新增回归);Linux-only 那条在 WSL 上跑通(`/home/dev/agc` + `CARGO_TARGET_DIR=/home/dev/agc-app-target`,从 Windows 工作树 `git fetch` 后打 patch 验证,验完还原分支)。注意 `ai-game-creator-app` 是 `Genarrative` 的 git worktree,WSL 里不能直接 fetch 它的路径(`.git` 文件写的是 Windows 路径),要从 `/mnt/c/projects/narrative/Genarrative` fetch。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/tool_plan_handoff/identity_order_validation.rs`、`.../agent/runtime_driver/recovery_scan.rs`、`.../agent/runtime_protocol/planning_storage.rs`;decision-log 同日条。
## 2026-08-15 `#[cfg(windows)]` 里的代码不参与 Linux CI 编译,CI 绿不代表能构建
- 现象:把 master(`9f5c84ee7`)合进 `feat/five_min_design` 后,`cargo check --all-targets` 在 Windows 上直接 `error[E0658]: use of unstable library feature 'windows_by_handle'`,位置是 `apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs` 的 `metadata.number_of_links()`。该文件与 `origin/master` **逐字节相同**,即 master 自身在 Windows 上就构建不过。
- 原因:`std::os::windows::fs::MetadataExt::number_of_links` 至今未稳定(rust-lang#63010),而 `rust-toolchain.toml` 锁的是 stable `1.96.0`。引入它的提交是 `578f8019f`(优化 AGC 项目入口并识别 Godot 工作区),其中 unix 分支用 `MetadataExt::nlink()`(已稳定)、windows 分支用了未稳定的对应物。**Linux CI 上 `#[cfg(windows)]` 整块不参与编译,所以 CI 全绿。**
- 更普遍的形状:只要一段代码只在某个 `#[cfg(target_os)]` 下编译,它就完全绕过了其它平台的 CI——不只是 unstable feature,还包括类型错误、借用错误、缺失 import。跨平台分支是「双写」,两侧都得有人真的编译过。
- 处理:本仓库对「文件是不是无硬链接普通文件」早有既定写法——自己声明 `ByHandleFileInformation` 并调 `GetFileInformationByHandle`,见 `runner/endpoint.rs`、`tool_plan_handoff/storage_windows.rs`、`project/agent_db.rs`、`git_inspect.rs`、`image_inspect.rs`、`agent/generation/canvas_generation.rs` 共六处。`manifest.rs` 已按同一写法改写,并保留原错误文案与 fail-closed 语义(取不到句柄信息与「确实是硬链接」同等拒绝),另按 `endpoint.rs` 的先例一并拒绝 directory / reparse point。**该修复目前只在 `feat/five_min_design` 上,须回流 master,否则下次合并会再撞一次。**
- 验证:改后 `cargo check --offline --all-targets` 通过、`cargo fmt --check` 通过、`project::manifest` 与 godot 相关定向测试 65 passed / 0 failed。判断「是不是本次合并引入」的通用手法:`git diff origin/master -- <file>` 为空即说明该文件就是 master 原样,问题不在合并。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs`;`rust-toolchain.toml`;master 提交 `578f8019f`。
## 2026-08-14 planning sidecar 必须区分“可读”与“可写”,canonical bytes 也不等于 typed 指纹
- 现象:如果为了保护 Runtime-owned 事实,直接把 `.agent/planning/**` 加进现有 private-control **读**门,planning 子 Agent 的 `file.read` / `file.list` 会一起失败;反过来若只依赖工具面约束,通用 `file.write`、`file.patch`、`file.delete`、`project.patchset` 或 checkpoint restore 仍可能覆盖 GDD/session。另一个常见误判是把“能反序列化且语义相同”的 JSON 当成已提交文件,导致尾换行、字段重排或重复键绕过不可变事实的字节身份。
- 原因:`.agent/planning/**` 是 Runtime 专用 durable sidecar,但 planning Agent 需要只读观察;`game/fast_gdd.md` 又是 Runtime renderer 的人读投影,二者都不能复用“读写一体”的旧 private path 判据。存储 bytes 与 typed fingerprint 是两个门:前者约束磁盘 canonical serialization(Rust struct 字段顺序、compact UTF-8、无 BOM/尾空白、无重复键),后者约束 domain-separated 业务 payload 的完整性;只过其中一门都不能视为 authoritative。
- 处理:保持现有 private-control **读**门不含 planning,新增只挡写 predicate;所有通用 mutation 与 restore 路径在推进 revision 前先拒绝 `.agent/planning/**` / `game/fast_gdd.md`,专用 writer 再校验 `project-planning + agent-delegate + standard + project-supervisor` 身份。GDD/index 等不可变文件走项目锁、同目录临时文件、`sync_all`、回读与 no-replace 发布;相同 canonical bytes 才是 replay,任何其它内容都是 identity conflict。session 只保留一个 `.session.json.previous`,primary 损坏时 fail closed,不得拿 previous 猜测新旧。
- 验证:先用 planning Agent 的只读 action 验证 sidecar 可列出/读取,再逐项证明 `file.write`、`file.patch`、`file.delete`、`project.patchset` 与 checkpoint restore 均拒绝;storage 测试应覆盖 duplicate key、BOM/尾空白、字段顺序、symlink/目录/硬链接、create-only replay/conflict、session 缺 primary 提升、primary 损坏和合法 successor。第 9.1 节 golden vector 当前为 3857 bytes / `sha256-serde-json-v2:a59856de7ef134cf2f49c4dedd2ba10ae4ab2340a9634d402eb792b6ee5458f0`。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs`、`apps/ai-game-creator-shell/src-tauri/src/project/filesystem.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs`、`apps/ai-game-creator-shell/src-tauri/src/patchset.rs`、`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` 第 8.3~10.2 节。
## 2026-08-12 在 autonomous-game-build 下试图向用户提问,会让整条工作流永久瘫痪
- 现象:给自主构建链路加「问用户一句」的需求时,最自然的两个想法——让 DAG 节点自己问、或让父 Supervisor 代问——**都不成立**,而且第二个的失败方式是灾难性的。
- 拦截一(节点自己问):`apps/ai-game-creator-shell/src-tauri/src/user_input.rs:367-394` 的 `validate_user_input_action_owner` 三路 OR 拒绝任何带 `parent_agent_id / parent_run_id / delegation_id` 的 run。autonomous 的 ready-task 调度器在 `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs:905-907` **显式**给每个 DAG 子节点写 parent,因此必然命中。
- 拦截二(父代问):`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs:198-215` 只要 `run_profile == autonomous-game-build` 就把 `user.input_request` 从 `auto_tools` / `confirm_tools` 移除并推进 `denied_tools`;`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs:2604-2616` 在执行层再拒一次。**两处判据都只看 profile、不看 `agent_id`,所以 root Supervisor 自己也在禁令内**——「父能问、子不能问」这个中转赖以成立的不对称,在 autonomous 下根本不存在。
- 真正的坑(后果放大):硬闯不是「这次失败」,而是**整条流水线停摆**。执行层拒绝会走 `mark_game_creator_agent_runtime_needs_reconciliation_at`,`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs:1157` 把 `runtime.status` 直接写成 `failed`;此后每次调度 ready task 必经的 `autonomous_game_build_root_task_is_active`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs:685-693`)只认 `pending | running | waiting-for-confirmation | waiting-for-user-input`,`failed` 不在其中,于是报「父 Run 已不再活跃」,**剩余节点一个都起不来,须人工核对才能恢复**。同一类故障本仓库已踩过一次,见下方「自主模式不能保留任何 RequiresConfirmation 漏口」条。
- 也别想着换 profile 绕开:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/run_configuration.rs:264-266` 明文「子 Run 不能切换父 Run 的 Run Profile」,且 profile 是 run 绑定时 CAS 锁死的终身属性。
- 处理:需要与用户往返的链路必须整体跑在 `standard` profile 下。`standard` 的 ready-task 调度器(`task_start.rs:566-580`,走 `..._with_source_at` 而非 `..._with_link_at`)不写 parent,节点是自己的 root run,上述拦截一并不适用。这是 2026-08-12 立项策划 D9 改用「Supervisor + standard 下游工作流节点」的直接原因,见 decision-log 同日条。
- 附带结论:`autonomous_owner_artifact_validation_available_for_run_at`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs:416-441`)同样四重绑死 owner 白名单、profile、source,且**要求 `parent_agent_id` 为 Project Supervisor**——standard 节点无 parent,第 436 行即不通过。想给 standard 路径加 owner 产物验证的人不要试图扩展它,必须另建。
## 2026-08-12 往 trusted supervisor source 里加新 source,等于同时授予 Goal Contract 参与者身份
- 现象:`agent_runtime_supervisor_source_is_trusted`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs:113`)读起来像一个「谁能启动 Project Supervisor」的入口白名单,实际早已是多条互不相干的授权判据的共同开关。2026-08-11 合入动态目标验收图后,它的非测试消费者从 2 个文件涨到 10 个文件 18 处调用:run 启动(`commands.rs:698`)、steer(`commands.rs:876`、`steering.rs:763`)、Goal Contract 创建权限(`goal_contract.rs:496`)、根控制面工具是否被剥离(`provider_request_builders.rs:134` 的 `root_control_authority`)、验收图完成门(`acceptance_graph.rs:595`)、run configuration、lifecycle_control、task_start、project_gates、autonomous_completion。
- 陷阱:这些判据**全都不看 Run Profile**。`goal_contract_acceptance_completion_blocker_at_locked`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/acceptance_graph.rs:568-611`)只要求「`agent_id` 是 `project-supervisor` + binding 是无 parent 的 root + source 可信」,未冻结 Goal Contract 就返回 `blocked`,并被 `main_loop.rs:213`、`main_loop.rs:1803`、`finalization.rs:398` 消费。因此给一个**用途完全不同**的新 source(例如立项策划的 plan chat)加进白名单,会让它的根 Run 立刻背上「必须先调 `agent.goal_contract`」的义务;如果该 source 的工具面按 exact allowlist 设计、不含这个工具,根 Run 就永远无法完成——而且症状是 run 卡在完成门,不是启动失败,排查方向容易跑偏。
- 更坏的一半:把新 source 排除出白名单**并不能**脱身。同一协议还有一道入口门 `validate_root_goal_contract_control_plan_at`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs:171`),由 `provider_tool_plan.rs:434` 在通用 tool-plan 解析路径上无条件调用,判据只有「`agent_id` 是 `project-supervisor` + binding 的 root 是自己 + 存在 run profile binding」——**连 source 都不看**。合同不存在时它强制本轮恰好一个 `agent.goal_contract` 动作且 `plan_update`/legacy plan/`response` 全为空,于是「第一轮先问用户一个问题」或「第一轮先回复」的 Agent 会被直接判协议错误。三处判据里只有 `agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID` 是共同项,改 `agent_id` 是唯一能一次性解耦的做法。
- 为什么现役 Agent 没事:工具面是 **deny-list** 模型。`agent_runtime_tool_policy_snapshot_at`(`tool_policy_snapshot.rs:130-178`)的 `allowed_tools` 直接等于全量 `agent_runtime_executable_tools()`,`agent.goal_contract` 天然在内;`standard` profile 在 `agent_runtime_tool_policy_snapshot_for_run_at:198` 提前返回、不做裁剪;只有 `!root_control_authority && goal_contract_participant` 的后代才在 `provider_request_builders.rs` 被剔除。所以 gui/cli/game-chat 根 Supervisor 默认就握着这个工具,两道门对它们是「照着做」而不是「过不去」。**按 exact allowlist 设计工具面的新 source 才会撞上**,而 allow-list 正是更安全的那个方向——这条陷阱专门惩罚更严格的设计。
- 处理:新增 trusted source 前,先逐个确认这些调用对新 source 的语义是否成立,尤其是 Goal Contract 创建、验收图完成门与 steer 三处,再单独确认不看 source 的入口门;需要区分时,应当拆出「可信入口」与「Goal Contract 参与者」两条判据,而不是继续复用同一个函数。立项策划已按第 23.1 节裁决进 matcher 并参与 Goal Contract;steer 用独立于 matcher 的显式否决(`reject_supervisor_plan_root_steer`),不得用「不进 matcher」实现。复核结论见 decision-log 2026-08-13 `M1A-1` 条。
- 双向提问:调用点**既是判据又是构造器**时,只问「会不会误得不该有的语义」不够,还要问「落到通用兜底会不会丢掉该有的语义」。`resolve_game_creator_agent_runtime_retry_configuration_at` 因此在 `M1A-1` 漏出,由 `M1A-3` 补强判据与保源;拒绝继续用弱判据,授予必须用强判据。
- 相关:`requiredEvidence` 只接受 `tool:<Runtime 工具名>` 且必须命中 `agent_runtime_acceptance_evidence_tools()`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs:74-96`,当前 18 项)。确定性收束的任务和 Runtime 内部产物验证都不产生 Provider 回执,因此无法为验收节点提供证据——不要指望「让 Runtime 自己验一下」能满足验收图。
## 2026-08-12 给 manifest 加"新鲜度门控"或身份字段的两个陷阱
- 现象:想给 game-chat 投影里的 manifest 读取加保护时,两条看起来最自然的路都会失效,且失效方式都是静默的——代码跑得通、测试也能编出来,但保护根本没生效。
- 陷阱一(时间戳单位):`AgentRuntimeState.updatedAt` 来自后端 `unix_timestamp()`,返回的是**秒**(`apps/ai-game-creator-shell/src-tauri/src/project/filesystem.rs:725-730` 的 `as_secs()`),全链路透传到前端不做任何换算。任何 `observedAt >= main.updatedAt` 形式的新鲜度门控,若 observedAt 取 `Date.now()`(毫秒),两侧相差约 1000 倍,判定恒为真、拒绝不掉任何陈旧读数,等于死代码。前端已有 `gameChatMessageTimestampMilliseconds`(`apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx:130-143`,`< 1_000_000_000_000` 则 ×1000)专门处理这个换算,任何新增的跨端时间比较必须复用它,不要重新裸比。
- 陷阱二(补身份字段只堵一条路):改写 `task.status` 的写入路径有两条互相独立的。除 `update_manifest_task_status_at`(`apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs:590-608`)外,还有毫无秩序守卫的 `set_task_status`(同文件 `580-588`,直接 `task.status = status`),其调用方 `record_draft_task_progress`(同文件 `387-413`)把 `code-prototype` 列进批量置 `Completed` 的清单,由用户可随时触发的 `game.generate_draft` 命令调用。只给前者补 `statusRunId` / `statusSource`,后者会原样保留上一次写入的旧身份印记,于是污染写入反而通过校验,比不校验更危险。对照组是 `apps/ai-game-creator-shell/src-tauri/src/agent/generation/trace.rs:613-637` 的 `set_task_status_if_current`,它有 `should_replace_task_status` 秩序判定——两者的不对称本身也是一个待处理项。
- 处理:本阶段不加机制,manifest 明确降级为 lineage 判定通过后的补充信号(见 decision-log 2026-08-12 条)。将来要做,必须同时覆盖两条写入路径,并统一走毫秒换算 helper。
- 验证:`apps/ai-game-creator-shell/tests/agentRuntimeModel.test.ts` 的「keeps an unbound manifest out of the verdict until the current main is terminal」钉住了现有边界与残余风险;该用例最后一条断言即为已记录的残余风险,改动它就意味着重新裁决,必须同步更新决策记录。
## 2026-08-11 game-chat 阶段归档不能只保存 current-by-agent map
- 现象:前端 Runtime map 以 Agent ID 保存当前记录。新一轮仍会复用 `code-prototype`、`art-director`、`art-asset-plan` 这些 Agent ID;若旧 root 已终态但 main/美术 child 或 manifest 仍在收口,直接从 live map 归档会在新 root 接管后丢失旧后代,或把新轮证据误接到旧阶段记录。
- 原因:Agent ID 只是当前视图索引,不是跨 root 的运行身份。game-chat 的正式投影身份至少需要 `agentId + sessionId + runId + source + parentAgentId + parentRunId`,而阶段记录还必须绑定 source-bound root。
- 修复:根进入终态时按完整 `agent/session/run` 身份保存 root-scoped Runtime 快照,后续只合并同一稳定身份的更新;manifest 快照只在该 root 仍为当前 root 时捕获。归档前重新执行严格 lineage、全终态、单 main/单 active art 与 reconciliation 门禁,并用 root run 派生稳定 message ID。
- 防回归:测试必须覆盖 root 先终态、main 或 child 仍 active、main 仍等待 delegate receipts、manifest hydration 滞后、initial hydration 和历史阶段记录去重。完整 DAG 继续使用自己的投影,不能把 game-chat selector 反向推广为全局任务图真相。
> 用途:记录已验证、未来很可能再次遇到的问题。每条都应包含现象、原因、处理方式和验证方式。
## 记录格式
@@ -14,6 +77,25 @@
- 关联:相关文件、文档、提交或 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`,引导用户继续 game-chat 对话,由下一轮 main `code-prototype` 重新 `asset.list` 后建立新委派。同一 main run 的同 target 重委派继续遵守“每个缺口最多一次”,不为失败 child 开豁免。对遗留/伪造的 retry source,美术分类入口只允许诊断性只读工具,全部 mutation fail closed;严格首次委派 predicate、delivery 和 Canvas 凭证不换绑、不续发、不放宽。完整 DAG、非美术委派和顶层 retry 不受影响。
- 验证:覆盖 failed/cancelled child 重试零 successor run、遗留 retry 的 file/patchset/Canvas/command/preview/再委派拒绝、失败回执认领后同 run 同 target 重委派拒绝、下一轮 main 重新审计后同 target 新委派放行且 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()`,同一路径却会“通过”。
- 原因:初始化 `game/index.html` 只是无 `<canvas>` 的占位页,真正游戏要到下游 `code-prototype` 才生成。把所有 mutation verification 都等同于可玩游戏 smoke,会让上游 artifact-only owner 在依赖顺序上自锁;预写 fake game 的夹具提前完成了下游职责,掩盖了真实新项目路径。
- 处理:四个 pre-code 固定 owner 在最终收束门由 Runtime 内部验证 canonical 产物:普通文件有界读取、非空,JSON 可解析,无 incomplete marker,且相对根完成合同 baseline 已变化。该能力不进入 Provider 工具目录,不新增 commandId;凭证类型为 `runtime.owner_artifacts_validate`,只写普通 `verifiedRevision`,不得写 `staticSmokeVerifiedRevision` 或制造 smoke / preview trace。文件路径门与验证必须复用同一 canonical owner 映射。`code-prototype` 和 `preview-readiness` 继续执行真实 `game.static_smoke`,`preview-playtest` 继续独立浏览器验收;`publish-package` 不借本修复扩入内部验证。
- 身份与恢复:只允许完整 GUI / CLI 16 任务 DAG 的 `agent-ready-task-scheduler` 确定性直接 child、当前活跃根和正确 parent/binding;错误 source、delegated run、历史/终态根、非当前 root、跨 Agent/run 一律失败关闭。owner 再次 mutation 必须令旧凭证失效;相同身份恢复时可按当前磁盘事实确定性重验。
- Canvas 补充:未配置 External Editor API Key 时 `art-director` 是只读协调;配置 Key 时它是条件 Canvas owner,只广告并执行 `canvas.asset_generate`,拒绝 `project.verify`、`command.run_limited` 和 preview。game-chat 临时 `art-asset-plan` 必须同时满足当前 `code-prototype -> agent-delegate`、durable delivery 与 binding 身份,并只认本人 Canvas 生成凭证,不能借普通验证。四个 fixed owner 配置 Key 后仍须满足既有图片、Canvas 登记和视觉门;内部文件验证不替代这些证据。
- 可玩凭证补充:完整 DAG 的 `code-prototype` 不能用已通过的 `project.verify` 代替本人 `game.static_smoke`;完成门要求 `staticSmokeVerifiedRevision >= mutationRevision`。GUI / CLI 的 preview 回执只能由确定性 `preview-playtest` child 生成,game-chat 只由唯一主 `code-prototype` 生成;回执必须冻结 executor Agent/run/source/binding,旧 v1 回执按缺失处理并重跑。
- 并发恢复补充:自主根任务 journal 写入后建立或重建 completion contract 时,初始 manifest reset 与 continuation reconciliation reset 不能重新使用 fail-fast 项目锁。异步 child finalization 可以合法插入两次取锁之间,使已入 journal 的新根被误记为 `completion-contract-failed`。这两条 reset 必须使用现有有界等待项目锁,超时仍失败关闭;只验证 scheduler 合同的测试应预占 child Runtime lane,不能真实启动后台 worker 后再手工改 manifest。确定性回归要显式持锁,分别证明初始合同与 continuation 合同等待释放后成功落盘。
- 验证:夹具必须从 `init_local_game_project_at` 开始,先断言无 `package.json` 且占位入口 smoke 失败,再证明产物不齐阻断、齐全后内部验证通过、无 smoke trace、再次 mutation 失效;另覆盖四个 owner 路径矩阵、错误身份、恢复、跨 run 凭证、`art-director` 有/无 Key、动态美术借凭证拒绝、`code-prototype` project.verify-only 阻断和试玩 executor 身份。
- 关联:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`。
## UI 设计 State 的 strict JSON round-trip 不能混用两种浮点序列化表示(2026-08-19)
- 现象:为节点拖拽/缩放生成非整数 Transform 后,保存报“UI 设计 State 安装后回读与待写内容不一致”;由于读取主文件失败关闭后恢复 `.previous`,后续回读表现为刚导入的 spirit/sprite 资产丢失。
@@ -4087,7 +4169,7 @@
- 现象:视觉 Agent 发现候选图不合格后,可能先删除 `assets/ui-prototype.png` 或 `assets/art-spritesheet.png`,再用猜测的尺寸、比例或另一条路径重新生成;远端生成期间项目文件又可能被其它 Agent 更新,迟到结果覆盖较新的文件。`design-foundation` 为了让静态或浏览器检查通过,也可能顺手改写 `game/index.html` 或自行启动 preview。
- 原因:把“允许一次语义返工”误解成“视觉 Agent 可以任意覆盖”,且只在 prompt 中描述角色职责,没有在 replacement 授权、文件写入、工具策略和提交时 fingerprint 上强制执行。
- 处理:固定 UI 与 spritesheet 路径、比例、尺寸、kind 和 label;普通生成 `replaceExisting=false`。只有 Project Supervisor 对已认领原 delivery 建立的唯一静态 repair,且父 run、目标 Agent 与 `expectedArtifacts` 全部匹配时,才允许 `replaceExisting=true` 原位替换;不得先删除固定正式产物,也不得对 repair 再 repair。请求外部生成前记录原路径 SHA-256,取得写锁准备提交时复算;不一致即按 stale fingerprint 失败关闭并保留当前文件。
- 职责隔离:`design-foundation` 只写 `memory/project.md`、`game/game_design.md` 和可选固定 UI 原型。Runtime 必须同时在单文件写入、patchset、delete 与工具 policy 层拒绝其修改 `game/index.html`、其它实现文件、启动 preview / playtest、运行进程、调用 `game.static_smoke` 或整项目恢复;只有 `preview-readiness` 可执行固定 smoke,只有 `preview-playtest` 可执行浏览器验收。
- 职责隔离(2026-08-11 补充):`design-foundation` 只写 `memory/project.md`、`game/game_design.md` 和可选固定 UI 原型。Runtime 必须同时在单文件写入、patchset、delete 与工具 policy 层拒绝其修改 `game/index.html`、其它实现文件、启动 preview / playtest、运行进程、执行 `project.verify`、`game.static_smoke` 或整项目恢复。它的两个固定文本产物由 Runtime 在收束门内验证;这不向模型开放验证工具,也不替代 `preview-readiness` 的最终静态 smoke 或 `preview-playtest` 的独立浏览器验收。
- 画布配置一致性:未配置 External Editor API Key 时,`design-foundation / art-asset-plan` 的委派合同与 manifest 终态投影必须一起降级为文本产物,不能仍把 `assets/ui-prototype.png / assets/art-spritesheet.png` 作为完成条件;配置 Key 时两张固定图片继续是严格必需产物。委派、完成合同和 manifest 投影必须读取同一配置事实,禁止一层降级、另一层仍要求图片。
- 验证与状态:当前回归已覆盖固定合同拒绝漂移、已登记 spritesheet 删除保护、静态 repair 授权、并发修改触发 stale fingerprint、`design-foundation` 的 write / patchset / delete 和 preview 工具拒绝。2026-07-27 的独立 75 分钟上限外部 E2E 已在同一轮完成两张真实画布图片、固定 `16` 任务、当前 revision 静态与双视口试玩并安全清理,当前状态为 **PASS**;后续改动仍须新轮复验。
@@ -4356,9 +4438,9 @@
- 现象:Supervisor collaboration durable isolated spawn 恢复测试或普通 `agent.delegate` 后台委派测试在默认 Tokio worker 栈下稳定 `stack overflow`;单独运行同样失败,提高 `RUST_MIN_STACK` 后通过。
- 原因:不是业务递归。debug 构建中 pending action continuation、后台 task queue、Agent 主循环,以及 Provider、Codex CLI、Codex app-server 组合模式分发的最大分支状态都会形成大型 async poll frame;恢复路径直接进入下一层状态机、普通后台任务把完整主循环放回默认 worker,或组合 future 进入泛型 helper,都会超过默认栈。
- 处理:整个 pending continuation、它进入的后台主循环,以及完成、取消或失败后 drain 同 Agent 后续队列时,都必须跨越独立 Tokio task 轮询边界,使上层 poll 先退栈后再轮询下一层状态机。传入边界的 future 必须先装箱;若泛型 helper 直接持有大型 future,即使随后 `spawn`,调用方 async frame 仍会把它保留在默认 worker 栈上。普通后台任务、静态委派子任务和 manifest ready-task 的首次执行统一复用 16 MiB 专用 Runtime worker,并在 worker 已启动后交接 Agent 任务锁;worker 创建或交接失败要持久化当前 run 失败。Provider 物理请求必须在持久重试 helper 与非持久压缩路径构造完整请求后、进入下层泛型 control/lifecycle helper 前装箱,不能等到底层 helper 才装箱。pending 边界继续保留结构化取消语义,父 continuation 被丢弃时同步 abort 子任务。不得逐个扩大 queue worker 栈,也不得增大 CI 的 `RUST_MIN_STACK` 掩盖问题,否则生产路径仍可能崩溃。
- 验证:失败用例必须在未设置 `RUST_MIN_STACK` 时通过;同时覆盖普通后台委派、policy batch 全组、拒绝 pending 后重规划并 drain 下一任务,以及 pending/cancellation 回归,证明任务锁只交接一次、恢复不重复生成 isolated spawn、队列继续推进且父任务取消不遗留后台子任务。另需运行 `background_agent_runtime_can_delegate_task_to_other_agent`、`provider_retry_`、`provider_handoff_`、`response_stream_` 与 Native shell 完整门禁,全部以默认 worker 栈通过。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_queue.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs`。
- 处理:整个 pending continuation、它进入的后台主循环,以及完成、取消或失败后 drain 同 Agent 后续队列时,都必须跨越独立 Tokio task 轮询边界,使上层 poll 先退栈后再轮询下一层状态机。传入边界的 future 必须先装箱;若泛型 helper 直接持有大型 future,即使随后 `spawn`,调用方 async frame 仍会把它保留在默认 worker 栈上。普通后台任务、静态委派子任务和 manifest ready-task 的首次执行统一复用 16 MiB 专用 Runtime worker,并在 worker 已启动后交接 Agent 任务锁;worker 创建或交接失败要持久化当前 run 失败。Provider 物理请求必须在持久重试 helper 与非持久压缩路径构造完整请求后、进入下层泛型 control/lifecycle helper 前装箱,不能等到底层 helper 才装箱。pending 边界继续保留结构化取消语义,父 continuation 被丢弃时同步 abort 子任务。不得逐个扩大 queue worker 栈,也不得增大 CI 的 `RUST_MIN_STACK` 掩盖问题,否则生产路径仍可能崩溃。**(2026-08-15 修订)判据从「逐个列举入口」改为不变量:所有会进入 Agent 主循环的 future 必须在 `agent-runtime-worker-*` 专用线程上轮询。** 原文按入口枚举(普通后台任务、静态委派子任务、manifest ready-task 首次执行),但**恢复重启是第四个入口,从未被列进去**——`recovery_scan.rs` 手写 `tauri::async_runtime::spawn` 直接跑 `drain_game_creator_agent_background_tasks`,把与 started 入口同样深的 poll 链放在默认 2 MiB worker 上;队列 drain(`spawn_next_..._with_lock`)同样留在默认栈。两条当时都还塞得下,直到 `M1B-2` 往主循环与恢复扫描加分支把余量吃穿才暴露。**枚举法漏掉一个入口不会产生任何信号**,因此改为统一常量 `AGENT_RUNTIME_BACKGROUND_WORKER_STACK_BYTES` 加单一 spawn helper;承载主循环的路径一律不得再手写 `tauri::async_runtime::spawn`。
- 验证:失败用例必须在未设置 `RUST_MIN_STACK` 时通过;同时覆盖普通后台委派、policy batch 全组、拒绝 pending 后重规划并 drain 下一任务,以及 pending/cancellation 回归,证明任务锁只交接一次、恢复不重复生成 isolated spawn、队列继续推进且父任务取消不遗留后台子任务。**(2026-08-15 补)只断言「默认栈下没崩」不够**——余量仅剩几百字节时它依然是绿的,这次崩溃前全部用例都通过,master 侧 `drain_next_*` 只剩 512~768 KiB 余量也毫无信号。必须同时**断言线程名**:drain 入口在 `#[cfg(test)]` 下记录 `std::thread::current().name()`,用例断言其全部以 `agent-runtime-worker-` 开头。该断言与栈余量无关,已用变异验证:把 `recovery_scan.rs` 改回手写 spawn 并把 `RUST_MIN_STACK` 抬到 16 MiB(因而不会溢出),用例仍以 `["tokio-rt-worker", "tokio-rt-worker"]` 失败。修复后的验收标准是「压到 1 MiB 默认栈仍通过」,而不是「默认栈下没崩」。另需运行 `background_agent_runtime_can_delegate_task_to_other_agent`、`provider_retry_`、`provider_handoff_`、`response_stream_` 与 Native shell 完整门禁,全部以默认 worker 栈通过。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_queue.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs`。
## Provider 可扩展不能用一个全局 protocol 枚举代替实例隔离
@@ -1060,7 +1060,7 @@ static delivery 在 V1.16 的 parent/target Agent、Session、run、action、del
```text
structuredResult = {
contractStatus: evidence-ready | needs-repair,
contractStatus: evidence-ready | needs-repair | needs-user-input | user-revision-requested,
artifacts: [{ path, sha256 }],
missingExpectedArtifacts: [path],
verificationRequired: boolean,
@@ -1070,6 +1070,11 @@ structuredResult = {
}
```
其中 `needs-user-input` 是静态委派澄清中转状态,`user-revision-requested` 是 Fast GDD
审批扩展预留的用户修订状态;后者不由通用 Runtime 自动生成,且不得被反序列化失败静默
降级为 `needs-repair`。用户修订的 lineage 计数语义与 M1C-0/M1C-1 边界以
`【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` 第 23.7 节为准。
`artifacts` 与 `missingExpectedArtifacts` 必须恰好分割全部 `expectedArtifacts`,路径保持合同中的精确相对路径;存在的普通文件在形成终态回执时重新计算 SHA-256。`evidence` 和 `error` 只允许有界、已清洗的安全摘要、项目内相对路径和哈希,不得保存凭据、绝对路径、私有 observation 正文或 Provider payload。旧空合同 delivery 可以继续没有 `structuredResult`,且不得为了升级格式改写原 sidecar;新带合同 delivery 进入 ready/claimed 时必须有完整 `structuredResult`。
### 客观门禁与语义验收
@@ -148,19 +148,20 @@ Supervisor 认领该回执后,由父 run 自己为每个原 delivery 逐一创
- 对话与事件:窗口固定使用 `project-supervisor + autonomous-game-build`,继续复用 active Session、External Runner、持久 conversation、流式回复、same-run steer、工具确认与用户追问。以 `/` 开头的输入必须继续走现有内置命令解析,例如 `/preview` 只能生成 `preview.start` 确认卡,不得作为自主构建任务投递给 Supervisor。game-chat 的自主链路中,Supervisor 持久化意图后只有 `code-prototype` 是主 Agent;它可能临时委派一个受限美术 child,后者只写 `assets/**`,回执返回同一主 Run 后由主 Agent 接入与验收。界面聚合当前 Supervisor 父 run、单主 Agent 及其直接美术 child 的最新原始事件,按时间倒序稳定去重并标注 Agent;默认显示 4 条,可展开至最新 20 条。原始 `summary / detail` 仍只作 Runtime 状态投影,不直接写入 conversation。需要进入聊天的事件必须由 Rust 同步生成唯一 `eventId` 与安全 `publicText`;前端只按这两个字段形成独立 assistant 消息,无 `eventId`、空 `publicText`、legacy 事件和内部 tool / Provider / Runner 协议一律忽略。
- 公开消息硬门:模型仍负责 Supervisor / 专业 Agent 回复的业务语义,Runtime 不根据 tool 或 Provider 事件自行补写业务结论;但用户直接投递的 Project Supervisor 根后台任务必须先落为不可执行的 `preparing / public-status-pending`,再以 `runtime-public-status-*` 稳定 message ID 把“任务已接收,正在启动处理”写入项目 conversation,成功后才转为 `pending / queued`;恢复预检只读,只能在验证到同 run accepted 消息后把该任务临时分类为可恢复,真实 resume 持有 Agent 锁后才可持久提升为 `pending / queued`;写入失败则落为 `failed / public-status-write-failed`,不得继续执行。这些 Runtime 公开状态只供 UI 展示,prompt 构建器必须按稳定前缀排除。根 Supervisor 通过正式失败 / 预算耗尽收束或 game-chat 绝对硬期限进入 reconciliation 时,必须在 task、event、state 等其它终态投影之前先幂等写入一条脱敏、用户可理解的失败消息;专业 Agent 命中该全局硬期限时,也必须通过权威 Run Profile 和根 task 将同一根终态写入项目 conversation,同时保留 child 私有 Session 状态;状态文件本身写坏也不能导致零公开结果。当前 Runtime 自称根 agent/run 时,其 session 和两个 parent 字段必须与权威根 task 一致;任一身份冲突必须失败关闭,不得以另一 session 派生第二条项目终态。前端把该前缀识别为 Runtime-owned,同秒时排在触发它的 Supervisor 用户消息之后,不二次持久化;仅根 Supervisor 的 `turn.started / turn.failed / turn.budget_exhausted` 只保留在 Runtime 详情和进度投影中,不能再生成第二条聊天消息,专业 Agent 的公开启动事件仍可见。该硬门不改变 final-reply 的唯一性;非 Supervisor 专业 Agent 的失败消息继续留在对应 Agent Session,不把私有诊断写进项目 conversation。
- 启动恢复和续跑边界:本条取代上一条中“只有 accepted 才可恢复”的窄口径。若进程在 Supervisor 用户消息已持久、accepted 未持久之间崩溃,只读 preflight 可以把该 `preparing` 识别为可恢复,但不改写 task/conversation;真实 resume 持有 Agent 锁后必须先幂等补写 accepted,再提升为 `pending / queued`。用户消息或 accepted conversation 已落盘而辅助审计失败时,以 conversation 为公开真相继续入队,不留下“已接收但永不执行”的任务;根终态首次公开写入的瞬时失败必须在终态投影后用相同 message ID 重试。receipt / isolated-join 等带 parent 的 Supervisor continuation 不再另写 Session 终态,只保留单一后端公开事件;`runtime-task-*` 与 `runtime-public-status-*` 共享同 run 的不透明关联摘要,秒级时间戳下多个连续任务必须按实际 run 对应的 `user -> accepted -> terminal` 顺序交错展示。
- Supervisor 进度播报:聊天消息流内保留且只保留一条当前 run 的 Runtime-owned 播报卡,由客户端从 manifest 任务图、Supervisor 结构化计划、`loopIteration`、当前动作、直接委派专业 Agent 及其持久事件确定性整理;显示当前轮次、任务 / 计划进度、活跃 Agent、最近试玩与静态检查、返工决定、代码修改和截图检查证据。同一 run 原位更新,切换 run 时替换,不调用额外模型、不追加持久 conversation,也不改变最终 assistant 回复的唯一性;任意详情必须有界且不展示绝对路径、Provider 元数据或内部指纹。运行详情弹窗在项目或 run 身份切换的同次提交中同步关闭,不能由延迟 effect 关闭用户在新 run 状态可见后刚打开的弹窗。
- Supervisor 进度播报:聊天消息流内保留且只保留一条当前 run 的 Runtime-owned 播报卡。game-chat 客户端只从当前 source-bound 根、唯一 `code-prototype` scheduler main、该 main 的动态美术 child、Supervisor 结构化计划和对应持久事件确定性整理,主阶段分母固定为 `0/1` 或 `1/1`;不再读取完整 DAG 的固定七节点,也不把旧根直属美术、错误 parent/source 或 `preview-playtest` child 混入当前轮。播报显示任务 / 计划进度、活跃 Agent、最近试玩与静态检查、返工决定、代码修改和截图检查证据;同一 run 原位更新,切换 run 时替换,不调用额外模型、不追加持久 conversation,也不改变最终 assistant 回复的唯一性。任意详情必须有界且不展示绝对路径、Provider 元数据或内部指纹。运行详情弹窗在项目或 run 身份切换的同次提交中同步关闭,不能由延迟 effect 关闭用户在新 run 状态可见后刚打开的弹窗。
- ready-task 启动活性:`background_task.queued`、`autonomous_ready_task.scheduled`、Runner heartbeat 或执行锁已移交都不等于 child 已启动。实际持有执行权的 Runner 必须在释放项目写锁后同步写入 child 的 running task、`turn.started` 与 started journal,再把已启动 state 和 per-Agent 执行锁交给已确认开始轮询的独立 execution worker;同步启动或 worker 接管失败时,要在仍持有执行锁期间依次把 child 和 manifest Graph 节点明确落为 failed,再释放锁并让 parent 收到调度错误。`autonomous_ready_task.scheduled` 只作诊断审计,其写入失败不能阻断 durable child 启动;external client 只 wake Runner,不在客户端抢占执行。Supervisor 进度卡通过 durable `startedAt`(旧 Run 从完整 task journal 恢复,最新 task-record fallback 保持 0)显示真实持续时间,并以父 Run 与当前关联专业 Agent 的最大事件时间计算运行态活跃度:运行超过 5 分钟无新事件时显示“运行中 · 疑似停滞”和静默时长;等待用户、等待确认、Provider retry、视觉资产、进程会话、pausing 与 paused 不误报。父 Run terminal 后,持续时间冻结在父 Run 自身最后活动,不随 child 晚到收口事件增长。消息时间统一校验为 JavaScript 可表示的 Date;越界值显示“时间未知”且不写无效 `datetime`。实时回复只显示 response stream 自己的 `updatedAt`,缺失时同样显示“时间未知”,不能借用其它 Runtime 活动时间或随前端时钟漂移。该提示只提供可观测性,不改变 Runtime/manifest 正式状态。
- ready-task manifest 漂移:父 Supervisor 必须分别判断“能否调度新节点”和“是否存在必须等待的工作”。派生视觉需要父规划修复时不再调度新 child,但当前最新且活跃的根 Run 下,只要存在确定性 runId、scheduler source、正确父绑定且 durable journal 为 queued/running 的 ready child,父 Run 就保持 `waiting-for-manifest-tasks`,不能因旧 hydration 快照把 manifest running 覆盖成 pending 而提前 fixed-graph-stalled。game-chat child 可在相同严格身份下容忍 pending 漂移;正式产物、Canvas、revision、`game.static_smoke` 与 `preview.validate` 门禁不放宽。GUI/CLI、旧父 Run、终态、确认/用户输入/reconciliation、伪造绑定或非确定性 runId 全部失败关闭;更新根 Run 后旧 child 不得继续维持新 DAG 或投影完成。
- Supervisor 持久决策与单主条件美术:game-chat 的关键词、用户是否报告“美术未接入”、占位状态和当前资产探测只形成 `advisoryOnly=true` 的补充上下文,不得直接重置 Graph、预完成美术节点、选择复用/生成分支或继承历史试玩类型。当前根 Run 没有持久化 Supervisor 决策时,scheduler 不启动任何 child;Supervisor Provider 只通过 auto-safe 的 `agent.route_manifest` 提交 `game-chat-workflow-decision.v2`:`intentSummary` 是 Supervisor 自行理解并持久化的用户意图,`strategy=audit-existing-first` 只是固定安全执行策略,两者不得混用。此动作不能审计、生成、委派或替代后续判断,也不能把整体视觉重做解释成整套美术的强制重生成;成功后 Runtime 只启动唯一 `code-prototype` 主 Agent。升级恢复时严格校验 v1 sidecar 的旧 fingerprint,并从完成合同绑定的有效任务恢复 `intentSummary`;旧 `code-director` coverage/route 只作为迁移输入,不作为当前完成证据,必须由同一根 Run 的 `code-prototype` 重新 `asset.list` 后原位替换为单主合同。确定性 `code-prototype` Run 仅兼容已知 canonical task 文本版本,其余 task/binding/root 身份继续失败关闭;升级前已运行的 fixed-graph 美术 child 不再具备任何 mutation 或生图权限。主 Agent 必须以当前正式资产、Canvas 登记、私有图集合同、四张语义切片和 art manifest 判断真实缺口;完整覆盖时直接接入,不得生成或扣费。只有可证实缺失 `art-spec` 或核心 spritesheet 时,主 Agent 才可对相应 `art-director` 或 `art-asset-plan` 建立一条 durable 委派;每次最多一个活跃美术 child,child 仅可写 `assets/**`,不得修改 `game/**` 或接入/验收游戏。若两个槽位都缺失,必须先完成 `art-director`,由同一主 Run 认领其 `EvidenceReady` delivery 后,才能委派依赖规范图的 `art-asset-plan`;失败或未就绪 delivery 不得消耗不可重试的图集委派槽位。主 Agent 认领必要回执后继续同一 Run 完成素材接入、原玩法语义校验、`game.static_smoke` 与桌面/移动 `preview.validate`。绝对硬截止对嵌套美术 child 继续核验 `root -> code-prototype -> agent-delegate` 完整身份并保留未知外部生成的 reconciliation 证据。Runtime 只负责校验根/父子身份、当前 revision、路径、Canvas 登记、缺口/路由 fingerprint、写入范围及完成证据;纯“继续”仍走既有正式 continuation 合同,普通美术措辞不得借用更老项目的具体试玩场景。不得以增加 loop 预算、伪造 revision、机械改写 manifest 或重放历史图片 action 代替 Supervisor 决策和程序侧审计。
- 动态美术 child 通用 retry 失败关闭:game-chat 的 `art-director` / `art-asset-plan` delivery 精确绑定原父动作派生 delegationId 与 targetRunId,不允许通用 Agent Runtime retry 换绑或继承。retry 入口以不要求 child 仍为 running 的结构身份识别当前 `root -> code-prototype -> art child` 后,在创建新 run 前返回 `kind=game-chat-dynamic-art-retry-unsupported`,引导用户继续 game-chat 对话,由下一轮 main `code-prototype` 重新 `asset.list` 并按仍存在的真实缺口创建新的 durable 委派。委派去重以 parent run 为键;同一 main run 内每个缺口最多委派一次,终态失败或取消 delivery 也不为同 run 开重试豁免,跨轮则以新 parent run、新 delegationId、targetRunId 和 delivery 自然放行。遗留或伪造的 `source=agent-delegate-retry` 美术 run 只允许诊断性只读工具,`file.write`、patchset、Canvas、command、preview、memory/manifest 和再次委派等 mutation 全部失败关闭,不能因严格授权不成立而回落普通美术权限。完整 16 任务 DAG、非美术委派、顶层 retry 与现有 Tauri String error wire 不变;前端隐藏/置灰控件留给 source-aware 单主投影阶段。
- ready-task 对账取消续跑:未知工具结果仍停在 `needs-reconciliation` 且禁止自动重放;人工核对后显式取消原 child,保留 cancel tombstone,旧 child 和旧父 Run 按真实终态收口。若随后创建同 Session、同 Supervisor source、同有效任务语义的 continuation,新完成合同只对同时具有历史 `failed / needs-reconciliation`、最终 `cancelled` 和 durable tombstone 的 ready-task,把当前 manifest 对应 failed 节点恢复为 pending,并由 scheduler 创建全新 child Run。manifest 的读取、failed 筛选、每任务一次的 child journal 索引、证据重验和写回必须位于同一项目写锁域;较新的无 child 根 Run 只有在 durable journal 精确表明为旧 failed Graph 在进入调度前即失败时才能跨过,scheduler 自身失败必须阻断借用更老 tombstone。普通失败、无 tombstone、不同 source/Session/任务语义或证据冲突均保持失败关闭;不得复活旧 pending action、补造 observation 或把取消任务标成 completed。
- 完成门静态分析预算:Canvas 视觉门必须先做只会提前拒绝的词法预检。经典或模块脚本同时不含大小写精确的 `import` 与 `export` 字节序列时,不运行模块依赖语义分析;纯 `export ... from` / `export * from` 仍须进入正式模块图分析。当前脚本不含目标文件名或任一已绑定 DOM 图片元素 ID 时,先低成本解码 `\\xNN`、`\\uNNNN`、`\\u{...}`、简单转义和续行;解码后仍无候选才不运行完整 Canvas alias / 函数可达性分析,解码不确定则保守进入 Oxc。存在任一候选时仍执行原 parser、semantic binding、解码后的 computed 属性/StringLiteral 路径、可达 `drawImage`、可见 Canvas、路径大小写和动态 namespace 写入门禁;HTML 中存在某个绑定元素不得使所有无关 JavaScript 单元进入重分析,禁止把词法命中当作通过条件。
- Provider 故障展示:Provider retry 的“是否可重试”继续使用 `upstream-5xx` 等稳定类别判断,但 durable retry record 保留安全的精确 `upstream-<HTTP status>` 身份。等待态必须从真实 record 显示 HTTP 状态、`nextAttempt/maxRetries` 与当前持久退避剩余秒数,例如“Provider 上游返回 HTTP 503,准备自动重试 1/3;预计 8 秒后重试”;不得以动画或前端自增计时伪造 attempt。重试耗尽的 Runtime 私有错误只保存 `kind/httpStatus/fingerprint/chars/retryAttempt/maxRetries/retryState`,前端和持久 conversation 仅在字段顺序、范围、状态一致且无尾随正文时派生“上游服务返回 HTTP 503;自动重试已耗尽(3/3)”。`codex_app_server` 收到 failed turn 时必须读取协议 `turn.error.codexErrorInfo`,按上下文超限、会话预算、用量、鉴权、请求、策略、sandbox、会话恢复和连接 / HTTP 状态生成封闭稳定分类;不得丢弃该字段后统一写“turn 执行失败”,也不得把 `message / additionalDetails` 原文公开。自主构建已有本地确定性完成文案时,也只允许 `empty-response / deserialize` 这类回复形状错误使用 fallback;鉴权、额度、上下文、策略、sandbox、配置、网络和上游错误必须保持失败,禁止用完成文案掩盖。正式面、阶段记录、Runtime 活动详情和持久 conversation 从稳定分类派生同一份可行动中文摘要;失败事件活动详情只消费后端 `publicText`,缺失时退回固定安全 summary,禁止公开私有 `detail`。旧 Supervisor 与专业 Agent 失败 conversation 必须在展示时经过相同安全映射。`needs-reconciliation` 是停止自动推进、轮询和活跃计数并等待人工处置的终态,明确显示为待核对,不能显示为普通运行中或已完成;未知分类仍使用固定安全兜底。Provider 响应正文、URL/query、凭据、本地绝对路径、fingerprint、字符数和 `[redacted ...]` 占位符均不得进入用户可见消息。
- 跨轮阶段记录:game-chat 父 run 进入真实 completed / failed / cancelled 终态后,客户端等待唯一 `code-prototype` 主 Run 及其所有必要美术委派都已形成真实终态,再把本轮、主 Agent 进度、是否复用/补齐素材、最新试玩 / 静态检查、最近返工决定和已登记成果图片路径整理成一条 `【Supervisor 阶段记录】` 项目 assistant 消息。父 run 先终态而 child 或 manifest 仍在 hydration 时不得以陈旧快照提前归档,要暂存终态 Runtime 并在状态刷新后重试。页面初始 hydration 若直接读到缺少阶段记录的真实终态 run,也必须补写,但 `idle` 不是可归档终态。每个“项目 + 父 run”最多追加一次,进入现有 `conversation.write` 权限与项目 conversation 持久化链路,下一轮及重载后继续保留。阶段记录不是 Supervisor Runtime 正式回复,不写入 Agent Session、不增加 final assistant 数量,也不逐条复制原始事件或内部正文。
- 跨轮阶段记录:game-chat 父 run 进入真实 completed / failed / cancelled 终态后,客户端等待唯一 `code-prototype` 主 Run、该 main 的全部动态美术 child 与 manifest 中 `code-prototype` 都形成终态,且全链没有冲突或 `needs-reconciliation`,再把本轮、主 Agent 进度、是否复用/补齐素材、最新试玩 / 静态检查、最近返工决定和已登记成果图片路径整理成一条 `【Supervisor 阶段记录】` 项目 assistant 消息。父 run 先终态而 child 或 manifest 仍在 hydration 时不得以陈旧快照提前归档;客户端以完整 `agent/session/run` 身份保留 root-scoped Runtime 快照并在状态刷新后重试,不能继续依赖会被新 run 覆盖的 current-by-agent map。页面初始 hydration 若直接读到缺少阶段记录的真实终态全链,也必须补写,但 `idle` 不是可归档终态。每个“项目 + 父 run”使用稳定 message ID 最多追加一次,进入现有 `conversation.write` 权限与项目 conversation 持久化链路,下一轮及重载后继续保留。阶段记录不是 Supervisor Runtime 正式回复,不写入 Agent Session、不增加 final assistant 数量,也不逐条复制原始事件或内部正文。
- 图片成果:当前 manifest 新增或恢复已登记的 PNG / JPEG / WebP 资源时,聊天消息流同步显示 Runtime-owned “Supervisor 成果图片”卡,最多展示最新 4 张并随 manifest 原位更新。图片必须通过现有 `read_local_project_image_preview` 读取,只允许当前授权项目中 `assets/` 下的已登记资源,继续执行 `file.read` auto 权限、真实格式、大小、尺寸、普通文件、祖先目录和项目根边界校验;前端只接受返回路径、媒体类型和 `data:` 前缀与请求完全一致的结果。缩略图点击后使用独立模态查看器,支持按钮与滚轮缩放、指针拖拽、双击 / 按钮复位、Esc / 按钮 / 遮罩关闭,移动端占满视口;不得在聊天卡下方追加展开区。图片卡不写入 conversation,不解析 assistant 文本中的任意 Markdown / 绝对路径,也不开放 `.agent` 验收截图读取。
- Run 接管:External Runner 模式下首次提交可能返回“旧 canonical state + 新 `acceptedRunId`”;页面必须以 `acceptedRunId` 作为本轮权威身份,在 state 尚未切换时显示“已投递,正在同步 Agent Runner”,并允许该 run 的 Tauri event 或轮询结果接管。不得把旧 idle state 当作本轮结果、过滤新 run 事件,自动预览授权也必须绑定 `acceptedRunId`。
- 运行容器:当前项目没有由 Tauri 客户端 `PreviewRegistry` 返回的有效 `running` 预览时,页面只渲染聊天,不显示游戏区域或占位文案,顶部运行状态必须明确显示“预览未启动”,不得再使用含义不明的“未启动”;预览运行后自动显示 iframe,桌面端按“游戏 2 / 聊天 1”分栏,移动端改为上下布局。预览停止、失败或切换项目后立即移除 iframe。运行容器继续只接受当前授权项目的 `http://127.0.0.1:*`,复用现有 CSP、iframe sandbox、autoplay、fullscreen 和 gamepad 约束;远程 URL、`file://`、手填地址或陈旧 manifest 状态均不得显示。
- 预览进程归属:External Runner 与 Tauri 客户端位于不同进程,双方 `PreviewRegistry`、server 子进程句柄和 running 状态严格进程内隔离;Runner 为 `preview.validate` 持有或回收的预览进程不能作为用户可见预览,也不能据此伪造 Tauri registry 的 running 状态。game-chat 用户可见 preview 必须由 Tauri 客户端启动、持有和停止,iframe 只使用同一 Tauri registry 返回的 loopback URL。
- 自动启动门禁:客户端只接受当前 accepted Supervisor 父 run 下真实 `preview-playtest` scheduler child 的结构化 `preview.validate` 事件,同 revision 采用最新事件且同时间失败优先;成功证据的 revision 必须精确等于当前项目原子 sidecar revision。客户端向 Tauri `preview.start` 传入 `expectedRevision`,后端在取得项目写锁后再次比对再启动,以闭合检查 / 启动 TOCTOU。same-run steer 的一次性授权使用带 revision / validation cursor 和唯一 generation ID 的 v2 记录,旧事件和旧异步 attempt 不得消费后续授权。旧 attempt 返回后的补偿清理按完整 preview identity 原子停止 registry server,不能让项目 stop policy 或写锁竞争造成后台 server 泄漏;若同项目新 server 已接管,则不能把其持久状态覆盖为 stopped。
- 自动启动门禁:客户端只接受当前 accepted、source-bound Supervisor 父 run 下唯一 `code-prototype` scheduler main 本人的证据序列:该 main 先成功执行 `game.static_smoke`,随后产生结构化 `preview.validate`,且 detail 同时满足 `passed=true`、`playtestPassed=true` 和正整数 revision。同 revision 采用最新事件且失败优先;错误 main/source/parent、旧固定 `preview-playtest` child、根 Supervisor、动态美术 child 和 smoke 之前的 preview 都不能建立可玩 revision。成功证据的 revision 必须精确等于当前项目原子 sidecar revision。客户端向 Tauri `preview.start` 传入 `expectedRevision`,后端在取得项目写锁后再次比对再启动,以闭合检查 / 启动 TOCTOU。same-run steer 的一次性授权使用带 revision / validation cursor 和唯一 generation ID 的 v2 记录,旧事件和旧异步 attempt 不得消费后续授权。旧 attempt 返回后的补偿清理按完整 preview identity 原子停止 registry server,不能让项目 stop policy 或写锁竞争造成后台 server 泄漏;若同项目新 server 已接管,则不能把其持久状态覆盖为 stopped。
- 固定试玩契约:`generic-v1` 初始状态必须为 `ready` 且 `level > 0`;点击 start 后 sequence 必须推进、phase 必须进入 `playing`,并先持续观察 2 秒、取得至少 8 个实际样本,期间保持 `playing`,以确认玩家获得正常操作机会。随后必须点击唯一可见、启用且真实可交互的 `data-playtest-id="primary-action"` 控件;该控件必须映射游戏的真实主要玩法操作,并以 sequence 相对点击前严格推进证明操作已被接受。玩家获得这次正常操作机会之前进入 `won | lost` 属于过早结束并失败;操作被接受后的单次 `lost` 是合法游戏结局,但不能成为所有受控尝试的唯一结果;若主要操作后仍为 `playing`,则继续观察 3 秒并取得至少 12 个实际样本,`won` 可提前证明非失败推进。点击 restart 后 sequence 必须再次推进并恢复到 `ready | playing`,随后持续观察 3 秒且取得至少 12 个实际样本。若首轮结果为 `lost`,重开稳定后必须再执行一次必要的 start、2 秒 / 8 样本操作机会和真实 primary-action;第二次必须进入或保持 `playing`(再观察 3 秒 / 12 样本且不得转为 `lost`)或进入 `won`,两次都固定 `lost` 代表无法正常推进的恶性 bug,必须失败。各观察窗口内 sequence 不得回退,restart 窗口只能保持 `ready | playing`;样本数门槛不能替代时长门槛,窗口末端必须强制再读取一次有效状态,不能只在前段快速取得足够样本后提前通过。控件 selector、观察时长、最少样本数、终态边界、非失败推进、末端覆盖、sequence 单调 / 严格推进规则及完整 required assertions 都进入 scenario fingerprint。读取旧 fingerprint 回执和检查 plan liveness 时,把合同升级造成的 fingerprint 不匹配视为 stale missing,允许同一 run 重新执行 `preview.validate` 自愈;身份、路径、digest 或内容完整性篡改仍失败关闭。最终完成门每次按当前合同重算 fingerprint,并严格拒绝旧 fingerprint、旧 assertion 集或仅保存历史 `passed=true` 的证据。
- 试玩证据展示:game-chat 的进度卡、可玩 revision 和自动预览只接受结构化 `preview.validate` detail 同时满足 `passed=true` 与 `playtestPassed=true`;工具 summary 中的 `:ok` 不能作为兜底。`image.inspect` 的 `status=ok` 只代表工具执行成功,不代表视觉验收通过;结构化 `passed=null` 或缺少布尔结论时,UI 必须以中性“截图分析完成”展示,只有显式 `passed=true` 才能显示“截图检查通过”。
- 一次性自动预览授权:用户在该入口成功提交本轮自主生成需求,即视为对“当前项目 + 当前 Supervisor 父 run”的一次 `preview.start` 授权。授权以仅含项目路径与 accepted parent runId 的客户端本地记录持久化,App / WebView 重启后仍可恢复,但项目或 run 身份不匹配时不得使用。只有当前 accepted parent run 成功完成 `preview.validate` 且给出有效 revision 后,客户端才可消费授权,由 Tauri 首次启动并自动展示该 revision 的用户可见预览;一次授权最多成功启动一个 Tauri preview server,并必须继续走现有权限、项目写锁、审计和客户端 `PreviewRegistry` 链路。项目或 Agent 策略的显式 deny 始终优先,不得被此授权绕过。启动成功、显式 deny、非瞬时失败、父 run 在首版验证前终止或切换项目后授权失效;`preview.start` 恰逢项目写锁竞争属于瞬时失败,不消费授权,释放写锁后由同一轮询链路重试。
@@ -1048,6 +1049,12 @@ game-project/
- `npm run agc:test:chat` 未显式指定配置且找不到 AppData 配置时,只在 stdin / stdout 都是 TTY 时询问并启动同一 `agc:config --configure-only` 向导,非 TTY 或显式无效 `--config-dir` 直接失败。测试环境只把主配置和存在时的 local overlay 复制到带随机 sentinel 的单次隔离 AppData;副本必须是独立的无符号链接普通文件,POSIX 权限为目录 `0700` / 文件 `0600`,不复制正式 Runner endpoint、lock 或其它 AppData。自动任务默认 50 分钟且可用 `--timeout-minutes` 显式设置;超时或信号会终止独立子进程树,POSIX 先向进程组发送 `SIGTERM`、等待 10 秒后发送 `SIGKILL` 并再等待 5 秒,Windows 使用 `taskkill /T` 并在强制阶段追加 `/F`。超时和信号分别以 `124 / 130 / 143` 失败退出,隔离 Runner 收束另有 20 秒上限;Runner 未空闲或收束失败时保留隔离配置和项目,验收未完成但 Runner 已安全退出时只保留一次性项目证据,不把中断报告为成功,也不误删正式 AppData。
- 自动验收现在严格要求 manifest 恰好包含固定 16 个不重复 task ID 且全部为 `completed`,并逐任务核对当前父 Run 下唯一 logical run、一次 started、一次 completed、零 failed / cancelled 和一次 manifest projection;七份基础正式产物存在并满足文件 / JSON / 非占位入口检查,当前模式具备画板服务授权时再增加 `art-spec / ui-prototype / art-spritesheet` 三张图片。PNG 验收不止检查 magic / IHDR / 比例,还会校验 chunk CRC、zlib 解压、scanline 长度、索引色 PLTE 和未知 critical chunk。Runtime 根 Supervisor 的完成合同已升级为 `game-creator-autonomous-completion-contract.v2`,`baselineArtifacts` 必填并纳入指纹。
- `design-foundation` 已增加专属职责边界:项目文件只允许写 `memory/project.md` 与 `game/game_design.md`;当前模式具备画板服务授权且合同要求界面原型时,只额外允许固定 `assets/ui-prototype.png`。它不得创建、修改、删除或补丁 `game/index.html`,不得改动其它程序实现、发布、音频或美术素材,也不得调用预览或试玩工具。
- 自动验收现在严格要求 manifest 恰好包含固定 16 个不重复 task ID 且全部为 `completed`,并逐任务核对当前父 Run 下唯一 logical run、一次 started、一次 completed、零 failed / cancelled 和一次 manifest projection;七份基础正式产物存在并满足文件 / JSON / 非占位入口检查,配置画布 API Key 时再增加 `art-spec / ui-prototype / art-spritesheet` 三张图片。PNG 验收不止检查 magic / IHDR / 比例,还会校验 chunk CRC、zlib 解压、scanline 长度、索引色 PLTE 和未知 critical chunk。Runtime 根 Supervisor 的完成合同已升级为 `game-creator-autonomous-completion-contract.v2`,`baselineArtifacts` 必填并纳入指纹,旧 v1 或缺基线合同失败关闭;最终门禁要求最后一次验证工具是 `game.static_smoke`、状态通过且 `verifiedRevision == currentRevision`。`preview.validate` 回执必须绑定同一 Agent、run、current revision、当前 `game/index.html` 摘要、固定试玩场景、持久浏览器报告以及 desktop / mobile 两张截图的路径、摘要和 PNG 身份,任一证据缺失、变化、过期或来自其它 run / revision 都阻止最终回复。旧两图合同的确定性证据不替代新三图 DAG 验收;新合同实现后必须新起独立单轮。
- 2026-08-11 M0-3 将固定 owner 产物验证与可玩验收分离。真实 `init_local_game_project_at` 项目没有 `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()` 把占位页替换成可玩页面后再证明活性;该夹具会提前完成下游职责并掩盖真实新项目死锁。
- 四个 pre-code artifact-only owner 在尝试收束时,由 Runtime 内部按 canonical owner 映射验证固定正式产物:`design-foundation -> memory/project.md + game/game_design.md`、`balance-seed -> game/balance.json`、`art-asset-plan -> assets/manifest.art.json`、`audio-asset-plan -> assets/manifest.audio.json`。同一映射还必须驱动 file write / patch / delete / patchset 边界和根完成检查。内部验证执行普通文件有界读取、非空、JSON 可解析、无 incomplete marker 和相对根完成合同 baseline 已变化;Provider 不获得新工具或 commandId,owner 也不调用 `project.verify`、`game.static_smoke` 或 preview。验证类型固定为 `runtime.owner_artifacts_validate`,只更新现有 gate 的普通 `verifiedRevision`,不写 `staticSmokeVerifiedRevision`,不生成 smoke / preview trace。
- 内部 owner 验证只接受 GUI / CLI 完整 16 任务 DAG 中 `agent-ready-task-scheduler` 启动的确定性直接 child、当前活跃根和完整 project/source/profile/Agent/run/parent/root/binding 身份。错误 source、delegated run、历史或终态根、非当前活跃根、跨 Agent/run 凭证均失败关闭;再次 mutation 使旧凭证失效,相同身份恢复可按当前事实确定性重验。本阶段不扩到后置 `publish-package`。`code-prototype` 与 `preview-readiness` 继续执行真实 `game.static_smoke`,`preview-playtest` 继续独立执行浏览器验收;任何 owner 文件凭证都不能替代可玩证据。
- M0-3 可玩与条件 Canvas 凭证继续按执行 owner 隔离:完整 DAG 的 `code-prototype` 即使 `project.verify` 已通过,仍须由本人取得覆盖 `mutationRevision` 的 `staticSmokeVerifiedRevision`;GUI / CLI 的试玩回执只能由当前确定性 `preview-playtest` child 写入 executor Agent/run/source/binding,game-chat 只保留唯一主 `code-prototype` 路径,旧 v1 回执按缺失处理。game-chat 临时 `art-asset-plan` 只有在当前 `code-prototype -> agent-delegate`、durable delivery 与 binding 全链一致时保留 Canvas 例外,并且只认本人 `canvas.asset_generate` 凭证;`art-director` 有 Key 时也只广告并执行 Canvas 生图,无 `project.verify`、command 或 preview 权限。
- `design-foundation` 的 2026-07-26 职责隔离继续有效:项目文件仍只允许 `memory/project.md`、`game/game_design.md` 和配置 Key 时的固定 `assets/ui-prototype.png`,禁止修改 `game/index.html`、调用 smoke / preview / process 或恢复整项目。未配置 External Editor API Key 时 `art-director` 保持只读协调;配置 Key 时它是条件 Canvas owner,必须生成并登记 `assets/art-spec.png`,成功 `canvas.asset_generate` 为本人当前 revision 形成普通验证凭证,不能被只读分类吞掉。配置 Key 时 UI 原型、透明图集、Canvas 登记和视觉门仍按既有合同执行,内部 owner 文件验证不替代图片证据。
- `canvas.asset_generate.replaceExisting` 默认并必须保持 `false`;只有静态专业 Agent 的 `delegated-*` 唯一 repair run 才能申请 `true`。Runtime 要求当前 delivery 带 `repairOfDelegationId`,原 delivery 已被同一父 Agent / 父 run 认领,原始与返工合同的目标 Agent 和精确 `expectedArtifacts` 路径一致;普通 run、未声明路径、错误 Agent、未认领原交付或缺失原图都失败关闭。图片生成仍服从 `art-director` / `design-foundation` / `art-asset-plan` 的固定输出路径、比例、尺寸、kind 和 label,禁止先删除正式图片;请求前记录旧文件 SHA-256,外部生成返回后在项目写锁内复核,旧图在网络请求期间变化即拒绝覆盖。授权替换先写私有临时文件,再以备份 / rename 切换;落盘或 manifest 登记失败时恢复旧图,不把新旧文件并存状态当作成功。
- 在既有 16-task manifest 内固定正式视觉 DAG,不新增平行任务系统:`art-director` 用当前调用模式的图片生成 `kind=spec` 生成 `assets/art-spec.png` 并登记为 `assetKind=icon-spec`;`design-foundation` 使用该规范图的稳定资源 ID 作为视觉规范参考,用同模式图片生成 `kind=ui-design` 生成 `assets/ui-prototype.png`;`art-asset-plan` 以同一 resource ID 调用同模式图标 spritesheet 生成,产出透明 `assets/art-spritesheet.png`。普通模式使用内部 `/api/editor/*`,standalone/高级模式使用对应 `/api/external/v1/*`;业务请求、依赖和验收完全一致。规范图缺失、未登记或缺少稳定资源 ID 时,下游任务不得退回普通生图。图集 warning、透明像素与切片门禁保持不变。
- 旧项目已有同路径派生图但缺少上述 provenance 时,一律标记为 legacy,不得只因文件、kind 或通用视觉检查存在就完成。原位替换仍走显式 repair:`design-foundation` 与 `art-asset-plan` 先在同一 Supervisor 批次分别建立 owner 精确原合同并交付 `needs-repair`,父 run 认领后再在同一批次分别发起各自唯一 repair;两个 repair 合称一个显式视觉返工阶段。`art-director` 不得跨 owner 声明或替换 UI / spritesheet,Runtime 在委派落盘前就拒绝这类合同,不再等到生图阶段才失败。
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,305 @@
# 【技术说明】AGC 接第三方 Provider 的兼容性缺陷
- 日期:2026-08-19
- 分支:`feat/five_min_design`(HEAD `c2d527387`)
- 触发场景:在「做方案」入口输入「贪吃蛇」,项目总控 Agent 第一个请求就失败
- 结论:**这不是策划链路的问题**。做游戏 / 做素材 / 做方案共用同一套 Provider 分发,任何一条路换成非官方端点都会立刻挂。共发现 4 个独立缺陷,均已取得直接证据;其中缺陷 4 是本分支自己引入的回归,同样会影响做游戏路径,已修复。
---
## 0. 摘要
| # | 缺陷 | 表现 | 性质 |
|---|---|---|---|
| 1 | 前端保存设置时把 `agentMode` 硬写成 `codex_app_server` | UI 里换 provider 只改了 `llm.*`,运行模式换不掉,且界面上看不到这个字段 | 产品缺陷 |
| 2 | `codex_app_server` 模式把第三方端点喂给 codex | apiKind≠openai_responses 时秒挂;否则 413 + 工具误用,180 秒超时后留下待核对的孤儿请求 | 模式前提未被约束 |
| 3 | `provider` 模式下 `tool_choice=required` 与 DeepSeek 思考模式互斥 | 首个 tool-plan 请求 400,整个 runtime 起不来 | 参数空间缺一个值 |
| 4 | 普通 action 批次带 plan update 时,两条预检规则互斥 | 「更新计划 + 委派专业 Agent」同一轮返回就报「批次成员身份或顺序不匹配」 | **本分支回归**(已修) |
缺陷 1~3 叠加的结果:**当前代码里没有任何一组配置能让 DeepSeek 跑起来**。缺陷 4 与 provider 无关,换成 `gpt-5.6-terra` 打通 LLM 链路后才暴露出来。
---
## 1. 缺陷 1:`agentMode` 无法从 UI 切换
### 现象
在「Agent 设置 - 常用设置」里把 provider 换成 DeepSeek 并保存,配置文件里 `llm.baseUrl/model/apiKind` 都变了,但 `agentMode` 始终是 `codex_app_server`。界面上也找不到这个字段。
### 根因
[RuntimeConfigDialog.tsx:844](../../apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx:844) 的保存分支:
```ts
const config = normalizeRuntimeConfigDraft(
{
...runtimeConfigDraft,
agentMode: 'codex_app_server', // ← 无条件覆盖 draft
editorApi: ...,
mcpServers: ...,
},
allowAdvancedExternalEditorConfig,
);
```
弹窗里没有对应控件,`defaultRuntimeConfigDraft`(同文件 :68)也把它写死。于是:
- 用户改不了;
- 就算手工改了配置文件,**下一次在弹窗里点保存又会被写回 `codex_app_server`**,而且整个 `llm` 块会被弹窗草稿覆盖。
### 生效配置的位置
`writable_game_creator_config_path()`([config.rs:1367](../../apps/ai-game-creator-shell/src-tauri/src/config.rs:1367))→ runtime config dir → Tauri `app_config_dir`:
```
%APPDATA%\world.genarrative.ai-game-creator\game-creator.config.json
```
注意 `agentMode` 缺省值是 `codex_app_server`([main.rs:1252](../../apps/ai-game-creator-shell/src-tauri/src/main.rs:1252)),所以**旧配置文件里没有这个 key 时同样落到 codex 模式**。
---
## 2. 缺陷 2:`codex_app_server` 模式 + 第三方端点
### 2.1 分发关系
Provider 分发按全局 `agentMode` 分支([provider_retry.rs:1742](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs:1742)),与入口(做游戏 / 做素材 / 做方案)无关。`codex_app_server` 模式下 AGC 不直连 LLM,而是拉起本机 `codex` 二进制当 app-server:
```
codex.exe app-server --stdio -c mcp_servers={} -c web_search="disabled"
-c agents.enabled=false
--disable apps --disable browser_use --disable computer_use --disable goals
--disable image_generation --disable plugins --disable shell_tool
--disable unified_exec --disable workspace_dependencies ...
-c model_provider="genarrative_agc"
-c model_providers.genarrative_agc.base_url="https://api.deepseek.com"
-c model_providers.genarrative_agc.env_key="GENARRATIVE_AGC_CODEX_API_KEY"
-c model_providers.genarrative_agc.wire_api="responses"
```
(取自 2026-08-19 13:35 / 13:44 / 13:48 三个存活进程的命令行。)
### 2.2 `apiKind=openai_chat` → 秒挂
[codex_app_server.rs:463](../../apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs:463) 的门禁在任何 HTTP 请求之前就返回 `InvalidConfig`:
> `codex_app_server 仅支持 apiKind=openai_responses;当前 apiKind=openai_chat,请改用 provider 模式`
**取证方式**:agent.db 里只留 `errorChars` + `errorSha256`(原文被 [loop_orchestration.rs:536](../../apps/ai-game-creator-shell/src-tauri/src/agent/generation/loop_orchestration.rs:536) 哈希掉了)。13:33:45 那次 run(`gameagent-a21e1b2a`)记录为 `errorChars=156`、`errorSha256=a9481526dab648ad70e7800446d9026a7abcde068f44c5c2bc5e466fd2b6850e`。按代码把公开错误串重建为
```
agentLlm.project-supervisor 后台 Agent 工具计划调用 LLM 失败:kind=invalid-config fingerprint=<sha256(原文)> chars=<原文字数>
```
后 sha256 完全命中,确认内层原文就是上面那句门禁。
### 2.3 `apiKind=openai_responses` → 413,然后超时
13:41 那次 run(`gameagent-edfa1ba1`)的 `agent.runtime.plan.provider_usage` 记录 `activeMillis: 180786`,正好等于当时的 `requestTimeoutMs: 180000`,随后写入 `agent.runtime.provider_request.needs_reconciliation` —— 也就是 UI 上的「待核对 / 确认孤立 Provider 请求后再恢复当前 run」。
codex 自己的 tracing 落在隔离 CODEX_HOME 里(`%TEMP%\genarrative-agc-codex-app-server-*\codex-home\logs_2.sqlite`),两类记录:
**(a) 上游 413**
```
WARN codex_core::responses_retry ... model=deepseek-v4-flash:
stream disconnected - retrying ...
unexpected status 413 Payload Too Large:
<html><head><title>413 Request Entity Too Large</title></head>
<hr><center>openresty</center></html>
, url: https://api.deepseek.com/responses
```
codex 组的请求体(自带 system prompt + 全套工具 schema)超过了 DeepSeek 网关的 body 上限。codex 把 413 归类成「stream disconnected」并重试——11 秒内 5 次,turn 永远走不到终态。
**(b) 模型把 `view_image` 当文件读取工具**
52 条 `ERROR codex_core::tools::router`,形状一致:
```
tool_name=view_image
error=unable to locate image at `...\gameagent-1d26566b\memory\README.md`: (os error 2)
```
目标全是 `memory/README.md`、`memory/gameplay.md`、`memory/play-memory.md`、`exports/index.html` 等文本文件。原因是 AGC 要求「AGC Runtime 是唯一 ToolHost」,把 codex 侧能读文件的工具全 `--disable` 掉了,模型只好拿剩下的 `view_image` 去读 `.md`。每次失败后模型继续试,对话越滚越长,反过来把 (a) 的请求体喂得更大。
### 2.4 结论
`codex_app_server` 模式是围绕自家 `dev.genarrative.world/gpt/v1` + `gpt-5.6-sol` + AGC 独占 ToolHost 这套组合设计的。换第三方端点时 (a)(b) 都不是配置能绕开的:413 是对方网关的硬限制,工具误用是模型对着被裁剪过的 codex 工具集乱猜。
**当前代码没有任何地方阻止这种组合**——用户只会等 180 秒然后拿到一个「待核对」。
---
## 3. 缺陷 3:`provider` 模式 + DeepSeek 的 `tool_choice`
把 `agentMode` 手工改成 `provider` 后重跑,14:03:38 仍然失败。这次原文有落盘(provider 模式会写 [logs/llm-raw](../../apps/ai-game-creator-shell/src-tauri/logs/llm-raw),文件 `1787148218861-33480-000001-upstream_status_failed.output.txt`):
```json
{"error":{"message":"Thinking mode does not support this tool_choice",
"type":"invalid_request_error","code":"invalid_request_error"}}
```
### 复现矩阵(直接打 DeepSeek,非流式)
| 请求 | 结果 |
|---|---|
| `tool_choice: "required"` | **400** Thinking mode does not support this tool_choice |
| `tool_choice: "auto"` | 200 |
| `tool_choice: "none"` | 200 |
| `tool_choice: "required"` + `reasoning.effort: "none"` | **200**,且正常返回 `function_call` |
| `tool_choice: "required"` + `reasoning.effort: "minimal"` | 400 |
| `tool_choice: "required"` + `thinking: {type:"disabled"}` | 400 |
`/chat/completions` 与 `/responses` 两条 wire、`deepseek-v4-flash` 与 `deepseek-v4-pro` 两个模型表现完全一致。即:**DeepSeek 支持强制工具调用,但必须先关掉思考模式,而唯一能关掉它的开关是 `reasoning.effort: "none"`。**
带 3 个工具、`effort=none`、`tool_choice=required` 连打 3 次,全部返回干净的 `function_call`(其中一次返回两个),行为稳定。
### 根因
两个硬约束正好互斥:
1. Runtime 主循环的 tool-plan 请求写死 `LlmToolChoice::Required`
([provider_tool_plan.rs:1482](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs:1482)、
[provider_request_builders.rs:462](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs:462) 与 :554、
[autonomous_policy.rs:1971](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs:1971))。这是协议要求,Agent 必须返回工具调用。
2. `reasoningEffort` 的取值只有 `default / low / medium / high / max`
([platform-llm lib.rs:195](../../server-rs/crates/platform-llm/src/lib.rs:195)、[config.rs:145](../../apps/ai-game-creator-shell/src-tauri/src/config.rs:145)、
[types.ts:605](../../apps/ai-game-creator-shell/src/app/types.ts:605))。**没有 `none`**,而 `default` 的语义是整个字段不发 → 思考模式默认开着。
于是第一个请求必挂。
---
## 4. 缺陷 4:普通 action 批次带 plan update 时预检自相矛盾(已修)
换成 `gpt-5.6-terra` 后 LLM 链路终于通了(loop 1 成功冻结根 Goal Contract,计划推到 1/4),但 14:15:11 挂在第二步「委派 project-planning 产出 Fast GDD」。这一条与 provider 无关,是**本分支自己引入的回归**。
### 现象
agent.db 里这条错误没有被哈希,是明文:
```
Provider action 批次预检失败:
Agent Runtime Provider action 批次成员身份或顺序不匹配:index=0
```
两轮的 `tool_plan.protocol` 记录:
| loop | 模型返回的 function call | 结果 |
|---|---|---|
| 1 | `runtime_tool_agent_goal_contract` | ok |
| 2 | `update_agent_plan` + `runtime_tool_agent_delegate` | 预检失败 |
即「更新计划 + 委派专业 Agent」同一轮返回——总控最常规的动作。
### 根因:两条校验规则互斥
[provider_batch_ledger.rs:316](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_batch_ledger.rs:316) 对**每个**批次成员要求 `pending.provider_batch_plan_update == batch.plan.plan_update`;
同一函数 [:358](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_batch_ledger.rs:358) 又对**非 planning** 批次成员要求该字段必须是 `None`("非 planning Provider action 批次成员不能携带 planning recovery material")。
而创建侧 [provider_action_batch.rs:636](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:636) 只给 `plan.submit_gdd` 这一个工具填该字段,其余动作保持默认 `None`(同文件 :279)。
于是「非 planning 批次 + 该轮带 plan update」被同时要求"等于 Some"和"必须是 None",无解,前者先命中。
### 归属
```
git log -S provider_batch_plan_update → 27c3eb847 立项策划:完成 M1B-2 GDD 提交与恢复
master 中该标识符出现 0 次
```
触发条件是「生成持久化批次 + 该轮带 plan update + 首动作不是 `plan.submit_gdd`」,**做游戏路径同样会撞**——违反本分支「做游戏/做素材与 master 一致」的准则。
测试没拦住的原因:`provider_batch_plan_update` 相关用例(pending_recovery.rs:2177 / 2552)全是 planning 提交路径的夹具,缺「非 planning 批次 + plan update」这个组合。
### 修法(已应用)
预检期望值按批次类型分叉:planning v4 批次保持相等(该约束在 [:241](../../apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_batch_ledger.rs:241) 已对 `actions[0]` 校验过一次),非 planning 批次期望 `None`。
```rust
let expected_member_plan_update = batch
.planning_session_binding
.is_some()
.then(|| batch.plan.plan_update.clone())
.flatten();
```
创建侧与 :358 的语义本来自洽,不动。
回归测试:`tests::collaboration::policy_batches::supervisor_collaboration_batch_keeps_plan_update_off_members`。已验证去掉修复后该用例复现出与线上完全一致的错误串。
---
## 5. 附:MiniMax 的强制工具调用不可靠
作为备选测过 `api.minimaxi.com/anthropic` + `MiniMax-M3` + `tool_choice:{type:"any"}`(AGC 的 `Required` 在 anthropic wire 上映射成 `any`):
- 请求本身 200,非流式与流式都通;
- 但 3 次试验只有 1 次真的返回 `tool_use`,另外 2 次是 `stop_reason: end_turn` 的纯文本。
也就是说 MiniMax 的 anthropic 兼容层**不强制**执行 `tool_choice: any`。AGC 有 format repair 兜底(`format_repair_attempts`),但会白烧轮次,不建议作为主力。
---
## 6. 影响面
- 三个入口(做游戏 / 做素材 / 做方案)共用同一套分发,缺陷 1~3 与入口无关;
- 缺陷 1 让绝大多数用户根本走不到 `provider` 模式;
- 缺陷 2 的失败形态最差:不是报错而是等满超时 + 留下待核对的孤儿请求 + 池化的 codex 进程;
- 缺陷 3 让 `provider` 模式对「思考型第三方模型」整体不可用(DeepSeek V4 全系);
- 缺陷 4 与 provider 无关,只要模型在同一轮里既更新计划又调用协作工具就会撞,做游戏路径同样中招。
---
## 7. 建议改动清单
### 已完成
0. **缺陷 4 的预检分叉 + 回归测试**(见 §4)。这是分支回归、且波及做游戏路径,不等排期,已直接修在工作区。
### P0 —— 让第三方 provider 可用
1. **`reasoningEffort` 增加 `none`**
- [platform-llm lib.rs:195](../../server-rs/crates/platform-llm/src/lib.rs:195):`LlmResponseReasoningEffort` 加 `None` 变体,`as_str()` 返回 `"none"`;注意仓库内对该枚举有多处 exhaustive match(`Max` 刚加时踩过),需一并补齐。
- [config.rs:145](../../apps/ai-game-creator-shell/src-tauri/src/config.rs:145) `parse_game_creator_llm_reasoning_effort` 接受 `none`,同步 :155 的错误文案。
- [types.ts:605](../../apps/ai-game-creator-shell/src/app/types.ts:605) `gameCreatorLlmReasoningEfforts` 加 `'none'`;RuntimeConfigDialog 的默认表(:40~:65)与下拉项同步。
- 验收:`reasoningEffort: "none"` + DeepSeek + `provider` 模式,做方案能跑到第一个工具调用。
2. **保存设置时不要硬写 `agentMode`**
- [RuntimeConfigDialog.tsx:844](../../apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx:844):删掉那行覆盖。
- 二选一:在常用设置里暴露「运行模式」选择;或按 `baseUrl` 推断(非官方端点自动落 `provider`)。推荐后者 + 高级设置里可覆盖。
### P1 —— 让失败可解释
3. **禁止「`codex_app_server` + 非官方 baseUrl」这一组合**
在配置校验期([config.rs:350](../../apps/ai-game-creator-shell/src-tauri/src/config.rs:350) `game_creator_codex_app_server_llm_route_error` 附近)直接拒绝并给出明确文案,而不是让用户等 180 秒超时、再手工核对孤儿请求。
4. **超时后的资源回收**
turn 超时后 codex app-server session 仍留在池里(本次留下 3 个)。至少在 `needs_reconciliation` 时把对应 session 作废。
### P2 —— 排障体验
5. `reasoningEffort` 没有传到 codex(日志里 `codex.turn.reasoning_effort=default`),配置与实际行为不一致。
6. 失败原文全部 sha256 化,本次定位缺陷 2 只能靠"按代码重建错误串再比对哈希"。建议 dev 构建下把公开错误串明文写进 agent.db,或至少把 `kind=` 留在 UI 的运行详情里。
---
## 8. 未验证 / 待确认
- 官方端点 + `provider` 模式:`gpt-5.6-terra` 已验证能跑通 loop 1(工具调用、Goal Contract 冻结、计划更新都正常),修掉缺陷 4 后能走多远还没实测。`gpt-5.6-sol` 未验证——仓库里 `apps/ai-game-creator-shell/game-creator.config.json` 的 key 打过去是 401(占位值)。
- `effort=none` 下 DeepSeek 做策划的**质量**(本次只验证了协议层能跑通)。
- DeepSeek 网关 413 的具体阈值,以及 `provider` 模式下 AGC 自组的请求体是否也会触顶(本次 provider 模式的请求约 39 KB,未触发)。
---
## 9. 现场证据索引
| 证据 | 位置 |
|---|---|
| 秒挂 run(openai_chat) | `~/Documents/Genarrative GameAgent/gameagent-a21e1b2a/.agent/agent.db` |
| 超时 + 待核对 run | `~/Documents/Genarrative GameAgent/gameagent-edfa1ba1/.agent/agent.db` |
| codex 侧 413 与 view_image 报错 | `%TEMP%\genarrative-agc-codex-app-server-*\codex-home\logs_2.sqlite`(表 `logs`,按 `level in ('ERROR','WARN')` 查) |
| provider 模式 400 原文 | `apps/ai-game-creator-shell/src-tauri/logs/llm-raw/1787148218861-33480-000001-upstream_status_failed.{input.json,output.txt}` |
| 缺陷 4 的批次预检失败 run | `~/Documents/Genarrative GameAgent/gameagent-9a524132/.agent/agent.db`(`agent.runtime.context.failed` 明文错误 + 两轮 `tool_plan.protocol` 的 `functionNames`) |
| 生效配置 | `%APPDATA%\world.genarrative.ai-game-creator\game-creator.config.json`(本次已手工把 `agentMode` 改为 `provider`,原件备份为同名 `.bak-agentmode`) |