立项策划:落地 M1A-4,plan 根 run 只能委派策划子 Agent
补 M1A-2 的反方向。原先只做了「目标是 project-planning 时要求父是 plan 根」,反过来「父是 plan 根时目标必须是 project-planning」没做, 也没记为 deferred。后果是 plan 根 run 可以委派任意专业 Agent,而被 委派者拿常规 standard 工具面(能写文件、跑命令),第 24 节「策划全程 零构建」当时只由 Prompt 兜底。 执行层:observe_agent_runtime_agent_delegate 补对称分支; observe_agent_runtime_agent_spawn_isolated 对 plan 根 run 一律拒——它是 第二条造子 Agent 的通道,只堵 delegate 等于留后门。两条共用 typed kind=plan-root-child-target-unsupported。 强弱判据分工与 M1A-3 一致:弱判据从 task journal 读 source 决定是否 管辖,强判据 validate_project_supervisor_plan_root_binding_at 决定是否 合法,其 Err 永远落进拒绝分支。不可写成 is_ok() 当作「不是 plan 根」, 那会在 binding 损坏时放行任意子 Agent 创建。 上下文层:plan source 下不拼 supervisorIntro 与 $visualContract。不改 .md 内容,不新增 composition key。 三条有意保留的取舍已冻结进 decision-log,非待办: $isolatedAgentTemplates 仍列出专业角色名(被执行层硬拒后的死文本, 上下文层收窄边界到此为止);task journal 读取失败对所有 source fail closed(改成放行是新的 fail-open);plan 根 prompt 断言偏弱 (执行层是该场景唯一保障)。 同步方案 §22 证据表、§23.8 PR 表、§24 不变量,并订正 M1A-2 决策条里 含糊的「Supervisor 根 run 继续使用现役工具面」。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,22 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-08-14 M1A-4:plan 根 run 的子 Agent 创建面收窄,并冻结三条已知残留
|
||||
|
||||
- **补的是 `M1A-2` 的反方向。** `M1A-2` 只做了「目标是 `project-planning` → 要求父是 plan 根」这一半;反过来「父是 plan 根 → 目标必须是 `project-planning`」当时没做,也没记为 deferred。后果是 plan 根 run 可以委派任意专业 Agent,而被委派者拿的是常规 `standard` 工具面(能写文件、跑命令),第 24 节「策划全程零构建」当时只由 Prompt 兜底、不是机制保证。
|
||||
- 落地:`observe_agent_runtime_agent_delegate` 补对称分支;`observe_agent_runtime_agent_spawn_isolated` 对 plan 根 run 一律拒绝(**它是第二条造子 Agent 的通道,只堵 delegate 等于留后门**)。两条通道共用 typed `kind=plan-root-child-target-unsupported`。Supervisor 根 run 的 prompt 在 plan source 下不拼 `supervisorIntro` 与 `$visualContract` 两段;不改 `.md` 内容、不新增 composition key(第 4.2 节)。
|
||||
- **强弱判据分工与 `M1A-3` 一致,不得合并**:弱判据(`task.source` 自称是 plan,经 task journal 读取而非 binding)决定「本约束是否管辖这条 run」;强判据 `validate_project_supervisor_plan_root_binding_at` 决定「它是否合法」。强判据 `Err` **永远落进拒绝分支**,绝不可写成 `else if validate(..).is_ok() { 拒 }`——那会把「binding 损坏」误归为「不是 plan 根」,恰好在 durable 状态损坏时放行任意子 Agent 创建。
|
||||
- 回归:`plan_root_delegate_rejects_non_planning_targets_but_keeps_planning_path`(含 `project-planning` 正向路径仍通过,避免把功能整个焊死)、`plan_root_delegate_and_spawn_isolated_fail_closed_when_binding_missing_or_corrupted`、`gui_root_delegate_and_spawn_isolated_are_unaffected_by_plan_root_symmetry`、`plan_root_supervisor_prompt_drops_intro_and_visual_contract_sections`、`non_plan_supervisor_prompt_stays_byte_identical_to_the_original_composition`。
|
||||
|
||||
**以下三条是经复核后有意保留的取舍,不是待办。后续 PR 不得在未重新裁决的情况下「顺手修掉」。**
|
||||
|
||||
1. **`$isolatedAgentTemplates` 仍会向 plan 根 run 列出全部专业角色名。** `art-director` / `design-foundation` / `art-asset-plan` / `code-prototype` 等名字来自 `$base`(`RUNTIME_PROMPT_RUNTIME_COMPOSITION` 的 `$isolatedAgentTemplates` 段,由 `GAME_CREATOR_AGENT_GROUP_DEFINITIONS` 运行时渲染),用途是广告 `agent.spawn_isolated` 的合法模板 id,**不在本次裁掉的两段之内**。裁掉它要动 `$base` 与隔离模板目录,波及全部 Agent。保留的依据是:`A2` 已对 plan 根 run 硬拒 `spawn_isolated`,该目录对 plan 根 run 是**死文本**,不构成可利用面。**结论:上下文层的收窄边界到此为止;「plan 根 run 的上下文里不出现其它 Agent 名」这一目标 M1 不成立,不要据此写验收句。**
|
||||
2. **plan 之外的 source 多了一个失败面。** 两个 observe 函数新增的 task journal 读取,其 `Err` 分支位于弱判据之前,对 `project-supervisor-gui/cli/game-chat` 同样生效。正常状态不触发(仅 journal I/O 损坏等异常)。保留的依据是:读不到 task 就无法判断这条 run 是不是 plan 根,此时 fail closed 是正确取向;改成「读失败就放行」会引入一个新的 fail-open。**这是对第 19 节「做游戏链路逐字不变」的一处有意例外,仅限异常路径。**
|
||||
3. **上下文层回归是弱断言。** `plan_root_supervisor_prompt_drops_intro_and_visual_contract_sections` 用「不含某几条独有短句」断言,不是对照组那种字节级 `assert_eq`。已实测非永真。已知漏报场景:若将来 `$visualContract` / `supervisorIntro` 被替换成措辞不同但仍暗示专业组扇出的新文本,这条不会报警。**执行层的 `A1`/`A2` 是该场景的唯一保障**——这也是本包把硬门排在裁段之前的原因。
|
||||
- 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` 第 4.3、22、23.8、24 节。
|
||||
|
||||
## 2026-08-13 M1A-2:planning 子 Agent 两层工具面与角色 brief 注入
|
||||
|
||||
- 落地范围:在 `M1A-1` 的 `project-supervisor-plan` source 基础上,收口两层工具面。Supervisor 根 run 继续使用 `standard` 的现役工具面;`project-planning` 只接受 `source=agent-delegate`、`profile=standard`、父 Agent 为 `project-supervisor` 的静态委派身份。
|
||||
- 落地范围:在 `M1A-1` 的 `project-supervisor-plan` source 基础上,收口两层工具面。Supervisor 根 run 继续使用 `standard` 的现役工具面;`project-planning` 只接受 `source=agent-delegate`、`profile=standard`、父 Agent 为 `project-supervisor` 的静态委派身份。**(2026-08-14 订正:「Supervisor 根 run 继续使用现役工具面」这句读起来像决定,实际是要求丢失——本包工作项原本还要求「按 source 收窄 Supervisor 侧委派面:做方案链路只应产生一条指向 `project-planning` 的委派」,该半未实现也未记为 deferred。已由 `M1A-4` 补齐,见本文件同名条。)**
|
||||
- planning 子 Agent 的当前 native action exact allowlist 只有 `file.read`、`file.list`;`update_agent_plan` / `respond_to_user` 是协议控制函数,不计入 action capability。MCP catalog 强制为空,`webSearchEnabled=false`,`collaborationPolicy=null`。`plan.submit_gdd` 刻意未注册、未广告、未执行,留给后续 `M1B-2`,因此本条不代表 GDD 提交、版本存储或审批闭环已完成。
|
||||
- Prompt:Prompt Bundle 新增并登记 `project-planning` role brief,standard planning child 的初始请求与 repair/rebuild 请求均注入同一 brief;Supervisor 和其它 Agent 不注入该 section。brief 只描述 Fast GDD 澄清、终态 `AGC_NEEDS_USER_INPUT_V1`、三轮边界、平台事实与低幻觉约束,不授予任何写入、命令、MCP、预览、生成或审批能力。
|
||||
- 纵深拒绝:广告层不再向 planning child 暴露 `user.input_request`;Provider parser、action batch/pending、并行只读、执行层和状态恢复均按原始 tool identity 再校验。伪造写入/命令/MCP、`project.search` 等映射为 `file.read` 的 alias、`user.input_request` 都 fail-closed;恢复旧快照不得把 planning 工具面扩回全量目录。委派子 Agent 原有 `validate_user_input_action_owner` 执行层拒绝继续保留。
|
||||
|
||||
@@ -1717,6 +1717,7 @@ M0 文档 PR 本身最低验证:Markdown 结构与三张 Mermaid 图可解析
|
||||
| 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/filesystem.rs:85-138`、`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_actions/project_gates.rs:808-860,934-948`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs:1792-1832` | **2026-08-13 收窄**:现役 collaboration blocker 对**策划子 Agent**返回不适用;Supervisor 根 run 侧不再整体豁免(见第 4.3 节)。仍需新增 plan GDD 专用完成门,检查提问/审批等待、receipt、observation、session 与 recovery 状态 |
|
||||
| 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 幂等;新增专用保留入口 |
|
||||
@@ -1961,6 +1962,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`、`M1A-2`、`M1A-3`
|
||||
| `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` | **第 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` 被拒;无该变体的历史记录分类不变 |
|
||||
@@ -1982,6 +1984,7 @@ M0 完成不表示完整策划闭环已经上线。`M1A-1`、`M1A-2`、`M1A-3`
|
||||
## 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-14 `M1A-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 是用户决定线性化点。
|
||||
|
||||
Reference in New Issue
Block a user