- 新建 project/write_lock.rs:取锁、等待分类、持锁方诊断、残留回收与 4 条锁用例整体搬移,逻辑不变 - project/filesystem.rs 只保留项目文件 IO(1533 → 680 行),锁相关常量、结构、进程判据与用例全部移出 - project.rs 注册 mod write_lock 并 pub(crate) use write_lock::*,crate::project:: 与 crate:: 既有路径不变 - windows_metadata_is_reparse_point 提为 pub(crate),供 write_lock 复用同一条 reparse point 判据 - agent_db.rs 与 checkpoint.rs 的 PROJECT_FILE_FLAG_OPEN_REPARSE_POINT 导入路径改为 super::write_lock - 同步修正技术方案、Fast GDD 技术方案、decision-log、pitfalls 中指向锁实现的文件路径,并把“拆锁”从后续事项改为已完成
400 KiB
立项策划 Agent(Fast GDD)技术方案
- 日期:2026-08-10
- 状态:已退役。本文描述的 V1 策划链路(
project-supervisor-plan根 Run、project-planning子 Agent、plan.submit_gdd工具、Fast GDD 审批门禁与恢复机制)已由策划会话 Runtime V2 取代,源码已于 2026-09 按四不写原则整体删除;现行方案见【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md。本文仅作为历史推导记录保留。 - 适用范围:AI 游戏创作独立 App、Project Supervisor、Agent Runtime、本地项目策划 sidecar 与后续完整构建准入
当前口径(2026-08-30):以本文件中标注的 D11 / 最新修订和当前
apps/ai-game-creator-shell实现为准。D6~D9 等被明确标注为作废或被取代的段落仅保留推导背景,不得作为现行拓扑、入口或 Runtime 真相;产品入口与 DirectProject 总体口径见docs/README.md和 App 实施计划。
1. 背景与目标
当前普通完整构建会从简短需求直接进入 autonomous-game-build,用户在消耗完整构建成本前没有正式确认玩法方向、MVP 范围和原型验证项的环节。现有完整构建中的 design-director 是只读协调任务,design-foundation 又会自行补齐玩法定位;用户意图与后续实现之间缺少可版本化、可审批、可恢复的策划基线。
本方案新增“立项策划”阶段:用户给出一句需求后,由 Project Supervisor 顶层 root run 通过 agent.delegate 发起一个独立 agentId 的静态委派子 Agent(agentId=project-planning;2026-08-13 起取代原“下游工作流节点由 manifest ready-task 调度器启动”的表述,见第 1.1 节「D11 新拓扑」与 D11),在最多 3 轮决策卡内形成 Fast GDD(该 3 轮上限依赖 WP1——静态委派澄清轮次与返工深度拆分——先落地,见第 1.1 节;落地前实际上限仍是 1 轮);Runtime 校验并提交不可变版本,用户通过审批卡批准、修改或退回。审批修改后的 continuation 沿用普通 Planning V2 回合协议,Provider 可以继续问询,也可以直接提交新的完整 GDD;既有问询计数和已确认问答继承,不因修改重置。只有不可变 GDD 与对应 approve receipt 同时有效时,后续完整构建才能取得 approvedGddRef。
目标:
- 一句需求经过不超过 3 轮关键澄清,形成包含一个完整可玩闭环的 Fast GDD。
- 策划阶段没有构建、委派、生成、预览或通用文件写能力。
- GDD 版本、用户决定和构建引用都有确定提交点、强身份绑定、幂等重放和崩溃恢复合同。
- 后续 M1 无需任何仓库外材料即可实现 source、Prompt、工具、schema、审批与恢复。
非目标:
- M0 不新增 Runtime source、Prompt composition、工具、命令、页面或项目数据,只冻结合同。
- M0/M1 不修改现行 16 任务 DAG,不新增第 17 个任务。
- 本期不实现知识图谱;只保留强类型可空槽,v1 必须为空且 UI 不渲染。
- 不提供引擎选择字段。平台只有自包含 Web 运行事实,由 Runtime 注入并校验。
- 不修改 SpacetimeDB、HTTP API、OpenAPI 或
shared-contracts中的正式作品数据合同。 - M1 不实现 300 秒硬停;以 3 轮硬上限和 240 秒 Agent 活跃时间软提示收束。
1.1 2026-08-12 设计变更与 M0 修订计划(工作包 M0A-3)
D6 已作废,策划 Agent 不再是 Project Supervisor 的第三 persona。 本节是当前唯一权威的变更说明与修订计划。在 M0A-3 收口前,第 2~6、12、14、18.1、19、22~24 节中凡与本节冲突的表述,一律以本节为准。
变更触发
产品侧先确定「做方案」入口独立成链:不动做游戏路径,最终产物只有策划方案,且保持 Supervisor 顶层、工作流节点与子 Agent 在下游的结构。据此复核代码后,D6 的三项前提逐条不成立:
-
策划不能跑在
autonomous-game-build下。user.input_request被两层拦死且判据只看 run profile、不看 agent 身份——广告层apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs:198-215按 profile 剔除并推进denied;执行层apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs:2604-2616直接判NeedsReconciliation。父 Supervisor 自己也在禁令内。 -
硬闯的后果是整条工作流永久瘫痪,不是单次失败。
apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs:1157把runtime.status写成failed,而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,此后任何 ready task 都起不来,须人工核对。 -
不能只给策划节点单独换 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下其实「能」自己提问,但「不得」这样做——理由是产品约束,不是机制阻拦。 两个调度器的血统不同:autonomous 的 ready-task 调度器在task_start.rs:905-907显式写parent_agent_id / parent_run_id,节点必然被validate_user_input_action_owner(apps/ai-game-creator-shell/src-tauri/src/user_input.rs:367-394)拒绝;而 standard 的 ready-task 调度器start_game_creator_agent_background_task_with_source_at(task_start.rs:141-160)向_with_link_at传入task_link = None,节点无 parent,其 sourceagent-ready-task-scheduler也不在该函数的黑名单内,agentId又不以child-开头——三路判据全不命中,它调用user.input_request会被放行。因此本方案不能依赖这道门来约束策划节点。若放任它自己提问,问答消息会按
apps/ai-game-creator-shell/src-tauri/src/user_input.rs:494-530落进project-planning自己的会话文件,Supervisor 的上下文里一个字都没有,直接违反下文第二条产品约束。所以user.input_request必须由策划节点的 exact allowlist 主动排除,并以回归用例钉死——这是本方案里少数几个「Runtime 不会替你拦、必须靠 allowlist 自律」的地方。
新拓扑(替代 D6)
2026-08-13 起已被取代:本小节描述的是 D9 拓扑(ready-task 调度器启动下游工作流节点 + Runtime 直投),已被 D11 取代——见本节末尾「2026-08-13 D11 新拓扑」。保留本小节仅作 D6→D9 推导记录,其中「Supervisor 仍是唯一顶层 root run」「用户侧只有一个对话对象、对话内容必须物理存在于 Supervisor 会话」两条方向性结论被 D11 继承;「manifest ready-task 调度器启动」「Runtime 直投」两条机制性结论被 D11 推翻,不要把本小节当作现行设计。
- Supervisor 仍是唯一顶层 root run,以新的可信 source 承载「做方案」入口,run profile 固定
standard。 - 策划 Agent 是独立
agentId的下游工作流节点,由 manifest ready-task 调度器在 Supervisor 下游启动,profile 随父固定为standard。本版工作流只有这一个节点,但框架按将来可加节点搭建。 - 问询改为「Runtime 直投」:策划节点提交结构化决策字段,Runtime 据此创建 owner 是 Supervisor 的
user.input_requestpending(agentId为 Project Supervisor、Supervisor 的 session 与 run),用户在 Supervisor 对话里回答。Supervisor 的 Provider 完全不参与提问,是纯函数搬运。 - 一份记录两路投影(复用现役机制,非新造):
AgentRuntimeUserInputRecord是唯一事实源;apps/ai-game-creator-shell/src-tauri/src/user_input.rs:494-530把它投影成会话消息写进 pending owner 的会话文件,:595-623投影成结构化 observation。直投只是让会话投影落到 Supervisor、observation 投影落到策划节点。会话文件按agentId分目录(apps/ai-game-creator-shell/src-tauri/src/project/conversation.rs:42),因此消息归属完全由 pending owner 决定;:803-807的「observation 重算冲突」校验保证两路不漂移。
该拓扑要满足的两条产品约束:一、用户侧只有一个对话对象;二、所有出现过的对话内容必须物理存在于 Supervisor 的会话里——用户后续引用或隐式依赖时,那些内容必须真在上下文中,不能靠前端把多个 Agent 的对话拼在一起显示。直投满足第二条不是靠约定,是靠「消息写在哪个会话文件里」这个物理事实。
修订范围:只有文档,代码零回退
| 工作包 | 判定 | 依据 |
|---|---|---|
M0A-1 文档基线 |
需修订,即本工作包 M0A-3 |
是 D6 的唯一载体 |
M0A-2 owner 产物验证 |
不受影响,但不可复用 | 见下 |
M0 三个代码工作包无一行按 D6 编写,不需要为新设计回退或修改任何已合入代码。在本次 M0A-3 文档修订时,全仓库检索 fast_gdd、.agent/planning、project-supervisor-plan-chat、is_exact_supervisor_plan_run_at 均零命中,M1 代码尚未落地,因此改文档没有迁移成本;后续 M1A 工作包的落地状态以本文当前状态行和第 23.6/23.8 节为准。
但 M0A-2 的实现不可被 standard 路径复用。 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 agent 白名单、profile == autonomous-game-build、source == agent-ready-task-scheduler、且要求 parent_agent_id 为 Project Supervisor。standard ready-task 节点无 parent,第 436 行即不通过。standard 路径的 owner 产物验证必须另建一套物理独立实现——这是新增,不是扩展,不要把「不受影响」误读为「可以直接改现有函数」。
分两批执行
批一(不依赖命名裁决,可立即修订)
- 第 1.1 节(本节)、文首状态行、第 2 节 D6 作废与新决定入表
- 第 19 节第 2 条、第 24 节第 8 条:按 Supervisor 根 run 与策划工作流节点两套判据拆写
- 第 23.1 节:删除 Goal Contract 四方案 A/B/C/D,替换为已验证的三条约束(见下)
- 第 22 节证据表:
trusted matcher 消费者行结论句改写;新增 standard 路径 owner 验证、Runtime 直投两行 - 第 23.4 节:M0 完成状态改为「代码完成、文档待修订」
批二(原被「策划节点 agentId / source 命名」裁决阻塞;2026-08-13 命名裁决已完成,批二不再被命名阻塞,一度改由下方「D11 新拓扑」第 6 条 build.rs 一致性待裁决项阻塞;该项已于 2026-08-13 定稿,见第 3.1 节与下方更新——批二不再被 catalog 一致性问题阻塞,但仍不在本轮范围内,另需等第 3.1 节新发现的 prompt.rs 角色身份合成缺口在 M1 处理后才具备可执行基础)
- 第 3 节注册表全部身份常量、第 4 节拓扑图、第 4.1 / 4.2 节(整节作废后重写)、第 4.3 节工具清单
- 第 5.1 / 5.2 节 checkpoint 触发点、第 6 节 Prompt 权威稿
- 第 8.3~8.6 节身份字段取值、第 9.1 节 golden vector 重算(旧值
d85c85dae3…已失效,2026-08-13 重新生成为 3857 bytes /a59856de7e…) - 第 12 节 submit dispatch 段、第 13.1 / 13.3 节
agentId常量、第 14 节与activeQuestion/ checkpoint handoff 相关的十行恢复语义(实际删除十行,另立四行 D11 语义 + 一行通用 handoff 安全边界) - 第 18.1 节前端入口
2026-08-13 更新:命名裁决已冻结——策划子 Agent agentId=project-planning,Supervisor 侧承载「做方案」入口的新可信 source 为 project-supervisor-plan。批二不再被命名阻塞。但 D11 把策划节点从「ready-task 工作流节点」改成「静态委派子 Agent」,批二这些小节的具体写法(尤其第 3 节注册表、第 4 节拓扑图)必须按 D11 新拓扑而非 D9 旧拓扑重写,且新增了「D11 新拓扑」第 6 条列出的 build.rs agentCatalog 一致性前置——project-planning 如何登记进编译期 seed task catalog 而不触发 validate_seed_task_catalog(apps/ai-game-creator-shell/src-tauri/build.rs:23-46)panic,此前尚未裁决,批二涉及注册表落笔前必须先冻结这一点。本轮文档修订范围不含批二,此处只记录状态变化。
2026-08-13 批一执行状态:批一所列五项已全部落笔并合入(文首状态行、第 1.1 节、第 2 节决定表、第 19 节第 2 条、第 24 节第 8 条、第 23.1 节、第 22 节证据表、第 23.4 节)。其中第 19 节第 2 条与第 24 节第 8 条在 D11 之后又按新拓扑二次改写,现行表述以 D11 为准。批一至此收口,M0A-3 的剩余工作只有批二。
2026-08-13 二次更新:build.rs agentCatalog 一致性问题已定稿并随 M1A 基础代码落地——project-planning 登记为与 supervisor 平级、不进 groups 数组的独立条目,specialist_nodes 只读 groups[].roles[],build.rs/种子 DAG/new_game_creation_app_seed_tasks() 均不需要改动。Prompt Bundle 已登记 project-planning brief,角色 overlay 已接入 Provider;本状态只表示身份与 brief 基础可执行,不表示 plan.submit_gdd、GDD 存储或审批闭环已完成。
Goal Contract:四方案作废,替换为三条已验证约束
原第 23.1 节的 A/B/C/D 四方案共同前提是「策划 run 自己就是那个需要豁免的 root」,新拓扑下 root 是未变的 Supervisor,四选一问题消解。但入口门仍然适用:validate_root_goal_contract_control_plan_at(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs:171-236)无 run profile 判断,standard root Supervisor 同样受约束。故替换为:
- Supervisor 第一轮必须且只能提交
agent.goal_contract,不得夹带其它 action、plan_update、legacy plan 或 response。做方案场景下可满足——交付物「一份获批的方案」开工即确定,不存在自主构建里「目标未澄清就要冻结」的矛盾。 - 验收节点的
requiredEvidence锚定game/fast_gdd.md配tool:file.read,不得指向.agent/planning/**。reject_agent_runtime_private_control_path(apps/ai-game-creator-shell/src-tauri/src/project/filesystem.rs:253-266)目前只挡.agent/runtime、.agent/checkpoints、.agent/workbench三类,planning 不在内;但该函数不区分读写,若为落实第 19 节第 3 条的写保护而把 planning 加入此列,file.read会被一并挡死、出口门永久 blocked。planning 的写保护必须用只挡写的独立判据实现。 - Supervisor 取证必须在 GDD 落盘之后、且在交付审批卡之前(后半句为 2026-08-13 补充,见第 13 节「审批前置门」)。验收节点 passed 要求非 mutation evidence 的 before / after / 当前 revision 三者相同;
.agent/planning/**与game/fast_gdd.md尚未实现,其写入是否推进 project revision属于待定设计而非待查事实,故无论如何定,都以「落盘后取证」为准。
M0A-3 门禁与完成定义
- 批一全部合入,且本文不再存在自相矛盾的表述(同一事实在两处给出不同结论即视为未完成)。
- 命名裁决冻结并写入决策记录后(2026-08-13 已完成),批二全部合入——
build.rsagentCatalog 一致性问题已于 2026-08-13 定稿,且 M1A 已补齐对应 catalog/role brief 基础;批二的实现收口不等于plan.submit_gdd或 GDD/审批闭环可用,后续仍按第 23.8 节拆包推进。 - decision log 与 pitfalls 同步补记:D6 作废理由、autonomous 下父子皆不可提问且硬闯会瘫痪整条工作流、
M0A-2owner 验证不可复用;2026-08-13 起还需同步补记 D9 二次作废、D10 作废、D11 新拓扑与 WP1 澄清轮次/返工深度拆分定稿(已完成,见 decision-log 2026-08-13 条)。 - 仅文档工作包,不含任何代码改动;合入只表示设计基线恢复自洽,不表示任何功能可用。
M0A-3 明确不做
- 不回退、不修改任何已合入的 M0 代码。
- 不删
design-director、不动design-foundation、不改 16 任务 DAG——这些论证已成立(design-director不在 owner 产物清单、产物无人读),但属于「做游戏」路径改造,等 GDD 真正被完整构建消费时另开工作包。 - 不实现 M1~M3 任何功能。
2026-08-13 D11 新拓扑:立项策划改为 Supervisor 静态委派子 Agent(取代 D9,连带作废 D10)
D9/D10 描述的「manifest ready-task 调度器在 Supervisor 下游启动策划节点」+「Runtime 直投创建 owner=Supervisor 的 pending」拓扑作废,改为:立项策划节点是 Project Supervisor 通过 agent.delegate 发起的静态委派子 Agent(agentId=project-planning),不再由 ready-task 调度器启动;问询不再新造「直投」机制,改为复用 PR #165(Issue #163 决策,见 docs/project-memory/shared-memory/decision-log.md 2026-08-12 条「子 Agent 澄清回执由 Supervisor 中转」)已实现的中转链路:子 Agent 以 AGC_NEEDS_USER_INPUT_V1 终态信封退出 → Supervisor 认领回执 → Supervisor 在自己的 runtime/session 上建 waiting-for-user-input pending → 用户在 Supervisor 会话内作答 → 答案经 questionsSha256/answersSha256 原子绑回 delivery → Supervisor 发起 continuation 子 Agent 续跑。命名裁决已冻结:agentId=project-planning,Supervisor 侧新可信 source 为 project-supervisor-plan。
支撑 D11 的六条已验证事实:
- 执行层拦截,广告层不拦。 委派子 Agent 天然带
parent_agent_id/parent_run_id,其user.input_request在落盘 pending 之前被执行层validate_user_input_action_owner(apps/ai-game-creator-shell/src-tauri/src/user_input.rs:367-394)拒绝——只要 task 存在parent_agent_id、parent_run_id或delegation_id中任一项即失败,不再依赖 D10 那种 exact allowlist 自律。但广告层不拦:build_agent_runtime_native_function_tools(apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs:279)没有agent_id参数,无法按身份裁剪;standardprofile 下tool_policy_snapshot.rs:144-147(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs)仍无条件把user.input_request归入auto_tools。模型看得见这个工具、会去调用,只是调用必失败并转成一次失败的 tool observation,不是零可见。 - 约束二(答案物理落 Supervisor 会话)成立,有完整机械证据链。
ensure_static_delegate_user_input_wait_at(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/provider_recovery.rs:327-408)→build_game_creator_agent_runtime_pending_tool_action(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:219-251,agent_id/session_id/run_id直接抄 Supervisor 自身的runtime参数)→build_new_user_input_record(user_input.rs:396-411,agent_id/session_id从 pending 原样继承)→append_user_input_question_message/append_user_input_answer_message(user_input.rs:494-532)→conversation_file_path_for_resolved_session(apps/ai-game-creator-shell/src-tauri/src/project/conversation.rs:48-59)。问答消息物理写进 Supervisor 自己的会话.jsonl文件,是后端持久化事实,不是前端把多个 Agent 的会话拼接显示。 - 多轮不失忆,但转述保真不由 Runtime 校验。 委派的
target_session_id每轮都解析为resolve_agent_conversation_session_id_at(root, target_agent_id, None, true)(apps/ai-game-creator-shell/src-tauri/src/project/conversation.rs:860-875),传None时走catalog.active_session_id(conversation.rs:849-850)——即每一轮委派都落在同一条持久 active session 上;而 prompt history 按(agent_id, session_id)组装(apps/ai-game-creator-shell/src-tauri/src/context_compaction.rs:453-490的prepare_game_creator_agent_runtime_prompt_history),run_id只用于observation_start的覆盖计数过滤。因此 continuation 子 Agent 是「新 run、同 session」,能看到自己前几轮的完整对话。但残留风险如实记录:用户的答案本身不在子 Agent 的 session 里(物理落在 Supervisor 那边),子 Agent 只能依赖 Supervisor 把已确认答案重新组织进新的委派 task 文本;Runtime 只校验questionsSha256/answersSha256的哈希绑定,不校验转述内容与已确认答案的语义一致性,转述保真完全依赖 Supervisor 侧 LLM 的行为质量。委派 task 有硬上限AGENT_RUNTIME_TASK_MAX_CHARS = 4_000(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs:147),超限直接Err,不会静默截断丢失内容。 - 产物路径与
.agent/planning/**不冲突,且有结构性保证而非文档层自律。normalize_static_delegate_expected_artifact(apps/ai-game-creator-shell/src-tauri/src/delegation.rs:1981-1998)拒绝首段为.agent的相对路径,因此.agent/planning/\*\*在委派发起阶段就无法进入expectedArtifacts,不需要额外的文档层面约束。game/fast_gdd.md不在.agent下,可以正常登记。定稿:expectedArtifacts=["game/fast_gdd.md"],质性标准写进acceptanceCriteria,verification_required=false——策划节点没有 verify/smoke 类工具,无法产生可供 Runtime 复核的验证凭证。 - 前置依赖:D11 的“最多 3 轮问询”依赖 WP1 先落地,是强制前置,不是并行工作包。该前置已于 2026-08-13 落地,本条改为记录其成立依据与已完成状态。 WP1(澄清轮次
clarification_round与返工深度repair_depth拆分,语义见下方,权威定义见 decision-log 2026-08-13 条)与其回归工作包WP2已合入本分支,完成状态与门禁见第 23.5 节。作为对照记录改造前的事实:改造前repair_of_delegation_id.is_some()即拒绝(apps/ai-game-creator-shell/src-tauri/src/delegation.rs:1119-1121),澄清 continuation 与质量返工共用同一条「深度最多为 1」判据,D11 描述的多轮问询实际上限只有 1 轮,且这 1 轮一旦用掉,同一条委派链就再也做不了任何质量返工;该结论由clarification_continuation_chain_supports_multiple_rounds走真实agent.delegate生产路径实证(改造后该用例的断言方向已按预期行为变更反转,见第 23.5 节)。本条不因 WP1 完成而降格:D11 的 3 轮问询能力只在 WP1 语义生效的前提下成立,任何回退 WP1 的改动都同时回退 D11 的问询上限。 - 登记机制已定稿(原「未决前置」,2026-08-13 处置完成):
project-planning的编译期 agentCatalog 登记方式,不会撞上build.rs一致性校验。project-planning目前不在编译期 agentCatalog 里;build.rs的validate_seed_task_catalog(apps/ai-game-creator-shell/src-tauri/build.rs:23-46)要求编译出的(taskId, groupId, role)集合与shared_contracts::game_creation_app::new_game_creation_app_seed_tasks()逐项相等,不等直接panic!;但该集合只来自manifest.agent_catalog.groups[].roles[](build_support/runtime_prompt_bundle.rs:387-398),不遍历agentCatalog其它顶层键。结论:project-planning登记为与supervisor平级、不进groups数组的独立条目(先例即supervisor自己——它是 catalog 成员但不是种子 DAG 任务,runtime_adapter.rs:4-38),new_game_creation_app_seed_tasks()不需要新增条目,该 catalog 本身也不需要拆分。完整机制、descriptor 元数据取值与编译链路改动清单见第 3.1 节。但登记只解决 catalog 成员资格,不解决可执行性——第 3.1 节调研同时发现一个新的、真正阻塞可执行的缺口:prompt.rs的game_creator_agent_role_definition(prompt.rs:661-679)硬编码「非 supervisor 即 group 角色」二分,不认识project-planning,需 M1 补一个平行分支;这才是仍然待处置(M1 范围)的前置,不是本条描述的 catalog 登记方式本身。
2. 已锁定决定 D1~D11
| 编号 | 冻结结论 |
|---|---|
| D1 | 每个项目生成 2~4 条游戏支柱;可感知体验、角色成长、探索、构建变体只作为候选,不固定套用。 |
| D2 | 仅首页“做方案”新建项目进入“立项策划”;“做游戏”和“做素材”保持直接开建,等价于当前无 GDD 基线的完整构建路径。 |
| D3 | 阶段、source、composition、工具、schema、审批命令和 UI 名称按第 3 节注册表冻结。 |
| D4 | M1 使用 .agent/planning/ sidecar,不改 shared-contracts;是否把 approvedGddRef 提升进 manifest 留给 M2,不能在 M1 临时决定。 |
2026-08-12 作废,由 D9 取代。原文:“立项策划 Agent”是 Project Supervisor 通道的第三 persona,以持久 source 区分,复用 standard profile,不注册新的 agentCatalog 身份。作废理由见第 1.1 节;其中“复用 standard profile”这一结论方向被 D9 继承,但成立理由完全不同。 |
|
| D7 | 本期不实现知识图谱;basis、知识 provider trait、composition 槽位可以预留,但 v1 数据必须为 null,空字段不渲染。 |
| D8 | GDD 不含引擎字段;平台事实固定由 Runtime 注入,Agent 不得向用户提问或修改。 |
2026-08-13 二次作废,由 D11 取代。原文:“取代 D6。立项策划是独立 agentId 的下游工作流节点,由 manifest ready-task 调度器在 Supervisor 下游启动,需登记 agentCatalog;Project Supervisor 以新的可信 source 承载「做方案」入口并保持唯一顶层 root;父子 run profile 必须同为 standard——不是为了复用顶层通道,而是因为 autonomous-game-build 对 user.input_request 的禁令按 profile 生效、父子皆不可提问,且子 Run 不能切换父 Run 的 profile。” 作废理由见第 1.1 节「D11 新拓扑」。“立项策划是独立 agentId 的下游工作流节点”这一结论方向被 D11 继承,但调度机制被 D11 推翻:不再由 manifest ready-task 调度器启动,改为 Project Supervisor 通过 agent.delegate 发起的静态委派子 Agent;「父子 run profile 必须同为 standard」这条论证随之失效——D11 拓扑下策划节点天然带 parent_agent_id/parent_run_id,其 user.input_request 直接被执行层 validate_user_input_action_owner(user_input.rs:367-394)拒绝,不再依赖“父子皆不可提问、子 Run 不能切换父 Run profile”这条 profile 层面的间接论证;「需登记 agentCatalog」被继承但登记方式生变,登记机制已于 2026-08-13 定稿(见第 3.1 节与第 23.1 节),代码落地留给 M1。 |
|
2026-08-13 作废,由 D11 取代。原文:“策划节点不得直接调用 user.input_request——注意这不是 Runtime 拦得住的:standard ready-task 调度器不写 parent,该调用会被放行,因此必须由 exact allowlist 主动排除并以回归钉死。禁止的理由是产品约束:直接提问会把问答落进策划节点自己的会话,Supervisor 上下文里什么都没有。问询改走「Runtime 直投」:策划节点提交结构化决策字段,Runtime 创建 owner 为 Project Supervisor 的 pending,会话消息落 Supervisor 会话、结构化 observation 落策划节点,二者是同一 AgentRuntimeUserInputRecord 的两路投影。Supervisor 的 Provider 不参与提问。” 作废理由:D10 存在的唯一前提——「ready-task 节点无 parent,Runtime 拦不住,必须靠 exact allowlist 自律排除」——在 D11 新拓扑下不成立。策划节点改为委派子 Agent 后天然带 parent_agent_id/parent_run_id,user.input_request 被执行层 validate_user_input_action_owner(user_input.rs:367-394)直接拒绝,是 Runtime 兜底而非产品自律(但在 M1A-2 之前广告层仍放行,模型看得见、会去调,只是必失败;M1A-2 后两层都拒,见第 19 节第 2 条)。「问询改走 Runtime 转发、Supervisor 会话与结构化 observation 两路投影」这一方向被 D11 继承,但落地机制不是同一套代码:D10 设想的是为 D9 拓扑新造的「Runtime 直投」,D11 复用的是 PR #165 已实现的 AGC_NEEDS_USER_INPUT_V1 终态信封 + Supervisor 中转链路,两者实现路径不同。 |
|
| D11 | 取代 D9,并连带作废 D10。 立项策划节点改为 Project Supervisor 通过 agent.delegate 发起的静态委派子 Agent(agentId=project-planning),不再由 ready-task 调度器启动;问询改为复用 PR #165 已实现的中转链路:子 Agent 以 AGC_NEEDS_USER_INPUT_V1 终态信封退出 → Supervisor 认领 → Supervisor 在自己的 runtime/session 上建 waiting-for-user-input pending → 用户在 Supervisor 会话内作答 → 答案经 questionsSha256/answersSha256 绑回 delivery → Supervisor 发起 continuation 子 Agent 续跑。最多 3 轮问询依赖 WP1(澄清轮次与返工深度拆分,见第 1.1 节)先落地,是强制前置,不是并行工作包;该前置已于 2026-08-13 落地并合入,现行上限为 3(完成状态与门禁见第 23.5 节)。作为改造前的对照记录:WP1 之前上限只有 1 轮,且用掉后连一次质量返工都做不了。完整支撑事实(6 条,均带 file:line)见第 1.1 节「D11 新拓扑」;project-planning 的 agentCatalog 登记方式与 build.rs 一致性校验已于 2026-08-13 定稿(见第 3.1 节、第 23.1 节),代码落地留给 M1;M1 的真正前置是第 3.1 节新发现的 prompt.rs 角色身份合成缺口。 |
3. 合同名称注册表
| 类别 | 冻结值 |
|---|---|
| UI 阶段名 | 立项策划 |
| 英文称呼 | plan phase |
| 策划 Agent 称呼 | 立项策划 Agent(2026-08-12 起不再是 persona;2026-08-13 起也不再是「工作流节点」,改为 Supervisor 的静态委派子 Agent,见 D11) |
| 策划节点 agentId | project-planning(登记 agentCatalog;project- 前缀标明项目级、不属任何专业组,故不与 design 组的 design-director / design-foundation 混淆;登记机制定稿见第 3.1 节,本轮只冻结机制、代码留给 M1) |
| 策划子 Agent durable source | agent-delegate(2026-08-13 按 D11 取代原值 agent-ready-task-scheduler。字面量见 apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/delegation.rs:1063;不新增 source,复用现役静态委派族。该 source 同时是 validate_user_input_action_owner 拒绝直接提问的判据之一,见第 19 节第 2 条) |
| Rust source 常量 | AGENT_RUNTIME_SUPERVISOR_PLAN_SOURCE |
| run profile | standard(Supervisor 根 run 与策划子 Agent 必须同为此值。2026-08-13 按 D11 更正成立理由:不是 ready-task 调度的约定,而是委派通用的父子继承——bind_game_creator_agent_runtime_run_profile_at 强制子 Run 等于父 Run 已绑定的 profile,「子 Run 不能切换父 Run 的 Run Profile」) |
| Prompt composition | 不新增。策划子 Agent 复用现役 runtime composition(委派/孤立子 Agent 通用模板),Supervisor 入口沿用现役 supervisor composition。2026-08-13 按 D11 取代原冻结值 projectPlanning:manifest.json 的 compositions 只有 runtime/supervisor/supervisorChat 三个固定字段,校验不遍历 agentCatalog,新增子 Agent 不需要也不能配第四套(依据见第 3.1 节) |
| Prompt source kind | 不新增;策划子 Agent 的 exact planning Provider request 固定使用现役 runtime,不得把 durable source agent-delegate 复制进 sourceKind |
| 原生 action tool | plan.submit_gdd。2026-08-13 按 D11 删除 plan.request_decision:该工具是 D10「Runtime 直投」的配套,其存在的唯一理由是「standard ready-task 节点无 parent、Runtime 拦不住直接提问,只能另造工具绕开」;D11 下策划 Agent 是委派子 Agent、天然带 parent,问询改走 AGC_NEEDS_USER_INPUT_V1 终态信封 + Supervisor 中转,该工具连同 D10 一并作废,从未实现,全仓库零命中 |
| 问询载体 | AGC_NEEDS_USER_INPUT_V1 终态信封(复用 PR #165 已实现的静态委派澄清中转,非本方案新增合同),单次信封 1~3 题 |
| pending kind | gdd-approval |
| 审批 Tauri command | decide_game_creator_plan_gdd |
| 审批动作 | approve | revise | reject |
| submit input schema | plan-submit-gdd-input.v1 |
2026-08-13 随 D10 删除(原值 plan-decision-checkpoint.v1)。D11 下解释由 continuation 子 Agent 的第一个普通 tool-plan turn 产生,走现役 handoff 通道,不需要专用 schema |
|
| Provider session binding schema | plan-provider-session-binding.v1(durable lifecycle/batch 嵌套对象) |
| Provider structured injections schema | plan-provider-structured-injections.v1(作为 dedicated user message 进入实际 LlmRunRequest,精确 wire 见第 12 节) |
| plan Provider request kinds | tool-plan | final-reply | context-compaction | final-reply-context-compaction(2026-08-13 按 D11 移除 plan-decision-checkpoint;2026-08-14 将 exact planning 的两类 context compaction 纳入同一 v3 binding) |
| plan Provider request lifecycle schema | game-creator-provider-request-lifecycle.v3 |
| plan Provider action batch schema | game-creator-provider-action-batch.v4 |
| GDD schema | plan-gdd.v1 |
| index schema | plan-gdd-index.v1 |
| approval schema | plan-gdd-approval.v1 |
| session schema | plan-session.v1 |
| approval pending schema | plan-gdd-approval-pending.v1 |
| decision audit schema | agent-runtime-plan-gdd-decided.v1 |
| hydrate Tauri command | hydrate_game_creator_plan_gdd_state |
| hydrate read-model schema | plan-gdd-state-view.v1 |
| GDD 状态 | draft | ready_for_approval | revision_requested | approved | rejected | superseded |
| 单项决定状态 | confirmed | default_pending | prototype_pending |
| 回答来源 | user_option | user_freeform | user_revision | default |
| 审计 recordType | agent.runtime.plan.gdd_decided |
| planning typed 指纹文本 | sha256-serde-json-v2:<64 位小写十六进制> |
| 现役 action/profile binding digest | <64 位小写十六进制>,无前缀 |
| 构建引用 | approvedGddRef {gddId, version, fingerprint} |
| 现有 design 组 UI 名 | 设计实现组,替代原“策划 Agent”卡片名称 |
| 新组件名 | GDD 审批卡 |
| 固定入口动作 | 当前客户端为 做成游戏:读取 game/fast_gdd.md,直接创建自动游戏工作区、导入参考附件并以固定建造指令启动 Direct Codex;不再回首页等待用户二次提交 |
approvalRequestId 与 responseId 是两个不同的持久 ID:前者由 Runtime 在 GDD 提交前生成、进入不可变 GDD,一张审批卡终身不变;后者由 UI 在用户执行一次决定时生成,并在传输重试中复用。不得继续用含义不明的单个 requestId 同时承担两种职责。
本文同时沿用现役 user-input transport 的同名 responseId,必须按所属 DTO 区分:user.input_request answer/checkpoint 中的 responseId 是“本轮问题回答 ID”,遵守现役 user-input 形状;现役 UI 继续用 app-user-input-* generator 产生不超过 160 scalar 的值,后端兼容合同仍是 trim 后 1~160 scalar、无控制字符,不新增会拒绝历史/重试 ID 的前缀门禁。decide_game_creator_plan_gdd/approval receipt 中的 responseId 是“GDD 审批决定 ID”,固定为 gdd-response-<uuid>。两者只在各自 requestId/action/ref 域内幂等,不能跨域比较、复用或互相恢复;下文需要消歧时分别称 answerResponseId 与 approvalResponseId,磁盘/wire 字段仍均为 responseId。
(2026-08-13 删除:原此处规定 requestKind=plan-decision-checkpoint 的合法性边界。该 kind 随 D10 作废,任何 lifecycle/batch 出现它一律失败关闭。)
3.1 project-planning 的 agentCatalog 登记机制定稿(2026-08-13,机制冻结,代码留给 M1)
本节处置第 1.1 节「D11 新拓扑」第 6 条与第 23.1 节「project-planning 的编译期 agentCatalog 登记方式与 build.rs 一致性校验」这一待裁决项。结论:机制成立(works_with_changes)——project-planning 可以在不改 build.rs、不改 16 任务种子 DAG、不改 new_game_creation_app_seed_tasks() 的前提下登记进编译期 agentCatalog;但「登记进 catalog」只解决成员资格,不解决能否真正跑起来——role 身份合成还卡着一个独立的硬编码二分,必须在 M1 一并处理,不能只做本节描述的登记就当作 D11 可执行。
登记位置:prompts/runtime/manifest.json(apps/ai-game-creator-shell/src-tauri/prompts/runtime/manifest.json)的 agentCatalog 下新增一个与 supervisor 平级、不放进 groups 数组的独立条目(内部 id/taskId 冻结为 project-planning,外层键名未冻结):
"agentCatalog": {
"supervisor": { /* 不动 */ },
"planning": {
"id": "project-planning",
"label": "立项策划",
"role": "Project Planning",
"briefPathName": "project-planning.md",
"roles": [
{
"id": "project-planning",
"role": "Project Planning",
"taskId": "project-planning",
"toolId": "agent.runtime.project-planning",
"briefPathName": "project-planning.md"
}
]
},
"groups": [ /* 现有 6 组不动 */ ]
}
为什么必须这样、且不影响「做游戏链路一行不动」:build.rs 的 validate_seed_task_catalog(apps/ai-game-creator-shell/src-tauri/build.rs:23-46)把编译出的 (taskId, groupId, role) 集合与 shared_contracts::game_creation_app::new_game_creation_app_seed_tasks() 做逐项相等比对,不等即 panic!;而这个被比对的 specialist_nodes 集合只来自 manifest.agent_catalog.groups[].roles[](build_support/runtime_prompt_bundle.rs:387-398),不遍历 agentCatalog 的其它顶层键。project-planning 挂在 groups 数组之外的平级条目上,天然不进入 specialist_nodes,因此 build.rs、种子 DAG、new_game_creation_app_seed_tasks() 三者都不需要改动,约束 A「做游戏链路一行不动」成立。
先例:project-supervisor 本身就是这个模式的既有先例——它是 agentCatalog 成员,但不是种子 DAG 任务。runtime_adapter.rs 的 build_game_creator_runtime_agent_catalog(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_adapter.rs:4-38)先单独 push 一个 project-supervisor 的 AgentDescriptor(5-18 行),再遍历 GAME_CREATOR_AGENT_GROUP_DEFINITIONS push 各组角色(19-35 行);project-planning 应照此同构镜像,在 supervisor 之后、group 循环之外再 push 一段。
descriptor 元数据取值(AgentDescriptor::with_metadata):
| 字段 | 取值 | 依据 |
|---|---|---|
groupId |
"project-planning"(自引用伪 group id) |
照抄 supervisor 先例(groupId: "supervisor");绝不能填 "design"——design 组中文 label 是「策划组」,与「立项策划」语义高度相似,填成 design 会让 project-planning 被任何按 groupId 过滤或按 label 展示的代码误认成 16 任务 DAG 里 design 组的成员 |
groupLabel |
不设置 | supervisor 的 descriptor 本就没有这个 key(只有 groupId/roleLabel/toolId/capabilityAuthority),同构省略;顺带避开「策划组」字符串碰撞 |
roleLabel |
跟随 project-planning 自己的角色定义(如 "Project Planning") |
无特殊约束,随角色定义走 |
toolId |
"agent.runtime.project-planning" |
跟随 supervisor 的「组外单节点」命名族 agent.runtime.<id>,而非组内角色的 agent.role.brief.<groupId>.<roleId> 族——后者会误导成「某组角色 brief 生成工具」 |
capabilityAuthority |
"game-creator-tool-policy-snapshot" |
与现有全部条目(supervisor、16 个组内角色、两个 run profile)取值相同;全仓库检索确认这是无差异的 App 级常量声明,不是 per-agent 差异化字段,没有理由为 project-planning 发明新值 |
全仓库检索确认:AgentDescriptor::metadata() 这个 accessor 目前零生产调用;game_creator_runtime_agent_catalog() 唯一生产调用点 runtime_state.rs:1491(即 normalize_game_creator_runtime_agent_id)只做 .get(agent_id).is_some(),不 touch .metadata();AgentCatalog::iter() 也零生产调用。也就是说上表五个字段今天是纯「写入态」,没有消费方;但正因为没人读,今天怎么填就是给未来消费方定下的先例,必须按上表小心填,不能因为「反正没人读」就随意取值。
编译链路要改的确切位置(均属 M1 落地范围,本轮不动):
build_support/runtime_prompt_bundle.rs:108-113的struct AgentCatalog需新增字段planning: AgentGroup(顶层#[serde(deny_unknown_fields)],manifest.json 加了planning键而这里不加字段会编译期反序列化报错)。- 同文件
validate_agent_catalog(692-758 行)里所有硬编码只处理supervisor的地方要镜像planning:693-695 行「必须恰好 1 个 role」的约束;700 行 briefPathName 去重的once(&catalog.supervisor).chain(...)要把&catalog.planning串进去;708 行generated_names占位防冲突集合要追加"PROJECT_PLANNING";728-733 行的显式单独调用validate_agent_group(&catalog.supervisor, ...)之后要紧跟一行validate_agent_group(&catalog.planning, ...)(否则planning的 id/taskId/toolId/briefPathName 格式与跨组重复检查全部被跳过);738 行 alias 循环只遍历catalog.groups,不需要改(planning和supervisor一样不参与agent_role_alias_id别名机制)。 compile_manifest的catalog_task_ids构造(297-310 行)要把manifest.agent_catalog.planning.roles.iter()链进去,否则未来若给project-planning配roleOverlay会在 314 行误报「引用了未知 agentId」。specialist_nodes构造(387-398 行)只读manifest.agent_catalog.groups,不改、也不能改——这正是planning放在groups数组外、不污染种子 DAG 校验的关键。render_agent_catalog(972-1009 行)要给planning镜像 supervisor 专属的四个产物:GAME_CREATOR_PROJECT_PLANNING_AGENT_ID、GAME_CREATOR_PROJECT_PLANNING_MEMORY_PATH(memory/agents/{planning.brief_path_name}格式)、PROJECT_PLANNING_AGENT_ROLES数组、static PROJECT_PLANNING_AGENT_DEFINITION——这些静态名是runtime_adapter.rs里 push 段要引用的名字来源。runtime_adapter.rs:4-38的build_game_creator_runtime_agent_catalog新增一段与 5-18 行完全对称的AgentDescriptor::try_new(...).with_metadata(...)push,用上表元数据取值。
composition/brief 相关的强制要求:
- 不需要新配置 composition。
manifest.json的compositions只有runtime/supervisor/supervisorChat三个固定字段,supervisor 之所以多出后两套是因为它是唯一「非孤立、带完整拼装」的身份,不是「配 composition」这件事本身的通用要求。project-planning作为委派子 Agent,天然复用runtimecomposition(孤立子 Agent 通用模板),不配不报错——校验(validate_composition516-545 行)只针对三个固定字段做,不遍历agentCatalog。 briefPathName只是运行时文件名 token(memory/agents/**长期记忆、.agent/passes/**单轮 brief 快照两类文件复用),不是指向仓库预先存在文件的引用;格式要求(.md结尾、裸文件名、首字符字母数字、只含字母数字/-/_/.)在validate_file_name(839-857 行)编译期强制,且project-planning组级 briefPathName(如project-planning.md)不得与现有 7 个(supervisor + 6 组)任何一个重名;但缺文件不会导致任何失败——read_previous_agent_role_brief等读取路径都把「文件不存在」当Ok(None)正常分支处理,这是 Agent 第一次跑之前的正常状态。
登记前必须先处理的代码改动(本节按调研如实列出,不粉饰——只在 catalog 里登记 project-planning 不足以让它可执行):
-
blocking:
prompt.rs的game_creator_agent_role_definition(apps/ai-game-creator-shell/src-tauri/src/agent/prompt.rs:661-679)是把agent_id合成出(group_definition, role_definition)身份信息的唯一函数,目前只做「agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID则特判,否则遍历GAME_CREATOR_AGENT_GROUP_DEFINITIONS的 group.roles」这个二分——project-planning落进 else 分支后遍历不到任何组角色,返回None。它的两个调用方都是.ok_or_else(...)?直接把None转成Err中断:provider_request_builders.rs:536-539(build_game_creator_background_agent_context)——agent.delegate派发后台任务后,Runtime 驱动每一轮实际执行都要经过这里,project-planning第一轮就会硬失败,报「未知 Agent 模板:project-planning」;prompt.rs:416-417(build_game_creator_role_agent_context_for_session)——直接对话式 chat 的身份/上下文构建函数,同样的失败方式。
也就是说,仅做本节描述的 catalog 登记,D11 的静态委派在执行层面完全跑不起来;
game_creator_agent_role_definition需要一个平行于 supervisor 特判的project-planning分支,这是 M1 必须一并落地的代码,不属于本轮「不改任何 .rs」的范围。 -
needs_change:
task_ops.rs的task.create工具(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/task_ops.rs:335-363)在拿不到角色定义、且调用方未显式传group/role时,把 group 兜底成Design、role 兜底成"Agent",而不是报错或识别「无 group」。task.create对所有agent_id统一开放,project-planning一旦调用它且未显式带参数,会被静默记成「Design 组 / Agent 角色」任务写进 manifest.json,污染 6 组任务审计和按 group 聚合的面板,且无任何报错提示误分类。 -
needs_change:
delegation.rs的agent.spawn_isolated校验(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/delegation.rs:1396-1412)只排除child-前缀和 supervisor 本体,其余 catalog 成员一律放行为合法的 isolated 模板。project-planning登记后会自动满足这个放行条件,于是任何持有agent.spawn_isolated权限的 Agent(不只是 Project Supervisor)都能拿它当模板动态孵生隔离子实例——这与 D11「project-planning只应由 Supervisor 通过agent.delegate静态委派」的设计意图不一致,是登记后新增的隐性能力扩权,需要在 M1 与「blocking」项一并处理,不能默认「反正没人这么调」。 -
needs_change(2026-08-13 对抗性复核补记):
task_start.rs的collect_game_creator_agent_runtime_agent_ids(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs:3-28)与game_creator_agent_role_definition是同构的二分硬编码——只显式插入 supervisor id(8 行),再无条件遍历GAME_CREATOR_AGENT_GROUP_DEFINITIONS插入全部 16 个组角色 task_id(19-23 行,不看当前是否有活跃任务),project-planning既不是 supervisor、也不是组角色、也不是agent.spawn_isolated产生的动态孤立实例,天然不进入这个集合。此函数是核心 Runtime 推进/恢复驱动read_game_creator_agent_runtimes_at(entrypoints.rs:756,被commands.rs/swarm_cli调用)与三处安全网的枚举来源:崩溃恢复扫描(recovery_scan.rs:448、:687)、root run 被 steer 时的级联取消(steering.rs:800的cancel_goal_contract_root_descendants_at)、委派回执兜底对账(delivery.rs:1287的reconcile_game_creator_agent_delegate_receipts_at,由recovery_scan.rs:1102触发)。经追踪agent.delegate派发路径(delegation.rs里对start_game_creator_agent_background_task_with_link_at的调用),委派首轮任务是同步直接起跑的,不依赖这个集合,因此不属于「首轮即挂」的 blocking 类;但一旦应用重启、Supervisor 根 run 被 steer、或首轮结果的直接投递失败需要兜底对账,project-planning的委派状态都不会被这三处安全网发现和处理,会静默变成孤儿任务,且与第 23.1 节「plan run 是否允许 steer」这条既有待裁决项直接相关(提供了其未验证交互的具体机制证据)。M1 需要给这个函数补一个与「group 角色无条件收录」对称的第三条分支;runtime_adapter.rs里game_creator_runtime_agent_catalog_matches_the_existing_role_directory测试(同文件 91-121 行)断言 catalog 成员集合与{supervisor} ∪ 16 组角色精确相等,project-planning登记后这条测试会立即失败,M1 需同步更新其期望集合,不能靠这条测试的失败倒逼才发现遗漏。 -
benign(不阻塞,仅记录):前端
projectProfessionalAgentLabel(apps/ai-game-creator-shell/src/features/agent-runtime/model.ts:1496-1529)的关键字匹配落不到project-planning,会退化成直接显示原始agentId。纯展示层缺口,不影响功能,M1 顺手补一条分支即可。 -
未来提醒(写入代码前必须核对,本轮不改):
pass_artifacts.rs的agent_role_memory_relative_path_for_task(apps/ai-game-creator-shell/src-tauri/src/agent/generation/pass_artifacts.rs:335-347)也是「先特判 supervisor、再遍历 group」的同构硬编码,若不加第三条project-planning分支,运行时对它调用会落入 346 行的Err("未知 Agent 任务")。 -
排雷提示(供未来维护者,非本轮改动项):全仓库检索确认,当前没有任何生产代码枚举 runtime
AgentCatalog(.iter()零生产调用),前端「团队」清单(agentPresentation.ts的groupConfigs)都是硬编码字面量/精确字符串判断,结构上不会把project-planning带进「做游戏」团队清单。但未来如果有人写「遍历 AgentCatalog 生成 UI 团队卡片/prompt 团队成员清单/任何 route 白名单」这类代码,必须显式按groupId ∈ {design, balance, art, audio, code, publishing}六个真实组过滤,或显式排除project-planning(和supervisor)这两个组外单节点——这也是坚持groupId必须自引用为"project-planning"、不能复用"design"的根本原因。
本轮范围声明:以上登记机制与代码改动清单均属 M1 实现范围,本轮(M0A-3 文档工作包)只冻结机制、不落地任何代码——不改 manifest.json、不改 runtime_prompt_bundle.rs、不改 runtime_adapter.rs、不改任何 .rs 文件。理由:project-planning 目前没有 prompt、没有任何路径能调用它,现在就注册 catalog 而不同步处理上面的 blocking 项,等于给发布产物加死重(一个「看似已登记、实则一调用就硬失败」的 Agent 身份),注册代码应与 M1 的 prompt/source 一起落地。
4. Runtime 拓扑与可信边界
flowchart TD
U["用户"]
subgraph SUP["Project Supervisor 顶层 root run(三个入口 source)"]
SPLAN["做方案入口<br/>source=project-supervisor-plan<br/>profile=standard"]
BUILD["完整构建 Supervisor<br/>source=project-supervisor-gui 或 project-supervisor-cli<br/>profile=autonomous-game-build"]
end
U <-->|"唯一对话对象;问答物理落 Supervisor 会话文件"| SPLAN
SPLAN -->|"agent.delegate 静态委派(profile 由父 run 继承)"| PLANAGENT["立项策划子 Agent<br/>agentId=project-planning<br/>source=agent-delegate<br/>profile=standard<br/>composition=runtime(复用)"]
PLANAGENT -->|"以 AGC_NEEDS_USER_INPUT_V1 终态信封退出"| DELIV{{"needs-user-input delivery"}}
DELIV -->|"Supervisor 认领回执"| SPLAN
SPLAN -->|"在自己的 runtime/session 上建 pending,向用户提问"| U
SPLAN -->|"answersSha256 绑回 delivery,再发 continuation 委派<br/>(最多 3 轮,受 clarification_round 上限约束,见第 23.5 节)"| PLANAGENT
PLANAGENT --> GDD["不可变 GDD + approve receipt"]
GDD -->|"用户动作:做成游戏;读取并导入 game/fast_gdd.md"| BUILD["自动创建游戏工作区<br/>参考附件:fast_gdd.md<br/>固定建造指令"]
U -.->|"直接开建"| BUILD
BUILD --> DAG["现行 16 任务 DAG"]
U --> CHAT
CHAT --> MAIN["code-prototype 单主 + 按需美术 child"]
GDD -.->|"M3 可选只读"| CHAT
4.1 source、profile 与 run 身份
2026-08-13 按 D11 整节重写。 原文按 D6 persona 模型写成「策划 run 就是
agentId=project-supervisor+source=project-supervisor-plan-chat的那一个顶层 run」,D11 下做方案链路是两个 run:未变的 Supervisor 顶层 root run,加一个由它静态委派出的策划子 run。凡本节与旧表述冲突处一律以本节为准;已作废的字面量project-supervisor-plan-chat与is_exact_supervisor_plan_run_at不得再使用。
**第一层:Supervisor 根 run(做方案入口)**必须同时满足:
agentId=project-supervisor;- 顶层 run,
parentAgentId、parentRunId和 delegation identity 均为空,rootAgentId=agentId、rootRunId=runId; source=project-supervisor-plan;runProfile=standard;- source、profile、session、run 与 profile binding fingerprint 已写入现有 durable run configuration,并在恢复、steer、工具执行和审批命令中逐项相等。
第二层:策划子 run必须同时满足:
agentId=project-planning;parentAgentId=project-supervisor、parentRunId等于上述根 run 的runId、delegationId非空——这三项由agent.delegate派发时写入 task link,是validate_user_input_action_owner拒绝它直接向用户提问的结构性判据;source=agent-delegate;runProfile=standard,且该值不是独立配置的,而是由父 run 的 profile binding 强制继承(子 Run 不能切换父 Run 的 Run Profile);因此「父子同为 standard」不需要额外的一致性校验去保证,只需要保证父 run 本身是 standard;targetRunId由 Runtime 从delegationId确定性派生,targetSessionId解析为project-planning的持久 active session——同一策划链路的多轮 continuation 共享同一条 session,这是多轮之间不失忆的机制依据(详见第 1.1 节「D11 新拓扑」第 3 条)。
任一字段缺失、漂移或与当前 active plan lineage 不同都失败关闭。普通 standard Supervisor 不能借 source 名称进入策划工具面,plan source 也不能切换到 autonomous-game-build。每个项目同一时刻最多一个非终态 plan lineage;首次开始策划时,Runtime 在项目锁内生成唯一 gddId 并 create-only 创建 session revision 1。第二窗口并发启动返回 typed PLAN_ACTIVE_RUN_EXISTS,不得创建第二条 lineage。
「同一条策划链路」的判定不能只看子 run 的 agentId。 委派子 run 的身份权威是 (parentAgentId, parentRunId, delegationId) 三元组加上 delivery 记录本身;多轮 continuation 每轮都是新的 delegationId 与新的 targetRunId,靠 repair_of_delegation_id 串成链。判定「这是不是当前策划链路的一环」必须沿该链上溯到根 delegation 并核对根 delegation 的 parentRunId 等于当前 plan 根 run,不得用「agentId=project-planning 即认为属于本链路」这种弱判据——否则跨 run 的旧策划残留会被误纳入当前轮。
2026-08-12 复核:上述三分法在 2026-08-11 动态目标验收图合入后已不足。原文把 plan 归入「top-level Supervisor trusted matcher」,而该 matcher(agent_runtime_supervisor_source_is_trusted)如今同时是 Goal Contract 创建权限、根控制面工具授权与验收图完成门的判据,plan 一进入即被当作 Goal Contract 参与者。至少需要第四种语义「Goal Contract 参与者」,且它与 top-level trusted 的关系必须显式冻结,不能靠默认相等。此外 Goal Contract 的入口门只按 agentId=project-supervisor + root binding 判定,与 source predicate 无关,因此拆 matcher 本身解决不了它。完整处置见第 23.1 节待裁决项;该裁决可能反过来影响本节已冻结的 agentId=project-supervisor(方案 D)。
(2026-08-13 按 D11 改写) 所有针对 Supervisor 根 run 的 plan 例外必须共用单一判据函数(原名 is_exact_supervisor_plan_run_at 连同其 project-supervisor-plan-chat 取值一并作废,M1 落地时取新名):只有 durable run-profile binding 已通过 project/fingerprint 校验,且 agentId=project-supervisor、source=project-supervisor-plan、profile=standard、parent/delegation 均为空、rootAgentId=agentId、rootRunId=runId,并与 task/runtime 以及存在时的 planning pending、尚存 batch 的 source/profile/binding fingerprint 逐项相等时才返回 true。缺失必需 binding、损坏或漂移不得获得 plan 例外,不能只比较内存中的 runtime.source。
策划子 run 不共用上述判据,它的合法性由第二层身份条件加委派 delivery 链决定(见本节前述)。不得把子 run 也塞进同一个函数——那会要求该函数同时接受「parent 为空」和「parent 非空」两种互斥形态,判据随即失去意义。
普通 standard retry 当前会改写为通用 background source。M1 必须在 generic standard fallback 前增加 exact plan root 分支,但不扩大现役 retry 的破坏面:只有 Provider 故障的旧 plan 根 task 已由现有生命周期收束为终态、没有 provider/action/planning pending ledger,且原 task 与已验证 root binding 逐项相等、无 parent/delegation 时,retry 才以同一 gddId/session 创建新 run,继续使用 project-supervisor-plan + standard,并在项目锁内写 session revision+1 successor;不得由 retry helper 主动终结 running/waiting run,不得新建 gddId,也不得降级为 agent-background-task。任一身份校验失败直接拒绝 retry。已经进入 gdd-approval 等待的 run 禁止走 generic retry,只能恢复并续跑 receipt 所绑定的精确原 run。
策划子 run 的失败恢复不走上述根 run retry 路径。 它是静态委派子 Agent,其失败按现役委派回执语义收束:子 run 终态形成 delivery,由 Supervisor 认领后决定是发质量返工还是终止本轮策划。这里必须注意 WP1 定稿的预算语义(见第 23.5 节)——澄清与质量返工是两个独立维度,repair_depth 上限仍是 1,因此一条策划链路最多只有一次质量返工机会;把它耗在可以靠 continuation 解决的问题上,后续真出现交付质量问题时就没有额度了。M1 的 Supervisor Prompt 须显式区分这两种情形。
4.2 Prompt 分流
2026-08-13 按 D11 整节重写。 原文的前提是「策划是 Supervisor 的第三套 persona,因此需要一套独立 composition
supervisorPlanChat」。D11 下策划是独立agentId的委派子 Agent,两侧各自沿用现役 composition,不新增第四套;supervisorPlanChat/SupervisorPlanChat两个字面量随 D6 一并作废,不得再出现在实现或文档中。
- Supervisor 根 run 沿用现役 supervisor composition。做方案入口不需要专用 persona 稿:它的 Provider 不生成策划内容,只做委派、认领回执、代为提问与审批收束;这些能力现役 supervisor composition 已经具备。差异化只体现在 Prompt 的任务描述与工具面上,不体现在 composition 选择上。
- 策划子 Agent 复用现役
runtimecomposition(委派/孤立子 Agent 通用模板)。其角色专属内容由 agentCatalog 条目的 brief 承载,而不是由 composition 承载——这与所有专业 Agent 的组织方式一致,见第 3.1 节。 - 因此本方案不新增 Prompt Bundle 的编译期 source kind。原
SourceKind::SupervisorPlanChat不再需要。 - 策划子 Agent 的每一轮 continuation 都是新 run、同 session,其 Provider 请求的历史来自该 session 的既有对话,不需要也不应该由 Prompt 层去拼接「前几轮问答」。需要显式传递的只有用户的答案——它物理落在 Supervisor 会话文件而非子 Agent 会话文件,只能由 Supervisor 写进新一轮委派的 task 文本;该转述的保真依赖 Supervisor 侧 Provider,Runtime 只校验
questionsSha256/answersSha256的哈希绑定,不校验转述语义(残留风险,见第 1.1 节「D11 新拓扑」第 3 条)。
4.3 两层工具面与两项协议控制函数
2026-08-13 按 D11 整节重写。 原文假设做方案链路只有一层「plan source」,因此把工具清单、
agent.delegate禁令和 collaboration 冻结全写成同一层的属性。D11 下是两层,且两层的结论在若干条上正好相反——尤其agent.delegate:策划子 Agent 禁止,Supervisor 根 run 恰恰必须使用。凡与本节冲突的旧表述一律作废。
第一层:Supervisor 根 run(source=project-supervisor-plan)
工具面按现役 standard deny-list 模型,不改为 allow-list。它需要且必须持有:agent.delegate(发起与续跑策划委派,这是做方案链路的主动作)、user.input_request(代策划子 Agent 向用户提问)、以及现役 standard Supervisor 既有的读取与审批相关能力。
关键约束不是「不许持有」,而是「持有但不得这样用」:其 Provider 不得自行发起策划性提问。user.input_request 的合法用法只有一种——认领策划子 Agent 的 AGC_NEEDS_USER_INPUT_V1 终态信封后代为创建 pending,且问题正文应与信封逐字一致。
2026-08-13 更正(此前本节声称该约束「靠机制保证,不靠 Prompt 劝阻」,该说法不成立):做校验的
static_delegate_clarification_pending_matches_delivery_at(结构相等 +questionsSha256)全仓库只有一个生产调用点——apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_recovery.rs:791,而它外层套着runtime.run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD。做方案链路跑的是standard,该校验根本不触发。因此在本链路上:Supervisor 既可以自行发起提问,也可以改写子 Agent 的问题原文再问,Runtime 都不拦(validate_user_input_action_owner只拦有 parent 的 Agent,根 Supervisor 不在其列)。这是产品约束,不是机制约束,目前只有 Prompt 兜底。若要把它变成机制约束,须为standard下的 plan 根 run 单独接一道等价校验——属 M1 范围,本文档不假装它已存在。
第二层:策划子 Agent(agentId=project-planning,source=agent-delegate)
M1A-2 已把本层的身份绑定落实到 Provider 初始请求、repair/rebuild 请求、tool-plan parser、action batch/pending、并行只读、执行和状态恢复:只有 source=agent-delegate、profile=standard、父 Agent 为 project-supervisor 的 planning child 才能使用该窄工具面;Supervisor 根 run 不继承该 allowlist。恢复时即使旧快照或调用方带入完整目录,也会归一化回窄面,避免从空/损坏快照扩权。
action 工具广告与执行双门都必须是 exact allowlist,MCP catalog 为空,webSearchEnabled 固定为 false。允许项恰好是:
file.readfile.listplan.submit_gdd
实现分层说明(M1A-2,2026-08-13):上表保留最终策划合同的
plan.submit_gdd位置,但该 capability 属于后续M1B-2,当前 M1A-2 不注册、不广告、也不执行提交逻辑。当前已落地的 planning native action 仅为file.read、file.list;update_agent_plan与respond_to_user仍是协议控制函数。这样既先收口身份隔离和 Provider 请求形状,也不把未实现的 GDD 提交误报为可用。
update_agent_plan 与 respond_to_user 是 Runtime 协议控制函数,不计入上述清单,但仍受现有结构、轮次和终态门禁约束。
明确禁止:通用写入、patch/delete、command、process、preview、canvas、asset generation、确认型副作用、agent.delegate、isolated child、任务图调度和所有 MCP 工具。
user.input_request 不在允许清单内。委派子 Agent 天然带 parent_agent_id/parent_run_id,该调用被执行层 validate_user_input_action_owner(apps/ai-game-creator-shell/src-tauri/src/user_input.rs:367-394)兜底拒绝;M1A-2 同时让它从 planning 的函数目录消失,避免模型浪费轮次。广告层过滤、Provider parser 原始身份校验、batch/pending/recovery 再验证和执行层兜底必须并存,不能只测其中一层。
collaboration policy 的处置(原「六类入口冻结」按 D11 修订)
原文的核心主张是「plan run 跳过 Supervisor 的 collaboration 强制委派」。该主张在 D11 下方向反转:策划子 Agent 恰恰是被 Supervisor 委派的下游,强制委派不再是需要绕开的东西,而是链路本身。修订如下:
- Supervisor 根 run:不再要求跳过 collaboration 预检,但项目级 collaboration policy 不得把做方案链路当成 16 任务 DAG 的协作场景来编排——它只应产生一条指向
project-planning的委派,不得注入专业组 delegate 示例、MCP 或 canvas 能力。具体是「按 source 收窄现役 policy」还是「为 plan source 单列一份最小 policy」,属 M1 详细设计。 - 策划子 Agent:collaboration policy 完全不适用,
collaborationPolicy固定为 not-applicable;Provider request/context 不读取或渲染它,MCP catalog 在构建请求前即为空。 - 策划子 Agent 的 action batch 若持久化了非空
collaborationContract,视为身份污染并进入 reconciliation;batch validate/write/recovery 只有先验证身份且collaborationContract=null才能读取。 - 完成门:策划链路改由专用 GDD completion blocker 检查提问/审批等待、receipt、observation、session 与 recovery 状态;现役 collaboration completion blocker 对策划子 Agent 返回 not-applicable,身份验证错误不能被吞成例外。
- prompt context、batch ledger 或 final-reply 任一调用点都不得另写宽松 source 字符串判断,全部调用第 4.1 节的统一谓词(根 run 与子 run 各一套,不可混用)。
4.4 平台事实
Runtime 注入并强校验以下精确结构:
{
"runtime": "self-contained-web",
"viewports": ["desktop", "mobile"],
"inputs": ["keyboard", "touch"],
"preview": "local-http"
}
数组顺序也是合同的一部分。Agent 不得修改、删减或向用户提问。game/index.html、远程依赖禁令和完整构建安全门仍由现役构建合同负责,不在 plan Prompt 中复制另一套运行规则。
5. 对话、决策卡与 Fast GDD
5.1 对话循环
-
最多 3 轮主动追问,每轮只问 1 个主要决定;审批修改后的 continuation 仍沿用同一问询预算,可以继续问询或直接提交新的完整 GDD,问询计数不重置。
-
建议顺序:核心行为与本局目标 → 重玩动力 → 制作边界与 MVP。
-
满足任一条件即出稿:用户明确说“直接出稿”;已经完成第 3 轮;剩余问题不影响首个可玩闭环;Runtime 注入 240 秒 Agent 活跃时间软提示。
-
accumulatedAgentMillis只累计 Provider 活跃区间,不包含等待用户、等待审批、进程休眠或应用关闭时间。2026-08-13 按 D11 重写以下四条。 原文描述的是 D10「Runtime 直投」状态机:策划节点持续存活于同一 run,用
plan.request_decision提交决策字段,Runtime 直投出 pending,回答后再在同一 run 内发起专用plan-decision-checkpointProvider turn,并以新 session primary 作为线性化点。D11 下这套整个不成立——策划子 Agent 不能调user.input_request,且它是以终态信封退出来提问的,该 run 随即结束,不存在「同一 run 内的第二个 turn」。D11 用已发布并有回归覆盖的 PR #165 中转链路替换了这台自造状态机。 -
提问:策划子 Agent 以
AGC_NEEDS_USER_INPUT_V1终态信封退出本轮 run,信封含 1~3 题的结构化问题。Runtime 据此形成contract_status=NeedsUserInput的 delivery,问题原文与questionsSha256一并落在 delivery 里——delivery 就是「已问出、未回答」这一状态的权威载体,不再需要 session.activeQuestion 承担该职责。 -
提问对用户可见:Supervisor 认领该 delivery 后,在自己的 runtime/session 上创建
waiting-for-user-inputpending。问题正文必须与信封逐字一致,由static_delegate_clarification_pending_matches_delivery_at做结构相等加questionsSha256双重校验;Supervisor 改不了子 Agent 的问题原文。 -
回答与线性化点:用户在 Supervisor 会话内作答,
requestId与answersSha256原子绑回原 delivery。该绑定就是本轮的线性化点,取代原「新 session primary」——它由已发布代码保证「首次写入或逐字相同则幂等,绑到不同请求或不同答案则拒绝」,页面重载、Runner 重启或重放都不会把一条回答计成两轮。 -
解释与下一步:Supervisor 随后发起 continuation 委派,Runtime 从
(parent_run_id, 原 delegationId, questionsSha256, answersSha256)确定性派生新的 delegation 身份——同一组问答只能派生同一个 continuation,重放天然幂等。新一轮子 Agent 是新 run、同 session,它在自己的第一个普通 tool-plan turn 里既完成对上一轮回答的设计解释,也决定下一题或plan.submit_gdd。因此不再需要独立的plan-decision-checkpoint请求 kind:原本要靠它保证的「解释与下一动作不能挤在同一个 Provider response 里」,在 D11 下由「上一轮 run 已终态、下一轮是全新 run」这一结构性事实免费保证。 -
轮次计数与上限:本轮是第几轮由委派链上的
clarification_round派生值决定(沿repair_of_delegation_id上溯推断,见第 23.5 节),上限 3;不再由 session 自行累加roundsUsed。session 仍是 decisions 数组与 GDD 草稿内容的权威,但不再是轮次状态机的权威。
随之而来的合同影响 —— 2026-08-13 已全部收口。 第 3 节注册表的
plan-decision-checkpoint.v1与 request kind、第 8.6 节plan-session.v1的activeQuestion/roundsUsed/supersededCheckpointHandoffs、第 9 节的 checkpoint domain 与supersededCheckpointProviderRequestIds、第 12 节的 checkpoint stale 状态机、第 14 节与 activeQuestion 相关的恢复行,均已随本节重写一并删除或改写;第 9.1 节 golden vector 已按新 identity 重新生成(3857 bytes,a59856de7e…)。
- 用户明确输入优先于 Agent 默认;默认建议必须标为
default_pending,手感、节奏、镜头、可读性或重玩差异等需要验证的结论标为prototype_pending。审批阶段的用户修改意见使用answerSource=user_revision、round=0;Runtime 仅在 session 带 revise/reject 的lastDecisionRef时接受该来源。 - session revision 1 由 Runtime 先写入固定
initial-request决定:topic=初始需求、state=confirmed、answerSource=user_freeform、round=0、answerSummary 精确等于规范化后的 1~400 scalar 初始用户需求。Provider 不能改写或省略这条来源记录;超过上限的初始输入先要求用户收束,不能截断。
5.2 决策卡
2026-08-18
M1C-2c实现已完成并合回feat/five_min_design(见第 23.9 节):选项语义为「方案 A(推荐)/ 方案 B(平行备选)/ 固定『需要原型验证』」(恒定三项),A、B 均记confirmed / user_option,default_pending只来自未提问默认项。Runtime 已按本节收口 label 形状、状态来源和answerSummary保真;M1D-1前端决策卡直接复用本节语义。
每次 user.input_request 固定只含一题:
- header:固定为
第{N}轮·关键决定,N 为 1~3,始终不超过现有 12 scalar 上限; - question:以
当前要决定:{主题}开头,再依次说明为什么现在问、推荐方案、好处、代价; - 选项固定为三项:第一项 label 以
A加·/:/:/-开头并说明推荐方案,第二项以B加同类分隔符开头并说明真实平行备选,第三项 label 逐字为需要原型验证;每项 description 都要写清选择后果与代价,第三项还要给出可执行的微型原型验证方式; - 自由填写由现有 Other 输入槽承载,placeholder 为
改成:……。
映射:
| 用户行为 | decision state | answer source |
|---|---|---|
| 选择 A 或 B | confirmed |
user_option |
| 需要原型验证 | prototype_pending |
user_option |
| 自由填写 | confirmed |
user_freeform |
default_pending / default / round=0 只允许出现在未提问、由 Agent 按默认建议补齐的决定中。
选择“需要原型验证”必须新增一条与该 decision 使用相同 ID 的 30~90 分钟微型原型验证项,包含问题、最小原型、可观察信号和明确通过标准;不允许只写“试玩后再看”。
exact plan source 的 user.input_request 仍是四个 action tool 之一,不新增第五个 action tool。它继续使用现役 strict input,且 questions 必须恰好一题:
{
"questions": [
{
"id": "route_replay",
"header": "第1轮·关键决定",
"question": "当前要决定:路线重玩……",
"options": [
{
"label": "A · 方案 A(推荐)",
"description": "说明推荐理由、收益与代价。",
},
{
"label": "B · 方案 B",
"description": "说明真实平行路线的后果与代价。",
},
{ "label": "需要原型验证", "description": "……" },
],
},
],
}
三个 option 的数量与 A/B/第三项形状必须符合上表;A/B 的短语可动态生成,分隔符允许 ·、:、: 或 -,第三项 label 必须逐字等于 需要原型验证。plan question ID 在现役 snake*case 规则上进一步限制为最多 32 个 ASCII 字符,并且不能映射成 initial-request。Runtime 确定性令 decisionId = questionId.replace('*', '-'),因此该 ID 必然满足第 8.3 节 GDD decision ID 合同,Provider 不能另选身份。D11/M1C-2b 下 Supervisor 的 user.input_request不是 Supervisor Provider tool-plan action:它由 Runtime 在认领NeedsUserInput delivery 后、释放父 run execution lane,再经 planning parent-wake 直接投影为唯一 pending,因此不创建 Supervisor v4 Provider action batch。exact planning 子 Agent 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不能走现役“少于两项则 NotNeeded”的优化;plan.submit_gdd仍是该 batch 的唯一 member。exact planning 的final-reply、context-compaction、final-reply-context-compaction 无 action、不创建 batch,但仍写第 12 节 v3 lifecycle/binding。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。
2026-08-13 按 D11 重写本节后半(原 D10「Runtime 直投」状态机整段作废)。 原文描述的链路是:策划节点持续存活于同一 run,自己调
user.input_request,Runtime 在同一 run 内截获、写activeQuestionsession checkpoint,回答后再发一次plan-decision-checkpoint专用 Provider 请求取得设计解释,并以新 session primary 作为线性化点。D11 下这条链路的每一环都换了承载物,且不是换实现是换机制——用的是 PR #165 已发布、已有回归覆盖的静态委派澄清中转,不再自造状态机。
决策卡的承载物不变,选项语义已由 M1C-2c 收口。 上面那张映射表、A/B/固定第三项、每次恰好一题、decisionId = questionId.replace('_', '-') 的确定性映射,全部继续有效;变的是这张卡由谁产生、经谁送达:
- 产生:策划子 Agent 不能调
user.input_request(委派子 Agent 带 parent 身份,执行层validate_user_input_action_owner直接拒绝)。它以AGC_NEEDS_USER_INPUT_V1终态信封退出本轮 run,信封内是同一个规范化单题对象。Runtime 据此形成contract_status=NeedsUserInput的 delivery,问题原文与questionsSha256一并落在 delivery 里。 - 权威载体:delivery 就是「已问出、未回答」这一状态的权威,不再需要 session 复制一份
activeQuestion。这消掉了 D10 里「batch 已有而 activeQuestion 未落」「activeQuestion 已落而 standalone pending 未落」等一整族需要逐格对账的中间态——它们的存在前提是同一份问题被复制到两处。 - 送达:Supervisor 认领该 delivery 后,在自己的 runtime/session 上创建
waiting-for-user-inputpending。问题正文必须与信封逐字一致。 - 回答与线性化点:用户在 Supervisor 会话内作答,
requestId与answersSha256原子绑回原 delivery。该绑定就是本轮的线性化点,取代 D10 的「新 session primary durable」。它由已发布代码保证「首次写入或逐字相同则幂等,绑到不同请求或不同答案则拒绝」,页面重载、Runner 重启或重放都不会把一条回答计成两轮。 - 解释与下一步:Supervisor 随后发起 continuation 委派,Runtime 从
(parent_run_id, 原 delegationId, questionsSha256, answersSha256)确定性派生新的 delegation 身份——同一组问答只能派生同一个 continuation,重放天然幂等。新一轮子 Agent 是新 run、同 session,它在自己的第一个普通 tool-plan turn 里既完成对上一轮回答的设计解释,也决定下一题或plan.submit_gdd。
因此 requestKind=plan-decision-checkpoint 及其 schema、requestSlot(decision-round-{N}-session-{sessionRevision}-repair-{K})、专用 handoff 与 superseded 传递闭包全部不再需要。原本要靠它保证的「设计解释与下一个动作不能挤在同一个 Provider response 里」,在 D11 下由「上一轮 run 已终态、下一轮是全新 run」这一结构性事实免费保证。现役 tool_plan_handoff::lookup_at(root, agent_id, run_id, &response_identity) 按 (agentId, runId) 寻址、与 agent 身份无关,continuation 子 Agent 的首轮已被它覆盖,不需要新机制。
Supervisor 侧的 user.input_request。 转达用的仍是现役 strict input、仍是 questions 恰好一题、三个 option 的标签与顺序仍须逐字等于上表。plan question ID 在现役 snake_case 规则上进一步限制为最多 32 个 ASCII 字符,且不能映射成 initial-request。回答提交沿用现役 requestId + responseId + answers transport;规范化答案精确等于三个固定 label 之一时按上表识别为 option,其它值一律是自由填写,不能由 UI 另传一个未持久化的「答案类型」布尔值。plan 回答额外限制为 1~400 scalar,不得截断。
M1C-2b 的确定性决定投影。 continuation 子 Run 的首个 Provider request 必须先取得合法 session,而 appliedAnswers 又必须引用同轮 decisionsSummary;因此不能等待该子 Run 再补决定字段。Runtime 在 delivery 已绑定回答、continuation 任务已 durable 后,从原 user-input sidecar 确定性派生本轮投影:decisionId = questionId.replace('_', '-');topic 取问题正文固定前缀 当前要决定: 之后、首个 。/;/,/?/? 之前的规范化 1~80 scalar;三个固定选项分别映射为上表状态与来源,answerSummary 固定为“选项 label:该选项 description”,自由填写则逐字使用规范化回答。选择“需要原型验证”时,Runtime 同时生成同 ID 的固定四字段微型原型项:问题=验证“{topic}”是否成立,最小原型=用 30~90 分钟制作只覆盖“{topic}”的最小可交互原型,观察=记录玩家在无额外提示时的行为与对“{topic}”的口头解释,通过标准=至少 3 次独立试玩中有 2 次出现预期行为,且测试者能说明对应取舍。其它回答不得生成原型项。任一题目形状、轮次 header、文本上限或既有同 ID 投影不一致都失败关闭;不得请求 Provider 猜字段,也不得覆盖已落 session。
M1C-2b 已把 planning 链路的问答保真从 Prompt 约束提升为结构约束。 Runtime 只允许把已认领、targetAgentId=project-planning、contractStatus=NeedsUserInput 的原 delivery 投影成澄清 pending;展示前逐字校验单题、轮次 header、固定三选项、questionsSha256 与当前 planning session lineage。回答仍物理落在 Supervisor 会话及私有 user-input sidecar,但 (requestId, questionsSha256, answersSha256) 必须原子绑回同一 delivery;continuation identity 与这些指纹绑定,coordinator 再直接读取原 sidecar,确定性生成 appliedAnswers / decisionsSummary / prototypeValidationItems 并注入 planning 子 Agent 的首个 Provider 请求。因此 Supervisor 的自然语言 continuation task 不再是子 Agent 获取已确认答案的唯一事实源,改写 task 文案不能改写结构化答案事实。
审批后的用户修订继续保留已确认问答,但只跨用户修订边。 完成至少一轮澄清后,revise/reject 产生的新 planning delivery 可以沿同一 session 继续 collecting,保留既有 appliedAnswers、decisionsSummary 与原型验证项。该继承不能只靠可重算的 session fingerprint:Runtime 在 session 写入、现有 primary/previous 读取、发布后回读与普通恢复读取边界,都从 latestDelegationId 反向核真实 static-delivery 谱系,直到最后回答 continuation;每一条跨越边必须由其父 delivery 的 UserRevisionRequested 状态授权,且 root/agent/session 身份一致、无 Unknown、缺节点或循环。任一质量返工边都必须按 coordinator 规则清空 appliedAnswers,不能借更早或最新节点上的用户修订状态保留旧 transport 绑定。
边界仍需如实保留:Runtime 不做自然语言语义等价判断,也不要求 Supervisor continuation task 逐字复述答案;上述机制兜底只覆盖 exact planning 澄清,不能反向宣称通用 PR #165 静态委派问答都已获得同等级结构化决定投影。
轮次计数。 本轮是第几轮由委派链上的 clarification_round 派生值决定(沿 repair_of_delegation_id 上溯推断,语义见第 23.5 节),上限 3;session 不再自累加 roundsUsed。达到上限后 Runtime 在下一轮子 run 的上下文里注入「必须出稿」,若子 Agent 仍输出信封则拒绝该信封并要求改为 plan.submit_gdd。
当前实现边界:第四轮澄清委派在 agent.delegate 工具边界按持久化血缘上限被硬拒,并以普通 failed observation 返回 Supervisor;正常路径不进入 reconciliation,Supervisor 可据此改走 plan.submit_gdd。planning_coordinator 的超三轮 reconciliation 分支仅用于损坏或篡改血缘的纵深防御;本包不把它扩展成自动 submit 状态机。
Provider batch 规则。 exact planning 子 Agent 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不走现役「少于两项则 NotNeeded」的优化,plan.submit_gdd 因此是唯一 v4 member。Supervisor 侧的澄清 user.input_request 由 Runtime parent-wake 从 delivery 直接投影,不是 Supervisor Provider action、也不创建 v4 batch;该 pending 的 exactly-once 由 delivery/request/answer 指纹、pending identity 与 planning session revision 共同保证。exact planning 的 final-reply、context-compaction、final-reply-context-compaction 均无 action batch但仍写 v3 lifecycle/binding,planning idle compaction 不支持。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。
5.3 Fast GDD 固定内容
人类可读投影固定包含:决定状态汇总、游戏名称、游戏分类、美术风格、一句话描述、2~4 条游戏支柱、核心循环、目标用户、Runtime 平台事实、3~6 个最小 MVP 系统、独立创作者提示和审批请求。
MVP 明确排除多人、商城、服务器、开放世界、赛季、复杂社交、完整剧情和全量内容,除非未来 schema 与产品范围另行升级。GDD 只描述一个首版完整可玩闭环,不调度下游 Agent,也不生成美术或代码。
6. Prompt 权威稿
(2026-08-13 按 D11 更正归属) 本节是策划子 Agent(agentId=project-planning)角色 brief 的语义基线,不是某套 composition 的稿子——D11 下不新增 composition,策划子 Agent 复用现役 runtime composition,角色专属内容由 agentCatalog 条目的 brief 承载(见第 3.1、4.2 节)。Supervisor 侧不需要专用 persona 稿,其差异只体现在任务描述与工具面上。允许因 Prompt Bundle 结构拆成多个 section,但不得改变边界、轮次、选项、状态、平台事实或工具面。
你是“立项策划 Agent”。用户通常只给一句游戏需求或简短玩法。你的职责是用短而尖锐的游戏设计对话,帮助用户确认最少量、最关键的决定,并提交一份可审批的 MVP Fast GDD。
【任务边界】
- 你只负责玩法澄清、原型验证建议和最小 GDD。
- 你不得委派或调度其他 Agent,不得写文件、生成图片、执行命令、启动预览或构建。
- 你**不能直接向用户提问**:你没有 user.input_request,即使看得见,调用也会被 Runtime 拒绝(委派子 Agent 带 parent 身份)。要提问就以 AGC_NEEDS_USER_INPUT_V1 终态信封结束本轮 run,由 Supervisor 代为向用户提问。
- 只定义一个完整可玩闭环;MVP 不含多人、商城、服务器、开放世界、赛季、复杂社交、完整剧情或全量内容。
【平台事实:已确认,不可更改,不得向用户提问】
- Runtime 会注入精确 platformFacts:自包含 Web、desktop/mobile 双视口、keyboard/touch 双输入、本地 HTTP 预览。
- plan.submit_gdd 的 Provider input 不含 platformFacts;由 Runtime 写入最终 GDD。你不得另加、修改或向用户询问这些字段。
【目标】
1. 用户最多回答 3 个关键问题后,提交一份最小闭环 GDD。
2. 每一轮是一个**新 run、同 session**:你看得到自己前几轮说过的话,但**看不到用户的答案原文**——答案物理落在 Supervisor 的会话里,你只能从本次委派的任务文本读到它的转述。任务文本与你的记忆冲突时以任务文本为准;任务文本里没有的已回答信息,按仍然空白处理,不要凭空假设。
3. 只有用户在 GDD 审批卡上批准后,Runtime 才会产生有效 approve receipt;你不能自行宣称批准,也不执行下游动作。
【快速追问规则】
- 除非用户说“直接出稿”,否则先澄清。最多 3 个主动问题,每轮只有 1 个主要决定。
- 优先顺序:核心行为与本局目标 → 重玩动力 → 制作边界与 MVP。
- 每轮以 AGC_NEEDS_USER_INPUT_V1 终态信封输出一张决策卡后停止,不要再调任何工具。header 固定为“第N轮·关键决定”;正文以“当前要决定:…”开头,只问未被平台事实或 MVP 规则排除的真实取舍,并说明为什么现在问;三个选项固定为 A(推荐方案)、B(真实平行备选)和逐字固定的“需要原型验证”,每个 description 写清后果与代价,第三项给出可执行的微型原型验证方式。自由填写按用户明确输入处理。
- 信封只放规范化后的问题本身,不预填用户尚未给出的决定解释。你对上一轮回答的设计解释,写在**下一轮 run 的第一个普通回合**里,与「下一题还是出稿」的判断一并给出。
- 用户说“直接出稿”、第 3 轮已经完成、剩余问题不影响首个可玩闭环,或 Runtime 提示接近 240 秒 Agent 活跃预算时,立即整理并提交。
【低幻觉规则】
- A、B 或自由填写得到的用户决定标 confirmed;只有未提问、由子 Agent 按默认建议填写的字段标 default_pending(answerSource=default、round=0)。
- 需要靠手感、节奏、镜头、可读性或重玩行为证明的内容标 prototype_pending,并给出 30~90 分钟微型原型、观察信号和通过标准。
- Runtime 注入的 initial-request 和既有 decisionsSummary 是来源事实;提交 GDD 时必须逐项保留,不能把默认建议改成 confirmed 或伪造回答来源/轮次。
- 不得编造具体游戏的机制、数值、销量、团队规模、研究来源或用户已经确认的内容。
- 默认建议只用于缩短对话,不能覆盖用户明确输入。
【默认建议】
- 缺局长偏好时建议 10~20 分钟一局。
- 缺美术方向时建议风格化、轮廓清楚、资产可复用,原型先控制素材范围。
- 缺成长时建议 1 条成长线和 2~3 个选择;缺探索时建议 1 条主路线加 1 个有意义的岔路;缺构建时建议高风险输出与稳健防御两种方向。
- 所有默认建议都标 default_pending。
【GDD 固定结构】
- 决定状态;游戏名称;游戏分类(1 主类型 + 最多 1 融合类型);美术风格(视觉类型、3~5 个关键词、色彩氛围、MVP 美术边界);一句话描述;2~4 条游戏支柱;核心循环;目标用户;Runtime 平台事实;3~6 个最小 MVP 系统;先做/暂缓/验证/扩展条件;审批请求。
- 游戏支柱候选可以参考可感知体验、角色成长、探索、构建变体,但必须按项目生成,不能固定套用。
- 字段校验由 Runtime 执行;错误时按工具返回逐项修正,不得绕过。
【用户可读性】
- 面向独立游戏创作者,用“玩家按什么、看见什么、得到什么、下一步做什么”描述。
- 不写“提升体验”“增强沉浸感”“丰富内容”等不可执行结论。
【输出模式】
- 澄清模式:只提交当前决策卡,不输出完整 GDD。
- 成稿模式:调用 plan.submit_gdd 后结束当前策划子 Run;审批由 Supervisor 承接,不展示内部思考过程。
- 收到 revise/reject observation 后,在同一 gddId 下修订并提交下一版本;收到 approve 后只做一句收尾确认。
Runtime 还必须提供三类结构化注入,而不是让 Prompt 猜测:当前轮次/活跃毫秒提示、恢复摘要、审批 observation。注入只含项目相对引用和有界摘要,不含绝对路径、Provider 元数据或内部密钥。
7. 数据权威、路径与写者
| 路径或记录 | 语义 | 写者 | 原地覆盖 |
|---|---|---|---|
.agent/planning/gdd.v{N}.json |
已提交 GDD 的不可变事实 | plan.submit_gdd Runtime handler |
禁止;create-only |
.agent/planning/approvals/v{N}.json |
该版本唯一用户决定;approve 是构建信任根 | 审批 command | 禁止;create-only |
.agent/planning/index.json |
不可变事实的连续索引与状态缓存 | Runtime projection/recovery | 允许原子重建 |
.agent/planning/session.json |
待答问题、未提交已解释决定与恢复 checkpoint | plan Runtime | 允许带 revision/hash 链的原子 CAS |
game/fast_gdd.md |
当前有效批准 GDD 或最新候选的人类可读投影 | Runtime renderer | 允许原子重渲染 |
.agent/planning/pending.json |
gdd-approval 交互与原 run observation 锚点;可重建投影 |
plan Runtime pending ledger | 允许原子重建,消费后清理 |
agent.db / runtime event / task state |
审计与 UI/Runner 投影 | Runtime | 按稳定身份幂等修复 |
权威顺序固定为:不可变 GDD + 不可变 approval receipt 高于 session 中的已提交摘要,高于 index、Markdown、pending、审计、event 与 observation。session 是尚未提交的模型解释决定的唯一权威 checkpoint;现有 user-input response sidecar只证明原始回答已收到,不能自行重建 Agent 对回答的设计解释。
每个 schema 都绑定 projectId 与创建它的 durable plan identity。只允许安全枚举名称精确匹配 gdd.v([1-9][0-9]{0,2}).json 与 approvals/v([1-9][0-9]{0,2}).json 的普通文件;严格解析、项目身份和指纹全部通过后,它们本身就是事实。禁止从临时文件、Markdown、任意孤儿文件名、审计摘要或 Agent 自述推测事实。
game/fast_gdd.md 是 planning 内部投影,即使位于 game/** 也不推进 project mutation revision、不产生专业 Agent mutation ownership、不触发 verification gate。它必须走专用 renderer 和项目锁,不能伪装成通用 file.write。因此 plan source 不需要也不得获得 game.static_smoke。
7.1 Markdown 选版
- 存在有效 approved 版本时,Markdown 始终投影当前最高有效 approved 版本。
- 尚无 approved 版本时,Markdown 投影最新一份严格有效的 submitted GDD;
ready_for_approval、revision_requested、rejected都保留该版本供用户追溯,并在头部逐字写明由 GDD/receipt 推导出的当前状态,不能因 receipt 已落而留下未决状态文案。 - 新候选的审批卡直接读取对应不可变 JSON,不要求覆盖仍有效的 approved Markdown。
- Markdown 缺失或头部
gddId/version/fingerprint不匹配时只从权威 JSON 重渲染,绝不反向解析 Markdown 修复 JSON。
8. 严格 schema 与规范化
8.1 共同规则
- Rust 持久结构和 Tauri command input 都使用
#[serde(rename_all = "camelCase", deny_unknown_fields)];unknown schema、unknown enum 和 unknown field 失败关闭。 - 所有字段都显式序列化;
null和空数组不能通过skip_serializing_if消失。 - 用户文本先把 CRLF/CR 变为 LF,再用 Rust
str::trim去掉首尾 Unicode whitespace;不做 Unicode NFC/NFKC 折叠,不改变内部空白。 - 拒绝 NUL、DEL,以及除 LF/TAB 外的 C0 控制字符。长度按 Unicode scalar count;另执行 serialized UTF-8 byte 上限。不得静默截断。
- 一般 opaque Runtime ID 满足
[A-Za-z0-9][A-Za-z0-9._:-]{0,127};现役 actionId 另固定为action-[0-9a-f]{24}。gddId为gdd-加小写 RFC 4122 UUID;approvalRequestId为gdd-approval-加小写 UUID;approval command/receipt 的 responseId 为gdd-response-加小写 UUID。user-input answerResponseId 是明确例外:继续按现役 trim 后 1~160 scalar、无控制字符合同读取,UI 继续生成app-user-input-*,不套用 GDD 审批前缀或一般 opaque ID 的 128 字符上限。 - 时间统一为 UTC、固定毫秒精度
YYYY-MM-DDTHH:mm:ss.SSSZ,由 Runtime 生成;输入时间不接受时区偏移或更高精度。 - planning typed fingerprint(GDD、decision、receipt、session、pending、comment、Provider session binding)完整匹配
sha256-serde-json-v2:[0-9a-f]{64}。现役actionFingerprint与runProfileBindingFingerprint保持已有[0-9a-f]{64}裸 digest,M1 不做全局格式迁移;两类字段不得互相比较。 - submit input 与 GDD 最大 64 KiB,Provider session binding 8 KiB,Provider structured injections 64 KiB,session 64 KiB,approval/pending 各 16 KiB,index 256 KiB,Markdown 128 KiB,hydrate view 512 KiB;限制按最终 UTF-8 bytes 计算。(随 D10 作废的 plan decision checkpoint 不再分配容量。)
- 一个 lineage 最多 128 个版本;版本是
u32且只能为1..=128。到达上限返回PLAN_VERSION_LIMIT_REACHED,不能绕回、删除或另建 lineage。 - v1 所有
basis必须为null;非空值返回PLAN_UNSUPPORTED_KNOWLEDGE_BASIS。
8.2 plan-submit-gdd-input.v1
Provider 只能提交设计内容,不能提交或覆盖任何 Runtime 身份、版本、时间、平台事实、知识依据或指纹。plan.submit_gdd 的完整 strict input 固定为:
{
"schemaVersion": "plan-submit-gdd-input.v1",
"game": {
"title": "...",
"genre": { "primary": "...", "fusion": null },
"artStyle": {
"visualType": "...",
"keywords": ["...", "...", "..."],
"moodAndColor": "...",
"mvpArtBoundary": "...",
},
"oneLiner": "...",
"pillars": [
{
"name": "...",
"playerFeel": "...",
"mechanism": "...",
"decisionState": "confirmed",
},
],
"coreLoop": ["..."],
"targetUsers": {
"coreUsers": "...",
"preferences": "...",
"sessionLength": "...",
"referenceGames": [],
},
"mvpSystems": [
{
"system": "...",
"minimalFunction": "...",
"whyRequired": "...",
"verifyMethod": "...",
"decisionState": "confirmed",
},
],
"outOfScope": ["..."],
"creatorTips": {
"doFirst": "...",
"deferForNow": "...",
"howToVerify": "...",
"expandWhen": "...",
},
},
"decisions": [
{
"id": "initial-request",
"topic": "初始需求",
"state": "confirmed",
"answerSource": "user_freeform",
"round": 0,
"answerSummary": "用户的规范化初始需求",
},
{
"id": "d1",
"topic": "...",
"state": "prototype_pending",
"answerSource": "user_option",
"round": 1,
"answerSummary": "...",
},
],
"prototypeValidationItems": [
{
"id": "d1",
"question": "...",
"microPrototype": "...",
"observation": "...",
"passCriterion": "...",
},
],
}
该 input 及所有嵌套类型都使用 deny_unknown_fields;game.platformFacts、任意 basis、projectId/gddId/version/submissionId/approvalRequestId、action/run/session identity、时间与任何 fingerprint 一旦出现在 Provider input 中即返回 PLAN_INVALID_REQUEST。Runtime 在发出本轮 Provider request 前把当前 sessionRevision/sessionFingerprint 绑定进内部执行上下文,在项目锁内验证该 CAS 后,才把 project、GDD、版本、durable action、source/profile、session/run、时间、固定 platformFacts、全部 basis:null 与 fingerprint 注入 plan-gdd.v1。字段数量和文本限制按第 8.3 节对应 durable 字段执行。
input 是当前 GDD 的完整快照,不要求与 source session 的 decisionsSummary 和 prototypeValidationItems 逐项相等。审批修订可用 user_revision + round=0 修改、删除或新增决定;未涉及内容由 Agent 以当前 GDD 为基线保持不变。Runtime 仍校验决定结构、原型项双射、initial-request 首项、身份和 CAS。payload 出现 answerSource=user_revision 时,当前 session 的 lastDecisionRef.action 必须是 revise 或 reject;首次提交、澄清续跑和没有待处理用户修订的普通质量返工返回 PLAN_INVALID_REQUEST。用户修订周期内的质量返工可以继续携带 user_revision,但其 planning child 必须直接继承当前 session 的 latestDelegationId,不得从旧 delivery 另起分支。该闸只作用于新版本 create,同 submissionId replay 不重判。唯一固定的 confirmed + user_freeform + round=0 是 Runtime 创建的 initial-request。
8.3 plan-gdd.v1
2026-08-13 按 D11 更正 identity 块。 原文的 identity 只记录了「唯一那个 plan run」,因为 D6/D9 下提交者就是 root
project-supervisor本人。D11 下提交者是委派子 Agent,故必须显式记录agentId,并补上把这份 GDD 锚回具体委派链的rootAgentId/rootRunId/delegationId。source相应由project-supervisor-plan-chat(作废字面量)改为提交 run 的真实 sourceagent-delegate。字段声明顺序即下方顺序,是第 9 节 typed 指纹的受保护字节。
顶层字段顺序和覆盖范围固定为:
{
"schemaVersion": "plan-gdd.v1",
"projectId": "<opaque project id>",
"gddId": "gdd-<uuid>",
"version": 1,
"submissionId": "<原 plan.submit_gdd durable actionId>",
"approvalRequestId": "gdd-approval-<uuid>",
"actionFingerprint": "<64 位小写 hex>",
"agentId": "project-planning",
"source": "agent-delegate",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"rootAgentId": "project-supervisor",
"rootRunId": "<Supervisor 根 run id>",
"delegationId": "<产生本次提交的委派 id>",
"sessionId": "<策划子 Agent 的持久 session id>",
"sourceSessionRevision": 3,
"sourceSessionFingerprint": "sha256-serde-json-v2:<hex>",
"createdByRunId": "<提交所在的策划子 run id>",
"createdAtUtc": "2026-08-10T00:00:00.000Z",
"game": {
"title": "...",
"genre": { "primary": "...", "fusion": null },
"artStyle": {
"visualType": "...",
"keywords": ["...", "...", "..."],
"moodAndColor": "...",
"mvpArtBoundary": "...",
},
"oneLiner": "...",
"pillars": [
{
"name": "...",
"playerFeel": "...",
"mechanism": "...",
"decisionState": "confirmed",
"basis": null,
},
],
"coreLoop": ["..."],
"targetUsers": {
"coreUsers": "...",
"preferences": "...",
"sessionLength": "...",
"referenceGames": [],
},
"platformFacts": {
"runtime": "self-contained-web",
"viewports": ["desktop", "mobile"],
"inputs": ["keyboard", "touch"],
"preview": "local-http",
},
"mvpSystems": [
{
"system": "...",
"minimalFunction": "...",
"whyRequired": "...",
"verifyMethod": "...",
"decisionState": "confirmed",
"basis": null,
},
],
"outOfScope": ["..."],
"creatorTips": {
"doFirst": "...",
"deferForNow": "...",
"howToVerify": "...",
"expandWhen": "...",
},
},
"decisions": [
{
"id": "initial-request",
"topic": "初始需求",
"state": "confirmed",
"answerSource": "user_freeform",
"round": 0,
"answerSummary": "用户的规范化初始需求",
"basis": null,
},
{
"id": "d1",
"topic": "...",
"state": "prototype_pending",
"answerSource": "user_option",
"round": 1,
"answerSummary": "...",
"basis": null,
},
],
"prototypeValidationItems": [
{
"id": "d1",
"question": "...",
"microPrototype": "...",
"observation": "...",
"passCriterion": "...",
},
],
"fingerprint": "sha256-serde-json-v2:<hex>",
}
字段限制:
| 字段 | 限制 |
|---|---|
| title | 1~80 scalar |
| genre.primary / fusion | primary 1~40;fusion 为 null 或 1~40 |
| artStyle | visualType 1~80;keywords 3~5 个且去重,每项 1~32;其余各 1~400 |
| oneLiner | Runtime 实际接受 10~160 scalar;模型 schema 提示 25~90 scalar(有意不对称,不是缺陷或 bug) |
| pillars | 2~4 条;name 1~40,其余文本各 1~240;name 唯一 |
| coreLoop | 4~8 步,每步 1~120 |
| targetUsers | 三个主文本各 1~240;referenceGames 0~5 项,每项 1~80 |
| mvpSystems | 3~6 项;system 1~40 且唯一,其余文本各 1~240 |
| outOfScope | 1~12 项,每项 1~80,去重 |
| creatorTips | 四个字段各 1~400 |
| decisions | 1~32 条;id 匹配 [a-z][a-z0-9-]{0,31} 且唯一;topic 1~80;answerSummary 1~400;round 0~3。round 0 只允许初始用户需求或未提问默认决定 |
| prototypeValidationItems | 0~3 条;id 必须精确引用 decisions 中同 ID 的 prototype_pending 项且一一对应;question、microPrototype、observation、passCriterion 各 1~400 |
sourceSessionRevision 必须大于 0,sourceSessionFingerprint 必须是 planning typed fingerprint;两者精确等于产生本次 Provider request 时绑定的 session primary。首次 create 时项目锁内要求它仍是 current primary 并做 CAS;同 submission replay 时先复用既有 GDD 的历史 source binding,不再要求后来已推进的 current session 与旧 revision 相等,只要求现有 session/receipt 未与同一 gddId/sessionId lineage 冲突,且绝不能拿新 primary 覆盖历史 binding。fingerprint 不参与自身计算;其余字段全部参与,包括时间、身份、null、空数组和数组顺序。GDD 不持久化可推导 status。
8.4 plan-gdd-index.v1
{
"schemaVersion": "plan-gdd-index.v1",
"projectId": "...",
"gddId": "gdd-...",
"entries": [
{
"version": 1,
"submissionId": "...",
"approvalRequestId": "gdd-approval-...",
"actionFingerprint": "<64 位小写 hex>",
"fingerprint": "...",
"file": "gdd.v1.json",
"agentId": "project-planning",
"source": "agent-delegate",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"rootRunId": "...",
"delegationId": "...",
"sessionId": "...",
"sourceSessionRevision": 3,
"sourceSessionFingerprint": "sha256-serde-json-v2:<hex>",
"createdByRunId": "...",
"createdAtUtc": "...",
"submittedAtUtc": "...",
},
],
"statusCache": {
"latestVersion": 1,
"pendingVersion": 1,
"approvedVersion": null,
"versions": [{ "version": 1, "status": "ready_for_approval" }],
},
"rebuiltAtUtc": "...",
}
entries 必须从 1 连续递增,文件名与 version 精确一致,且逐项等于对应 GDD 权威字段。v1 的 submittedAtUtc 固定等于 GDD createdAtUtc,因此可以确定性重建。pendingVersion 为 null 或唯一未决定版本;approvedVersion 为 null 或当前最高有效 approve 版本;versions 与 entries 等长、同序。statusCache 和 rebuiltAtUtc 全部可重建,不参与信任判断。index 损坏或缺失时直接从严格验证后的 GDD/receipt 重建,不以 .previous 为真相源。
8.5 plan-gdd-approval.v1
{
"schemaVersion": "plan-gdd-approval.v1",
"projectId": "...",
"gddId": "gdd-...",
"version": 1,
"fingerprint": "sha256-serde-json-v2:<GDD hex>",
"pendingActionId": "<原 plan.submit_gdd actionId>",
"actionFingerprint": "<原 64 位小写 hex action fingerprint>",
"approvalRequestId": "gdd-approval-<uuid>",
"responseId": "gdd-response-<uuid>",
"decisionFingerprint": "sha256-serde-json-v2:<hex>",
"source": "project-supervisor-plan",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"sessionId": "...",
"runId": "...",
"action": "approve",
"comment": null,
"decidedAtUtc": "2026-08-10T00:00:00.000Z",
"receiptFingerprint": "sha256-serde-json-v2:<hex>",
}
每个版本最多一个 receipt。approve 的 comment 可以为 null 或 1~1000 scalar;空白规范化后写为 null。revise/reject 的 comment 必须为 1~1000 scalar。decisionFingerprint 只用于同一用户意图的幂等判断;receiptFingerprint 覆盖 receipt 除自身外的全部字段,是读取信任根时的完整性校验。两者不能互相替代。
8.6 plan-session.v1
2026-08-13 按 D11 整节改写。 删除三处 D10 遗留:
activeQuestion(问题现在落在 delivery 上,delivery 就是「已问出、未回答」的权威载体)、roundsUsed作为轮次权威(轮次改由委派链推断的clarification_round决定)、appliedAnswers里全部checkpoint*/supersededCheckpointHandoffs字段(plan-decision-checkpoint请求 kind 随 D10 作废)。同时补上 D11 需要的链路锚点rootAgentId/rootRunId/latestDelegationId。
{
"schemaVersion": "plan-session.v1",
"projectId": "...",
"gddId": "gdd-...",
"sessionRevision": 1,
"previousFingerprint": null,
"sessionFingerprint": "sha256-serde-json-v2:<hex>",
"agentId": "project-planning",
"source": "agent-delegate",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"rootAgentId": "project-supervisor",
"rootRunId": "...",
"latestDelegationId": "...",
"sessionId": "...",
"activeRunId": "...",
"lastRunId": "...",
"phase": "collecting",
"accumulatedAgentMillis": 0,
"appliedSteerCursor": 0,
"decisionsSummary": [
{
"id": "initial-request",
"topic": "初始需求",
"state": "confirmed",
"answerSource": "user_freeform",
"round": 0,
"answerSummary": "用户的规范化初始需求",
},
],
"prototypeValidationItems": [],
"appliedAnswers": [],
"latestSubmittedRef": null,
"lastDecisionRef": null,
"updatedAtUtc": "...",
}
phase 取 collecting | awaiting_user_input | awaiting_gdd_approval | revision_requested | approved | rejected | recovery_required。activeRunId 为 opaque ID 或 null,只有非终态策划子 run 时可非空;lastRunId 始终记录最近一次策划子 run。accumulatedAgentMillis 为 u64,只单调增加,两层的 Provider 活跃区间都计入。appliedSteerCursor 为现役 durable steer cursor,初始 0、只单调增加。
轮次不是 session 的字段(D11)。 本轮是第几轮由委派链上的 clarification_round 派生值决定(沿 repair_of_delegation_id 上溯推断,语义见第 23.5 节),上限 3;返工深度 repair_depth 同理,上限 1。session 不得保存 roundsUsed 之类的计数字段——那会形成与链路推断并行的第二个真相,且对历史记录 fail-open。需要展示轮次时从链路现算,decisionsSummary[].round 只是每条决定被回答时所处轮次的只读留痕,不参与判定。
activeQuestion 已删除(D11)。 「已问出、未回答」这个状态的权威载体是 delivery 本身:问题原文与 questionsSha256 落在 contract_status=NeedsUserInput 的 delivery 上,phase=awaiting_user_input 时可由 latestDelegationId 定位到它。session 不再复制一份问题,因此也不存在「session 有 activeQuestion 但 delivery 没有」这类需要对账的分叉态。
decisionsSummary 为 1~32 项,元素与 GDD decision 去掉 basis 后同形并保持 ID 唯一;首项永远是 Runtime 创建的 initial-request。prototypeValidationItems 为 0~3 项,元素与 GDD 同形,必须与 decisionsSummary 中全部且仅有的 prototype_pending 回答决定按 ID 一一对应;提交 GDD 时必须逐项保留,不能让下一次 Provider 重新生成。
appliedAnswers 为 0~3 个按 round 递增的 strict 对象,2026-08-13 按 D11 收窄为:{delegationId, continuationDelegationId, requestId, questionId, responseId, questionsSha256, answersSha256, decisionId, round}。
delegationId是产生该问题的那条NeedsUserInputdelivery;continuationDelegationId是用该答案确定性派生出的 continuation,二者构成「问—答—续」的完整链路证据,取代原先由checkpointProviderRequestId/questionSessionRevision承担的溯源职责。continuationDelegationId必须精确等于 Runtime 从(parentRunId, delegationId, questionsSha256, answersSha256)派生的值。这条等式就是幂等性的全部来源:同一组问答只能派生同一个 continuation,重放不会产生第二轮。appliedAnswers.responseId明确是现役 user-input 的 answerResponseId,不是gdd-response-*审批 ID;answersSha256精确复用现役 user-input sidecar 对规范化 answers map 的裸 64-hex SHA-256,只绑定 transport,不冒充 planning typed fingerprint。- 每项 decisionId 必须唯一引用同 round 的 decisionsSummary;
(requestId, responseId)组合唯一。answerResponseId 只在所属 requestId 域内幂等,不同问题允许合法复用同一文本值,读取或 CAS 不能单独按 responseId 判冲突。 appliedAnswers.length必须等于链路现算的clarification_round;不一致进入PLAN_NEEDS_RECONCILIATION,不得以 session 为准回写链路。
原 supersededCheckpointHandoffs 字段随 D10 一并删除:它服务的是「同一 session 内多次 checkpoint 协议修复」这个场景,而 D11 下每轮都是独立的新 run、上一轮 run 已终态,不存在需要在 session 里维护 superseded 传递闭包的情形。全仓库对该字段零代码引用,删除无迁移成本。
latestSubmittedRef 为 null 或 {gddId, version, fingerprint};lastDecisionRef 为 null 或 {version, responseId, action, receiptFingerprint},其中 lastDecisionRef.responseId 明确是 approval receipt 的 approvalResponseId。
phase 不变量:awaiting_gdd_approval 必须有 latestSubmittedRef 且对应版本无 receipt;revision_requested、approved、rejected 必须有相符 lastDecisionRef;approved 的 lastDecisionRef.action 必须是 approve;recovery_required 禁止 Provider continuation 和任何新 submit/decision mutation。开始新的 continuation 时,若已有 valid session,则以新 activeRunId 写 revision+1 successor;只有 session 不存在、没有 active run 且全部 durable GDD/receipt 已验证时,才允许从事实创建 revision 1 的最小恢复 session。
sessionRevision 初始为 1,每次成功推进必须在项目锁内以磁盘 revision/fingerprint 做 CAS 后加一;不得跳号。sessionFingerprint 覆盖除自身外所有字段,包含 previousFingerprint。合法 successor 必须满足 new.revision=old.revision+1 且 new.previousFingerprint=old.sessionFingerprint。
(2026-08-13 按 D11 改写)本轮的线性化点不在 session。 D10 下一轮是否算数由「新 session primary durable」界定,因为解释来自同一 run 内的专用 checkpoint 请求。D11 下轮次的线性化点是答案绑回 delivery(answersSha256 原子写入,首次写入或逐字相同则幂等,绑到不同请求或不同答案则拒绝)——这条由 PR #165 已发布代码保证,页面重载、Runner 重启或重放都不会把一条回答计成两轮。session 的 CAS 退化为派生投影的收口:它记录已应用的答案与决定摘要,但不再是「这一轮成立了没有」的判据。
因此恢复顺序也变了:先看 delivery 的绑定状态,再补 session。若 delivery 已绑定答案而 session 的 appliedAnswers 落后,只能补齐逐项相同的派生投影,不能再次请求模型或重新计轮次;若 session 声称的 appliedAnswers 多于链路现算的 clarification_round,是 session 侧污染,进入 PLAN_NEEDS_RECONCILIATION 而不是反向修改链路。
session 仍是未提交设计解释的权威:有活跃策划子 run 时 primary 缺失、损坏或链分叉必须进入 PLAN_SESSION_RECOVERY_REQUIRED,不能根据 GDD、聊天摘要或原始回答静默重建解释。但它不再是轮次状态机的权威,也不再需要为「answer-prepared 但 checkpoint 未落」这个 D10 专有的中间态设计恢复路径——该中间态在 D11 下不存在。
9. typed serde 指纹合同
M1 必须新增单一共享 helper;仓库当前没有可以直接满足本合同的通用实现,不能把现有裸 SHA-256 hex 或 map/string 拼接 helper 冒充本合同。
逻辑签名固定为:
typed_serde_fingerprint<T: Serialize>(domain: &'static str, value: &T)
-> Result<String, FingerprintError>
算法:
- 先按第 8.1 节完成文本规范化和 strict struct 反序列化;禁止对原始 JSON 文本直接哈希。
- 各 canonical payload 必须是字段声明顺序冻结的 Rust struct。payload 内禁止
HashMap、BTreeMap、flatten 和自定义跳字段;数组使用Vec,保持业务顺序,不排序。 - 构造字段顺序固定为
domain、value的FingerprintEnvelope { domain, value }。 - 使用
serde_json::to_vec(&envelope);不 pretty print、不追加换行、不转义非 ASCII Unicode,不省略null或空数组。 - 对所得 UTF-8 bytes 计算 SHA-256,输出
sha256-serde-json-v2:加 64 位小写 hex。
domain 固定为:
| 对象 | domain | 排除字段 |
|---|---|---|
| GDD | genarrative.plan.gdd.v1 |
外层 fingerprint |
genarrative.plan.decision-checkpoint.v1 |
2026-08-13 随 D10 删除:plan-decision-checkpoint 请求 kind 不复存在,该 domain 不再分配 |
|
| 用户决定 intent | genarrative.plan.gdd-decision.v1 |
无;本身不含服务器时间或 receipt 字段 |
| approval receipt | genarrative.plan.gdd-approval-receipt.v1 |
receiptFingerprint |
| session | genarrative.plan.session.v1 |
sessionFingerprint |
| approval pending | genarrative.plan.gdd-approval-pending.v1 |
pendingFingerprint |
| decision comment | genarrative.plan.gdd-comment.v1 |
无 |
| Provider session binding | genarrative.plan.provider-session-binding.v1 |
末尾 fingerprint |
| Provider request context | genarrative.plan.provider-request-context.v1 |
无 |
(2026-08-13 删除:原此处规定「回答解释 checkpoint 的字段声明顺序固定为第 5.2 节 decisionCheckpoint 内显示顺序」。该 payload 随 D10 的 plan-decision-checkpoint 请求 kind 一并作废,其 domain 也已从上表移除。D11 下这一轮的设计解释由 continuation 子 Agent 的第一个普通 tool-plan turn 产生,走现役 tool_plan_handoff 通道,不需要专用 typed 指纹。)
用户决定 intent 的字段声明顺序固定为:projectId, gddId, version, fingerprint, pendingActionId, actionFingerprint, approvalRequestId, responseId, source, runProfile, runProfileBindingFingerprint, sessionId, runId, action, normalizedComment。它只回答“同一 responseId 是否为同一决定”,不证明服务器落盘 receipt 的完整性。
receipt fingerprint payload 的字段声明顺序固定为第 8.5 节除 receiptFingerprint 外的显示顺序。它包含 Runtime 生成的 decidedAtUtc,读取、恢复和构建准入时必须重算。
plan-provider-session-binding.v1 的 typed fingerprint value 字段声明顺序固定为 schemaVersion, projectId, gddId, agentId, taskId, providerRequestId, sessionId, runId, rootAgentId, rootRunId, delegationId, goalId, goalRevision, goalSnapshotFingerprint, source, runProfile, runProfileBindingFingerprint, sessionRevision, sessionFingerprint, appliedSteerCursor, requestKind, requestSlot, webSearchEnabled, requestContextFingerprint。durable JSON 在这些字段之后还必须以 fingerprint 收尾;该字段排除在自身 typed value 之外,但读取时必须按上述 domain 重算并逐字相等。rootAgentId 与末尾 required fingerprint 是 M1B-2 实现对委派根身份和 binding 自完整性的有意加固,属于 v1 当前 exact 合同,不得再称为“额外字段”或在恢复时补默认值;缺失、重排或夹带其它字段都失败关闭。
exact plan 的 base Provider request ID 不复用现役换行拼接算法,也不改变非 plan request identity。它对 domain genarrative.plan.provider-request-id.v1 的 canonical envelope bytes 直接取 SHA-256,输出仍保持 provider-request-<64 位小写 hex>。value 字段声明顺序固定为 projectId, gddId, agentId, taskId, sessionId, runId, rootAgentId, rootRunId, delegationId, source, runProfile, runProfileBindingFingerprint, goalId, goalRevision, goalSnapshotFingerprint, sessionRevision, sessionFingerprint, appliedSteerCursor, requestKind, requestSlot, webSearchEnabled, requestContextFingerprint;所有字段来自同一 captured context/lifecycle binding。(2026-08-14 按当前实现收口:在 D11 已补入的 rootRunId / delegationId 前进一步补入 rootAgentId,避免只凭 run 字符串猜根 Agent;随 D10 作废的 supersededCheckpointProviderRequestIds 继续不存在。)attempt 0 等于 base ID;attempt N>0 对 domain genarrative.plan.provider-request-attempt.v1 与 strict value {baseProviderRequestId, attempt} 的 canonical envelope bytes 取 SHA-256,并保持相同外形。合法 session/context 前滚会改变 base ID 并从 attempt 0 开始;只有同一 session/context 的 transient retry 或物理 interrupted retry 可增加 attempt。
9.1 GDD golden vector
2026-08-13 按 D11 重新生成。 identity 块按第 8.3 节新的字段声明顺序重建(新增
agentId/rootAgentId/rootRunId/delegationId,source由作废的project-supervisor-plan-chat改为agent-delegate,createdByRunId改为策划子 run),game/decisions/prototypeValidationItems三段逐字节未动。生成方式:对新的 canonical bytes 直接取 SHA-256,并以「重算旧向量得到旧指纹」验证过方法本身——旧的 3707 bytes 重算确实得到旧值
d85c85dae3…,说明本节的 canonical bytes 就是被哈希的原文,不存在额外的序列化中间层。指纹是算出来的,不是手写的。
以下一行是完整 FingerprintEnvelope<PlanGddFingerprintV1> canonical value;外层存储字段 fingerprint 不在 value 中。数组顺序、中文、null、空数组、Runtime 注入的 session binding、initial request 和 prototype pass criterion 均为受保护字节。精确 UTF-8 bytes 无 BOM、无结尾换行,共 3857 bytes:
{
"domain": "genarrative.plan.gdd.v1",
"value": {
"schemaVersion": "plan-gdd.v1",
"projectId": "project-golden-001",
"gddId": "gdd-00000000-0000-4000-8000-000000000001",
"version": 1,
"submissionId": "action-0123456789abcdef01234567",
"approvalRequestId": "gdd-approval-00000000-0000-4000-8000-000000000002",
"actionFingerprint": "1111111111111111111111111111111111111111111111111111111111111111",
"agentId": "project-planning",
"source": "agent-delegate",
"runProfile": "standard",
"runProfileBindingFingerprint": "2222222222222222222222222222222222222222222222222222222222222222",
"rootAgentId": "project-supervisor",
"rootRunId": "run-golden-root-001",
"delegationId": "clarification-continuation-4444444444444444",
"sessionId": "session-golden-001",
"sourceSessionRevision": 3,
"sourceSessionFingerprint": "sha256-serde-json-v2:3333333333333333333333333333333333333333333333333333333333333333",
"createdByRunId": "run-golden-plan-001",
"createdAtUtc": "2026-08-10T00:00:00.000Z",
"game": {
"title": "萤火守夜人",
"genre": { "primary": "轻量动作解谜", "fusion": null },
"artStyle": {
"visualType": "低多边形剪影",
"keywords": ["萤火", "深蓝", "暖金"],
"moodAndColor": "深蓝夜色配暖金反馈",
"mvpArtBoundary": "仅玩家、灯塔、三类障碍与HUD"
},
"oneLiner": "玩家扮演守夜人,在会熄灭的群岛间收集萤火、点亮灯塔并规划安全返回路线,每局用有限光源换取更远探索。",
"pillars": [
{
"name": "光源抉择",
"playerFeel": "每一步都在安全与收益间权衡",
"mechanism": "光量同时承担生命、视野与开门消耗",
"decisionState": "confirmed",
"basis": null
},
{
"name": "短局探索",
"playerFeel": "十分钟内完成一次清晰冒险",
"mechanism": "岛屿分支和撤离时机形成重玩差异",
"decisionState": "prototype_pending",
"basis": null
}
],
"coreLoop": [
"观察剩余光量与岛屿分支",
"选择路线和光源投入",
"移动、收集并处理障碍",
"点亮灯塔或及时撤离"
],
"targetUsers": {
"coreUsers": "喜欢短局策略与轻量探索的玩家",
"preferences": "清晰反馈、低操作压力、可复盘选择",
"sessionLength": "10至15分钟",
"referenceGames": []
},
"platformFacts": {
"runtime": "self-contained-web",
"viewports": ["desktop", "mobile"],
"inputs": ["keyboard", "touch"],
"preview": "local-http"
},
"mvpSystems": [
{
"system": "光量资源",
"minimalFunction": "移动和交互消耗光量",
"whyRequired": "承载核心取舍",
"verifyMethod": "观察玩家是否因光量改变路线",
"decisionState": "confirmed",
"basis": null
},
{
"system": "分支岛屿",
"minimalFunction": "每局提供两次二选一路线",
"whyRequired": "形成重玩差异",
"verifyMethod": "记录第二局路线变化",
"decisionState": "prototype_pending",
"basis": null
},
{
"system": "灯塔结算",
"minimalFunction": "点亮终点或撤离时结算",
"whyRequired": "闭合本局目标",
"verifyMethod": "玩家能理解三类结算",
"decisionState": "default_pending",
"basis": null
}
],
"outOfScope": ["多人"],
"creatorTips": {
"doFirst": "先验证光量与路线取舍",
"deferForNow": "完整剧情和大量岛屿",
"howToVerify": "让三名玩家各试玩两局并说明路线理由",
"expandWhen": "多数玩家会主动改变第二局路线"
}
},
"decisions": [
{
"id": "initial-request",
"topic": "初始需求",
"state": "confirmed",
"answerSource": "user_freeform",
"round": 0,
"answerSummary": "做一款围绕有限光源探索群岛的短局动作解谜游戏",
"basis": null
},
{
"id": "route-replay",
"topic": "路线重玩",
"state": "prototype_pending",
"answerSource": "user_option",
"round": 1,
"answerSummary": "用微型原型验证分支是否驱动重玩",
"basis": null
}
],
"prototypeValidationItems": [
{
"id": "route-replay",
"question": "分支路线是否驱动第二局选择变化",
"microPrototype": "制作两次二选一路线和光量结算",
"observation": "记录第二局是否主动改变分支并说明原因",
"passCriterion": "三名测试者中至少两名主动改变路线且能说出取舍"
}
]
}
}
预期输出:
sha256-serde-json-v2:a59856de7ef134cf2f49c4dedd2ba10ae4ab2340a9634d402eb792b6ee5458f0
M1 测试必须证明:改动任一中文字符、把 fusion:null 省略、交换关键词/平台数组顺序或改变任一 durable identity 都会得到不同 typed fingerprint。给原始 JSON 追加换行后 strict parse + canonical reserialize 的 typed fingerprint本身不变,但该文件不满足第 10.1 节 compact/no-newline storage bytes,必须以 non-canonical authority 拒绝,不能混淆两个门禁。
10. 文件发布与崩溃一致性
10.1 不可变 JSON 的 create-only 发布
现有可覆盖 JSON sidecar writer 不适用于 GDD 和 approval receipt。两个不可变文件的磁盘 bytes 固定为对完整 strict persisted struct 调用 serde_json::to_vec 的结果:字段使用 Rust 声明顺序、compact JSON、UTF-8、无 BOM、无结尾换行;typed fingerprint envelope 只用于计算指纹,不包在磁盘对象外。replay 比较的是 strict struct、重算 fingerprint 与这组 canonical storage bytes,pretty JSON 或语义等价但字节不同的文件不能被 writer 当作自己已提交的 canonical 文件。
M1 必须新增跨平台 durable_create_json_no_replace,固定流程如下:
- 持有项目级 write lock,重新验证 canonical target、父目录、普通文件边界、project identity 和当前事实集合。
- 在 target 同目录以随机且有界名称
create_new创建私有 temp;拒绝 symlink/reparse point、目录、硬链接异常和路径逃逸。 - 写入完整 bytes,
sync_all,通过仍持有的句柄回读并做 strict parse、typed fingerprint 与文件身份复核。 - 使用 OS 原生 no-replace 原子发布:Windows
MoveFileExW不带 replace 且带 write-through;Linuxrenameat2(RENAME_NOREPLACE);macOSrenameatx_np(RENAME_EXCL)。平台不支持时只允许同文件系统 hard-link no-replace fallback,随后验证 temp/target file identity;无法证明时失败关闭。 - 发布成功后重新打开 target,验证普通文件、字节、typed identity 与 fingerprint;删除 temp,并同步父目录。
- target 已存在时不得覆盖:先读取既有 strict struct。GDD replay 复用既有文件中的
version/approvalRequestId/createdAtUtc与 Runtime 注入 identity,receipt replay 复用既有decidedAtUtc,再从本次规范化业务 input 重建 canonical candidate;相同 logical ID 且完整 strict value、storage bytes 与 fingerprint 相同才视为 replay,否则返回 identity conflict。临时文件在完成比较后清理并再次同步父目录。
成功返回的耐久语义是:在本地文件系统正确实现 sync_all、no-replace publish 与父目录同步的前提下,承诺进程崩溃、操作系统崩溃和断电后 target 要么不存在、要么是完整有效文件,不出现半个 authoritative primary。父目录无法同步时不能返回 committed;只允许留下可清理 temp,不得把未确认 target 当成提交。
不可变 GDD/receipt 不创建 .previous。tmp 永远不是事实,恢复只能在持锁并确认没有活跃 writer 后清理。
10.2 index 与 session
- index 是纯投影;使用原子 replace,但损坏、丢失或存在历史 backup 时一律从权威 GDD/receipt 重建,不依据 backup 猜新旧。
- session 使用原子 replace + 单份
.previous,但读取必须验证 revision/hash 链。primary 与 previous 相同,或 primary 是 previous 的精确revision+1successor 时 primary 胜出并清理 previous;primary 缺失且 previous 唯一有效时可在项目锁内提升并同步父目录;两者有效但不是同值/合法 successor 时 reconciliation;primary 存在但损坏时即使 previous 有效也失败关闭。 - 这套规则只识别合法 successor,不会把一次正常的 Windows 安装中断留下的“新 primary + 旧 previous”误判为冲突。
11. 版本与状态推导
flowchart LR
S["plan-session.v1 draft"] -->|"plan.submit_gdd"| G["gdd.vN.json<br/>create-only 提交点"]
G --> R["ready_for_approval"]
R -->|"receipt.action=revise"| RV["revision_requested"]
R -->|"receipt.action=reject"| RJ["rejected"]
R -->|"receipt.action=approve"| AP["approved"]
RV -->|"同一 gddId 提交 vN+1"| G2["gdd.vN+1.json"]
RJ -->|"同一 gddId 重新提交 vN+1"| G2
AP -->|"后续 vN+1 被批准"| SS["superseded"]
G2 --> R2["ready_for_approval"]
R2 -->|"approve receipt"| AP2["新 approved"]
draft只存在于 session,不创建 GDD 文件。- GDD create-only 成功且尚无 receipt 时为
ready_for_approval。 - receipt.action=
revise为revision_requested;reject为rejected。 - 有 approve receipt 的最高版本为
approved,更低的 approve 版本为superseded。 - revise/reject 新候选不撤销此前有效 approved;只有新版本 approve 才 supersede 旧批准。
- status 只从 GDD/receipt 推导,index.statusCache、session、Markdown 和 UI 徽章不得反向决定状态。
12. GDD 提交合同
2026-08-13 按 D11 更正。 提交者由「唯一那个 plan run」改为策划子 Agent(
agentId=project-planning,source=agent-delegate);binding 补rootRunId/delegationId;随 D10 作废的plan-decision-checkpointrequest kind 与supersededCheckpointProviderRequestIds数组一并移除。2026-08-14 再按 M1B-2 实现加固:binding 与 typed/base request identity 补rootAgentId,binding 末尾加入 required typedfingerprint。提交点、幂等域与 create-only 发布语义未变。
plan.submit_gdd 由策划子 Agent 调用,但只有 Runtime 写文件。它必须是一次 Provider 响应中的唯一 action tool;同一响应可以更新 update_agent_plan,但不得把 submit 与 file.read、file.list 或第二次 submit 混入同一 action batch。(user.input_request 不在策划子 Agent 的工具面内,故不存在与它混批的情形;Supervisor 侧则不持有 plan.submit_gdd。)Runtime 在 batch 建立 durable actionId 之前拒绝混批,避免 Runtime-owned commit 落在 generic multi-action cursor 中间。
每个 exact planning Provider request 都必须先冻结 durable source-session binding,不能只放在内存:
{
"schemaVersion": "plan-provider-session-binding.v1",
"projectId": "...",
"gddId": "gdd-...",
"agentId": "project-planning",
"taskId": "...",
"providerRequestId": "provider-request-<64 位小写 hex>",
"sessionId": "...",
"runId": "...",
"rootAgentId": "project-supervisor",
"rootRunId": "...",
"delegationId": "...",
"goalId": "...",
"goalRevision": 1,
"goalSnapshotFingerprint": "<现役 goal snapshot fingerprint>",
"source": "agent-delegate",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"sessionRevision": 3,
"sessionFingerprint": "sha256-serde-json-v2:<hex>",
"appliedSteerCursor": 0,
"requestKind": "<tool-plan|final-reply|context-compaction|final-reply-context-compaction>",
"requestSlot": "loop-2-repair-0",
"webSearchEnabled": false,
"requestContextFingerprint": "sha256-serde-json-v2:<hex>",
"fingerprint": "sha256-serde-json-v2:<hex>",
}
binding 的 durable 字段顺序与上方 JSON 完全一致;第 9 节另行冻结排除末尾 fingerprint 后的 typed value 顺序,以及不含 schemaVersion/providerRequestId/fingerprint 的 base request ID value 顺序。rootAgentId 与末尾 required fingerprint 都是 v1 当前合同的一部分,不是 reader 可忽略的扩展字段。
每个 exact planning Provider request 都必须携带同一个 plan-provider-structured-injections.v1 strict DTO;顶层字段顺序固定为 schemaVersion, clarificationRound, accumulatedAgentMillis, session, platformFacts, approvalObservation:
{
"schemaVersion": "plan-provider-structured-injections.v1",
"clarificationRound": 0,
"accumulatedAgentMillis": 0,
"session": {
"phase": "collecting",
"decisionsSummary": [],
"prototypeValidationItems": [],
"latestSubmittedRef": null,
"lastDecisionRef": null,
},
"platformFacts": {
"runtime": "self-contained-web",
"viewports": ["desktop", "mobile"],
"inputs": ["keyboard", "touch"],
"preview": "local-http",
},
"approvalObservation": null,
}
session 的字段顺序固定为 phase, decisionsSummary, prototypeValidationItems, latestSubmittedRef, lastDecisionRef,逐项复用当前 plan-session.v1 的 Provider-facing 语义类型;不得把 session identity、revision、fingerprint 或 Runtime 控制元数据带入。platformFacts 直接复用固定 PlanPlatformFacts,字段顺序与值均为上方所示。approvalObservation 只能是 null,或当前 observations 中最后一条 tool=plan.submit_gdd + status=ok 的现役 strict 四字段对象,字段顺序固定为 tool, status, summary, detail,其中 detail 必须显式为字符串或 null。DTO 的 canonical compact UTF-8 JSON 最大 64 KiB,不得 pretty print、追加空白、静默截断或省略 null/空数组。
上述 canonical JSON bytes 必须以一条 dedicated user message 实际进入最终 LlmRunRequest,wire 精确为:第一行 AGC_PLAN_PROVIDER_STRUCTURED_INJECTIONS_V1,第二行该 compact JSON;两行之间只有一个 LF,不存在第三行,也没有尾换行。同一份第二行 JSON bytes 不经重建地同时生成 request-context 中 structuredInjections.wireBytes 与 structuredInjections.wireSha256;前者是该 JSON 的 u32 UTF-8 byte 长度,后者是同一 bytes 的裸 64 位小写 SHA-256。消息摘要则继续覆盖实际发送的完整 user message,因此 header、LF 或 JSON 任一字节变化也会改变 messages 摘要。
requestContextFingerprint 使用第 9 节 helper 与 domain genarrative.plan.provider-request-context.v1。canonical value 的字段声明顺序固定为 effectiveModel, apiKind, stream, officialFallback, anthropicStrictToolSupport, openAiChatTokenBudgetField, maxOutputTokens, responseReasoningEffort, responseTextVerbosity, toolChoice, composition, sourceKind, webSearchEnabled, messages, nativeTools, mcpTools, structuredInjections。前十项必须取最终不可变 LlmRunRequest 与同一 captured LlmConfig/adapter wire 配置的实际语义值:effectiveModel 是显式 model 或 Provider 默认值解析后的非空模型名;apiKind 只取 openai_responses | openai_chat | anthropic;stream 是实际请求体 boolean;officialFallback 对 openai_chat/openai_responses 为实际 boolean、对 anthropic 必须为 null;anthropicStrictToolSupport 只在 anthropic 为实际 boolean、其它 apiKind 必须为 null;openAiChatTokenBudgetField 只在 openai_chat 取 max_completion_tokens | legacy_max_tokens、其它 apiKind 必须为 null;maxOutputTokens 为 null 或正 u32;reasoning/verbosity 为 null | low | medium | high;toolChoice 为 null | auto | required。只有会改变当前 apiKind 实际 wire 的配置进入非空值,避免无关 adapter 配置制造假 context/base 漂移。exact planning 四类请求的 composition 与 sourceKind 都固定为现役 Prompt Bundle 值 runtime,不得把 durable Runtime source agent-delegate 复制进 sourceKind,也不新增第四套 composition/source kind(原 supervisorPlanChat / SupervisorPlanChat 随 D6/D11 作废);webSearchEnabled 固定为 false;mcpTools 必须是显式空数组,且 project-planning 请求构建器直接使用空 catalog,不得先读取项目 MCP catalog 再清空;structuredInjections 只使用上方冻结的 DTO。providerRequestId、lifecycle/handoff ID、action/binding fingerprint 及其它 Provider/Runtime 控制元数据不得进入 messages、工具描述或 structured injection;它们只存在于 durable binding/base identity、session 私有 checkpoint 与恢复校验。
为遵守第 9 节“canonical payload 禁止 map/任意 Value”,messages 不是原始 JSON map,而是按实际发送顺序排列的 strict {role, wireBytes, wireSha256};nativeTools 是按实际广告顺序排列的 strict {name, kind, wireBytes, wireSha256},kind 只取 action | control;structuredInjections 固定为单个 strict {wireBytes, wireSha256}。wireBytes 是最终交给 provider-independent client 的单项 compact UTF-8 DTO u32 byte 长度,wireSha256 是同一字节串的裸 64-hex SHA-256;消息顺序/正文、当前请求实际广告的工具名称/描述/参数 schema,以及注入对象的字段/null/空数组/正文任一字节变化都会改变它。策划子 Agent 的 tool-plan 按第 4.3 节广告恰好三个 action tool(file.read / file.list / plan.submit_gdd)与两个 control function;Supervisor 侧按现役 standard 工具面广告。(原此处的「四 action tool」计入了 user.input_request,随 D11 该工具不再属于策划子 Agent;「checkpoint 请求只广告 plan 专用 strict update_agent_plan」随 D10 作废。)Runtime 在内存中先完成 model/default、stream mode、adapter wire 配置与全部输出参数解析,再对实际 DTO 生成这些摘要并计算外层 typed fingerprint;lifecycle/batch 只持久化外层 fingerprint,不写 messages、Prompt、工具 schema 或注入正文。requestTimeoutMs、maxRetries、retryBackoffMs、rawLogDir、API Key、base URL 与 transport header 属于传输/诊断配置,不进入 fingerprint,也不得被用来改变请求正文、模型或输出语义;除这些明确排除项外,Provider client 实际收到的任一请求语义变化都必须改变 fingerprint 和 base providerRequestId。
Goal 三元组沿用现役精确空值合同:没有 Goal 时必须是 goalId=null + goalRevision=0 + goalSnapshotFingerprint="";存在 Goal 时 goalId 为合法 opaque ID、goalRevision 大于 0、goalSnapshotFingerprint 为该 durable Goal strict snapshot 的裸 64-hex SHA-256。其它组合失败关闭。Agent DB v3 validator 必须把空 fingerprint 只留给前一种显式无 Goal 组合,不能先用通用“非空 identity”校验把合法无 Goal binding 拒掉。该三元组进入 binding 与 base request ID,恢复不能从当前 Goal 补写旧请求。
同快照构建算法固定如下:
- Runtime 在项目锁内读取并严格验证 current session primary、run/profile binding、Goal/steer 与构造请求所需的全部 durable 输入,形成不可变
CapturedPlanProviderContext;不得先从旧 conversation/messages 构建 request,再用较新的 session revision 给它盖章。 - 只从该 captured context 构造 composition、messages、结构化注入与工具目录;构建后的 provider request object 不再可变,并据其实际字段计算 requestContextFingerprint。
- 发送前重新取得项目锁,逐项重读 session revision/fingerprint、appliedSteerCursor、run/profile/Goal identity 与 captured context。任一变化都丢弃整个已构建 object,回到第 1 步;不能局部替换 binding 或 messages。
- 仍持锁时,从第 9 节规定的 plan request identity 生成稳定 base providerRequestId,解析本次 attempt,把上述 strict binding 先 durable 写入 lifecycle
started;释放锁后发送的必须是第 2 步同一个 object,不能再次调用 builder。
四种 exact planning request kind 都写 game-creator-provider-request-lifecycle.v3、同一 strict binding 与同一 structured-injection message。只有 tool-plan 能产生 action,因而也只有它能产生 game-creator-provider-action-batch.v4 或执行 plan.submit_gdd;final-reply、context-compaction、final-reply-context-compaction 一律没有 action batch。planning 的 idle context compaction 不具备 active run/session captured context,必须 fail-closed,不得借通用 idle compaction 伪造 binding。成功 tool-plan response handoff 创建 provider batch 时,batch 必须持久化完全相同的 providerRequestId + planningSessionBinding,且 batch identity 计算覆盖两者;batch 自身的 runId 与 actionId/actionFingerprint 完成 request → context → session → run → action 关联。若项目或 Agent policy 把 plan.submit_gdd 配为 confirm,M1B-2 必须把它按 deny 失败关闭,不能创建本包没有消费路径的 generic confirmation sidecar;显式 deny 同样拒绝。(原此处描述的 plan-decision-checkpoint 特例随 D10 作废:D11 下不存在这种「有 handoff 无 batch」的专用请求 kind。)非 plan lifecycle/batch 不伪造该 binding。
外层 wire 同时冻结版本与兼容边界:exact plan 的新 lifecycle 必须使用 game-creator-provider-request-lifecycle.v3,在 v2 字段白名单后唯一增加 required planningSessionBinding;exact plan 的新 batch 必须使用 game-creator-provider-action-batch.v4,在 v3 字段白名单后唯一增加 required providerRequestId 与 planningSessionBinding,batch ID v4 覆盖新增字段。binding.providerRequestId 必须精确等于 lifecycle/batch 外层 request ID;agent/task/session/run/source/requestKind/requestSlot/webSearchEnabled 与存在于外层的同名字段也必须逐项相等。(2026-08-14 按当前实现收口:binding v1 携带 rootAgentId / rootRunId / delegationId 与末尾 required fingerprint,共同把请求锚到具体委派根和委派跳;随 D10 作废的 supersededCheckpointProviderRequestIds 数组继续不存在。)非 plan 路径继续写现役 lifecycle v2 / batch v3。reader 继续按原合同双读既有 lifecycle v1/v2 与 batch v1/v2/v3,不原地升级或重写;这些旧记录只能按非 plan 语义恢复。任何记录声称 source=project-supervisor-plan-chat(作废字面量,全仓库零命中)或 composition=supervisorPlanChat,以及新 plan schema 缺 binding/夹带合同外字段,都失败关闭并进入 reconciliation,不能用默认值补齐。
GDD handler 只能从已验证 batch binding 复制 sourceSessionRevision/sourceSessionFingerprint,并要求它与 started lifecycle 记录、request context 和 current session primary 逐项相等。GDD 已存在的同 submission replay 才按第 8.3/10.1 节复用既有 historical binding;已提交事实不因 session 后续前滚而被判 stale。
任何 stale 判断前必须先在项目锁内检查“Provider 输出是否已被后续业务线性化点消费”。以下三类是互斥且优先于 session-revision 比较的成功历史,不得 supersede 或重新请求:
- 同 submissionId 的严格 GDD 已 create-only 提交:按 historical binding replay。
- (2026-08-13 按 D11 重写)Supervisor 侧 sole
user.input_request的 pre-wait 收口。原文把消费证明挂在session.activeQuestion的 primary 安装上;D11 下 session 不再持有问题,消费证明改为delivery 上的问题落盘(contract_status=NeedsUserInput且questionsSha256已写入)。允许的 exact pre-wait 状态不变:v4 batchstatus=ready + nextActionIndex=0 + actions.length=1,唯一内嵌 memberstatus=approved且 actionIndex=0,user-input sidecarstatus=pending,同 run 的 standalonegame-creator-pending-action.v5尚不存在;随后 waiting writer 创建同 identity 的 standalone pending,固定executionMode=auto + status=waiting-for-user-input + observation=null。恢复只允许三种状态:① standalone pending 尚不存在且 sidecar pending,从 delivery 的问题 + exact v4 batch 补同一 waiting;② standalone pending 已是上述 waiting 形状且 sidecar pending,幂等补 runtime/task/event/card 后恢复同一等待;③ standalone pending 仍是 exact waiting 形状且 sidecar 已单向进入 answer-prepared,原 batch 继续视为已消费,不再展示可重复提交的卡片,转入第 3 项。standalone pending 为approved/executing/observed-*、batch/member/cursor 非 exact、sidecar 为其它状态,或任一 identity 冲突都进入 reconciliation。 - **(2026-08-13 按 D11 重写)**答案已绑回 delivery:
answersSha256durable 写入原 delivery 就是本轮的线性化点(原文此处是session.appliedAnswers引用 checkpoint handoff)。该绑定由已发布代码保证首次写入或逐字相同则幂等、绑到不同答案则拒绝。只补 sidecar answered、原 user-input terminal observation、batch member completion、session 的派生appliedAnswers与 cleanup;不得再次调用模型,也不得因 session 落后而重新计轮次。
第 2 项处于 pre-wait 时必须先补齐 waiting/pending/card,修复完成前不接受回答。第 3 项在答案绑定后按固定顺序终结:先以 delivery 的 answersSha256 证明同一回答已线性化,再补 sidecar answered 和原 user-input observation,把原 action/batch 幂等推进 completed,写 session 的派生 appliedAnswers(含 continuationDelegationId,必须等于确定性派生值),最后清理 pending/batch。崩溃留下任一锚点时按相同步骤前滚。
**(2026-08-13 删除)**原第 3 项之后一整段关于「最终 checkpoint handoff 与 superseded 链上全部 success handoff 是 CAS 前的恢复锚点、必须保留到 session 内摘要重算完成才允许清理」的规定,随 plan-decision-checkpoint 与 supersededCheckpointHandoffs 一并作废:D11 下续跑靠 delegation 身份的确定性派生,不靠保留历史 handoff 文件。
只有不存在上述业务消费证明、GDD 也尚未提交时,Provider stale/retry 状态机才允许以下行为:
- binding 完整有效且 current session/context 仍逐项相等:接受 response handoff;tool-plan 先 durable 写 v4 batch,再把同一 v3 lifecycle 从
started终结为completed。崩溃留下started + 同 binding ready batch时,ready batch 已证明 success handoff durable;恢复必须先幂等补 lifecyclecompleted,再执行/恢复 batch,不能误记 interrupted。 - binding 完整有效,但同一 project/gdd/session/run/source/profile 下的 session 已沿合法 successor 链因回答、审批 observation 或 steer 前滚:旧物理请求为
started且没有 batch 时,只有当前 handler 已取得并决定丢弃旧 response,或 durable owner/lease/boot 证明旧调用不再存活,才终结为interrupted;旧终态同步回读前不得启动 replacement。若已有同 binding 的readyv4 batch,无论 lifecycle 是 started 还是 completed,都先按第 1 项补成真实completed,再持锁把 batch durable 标为superseded,确认回读后清理。sole user-input 的问题尚未落到 delivery 时,还必须按第 5.2 节验证没有 standalone pending/card/answer-prepared,并在 superseded 回读后删除仅可能存在的 exact pending sidecar、确认 durable absent,再清理 batch;这些步骤未收口前不得启动 replacement。completedlifecycle 保持真实历史终态,不能改写;superseded batch 永不执行、永不恢复成 action。 - 完成第 2 项后,从新 session 与新 requestContextFingerprint 派生不同的 base providerRequestId,attempt 从 0 开始并自动重新请求。不得沿用旧 base ID,也不得把旧 response 绑定到新 session。
- Provider transient failure/物理中断但 session、context 和 request slot 未变时,才沿用同一 base ID 的 attempt 派生规则。已知 retryable transport/upstream failure 先把旧 attempt durable 闭合为
failed;Runner/进程恢复只有在 boot/owner/lease 证据证明旧物理请求不再存活且无 handoff/batch 时,才闭合为interrupted。旧终态写入、同步并回读成功后,才能创建 attempt N+1 的新started;不能原地复用同一 providerRequestId,也不能让两个 started attempt 并存。无法证明旧请求已终止时进入 recovery required,不自动重发。每个 attempt 始终有独立started → completed|failed|interruptedlifecycle。 - 除第 1~2 项明确允许的同 binding
started + ready batch崩溃组合,以及上文 delivery 问题落盘/答案绑定的已消费证明外,binding 缺失/损坏、lifecycle 与 batch 不一致、同 revision 下 requestContextFingerprint 漂移、session 不是合法 successor,或 batch 已进入执行/等待状态时返回PLAN_NEEDS_RECONCILIATION。此路径不自动删除、不补默认 binding、不重绑、不重试。
严格 submit input 被 Runtime 以 PLAN_INVALID_REQUEST 或由该输入导出的候选 GDD PLAN_SIZE_LIMIT 拒绝时,当前策划子 run 最多产生 5 次 plan.submit_gdd / rejected observation:前 4 次关闭原 sole-action batch 后可在同一 run 续跑,让 Provider 根据最后一条 observation 修正;第 5 次仍须先完整落盘 rejected observation,再把该 run 终态失败,不得请求第 6 次 Provider tool-plan。该分类只针对本次 Provider input / 候选 GDD;读取既有不可变 GDD 或 receipt 时出现同名大小上限、既有 lineage 已达版本上限,或任何其它 durable authority 异常,一律是 PLAN_NEEDS_RECONCILIATION,不得消耗 Provider 重试额度。计数是 Runtime state 的 durable、每个 child run 独立的字段,进程重启不能清零;只有新建的策划 child run 才从 0 开始。它不依赖前端、Prompt 文字或 Provider 自报,且普通工具 observation 不计入。
第 2 项的自动前滚必须与 session successor、batch supersede/cleanup 和 replacement request 的 started 写入都在项目锁内按幂等步骤恢复;任一断点重启后只能继续相同步骤。这样合法 steer 能确定性替换旧输出,而身份污染不会被“自动恢复”掩盖。
**(2026-08-13 删除)**原此处一整段规定 plan-decision-checkpoint 的 stale/repair/superseded 状态机(repair-{K+1} 转换、传递闭包数组、handoff 与 successor 的应用边界)。该请求 kind 随 D10 作废,整段无对应物:D11 下续跑是「新 run、同 session」,其 stale 判据就是普通 tool-plan 的那一套(本节第 1~5 项),不需要第二套。
main loop 不能把 submit 当成普通 action dispatch:在 durable action identity 建立后、生成普通 command ID 或进入 action executor 前,必须进入 plan.submit_gdd 专用分支。该分支重验 exact plan identity,执行下列提交与投影。(2026-08-14 按 M1B-2 实现边界收口) 本包只负责校验、定版、写不可变 GDD、重建 index、渲染 game/fast_gdd.md、安装 session successor 并终止策划子 run;不创建 .agent/planning/pending.json / gdd-approval planning pending,不创建审批卡,也不把 Supervisor 或策划子 run 投影为审批等待。gdd-approval pending 与 Supervisor 等待态属于 M1C-1,还要受第 13.0 节 M1C-2a 验收取证门约束。原 submit 在进入专用分支前已经建立的 generic game-creator-pending-action.v5 standalone pending 与 game-creator-provider-action-batch.v4 action batch 必须原样保留,作为后续 receipt/terminal observation 的同 action 恢复锚点;GDD create 成功不等于该 action 已 observed。
- 解析第 8.2 节 strict input;在项目锁内重读 project identity、策划子 run 与委派根身份、Provider request 所绑定的 session CAS、canonical GDD 链及原 submit 的 generic v5 standalone pending / v4 batch anchors。不信任 Provider payload 中不存在也不允许出现的版本、时间、平台事实或身份;M1B-2 不读取或创建尚未实现的 approval receipt / planning pending。
- 验证文本上限、轮次、决定状态和 prototype item 一一对应;
decisions按本次完整 GDD 快照校验,不与旧 session 内容逐项比较。round=0的非首项决定只能是answerSource=default(默认建议)或answerSource=user_revision(审批修改),分别对应允许的状态集合。user_revision还要求当前 session 已有lastDecisionRef.action ∈ {revise, reject};没有该引用时不得把未确认项标成审批修改。已有 session 创建新的 planning child 时,repairOfDelegationId必须精确等于旧 session 的latestDelegationId;这条 continuation 游标约束在 Provider 请求前生效,防止旧 delivery 重新成为当前分支。Runtime 注入固定 platformFacts 和所有basis:null,以当前 durable actionId/裸 action fingerprint 作为 submission identity。 - M1B-2 尚无 receipt writer:只要已有任一 GDD,新的不同 submissionId 就返回
PLAN_PENDING_GDD_EXISTS;同 submissionId 只允许按历史 binding replay。M1C-1接入有效 approve/revise/reject receipt 后,才把边界扩为“最新版本已有 receipt 才允许下一版本”。 - 当前 M1B-2 的首次版本固定为 1;未来版本仍只能取最后一个连续有效版本加一,范围 1~128,不允许缺号或扫描任意文件补号。
- 新提交由 Runtime 生成并冻结
approvalRequestId/createdAtUtc,填充全部 durable identity、source session binding 和时间,计算 GDD fingerprint,以第 10.1 节算法 create-only 发布gdd.v{N}.json。同 submissionId replay 必须先找到并严格读取既有 GDD,复用其中 Runtime 生成的版本、request/time 与 identity 后再比较,不能用新时间制造假冲突。 - GDD target 完整发布并完成父目录同步是唯一提交点。提交点前失败不产生版本;提交点后任何投影失败都仍是已提交。
- 提交点后按固定顺序修复:从严格 GDD 链重建 index/status → 按第 7.1 节渲染 Markdown → 以原 source session CAS 安装或确认精确的
awaiting_gdd_approvalsession successor → 将策划子 run 与 delivery 投影为已提交终态。child terminal ensure 必须可重入:task projection、action-scoped event、action-scoped Agent DB audit 和 delivery 都按原 action identity 幂等;delivery 发布后必须 exact 回读同 agent/session/run/delegation 的completed终态,未 durable 前不得写recoveryPending=false的 committed audit,重复恢复不得增加第二条逻辑投影。不得在这一步创建 planning pending 或任何 waiting 投影;completed 子 run 的waitingOn/nextStep/lastResponse也不得伪装成审批等待。原 generic v5 standalone pending 与 v4 batch anchors 也不得被 terminal cleanup 删除、标为 observed 或推进 cursor。 - 同 submissionId + 同 canonical payload 返回同一 ref 并补投影;同 ID + 不同 payload 返回
PLAN_SUBMISSION_IDENTITY_CONFLICT,不得生成 vN+1。
M1B-2 的恢复只处理提交事实及其现役 generic anchors,不恢复审批等待。若 v5 standalone pending 与 v4 batch 都存在,必须逐项核对两者、binding 与 immutable GDD 的 submissionId/actionFingerprint/agent/task/session/run/source/profile/root/delegation 等身份并保持原状;standalone pending 还必须携带 v4 batch 独有但重建必需的 providerBatchPlanUpdate recovery material,使 pending-only 能逐字恢复原完整 plan、planUpdate 与 batchId。若恰有一个缺失,而另一个与 immutable GDD 严格匹配,则从“存活 anchor + immutable GDD + frozen binding”确定性重建缺失投影,重建后回读全等,不能重新调用 Provider 或重新分配 action/GDD identity;pending-only 补原 v4 batch,batch-only 补原 standalone pending。两者同时缺失时即使 GDD 已提交也必须 fail-closed 进入 reconciliation,因为只靠 GDD 不能无歧义恢复完整 action/batch wire;恢复扫描必须覆盖真实的“GDD 已 create、child 尚未 finish、Runtime pendingToolAction=null”窗口,并且重复扫描不得重复写 reconciliation audit。任一存活 anchor 损坏或与 GDD 不匹配同样 reconciliation,不得拿另一个覆盖。已提交 GDD 是先于当前 repository/steer 漂移的业务线性化证明,同 action replay 的 recoveryPending 必须能穿过通用 drift barrier 收口原投影,不能误开新请求或新 GDD 版本。M1C-1 后续才创建 Supervisor 的 planning pending,并在 receipt 后把第 13.1 节 terminal observation 幂等写回这些原 submit anchors,再按固定顺序完成与清理;M1B-2 不提前实现该消费路径。
结果 DTO:
type PlanSubmitGddResult = {
outcome: 'submitted' | 'replayed';
gddRef: { gddId: string; version: number; fingerprint: string };
pendingActionId: string;
approvalRequestId: string;
recoveryPending: boolean;
};
只有 index、Markdown、session successor、策划子 run 终态及两枚 generic anchors 全部 durable 且 identity 对账通过时 recoveryPending=false。已越过提交点但这些投影未齐时仍按首次提交或重放返回 submitted/replayed,并以 recoveryPending=true 表示未收口;页面不得重新提交新版本,只能用同 action identity 触发恢复。outcome 只表达权威事实是首次创建还是重放,投影恢复状态只由 boolean 表达。
13. 审批 pending、提交点与幂等
2026-08-13 按 D11 更正。
gdd-approvalpending 建在 Supervisor 根 run 上(source=project-supervisor-plan),因为审批卡是给用户看的、而用户只跟 Supervisor 对话;提交 GDD 的策划子 run 在plan.submit_gdd返回后即终态,不等待审批。receipt、幂等域、decisionFingerprint/receiptFingerprint与投影顺序未变。
分包边界(2026-08-14):本节的 planning pending、审批等待、command 与 receipt 均从
M1C-1起实现,不属于当前M1B-2。M1B-2 只保留原 submit 的 generic v5 standalone pending + v4 batch anchors,二者不是审批 pending,也不得被改名或迁移为审批 pending。
13.0 审批前置门:验收取证必须先于审批卡(2026-08-13 补充)
原文只规定了「取证必须在 GDD 落盘之后」,没有规定取证在用户审批之前还是之后,第 13.3 节的 receipt 后投影顺序里也没有验收图这一步。该空白必须补上,因为反序会产生一个无回退路径的死角:
用户点批准 → receipt durable(线性化点,此后不回滚批准)
→ Supervisor 才取证 → fast-gdd-serves-intent 判定不 passed
→ goal_contract_acceptance_completion_blocker_at_locked 返回 blocked
→ 根 run 完不成,而 GDD 状态已是 approved
此时用户以为已经定稿、系统却卡住,且 receipt 不可回滚(要改只能提交并决定新版本),这个状态对用户不可解释。
固定顺序:Supervisor 收到 plan.submit_gdd 的 Submitted 回执后,必须先以 tool:file.read 对 game/fast_gdd.md 取证并确认合同的全部 required 验收节点,通过后才允许把 gdd-approval pending 暴露给用户。尚未取证、分页未覆盖全文或回执 SHA-256 已不是当前 Markdown 时,下一步仍是由当前 Supervisor 根 run 重新读取并更新验收图,不能提前返工;只有当前根、当前 project revision、当前 Markdown 全文证据支撑的显式 failed 才表示“取证未通过”,此时不创建审批卡,改为发起返工委派(repair_of 指向原 delivery),由策划子 Agent 修订后重新提交。
M1C-2a 将该取证合同进一步冻结为:evidence 必须来自当前 project-supervisor-plan / standard 根 run 自己的 file.read,策划子 run 或同根其它 child 的回执不能替代 Supervisor 的语义判断;路径必须精确为 game/fast_gdd.md。读取从 startLine=1 开始,按工具上限分页,无缺口、无重叠地覆盖到 EOF;所有页必须报告同一完整内容 SHA-256 和总行数,agent.acceptance_update.evidence 必须列出全部分页 actionId。普通 Acceptance Graph durable 读取只复核回执形状、身份与完整覆盖,不把审批决定后 Markdown 状态投影造成的 hash 变化误判为 Graph 损坏;当前 Markdown hash 只在尚无 receipt/pending 的审批前置门复核。
原 planning delivery 必须先由同一 Supervisor 根 run 通过 agent.run_status durable 认领。Ready 只返回认领动作,不给 repairOfDelegationId、也不建审批卡;ClaimedByParent 后才根据上述三态产生“继续取证 / 指向原 delivery 的返工 / 建卡”。plan.submit_gdd 已 create 但 child 或 delivery 尚未完成的恢复窗口保持惰性,由 M1B-2 原恢复链先收口,不能被审批门抢先判错。
为什么必须是这个方向:两道检查的性质不同,混序会让它们互相抵消。
| 检查 | 判什么 | 谁判 | 不通过怎么办 | 计数 |
|---|---|---|---|---|
| strict schema(第 12 节) | 字段存在性与约束 | Runtime | 工具错误回灌,不分配版本号 | 不计 |
| Goal Contract 验收图 | 产物是否服务本次意图 | Supervisor(agent) | 返工委派,不惊动用户 | repair_depth += 1,上限 1 |
| 用户审批 | 满不满意 | 人 | 用户修订委派 | 两个计数都不动(见第 23.7 节) |
机器判合格在前、人判满意在后,三道门各自的失败路径互不干扰;反序则会出现「人已经批了、机器才说不合格」这种没有出路的组合。schema 那道天然满足本约束——校验失败不产生版本,不产生版本就没有审批卡,结构上不可能反序。
恢复语义:取证已完成但 pending 尚未创建时,按既有 identity 幂等补建审批卡,不重新执行 Provider 或 file.read 动作;恢复只复核 durable Acceptance Graph、动作回执与当前 Markdown hash。pending 已创建则证明取证已通过,恢复路径不得再次要求取证,也不得因审批后的 Markdown 状态改写否定旧 Graph。取证结果本身不落新的平行记录,它就是现役 Acceptance Graph 的 evaluation。
13.1 gdd-approval pending
M1 新增独立 .agent/planning/pending.json,不升级、不迁移、不重写现役全局 game-creator-pending-action.v5,也不能把普通 user.input_request 或 confirmation pending 改名冒充审批。独立文件使用 plan-gdd-approval-pending.v1 strict schema:
{
"schemaVersion": "plan-gdd-approval-pending.v1",
"kind": "gdd-approval",
"projectId": "...",
"agentId": "project-supervisor",
"gddRef": {
"gddId": "gdd-...",
"version": 1,
"fingerprint": "sha256-serde-json-v2:<hex>",
},
"submission": {
"tool": "plan.submit_gdd",
"pendingActionId": "action-<24 位小写 hex>",
"actionFingerprint": "<64 位小写 hex>",
"approvalRequestId": "gdd-approval-<uuid>",
},
"runIdentity": {
"source": "project-supervisor-plan",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"sessionId": "...",
"runId": "...",
},
"status": "awaiting_decision",
"observation": null,
"pendingFingerprint": "sha256-serde-json-v2:<hex>",
}
所有嵌套对象同样 deny_unknown_fields。agentId 与 tool 是固定常量;gddRef、submission 和 runIdentity 的其它字段逐项来自不可变 GDD,其中 pendingActionId=GDD.submissionId、runId=GDD.createdByRunId。因此唯一无 receipt 的严格有效 GDD 足以确定性重建 awaiting_decision 文件,不需要也不允许猜现役 common pending 所需的 task、原 action input、loop/action index、nonce、project revision 或 verification gate。
status 只允许 awaiting_decision | observed_approve | observed_revise | observed_reject。无 receipt 时必须是 awaiting_decision + observation:null;receipt 存在时必须是与 receipt action 对应的 observed 状态和下述完整 observation。pendingFingerprint 使用第 9 节 helper,覆盖除自身外全部字段。文件是可重建投影而非审批真相;原 run durable 消费 observation 后直接清理,不持久化不可重建的 consumed 状态。若消费后、清理前崩溃,恢复会再次生成相同 observed 状态,依靠原 action terminal observation 幂等键安全重放后清理。
顶层 Runtime 状态继续复用 waiting-for-user-input 同族等待语义,不新增第二套 run status,但 action projection 必须显示 GDD approval waiting,不能当作通用自由输入。approvalRequestId 从不可变 GDD 读取,恢复不得换号。审批 observation 的稳定身份是 (runId, pendingActionId),不是给现有 observation DTO 临时增加自由字符串 ID;observation body 从 receipt 确定性生成。
observation 使用现役四字段 strict shape {tool,status,summary,detail},固定 tool=plan.submit_gdd、status=ok;approve summary/detail 分别为 Fast GDD v{N} 已批准 / 用户已批准当前版本。;revise 为 Fast GDD v{N} 需要修改 / 用户修改意见:{normalizedComment};reject 为 Fast GDD v{N} 已退回 / 用户退回原因:{normalizedComment}。除规范化 comment 外不拼接自由错误文本,重放必须生成逐字相同 observation。
13.2 command 输入与线性化点
type DecideGameCreatorPlanGddInput = {
projectPath: string;
gddId: string;
version: number;
fingerprint: string;
pendingActionId: string;
approvalRequestId: string;
responseId: string;
action: 'approve' | 'revise' | 'reject';
comment: string | null;
};
projectPath 只是 Tauri transport 定位参数,不进入 receipt 或指纹;Runtime 从经过授权的项目根读取 projectId。UI 在一次用户决定开始时生成 responseId,busy、超时和网络重试都复用;用户改变 action/comment 后必须生成新 responseId。
项目锁内先验证项目与文件边界、GDD 文件名/projectId/gddId/version/重算 fingerprint、comment 规则并读取既有 receipt。若尚无 receipt,再要求当前唯一 active top-level exact plan run、独立 pending 的 kind/ID/request/source/profile/binding 与 GDD 逐项一致,随后计算 decisionFingerprint 并 create-only 发布 approvals/v{N}.json。若已有有效 receipt,不要求已经结束的原 run 仍是 current active:同 responseId 必须用既有 Runtime decidedAtUtc 重建 intent/receipt 做 replay 校验,不同 responseId 返回 already-decided;两者都按 receipt 的 durable run identity 补齐投影。无 receipt 且卡片或 pending 已过期才返回 PLAN_STALE_APPROVAL。
receipt 完整发布并同步父目录是用户决定的线性化点。此后不回滚、不覆盖、不删除,也不以“后续步骤失败”为由声称用户没有决定;改变决定只能提交并审批新版本。
13.3 receipt 后固定投影顺序
sequenceDiagram
participant UI as GDD 审批卡
participant CMD as decide_game_creator_plan_gdd
participant LOCK as 项目 write lock
participant FACT as 不可变 GDD/receipt
participant PROJ as index/Markdown/audit/observation/session
participant RUN as 原 plan run
UI->>CMD: approvalRequestId + responseId + ref + action
CMD->>LOCK: 获取锁并重读全部身份
LOCK->>FACT: 校验 GDD,no-replace create receipt
FACT-->>LOCK: receipt durable(线性化点)
LOCK->>PROJ: 1 重建 index/status 与 Markdown
LOCK->>PROJ: 2 幂等追加 decision audit
LOCK->>PROJ: 3 写入 pending terminal observation 与 event/state
LOCK->>PROJ: 4 checkpoint session
LOCK-->>CMD: typed outcome + recoveryPending
CMD->>RUN: recoveryPending=false 时续跑精确原 run
RUN->>PROJ: durable 消费 observation 后清理 pending
CMD-->>UI: typed result
固定顺序是:权威投影 → audit → terminal observation → session → continuation 消费后清理 pending。pending 在 observation durable 前不能删除,因为它是原 action/run 的身份锚点。UI 发现有效 receipt 后立即隐藏 stale 审批卡,即使 pending ledger 尚待清理;恢复不得重新让用户决定。
agent.db 必须新增专用 append_plan_gdd_decision_if_missing。以下是 helper 入参的完整 strict payload;不允许额外字段或 comment 正文:
{
"recordType": "agent.runtime.plan.gdd_decided",
"auditSchemaVersion": "agent-runtime-plan-gdd-decided.v1",
"projectId": "...",
"agentId": "project-supervisor",
"gddId": "gdd-...",
"version": 1,
"gddFingerprint": "sha256-serde-json-v2:<hex>",
"pendingActionId": "action-<24 位小写 hex>",
"actionFingerprint": "<64 位小写 hex>",
"approvalRequestId": "gdd-approval-<uuid>",
"responseId": "gdd-response-<uuid>",
"source": "project-supervisor-plan",
"runProfile": "standard",
"runProfileBindingFingerprint": "<64 位小写 hex>",
"sessionId": "...",
"runId": "...",
"action": "approve",
"decisionFingerprint": "sha256-serde-json-v2:<hex>",
"commentHash": "sha256-serde-json-v2:<hex>",
"commentLength": 0,
"receiptFingerprint": "sha256-serde-json-v2:<hex>",
"decidedAtUtc": "2026-08-10T00:00:00.000Z",
}
除固定 agentId 外,字段全部可从严格 GDD 与 receipt 确定性重建。commentHash 使用 domain genarrative.plan.gdd-comment.v1,canonical value 固定为字段顺序只有 comment 的 { "comment": normalizedCommentOrNull };comment 为 null 时也对显式 null 计算 typed hash,不使用空字符串或空 hash 代替。commentLength 是规范化后 comment 的 Unicode scalar count,null 固定为 0。
comment hash golden:null 的 69-byte envelope 是 {"domain":"genarrative.plan.gdd-comment.v1","value":{"comment":null}},结果为 sha256-serde-json-v2:3cae1a9b6efce6311511e88badea24f9be42814d0e9efb82e4396089999c0623;玩法更聚焦 的 82-byte envelope 结果为 sha256-serde-json-v2:a3f4cbbb5af197fc21623cbcea2b6f6b574f7a3acc5daa308fbda1013f83a19d。
现役 agent.db serializer 在写 JSONL 时只额外加入通用 schemaVersion=GAME_CREATOR_AGENT_DB_SCHEMA_VERSION 与 updatedAt;stored validator 必须要求恰好这两个 envelope 字段,禁止其它额外字段。幂等比较先验证 envelope,再忽略 updatedAt 的写入时刻并对上述 helper payload 做完整相等比较;调用方不得传入或控制两个 envelope 字段。
幂等键为 (recordType=agent.runtime.plan.gdd_decided, gddId, version, responseId);approvalResponseId 只在所属 GDD ref/action 域内幂等,不同版本允许合法复用同一文本值。same compound key/same 完整 strict payload replay,same compound key/different payload 返回 PLAN_DECISION_IDENTITY_CONFLICT。该 recordType 使用与 terminal observation 同级的专用保留额度,至少覆盖单 lineage 的 128 个版本,不能走 Ordinary append;尾部修复、容量上限和压缩后仍保留每个 receipt 恰一条逻辑记录。validator 必须区分 planning typed fingerprint 与裸 action/profile binding digest。
同一 gddId/version 内,同 responseId + 同 decisionFingerprint 返回原 receipt 并补投影;同 responseId + 不同 fingerprint 返回 PLAN_DECISION_IDENTITY_CONFLICT;不同 responseId 决定同一版本返回 already-decided,附既有 action/ref,不创建第二份 receipt。不同版本按上段复合键落入独立幂等域。
结果 DTO:
type PlanGddDecisionResult = {
outcome: 'committed' | 'replayed' | 'already-decided';
requestedResponseId: string;
decisionRef: {
gddId: string;
version: number;
gddFingerprint: string;
approvalRequestId: string;
responseId: string;
action: 'approve' | 'revise' | 'reject';
decisionFingerprint: string;
receiptFingerprint: string;
};
approvedGddRef: {
gddId: string;
version: number;
fingerprint: string;
} | null;
recoveryPending: boolean;
};
requestedResponseId 始终回显本次 command 入参;decisionRef.responseId/action 始终来自新建或既有 receipt。不同 responseId 决定已有 receipt 的版本返回成功 already-decided,同时返回请求 ID、receipt ID 与既有 action,不创建第二份 receipt;同 responseId、不同 intent 才返回 PLAN_DECISION_IDENTITY_CONFLICT。只有 receipt action 为 approve 才返回 approvedGddRef。committed/replayed/already-decided 均可与 recoveryPending=true 组合,表示 receipt 已确定但派生投影未齐;只有 recoveryPending=false 才允许“批准并开建”发出第二条独立开建命令。两项业务动作始终有两条 durable 记录。
14. 恢复与兼容矩阵
2026-08-13 按 D11 更正。 表中原有 6 行围绕
activeQuestion落盘时序与plan-decision-checkpointhandoff 重放的场景已删除——这两样在 D11 下都不存在。等价的恢复语义由已发布的 PR #165 中转链路承担,另立 4 行覆盖。GDD / receipt / index / Markdown / session / pending / 老项目兼容各行未变。
恢复顺序固定为:路径/普通文件/大小/schema → project/source/profile/run identity → GDD 连续性与 typed fingerprint → receipt 引用、decisionFingerprint 与 receiptFingerprint → 状态推导 → index/Markdown → pending → audit/observation/session。权威事实冲突时停止 mutation;只有缺失或落后的投影允许自动修复。
| 磁盘或运行状态 | 冻结行为 |
|---|---|
无 .agent/planning/ |
老项目或未走首页“做方案”入口的新项目;直接开建零差异;仅首页“做方案”首次创建可在项目锁内懒创建 planning sidecar |
| valid v1 | 重算全部指纹、连续版本、项目/run identity 和 receipt 引用后读取 |
| unknown schema/field | PLAN_UNSUPPORTED_SCHEMA,只读失败;不覆盖、不降级 |
| GDD/receipt target 旁有 tmp | tmp 不是事实;持锁确认无活跃 writer 后清理 |
GDD/receipt 有 .previous |
非法布局,进入 reconciliation;不可变 writer 永不创建 backup |
| GDD 存在、index 缺失/损坏/落后 | 从全部严格有效 GDD/receipt 重建 index/statusCache |
| index 引用缺失 GDD、版本缺号、同版本身份冲突 | PLAN_NEEDS_RECONCILIATION;禁止分配新版本 |
| receipt 缺 GDD,或 ref/project/run/fingerprint 不符 | 无效信任根;PLAN_CORRUPT_AUTHORITY,禁止开建 |
| GDD 无 receipt 且 pending 缺失 | 若全 lineage 恰好一个无 receipt 版本,按 GDD 内的 durable action identity 与 approvalRequestId 重建唯一 pending;不得生成新卡身份 |
| 两个无 receipt GDD | reconciliation;不猜哪一个待审 |
| stale pending 已有 receipt | UI 隐藏;补 audit/observation/session,精确原 run 消费后清理 |
| Markdown 缺失或头部不匹配 | 按第 7.1 节从权威 JSON 重渲染 |
| session primary + previous 为合法 successor 链 | primary 为准,清理 stale previous |
| session primary 缺失、previous 唯一有效 | 持锁提升、回读并同步父目录 |
| session primary 损坏但 previous 有效 | fail closed;不静默回退较旧 draft |
| session 两份有效但分叉 | PLAN_SESSION_RECOVERY_REQUIRED |
| session 缺失且存在 active run/未提交决定 | recovery required;不重置轮次或 gddId |
子 Agent 已以 AGC_NEEDS_USER_INPUT_V1 终态信封退出,delivery 的问题与 questionsSha256 已落,Supervisor 侧 pending 未落 |
从 delivery 确定性重建 Supervisor 的 waiting-for-user-input pending;问题正文必须与信封逐字一致。不得重发子 Agent、不得改写问题 |
| v4 ready batch + 问题尚未落到 delivery | user-input sidecar 只允许 absent 或 exact pending;缺失时确定性创建、已有 exact pending 时复用。其它 sidecar 状态/hash/identity 进入 reconciliation;不重新请求 Provider |
delivery 已绑定 answersSha256,continuation 尚未派发 |
从 (parentRunId, delegationId, questionsSha256, answersSha256) 确定性派生 continuation 身份并派发。同一组问答只能派生同一个 continuation,重放幂等,不增加轮次 |
delivery 的答案绑定与 session appliedAnswers 不一致 |
以 delivery 为准补 session 派生投影;session 声称的 appliedAnswers 多于链路现算的 clarification_round 时进入 PLAN_NEEDS_RECONCILIATION,不得反向修改链路 |
| Provider 成功但 response handoff 被 storage 安全/容量/identity 门拒绝或 durable 提交失败 | 不保存不安全正文、不补 lifecycle completed、不自动 repair/retry;只写安全诊断并进入 reconciliation,保留真实 started 历史供人工处置。(这是现役通用 handoff 安全边界,不专属任何 request kind;随 checkpoint 行删除后在此单列,避免连同 D10 一起丢掉。) |
| appliedAnswers 指向最终 Hn,且 superseded handoff 摘要覆盖 H1…Hn-1 | 摘要 ID 序列必须等于 Hn binding 数组,并保留各祖先 session/response/checkpoint fingerprint;H1…Hn-1 均为稳定被替换历史,只补最终回答投影,不重新应用/请求旧 handoff。历史 handoff 文件可在 session durable 后清理;摘要缺口、乱序、分叉或 identity 不同则 reconciliation |
| session.appliedAnswers 已含相同 checkpoint,sidecar/observation 落后 | 不再请求 Agent 或写 decision;只补 answered/observation 与 same-run continuation |
| 同 request/response 的 answer hash、handoff response 或 decisionCheckpoint fingerprint 冲突 | PLAN_ANSWER_IDENTITY_CONFLICT;不选择任一版本 |
| 无三类业务消费证明;plan lifecycle 只有 started、无 batch,binding 指向 current session 的合法祖先 | 若无 GDD,仅在 handler 已取得并丢弃旧 response,或 owner/lease/boot 证明旧调用终止后追加 interrupted;回读终态后按 current session/context 的新 base attempt 0 前滚。否则保持恢复态,不并发 replacement |
| 无三类业务消费证明;plan lifecycle started + 同 binding ready batch | batch 证明 success handoff 已 durable;先幂等补 completed。若 binding 仍 current 则恢复 batch;若指向合法祖先则 supersede/回读/清理后新 base attempt 0 |
| 无三类业务消费证明;plan lifecycle completed + ready batch,binding 指向 current session 的合法祖先 | 若无 GDD,持锁把 batch 标记 superseded 并清理,再按新 base attempt 0 前滚;completed lifecycle 保留 |
| 无三类业务消费证明;plan lifecycle completed + batch 已清理,binding 指向 current session 的合法祖先 | 视为上一行崩溃恢复,继续创建新 base;同 session/context 下 completed 却无 batch 则 reconciliation |
| plan lifecycle/batch/handoff binding 缺失、损坏或互相冲突 | PLAN_NEEDS_RECONCILIATION;不删除、不补值、不重绑、不自动请求 |
| 无 active run、已有 durable GDD/receipt,用户显式“继续策划” | valid session 存在时写 revision+1 successor 并绑定新 activeRunId;session 确实不存在时才从事实创建相同 gddId 的最小 revision 1;不改变既有版本或 receipt |
| projectId/source/profile/binding/run 不符 | fail closed;不得跨项目复制信任或把普通/autonomous run hydrate 为 plan |
| receipt committed、command response 丢失 | 同 responseId 重试返回 replay;新窗口从 receipt 投影 already-decided |
恢复“扫描”只枚举 canonical GDD/receipt 并逐个做完整验证,因而不是从任意孤儿猜意图。提交 GDD 自带 submission/action identity;receipt 自带 pending、request、response 和 run identity,pending 正常清理后仍能证明事实来源。
15. typed 错误合同
前端不得解析中文错误文案。M1 的 submit、approval、hydrate 和 build admission 返回统一错误对象:
type PlanGddError = {
code:
| 'PLAN_INVALID_REQUEST'
| 'PLAN_PATH_UNSAFE'
| 'PLAN_PROJECT_ID_MISMATCH'
| 'PLAN_SOURCE_PROFILE_MISMATCH'
| 'PLAN_ACTIVE_RUN_EXISTS'
| 'PLAN_PENDING_GDD_EXISTS'
| 'PLAN_VERSION_LIMIT_REACHED'
| 'PLAN_SUBMISSION_IDENTITY_CONFLICT'
| 'PLAN_ANSWER_IDENTITY_CONFLICT'
| 'PLAN_DECISION_IDENTITY_CONFLICT'
| 'PLAN_STALE_APPROVAL'
| 'PLAN_UNSUPPORTED_SCHEMA'
| 'PLAN_UNSUPPORTED_KNOWLEDGE_BASIS'
| 'PLAN_CORRUPT_AUTHORITY'
| 'PLAN_SESSION_CAS_CONFLICT'
| 'PLAN_SESSION_RECOVERY_REQUIRED'
| 'PLAN_NEEDS_RECONCILIATION'
| 'PLAN_DURABILITY_FAILED';
message: string;
retryable: boolean;
recoveryAction:
| 'retry-same-id'
| 'reload'
| 'resume-original-run'
| 'manual-reconcile'
| 'none';
};
message 是有界、脱敏、用户可理解的中文;不得包含绝对路径、Provider payload、指纹全文、密钥或上游正文。提交点后的投影未齐使用成功 DTO 的 recoveryPending=true,不增加组合 outcome,也不伪装为“提交失败”。
16. M2 完整构建绑定
M1 只完成策划闭环。M2 才允许完整构建读取 approved GDD,并且必须显式区分两种启动意图:
type PlanningBaselineInput =
| {
mode: 'approved-gdd';
approvedGddRef: { gddId: string; version: number; fingerprint: string };
}
| {
mode: 'direct-build';
approvedGddRef: null;
};
不能把“缺少 ref”自动解释为直接开建;否则页面漏传、恢复丢字段或攻击者删字段会静默绕过策划。只有用户明确选择“直接开建”才能提交 mode=direct-build。
16.1 构建准入与 ref 贯穿
mode=approved-gdd 时,后端在项目锁内:
- 从 canonical GDD/receipt 重算 project identity、GDD fingerprint、receiptFingerprint、状态与当前 approved 版本;不信任 UI、index、Markdown、Agent 自述或 command 返回缓存。
- 核对输入 ref 精确等于当前有效 approved ref;旧 superseded ref、未审批候选或只有 Agent 文案均拒绝。
- 在同一个锁/CAS 边界内把 planning baseline 写入 root task record、durable run configuration binding 和 autonomous completion contract,再允许任务入队。当前在 completion contract 创建前释放项目锁的顺序必须调整或增加等价的可恢复提交标记,不能留下“task 已启动但基线未冻结”的窗口。
- Provider context bundle 持久携带
planningBaseline、权威相对路径与有界 GDD 摘要;恢复、retry、child 调度和 final completion 都核对相同 ref,不因后来批准新版本而换稿。
2026-08-11 合入的动态目标验收图给可信根 Run 增加了 steer 替换协议:根 Run 收到 steer 后整棵旧根任务树先写取消栅栏并逐个收束,停稳后才在同一 Session、source 和 Run Profile 启动 replacement root run。因此 planning baseline 不是「一次冻结永久有效」。replacement root run 必须在自己的项目锁/CAS 边界内重新冻结与被替换根完全相同的 approvedGddRef,即使期间已经产生更新的 approved 版本也不换稿;无法从 canonical GDD/receipt 证明同一 ref 时失败关闭,不得降级为 mode=direct-build,也不得让 replacement run 以「缺基线」重新开始。旧根的 completion contract 终结与新根的基线冻结之间不允许出现「已启动但基线未冻结」的窗口,这与上面第 3 条是同一条要求。
是否把 ref 写入 .agent/manifest.json 由 M2 结合迁移成本另行决策;但 task/run/completion identity 的贯穿是本方案已冻结的最低要求,不能省略。第 19 节的 manifest 非权威源不变量对这条决策是负面证据,而 Goal Contract 提供了正面先例:它落在 .agent/runtime/goal-contracts/{rootAgent}/{rootRun}.json,天然按 root run 分文件,并绑定根 Run Profile binding fingerprint 与 source SHA-256。approvedGddRef 若要落盘,应当照这个形态而不是塞进项目级单例。
mode=direct-build 不读取 planning sidecar,保持老项目行为。两种模式都继续使用现行 project-supervisor-gui|cli + autonomous-game-build,不把 plan source 带进 DAG。
16.2 现行 16 任务 DAG 的职责调整
- seed task ID、依赖和数量不变。
design-director在 M2 变为确定性 GDD 准入门,不再启动 LLM。有 approved ref 时由 Runtime 物化.agent/spec.md并投影 completed;direct-build 时以“无策划基线”审计后确定性完成。design-foundation有 ref 时只把 approved GDD 具体化为实现规格、输入映射、HUD、实体、规则细节与 UI 原型,不得改玩法定位、支柱、目标用户或 MVP 范围;无 ref 时保持现行自主设计行为。game/game_design.md在有 ref 时写明Fast GDD v{N}与指纹前 12 位,完成门确定性核对;冲突时失败并要求新策划版本,不自行换玩法。art-director只读消费批准 GDD 的美术风格与 MVP 美术边界;详细视觉语言仍归美术链。quality-review检查核心循环、MVP 系统和支柱承诺是否偏离;preview-playtest把 prototype validation items 转为观察清单;publish-strategy可复用标题、一句话描述、目标用户和支柱。- 试玩发现只形成实现证据或下一版本建议,不回写、覆盖或“自动修正”已批准 GDD。
确定性收束与动态 Acceptance Graph 是两套证据体系,M2 不能混用。design-director 变成确定性 GDD 准入门后不再启动 LLM,因此不产生任何 Provider 动作回执;M0-3 的 Runtime 内部固定产物验证同样不产生 Provider 可见回执。而 Acceptance Graph 的 passed 节点必须引用当前根任务树中真实成功动作回执,requiredEvidence 只接受 tool:<Runtime 工具名> 且必须命中 Runtime 允许的持久证据工具集合(agent_runtime_acceptance_evidence_tools(),当前 18 项,既不含 Runtime 内部 owner 产物验证,也不含任何控制面或纯协调工具)。结论固定为:确定性 completed 投影与内部产物验证都不构成验收证据;涉及 GDD 落地的验收标准要么由根 Supervisor 用允许的证据工具自行取证,要么不写成 required 节点。不得为了让确定性节点“可验收”而把内部验证工具暴露成 Provider 可见工具——那会直接推翻 M0-3 已冻结的 owner 验证边界。
注入前仍从不可变 GDD/receipt 验证,不能读取 index 或 Markdown 当信任源。唯一 code-prototype 主 Agent、按需美术 child 与 audit-existing-first 执行安全策略不变;后续用户要求与 GDD 冲突时由 Supervisor 明示差异,不自动产生新批准版本。
2026-08-11 合入的动态目标验收图改变了本节的两条前提,M3 必须按新事实设计。
第二,approved GDD 不得自动 seed Goal Contract。GDD 是一次历史批准事实,Goal Contract 是 Supervisor 对当前这一轮用户意图的理解;由 Runtime 用 GDD 字段直接物化合同,等于让旧策划静默冻结新一轮目标,并且绕过了「Agent 必须自行理解用户真正要做的事」这条约束。GDD 只能作为上下文进入理解过程,合同内容仍由 Supervisor 产出。
第三,GDD 身份不能进 Acceptance Graph 的证据面。requiredEvidence 只接受 tool:<Runtime 工具名>,approvedGddRef、fingerprint、版本号与任何自然语言证据描述都不是合法取值;「与已批准 GDD 一致」这类标准无法用回执证明,不得写成 required 节点。GDD 与后续用户要求的差异可以记进 Goal Contract 的 openQuestions 或 forbiddenAssumptions,但记录差异不产生新的批准版本,也不改变 .agent/planning/** 的任何权威事实。
第四,goal_contract_acceptance_completion_blocker_at_locked 会阻断根 Run 的普通完成、finalization 与恢复。M0B-2 的前端投影按「root、main、动态 child 与 manifest 主任务全部终态且无 reconciliation」才归档阶段记录,验收图 blocker 只会推迟根终态、不会伪造终态,因此归档门本身不需要改;但 M3 接入时必须确认进度展示对「根 Run 长时间停在待核对」有合理呈现,不能让用户看到停滞而无解释。
18. 前端入口与交互合同
18.1 入口
- 仅首页“做方案”新建项目提交
standard + project-supervisor-plan(2026-08-13 按 D11 更正,旧值project-supervisor-plan-chat作废);“做游戏”和“做素材”保持autonomous-game-build直接开建。前端只提交 Supervisor 根 run 的身份,不提交也不感知策划子 Agent——后者由 Supervisor 在服务端通过agent.delegate派生,页面侧不得直接创建或引用它。 - 项目页新建、打开既有项目和 Godot 导入不新增“进入立项策划”入口,保持现行构建/打开语义;只有已存在 planning sidecar 或 active plan lineage 的项目恢复原有策划链路。
- 策划阶段聊天输入属于当前 run:有活跃决策卡/审批卡时,输入回到该卡片对应 action;无 active run 时才可创建新的 plan continuation。
- approved 后显示“做成游戏”。点击后读取当前有效的
game/fast_gdd.md,直接沿现有自动做游戏链路创建新的工作区、导入text/markdown参考附件,并以固定建造指令作为initialSupervisorMessage自动启动 Direct Codex;固定指令只说明 GDD 覆盖的栏目,不根据具体 GDD 内容生成总结。该动作不回首页等待二次提交,不把原项目的approvedGddRef复制到新项目。 - 用户仍可在项目工作台继续补充需求;普通首页“做游戏”入口的手动提交行为保持不变。
18.2 GDD 审批卡
审批卡直接从待审 gdd.v{N}.json 渲染:版本、短指纹、决定状态汇总、GDD 正文,以及“批准 vN”“修改”“退回重做”三项操作。revise/reject 要求多行原因;approve 原因可空。
卡片必须:
- 持有 Runtime 提供的 approvalRequestId;一次点击生成 responseId,busy/超时重试复用。
- command 进行中禁用重复点击;另一个窗口先决定后,当前卡刷新为
already-decided,不能覆盖。 recoveryPending=true时显示可恢复状态,只允许重试同一 ID,不允许提交新版本或启动构建。- 已渲染审批卡的后台 hydrate 仅更新权威投影,不因短暂的
hydrateBusy竞态禁用当前决定或已打开的评论弹层;只有 hydrate 返回的recoveryPending=true(或决定请求自身 busy)才阻止提交,并保留用户已输入的原因。 - 待审版本的 receipt 已存在时隐藏对应 stale pending 卡;hydrate 只恢复精确 project/session/run/action identity。
- approved 后只有在所有必需投影恢复完成时启用“做成游戏”;恢复态不提供该出口。
决策卡继续复用现有用户输入卡,不新建平行提问系统。现有完整构建 design 组用户名称改为“设计实现组”,与新阶段“立项策划”区分;内部 Agent ID 不改。
AI 游戏创作客户端壳继续遵守现行横屏工作台布局;GDD 中 desktop/mobile 是生成游戏产物的双视口合同,两者不是同一个 UI 尺寸要求。新增审批交互移动端优先、网页端可操作,不在面板内默认堆叠开发解释文案;独立详情应以弹层/独立面板打开,不能在当前卡下方无限追加。
18.3 权威 hydrate/read model
M1 必须新增 hydrate_game_creator_plan_gdd_state,作为前端读取策划状态的唯一 command;input 使用 deny_unknown_fields 且只能是 {projectPath: string}。projectPath 只用于经过权限策略验证的 transport 定位,不回传、不进入任何业务 DTO 或 fingerprint。前端不能扫描 .agent/planning/、解析 Markdown、读取 index.statusCache 或自行拼接 receipt/session 状态。
返回值是以下完整、无额外字段的 plan-gdd-state-view.v1:
type PlanGddStateViewV1 = {
schemaVersion: 'plan-gdd-state-view.v1';
projectId: string;
gddId: string | null;
state:
| 'not_started'
| 'draft'
| 'ready_for_approval'
| 'revision_requested'
| 'approved'
| 'rejected';
session: {
sessionId: string;
sessionRevision: number;
sessionFingerprint: string;
phase:
| 'collecting'
| 'awaiting_user_input'
| 'awaiting_gdd_approval'
| 'revision_requested'
| 'approved'
| 'rejected'
| 'recovery_required';
clarificationRound: number;
repairDepth: number;
accumulatedAgentMillis: number;
activeRunId: string | null;
awaitingAnswerFor: {
delegationId: string;
requestId: string;
questionId: string;
round: number;
} | null;
decisionStateCounts: {
confirmed: number;
defaultPending: number;
prototypePending: number;
};
} | null;
versions: Array<{
gddRef: { gddId: string; version: number; fingerprint: string };
status:
| 'ready_for_approval'
| 'revision_requested'
| 'approved'
| 'rejected'
| 'superseded';
approvalRequestId: string;
createdAtUtc: string;
decision: {
action: 'approve' | 'revise' | 'reject';
decidedAtUtc: string;
} | null;
}>;
displayGdd: PlanGddV1 | null;
pendingApproval: {
gddRef: { gddId: string; version: number; fingerprint: string };
pendingActionId: string;
actionFingerprint: string;
approvalRequestId: string;
sessionId: string;
runId: string;
} | null;
approvedGddRef: {
gddId: string;
version: number;
fingerprint: string;
} | null;
recoveryPending: boolean;
};
PlanGddV1 精确等于第 8.3 节完整 strict GDD;页面不得反向提交或裁剪后冒充权威对象。无 planning 目录时成功返回 not_started + gddId/session/displayGdd/pendingApproval/approvedGddRef=null + versions=[] + recoveryPending=false,且不得为只读打开创建目录。存在版本时 versions 为 1~128 项并按 version 升序;approvedGddRef 始终是最高有效 approve 版本,即使更高版本后来 revise/reject 或当前正在形成新 draft 也不被撤销。
command 的权威读取与修复顺序固定为:
- 验证 canonical project root、manifest 与 projectId;路径不安全或项目身份不符直接返回 typed error。
- 在项目锁内只枚举第 7 节 canonical GDD/receipt 普通文件名,不把 tmp、
.previous、index、Markdown、pending、audit、observation 或任意孤儿文件当事实。 - 对每个 GDD/receipt 做大小、strict schema/unknown field、canonical compact storage bytes、project/gdd/version/source/profile/run identity、typed fingerprint 和 receipt 引用验证;要求版本从 1 连续、同 projectId/gddId lineage、每版本至多一个 receipt、全 lineage 至多一个无 receipt 版本。
- 只从有效 GDD/receipt 推导 versions、approvedGddRef 和已提交版本状态。session 只决定尚未提交的 draft、active run 与待答问题,不得覆盖 GDD/receipt 结论。
- 以严格 session primary/previous 规则读取未提交解释状态;只有 receipt/GDD 已确定的 phase 前滚可以确定性修复 stale session,不能从聊天、Markdown、原始回答或版本摘要重造丢失的 draft decision。
- 最后才检查并按需重建 index、Markdown、planning pending、decision audit、terminal observation 和 session 已提交摘要。command 可以完成这些有限投影修复,但不得创建、覆盖或删除 GDD/receipt,不得启动/重试 Provider、创建 active run、增加轮次或替用户作决定。
顶层 state 按以下优先级确定,不能从 UI 徽标或 session 自述猜测:
- 存在唯一无 receipt GDD:
ready_for_approval。 - 没有待审 GDD,且有效 session 正处于
collecting | awaiting_user_input的 active draft:draft;此前 approvedGddRef 如有仍照常返回。 - 否则按最新 GDD 的 receipt:revise →
revision_requested,reject →rejected,approve →approved。 - 没有 GDD、但存在有效 session:
draft。 - planning 完全不存在:
not_started。
displayGdd 依次选择唯一待审 GDD、最高有效 approved GDD、否则最新 revise/reject GDD;没有 GDD 时为 null。它的 ref 必须逐项等于 versions 中对应项。pendingApproval 只有在“恰好一个严格有效 GDD 无 receipt”时非空,六个 identity 字段都从该 GDD 确定性复制或推导;pending 文件缺失时可据此重建,不能生成新 approvalRequestId。
receipt 永远压过 stale pending:一旦该版本存在有效 receipt,pendingApproval 必须为 null,即使 pending 清理、audit、observation 或 session 前滚尚未完成;清理失败只令 recoveryPending=true,绝不重新显示审批卡。session 声称 awaiting approval 但 receipt 已存在时,按 receipt 返回 state 并确定性前滚 session;stale index/Markdown 不参与 state,能修就重建,暂未修齐同样只返回 recoveryPending=true。recoveryPending 可以与 draft/ready/revision/approved/rejected 任一已验证 state 组合,不是第二套权威状态。
错误语义固定如下:
- 无 planning 目录是成功空 view,不是错误。
- unknown/newer schema 或 unknown field 返回
PLAN_UNSUPPORTED_SCHEMA,不得由旧客户端覆盖。 - GDD/receipt 非 canonical bytes、fingerprint 不符、引用损坏或不可变布局非法返回
PLAN_CORRUPT_AUTHORITY。 - project/source/profile/run/session identity 不符返回对应 mismatch;无法更精确分类的权威身份冲突返回
PLAN_CORRUPT_AUTHORITY,不得降级为空状态。 - active draft/run 的 session 缺失、损坏或分叉返回
PLAN_SESSION_RECOVERY_REQUIRED;不能凭已有 GDD 重置轮次。 - 两个无 receipt GDD、版本断档、同版本身份冲突或合法性无法唯一证明返回
PLAN_NEEDS_RECONCILIATION,不得猜测待审版本。 - 权威事实有效、只有派生投影暂未完全修复时返回成功 view +
recoveryPending=true,不能把已经 committed 的 submit/decision 报成失败。
返回值不包含绝对路径、Prompt/system messages、Provider metadata、API 配置、审批 comment 正文或内部诊断;versions.decision 只暴露 action/time。前端在项目打开、页面 reload、App resume、submit 返回后、decision checkpoint 完成后和 approval decision 返回后调用 hydrate;已有 runtime polling 只负责运行态和现役 user-input 卡片 transport,不得作为 GDD authority。submit/decision 结果与随后 hydrate 不一致时,以 hydrate 对权威文件的重验为准并显示 typed 恢复状态,不在前端乐观改写审批事实。
19. 安全不变量与构建验证边界
- 不放松
autonomous-game-build对user.input_request的现行禁等待防线;多轮策划只发生在 top-level standard plan run。 - (2026-08-13 按 D11 二次拆写,取代 2026-08-12 按 D9 的拆写) 做方案链路的工具边界分两层:Supervisor 根 run 以新可信 source
project-supervisor-plan承载入口,工具面按现役standarddeny-list 模型,其 Provider 不直接向用户提问,而是在认领策划子 Agent 的AGC_NEEDS_USER_INPUT_V1终态信封后,在自己的 runtime/session 上创建waiting-for-user-inputpending(复用 PR #165 中转链路,见第 1.1 节「D11 新拓扑」,不是 D9/D10 设想的「Runtime 直投」);策划子 Agent(agentId=project-planning,由 Project Supervisor 通过agent.delegate静态委派,不再由 ready-task 调度器启动)的 action 工具广告与执行双门都必须是 exact allowlist,MCP 为空。user.input_request在此拓扑下由执行层 Runtime 兜底拒绝,不是产品自律要求——委派子 Agent 天然带parent_agent_id/parent_run_id,validate_user_input_action_owner(apps/ai-game-creator-shell/src-tauri/src/user_input.rs:367-394)在落盘 pending 之前直接判定失败并拒绝。但广告层仍可见:build_agent_runtime_native_function_tools(apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs:279)没有agent_id参数、无法按身份裁剪;standardprofile 下tool_policy_snapshot.rs:144-147(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs)仍无条件把user.input_request列入auto_tools,模型看得见、会去调用,只是调用必然失败并转成一次失败的 tool observation。以上描述的是 M1 之前的现状,不是目标态(2026-08-13 更正:原文把「广告层仍放行」写成了回归须钉死的一半,与第 4.3 节「allowlist 的作用是让它在广告层就消失」直接冲突,两节互相引用却结论相反)。目标态是两层都拒:M1 的 exact allowlist 让user.input_request不再出现在策划子 Agent 的函数目录,执行层兜底继续保留。回归用例仍须同时钉死两半,只是第二半从「广告层仍放行」翻为**「广告层不再出现」**:① 策划子 Agent 的函数目录中不含user.input_request;② 即便绕过广告层伪造一次调用,仍被validate_user_input_action_owner拒绝。不能只测其中一半就当作已覆盖——两层是纵深防御,将来若有重构把广告层泄漏带回来,执行层兜底必须仍然成立。原 D10「排除是产品约束的自律要求而非 Runtime 兜底」的表述在新拓扑下已失效,见第 2 节 D10 作废条目。 .agent/planning/**与game/fast_gdd.md唯一写者是 Runtime;任何 Agent 通用写工具都不能触达。- GDD、receipt 追加不可变;index、session、Markdown 和 UI 只作有限投影,不能反写权威事实。
- approve receipt 是构建信任根;Agent 文本、“已批准”状态缓存、Markdown 徽标或 UI 内存都无批准权。
- 所有 mutation 在项目锁内重新核对当前 root/project/run identity;跨项目复制、旧 run、错误 source/profile/binding 失败关闭。
- 构建始终由用户动作启动;plan tool、approval observation 和 final reply 都不能直接播种 DAG。
正式任务 M0-3(PR 工作包标签 M0A-2)统一“owner 固定产物验证”与“可玩验收”的边界。真实新项目没有 package.json,默认 game/index.html 是无活动 <canvas> 的占位页;在 code-prototype 之前运行 project.verify 或 game.static_smoke 都不能验证策划/JSON,后者只会对尚未生成的占位游戏失败。测试不得用预写 fake_llm_game_draft() 的夹具替换这条真实初始化路径。2026-07-26 对 design-foundation 禁止 smoke、preview、进程工具和 game/index.html 写入的职责隔离继续有效,不被 M0-3 取代。
| Runtime 路径 | Agent / task | 验证边界 | 阶段职责 |
|---|---|---|---|
| 立项策划 | 立项策划 persona | command/smoke/preview 全部不可见且执行拒绝 | 无构建或验证职责 |
| 完整 16 任务 DAG | design-foundation、balance-seed、art-asset-plan、audio-asset-plan |
Provider 不获得验证工具;收束时由 Runtime 内部以 runtime.owner_artifacts_validate 验证 canonical 固定文件,只写普通 verifiedRevision |
pre-code owner 产物验真,不是可玩验收 |
| 完整 16 任务 DAG | art-director |
无 Editor Key 时只读;有 Key 时为条件 Canvas owner,成功 canvas.asset_generate 形成普通验证凭证 |
只交付并登记 assets/art-spec.png,无 smoke / preview 权限 |
| 完整 16 任务 DAG | code-prototype |
对真实可玩入口执行 game.static_smoke |
代码原型写入与本人可玩静态自检 |
| 完整 16 任务 DAG | preview-readiness |
以自己的 child run 对最终 project revision 执行 game.static_smoke |
正式最终静态验收 |
| 完整 16 任务 DAG | preview-playtest |
独立执行 preview.validate |
正式浏览器试玩验收 |
四个固定 owner 的 canonical 路径分别是 memory/project.md + game/game_design.md、game/balance.json、assets/manifest.art.json、assets/manifest.audio.json。同一映射必须同时驱动写入边界、完成检查与内部验证;验证要求有界读取、非空、JSON 可解析、无 incomplete marker,并相对根完成合同 baseline 已变化。内部验证不新增 Provider 可见 tool / commandId,不写 staticSmokeVerifiedRevision,不产生 smoke / preview trace;错误 source、delegated run、错误或终态 root、错误 parent/binding、跨 Agent/run 和非当前活跃根全部失败关闭,再次 mutation 必须使旧凭证失效。本阶段明确不扩到后置 publish-package;配置 Key 时的 UI 原型、透明图集、Canvas 登记和视觉验收仍是附加必需证据,不能被固定文件验证替代。
2026-08-12 记录一处尚未裁决的不变量冲突。本节第 2 条(plan source 的工具广告与执行双门只有四项)与 2026-08-11 合入的动态目标验收图存在结构性冲突。现行工具面是 deny-list 模型,可信 root Supervisor 默认持有 agent.goal_contract;而 plan source 按 exact allowlist 设计,不持有该工具。由此卡在两道独立的门:入口门 validate_root_goal_contract_control_plan_at 在通用 tool-plan 解析路径上强制「合同不存在时本轮必须是唯一的 agent.goal_contract 动作、且无 plan/plan_update/response」,判据既不看 source 也不看 Run Profile;出口门 goal_contract_acceptance_completion_blocker_at_locked 在 root project-supervisor + 可信 source 且合同不存在时返回 blocked,卡住完成、finalization 与恢复。同一批判据还决定 Goal Contract 的创建权限和 agent.goal_contract / agent.acceptance_update 是否被剥离。四个处置方案与代价见第 23.1 节的待裁决项;在裁决冻结前,本节第 2 条按「设计意图」保留,不得据此认为现行代码已经满足它。
20. rollout、停用与回滚
- M1 以 AppData/Runtime capability 开关启用,不把 feature flag 写进用户项目。
- 开关字段为 AppData 配置中的
planning.capabilityEnabled,默认true;关闭时不创建新 plan run、不接受新的 submit/decision mutation,也不执行 approval pending、session/index/Markdown/audit 的恢复写入。已有 sidecar 仅以只读 hydrate 视图保留,“直接开建”保持现状。 - 重新开启后从同一 gddId、版本、receipt 和 session chain 恢复,不重置版本。
- unknown/newer schema 只读失败,不由旧客户端降级覆盖。
- 任意冲突只前滚修复;禁止自动删除 planning 目录、重编号 GDD、改指纹、覆盖回执或抹去用户 comment。
- M0 文档回滚时,本方案、docs 索引与 decision log 必须同进同退,不能留下悬空引用。
- M1 已开始后,如需修改 durable source、路径、schema、domain、enum 或 ID 语义,必须新增 breaking-change 决策和迁移方案,不能静默改名。
21. 测试与故障注入矩阵
2026-08-13 按 D11 收口。 原横幅标记的
plan decision checkpoint/activeQuestion/checkpoint handoff相关必测项已随 D10 删除;下方 D11 必测项合并进正表。
| 委派链计数 | 沿 repair_of_delegation_id 推断 (repair_depth, clarification_round):澄清跳 round+1 且 depth 不变、返工跳 depth+1 且 round 清零;3 轮上限与 1 层返工上限各自的拒绝文案互不串扰;交替链 根→返工→澄清→再返工必须被拒(唯一能同时证伪「返工跳漏加 1」与「澄清跳误清零」的用例);环/悬空/超长一律 fail closed |
| 终态信封 | 合法信封解析;questions 非 1 题、选项数越界、header 超长、非法 JSON、字符串内含花括号不提前截断;达 round 上限后仍发信封则被拒并要求改为 plan.submit_gdd |
| continuation 幂等 | (parentRunId, delegationId, questionsSha256, answersSha256) 确定性派生;同一组问答重放不产生第二条委派;答案不同则身份不同 |
| 答案绑定 | 首次绑定成功;逐字相同幂等;绑到不同答案拒绝;非 NeedsUserInput 的 delivery 拒绝绑定 |
| 转述保真 | 问题正文/选项 label 被改写可检出;已确认答案未原样出现在 continuation task 可检出;task 超 4000 字符直接 Err 不截断。注意生产 standard 下这两项均只告警不拦截,回归须同时覆盖「告警但放行」与(若 M1 补上机制约束后)「拦截」两种期望 |
| 会话分离 | 用户问答物理落 Supervisor 会话文件;策划子 Agent 会话中 user-input 记录数恒为 0 |
| 层 | 必测合同 |
|---|---|
| schema | submit input + Provider session binding + Provider structured injections + hydrate view + 五个 durable/projection strict v1 struct;binding 的 rootAgentId 与末尾 fingerprint required,缺失/未知/重排失败关闭;structured injections 顶层、session、固定 PlanPlatformFacts 与四字段 observation 的 exact 顺序,null/空数组不省略,64 KiB 边界;unknown field/schema/enum;Agent 注入 Runtime 字段拒绝;ID/time/各类指纹形状;所有长度、数量、bytes 与 128 版本上限 |
| fingerprint | 本文 golden;中文/null/空数组/数组顺序;domain separation;任一 durable identity 变化;GDD/decision/receipt/session/pending/comment/Provider session binding/request context;typed binding 与 base request ID 都覆盖 rootAgentId;裸 action/profile/answers digest 不得带 typed 前缀 |
| create-only storage | temp 强杀无半 primary;no-replace 竞态;父目录同步;existing same replay/different conflict;symlink/reparse/目录/硬链接/路径逃逸 |
| session | revision/hash 合法 successor;missing primary 提升;primary corrupt fail closed;分叉 reconciliation;plan question strict mapping;sidecar absent/exact pending 的前置窗口;delivery 问题落盘后才展示;session appliedAnswers 与链路现算 clarification_round 不一致时以 delivery 为准并进 reconciliation、不得反写链路;pre-wait 的 v4 ready/0/单 approved member + sidecar pending + standalone absent 与 waiting 的 standalone exact 状态;answer-prepared 必须重验 waiting anchor;其它状态 fail closed;delivery 问题落盘 + waiting successor 不判 stale;答案绑回 delivery 即线性化(answersSha256 首次或逐字相同幂等、不同答案拒绝);不同 request 合法复用 answerResponseId;prototypeValidationItems/appliedAnswers 同异 replay;session 已落而 sidecar/observation 落后的恢复 |
| Provider binding | effective model/api kind/stream/当前 apiKind 生效的 official fallback/Anthropic strict/OpenAI Chat token field/output tokens/reasoning/verbosity/tool choice、messages/实际工具目录与 binding 来自同 captured session;composition=runtime、sourceKind=runtime,不得复制 agent-delegate;dedicated user message 精确两行且无尾换行,同一第二行 JSON bytes 生成 structuredInjections wire 摘要;tool-plan/final-reply/context-compaction/final-reply-context-compaction 均写 v3 binding,只有 tool-plan 可写 v4 batch,planning idle compaction fail-closed;不适用 adapter 字段为 null;任一实际语义字段变化均改变 requestContextFingerprint/base ID;plan base ID/attempt vector;旧 started 必须先 failed/interrupted 再开下一 attempt;损坏 binding 只 reconciliation |
| submit | durable request/context/session/batch/action binding;缺失 binding 时丢弃旧响应;sole-action v4 batch;main-loop 专用 commit 分支且零 approval waiting;同 submission + 同/异 payload;复用既有 Runtime ID/time;session CAS;决定元数据与 session 真相逐项一致;M1B-2 已有 GDD 时拒绝不同 submission;GDD 提交点前后强杀;提交后 index/Markdown/session successor/child terminal;generic v5/v4 anchors 都在时严格对账、单缺时确定性重建、双缺或漂移时 reconciliation |
| approval | 同 response 同/异决定;不同 response 同版本;不同版本合法复用 responseId;两个窗口并发;旧卡决定新版本;approve/revise/reject comment;approve 后显式 continuation 提交并批准新版本;响应丢失重试 |
| projection recovery | M1B-2 覆盖 index/Markdown/session successor/child terminal/generic v5+v4 anchors 每个断点恢复且零 planning pending;M1C-1 才覆盖独立 planning pending/audit/terminal observation/receipt 后 cleanup;现役 global pending v5 零迁移;恰一逻辑 audit/observation;无永久 stale 卡 |
| agent.db | 专用幂等 helper;same key conflict;日志达到普通容量、尾部截断与压缩后仍能补齐并保留决定记录 |
| source/security | durable exact identity;三个 action tool(file.read / file.list / plan.submit_gdd)广告与执行;MCP 空且 webSearchEnabled=false;control functions 单列;tool-plan/batch/ledger/context/repair/completion 全部跳过 collaboration;plan retry 保留 source/profile;除 Runtime-owned submit 外的副作用工具拒绝 |
| Prompt | 2026-08-13 按 D11 改写:不新增 composition,Supervisor 根 run 沿用现役 supervisor composition、策划子 Agent 复用 runtime composition(见第 4.2 节);decision-checkpoint 请求 kind 随 D10 作废(见第 5.1 节)。仍冻结:3 轮/单题/固定选项;回答后设计解释与 prototype item;平台事实;恢复摘要;direct-out 条件 |
| frontend | 首页“做方案”目录提交与 Enter 自动创建均为 standard + project-supervisor-plan;“做游戏/做素材”均保持 autonomous-game-build;项目页新建/打开不首次注入 planning;stable approvalRequestId/responseId;busy;stale card;hydrate strict input/view;无目录空态;receipt 隐藏 stale pending;corrupt authority typed error;project open/reload/resume/submit/decision 刷新;recovery pending;批准 GDD 后“做成游戏”直接创建自动工作区、导入 fast_gdd.md 并自动启动 Direct Codex;重复点击不重复创建;普通首页链路不回归 |
| M2 integration | explicit approved/direct mode;锁内重验 receipt;ref 贯穿 task/run/completion/context;无 ref 非回归;恢复不换稿 |
关键强杀点逐项覆盖:
- GDD temp 写入前、写入后、no-replace 发布前、发布后父目录同步前、提交点后。
- GDD 提交后、index/Markdown/pending/session 各投影之间。
- receipt temp 写入前、发布前、线性化点后。
- receipt 后,index/Markdown 前后;audit 前后;terminal observation 前后;session checkpoint 前后;command response 前。
- command response 丢失、原 run continuation 前、observation durable 消费后 pending 清理前。
- user-input request sidecar 前后、delivery 问题落盘前后、standalone waiting/pending/card 前后,以及 waiting durable 后;覆盖 sidecar absent/exact pending 与全部冲突状态,恢复必须保持原 question/request/action/provider identity。
- answer-prepared 后重验 waiting anchor;checkpoint lifecycle started 后、protocol repair 数组继承前后、success handoff 后、lifecycle completed 前、successor replacement H1/H2/Hn 每一链边、session CAS 写入 superseded 摘要前后、历史 handoff 清理前后,以及 appliedAnswers 已落但 sidecar/observation/batch cleanup 前;已被 session 摘要稳定收口的 checkpoint 不得再次调用 Provider。
共同验收:每个版本最多一份完整 GDD 和一份完整 receipt;相同逻辑决定恰一条 audit/observation;没有自动覆盖、重编号、删除历史或永久 stale pending;越过提交点的操作返回 committed/replayed 语义而不是假失败。
M0 文档 PR 本身最低验证:Markdown 结构与三张 Mermaid 图可解析;golden envelope bytes/hash 重算一致;相对链接存在;tracked diff 不包含仓库外依赖或本机路径;npm run check:encoding 与 git diff --check 通过。
22. 当前代码证据与 M1 接入点
以下行号基于 2026-08-10 当时基线;实施时必须打开源文件复核,不能只复制行号。M0 期间已合入 03a441027 无限画布、1363b9374 动态目标验收图与四个 M0 工作包,大部分行号已漂移,仅逐行标注 2026-08-12 复核的条目为准。
| 主题 | 当前证据 | M1/M2 要点 |
|---|---|---|
project-planning 身份登记(已落地) |
apps/ai-game-creator-shell/src-tauri/prompts/runtime/manifest.json(agentCatalog.planning)、build_support/runtime_prompt_bundle.rs、src/agent/runtime_adapter.rs、src/agent/prompt.rs、src/agent/generation/pass_artifacts.rs、src/agent/runtime_driver/task_start.rs、src/agent/runtime_tools/task_ops.rs、src/agent/runtime_tools/delegation.rs |
2026-08-13 已合入。登记为与 supervisor 平级、不进 groups 的独立条目,build.rs/种子 DAG/new_game_creation_app_seed_tasks() 一行未动。同批修掉「非 supervisor 即专业组成员」二分假设的四个受害点:角色身份合成(blocking,不修则委派第一轮即硬失败)、内存路径解析、恢复枚举漏收、task.create 静默兜底成 Design 组;并把 project-planning 排除出 agent.spawn_isolated 的合法模板集。回归见 project_planning_is_a_delegatable_identity_outside_the_seed_dag |
| trusted matcher 消费者 | 2026-08-12 复核为 10 个非测试文件、18 处调用:commands.rs(2)、agent/runtime_actions/project_gates.rs、agent/runtime_protocol/acceptance_graph.rs(2)、agent/runtime_protocol/goal_contract.rs(2)、agent/runtime_protocol/run_configuration.rs、agent/runtime_protocol/steering.rs、agent/runtime_driver/lifecycle_control.rs、agent/runtime_driver/task_start.rs(2)、agent/runtime_actions/provider_request_builders.rs、agent/runtime_protocol/autonomous_completion.rs(5) |
2026-08-10 记录的两个消费者已过期。agent_runtime_supervisor_source_is_trusted 现在同时是 run 启动门、steer 门、Goal Contract 创建权限、验收图完成门与根控制面工具授权的共同判据,都不看 Run Profile。M1 只拆 autonomous-only matcher 已不够。2026-08-13 裁决:plan source 进该 matcher;M1A-1 已逐点复核并落地:适用者保留 matcher、steer 独立否决(kind=plan-root-steer-unsupported)、autonomous 消费点已由 profile 挡住本包不改函数。复核表见 decision-log 2026-08-13 M1A-1 条。2026-08-13 复核调用数已增至 19 处,以当时源码为准 |
| Run Profile | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_adapter.rs:48-75、apps/ai-game-creator-shell/src-tauri/src/main.rs:1243-1244 |
复用 standard,不新增 profile |
| Supervisor start 校验 | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs、apps/ai-game-creator-shell/src-tauri/src/commands.rs |
M1A-1 已落地:plan 必须 standard;plan + autonomous-game-build 拒绝 |
| durable run binding | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/run_configuration.rs:72-125,226-374 |
source/profile/root/parent CAS 与恢复必须贯穿 |
| 前端提交链 | apps/ai-game-creator-shell/src/features/agent-runtime/model.ts:844-899、apps/ai-game-creator-shell/src/App.tsx:5743-5800 |
已有 source/profile DTO;后端仍须重验 |
| Prompt Bundle | apps/ai-game-creator-shell/src-tauri/prompts/runtime/manifest.json:30-74、apps/ai-game-creator-shell/src-tauri/build_support/runtime_prompt_bundle.rs:73-85,867-925 |
2026-08-13 改写:supervisorPlanChat / SupervisorPlanChat 随 D6 作废,不新增 composition 也不新增编译期 source kind(见第 4.2 节)。实际改动是 agentCatalog 新增与 supervisor 平级、不进 groups 的 planning 条目——已于 2026-08-13 落地,含结构体、validate_agent_catalog、render_agent_catalog 三处 |
| Prompt 请求/收尾 | apps/ai-game-creator-shell/src-tauri/src/agent/prompt.rs:551-630、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs:123-178,208-241,314-318 |
2026-08-13 改写:D11 下不按 source 分流 composition——Supervisor 根 run 沿用现役 supervisor composition,策划子 Agent 复用 runtime composition(见第 4.2 节)。仍成立的是:策划子 Agent 的 context 不读取/渲染 collaboration 或通用能力,其角色内容由 agentCatalog 条目的 brief 承载 |
| Provider request lifecycle / wire | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/models.rs:233-248、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_control.rs:316-343、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/real_e2e_checkpoint.rs:1073-1276、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs:1391-1451、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:897-922、server-rs/crates/platform-llm/src/lib.rs:55-75,167-180,2286-2349 |
exact plan 从同一 captured session 构造 request/context binding;v3 lifecycle 与 v4 batch 同 binding;最终 model/request 字段及 adapter wire 配置进入 context fingerprint;旧 attempt 先终结再重试;stale 合法前滚和 identity 污染分流,replacement base/attempt 确定 |
| Provider 工具广告 | apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs:279-314、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs:19-128 |
plan native 广告 exact allowlist,MCP 为空 |
| 执行 policy | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs:52-104,178-255、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/action_execution.rs:78-130 |
source-aware 广告/执行双门;action_execution 只保留防御,submit 实际执行必须在普通 dispatch 前被专用分支截获 |
| submit sole-action 语义 | apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs:317-470、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs:540-594 |
在 action plan/batch identity 持久化前拒绝 mixed submit 或多次 submit |
| Supervisor collaboration | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs:540-594,852-914、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:342-455,560-606、apps/ai-game-creator-shell/src-tauri/src/collaboration.rs:82-124,846-868 |
2026-08-13 方向反转:原表述「plan 跳过 collaboration 强制委派」在 D11 下不成立——Supervisor 在做方案链路的主动作恰恰是 agent.delegate。现行口径见第 4.3 节:Supervisor 根 run 不再要求跳过预检,但 policy 不得把做方案当 16 任务 DAG 的协作场景编排;策划子 Agent 侧 collaboration 完全不适用、collaborationPolicy 固定 not-applicable |
| provider batch ledger | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_batch_ledger.rs:130-230,319-372,432-464,545-598 |
validate/write/recovery 都不得为 plan 绑定或恢复 collaboration snapshot;submit sole action |
| user.input_request / 静态委派澄清中转 | apps/ai-game-creator-shell/src-tauri/src/user_input.rs:21-43,209-307,534-623,765-778,835-898、apps/ai-game-creator-shell/src/features/agent-runtime/model.ts:1164-1169、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/interaction.rs:444-548、apps/ai-game-creator-shell/src-tauri/src/tool_plan_handoff/model.rs:86-157、apps/ai-game-creator-shell/src-tauri/src/tool_plan_handoff/ledger.rs:126-205 |
现役 input 只有 questions;M1 保持 wire 与 answer responseId 兼容合同,plan 额外限制单题/固定选项/32 字符 ID,非 plan 行为不变。2026-08-13 作废后半段:「answer-prepared 后发起只含 strict update_agent_plan 的 checkpoint turn,success handoff 后 session CAS」是 D10「Runtime 直投」机制,D11 下该请求 kind 不存在——问题落 delivery、线性化点是答案绑回 delivery、解释与下一步由 continuation 子 Agent 的第一个普通 tool-plan turn 完成(见第 5.1 节);其专用 handoff 契约随之作废(见第 23.1 节) |
| 现役 pending wire | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs:26-27、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:3-46,219-270、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/pending_confirmation_ledger.rs:290-520 |
保持 game-creator-pending-action.v5;M1 另建 planning pending,不升级全局 wire |
| submit 专用提交/恢复分支 | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_recovery.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/recovery_scan.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs |
2026-08-14 按 M1B-2 收口:在普通 dispatch 前处理 Runtime-owned submit;提交后终止策划子 run,并保留 generic v5 standalone pending + v4 batch anchors,不复用 WaitingForUserInput,不创建 planning pending。审批等待属于 M1C-1 |
| JSON sidecar | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/json_sidecar.rs:44-104,122-250 |
现有 writer 可覆盖;不可变文件必须新增 no-replace helper |
| 项目锁 | apps/ai-game-creator-shell/src-tauri/src/project/write_lock.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs:1467-1505 |
所有 planning mutation 在同一项目锁内重读事实 |
| completion blocker | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_approval.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs |
M1C-1 隔离 worktree 已补齐:仅对 exact project-supervisor-plan standard 顶层根 Run 生效,读取 GDD lineage、approval pending/receipt、generic submit anchors、terminal observation、decision audit、planning session 与 recovery,且只读不创建 pending;现役 collaboration blocker 对策划子 Agent 仍不适用 |
| plan 根 run 子 Agent 创建面 | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/delegation.rs(observe_agent_runtime_agent_delegate / observe_agent_runtime_agent_spawn_isolated)、agent/runtime_protocol/run_configuration.rs(validate_project_supervisor_plan_root_binding_at)、agent/prompt.rs(game_creator_project_supervisor_tool_plan_prompt) |
2026-08-14 M1A-4 已落地:plan 根 run 只能委派 project-planning,agent.spawn_isolated 一律拒,两条通道共用 typed kind=plan-root-child-target-unsupported;plan source 下不拼 supervisorIntro 与 $visualContract。已知残留(有意保留,见 decision-log 2026-08-14 M1A-4 条):$base 的 $isolatedAgentTemplates 段仍会向 plan 根 run 列出全部专业角色名——那是 agent.spawn_isolated 的模板目录,因执行层硬拒而成为死文本;因此不得写「plan 根 run 上下文不出现其它 Agent 名」这类验收句 |
| plan retry | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs(resolve_game_creator_agent_runtime_retry_configuration_at)、runtime_driver.rs(supervisor_plan_root_identity_holds_at) |
2026-08-13 M1A-3 已落地 source 保源:task.source == project-supervisor-plan 时先走强判据,通过则保留该 source,失败 kind=plan-root-retry-identity-unsupported、不降级。gui/cli 仍走 agent-background-task。plan-session revision / gddId / 按 gdd-approval kind 禁 retry 仍属后续包(现役已拒 waiting-*) |
| planning hydrate command | apps/ai-game-creator-shell/src-tauri/src/commands.rs:856-875、apps/ai-game-creator-shell/src-tauri/src/main.rs:2178-2180、apps/ai-game-creator-shell/src/App.tsx:1550,1650,2957,3364,3702 |
新增单一 hydrate command/注册与前端生命周期调用;不把 runtime polling 当 GDD authority |
| agent.db | apps/ai-game-creator-shell/src-tauri/src/project/agent_db.rs:955-1034,1551-1605,1995-2088 |
普通 append 不满足 decision 幂等;新增专用保留入口 |
| 16 任务 DAG | server-rs/crates/shared-contracts/src/game_creation_app.rs:263-425 |
2026-08-12 复核 seed 仍是 16 个,M0/M1 不改拓扑;但固定 DAG 已不是完成语义的唯一来源,见下一行 |
| standard 路径 owner 产物验证 | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs:416-441 |
2026-08-12 复核:autonomous_owner_artifact_validation_available_for_run_at 四重绑死——owner agent 白名单、profile == autonomous-game-build、source == agent-ready-task-scheduler、且要求 parent_agent_id 为 Project Supervisor。standard ready-task 节点无 parent,第 436 行即不通过。做方案链路不能扩展该函数。2026-08-13 按 D11 收窄结论:策划 Agent 改为静态委派子 Agent 后,「产物是否交付」这件事已被静态委派内建的 expectedArtifacts 校验覆盖(存在性 + 非符号链接 + sha256),不需要再造一套;仍然缺的是语义级校验(JSON 可解析、非空、无 incomplete marker、相对 baseline 已变化),而 GDD 的内容正确性本就该由 plan.submit_gdd 的 strict schema 在落盘时把关,不由委派产物验证兜底。故本项范围收窄,不是整体另建 |
| 问询与直投 | apps/ai-game-creator-shell/src-tauri/src/user_input.rs:367-394,494-530,595-623,803-807、apps/ai-game-creator-shell/src/App.tsx 的 handleProjectSupervisorUserInput、apps/ai-game-creator-shell/src/project/conversation.rs:42 |
2026-08-12 复核:validate_user_input_action_owner 拒绝任何带 parent 的 run;会话文件按 agentId 分目录,消息归属完全由 pending owner 决定;一份 record 已经两路投影(会话消息 + observation)且有「observation 重算冲突」校验。直投只需让两路投影分别落到 Supervisor 与策划节点,前端可直接复用现有 Supervisor 问答通道。2026-08-13 补记:本行「直投」是 D9/D10 旧机制记录,已被 D11 取代(见第 1.1 节「D11 新拓扑」)——D11 不新造直投,改为复用 PR #165 已实现的 AGC_NEEDS_USER_INPUT_V1 终态信封 + Supervisor 中转链路;本行列出的 validate_user_input_action_owner 等机械证据本身仍成立,「前端复用现有问答通道」这一结论方向也不受影响,只是不再由「直投」这个已作废的机制名承载,本行未随批二重写,读者以第 1.1 节为准 |
| design-director 现状 | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs:70-85、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs:1480-1506 |
mutation owner 清单不含 design-director,因此当前落入只读协调 Prompt;M2 才确定性化 |
| scheduler delivery | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/delivery.rs:171-185 |
下游不能依赖普通 durable delivery,必须读权威文件/ref |
| owner 产物验证 | apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs、apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs |
M0-3 / M0A-2 让四个 pre-code fixed owner 复用 canonical 路径映射,由 Runtime 内部验固定产物;Provider 不见验证工具,code / preview 节点继续真实 smoke |
| approvedGddRef | 当前仓库无匹配实现 | M2 从 command 到 task/run/completion/context 全链新增 |
23. 分期门禁与完成定义
M0A-*、M0B-* 只是便于拆 PR 的工作包标签,不是新增里程碑;正式编号始终只有 M0-1~M0-4。
23.1 M0-1 / M0-2(PR 工作包 M0A-1)
- 本文、
docs/README.md、document map 和 decision log 同步合入。 - D1~D8、注册表、Prompt、submit/GDD/receipt/session 的业务语义、指纹、golden、create-only 算法、提交点、typed DTO/error、恢复矩阵和 M1 测试方向已形成阶段基线;checkpoint handoff 的私有 schema/path/ledger 排序作为 M1 详细设计与合入前置决策保留,不在当前非交付检查点内选择实现方案。
- 三张 Mermaid 图分别覆盖运行拓扑、版本/审批状态、提交/恢复时序。
- 合入只表示 M0A-1 非交付阶段设计检查通过,不表示 M0A、M1 入口门或 M0 全部完成,也不表示任何功能已上线。
M0A-1 是 M1 详细设计与技术 spike 的输入,不是功能交付。M1 可以与 M0-3 并行准备;但 checkpoint handoff 私有持久化决策未冻结前,对应 checkpoint 代码不得合入,M0-3 的 dated 决策、Prompt、source/tool policy 与测试未全部合入前,依赖 Fast GDD source/tool policy 的 M1 代码也不得合入主线。M0-3 采用 Runtime 内部 owner 验证,不为未来 plan source 暴露 command、smoke 或替代验证工具。M0-4 不阻塞 M1/M2,仅阻塞 M3-4 和“M0 全部完成”;不得因为并行关系把工作包标签误报为阶段完成。
⚠️ 以下四方案分析已于 2026-08-12 随 D6 作废,保留仅作推导记录,不再是待裁决项。 四方案的共同前提是「策划 run 自己就是那个需要豁免的 root
project-supervisorrun」;D9 之后 root 是未变的 Supervisor,策划是独立agentId的下游节点,四选一问题消解(方案 D 的方向被 D9 实质采纳)。现行结论见本小节末尾「2026-08-12 替换结论」与第 1.1 节。 其中方案 C 不可行的论证(验收图强制 required 节点 + evidence 必须命中 18 项工具集)仍然有效,是理解第 1.1 节第 2 条约束的背景。
2026-08-12 新增第二项 M1 合入前置决策:plan source 与 Goal Contract 协议的关系。(2026-08-13 已裁决关闭:project-supervisor-plan 进可信 matcher、正常参与 Goal Contract 协议,原阻塞理由随 D11 拓扑失效;详见本节下方「仍然保留的 M1 入口前置决策」中该条。以下段落保留作推导记录。)
具体卡在两道互相独立的门,判据不同,必须分别处置:
- 入口门
validate_root_goal_contract_control_plan_at(agent/runtime_actions/autonomous_policy.rs:171,由provider_tool_plan.rs:434在通用 tool-plan 解析路径上无条件调用)。判据只有「agent_id是project-supervisor+ binding 的 root 是自己 + 存在 run profile binding」,既不看 source 也不看 Run Profile。合同不存在时强制本轮恰好一个agent.goal_contract动作,且plan_update为空、legacy plan 为空、response为空——plan run 第一轮无论发决策卡还是回复都被拒。 - 出口门
goal_contract_acceptance_completion_blocker_at_locked(agent/runtime_protocol/acceptance_graph.rs:568)。判据是 root project-supervisor + 可信 source,合同不存在即blocked,卡住普通完成、finalization 与恢复。
四个候选方案与已知代价:
| 方案 | 做法 | 已知代价 |
|---|---|---|
| A. plan source 不进可信集合 | 为 plan 另开平行的启动/steer 门 | 只解除出口门,入口门照卡——入口门不看 source。仍须额外改 validate_root_goal_contract_control_plan_at 或让 plan 完全绕开通用 tool-plan 解析路径(但该 validator 位于 parse 的 and_then 里,是最通用的位置)。另外 commands.rs 的启动与 steer 两处都要改,从此存在两套 source 信任判据,后续新增可信消费者容易只接一套 |
| B. 进可信集合但按 profile 豁免 | 在入口门、Goal Contract 创建、出口门、steer 四处加 standard + plan source 豁免 |
豁免点分散且会继续增加;必须同时冻结「可信 root ≠ Goal Contract 参与者」这条新不变量,否则下一个消费者默认把 plan 当参与者 |
| C. plan 接纳 Goal Contract 协议 | 把 agent.goal_contract 纳入 plan 工具面(等价于放弃 allow-list、改用现行 deny-list 模型) |
只解除入口门,出口门在验收阶段照卡,且更难救。validate_goal_contract_acceptance_graph 强制合同至少一个验收节点、至少一个 required 节点、每个 required 节点至少一项 requiredEvidence,且 evidence 必须命中 agent_runtime_acceptance_evidence_tools() 的 18 项;出口门再要求每个 required 节点在当前 project revision 下有真实成功动作回执。这 18 项全是项目读写/命令/预览/生成类工具,策划 Agent 按第 1 节目标 2 一项都不该有,.agent/planning/** 又由 Runtime 写、不产生 Agent 回执,因此合同必然带一个永不 passed 的节点。要救须把 plan 工具加进 evidence 集合并追加 agent.acceptance_update(独占一轮,与第 12 节 submit sole-action 和第 13 节决策卡流程冲突),且上游已明令禁止「用无关成功动作自证」。此外与第 4.3 节 exact allowlist、第 19 节第 2 条、第 24 节「plan source 无……」直接冲突,GDD 与 Goal Contract 语义大面积重叠。判定为不可行,保留在表内仅作已排除记录 |
D. plan root 改用独立 agentId |
不再复用 project-supervisor |
入口门、创建权限与出口门三处同时自动不适用(三者都要求 agent_id == project-supervisor),是唯一一次性解耦的方案;但直接推翻第 4.1 节已冻结的 agentId=project-supervisor,牵动顶层通道假设、前端 hydrate、run lineage 与「每项目最多一个 active plan run」的判定口径,须先重开第 4.1 节 |
因此真正的分岔不是 allow-list 与 deny-list,而是策划 run 是否应当是一个 root project-supervisor run:三处判据的唯一共同项是 agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,方案 D 一次性解耦全部三处且不需要在他人协议里挖豁免,方案 B 需要四处豁免并随消费者增加持续维护。
(以上为作废的四方案推导记录。)
2026-08-12 替换结论
D9 之后,root 是未变的 Project Supervisor,它在 standard 下天然持有 agent.goal_contract(deny-list 模型,且 root_control_authority 为真时不被剥夺),四方案作废。但入口门仍然适用——validate_root_goal_contract_control_plan_at 无 run profile 判断,standard root Supervisor 同样受约束。因此三条已实测约束取而代之,完整表述见第 1.1 节「Goal Contract:四方案作废,替换为三条已验证约束」:
- Supervisor 第一轮必须且只能提交
agent.goal_contract;做方案场景可满足。 - 验收
requiredEvidence锚定game/fast_gdd.md配tool:file.read;.agent/planning/**的写保护必须用只挡写的独立判据,不得加入reject_agent_runtime_private_control_path。 - Supervisor 取证必须在 GDD 落盘之后、且在交付审批卡之前(后半句 2026-08-13 补充,见第 13 节「审批前置门」)。
M1 入口前置决策(2026-08-13 起全部关闭,以下逐条保留处置记录):
-
checkpoint handoff 私有持久化——2026-08-13 随 D10 一并作废,无需裁决。 本项待的是「plan-decision-checkpoint这份 Provider 响应的专用 handoff schema / path / requestSlot / ledger 排序语义」。该请求 kind 是 D10「Runtime 直投」的组成部分——策划节点持续存活于同一 run,用户回答后 Runtime 在同一 run 内再发一次专用请求让 Agent 形成设计解释,那份响应需要自己的持久化契约。D11 下策划子 Agent 是以终态信封退出来提问的,该 run 随即结束,解释与下一步由 continuation 子 Agent 的第一个普通 tool-plan turn 完成(见第 5.1 节),专用请求 kind 不复存在,它的专用 handoff 自然也不需要。两条核实依据:① 现役
tool_plan_handoff::lookup_at(root, agent_id, run_id, &response_identity)(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs:367)按(agentId, runId)寻址、与 agent 身份无关,任何 Agent 的普通 tool-plan 响应都已被它覆盖,continuation 子 Agent 的首轮不需要新机制;② 第 8.6 节为该方案预留的supersededCheckpointHandoffs全仓库零代码引用,是纯设计构想,作废不产生迁移成本。仍然成立、且与本项无关的是通用 handoff 安全边界:任何 Provider 成功响应都必须先过现役 storage 的大小、控制字符、敏感键、绝对路径、容量与 durable identity 门并落盘,才允许被消费;storage 门拒绝或 handoff 无法 durable 提交时,禁止保存不安全正文、补 lifecycle completed 或自动重发。这是现役机制,策划链路照用,不需要额外裁决。
-
plan source 与 Goal Contract 协议的关系——2026-08-13 已裁决:project-supervisor-plan进agent_runtime_supervisor_source_is_trusted;做方案链路正常参与 Goal Contract 协议,不做豁免。原「裁决冻结前依赖 plan source 可信身份的 M1 代码不得合入」的硬门随本裁决解除。原阻塞理由已失效:2026-08-12 排除该方向的依据是「出口门在验收阶段照卡且更难救——合同强制至少一个带
requiredEvidence的 required 节点,而策划 Agent 按设计一项证据工具都不该有,.agent/planning/**由 Runtime 写、不产生 Agent 回执,必然留下永不passed的节点」。该推理写于 D6/D9 拓扑,当时「plan run」就是策划 Agent 本身。D11 拆成两层后,Goal Contract 与最终语义验收都属于 Supervisor 根 run;策划子 Agent 负责提交结构化 GDD,不负责替根判断“是否服务用户意图”。M1C-2a 因此把 Fast GDD 特例收紧为只接受当前 Supervisor 根 run 自己的file.read回执,不再沿用通用 Acceptance Graph 对同根 child evidence 的宽松能力。Supervisor 的 standard 工具面已有file.read/agent.acceptance_update,固定节点可正常收敛,同时不扩大策划子 Agent 的工具面。三道门逐一可满足:入口门只规定时序——合同不存在时本轮必须且只能是一个
agent.goal_contract,因此做方案链路的 turn 1 冻结合同、turn 2 才发委派,多一次 Provider 调用而已,不是阻塞;内容门要求 ≥1 个 required 节点且requiredEvidence命中白名单,tool:file.read满足;出口门要求合同已冻结且验收图收敛,两者都能达成。落地前必须补的功课:
agent_runtime_supervisor_source_is_trusted现有约 19 处生产消费点,同时承担 run 启动门、steer 门、Goal Contract 创建权限、根控制面工具授权与验收图完成门等多种语义。本裁决只确定「plan 进入该 matcher」,不等于每个消费点对 plan 的语义都正确——M1 落地前须逐点复核,凡语义不适用的必须单独收窄,而不是靠 matcher 一刀切。其中 steer 门已由同日「plan 根 run 不允许 steer」裁决单独否决,且明确要求实现为独立于本 matcher 的显式否决。 -
做方案链路的验收图取自固定的 Fast GDD 合格标准,不由 Supervisor 每轮自由发挥(2026-08-13 同批裁决)。理由:一份 GDD 是否合格与它描述的是什么游戏无关——变的是游戏概念,不变的是「要有哪些字段、每个字段什么约束」。因此「验收标准必须在产物不存在的 turn 1 冻结且不可改」不构成矛盾。分工固定为:
plan.submit_gdd的 strict schema 承担机器可判的字段存在性与字段约束(oneLiner长度、pillars条数、MVP 系统条数等,见第 8.2 / 8.3 节),这是硬校验;- Goal Contract 验收图承担「产物确实服务了用户这次的意图」,由 Supervisor 以
tool:file.read对game/fast_gdd.md取证后确认节点。
Goal Contract 中按项目变化的只有
outcome/nonNegotiables/forbiddenAssumptions/openQuestions四项——它们承载入口门 Prompt 要求的「自行理解用户真正要做的事」;preferences在该 source 下固定为空数组,acceptanceNodes固定为唯一fast-gdd-serves-intent节点,不算「照抄固定信号代替理解」,因为「什么算一份合格 GDD」本就不是本轮需要理解的东西。其它可信 source 继续使用现役动态 Goal Contract,不因 criterionId 恰好同名而套用 Fast GDD 的路径、actor 或分页特例。本条不改变 D1(游戏支柱按项目生成 2~4 条,四个候选不固定套用)与 D8(GDD 不含引擎字段,平台事实由 Runtime 注入)已冻结的口径;验收图据以生成的合格标准以本文档第 5.3、8.2、8.3 节为准。
-
策划节点——2026-08-13 已裁决冻结:策划子 AgentagentId/ source 命名agentId=project-planning;Supervisor 侧承载「做方案」入口的新可信 source 取全新常量名project-supervisor-plan(不沿用历史project-supervisor-plan-chat字面,避免语义改变后接错旧代码路径)。第 1.1 节批二不再被本项阻塞,改由下方新增的build.rs一致性待裁决项阻塞。 -
plan run 是否允许 steer——2026-08-13 已裁决:不允许。 plan 根 run 不接受 steer 替换协议。裁决理由:steer 替换会终止旧根、另起 replacement root run,而 D11 把策划的轮次预算挂在了委派链上——
clarification_round沿repair_of_delegation_id上溯推断,且每一跳都强制parent_run_id等于当前根 run(见第 4.1 节与第 23.5 节)。换根之后parent_run_id变了,旧链的 continuation 会被跨 run 隔离判据直接拒绝,这条策划链路剩余的问询轮次全部作废,用户已经回答过的内容也无法续接。这不是"交互未验证",是确定性的能力损失。实现要求(重要,不能靠副作用实现):steer 资格判据所在函数是
goal_contract_root_steer_task_at(apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/steering.rs:747,判据在 763 行;2026-08-13 订正:原文误记为同文件 714 行的goal_contract_root_steer_replacement_run_id,行号对但函数名错),它现在用的是agent_runtime_supervisor_source_is_trusted(&task.source)——与 Goal Contract 创建权限、根控制面工具授权是同一个 matcher。因此不得用「不把project-supervisor-plan加进该 matcher」来实现本裁决:那会连带否掉 Goal Contract,正好撞上本节尚未裁决的「plan source 与 Goal Contract 协议的关系」。本裁决必须实现为一条独立于可信 source 判定的显式否决:steer 入口识别出 plan 根 run 后直接拒绝并返回 typed 错误,无论该 source 是否在可信 matcher 内。回归用例须同时覆盖「plan source 在 matcher 内」与「不在 matcher 内」两种情形下 steer 均被拒。产品侧替代路径:用户中途要改方向,走既有的两条——本轮问询里回答/自由填写来纠偏;或在 GDD 审批卡上
revise/reject。都不行时放弃本轮、重开一条 plan lineage(第 4.1 节的PLAN_ACTIVE_RUN_EXISTS约束保证同一时刻只有一条非终态 lineage)。 -
——2026-08-13 机制与 M1A 基础代码已落地(见第 3.1 节):project-planning的编译期 agentCatalog 登记方式与build.rs一致性校验project-planning在agentCatalog下登记为与supervisor平级、不进groups数组的独立条目,specialist_nodes只读manifest.agent_catalog.groups[].roles[](build_support/runtime_prompt_bundle.rs:387-398),因此build.rs的validate_seed_task_catalog(apps/ai-game-creator-shell/src-tauri/build.rs:23-46)、16 任务种子 DAG、new_game_creation_app_seed_tasks()均不需要改动。Prompt Bundle 已登记并注入 planning role brief,prompt.rs已补齐project-planning角色 overlay;本段只代表身份/brief 基础可执行,不代表plan.submit_gdd或 GDD 存储/审批闭环已完成。
23.2 M0-3:统一 owner 产物验证与可玩验收边界(PR 工作包 M0A-2)
以 2026-08-11 dated 决策确认四个 pre-code fixed owner 由 Runtime 内部验证 canonical 正式产物,Provider 不获得新工具或 commandId;code-prototype / preview-readiness 保留真实 game.static_smoke,preview-playtest 保留独立浏览器验收,后置 publish-package 不在本阶段扩展。实现与回归必须覆盖真实新项目占位入口、canonical owner 路径矩阵、mutation 后凭证失效、恢复和错误 source/run/root/parent 失败关闭,以及 art-director 有/无 Editor Key 的条件 Canvas owner 分类。只有文档、Prompt、工具广告、执行 policy、完成门与上述测试全部通过并合入,才可把 M0-3 计入“M1 入口门完成”;M1 plan source 本身仍属于后续功能实现。
23.3 M0-4(PR 工作包 M0B-1 / M0B-2)
2026-08-12 收敛 M0B-2 遗留的唯一挂起项——manifest 无 root/run 绑定,无法安全判定跨轮残留状态的归属。处置为三项:确立“manifest 非轮次身份权威源”的不变量(见 §19);补齐阶段归档中终态会话同步异步捕获缺失的 root 身份校验,使四处快照写入点身份口径一致;以回归钉住“当前 main 未终态时 manifest 不参与裁定”这条既有但零覆盖的边界。明确保留并记录的残余风险是:当前 main 到达 Runtime 终态且非 failed/cancelled 后 manifest 被逐字采信,若本轮 manifestInvalidated 尚未落地,跨轮残留的 failed 仍可能被显示为本轮结论;该窗口实际宽度未量化,三条候选机制(时间戳新鲜度门控、root-scoped 永久缓存、manifest 补身份字段)经审查均不可安全落地——分别因跨端时间戳单位错配导致门控恒真、缓存语义与后端“每次重新校验”哲学相悖、以及漏掉第二条 status 写入路径——故本阶段只记录不实现。回归用例中该残余风险的断言即裁决锚点,改动它意味着重新裁决。重新裁决触发条件见 decision-log 2026-08-12 条。该挂起项至此已处置,不再阻塞 M0-4 收口。
23.4 M0 完成状态(2026-08-12)
2026-08-13 更新:
M0A-3已收口,M0 全部完成。 2026-08-12 记录的「文档部分待修订」已处置完毕:批一(D9 二次作废 / D10 作废 / D11 入表)、批二(拓扑与工具面)、四之余(schema 与 golden vector 收口)三批全部完成,详见第 23.6 节。第 9.1 节 golden vector 已按 D11 identity 重新生成并自洽(3857 bytes /a59856de7e…)。因此三种阶段表述现在全部成立:M1 入口门完成、M3-4 入口门完成、M0 全部完成。三个代码工作包的完成状态自始未受 D6/D9/D11 影响,无需回退——全仓库检索确认 M0 无一行代码按 D6 编写。
M0 代码工作包全部完成。 M0-1~M0-4 四个正式任务、对应的 M0A-1 / M0A-2 / M0B-1 / M0B-2 四个工作包及各自门禁均已通过,全部落在 M0 集成分支 feat/five_min_design 并已推送。
此处「合入」指合入 M0 集成分支,不是合入 master:M0 按整体切片交付,不逐工作包直接进 master,因此不得把「未进 master」当作 M0 未完成。M0 期间该分支已两次合入上游 master(含 03a441027 无限画布、1363b9374 动态目标验收图、51e35468a 自主构建测试锁竞态修复),origin/master 已是分支祖先。
M0 收口时的三项已知残留,均已定性且不阻塞后续阶段:
- 第 23.3 节记录的 manifest 新鲜度残余风险,只记录不实现,回归用例中的断言即裁决锚点。
set_task_status无秩序守卫的无条件覆盖,属 master 既有问题,另行提 issue,不在 M0 范围内修。- 第 23.1 节的 M1 入口前置决策:2026-08-13 起为空。三项当日全部关闭——「plan source 与 Goal Contract 协议的关系」裁决为进可信 matcher;「plan run 是否允许 steer」裁决为不允许;「checkpoint handoff 私有持久化」随 D10 一并作废、无需裁决。M1 不再有未决的合入前置决策。
M0 完成不表示完整策划闭环已经上线。M1A-1~M1A-4、M1B-1、M1B-2、M1C-0 与 M1C-0b 已合入,其中 .agent/planning strict schema/版本链已由 M1B-1 提供;M1B-2 已提供 plan.submit_gdd、exact planning Provider binding/structured injection、专用提交点与崩溃恢复,M1C-0b 已为既有静态委派读路径补未知 durable status 的显式保留与保守阻塞,审批 pending/receipt、前端入口和构建准入仍待后续 M1C~M1E 工作包。第 19 节第 2 条中关于工具面与执行拒绝的目标不变量已由 M1A-2/M1A-4 覆盖,其余 GDD 审批不变量仍按第 23.8 节推进。
23.5 WP1 / WP2:静态委派澄清轮次与返工深度拆分(2026-08-13 完成)
这是 D11 的强制前置(见第 1.1 节「D11 新拓扑」第 5 条)。它不属于 M1~M3,改的是 master 已发布的 PR #165 静态委派澄清中转机制,服务对象也不止策划链路;因此单独立包,不并入 M0/M1 的门禁。
问题:repair_of_delegation_id 一个字段同时承载「质量返工」与「澄清 continuation」两种语义,validate_static_delegate_repair_request_at 用同一道「深度最多为 1」的门对两者无差别拒绝。后果不只是「第 3 轮不可达」——一次澄清会耗尽整条委派链唯一一次质量返工额度。
定稿语义(权威定义见 decision-log 2026-08-13 条):repair_depth 与 clarification_round 拆成两个互相独立的维度,且都是运行时沿链推断的派生值,不新增持久字段。
- 分类判据(唯一权威):
parent.structured_result.contract_status == NeedsUserInput⟺ 该跳是澄清 continuation;否则是质量返工。 - 传播:根节点
(0, 0);澄清跳round += 1且depth不变;返工跳depth += 1且round重置为 0。 - 单链总跳数上界
1 + 2 × 3 = 7跳、8 条 delivery 记录。注意是 7 不是 6——连接两层的返工跳本身也算一跳。
为什么不加持久字段:给 StaticDelegateDeliveryRecord 新增 repair_depth 并用 #[serde(default)] 兜底,会让磁盘上已有的返工记录读出 0,深度门失效,「返工的返工」漏洞原样复活——方向是 fail-open,不可接受。链上推断对历史记录是精确而非仅保守:PR #165 之前不存在 NeedsUserInput,旧记录天然被正确分类为「非澄清」。
落地:static_delegate_lineage_counters(apps/ai-game-creator-shell/src-tauri/src/delegation.rs)沿 repair_of_delegation_id 上溯到根后正向重放,带环检测与跳数上限,异常一律 fail closed;判据抽成与 validate_static_delegate_clarification_continuation_at 共用的小函数,避免两处口径漂移。validate_static_delegate_repair_request_at 的函数签名未变;同级返工门(同一 delivery 最多一个非 suppressed 子请求)未动;STATIC_DELEGATE_DELIVERY_SCHEMA_VERSION 与 new_static_delegate_delivery_with_contract 签名均未动。
门禁与完成定义:
- 返工门错误文案必须原样保留(现有回归断言了该字符串),澄清上限用一条明显不同的新文案,且用例显式断言两个维度的文案互不串扰。
- 必须继续绿的回归防线:「返工的返工必须被拒」、
project_supervisor_static_delegate_repair_is_single_bounded_wave、单跳澄清基准。 clarification_continuation_chain_supports_multiple_rounds的断言方向反转属有意的行为变更,测试内注释须写明;反转的同时新增对(depth, round)取值的正向断言,不得只删旧检查。- 新增用例须走真实
observe_agent_runtime_agent_delegate生产路径,不得只调校验函数。其中交替链用例是必需项:根 → 返工(depth=1)→ 澄清(depth 仍为 1、round=1)→ 再返工(必须被拒);它是唯一能同时证伪「返工跳漏加 1」与「澄清跳误清零」两种失误的用例,而改造前的测试要么纯返工链要么纯澄清链,从未覆盖交替。 - 另需覆盖:第 4 轮边界、返工重置澄清轮次、澄清不占同级返工门配额、历史记录分类、跨 run 隔离、source 区分上限。
完成状态:上述门禁全部通过,WP1 生产代码与 WP2 回归已合入本分支。已知遗留:project_supervisor_concurrent_repair_dispatch_creates_exactly_one_delivery 是 Barrier 同步的并发用例,在本机 Windows 上间歇失败;经与改造前基线对照(同一用例各跑 6 次,改造前后同为 1 次失败)确认为既有抖动,不是本工作包引入,不作为回归处理。
23.6 后续执行计划(2026-08-13 起;2026-08-15 状态更新)
本节记录 D11 之后的执行顺序与各步状态,避免「设计结论持续更新、但做到哪了无人维护」。
| 步骤 | 内容 | 状态 |
|---|---|---|
| 一 | M0A-3 批一:D9 二次作废 / D10 作废 / D11 入表、第 1.1 节新拓扑段、第 19 节第 2 条与第 24 节第 8 条改写、decision-log 补记 |
已完成(见第 1.1 节批一执行状态) |
| 二 | WP1 + WP2:澄清轮次与返工深度拆分及其回归 |
已完成(见第 23.5 节) |
| 三 | project-planning 的 agentCatalog 登记 |
已完成(机制冻结见第 3.1 节;代码亦已落地,2026-08-13:manifest、prompt bundle、runtime adapter、prompt.rs 角色合成分支及四处 needs_change 全部合入) |
| 四 | M0A-3 批二:拓扑与工具面部分 |
已完成(2026-08-13),拆解见下 |
| 四之余 | schema 与 golden vector 收口 | 已完成(2026-08-13),拆解见下 |
| 五 | M1 本体:策划闭环功能实现 | M1A-1、M1A-2、M1A-3、M1A-4、M1B-1、M1B-2、M1C-0、M1C-0b、M1C-1、M1C-2a、M1C-2b 已落地;M1C-2b 已实现 planning 澄清中转、三轮确定性 session/continuation 投影、审批后用户修订谱系、Provider 活跃时间 usage fact/fold 与末次 submit usage 收口,并已快进合回 feat/five_min_design,11 条 planning 澄清定向回归及关联 Rust 门禁通过。审批 UI、hydrate、构建准入与下游完整构建仍未完成,因此 M1 整体仍不可交付。合入门见第 23.8 节 |
批二在 2026-08-13 拆成两半,因为其中一半在 M1 代码存在之前做不完:
已完成——第 3 节注册表按 D11 更正(策划子 Agent 的 durable source 由 agent-ready-task-scheduler 改为 agent-delegate;删除随 D10 作废的 plan.request_decision;Prompt composition 由冻结值 projectPlanning 改为「不新增、复用现役」,消解了与第 3.1 节的自相矛盾;run profile 的成立理由改为委派父子继承);第 4 节拓扑图重画;第 4.1 节按「Supervisor 根 run + 策划子 run 两层」整节重写;第 4.2 节整节重写(supervisorPlanChat / SupervisorPlanChat 一并作废);第 4.3 节按两层工具面整节重写,并修正了原文「明确禁止 agent.delegate」与 D11「Supervisor 在做方案链路的主动作恰恰是委派」之间的方向性冲突;第 5.1 节对话循环四条按 D11 重写;第 18.1 节前端入口 source 更正;第 22 节 Prompt 行更正。
四之余(2026-08-13 完成)——第 5.2 节后半整段重写;第 8.3 / 8.4 / 8.6 节 identity 块按 D11 补 agentId / rootAgentId / rootRunId / delegationId,第 8.5 / 13 节的 source 定为 project-supervisor-plan(审批在 Supervisor 侧决定)、第 8.3 / 8.4 / 12 节定为 agent-delegate(提交在策划子 Agent 侧);第 8.6 节删除 activeQuestion / roundsUsed / supersededCheckpointHandoffs,appliedAnswers 收窄并改挂 delegationId / continuationDelegationId;第 9 节删除 checkpoint domain 与 supersededCheckpointProviderRequestIds、request-id value 补 rootRunId / delegationId;第 12 节重写消费证明三项与 stale 状态机、删除 checkpoint 整段;第 14 节删除十行 checkpoint/activeQuestion 恢复语义,另立四行 D11 语义加一行通用 handoff 安全边界;第 6 节 Prompt 权威稿改为策划子 Agent 的角色 brief 基线并把提问改为终态信封;第 18.3 节 hydrate read model 的 roundsUsed / activeQuestion 改为 clarificationRound / repairDepth / awaitingAnswerFor;第 21 节补六类 D11 必测项;第 24 节线性化点不变量改写。
第 9.1 节 golden vector 已重新生成:3857 bytes、sha256-serde-json-v2:a59856de7e…。生成方式与「必须由代码生成、不得手写」的核对——先用旧的 3707 bytes 重算,确实得到旧值 d85c85dae3…,证明本节 canonical bytes 就是被哈希的原文、不存在额外序列化中间层;再对按第 8.3 节新字段顺序重建的 identity 块取 SHA-256。game / decisions / prototypeValidationItems 三段逐字节未动,已断言验证。
订正一条此前的判断:本工作包一度被记为「前置是 M1 的 strict schema 实现(届时才有能生成指纹的代码)」。该判断不成立——字段声明顺序本就由第 8.3 节冻结、canonical bytes 本就在第 9.1 节,改字段等于改这串字节再重算,不需要等 Rust 结构体存在。真正需要核实的是指纹取的是哪种序列化:仓库里 json! 构造(BTreeMap,键排序)与 serde_json::to_vec(&结构体)(字段声明顺序)两种模式并存,而第 9 节明确「不允许以 map 重算」,故取后者;确认 serde_json 未启用 preserve_order 后,两条路径的判别与重算方式都已闭合。
第四步的范围纪律:批二与四之余都是把小节从 D9/D10 旧拓扑改写到 D11 新拓扑,不是实现功能。
第五步开工前必须先处置的事项,按性质分两类:
一、身份可执行性缺口(第 3.1 节「登记前必须先处理的代码改动」,仅 catalog 登记不足以让静态委派跑起来)——其中角色身份合成缺口为 blocking,另有若干 needs_change 项与一条既有测试的期望集合需同步更新。清单以第 3.1 节为准,本节不重复。
二、既有待裁决项(第 23.1 节)——2026-08-13 起为空,M1 不再有未决的入口前置决策。 三项分别于同日关闭:「plan run 是否允许 steer」裁决为不允许;「plan source 与 Goal Contract 协议的关系」裁决为 project-supervisor-plan 进可信 matcher、正常参与 Goal Contract 协议,原「依赖 plan source 可信身份的 M1 代码不得合入」硬门随之解除;「checkpoint handoff 私有持久化」随 D10 一并作废、无需裁决——它待的是 plan-decision-checkpoint 专用请求 kind 的持久化契约,而该 kind 在 D11 下不存在。
三、待执行项(已有结论,只差落地,不阻塞开工决策):
——2026-08-13agent_runtime_supervisor_source_is_trusted的约 19 处生产消费点逐点复核,确认对 plan 语义正确,不适用者单独收窄;其中 steer 门须实现为独立于该 matcher 的显式否决。M1A-1已落地。复核结论见 decision-log 同日条。第 4.3 节记录的「Supervisor 自行发起提问目前只有 Prompt 兜底、缺机制约束」——2026-08-13 裁决:M1 不做成机制约束,维持 Prompt 兜底。 理由:本链路上 Supervisor 是自家 Prompt 驱动的受控角色,不是外部输入;真正的失效后果(问题被改写、答案转述失真)属于产出质量问题,由用户在审批卡上兜底,不是安全边界被突破。为它单独接一道等价于static_delegate_clarification_pending_matches_delivery_at的校验,成本落在standard全链路上,与收益不成比例。 本裁决的纪律:第 4.3 节与第 5.2 节现有的如实记录必须原样保留,不得因为「已裁决」就改写成「已保证」或删掉——它记的是事实(standard 下该校验不触发),事实没变。M1 的相关回归也不得断言「Supervisor 无法自行提问」。 重新裁决的触发条件:若未来做方案链路允许非自家 Prompt 驱动的 Supervisor(如用户自定义 persona、外部编排接入),该假设失效,须重开本项。- 第 3.1 节「登记前必须先处理的代码改动」清单中,
project-planning身份登记已于 2026-08-13 落地(含 blocking 项与四处 needs_change),该清单已清空。
以下为已识别但明确后置、不阻塞上述任何一步的工作项:
agent.delegate澄清 continuation 相关的观测增强(把派生的repair_depth/clarification_round暴露进 run status 观测,便于 Supervisor 感知预算与排障)。- 画布替换授权是否收紧为「只有真实质量返工可替换正式图片,澄清续跑不可」。这是 PR #165 之后就存在的既有行为,与本方案诉求无关,单列待评估。
- 跨 run 全局预算缺口:第 23.5 节的 7 跳上界是单条链(同一
parent_run_id)粒度的界,不是某专业 Agent 在项目生命周期内的累计界;只要不断开新 run 就能不断获得新配额。本阶段只记录不实现。 - 「做游戏」路径改造(删
design-director、收窄design-foundation、调整 16 任务 DAG),等 GDD 真正被完整构建消费时另开工作包;见第 1.1 节「M0A-3明确不做」。
23.7 用户修订不计入 repair_depth:新增 UserRevisionRequested(2026-08-13 裁决)
问题:静态委派的跳分类判据只看父节点的 structured_result.contract_status 是否为 NeedsUserInput(apps/ai-game-creator-shell/src-tauri/src/delegation.rs 的 static_delegate_original_is_awaiting_clarification),凡「父节点不在等回答」的续跑一律 repair_depth += 1。用户在审批卡上点「修改」时父节点回执是 Submitted,因此必然落进返工计数,而 WP1 冻结的 repair_depth 上限是 1——后果是用户每条 plan lineage 只能修订一次,第二次被 静态委派返工深度最多为 1 直接拒。
裁决:repair_depth 防的是 runaway agent——autonomous 链路上 Supervisor 是 LLM,自行判定产出不合格就重新委派,无人拦截时会无限消耗预算。用户点「修改」则每一轮都由人触发,人本身就是循环边界,两者性质不同,不应共用计数。
给 StaticDelegateContractStatus(现有 EvidenceReady | NeedsRepair | NeedsUserInput)新增第四个变体 UserRevisionRequested,分类判据保持纯函数,只增加一个分支:
父节点 contract_status |
该跳性质 | 传播 |
|---|---|---|
NeedsUserInput |
澄清 continuation | round += 1,depth 不变 |
UserRevisionRequested |
用户修订(新增) | 两个计数都不动 |
| 其它 | 质量返工 | depth += 1,round = 0 |
审批命令写下 action=revise | reject 的 receipt 后,把原 delivery 的 structured_result.contract_status 置为 UserRevisionRequested;Supervisor 随后的续跑委派仍挂在原链上(repair_of 指向原 delivery),谱系与澄清轮次预算完整保留。
本裁决不推翻 WP1:「repair_depth 上限维持 1 不放松」继续成立,因为用户修订压根不算 repair。也不需要给 repair_depth 按 source 分流上限——做游戏链路不会出现该变体,不受任何影响。
「无限修订」在机制上兑现不了,必须如实告知产品:
| 硬边界 | 值 | 性质 |
|---|---|---|
STATIC_DELEGATE_LINEAGE_MAX_HOPS |
32 | 链上推断每次走完整条链,超限 fail closed 返回 (u32::MAX, u32::MAX);防环/防越界兜底,不是可调参数。该常量限的是链上节点数,不是跳数:判据 chain.len() >= MAX_HOPS 排在入链之前,故链最多 32 个节点 = 31 跳(2026-08-14 订正,此前本节把它写成 32 跳) |
| 单 lineage 版本数(第 8.8 节) | 128 | 版本链只增不改 |
只要修订挂在同一条链上,真实上限就是 31 跳。让修订不挂链(repair_of=None 另起)可绕开,但会丢失谱系关联并重置澄清预算,等于绕开整套约束,不采纳。产品阈值定为 16 次修订且为软提示:远低于机制边界、又高到用户实际碰不到;超过时不拒绝,提示「已修订 16 次,建议退回重做重新梳理需求」——每次修订都是一次完整 Provider 调用加一个新版本,改到十几次仍不满意时问题多半在需求本身,此时应走退回重做(新 lineage、重新问询)而非无限微调。
落地约束:
- 拆包(2026-08-13 按第 23.8 节更正):变体与分类分支在
M1C-0——该 PR 无写入方、是惰性路径、行为零变化,便于把「做游戏链路返工额度未被误放宽」这条关键回归单独证明;写入方与 receipt 在M1C-1同进同退——只置 status 不写 receipt(或反之)会让链路计数与审批事实脱节。这样拆全程不产生半状态:UserRevisionRequested直到M1C-1才可能被写出。 StaticDelegateContractStatus是 durable schema 的一部分。新增UserRevisionRequested属前向兼容问题(旧代码读不了新记录),非后向兼容问题:只有做方案链路会写它,做游戏链路既有记录不受影响。M1C-0本包不处理未知 durable status,但不得把未知值静默降级为NeedsRepair;未知值的前向兼容由M1C-0b单独收口为显式Unknown(并原样回写),损坏输入仍整体 fail closed。- (2026-08-14 补,
M1C-0合入复核)前向兼容失败的粒度是整个子系统,不是单条记录。list_static_delegate_deliveries_at对每条 sidecar 用?上抛,一条解析失败即整个目录枚举失败,barrier 计算、lineage 重放、返工校验与 run status 投影同时不可用。因此M1C-1写出该状态后,旧版本 App(回滚、或多机版本不一致)打开同一项目会整个静态委派面不可用。注意 bumpSTATIC_DELEGATE_DELIVERY_SCHEMA_VERSION救不了这一条:版本号校验排在 serde parse 之后,parse 已先失败。(2026-08-15 订正)此处原写「要处置只能改枚举粒度(单条损坏跳过并告警)」并要求M1C-1上线前二选一——两者都不成立:单条跳过对 delivery 不安全,裁决见下方「裁决三」。 - (2026-08-15 裁决一,落在
M1C-1)新变体必须进 barrier,且必须是独立的第六个计数user_revision_pending_count。 不补的后果:repair_required_count与user_input_required_count都用==比具体变体,UserRevisionRequested两边都不落,is_clear()因此可能为真——写入方一落地就意味着 Supervisor 可以在用户修订尚未派出时收束用户任务(收束门是project_gates.rs的static_delegate_completion_blocker_at_locked)。不复用现有两个计数的理由是硬的:repair_required_count带repair_of_delegation_id.is_none(),只算原始委派——因 depth≤1,返工的返工本就不该阻塞;而用户第 2 次修订的父节点自身就是 repair 节点,并进去会让第 2 次及以后的修订全部不阻塞,恰好在最需要处失效。user_input_required_count的语义是「等用户回答」,与「用户已发话、等 Supervisor 派发」不同,且detail()文案会说反。判据形状照user_input_required_count——ClaimedByParent+ 该 status + 无活跃 child(repair_of指向自身且状态属Dispatched|Ready|ClaimedByParent),不带repair_of.is_none();该字段正是仓库里「轮次无上限」概念的既有先例。连带必须逐处裁决:除is_clear()与detail()外,另有四处调用点逐字段读而不走is_clear()(autonomous_policy.rs两处、runtime_tools/delivery.rs两处),加字段不会自动传播过去。这是M1C-0那个失败模式的同构版本,载体从 enum 变体换成 struct 字段,编译器同样沉默。 - (2026-08-15 裁决二,落在
M1C-1)正向一致性锁死在「只能从EvidenceReady改写而来」,不得更严。UserRevisionRequested复用EvidenceReady的三条客观证据约束:终态为completed、missing_expected_artifacts为空、verification_required时verified_revision必须存在。依据是本节定的唯一转移——审批命令把一条既有 delivery 从EvidenceReady改写过来,且第 13.0 节审批前置门保证取证已过才会出现审批卡。产物覆盖校验本就对所有状态生效,「不得携带user_input_questions」已被现有 else 分支挡住,均不需改。实现形状是本条重点:把validate_static_delegate_structured_result里按contract_status分支的 if/else-if 链改成穷尽match、不留_通配——同一失败形状在本仓库已出现两次,靠纪律没拦住,穷尽match把它变成编译错误。反向风险必须一并记住:该函数不是入口过滤器,read_static_delegate_delivery_at→validate_static_delegate_delivery_record让它每次读取都跑;约束定得比写入方实际产出更严,等于把写入方的一个 bug 变成「该 delivery 永久读不出来」,直接触发裁决三的锁死。故不得再加EvidenceReady自己都没有的条款(例如要求error为None)。另订正一处可能的误读:本条不是既有漏洞——专业 Agent 自证「用户要求修订」今天不可达,唯一派生点build_static_delegate_structured_result_at只能产出三个旧变体,claim 回执的structuredResult从 delivery 拷贝,均为 Runtime 侧;本条约束的是M1C-1引入的第二个写入方。 - (2026-08-15 裁决三,另开
M1C-0b)「单条跳过并告警」作废,走第三条路。 单条跳过不安全的原因很具体:list_static_delegate_deliveries_at喂的是 barrier 的计数,跳过一条损坏的Dispatched记录会让waiting_count少 1、barrier 变 clear,Supervisor 在仍有未完成委派时收束——把可用性故障换成了正确性故障,比锁死更糟。对照可见危险面很精确:谱系重放不受影响,缺节点走「上游缺失」出口返回(u32::MAX, u32::MAX)哨兵,本就 fail closed。第三条路是把「解析失败」拆成两类:损坏(截断、非 UTF-8、超限)状态未知,维持整体锁死,今天的行为正确不动;前向不兼容(结构良好、只有一个 enum 值不认识)是已知的未知,让枚举有显式Unknown承接,解析成功但最大化阻塞——进 barrier、返工门无条件拒、谱系按最保守的「其它」分类(只会拒不会放)。这不违反上面「不得静默降级为NeedsRepair」那条:它禁的是把记录当正常记录继续走流程,Unknown是显式隔离。效果差别:旧版本打开新项目,今天是整个静态委派面不可用(做游戏链路一起挂),改后只冻结携带该状态的那条 lineage。两条比 2026-08-14 原文更糟的事实:锁死半径是整个项目目录——list_static_delegate_deliveries_at(root)先读全目录再按 parent run 过滤,任何一条无关 run 的损坏记录毒化所有 run 的 barrier;.json.previous备份救不了——sidecar 读取只在 primary NotFound 时才回退,损坏但存在的 primary 不回退。实现坑:#[serde(other)]用不了(只允许在 internally/adjacently tagged 枚举上,而这是序列化成纯字符串的 unit-variant 枚举),需自定义Deserialize,且Serialize必须原样回写原始字符串,否则旧版本任何一次读-改-写都会把未知值抹掉。该改动触及 master 已发布机制的所有读路径,正是当初把M1C-0单独拆出的风险类别,不得塞进M1C-1的 diff——另开M1C-0b(见第 23.8 节)。M1C-1的门禁因此为二选一:M1C-0b先落,或M1C-1直接上线并在 release note 明写「用过策划审批后回滚旧版本会让该项目静态委派面不可用」;建议前者,因为多机/版本不一致不需要用户主动回滚就会发生。 - 本裁决改的是 master 已发布的静态委派机制,须在
decision-log.md补 dated 记录,写明WP1的 depth≤1 结论未被推翻。 - 回归必须覆盖:用户修订跳不增
depth也不重置round;连续多次修订均可通过;做游戏链路的返工仍在depth=1被拒;无该变体的历史记录分类行为不变;链长边界仍 fail closed;用户修订父节点下「同一静态委派最多允许一轮返工」的兄弟检查仍然生效(含并发)——depth 门对用户修订不再适用后,兄弟检查是该路径上唯一剩下的扇出约束。(2026-08-15 补)M1C-1另须覆盖:UserRevisionRequested且无活跃 child 时 barrier 不 clear 且收束门拒绝,派出续跑后 barrier 恢复 clear;第 2 次及以后的修订同样阻塞(父节点自身是 repair 节点,专钉裁决一里被否掉的repair_of.is_none()写法);穷尽match下终态为failed或缺产物的UserRevisionRequested落盘被拒。M1C-0b另须覆盖:未知 durable status 解析为Unknown后 barrier 不 clear、返工门拒、原始字符串经读-改-写后不变;损坏记录(截断/非 UTF-8/超限)仍整体锁死;既有三变体记录的行为逐条不变。
23.8 M1 的 PR 结构与合入门禁(2026-08-13)
第 23.6 节步骤五原本只有一句「未开始」。M1 一旦并行开工,PR 划分与依赖顺序就是工程流程契约——它决定谁能先合、谁阻塞谁、每个 PR 的回滚边界在哪,属于必须团队可见的内容,不能只存在于个人工作稿里(PR 描述也不允许引用工作稿)。故在此冻结结构;每个 PR 的逐项做什么与逐点复核清单不在本节范围。
| PR | 主题 | 依赖 | 合入门禁 |
|---|---|---|---|
M1A-1 |
source 常量 project-supervisor-plan + 进可信 matcher + steer 独立否决 |
— | 已落地。消费点复核见 decision-log 2026-08-13 M1A-1 条;steer kind=plan-root-steer-unsupported,独立于 matcher |
M1A-2 |
两层工具面 + project-planning 角色 brief |
M1A-1 |
已落地:Supervisor 根 run 保持 standard 工具面;planning 子 Agent 按 agent-delegate/standard/Supervisor parent 身份构建 exact native allowlist(file.read、file.list 与两个协议控制函数),MCP 为空、web search 关闭、collaboration policy 为 null;Prompt Bundle brief 只注入 project-planning,repair/rebuild 与初始请求一致;伪造写入、命令、MCP、project.search alias、user.input_request 等原始工具在广告/解析/执行/恢复路径均 fail-closed。不包含 plan.submit_gdd 注册或 GDD 提交/审批。 |
M1A-3 |
plan 根 run 强判据函数 + retry 保源(本节下方「M1A-3 的由来」,规格见第 4.1 节第 5、7 段) |
M1A-1 |
已落地(source 保源与强判据)。kind=plan-root-retry-identity-unsupported;gui/cli 兜底不变。第 4.1 节第 7 段里依赖 plan-session / gddId / gdd-approval kind 的部分仍后置。必须早于 M1C-2a |
M1A-4 |
plan 根 run 的子 Agent 创建面收窄:agent.delegate 目标必须是 project-planning、agent.spawn_isolated 一律拒;plan source 下不拼 supervisorIntro 与 $visualContract |
M1A-2 |
执行层与上下文层都要做且不可互相替代(同第 19 节第 2 条纪律);agent.spawn_isolated 是第二条造子 Agent 的通道,只堵 agent.delegate 视为未完成;强判据 Err 必须落进拒绝分支而非当作「不是 plan 根」;project-supervisor-gui/cli 的委派与 spawn 行为正常路径不变。必须早于 M1D-2 与 M1E |
M1B-1 |
.agent/planning/ 存储层、strict schema、typed 指纹、版本链;含 .agent/planning/** 只挡写判据 |
M1A-2 |
2026-08-14 已通过门禁并合入本分支:GDD / index / session strict DTO、canonical bytes、duplicate-key 与后缀门、typed 指纹、连续版本链、session 原子写入/恢复、Runtime writer identity 及 game/fast_gdd.md / .agent/planning/** 通用写入拒绝;第 9.1 节 golden vector(3857 bytes)与 11 个定向 storage 测试通过,writer 目标 schema 重验、index 权威对账与锁内 index recovery(缺失/损坏/陈旧从严格 GDD 链重建)、恢复故障矩阵、cargo check --offline、npm run check:encoding 与 git diff --check 均通过。2026-08-15 订正:本包合入时 immutable_writer_rejects_symlink_and_hardlink_targets 实际从未通过——该用例是 #[cfg(unix)],Windows 上不参与编译(0 tests),本包门禁在 Windows 上跑因而看不见;符号链接被通用路径解析器先行拒绝并压成 PLAN_INVALID_PATH,与本模块「链接一律 PLAN_UNTRUSTED_PATH」的分类冲突。已修(详见 decision-log.md 同日条裁决三)。合入时无 approval receipt schema,多版本 statusCache 只是无 receipt 的预审批投影;M1C-1 接入 receipt 后必须重建 approved/revise/reject/superseded 状态。create-only 与等前缀不可变 |
M1B-2 |
plan.submit_gdd 原生工具、exact planning Provider 请求绑定与 GDD 提交点 |
M1B-1 |
工作包已通过本包门禁并以 27c3eb847 合入本分支。实现合同:四类 request kind 全部写 v3 lifecycle、required binding 与同一 dedicated structured-injection user message;只有 tool-plan 可生成 sole-submit v4 batch;提交点后只修复 index/Markdown/session successor、终止策划子 run,并保留 generic v5 standalone pending + v4 batch anchors,不创建 gdd-approval planning pending/审批等待。定向 Rust、cargo check --offline、格式、编码与 diff 门禁均已通过;完整审批链路与产品可交付仍留给后续工作包。2026-08-15 订正:本包的定向门禁漏掉了两处跨包回归,全量 CI 才暴露——① validate_next_entry 把 same_tool_plan_repair_chain 提到 loop_iteration 分支之外,使含本轮量(steer cursor / goal revision / planning binding)的判据作用于跨 loop 续跑,两条 tests::goal 与一条交接用例失败;② 新增的 missing_plan_submit_anchor_candidate_at 在 resume 最前面强读 runtime state,短路了下游对不可读 state 的 fail-closed 兜底。均已修,详见 decision-log.md 同日条裁决一/二。教训已记入门禁:新增或移动校验必须同时补一条「正向必须被接受」的回归,只钉拒绝挡不住作用域被放大 |
M1C-0 |
新增 StaticDelegateContractStatus::UserRevisionRequested 与分类分支 |
M1A-1 |
已落地:无审批写入方、是惰性路径;用户修订跳不增 repair_depth 也不重置 clarification_round,连续修订可通过;做游戏链路返工仍在 depth=1 被拒;原有三种状态与 UserRevisionRequested 的已知行为保持不变,未知 durable status 的前向兼容由已完成的 M1C-0b 显式承接,不在本包静默降级或改变 |
M1C-0b |
静态委派 durable enum 的前向兼容粒度:未知 contract_status 解析为显式 Unknown 并最大化阻塞 |
M1C-0 |
已完成:纯读路径、无写入方、审批状态、pending、receipt 或 UI(不含 M1C-1)。四种已知 durable 值保持原 serde;未知字符串解析为 Unknown(raw),非字符串仍拒绝,Serialize 及读-改-写均原样保留 raw。Unknown 计入 completion barrier 与 waiting blocker,返工入口无条件拒绝(含 depth=0),lineage 按“其它”最保守分类(depth + 1、round = 0);planning Provider、自治 liveness、终态扫描等既有读路径同步 fail closed。截断、非法 JSON、非 UTF-8、超过 128 KiB 的 sidecar 仍按整目录 fail closed,不做单条跳过。不新增或改变 M1B-* 功能依赖(仅复核其既有读路径);不包含 M1C-1 的审批写入、receipt、UI 或构建准入 |
M1C-1 |
gdd-approval pending、审批命令、receipt;receipt 写入上述 status 与 plan 根完成门 |
M1B-2、M1C-0(前向兼容粒度另见 M1C-0b) |
已落地并合入:三动作幂等、版本/指纹竞态防护、receipt 后 index/Markdown/audit/terminal observation/session 投影与恢复、generic v5/v4 anchor 精确消费、terminal summary 完整性校验,以及仅作用于 exact plan 根的只读 completion blocker;生产 acceptance-gate pending caller 与验收前置取证门按拆包纪律由 M1C-2a 承接。审批 UI / 澄清中转 / 构建准入仍未完成。连续修订 barrier 与 UserRevisionRequested 规则按第 23.7 节执行 |
M1C-2b |
澄清中转接线、轮次派生、预算注入 | M1C-2a |
实现与本包门禁已完成并已快进合回 feat/five_min_design:首 child 的 revision 1 session、NeedsUserInput → awaiting_user_input、回答绑定后 continuation 的确定性 session 投影、审批后 revise/reject 用户修订谱系及 Provider 活跃时间 usage fact/fold 已接线;末次 plan.submit_gdd usage 在 receipt/session successor 落盘且 standalone/v4 anchors 精确消费后于同一项目锁内折叠,真实 receipt 回归证明累计值恰好推进一次,重复审批与 recovery reconcile 不二次推进。planning_clarification_* 13 passed / 0 failed(M1C-2c 语义回归另见本包),另有 static deliveries 44、planning storage 13、planning submit 53、Provider usage 4、末次 usage receipt 1、barrier detail 3 条定向回归通过;格式、offline all-targets、编码与 diff 门禁通过。锁序承诺只适用于 M1C-2b 新增的 planning 澄清写投影路径;main_loop 既有通用 completion blocker 的 execution→project 路径不在本包。第 4 轮信封在正常路径不可达:agent.delegate 已在工具边界按血缘上限硬拒并返回 failed observation;coordinator 的超三轮 reconciliation 仅用于损坏血缘纵深防御。审批 UI、hydrate、构建准入和下游完整构建不在本包范围 |
M1C-2c |
决策卡 A/B 语义(第 23.9 节,2026-08-18 实现完成并合回):选项 → 台账映射改为 A/B 均 confirmed/user_option、信封 label 形状校验、planning role brief 与 Supervisor playbook/final-reply 文案(B 必须是真实岔路、第 3 项恒定且 description 须给出可执行验证方式、改口转述规则、提问纪律) |
M1C-2b |
实现与门禁完成,已由 6e4bd9703 合回 feat/five_min_design:Runtime 已实现 A/B/固定第三项校验、B 不再生成 default_pending、answerSummary 逐字保真;非法 C/缺项 fail-closed,A/B/自由填写回归已通过。planning_clarification_* 13、project_planning prompt 5、planning_submit 定向回归、prompt bundle、格式、编码、diff、offline all-targets 均通过;不含 M1D-1 前端、hydrate、构建准入或下游完整构建 |
M1D-1 |
前端 hydrate 与 GDD 审批卡;决策卡按第 23.9 节实现(label 动态渲染、默认焦点 A、Other 槽不变) | M1C-2b |
已完成并合入 feat/five_min_design(落地 0052a80da,其后 ESLint 修正 5b11a0530):新增严格 {projectPath} hydrate command、plan-gdd-state-view.v1 Rust read model、审批卡与独立 GDD 正文详情弹层;页面只消费 hydrate,决定 responseId 按审批请求/动作复用,recoveryPending 仅提供恢复重试;审批前置 pending 与错绑 session 继续 fail-closed。 |
M1D-2 |
入口分流与阶段进度 | M1D-1 |
2026-08-19 更正 bf2185fba 的错误入口映射:仅首页“做方案”新项目以 standard + project-supervisor-plan 启动;“做游戏/做素材”保持 autonomous-game-build,不再展示额外“直接开建”按钮;项目页新建、打开和 Godot 导入不新增策划入口,仅恢复已有 planning lineage。阶段进度显示轮次 x/3、当前版本和状态徽章;实际项目总控页面挂载 hydrate/审批卡,并将 project-planning / 设计组展示名收口。2026-08-30 变更:批准 GDD 后的“做成游戏”直接创建自动工作区、导入 game/fast_gdd.md 并自动启动 Direct Codex;仍未接入正式 approvedGddRef 构建绑定。 |
M1E |
端到端与故障注入收口 | M1D-2 |
已完成:planning 覆盖审计与 submit 拒绝上限收口完成。PLAN_INVALID_REQUEST / Provider input 或候选 GDD 的 PLAN_SIZE_LIMIT 每 child run 最多 5 次 rejected observation,第 5 次在 observation durable 后终态失败;counter durable,重启不清零。若在第五条 rejected observation 落盘与终态失败之间崩溃,恢复入口会按 durable counter 直接终态失败,不请求第六次 Provider tool-plan。既有不可变 GDD/receipt 的超限读取及 lineage 版本已耗尽均改走 reconciliation,不误耗 Provider 重试额度。第 21 节已存在的三轮、续跑、receipt/replay、投影恢复及 hydrate 回归复核通过;不为“拼接已有单测”新增脆弱大 E2E。 |
2026-08-18 M1D 审查修复快照:对 14c00017c..bf2185fba 做规格对照审查后,修复三条决定链路缺陷并补齐回归。① decidePlanGdd 的失败分支原来不 hydrate,命中后端任一 PLAN_STALE_APPROVAL 分支后卡片会停在已失效的 pending 身份上、recoveryPending 永不翻真导致「重试恢复」入口不渲染,现已按第 18.3 节在失败分支同样重灌(顺序钉死:hydratePlanGddState 入口会清空错误,必须先 hydrate 再写决定错误)。② responseId 复用键原为 approvalRequestId:action,不含 comment,违反第 13.2 节「改变 action/comment 必须换新 responseId」,现改为比对 {action, comment} 完整意图,判据方向为宁可多换不可少换。③ 第 18.2 节「recoveryPending 时不允许提交决定」原来只作用于三个触发按钮,已打开的评论弹层仍可提交,现已同门控并保留用户已输入内容。回归位于 tests/appSurface/plan-gdd.suite.ts,三条均经变异验证(逆转对应修复即变红);appSurface.test.ts 381 passed,agc:typecheck、ESLint、编码检查通过。其中锁错误回传绝对路径、阶段进度轮次差一格与 design 组展示名收口三条已于同日补修(见 decision-log 同日两条);仅 hydrate 身份校验与落盘投影修复的顺序一条单列后续,未并入。
2026-08-14 M1B-1 合入验收快照:已合入实现集中在 apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_storage.rs,并由 runtime_protocol.rs 注册。已具备 plan-gdd.v1、plan-gdd-index.v1、plan-session.v1 及 plan.submit_gdd input 的 strict serde 形状校验、文本/ID/时间/枚举边界、typed serde fingerprint、canonical JSON(重复键、BOM、尾空白、字段顺序)解析、GDD 连续版本链、session revision + 1 / previousFingerprint 链、create-only durable writer、session 原子替换与受限 recovery,以及 project-planning / agent-delegate / standard / project-supervisor writer identity。通用 file.write、file.patch、file.delete、project.patchset 与 checkpoint restore 对 .agent/planning/** 和 game/fast_gdd.md 只挡写,planning 的 file.read / file.list 仍可读;M1B-1 合入时没有注册或执行 plan.submit_gdd,也没有实现 approval pending、receipt、UI 或构建准入。
已验证第 9.1 节 golden vector(canonical envelope 3857 bytes 与指纹 sha256-serde-json-v2:a59856de7ef134cf2f49c4dedd2ba10ae4ab2340a9634d402eb792b6ee5458f0)及 11 个定向 storage 测试;durable writer 的目标 schema 重解析与文件名/版本核对、index 与权威 GDD 对账、缺失/损坏/陈旧 index 的锁内链重建、(requestId,responseId) / decision/ref / active run/phase 等 session 约束、GDD 文件枚举/链读取及 primary 损坏/previous 提升/分叉 recovery 矩阵均已覆盖。M1B-1 尚无 approval receipt schema,因此多版本 statusCache 只表达无 receipt 的预审批投影;M1C-1 接入 receipt 后重建真实 approved/revise/reject/superseded 状态。cargo check --offline、npm run check:encoding、git diff --check 均通过;该包现已合入本分支,这一历史快照不把后续 plan.submit_gdd、审批闭环、UI 或 renderer 误报为 M1B-1 已实现。
2026-08-15 M1B-2 本包验收快照(已合入本分支):代码已把 plan.submit_gdd 接到原生工具目录、Provider 请求、v3 lifecycle/v4 batch、main-loop 专用提交分支与恢复扫描;同时以 required rootAgentId + fingerprint 加固 plan-provider-session-binding.v1,冻结并实际注入 plan-provider-structured-injections.v1 dedicated user message。tool-plan、final-reply、context-compaction、final-reply-context-compaction 四类 exact planning 请求共享 v3 binding 和 structured injection,只有 tool-plan 能生成 v4 batch,planning idle compaction 失败关闭。提交点后的 M1B-2 投影仅为 index、Markdown、session successor、策划子 run 终态以及原 generic v5/v4 anchors 的保留/重建;gdd-approval planning pending、审批等待、receipt、审批命令和 UI 明确留给 M1C-1 及之后。本包定向 Rust、cargo check --offline、格式、编码与 diff 门禁已通过;这只表示 M1B-2 工作包完成,不表示完整产品或审批闭环可交付。
拆包纪律:
M1C-0与M1C-1拆开的理由是风险类别不同:前者改的是 master 已发布的静态委派机制,后者是 M1 新增功能。合并成一个 PR 会让「做游戏链路返工额度未被误放宽」这条最关键的回归淹没在审批闭环的 diff 里。拆开后M1C-0全程不产生半状态——它没有写入方,UserRevisionRequested要到M1C-1才被写出。M1C-0b(2026-08-15 新增)从M1C-1里拆出的理由与上一条同源:它改的是 master 已发布机制的所有读路径,而M1C-1是 M1 新增功能。混在审批闭环的 diff 里,复核者只会看审批逻辑,读路径容错对既有记录的行为等价性无人证明。M1C-0b依赖M1C-0(Unknown的分类分支要与用户修订分支落在同一处穷尽判定里),但不依赖M1B-*。M1C-2拆成 a/b 的理由是两者失败模式无关:Goal Contract 是协议时序问题,澄清中转是身份派生与幂等问题。- 顺序是依赖顺序不是优先级。
M1C-0只依赖M1A-1,可与M1B-*并行;M1A-3同样只依赖M1A-1,可与M1A-2、M1B-*并行。
M1A-3 的由来(2026-08-13 补列):本 PR 的内容原属 M1A-1 的消费点复核范围,被判成「已被 profile 挡住、本包不改」而漏出。漏出的机制原因是复核方向单一——M1A-1 的模板只问「plan 进可信 matcher 后会不会误得不该有的语义」,而 resolve_game_creator_agent_runtime_retry_configuration_at 既是判据也是 run 构造器,还必须反过来问「plan 落到通用兜底后会不会丢掉该有的语义」。后者的答案是会:plan 根 run 落 agent-background-task 兜底后 steer 重新放开,且 root_control_authority 转为 false 使其建不出 Goal Contract,第 13.0 节审批前置门要的取证永远收敛不了——而因为 agent.delegate 不受该权限影响,故障要到审批那一步才暴露。不回改已合入的 M1A-1,单列本 PR;详细定位见 decision-log 2026-08-13「订正 M1A-1 的 retry 复核结论」条。凡「既是判据又是构造器」的调用点,后续 PR 的复核必须双向提问。
23.9 决策卡选项语义修订:A/B 平行方案与决定状态来源唯一化(2026-08-18 M1C-2c 隔离实现完成)
问题:第 5.2 节固定的三个选项「接受推荐 / 暂按推荐 / 需要原型验证」是对同一条推荐的三种采纳程度,不是三个方案。以 DeepSeek v4-flash 做的本地原型多轮实测(原型不入库、只用于开发调试,本节只记结论):选 1 与选 2 产出的 GDD 内容一字不差,差别只是一个 default_pending 标签;该标签不进任何机制(不锁台账、不进构建准入、不进验证环),却消耗一轮问询名额(上限 3)。同时台账 answerSummary 只落「接受推荐」三个字——被接受的方案内容只存在于信封问题正文与子 Agent 自己的会话里,事后审计看不出用户到底确认了什么;用户在下一轮自由填写里推翻上一轮已确认决定时,这一点让 Supervisor 转述与子 Agent 出稿都只能靠改写文本来消化。
裁决:
- 选项结构改为「A / B / 固定第 3 项」,仍恒定三项:第 1 项 = 方案 A,策划子 Agent 的推荐,label 形如
A · {方案短语}(推荐);第 2 项 = 方案 B,一条真实可行、形状不同的平行备选(另一条设计岔路,不是 A 的反面、不是稻草人、不是"先按 A 走"),label 形如B · {方案短语};第 3 项 = 固定文案需要原型验证,每张卡都给,description 写清这一题做原型要验证什么、怎么做(30~90 分钟占位资产微型原型、让谁试玩、观察什么),对方向性问题也要给出可执行的验证方式。user.input_requeststrict input 与 Other 输入槽(placeholder改成:……)不变。question 正文只说要决定什么、为什么现在决定;推荐理由写进 A 的 description;每个 description 说明选择后果与代价。 - 映射表改为:A / B →
confirmed / user_option;需要原型验证→prototype_pending / user_option(同 ID 30~90 分钟微型原型项的规则不变);自由填写 →confirmed / user_freeform。default_pending收回为唯一来源——未提问、由子 Agent 按默认建议填写的字段(answerSource=default、round=0,与第 12 节提交校验「前缀之后只允许追加default_pending默认决定」完全一致)。由此三个决定状态各只有一个来源:confirmed= 用户拍板,prototype_pending= 用户要求验证,default_pending= 没问过。 - 状态映射按 label 而非位置:Runtime 校验信封恰好三项、第 1 项以
A加分隔符开头、第 2 项以B加分隔符开头(分隔符·/:/:/-宽松;全角冒号必须在集合里——prompt 全中文,模型高频输出A:,漏掉它等于让合法信封被拒并白吃一个未推进回合预算)、第 3 项逐字等于需要原型验证,不满足按信封格式错误回灌重试(受既有未推进回合预算约束);answerSummary直接落所选 label,台账自描述;label 与用户回答必须经同一套规范化后再比对,否则模型吐出带尾随空白的 label 会让用户的点选被记成user_freeform,恰好污染本节要立起来的 answerSource 唯一来源。 - 已确认决定被后续自由填写推翻(改口):M1 内不改合同。台账前缀不可变,两条
confirmed并存;Supervisor 转述 continuation 时必须在被推翻的那条之后注明「已被第 N 轮回答推翻,以后者为准」,用户答案原文仍逐字保留(不得改写、拆分或搬到别的轮次),子 Agent 按后者出稿并在新决定的 topic 中写明推翻关系。supersedes字段进plan-gdd.v1列为 M2 候选。原型实测三次改口 Supervisor 均能自行消化,但其中一次是靠改写用户答案原文(转述保真审计报「内容缺失」),故此规则必须写进 Supervisor prompt,不能默认它会做对。 - 提问纪律补一条:平台事实已定的事(含"移动/桌面优先级")与 MVP 规则已排除的事(多人/联机/商城/服务器)不作为问题。A/B 格式会诱使模型去问"天然二选一"但无价值的问题(实测一轮 3 张卡里 2 张是这种),必须在 prompt 里堵住。
为什么不进 M1C-2b、由 M1C-2c 承接:M1C-2b 的合入门禁(3 轮上限、continuation 重放幂等、答案绑定冲突被拒)与选项文案无关;它按旧映射表完成了澄清中转,并已于 2026-08-17 快进合回。M1C-2c 已在隔离 worktree 翻映射、改信封形状校验并更新 role brief、Supervisor playbook 与 final-reply 提示;planning_clarification_* 13 条、project_planning prompt 5 条、planning_submit 定向回归、prompt bundle、格式、编码、diff 和 offline all-targets 门禁均通过。M1D-1 前端决策卡必须直接按本节实现(label 动态渲染、默认焦点在 A、Other 槽不变),避免做两遍;本包不接前端、hydrate、构建准入或下游完整构建。
顺带核对项(M1E 已完成):plan.submit_gdd 连续校验失败有5 次上限。本地实测无界时,模型对大载荷序列化出错后连续 40 余次重试,每次重放全部历史,单次 prompt 涨到 15 万 token;生产的 240/300 秒预算注入兜不住「硬超时注入后仍连续校验失败」的情形。第 12 节 retry 状态机现以 durable counter 收束,第五次 rejected observation 落盘后终态失败;恢复入口同样按该 counter 终态收束,不能因两步之间崩溃而发起第六次请求。
24. 最终不变量摘要
- 策划与构建是两个不同 profile/source 的 run,由用户动作连接,不由 Agent 自行升级。
- (2026-08-14 由
M1A-4落为机制) plan 根 run 与策划子 Agent 互为唯一对应:source=project-supervisor-plan的根 run 只能委派project-planning,且一律不得agent.spawn_isolated;身份判据失败(binding 缺失/损坏/漂移)时拒绝创建任何子 Agent,不得放行。因此「策划全程零构建、零文件写」是执行层保证,不再依赖 Prompt。上下文层的裁段是纵深防御的第二层、不可替代第一层——$base的隔离模板目录仍会列出专业角色名,那是被硬拒后的死文本(见 decision-log 2026-08-14M1A-4条)。 - (2026-08-13 按 D11 改写) 每轮的线性化点是答案绑回 delivery(
answersSha256原子写入,首次或逐字相同则幂等、不同答案则拒绝),不是 session CAS;轮次由委派链上的clarification_round派生,session 不保存计数字段。同一组问答只能派生同一个 continuation,因此同一回答永远不能产生两轮。 - 每个 plan Provider request 的实际 messages、工具、结构化注入与 durable binding 来自同一 captured session;stale 输出不能重绑到新 session。
- GDD durable create 是提交点;approval receipt durable create 是用户决定线性化点。
- 不可变事实只增不改;其它内容都能从事实或 session checkpoint 有界恢复。
- approvalRequestId 属于不可变 GDD,approval receipt 的 responseId 属于一次审批决定,两者都不可在重试中漂移;user-input answer/checkpoint 的同名 responseId 属于独立回答幂等域,不能跨域复用或恢复。
- decisionFingerprint 解决意图幂等,receiptFingerprint 保护完整信任根。
- (2026-08-13 按 D11 二次拆写,取代 2026-08-12 按 D9 的拆写) 策划子 Agent(
agentId=project-planning,由 Supervisor 静态委派而非 ready-task 调度启动)无 command/smoke/preview/再委派/MCP,其工具面是 exact allowlist 且不含user.input_request——目标态是广告层与执行层都拒:allowlist 让它不出现在函数目录,执行层validate_user_input_action_owner兜底(2026-08-13 按第 19 节第 2 条更正;原文「广告层仍可见」描述的是 M1 之前的现状,不是终态合同);Supervisor 根 run 保持现役standard工具面,其 Provider 不直接向用户提问,而是认领子 Agent 的AGC_NEEDS_USER_INPUT_V1终态信封后代为创建 pending(详见第 19 节第 2 条)。内部 Markdown 投影不推进代码 revision。 - 用户侧只有一个对话对象;所有呈现给用户的对话内容必须物理存在于 Supervisor 的会话文件中,不得由前端把多个 Agent 的会话拼接成单一视图。
- 前端只通过
hydrate_game_creator_plan_gdd_state读取策划权威状态;文件、Markdown 和 runtime polling 不能在页面侧合成批准事实。 - 完整构建只认后端在项目锁内重算并冻结的 approvedGddRef;直接开建必须显式声明,不能由缺字段降级。根 Run 被 steer 替换时,replacement root run 必须重新冻结同一 ref,不换稿、不降级、不留空窗。
- 已批准 GDD 只能作为上下文进入 Supervisor 的目标理解,不能被 Runtime 直接物化成 Goal Contract;GDD 身份也不是 Acceptance Graph 的合法证据取值。
- 确定性收束与 Runtime 内部产物验证都不产生验收证据;不得为了让确定性节点可验收而把内部验证工具暴露给 Provider。
- 任何无法证明 project/source/profile/session/run/action/ref 身份的恢复或 mutation 都失败关闭。
- 不携带 run/root 身份的项目级单例文件(当前为
.agent/manifest.json)永远不是轮次身份的权威源,只能作为身份判定通过后的补充信号。