diff --git a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md index fd65c11b9..5d9c50689 100644 --- a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md +++ b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md @@ -1602,7 +1602,7 @@ receipt 永远压过 stale pending:一旦该版本存在有效 receipt,pendi ## 19. 安全不变量与构建验证边界 1. 不放松 `autonomous-game-build` 对 `user.input_request` 的现行禁等待防线;多轮策划只发生在 top-level standard plan run。 -2. **(2026-08-13 按 D11 二次拆写,取代 2026-08-12 按 D9 的拆写)** 做方案链路的工具边界分两层:**Supervisor 根 run** 以新可信 source `project-supervisor-plan` 承载入口,工具面按现役 `standard` deny-list 模型,其 Provider 不直接向用户提问,而是在认领策划子 Agent 的 `AGC_NEEDS_USER_INPUT_V1` 终态信封后,在自己的 runtime/session 上创建 `waiting-for-user-input` pending(复用 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` 参数、无法按身份裁剪;`standard` profile 下 `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。回归用例须同时钉死「执行层拒绝」与「广告层仍放行」这两半,不能只测其中一半就当作已覆盖。原 D10「排除是产品约束的自律要求而非 Runtime 兜底」的表述在新拓扑下已失效,见第 2 节 D10 作废条目。 +2. **(2026-08-13 按 D11 二次拆写,取代 2026-08-12 按 D9 的拆写)** 做方案链路的工具边界分两层:**Supervisor 根 run** 以新可信 source `project-supervisor-plan` 承载入口,工具面按现役 `standard` deny-list 模型,其 Provider 不直接向用户提问,而是在认领策划子 Agent 的 `AGC_NEEDS_USER_INPUT_V1` 终态信封后,在自己的 runtime/session 上创建 `waiting-for-user-input` pending(复用 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` 参数、无法按身份裁剪;`standard` profile 下 `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 作废条目。 3. `.agent/planning/**` 与 `game/fast_gdd.md` 唯一写者是 Runtime;任何 Agent 通用写工具都不能触达。 4. GDD、receipt 追加不可变;index、session、Markdown 和 UI 只作有限投影,不能反写权威事实。 5. approve receipt 是构建信任根;Agent 文本、“已批准”状态缓存、Markdown 徽标或 UI 内存都无批准权。 @@ -1803,7 +1803,7 @@ D9 之后,root 是未变的 Project Supervisor,它在 `standard` 下天然 *裁决理由*:steer 替换会终止旧根、另起 replacement root run,而 D11 把策划的轮次预算挂在了委派链上——`clarification_round` 沿 `repair_of_delegation_id` 上溯推断,且每一跳都强制 `parent_run_id` 等于当前根 run(见第 4.1 节与第 23.5 节)。换根之后 `parent_run_id` 变了,旧链的 continuation 会被跨 run 隔离判据直接拒绝,**这条策划链路剩余的问询轮次全部作废**,用户已经回答过的内容也无法续接。这不是"交互未验证",是确定性的能力损失。 - *实现要求(重要,不能靠副作用实现)*:`goal_contract_root_steer_replacement_run_id`(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/steering.rs:763`)现在的资格判据是 `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 均被拒。 + *实现要求(重要,不能靠副作用实现)*: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)。 @@ -1875,7 +1875,7 @@ M0 完成不表示任何策划功能已上线。M1 的功能实现仍未开始 | --- | --- | --- | | 一 | `M0A-3` 批一:D9 二次作废 / D10 作废 / D11 入表、第 1.1 节新拓扑段、第 19 节第 2 条与第 24 节第 8 条改写、decision-log 补记 | **已完成**(见第 1.1 节批一执行状态) | | 二 | `WP1` + `WP2`:澄清轮次与返工深度拆分及其回归 | **已完成**(见第 23.5 节) | -| 三 | `project-planning` 的 agentCatalog 登记机制定稿 | **已完成**(机制冻结见第 3.1 节;代码落地属 M1) | +| 三 | `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 本体:策划闭环功能实现 | 未开始;合入门见下 | @@ -1948,6 +1948,30 @@ M0 完成不表示任何策划功能已上线。M1 的功能实现仍未开始 - 本裁决改的是 master 已发布的静态委派机制,须在 `decision-log.md` 补 dated 记录,写明 `WP1` 的 depth≤1 结论未被推翻。 - 回归必须覆盖:用户修订跳不增 `depth` 也不重置 `round`;连续多次修订均可通过;做游戏链路的返工仍在 `depth=1` 被拒;无该变体的历史记录分类行为不变;32 跳边界仍 fail closed。 +### 23.8 M1 的 PR 结构与合入门禁(2026-08-13) + +第 23.6 节步骤五原本只有一句「未开始」。M1 一旦并行开工,PR 划分与依赖顺序就是**工程流程契约**——它决定谁能先合、谁阻塞谁、每个 PR 的回滚边界在哪,属于必须团队可见的内容,不能只存在于个人工作稿里(PR 描述也不允许引用工作稿)。故在此冻结结构;每个 PR 的逐项做什么与逐点复核清单不在本节范围。 + +| PR | 主题 | 依赖 | 合入门禁 | +| --- | --- | --- | --- | +| `M1A-1` | source 常量 `project-supervisor-plan` + 进可信 matcher + steer 独立否决 | — | 全部可信 source 消费点逐点复核结论进 PR 描述;steer 在「plan source 在 matcher 内」与「不在」两种构造下均被拒 | +| `M1A-2` | 两层工具面 + `project-planning` 角色 brief | `M1A-1` | 两层工具面快照;`user.input_request` 在广告层不出现且执行层仍拒(第 19 节第 2 条两半) | +| `M1B-1` | `.agent/planning/` 存储层、strict schema、typed 指纹、版本链;含 `.agent/planning/**` 只挡写判据 | `M1A-2` | **第 9.1 节 golden vector 逐字节相等且指纹相等**(先于其它测试);create-only 与等前缀不可变 | +| `M1B-2` | `plan.submit_gdd` 原生工具与提交点 | `M1B-1` | 全部拒绝分支;提交点前后强杀恢复;同 submissionId replay 不产生 vN+1 | +| `M1C-0` | 新增 `StaticDelegateContractStatus::UserRevisionRequested` 与分类分支 | `M1A-1` | **本 PR 无写入方、是惰性路径,行为零变化**;做游戏链路返工仍在 `depth=1` 被拒;无该变体的历史记录分类不变 | +| `M1C-1` | `gdd-approval` pending、审批命令、receipt;receipt 写入上述 status | `M1B-2`、`M1C-0` | 第 13.0 节审批前置门:取证未通过时不出现审批卡;连续多次修订均可通过且 `repair_depth` 不变 | +| `M1C-2a` | Goal Contract 接线:turn 1 冻结、固定验收图、审批前置门取证 | `M1C-1` | turn 1 只能是一个 `agent.goal_contract`;`acceptanceNodes` 不接受自定义 | +| `M1C-2b` | 澄清中转接线、轮次派生、预算注入 | `M1C-2a` | 3 轮上限;continuation 重放幂等不增加轮次;答案绑定冲突被拒 | +| `M1D-1` | 前端 hydrate 与 GDD 审批卡 | `M1C-2b` | 前端只经 `hydrate_game_creator_plan_gdd_state` 读权威状态,不在页面侧合成批准事实 | +| `M1D-2` | 入口分流与阶段进度 | `M1D-1` | 「直接开建」跳过路径与现状零差异 | +| `M1E` | 端到端与故障注入收口 | `M1D-2` | 第 21 节测试矩阵中跨层场景 | + +**拆包纪律**: + +- `M1C-0` 与 `M1C-1` 拆开的理由是**风险类别不同**:前者改的是 master 已发布的静态委派机制,后者是 M1 新增功能。合并成一个 PR 会让「做游戏链路返工额度未被误放宽」这条最关键的回归淹没在审批闭环的 diff 里。拆开后 `M1C-0` 全程不产生半状态——它没有写入方,`UserRevisionRequested` 要到 `M1C-1` 才被写出。 +- `M1C-2` 拆成 a/b 的理由是两者失败模式无关:Goal Contract 是协议时序问题,澄清中转是身份派生与幂等问题。 +- 顺序是依赖顺序不是优先级。`M1C-0` 只依赖 `M1A-1`,可与 `M1B-*` 并行。 + ## 24. 最终不变量摘要 - 策划与构建是两个不同 profile/source 的 run,由用户动作连接,不由 Agent 自行升级。