diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 1e0e684a3..78b37aae6 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -9,7 +9,7 @@ - M2 裁决一:确定性收束与 Runtime 内部产物验证**都不构成验收证据**。`design-director` 确定性化后不产生 Provider 回执,M0-3 的内部 owner 验证同样不产生;而 passed 节点必须引用真实成功动作回执且 `requiredEvidence` 须命中 `agent_runtime_acceptance_evidence_tools()`。涉及 GDD 落地的验收标准要么由根 Supervisor 用允许的证据工具自行取证,要么不写成 required 节点。**不得**为使确定性节点可验收而把内部验证工具暴露给 Provider——那会推翻 M0-3 已冻结的 owner 验证边界。 - M2 裁决二:planning baseline 不是「一次冻结永久有效」。steer 替换协议会终止旧根树并另起 replacement root run,该 run 必须在自己的锁/CAS 边界内重新冻结与被替换根**完全相同**的 `approvedGddRef`,即使期间已有更新的 approved 版本也不换稿;无法证明同一 ref 时失败关闭,不得降级为 `mode=direct-build`。 - M2 附带证据:Goal Contract 落在 `.agent/runtime/goal-contracts/{rootAgent}/{rootRun}.json`,按 root run 分文件并绑定 binding fingerprint 与 source SHA-256。这是「自带 root/run 身份」的正面先例,D4 关于 `approvedGddRef` 是否进 manifest 的决策应参照此形态,而非项目级单例。 -- 保留待裁决:**plan source 与 Goal Contract 协议的关系**(M1 入口前置,与 checkpoint handoff 私有持久化决策并列)。触发事实是 `agent_runtime_supervisor_source_is_trusted` 已成为 run 启动、steer、Goal Contract 创建权限、根控制面工具授权与验收图完成门的共同判据且全部不看 Run Profile;plan source 一旦进入该集合,未冻结 Goal Contract 即完成门永久 blocked,而四项 exact allowlist 无解除手段。三方案与代价见技术方案第 23.1 节。裁决冻结前,依赖 plan source 可信身份的 M1 代码不得合入。陷阱细节见 pitfalls 2026-08-12「往 trusted supervisor source 里加新 source」条。 +- 保留待裁决:**plan source 与 Goal Contract 协议的关系**(M1 入口前置,与 checkpoint handoff 私有持久化决策并列)。根源是两套工具模型不兼容:现行 deny-list 模型下 `allowed_tools` 直接等于全量执行工具目录,可信 root Supervisor 默认持有 `agent.goal_contract`,两道门对现役 gui/cli/game-chat 是「照着做」;plan source 是首个 allow-list 模型的 source,不持有该工具。卡点是两道**互相独立**的门:入口门 `validate_root_goal_contract_control_plan_at` 在通用 tool-plan 解析路径上强制「合同不存在时本轮必须是唯一的 `agent.goal_contract` 动作」,判据既不看 source 也不看 Run Profile;出口门 `goal_contract_acceptance_completion_blocker_at_locked` 判据含可信 source,未冻结合同即完成门永久 blocked。四方案与代价见技术方案第 23.1 节,其中「plan root 改用独立 `agentId`」是唯一一次性解耦三处判据的方案,但会推翻第 4.1 节已冻结的 `agentId=project-supervisor`。裁决冻结前,依赖 plan source 可信身份的 M1 代码不得合入。陷阱细节见 pitfalls 2026-08-12「往 trusted supervisor source 里加新 source」条。 ## 2026-08-12 manifest 不是 game-chat 轮次身份的权威源(M0B-2 挂起项收敛) diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index d0ed44258..74f793e1c 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -4,7 +4,9 @@ - 现象:`agent_runtime_supervisor_source_is_trusted`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs:113`)读起来像一个「谁能启动 Project Supervisor」的入口白名单,实际早已是多条互不相干的授权判据的共同开关。2026-08-11 合入动态目标验收图后,它的非测试消费者从 2 个文件涨到 10 个文件 18 处调用:run 启动(`commands.rs:698`)、steer(`commands.rs:876`、`steering.rs:763`)、Goal Contract 创建权限(`goal_contract.rs:496`)、根控制面工具是否被剥离(`provider_request_builders.rs:134` 的 `root_control_authority`)、验收图完成门(`acceptance_graph.rs:595`)、run configuration、lifecycle_control、task_start、project_gates、autonomous_completion。 - 陷阱:这些判据**全都不看 Run Profile**。`goal_contract_acceptance_completion_blocker_at_locked`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/acceptance_graph.rs:568-611`)只要求「`agent_id` 是 `project-supervisor` + binding 是无 parent 的 root + source 可信」,未冻结 Goal Contract 就返回 `blocked`,并被 `main_loop.rs:213`、`main_loop.rs:1803`、`finalization.rs:398` 消费。因此给一个**用途完全不同**的新 source(例如立项策划的 plan chat)加进白名单,会让它的根 Run 立刻背上「必须先调 `agent.goal_contract`」的义务;如果该 source 的工具面按 exact allowlist 设计、不含这个工具,根 Run 就永远无法完成——而且症状是 run 卡在完成门,不是启动失败,排查方向容易跑偏。 -- 处理:新增 trusted source 前,先逐个确认这 18 处调用对新 source 的语义是否成立,尤其是 Goal Contract 创建、验收图完成门与 steer 三处;需要区分时,应当拆出「可信入口」与「Goal Contract 参与者」两条判据,而不是继续复用同一个函数。立项策划的对应裁决见《【技术方案】立项策划Agent(Fast GDD)-2026-08-10》第 23.1 节,尚未冻结。 +- 更坏的一半:把新 source 排除出白名单**并不能**脱身。同一协议还有一道入口门 `validate_root_goal_contract_control_plan_at`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/autonomous_policy.rs:171`),由 `provider_tool_plan.rs:434` 在通用 tool-plan 解析路径上无条件调用,判据只有「`agent_id` 是 `project-supervisor` + binding 的 root 是自己 + 存在 run profile binding」——**连 source 都不看**。合同不存在时它强制本轮恰好一个 `agent.goal_contract` 动作且 `plan_update`/legacy plan/`response` 全为空,于是「第一轮先问用户一个问题」或「第一轮先回复」的 Agent 会被直接判协议错误。三处判据里只有 `agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID` 是共同项,改 `agent_id` 是唯一能一次性解耦的做法。 +- 为什么现役 Agent 没事:工具面是 **deny-list** 模型。`agent_runtime_tool_policy_snapshot_at`(`tool_policy_snapshot.rs:130-178`)的 `allowed_tools` 直接等于全量 `agent_runtime_executable_tools()`,`agent.goal_contract` 天然在内;`standard` profile 在 `agent_runtime_tool_policy_snapshot_for_run_at:198` 提前返回、不做裁剪;只有 `!root_control_authority && goal_contract_participant` 的后代才在 `provider_request_builders.rs` 被剔除。所以 gui/cli/game-chat 根 Supervisor 默认就握着这个工具,两道门对它们是「照着做」而不是「过不去」。**按 exact allowlist 设计工具面的新 source 才会撞上**,而 allow-list 正是更安全的那个方向——这条陷阱专门惩罚更严格的设计。 +- 处理:新增 trusted source 前,先逐个确认这 18 处调用对新 source 的语义是否成立,尤其是 Goal Contract 创建、验收图完成门与 steer 三处,再单独确认不看 source 的入口门;需要区分时,应当拆出「可信入口」与「Goal Contract 参与者」两条判据,而不是继续复用同一个函数。立项策划的对应裁决见《【技术方案】立项策划Agent(Fast GDD)-2026-08-10》第 23.1 节,尚未冻结。 - 相关:`requiredEvidence` 只接受 `tool:` 且必须命中 `agent_runtime_acceptance_evidence_tools()`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs:74-96`,当前 18 项)。确定性收束的任务和 Runtime 内部产物验证都不产生 Provider 回执,因此无法为验收节点提供证据——不要指望「让 Runtime 自己验一下」能满足验收图。 ## 2026-08-12 给 manifest 加"新鲜度门控"或身份字段的两个陷阱 diff --git a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md index c78882677..9f6e93e73 100644 --- a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md +++ b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md @@ -123,6 +123,8 @@ flowchart TD M1 不能把新 source 直接塞进一个被 autonomous 语义复用的总 matcher。source predicate 必须拆成三种语义:top-level Supervisor trusted matcher 接受 gui/cli/game-chat/plan;autonomous-build trusted matcher仍只接受现役 gui/cli/game-chat;plan binding matcher只接受 top-level `project-supervisor + standard + project-supervisor-plan-chat`。现有 run configuration、completion gate 和恢复调用点逐个改用正确 predicate,确保 `plan + autonomous-game-build` 永远非法。前端已有 `runProfile + source` 提交链只能作为请求;后端必须重新验证,不能信任页面选择。 +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)。 + 所有 plan 例外必须共用单一 `is_exact_supervisor_plan_run_at(root, agentId, runId)`:只有 durable run-profile binding 已通过 project/fingerprint 校验,且 `agentId=project-supervisor`、`source=project-supervisor-plan-chat`、`profile=standard`、parent/delegation 均为空、`rootAgentId=agentId`、`rootRunId=runId`,并与 task/runtime 以及存在时的 planning pending、尚存 batch 的 source/profile/binding fingerprint 逐项相等时才返回 true。缺失必需 binding、损坏或漂移不得获得 plan 例外,不能只比较内存中的 `runtime.source`。 普通 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-chat + standard`,并在项目锁内写 session revision+1 successor;不得由 retry helper 主动终结 running/waiting run,不得新建 gddId,也不得降级为 `agent-background-task`。任一身份校验失败直接拒绝 retry。已经进入 `gdd-approval` 等待的 run 禁止走 generic retry,只能恢复并续跑 receipt 所绑定的精确原 run。 @@ -1366,7 +1368,7 @@ Canvas 是条件外部能力,不是 M0-3 四个固定 owner 的通用前置: 2026-08-12 裁决补充:manifest 不是 game-chat 轮次身份的权威源。`GameCreationAppManifest` / `GameCreationAppTaskState` 在 TS 与 Rust 双侧都不含 run/root/agent 身份字段,且 manifest 是项目级单例文件并跨轮复用同一份,因此任何任务状态都无法从数据本身归属到具体轮次。轮次结论的权威事实只有 Runtime lineage(`agentId + sessionId + runId + source + parentAgentId + parentRunId`);manifest 只能作为 lineage 判定通过后的补充信号,不得单独裁定当前轮结论,也不得单独解锁阶段归档。阶段归档快照的全部写入点都必须校验被归档 root 仍是当前 root,终态会话同步中的异步捕获同样适用——该读取若在下一轮接管后才 resolve,会把掺入新轮活动的清单冻结成旧轮快照;跳过捕获不会饿死归档,因为开下一轮前的阻塞门在快照未冻结时直接失败要求重试。本阶段明确不为 manifest 或其投影新增 `statusRunId` / `statusSource` 等身份字段:补字段必须同时覆盖 `update_manifest_task_status_at` 与无秩序守卫的 `set_task_status` 两条写入路径,只改前者会让后者原样保留上一次写入的旧身份印记,制造“校验通过”的假象而比不校验更危险。 -2026-08-12 记录一处尚未裁决的不变量冲突。本节第 2 条(plan source 的工具广告与执行双门只有四项)与 2026-08-11 合入的动态目标验收图存在结构性冲突:`goal_contract_acceptance_completion_blocker_at_locked` 只按「`agent_id` 是 `project-supervisor` + binding 是无 parent 的 root + source 可信」三条判定,**不看 Run Profile**;同一判据也决定 Goal Contract 的创建权限和 `agent.goal_contract` / `agent.acceptance_update` 是否被剥离。M1 一旦按第 22 节把 plan source 加入可信集合,plan root 就同时满足三条,未提交 Goal Contract 即完成门永久 blocked,而四项 exact allowlist 里没有解除该 blocker 的工具。处置方案见第 23.1 节的待裁决项;在裁决冻结前,本节第 2 条按「设计意图」保留,不得据此认为现行代码已经满足它。 +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、停用与回滚 @@ -1463,13 +1465,23 @@ M0 文档 PR 本身最低验证:Markdown 结构与三张 Mermaid 图可解析 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 新增第二项 M1 合入前置决策:**plan source 与 Goal Contract 协议的关系**。触发事实见第 19 节与第 22 节 trusted matcher 行——`agent_runtime_supervisor_source_is_trusted` 现在同时决定 run 启动、steer、Goal Contract 创建权限、根控制面工具授权与验收图完成门,且全部不看 Run Profile。三个候选方案与已知代价: +2026-08-12 新增第二项 M1 合入前置决策:**plan source 与 Goal Contract 协议的关系**。 + +冲突的根源不是某处判据写错,而是两套工具模型不兼容。现行是 **deny-list 模型**:`agent_runtime_tool_policy_snapshot_at` 的 `allowed_tools` 直接等于全量 `agent_runtime_executable_tools()`,`agent.goal_contract` 天然在内,`standard` profile 在 `agent_runtime_tool_policy_snapshot_for_run_at` 里更是提前返回、不做任何裁剪;`provider_request_builders.rs` 只对 `!root_control_authority && goal_contract_participant` 的后代做剔除。因此现役可信 root Supervisor(gui / cli / game-chat)**默认就持有**这个工具,两道门对它们是「照着做」。而 M1 是第一个 **allow-list 模型**的 source(第 4.3 节 exact allowlist),Goal Contract 协议隐含的「root Supervisor 一定持有 `agent.goal_contract`」前提对它不成立。 + +具体卡在两道**互相独立**的门,判据不同,必须分别处置: + +- **入口门** `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 门 | `commands.rs` 的启动与 steer 两处都要改;从此存在两套 source 信任判据,后续新增可信消费者容易只接一套 | -| B. 进可信集合但按 profile 豁免 | 在 Goal Contract 创建、验收图完成门、steer 三处加 `standard + plan source` 豁免 | 豁免点分散且会继续增加;必须同时冻结「可信 root ≠ Goal Contract 参与者」这条新不变量,否则下一个消费者默认把 plan 当参与者 | -| C. plan 接纳 Goal Contract 协议 | 把 `agent.goal_contract` 纳入 plan 工具面 | 与第 4.3 节 exact allowlist、第 19 节第 2 条和第 24 节「plan source 无……」直接冲突;且 GDD 与 Goal Contract 在语义上大面积重叠,等于让同一轮策划冻结两份目标事实 | +| 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 工具面 | 两道门都自然满足,但与第 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 节 | 裁决冻结前,依赖 plan source 可信身份的 M1 代码不得合入主线;该决策与 checkpoint handoff 私有持久化决策并列,均属 M1 入口前置。此外需一并确认:plan run 是否允许 steer——`steering.rs` 的 steer 门同样只按可信 source 判定,而 steer 替换协议会终止旧根并另起 replacement root run,与第 5.1 节的 session CAS、`roundsUsed` 与 `activeQuestion` 语义存在未验证的交互,第 14 节恢复矩阵目前没有这条路径。