diff --git a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md index 8c2801ab3..36864c24e 100644 --- a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md +++ b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md @@ -82,8 +82,8 @@ M0 三个代码工作包无一行按 D6 编写,**不需要为新设计回退 - 第 3 节注册表全部身份常量、第 4 节拓扑图、第 4.1 / 4.2 节(整节作废后重写)、第 4.3 节工具清单 - 第 5.1 / 5.2 节 checkpoint 触发点、第 6 节 Prompt 权威稿 -- 第 8.3~8.6 节身份字段取值、第 9.1 节 golden vector 重算(现值 `d85c85dae3…` 必然失效) -- 第 12 节 submit dispatch 段、第 13.1 / 13.3 节 `agentId` 常量、第 14 节 activeQuestion 相关五行恢复语义 +- 第 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,此前尚未裁决,批二涉及注册表落笔前必须先冻结这一点。本轮文档修订范围不含批二,此处只记录状态变化。 @@ -164,9 +164,9 @@ WP1 定稿的澄清轮次/返工深度语义(供 D11 依赖,权威定义见 | 审批 Tauri command | `decide_game_creator_plan_gdd` | | 审批动作 | `approve \| revise \| reject` | | submit input schema | `plan-submit-gdd-input.v1` | -| 回答解释 checkpoint schema | `plan-decision-checkpoint.v1`(回答后的 plan 专用 `update_agent_plan` strict 变体) | +| ~~回答解释 checkpoint schema~~ | **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 嵌套对象) | -| plan Provider request kinds | `tool-plan \| plan-decision-checkpoint \| final-reply` | +| plan Provider request kinds | `tool-plan \| final-reply`(**2026-08-13 按 D11 移除 `plan-decision-checkpoint`**) | | 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` | @@ -192,7 +192,7 @@ WP1 定稿的澄清轮次/返工深度语义(供 D11 依赖,权威定义见 本文同时沿用现役 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-`。两者只在各自 requestId/action/ref 域内幂等,不能跨域比较、复用或互相恢复;下文需要消歧时分别称 answerResponseId 与 approvalResponseId,磁盘/wire 字段仍均为 `responseId`。 -`requestKind=plan-decision-checkpoint` 只对已通过 exact plan binding 的回答后解释请求合法;非 plan lifecycle、普通 tool-plan/final-reply 或 action batch 出现该 kind 一律失败关闭。checkpoint success 无 action batch,不能借新 kind 扩大 action 工具面。 +(**2026-08-13 删除**:原此处规定 `requestKind=plan-decision-checkpoint` 的合法性边界。该 kind 随 D10 作废,任何 lifecycle/batch 出现它一律失败关闭。) ### 3.1 `project-planning` 的 agentCatalog 登记机制定稿(2026-08-13,机制冻结,代码留给 M1) @@ -406,7 +406,7 @@ Runtime 注入并强校验以下精确结构: - **解释与下一步**: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 草稿内容的权威,但**不再是轮次状态机的权威**。 -> **随之而来的合同影响(M1 落地前必须收口,本轮只登记不展开)**:第 3 节注册表中的 `plan-decision-checkpoint.v1`、plan Provider request kinds 里的 `plan-decision-checkpoint`、以及为承载 checkpoint 而引入的 lifecycle `v3` / batch `v4` 增量字段,其存在理由均随本节重写而消失;第 8.6 节 `plan-session.v1` 的 `activeQuestion` 与 `roundsUsed` 字段、第 14 节与 activeQuestion 相关的恢复语义同理。这些是本次重写的**必然推论**,不是新的设计选择,但涉及多份 strict schema 与 golden vector,须作为一个独立小工作包统一改,避免边改边漏。 +> **随之而来的合同影响 —— 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`。 - session revision 1 由 Runtime 先写入固定 `initial-request` 决定:topic=`初始需求`、state=`confirmed`、answerSource=`user_freeform`、round=0、answerSummary 精确等于规范化后的 1~400 scalar 初始用户需求。Provider 不能改写或省略这条来源记录;超过上限的初始输入先要求用户收束,不能截断。 @@ -451,57 +451,30 @@ exact plan source 的 `user.input_request` 仍是四个 action tool 之一,不 三个 option 的标签与顺序必须逐字等于上表;plan question ID 在现役 snake_case 规则上进一步限制为最多 32 个 ASCII 字符,并且不能映射成 `initial-request`。Runtime 确定性令 `decisionId = questionId.replace('_', '-')`,因此该 ID 必然满足第 8.3 节 GDD decision ID 合同,Provider 不能另选身份。Provider batch 仍须满足 `user.input_request` sole-action 规则;exact plan 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不能走现役“少于两项则 NotNeeded”的优化。`user.input_request` 与 `plan.submit_gdd` 因此都有唯一 v4 member;decision-checkpoint/final-reply 无 action 才不创建 batch。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。 -> **2026-08-13 D11 作废横幅:本节以下段落至第 5.2 节末,连同第 8.6 节 `activeQuestion`/`roundsUsed`、第 9.1 节 golden vector、第 12 节、第 13 节、第 14 节中依赖 `plan-decision-checkpoint` 的部分,整体待改。** -> -> 这些内容描述的是 D10「Runtime 直投」状态机——策划节点持续存活、`user.input_request` 由它自己产生、Runtime 在同一 run 内截获并写 `activeQuestion` session checkpoint、回答后再发 `plan-decision-checkpoint` Provider 请求。D11 下这条链路的每一环都换了承载物:问题落在 delivery 而非 `activeQuestion`,线性化点是 delivery 的答案绑定而非新 session primary,「解释 + 下一步」由 continuation 子 Agent 的第一个普通 turn 完成而非专用请求 kind(依据见第 5.1 节重写后的四条)。 -> -> **未在本轮直接改写的原因,是它们与 golden vector 强耦合**:第 8.3~8.6 节的 golden JSON 示例、第 9.1 节的 typed 指纹(现值 `d85c85dae3…`)与第 12/13 节的 wire 版本(lifecycle `v3` / batch `v4` 的增量字段专为 checkpoint 而设)必须**一次性一起改**,且指纹必须由代码重新生成、不得手写。逐段修补会产生「schema 已改、vector 未换」的中间态,比暂时保留旧文更危险。 -> -> 处置:作为一个独立小工作包统一收口,前置是 M1 的 strict schema 实现(届时才有能生成指纹的代码)。在该工作包完成前,本横幅以下内容**只作为 D10 时代的历史记录**,不得作为实现依据;凡与第 4.1 / 4.2 / 4.3 / 5.1 节冲突处,一律以那四节为准。 +> **2026-08-13 按 D11 重写本节后半(原 D10「Runtime 直投」状态机整段作废)。** 原文描述的链路是:策划节点持续存活于同一 run,自己调 `user.input_request`,Runtime 在同一 run 内截获、写 `activeQuestion` session checkpoint,回答后再发一次 `plan-decision-checkpoint` 专用 Provider 请求取得设计解释,并以新 session primary 作为线性化点。D11 下这条链路的每一环都换了承载物,且**不是换实现是换机制**——用的是 PR #165 已发布、已有回归覆盖的静态委派澄清中转,不再自造状态机。 -Runtime 在普通 user-input dispatch 前截获 exact plan:先用 v4 batch 中的 durable provider/session/action binding 校验问题,再创建稳定 request sidecar;随后在项目锁内写 session successor,把完整规范化 question、`questionsSha256`、request/action/provider 身份和 round 放进 `activeQuestion`。只有该 session primary durable 后才能发布等待态和决策卡。batch 已有而 activeQuestion session checkpoint 未落时分两种:① current session 仍精确等于 binding,只允许 user-input sidecar 不存在,或已经是与 v4 batch/request identity 逐项相等的 pending 记录;恢复重放同一 action,缺 sidecar 时确定性创建同一 request,已有 exact pending 时直接复用并安装 activeQuestion。② current session 已是 binding 的合法 successor,且 activeQuestion、standalone pending、决策卡和 answer-prepared 均未形成,则旧问题尚未被业务消费;sidecar 只允许 absent/exact pending,Runtime 按第 12 节 stale 顺序先补 lifecycle completed、持锁把旧 batch 标为 superseded 并回读,再删除 exact pending sidecar并确认其 durable absent,最后清理旧 batch,之后才可按 successor 新 base 请求新问题。任一断点只重放同一清理步骤,不得展示或重发旧问题。sidecar 为 answer-prepared/answered/cancelled、内容/hash 冲突、存在 standalone pending/card,或 session 漂移不能证明为合法 successor 时进入 reconciliation;不得生成新的 question/request/decision ID 或把旧问题绑定到新 session。 +**决策卡的形状不变,承载物变了。** 上面那张映射表、三个固定选项、每次恰好一题、`decisionId = questionId.replace('_', '-')` 的确定性映射,全部继续有效;变的是这张卡由谁产生、经谁送达: -回答提交沿用现役 `requestId + responseId + answers` transport。规范化答案精确等于三个固定 label 之一时按上表识别为 option;其它值一律是自由填写,不能由 UI 另传一个未持久化的“答案类型”布尔值。plan 回答额外限制为 1~400 scalar,不得截断。exact plan 先把 sidecar写到可恢复的 `answer-prepared`,不能先发布 terminal observation,也不能直接把用户原文或预设 option description 当作 Agent 解释写入 session。 +1. **产生**:策划子 Agent **不能**调 `user.input_request`(委派子 Agent 带 parent 身份,执行层 `validate_user_input_action_owner` 直接拒绝)。它以 `AGC_NEEDS_USER_INPUT_V1` **终态信封退出本轮 run**,信封内是同一个规范化单题对象。Runtime 据此形成 `contract_status=NeedsUserInput` 的 delivery,问题原文与 `questionsSha256` 一并落在 delivery 里。 +2. **权威载体**:**delivery 就是「已问出、未回答」这一状态的权威**,不再需要 session 复制一份 `activeQuestion`。这消掉了 D10 里「batch 已有而 activeQuestion 未落」「activeQuestion 已落而 standalone pending 未落」等一整族需要逐格对账的中间态——它们的存在前提是同一份问题被复制到两处。 +3. **送达**:Supervisor 认领该 delivery 后,在**自己的** runtime/session 上创建 `waiting-for-user-input` pending。问题正文必须与信封逐字一致。 +4. **回答与线性化点**:用户在 Supervisor 会话内作答,`requestId` 与 `answersSha256` 原子绑回原 delivery。**该绑定就是本轮的线性化点**,取代 D10 的「新 session primary durable」。它由已发布代码保证「首次写入或逐字相同则幂等,绑到不同请求或不同答案则拒绝」,页面重载、Runner 重启或重放都不会把一条回答计成两轮。 +5. **解释与下一步**:Supervisor 随后发起 continuation 委派,Runtime 从 `(parent_run_id, 原 delegationId, questionsSha256, answersSha256)` 确定性派生新的 delegation 身份——**同一组问答只能派生同一个 continuation,重放天然幂等**。新一轮子 Agent 是新 run、同 session,它在自己的**第一个普通 tool-plan turn** 里既完成对上一轮回答的设计解释,也决定下一题或 `plan.submit_gdd`。 -`answer-prepared` 后,Runtime 从同一 current session primary 捕获 activeQuestion、完整答案、`requestId/responseId/answersSha256`、run/source/profile/Goal/steer identity,发起专用 Provider 请求: +因此 `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 的首轮已被它覆盖,不需要新机制。 -- `requestKind=plan-decision-checkpoint`;`requestSlot=decision-round-{N}-session-{sessionRevision}-repair-{K}`。首次请求和每次合法 session successor 都令 `K=0`,同一 captured session 的协议修复才令 `K` 单调加一、产生新 base attempt 0,并原样继承前一 repair binding 的 `supersededCheckpointProviderRequestIds`。触发修复且已通过 handoff storage 安全/容量门的 Provider 响应仍须先持久化 raw response handoff 并把原 lifecycle 补成 completed;它因未通过 checkpoint validator,不是可应用或可 supersede 的已验证 checkpoint success handoff,不能追加到该数组。恢复遇到原 request 的 raw handoff 时必须重放同一验证结果并幂等进入 `repair-{K+1}`,不得重发原 request;transport transient retry 保持完整 requestSlot 与该数组不变、只增加同 base request 的 attempt; -- composition/source kind 仍是 `supervisorPlanChat` / `SupervisorPlanChat`,MCP 与 action tool 均为空;只广告同名但 plan 专用 strict 变体的 `update_agent_plan`,不广告普通 steps 变体或 `respond_to_user`; -- structured injection 必须包含旧 session 的 Provider-facing 语义投影、activeQuestion 的 question/questionId/round、完整规范化 answer map,以及 Provider 必须回显的 `requestId/responseId/answersSha256/decisionId`。语义投影只含 phase/roundsUsed/accumulatedAgentMillis/decisionsSummary/prototypeValidationItems 和业务文本;不得注入 actionId/actionFingerprint/providerRequestId、source session fingerprint、run/profile binding、appliedAnswers、handoff/superseded 身份或其它 Runtime 控制字段。Provider response 不得同时包含普通 plan steps、action、response 文本或第二个函数调用; -- Provider transport/上游成功响应必须先通过现役 handoff storage 的大小、控制字符、敏感键、绝对路径、容量和 durable identity 门。通过该门后,无论 checkpoint 协议随后是否验证通过,都必须先写入 raw response handoff;通过 checkpoint validator 后它才成为可应用的 checkpoint success handoff。若 storage 门拒绝或 handoff 无法 durable 提交,禁止保存不安全正文、补 lifecycle completed、启动 protocol repair 或自动重发;沿现役 success-handoff failure 路径写安全诊断并进入 `PLAN_NEEDS_RECONCILIATION`,保留真实 started 历史供人工处置。checkpoint handoff 的专用 schema/path/slot/ledger 排序语义属于 M1 详细设计与合入前置决策,当前非交付检查点不选择实现方案;非 plan request 不得借该待定实现扩大 handoff 语义。 +**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,不得截断。 -该请求唯一允许的函数参数是完整 `plan-decision-checkpoint.v1`: +**转述保真是本方案唯一没有机制兜底的地方,此处如实记录。** Runtime 只校验 `questionsSha256` / `answersSha256` 的哈希绑定,**不校验转述内容与已确认答案的语义一致性**。做结构相等校验的 `static_delegate_clarification_pending_matches_delivery_at` 唯一生产调用点(`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_recovery.rs:791`)外层套着 `run_profile == AGENT_RUNTIME_RUN_PROFILE_AUTONOMOUS_GAME_BUILD`,而做方案链路跑 `standard`,**该校验根本不触发**。因此在本链路上,Supervisor 既可以自行发起提问,也可以改写子 Agent 的问题原文,Runtime 都不拦。两个后果都要写清楚: -```jsonc -{ - "decisionCheckpoint": { - "schemaVersion": "plan-decision-checkpoint.v1", - "requestId": "<原 user-input request id>", - "responseId": "<本次用户回答 response id>", - "questionId": "route_replay", - "answersSha256": "<64 位小写 hex>", - "decisionId": "route-replay", - "topic": "路线重玩", - "round": 1, - "state": "prototype_pending", - "answerSource": "user_option", - "answerSummary": "用户希望先验证分支是否足以驱动第二局改变路线", - "prototypeValidationItem": { - "id": "route-replay", - "question": "分支路线是否驱动第二局选择变化", - "microPrototype": "制作两次二选一路线和光量结算", - "observation": "记录第二局是否主动改变分支并说明原因", - "passCriterion": "三名测试者中至少两名主动改变路线且能说出取舍" - } - } -} -``` +- **问题侧**:用户看到的问题可能不是子 Agent 想问的。 +- **答案侧**:用户的答案物理落在 Supervisor 的会话文件里,**不在子 Agent 的会话里**。子 Agent 对「用户答了什么」的全部认知,来自 Supervisor 写进 continuation 委派 task 文本的转述(硬上限 `AGENT_RUNTIME_TASK_MAX_CHARS = 4_000`,超限直接 `Err`,不静默截断)。漏一条或改写一条,子 Agent 就会按空白重问或按错误前提出稿。 -Runtime 必须从 sidecar 与 activeQuestion 推导并逐项校验 `requestId/responseId/questionId/answersSha256/decisionId/round`。固定标签分别只接受表中的 state/answerSource;自由填写只接受 `confirmed + user_freeform`。`topic` 为 1~80 scalar,`answerSummary` 为 Agent 对回答形成的 1~400 scalar 设计解释:必须保留用户意图,但不要求逐字等于自由填写原文。`prototype_pending` 必须携带同 decision ID 的完整 prototypeValidationItem;其它 state 必须精确为 `null`。任何身份回显、固定映射、长度或 prototype 一一对应不符都按协议错误处理,不能由 Runtime自行补写解释。 +这是**产品约束不是机制约束**,M1 前只有 Prompt 兜底。要把它变成机制约束,须为 `standard` 下的 plan 根 run 单独接一道等价校验(见第 23.6 节「待执行项」)。本文档不假装该校验已经存在。 -最终有效 checkpoint success handoff durable 后,Runtime 在统一锁顺序下取得项目锁,重验 handoff/lifecycle/request context、answer sidecar 和 current session 仍逐项一致,再以磁盘 session revision/fingerprint 做 CAS:`roundsUsed += 1`,追加 decisionsSummary、prototypeValidationItems 与 appliedAnswers,清空 activeQuestion,并把 phase 改回 collecting。新 session primary 完整安装、文件与父目录同步并回读成功是本轮解释的线性化点;`appliedAnswers` 必须保存最终 checkpoint 的 providerRequestId、handoff responseFingerprint 与 decisionCheckpointFingerprint,证明设计解释来自哪次 durable Provider success。 +**轮次计数。** 本轮是第几轮由委派链上的 `clarification_round` 派生值决定(沿 `repair_of_delegation_id` 上溯推断,语义见第 23.5 节),上限 3;session 不再自累加 `roundsUsed`。达到上限后 Runtime 在下一轮子 run 的上下文里注入「必须出稿」,若子 Agent 仍输出信封则拒绝该信封并要求改为 `plan.submit_gdd`。 -只有越过该线性化点,Runtime 才把 sidecar 置为 answered、写原 user-input terminal observation、把原 action 标为 observed,并基于新 session 发起普通 tool-plan 请求。checkpoint success handoff 已落而对应 session CAS 尚未落时分两种:current session 仍精确等于 handoff binding,则只重放同一 handoff 做 CAS,不再请求模型;current session 已沿合法 successor 链前滚、仍保留同一 activeQuestion/answer identity 且 appliedAnswers 尚未消费该 handoff,则旧 handoff 只作为 completed 历史,不能应用到新 context,必须从 successor 的 `repair-0` 新 base attempt 0 重新请求,并把前驱 superseded 数组复制后追加该成功 request ID。若 session 已含逐项匹配的 appliedAnswers,只补派生投影,不再增加轮次或请求模型。`answer-prepared` 后尚无 lifecycle 时启动 checkpoint base request;started 且 session/context/answer 未变时,只有已取得已知 retryable failure,或经 durable owner/lease/boot identity 证明旧物理请求已终止,才能先把旧 attempt 分别闭合为 `failed` 或 `interrupted`,同步回读后以同 base 的 attempt N+1 重试。无法证明旧 attempt 已终止时保持恢复态,不得并发重发。合法 session 前滚但 activeQuestion/answer identity 仍相同时,先把无 handoff 的旧 started attempt 闭合为 interrupted,再从新 base attempt 0 重发并原样复制前驱 superseded 数组。activeQuestion 消失/换题、binding 交叉指向或同 requestId/responseId 的 answer/checkpoint fingerprint 冲突时返回 `PLAN_SESSION_RECOVERY_REQUIRED`、`PLAN_NEEDS_RECONCILIATION` 或 `PLAN_ANSWER_IDENTITY_CONFLICT`,不得从聊天正文、option 文案或原始回答自行重建 Agent 解释。 +**Provider batch 规则。** exact plan 的普通 tool-plan 只要产生 action,即使仅一项也必须强制 durable 写 v4 batch,不走现役「少于两项则 NotNeeded」的优化。Supervisor 侧的 `user.input_request` 与策划子 Agent 侧的 `plan.submit_gdd` 因此各有唯一 v4 member;final-reply 无 action 才不创建 batch。非 plan source 的 input wire、数量和校验保持现状,任何额外 plan metadata 都因 unknown field 失败。 ### 5.3 Fast GDD 固定内容 @@ -511,7 +484,7 @@ MVP 明确排除多人、商城、服务器、开放世界、赛季、复杂社 ## 6. Prompt 权威稿 -M1 的 `supervisorPlanChat` 必须以本节为语义基线;允许因 Prompt Bundle 结构拆成多个 section,但不得改变边界、轮次、选项、状态、平台事实或工具面。 +**(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,但不得改变边界、轮次、选项、状态、平台事实或工具面。 ```text 你是“立项策划 Agent”。用户通常只给一句游戏需求或简短玩法。你的职责是用短而尖锐的游戏设计对话,帮助用户确认最少量、最关键的决定,并提交一份可审批的 MVP Fast GDD。 @@ -519,6 +492,7 @@ M1 的 `supervisorPlanChat` 必须以本节为语义基线;允许因 Prompt Bu 【任务边界】 - 你只负责玩法澄清、原型验证建议和最小 GDD。 - 你不得委派或调度其他 Agent,不得写文件、生成图片、执行命令、启动预览或构建。 +- 你**不能直接向用户提问**:你没有 user.input_request,即使看得见,调用也会被 Runtime 拒绝(委派子 Agent 带 parent 身份)。要提问就以 AGC_NEEDS_USER_INPUT_V1 终态信封结束本轮 run,由 Supervisor 代为向用户提问。 - 只定义一个完整可玩闭环;MVP 不含多人、商城、服务器、开放世界、赛季、复杂社交、完整剧情或全量内容。 【平台事实:已确认,不可更改,不得向用户提问】 @@ -527,15 +501,14 @@ M1 的 `supervisorPlanChat` 必须以本节为语义基线;允许因 Prompt Bu 【目标】 1. 用户最多回答 3 个关键问题后,提交一份最小闭环 GDD。 -2. 每轮回答进入同一 run;中断后从 Runtime 提供的最近持久状态继续。 +2. 每一轮是一个**新 run、同 session**:你看得到自己前几轮说过的话,但**看不到用户的答案原文**——答案物理落在 Supervisor 的会话里,你只能从本次委派的任务文本读到它的转述。任务文本与你的记忆冲突时以任务文本为准;任务文本里没有的已回答信息,按仍然空白处理,不要凭空假设。 3. 只有用户在 GDD 审批卡上批准后,Runtime 才会产生有效 approve receipt;你不能自行宣称批准,也不执行下游动作。 【快速追问规则】 - 除非用户说“直接出稿”,否则先澄清。最多 3 个主动问题,每轮只有 1 个主要决定。 - 优先顺序:核心行为与本局目标 → 重玩动力 → 制作边界与 MVP。 -- 每轮调用 user.input_request 输出一张决策卡。header 固定为“第N轮·关键决定”;正文以“当前要决定:…”开头,并包含为什么现在问、我的推荐、好处、代价;三个选项固定为“接受推荐”“暂按推荐”“需要原型验证”。自由填写按用户明确输入处理。 -- user.input_request 只提交现役 questions strict input,不预填用户尚未给出的决定解释。回答后 Runtime 会发起专用 decision-checkpoint turn;该 turn 只调用 plan 专用 update_agent_plan strict 变体,用 plan-decision-checkpoint.v1 解释用户回答,不调用 action 或 respond_to_user。 -- checkpoint 中必须逐字回显 Runtime 注入的 requestId、responseId、questionId、answersSha256、deterministic decisionId 和 round。answerSummary 是你基于完整回答形成的设计解释,不是机械复制选项说明;自由填写必须保留用户意图。只有“需要原型验证”携带同 ID 的 30~90 分钟 prototypeValidationItem,其它状态必须为 null。 +- 每轮以 AGC_NEEDS_USER_INPUT_V1 终态信封输出一张决策卡后停止,不要再调任何工具。header 固定为“第N轮·关键决定”;正文以“当前要决定:…”开头,并包含为什么现在问、我的推荐、好处、代价;三个选项固定为“接受推荐”“暂按推荐”“需要原型验证”。自由填写按用户明确输入处理。 +- 信封只放规范化后的问题本身,不预填用户尚未给出的决定解释。你对上一轮回答的设计解释,写在**下一轮 run 的第一个普通回合**里,与「下一题还是出稿」的判断一并给出。 - 用户说“直接出稿”、第 3 轮已经完成、剩余问题不影响首个可玩闭环,或 Runtime 提示接近 240 秒 Agent 活跃预算时,立即整理并提交。 【低幻觉规则】 @@ -693,6 +666,8 @@ input 中必须逐项包含并精确等于 source session 的全部 `decisionsSu ### 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 的真实 source `agent-delegate`。字段声明顺序即下方顺序,是第 9 节 typed 指纹的受保护字节。 + 顶层字段顺序和覆盖范围固定为: ```jsonc @@ -704,13 +679,17 @@ input 中必须逐项包含并精确等于 source session 的全部 `decisionsSu "submissionId": "<原 plan.submit_gdd durable actionId>", "approvalRequestId": "gdd-approval-", "actionFingerprint": "<64 位小写 hex>", - "source": "project-supervisor-plan-chat", + "agentId": "project-planning", + "source": "agent-delegate", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", - "sessionId": "", + "rootAgentId": "project-supervisor", + "rootRunId": "", + "delegationId": "<产生本次提交的委派 id>", + "sessionId": "<策划子 Agent 的持久 session id>", "sourceSessionRevision": 3, "sourceSessionFingerprint": "sha256-serde-json-v2:", - "createdByRunId": "", + "createdByRunId": "<提交所在的策划子 run id>", "createdAtUtc": "2026-08-10T00:00:00.000Z", "game": { "title": "...", @@ -829,9 +808,12 @@ input 中必须逐项包含并精确等于 source session 的全部 `decisionsSu "actionFingerprint": "<64 位小写 hex>", "fingerprint": "...", "file": "gdd.v1.json", - "source": "project-supervisor-plan-chat", + "agentId": "project-planning", + "source": "agent-delegate", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", + "rootRunId": "...", + "delegationId": "...", "sessionId": "...", "sourceSessionRevision": 3, "sourceSessionFingerprint": "sha256-serde-json-v2:", @@ -866,7 +848,7 @@ entries 必须从 1 连续递增,文件名与 version 精确一致,且逐项 "approvalRequestId": "gdd-approval-", "responseId": "gdd-response-", "decisionFingerprint": "sha256-serde-json-v2:", - "source": "project-supervisor-plan-chat", + "source": "project-supervisor-plan", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", "sessionId": "...", @@ -882,7 +864,7 @@ entries 必须从 1 连续递增,文件名与 version 精确一致,且逐项 ### 8.6 `plan-session.v1` -> **2026-08-13:本节含依赖 `plan-decision-checkpoint` / `activeQuestion` 的 D10 时代内容,与 golden vector 强耦合,整体待改。处置与理由见第 5.2 节「D11 作废横幅」;凡与第 4.1 / 4.2 / 4.3 / 5.1 节冲突处以那四节为准。** +> **2026-08-13 按 D11 整节改写。** 删除三处 D10 遗留:`activeQuestion`(问题现在落在 delivery 上,delivery 就是「已问出、未回答」的权威载体)、`roundsUsed` 作为轮次权威(轮次改由委派链推断的 `clarification_round` 决定)、`appliedAnswers` 里全部 `checkpoint*` / `supersededCheckpointHandoffs` 字段(`plan-decision-checkpoint` 请求 kind 随 D10 作废)。同时补上 D11 需要的链路锚点 `rootAgentId` / `rootRunId` / `latestDelegationId`。 ```jsonc { @@ -892,15 +874,17 @@ entries 必须从 1 连续递增,文件名与 version 精确一致,且逐项 "sessionRevision": 1, "previousFingerprint": null, "sessionFingerprint": "sha256-serde-json-v2:", - "source": "project-supervisor-plan-chat", + "agentId": "project-planning", + "source": "agent-delegate", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", + "rootAgentId": "project-supervisor", + "rootRunId": "...", + "latestDelegationId": "...", "sessionId": "...", "activeRunId": "...", "lastRunId": "...", "phase": "collecting", - "roundsUsed": 0, - "activeQuestion": null, "accumulatedAgentMillis": 0, "appliedSteerCursor": 0, "decisionsSummary": [ @@ -921,21 +905,35 @@ entries 必须从 1 连续递增,文件名与 version 精确一致,且逐项 } ``` -`phase` 取 `collecting | awaiting_user_input | awaiting_gdd_approval | revision_requested | approved | rejected | recovery_required`。`activeRunId` 为 opaque ID 或 `null`,只有非终态 plan run 时可非空;`lastRunId` 始终记录最近一次 plan run。`roundsUsed` 为 `0..=3`;`accumulatedAgentMillis` 为 `u64`,只单调增加。`appliedSteerCursor` 为现役 durable steer cursor,初始 0、只单调增加;每次接受会改变后续 Provider context 的 plan steer 时,必须先写 session successor,不能只改聊天消息。 +`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、只单调增加。 -`activeQuestion` 为 `null`,或以下完整 strict shape:`{requestId, actionId, actionFingerprint, providerRequestId, sourceSessionRevision, sourceSessionFingerprint, question, questionsSha256, questionId, round, askedAtUtc}`。`question` 是第 5.2 节完整规范化单题对象,`questionsSha256` 精确等于现役 sidecar 的 questions hash;request/action/provider ID 分别来自 user-input sidecar、pending action 和产生该 action 的 v4 batch,source session binding 来自同一 v3 lifecycle/v4 batch。`questionId` 必须等于 question.id,round 必须等于 `roundsUsed+1` 且不超过 3;只有 `phase=awaiting_user_input` 时允许非空。 +**轮次不是 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 对象:`{requestId, questionId, responseId, actionId, actionFingerprint, answersSha256, decisionId, questionSessionRevision, questionSessionFingerprint, checkpointProviderRequestId, supersededCheckpointHandoffs, checkpointResponseFingerprint, decisionCheckpointFingerprint}`。这里的 `appliedAnswers.responseId` 明确是现役 user-input 的 answerResponseId,不是 `gdd-response-*` 审批 ID;`answersSha256` 精确复用现役 user-input sidecar 对规范化 answers map 的裸 64-hex SHA-256,只绑定 transport,不冒充 planning typed fingerprint。question session identity 指向最终 checkpoint Provider request 与 CAS 前共同绑定、且含 activeQuestion 的 primary。`checkpointProviderRequestId` 与 `checkpointResponseFingerprint` 精确来自最终有效 durable success handoff,`decisionCheckpointFingerprint` 使用第 9 节专用 domain 重算。 +`appliedAnswers` 为 0~3 个按 round 递增的 strict 对象,**2026-08-13 按 D11 收窄为**:`{delegationId, continuationDelegationId, requestId, questionId, responseId, questionsSha256, answersSha256, decisionId, round}`。 -`supersededCheckpointHandoffs` 为 0~16 个按最老到最新排列的 strict 对象:`{providerRequestId, sessionRevision, sessionFingerprint, checkpointResponseFingerprint, decisionCheckpointFingerprint}`。CAS 前,Runtime 必须从仍保留的完整 durable lifecycle/handoff 重验并生成这些摘要;其 providerRequestId 序列必须逐项等于最终 request binding 的 `supersededCheckpointProviderRequestIds`。每项必须属于同 project/session/run/requestId/responseId/answersSha256、绑定当前 question session 的严格祖先、lifecycle completed 且 handoff success,不能包含 failed/interrupted、分叉、不同答案或最终 checkpointProviderRequestId。新 session primary durable 后,这些摘要由 sessionFingerprint 保护,成为旧 handoff 清理后的长期恢复证明;后续读取不再要求历史 handoff 文件仍存在。`appliedAnswers.length=roundsUsed`,每项 decisionId 必须唯一引用同 round 的 decisionsSummary;requestId、actionId 与最终 checkpointProviderRequestId 分别全局唯一,`(requestId, responseId)` 组合唯一。answerResponseId 只在所属 requestId 域内幂等,不同问题允许合法复用同一文本值,读取或 CAS 不能单独按 responseId 判冲突;各轮 superseded 摘要彼此不相交且不得与任一 final checkpoint ID 相交。`latestSubmittedRef` 为 `null` 或 `{gddId, version, fingerprint}`;`lastDecisionRef` 为 `null` 或 `{version, responseId, action, receiptFingerprint}`,其中 `lastDecisionRef.responseId` 明确是 approval receipt 的 approvalResponseId。 +- `delegationId` 是产生该问题的那条 `NeedsUserInput` delivery;`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,每次成功 checkpoint 必须在项目锁内以磁盘 revision/fingerprint 做 CAS 后加一;不得跳号。`sessionFingerprint` 覆盖除自身外所有字段,包含 `previousFingerprint`。合法 successor 必须满足 `new.revision=old.revision+1` 且 `new.previousFingerprint=old.sessionFingerprint`。 +`sessionRevision` 初始为 1,每次成功推进必须在项目锁内以磁盘 revision/fingerprint 做 CAS 后加一;不得跳号。`sessionFingerprint` 覆盖除自身外所有字段,包含 `previousFingerprint`。合法 successor 必须满足 `new.revision=old.revision+1` 且 `new.previousFingerprint=old.sessionFingerprint`。 -session 是未提交解释状态的权威:有活跃 plan run 时 primary 缺失、损坏或链分叉必须进入 `PLAN_SESSION_RECOVERY_REQUIRED`,不能根据 GDD、聊天摘要或原始回答静默重置轮次。若回答 sidecar 已到 answer-prepared 但 session 尚未 checkpoint,恢复必须先取得或重放与该 activeQuestion/answer identity 精确绑定的 durable decision-checkpoint handoff;没有有效 handoff 时只能恢复同一专用 Provider 请求,不能由 Runtime 猜解释。在新的 session revision durable 前不算已完成一轮。若 session 已有 appliedAnswers 而 sidecar/observation 落后,则只能补齐逐项相同的派生投影,不能再次请求模型、写 decision 或增加 roundsUsed。 +**(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 指纹合同 @@ -961,7 +959,7 @@ domain 固定为: | 对象 | domain | 排除字段 | | --- | --- | --- | | GDD | `genarrative.plan.gdd.v1` | 外层 `fingerprint` | -| 回答解释 checkpoint | `genarrative.plan.decision-checkpoint.v1` | 无;覆盖完整 `plan-decision-checkpoint.v1` | +| ~~回答解释 checkpoint~~ | ~~`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` | @@ -969,28 +967,30 @@ domain 固定为: | decision comment | `genarrative.plan.gdd-comment.v1` | 无 | | Provider request context | `genarrative.plan.provider-request-context.v1` | 无 | -回答解释 checkpoint 的字段声明顺序固定为第 5.2 节 `decisionCheckpoint` 内显示顺序;`prototypeValidationItem` 即使为空也显式序列化为 `null`。该 typed fingerprint 写入 session.appliedAnswers,只证明某次 durable Provider success handoff 中被接受的设计解释,不能代替 handoff responseFingerprint、answer transport hash 或 approval decisionFingerprint。 +(**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`,读取、恢复和构建准入时必须重算。 -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, source, runProfile, runProfileBindingFingerprint, goalId, goalRevision, goalSnapshotFingerprint, sessionRevision, sessionFingerprint, appliedSteerCursor, requestKind, requestSlot, supersededCheckpointProviderRequestIds, webSearchEnabled, requestContextFingerprint`;所有字段来自同一 captured context/lifecycle binding。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。 +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, rootRunId, delegationId, source, runProfile, runProfileBindingFingerprint, goalId, goalRevision, goalSnapshotFingerprint, sessionRevision, sessionFingerprint, appliedSteerCursor, requestKind, requestSlot, webSearchEnabled, requestContextFingerprint`;所有字段来自同一 captured context/lifecycle binding。(**2026-08-13 按 D11 更正**:移除随 D10 作废的 `supersededCheckpointProviderRequestIds`——它只服务 checkpoint 协议修复的传递闭包;补入 `rootRunId` / `delegationId`,因为 D11 下同一 gddId 会跨多个策划子 run,request identity 必须能定位到具体是哪一跳。)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:本节含依赖 `plan-decision-checkpoint` / `activeQuestion` 的 D10 时代内容,与 golden vector 强耦合,整体待改。处置与理由见第 5.2 节「D11 作废横幅」;凡与第 4.1 / 4.2 / 4.3 / 5.1 节冲突处以那四节为准。** +> **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` canonical value;外层存储字段 `fingerprint` 不在 value 中。数组顺序、中文、`null`、空数组、Runtime 注入的 session binding、initial request 和 prototype pass criterion 均为受保护字节。精确 UTF-8 bytes 无 BOM、无结尾换行,共 3707 bytes: +以下一行是完整 `FingerprintEnvelope` canonical value;外层存储字段 `fingerprint` 不在 value 中。数组顺序、中文、`null`、空数组、Runtime 注入的 session binding、initial request 和 prototype pass criterion 均为受保护字节。精确 UTF-8 bytes 无 BOM、无结尾换行,共 3857 bytes: ```json -{"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","source":"project-supervisor-plan-chat","runProfile":"standard","runProfileBindingFingerprint":"2222222222222222222222222222222222222222222222222222222222222222","sessionId":"session-golden-001","sourceSessionRevision":3,"sourceSessionFingerprint":"sha256-serde-json-v2:3333333333333333333333333333333333333333333333333333333333333333","createdByRunId":"run-golden-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":"三名测试者中至少两名主动改变路线且能说出取舍"}]}} +{"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":"三名测试者中至少两名主动改变路线且能说出取舍"}]}} ``` 预期输出: ```text -sha256-serde-json-v2:d85c85dae3500ef90e5bc268e00dc4ef17fa039798837dd283310aa31f2c644d +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 拒绝,不能混淆两个门禁。 @@ -1045,9 +1045,9 @@ flowchart LR ## 12. GDD 提交合同 -> **2026-08-13:本节含依赖 `plan-decision-checkpoint` / `activeQuestion` 的 D10 时代内容,与 golden vector 强耦合,整体待改。处置与理由见第 5.2 节「D11 作废横幅」;凡与第 4.1 / 4.2 / 4.3 / 5.1 节冲突处以那四节为准。** +> **2026-08-13 按 D11 更正。** 提交者由「唯一那个 plan run」改为**策划子 Agent**(`agentId=project-planning`,`source=agent-delegate`);binding 补 `rootRunId` / `delegationId`;随 D10 作废的 `plan-decision-checkpoint` request kind 与 `supersededCheckpointProviderRequestIds` 数组一并移除。提交点、幂等域与 create-only 发布语义**未变**。 -`plan.submit_gdd` 由 Agent 调用,但只有 Runtime 写文件。它必须是一次 Provider 响应中的唯一 action tool;同一响应可以更新 `update_agent_plan`,但不得把 submit 与 `file.read`、`file.list`、`user.input_request` 或第二次 submit 混入同一 action batch。Runtime 在 batch 建立 durable actionId 之前拒绝混批,避免审批等待落在 generic multi-action cursor 中间。 +`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 之前拒绝混批,避免审批等待落在 generic multi-action cursor 中间。 产生 submit 的 Provider request 必须先冻结 durable source-session binding,不能只放在内存: @@ -1056,15 +1056,17 @@ flowchart LR "schemaVersion": "plan-provider-session-binding.v1", "projectId": "...", "gddId": "gdd-...", - "agentId": "project-supervisor", + "agentId": "project-planning", "taskId": "...", "providerRequestId": "provider-request-<64 位小写 hex>", "sessionId": "...", "runId": "...", + "rootRunId": "...", + "delegationId": "...", "goalId": "...", "goalRevision": 1, "goalSnapshotFingerprint": "<现役 goal snapshot fingerprint>", - "source": "project-supervisor-plan-chat", + "source": "agent-delegate", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", "sessionRevision": 3, @@ -1072,15 +1074,14 @@ flowchart LR "appliedSteerCursor": 0, "requestKind": "tool-plan", "requestSlot": "loop-2-repair-0", - "supersededCheckpointProviderRequestIds": [], "webSearchEnabled": false, "requestContextFingerprint": "sha256-serde-json-v2:" } ``` -`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 漂移。composition/sourceKind 必须为 `supervisorPlanChat` / `SupervisorPlanChat`;webSearchEnabled 固定为 false;mcpTools 必须是显式空数组;structuredInjections 只含当前轮次/活跃毫秒、上述 Provider-facing session 语义投影、平台事实、存在时的审批业务 observation,以及 checkpoint 所需的最小 answer identity。`providerRequestId`、lifecycle/handoff ID、action/binding fingerprint、superseded 数组/摘要及其它 Provider/Runtime 控制元数据不得进入 messages、工具描述或 structured injection;它们只存在于 durable binding/base identity、session 私有 checkpoint 与恢复校验。 +`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 漂移。composition/sourceKind 按第 4.2 节:策划子 Agent 复用现役 `runtime` composition,Supervisor 沿用现役 supervisor composition,**不新增第四套**(原此处写死的 `supervisorPlanChat` / `SupervisorPlanChat` 随 D6/D11 作废);webSearchEnabled 固定为 false;mcpTools 必须是显式空数组;structuredInjections 只含链路现算的澄清轮次/活跃毫秒、上述 Provider-facing session 语义投影、平台事实,以及存在时的审批业务 observation。`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/空数组/正文任一字节变化都会改变它。普通 plan tool-plan 按当前状态广告四 action tool 与两 control function;第 5.2 节 checkpoint 请求只广告 plan 专用 strict `update_agent_plan`。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。 +为遵守第 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。其它组合失败关闭。该三元组进入 binding 与 base request ID,恢复不能从当前 Goal 补写旧请求。 @@ -1091,31 +1092,33 @@ Goal 三元组沿用现役精确空值合同:没有 Goal 时必须是 `goalId= 3. 发送前重新取得项目锁,逐项重读 session revision/fingerprint、appliedSteerCursor、run/profile/Goal identity 与 captured context。任一变化都丢弃整个已构建 object,回到第 1 步;不能局部替换 binding 或 messages。 4. 仍持锁时,从第 9 节规定的 plan request identity 生成稳定 base providerRequestId,解析本次 attempt,把上述 strict binding 先 durable 写入 lifecycle `started`;释放锁后发送的必须是第 2 步同一个 object,不能再次调用 builder。 -成功 response handoff 创建 provider batch 时,batch 必须持久化完全相同的 `providerRequestId + planningSessionBinding`,且 batch identity 计算覆盖两者;batch 自身的 runId 与 actionId/actionFingerprint 完成 request → context → session → run → action 关联。exact plan 的 final-reply 等无 action batch 请求仍写 v3 lifecycle 和同一 binding;第 5.2 节 `plan-decision-checkpoint` 先写 durable success handoff、再把 v3 lifecycle 终结为 completed,但绝不创建 v4 action batch。非 plan lifecycle/batch 不伪造该 binding。 +成功 response handoff 创建 provider batch 时,batch 必须持久化完全相同的 `providerRequestId + planningSessionBinding`,且 batch identity 计算覆盖两者;batch 自身的 runId 与 actionId/actionFingerprint 完成 request → context → session → run → action 关联。exact plan 的 final-reply 等无 action batch 请求仍写 v3 lifecycle 和同一 binding。(原此处描述的 `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 与存在于外层的同名字段也必须逐项相等。`supersededCheckpointProviderRequestIds` 是 binding v1 的 required 数组:tool-plan/final-reply 和首次 checkpoint 必须为空;后续 checkpoint replacement 先复制唯一前驱 binding 的既有数组。若前驱已有 completed、已通过 validator 的 checkpoint success handoff 且尚未被 session 消费,再在末尾追加该前驱 providerRequestId;若前驱只有 failed/interrupted/无 handoff,则数组保持不变;同 session 的 protocol repair 也必须原样复制数组,不能把协议无效的 raw response handoff 当作已验证 checkpoint success handoff 追加。数组因此只收集成功、协议有效但未应用的历史 checkpoint handoff,允许 0~16 项,形成无重复、无分叉的传递闭包;它只进入 durable binding、lifecycle 与第 9 节 base ID,不进入 Provider messages/tools/structured injection 或 requestContextFingerprint。超过 16、前驱链缺失、repair 改写数组或出现两个 replacement 分支时进入 `PLAN_SESSION_RECOVERY_REQUIRED`。非 plan 路径继续写现役 lifecycle v2 / batch v3。reader 继续按原合同双读既有 lifecycle v1/v2 与 batch v1/v2/v3,不原地升级或重写;这些旧记录只能按非 plan 语义恢复。任何旧 schema 声称 `source=project-supervisor-plan-chat`,或新 plan schema 缺 binding/夹带额外字段,都失败关闭并进入 reconciliation,不能用默认值补齐。 +外层 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-13 删除** binding v1 的 `supersededCheckpointProviderRequestIds` 数组:它唯一的用途是维护「同一 session 内多次 checkpoint 协议修复」的传递闭包,而 D11 下每轮都是独立的新 run、上一轮已终态,不存在需要跨请求累积 superseded 闭包的情形。binding 改为携带 `rootRunId` 与 `delegationId`,把请求锚到具体委派跳。)非 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 或重新请求: 1. 同 submissionId 的严格 GDD 已 create-only 提交:按 historical binding replay。 -2. sole `user.input_request` 的 current session.activeQuestion,或仍保留同一 activeQuestion 的合法 successor,精确引用原 `providerRequestId/sourceSessionRevision/sourceSessionFingerprint/actionId/actionFingerprint/requestId/questionId/questionsSha256`,且 request sidecar 与 v4 batch 中唯一 action 逐项同一:activeQuestion primary 完整安装、同步并回读成功就是原 v4 batch 被业务消费的线性化点;common pending/runtime/card 只是可修复投影。该线性化点允许的 exact pre-wait 状态固定为:v4 batch `status=ready + nextActionIndex=0 + actions.length=1`,唯一内嵌 member `status=approved` 且 actionIndex=0,user-input sidecar `status=pending`,同 run 的 standalone `game-creator-pending-action.v5` 尚不存在。之后 waiting writer 直接创建同 identity 的 standalone pending,固定 `executionMode=auto + status=waiting-for-user-input + observation=null`;v4 batch 及内嵌 member仍保持上述 ready/approved/0 状态,直到回答 observation 完成原 action。恢复只允许三种状态:① standalone pending 尚不存在且 sidecar pending,只能从 activeQuestion + exact v4 batch 补同一 waiting;② standalone pending 已是上述 waiting 形状且 sidecar pending,幂等补 runtime/task/event/card 后恢复同一等待;③ standalone pending 仍是 exact waiting 形状且 sidecar 已单向进入 answer-prepared,原 user-input batch 继续视为已消费,但不再展示可重复提交的卡片,转入第 5.2 节 checkpoint 恢复。standalone pending 为 `approved/executing/observed-*`、batch/member/cursor 非 exact、sidecar 为其它状态,或任一 identity 冲突都进入 reconciliation。这三种合法状态都不得把 ready batch 或 waiting standalone pending 视为 stale、supersede 或重新请求原提问 Provider。 -3. session.appliedAnswers 已有一项精确引用 checkpointProviderRequestId、handoff responseFingerprint、decisionCheckpointFingerprint 和原 answer/action identity:该 decision-checkpoint handoff 已被 session CAS 线性化消费。只补 sidecar answered、原 user-input terminal observation、batch member completion 与 cleanup;不得再次调用模型。 +2. **(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 batch `status=ready + nextActionIndex=0 + actions.length=1`,唯一内嵌 member `status=approved` 且 actionIndex=0,user-input sidecar `status=pending`,同 run 的 standalone `game-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。 +3. **(2026-08-13 按 D11 重写)**答案已绑回 delivery:`answersSha256` durable 写入原 delivery **就是本轮的线性化点**(原文此处是 `session.appliedAnswers` 引用 checkpoint handoff)。该绑定由已发布代码保证首次写入或逐字相同则幂等、绑到不同答案则拒绝。只补 sidecar answered、原 user-input terminal observation、batch member completion、session 的派生 `appliedAnswers` 与 cleanup;**不得**再次调用模型,也不得因 session 落后而重新计轮次。 -第 2 项处于 pre-wait 时必须先补齐 waiting/pending/card;修复完成前不接受回答,也不启动 decision-checkpoint。sidecar 已是 answer-prepared 时只恢复 checkpoint,不重新展示原卡。第 3 项在 answer CAS 后按固定顺序终结:先以 appliedAnswers 证明同一回答已线性化,再补 sidecar answered 和原 user-input observation,把原 action/batch 幂等推进 completed,最后清理 pending/batch。最终 checkpoint handoff 与 superseded 链上全部 success handoff 是 CAS 前的恢复锚点;必须保留到新 session primary 已包含逐项重算的 `supersededCheckpointHandoffs` 摘要、完成同步并回读,且 sidecar/observation/原 batch 都 durable 收口,之后才允许清理 handoff。lifecycle 始终保留真实历史终态;历史 handoff 清理后由 session 内摘要长期证明 superseded,不得要求文件仍存在。崩溃留下任一锚点时按相同步骤前滚。 +第 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 状态机才允许以下行为: 1. binding 完整有效且 current session/context 仍逐项相等:接受 response handoff;tool-plan 先 durable 写 v4 batch,再把同一 v3 lifecycle 从 `started` 终结为 `completed`。崩溃留下 `started + 同 binding ready batch` 时,ready batch 已证明 success handoff durable;恢复必须先幂等补 lifecycle `completed`,再执行/恢复 batch,不能误记 interrupted。 -2. binding 完整有效,但同一 project/gdd/session/run/source/profile 下的 session 已沿合法 successor 链因回答、审批 observation 或 steer 前滚:旧物理请求为 `started` 且没有 batch 时,只有当前 handler 已取得并决定丢弃旧 response,或 durable owner/lease/boot 证明旧调用不再存活,才终结为 `interrupted`;旧终态同步回读前不得启动 replacement。若已有同 binding 的 `ready` v4 batch,无论 lifecycle 是 started 还是 completed,都先按第 1 项补成真实 `completed`,再持锁把 batch durable 标为 `superseded`,确认回读后清理。sole user-input 尚未安装 activeQuestion 时,还必须按第 5.2 节验证没有 standalone pending/card/answer-prepared,并在 superseded 回读后删除仅可能存在的 exact pending sidecar、确认 durable absent,再清理 batch;这些步骤未收口前不得启动 replacement。`completed` lifecycle 保持真实历史终态,不能改写;superseded batch 永不执行、永不恢复成 action。 +2. binding 完整有效,但同一 project/gdd/session/run/source/profile 下的 session 已沿合法 successor 链因回答、审批 observation 或 steer 前滚:旧物理请求为 `started` 且没有 batch 时,只有当前 handler 已取得并决定丢弃旧 response,或 durable owner/lease/boot 证明旧调用不再存活,才终结为 `interrupted`;旧终态同步回读前不得启动 replacement。若已有同 binding 的 `ready` v4 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。`completed` lifecycle 保持真实历史终态,不能改写;superseded batch 永不执行、永不恢复成 action。 3. 完成第 2 项后,从新 session 与新 requestContextFingerprint 派生不同的 base providerRequestId,attempt 从 0 开始并自动重新请求。不得沿用旧 base ID,也不得把旧 response 绑定到新 session。 4. 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|interrupted` lifecycle。 -5. 除第 1~2 项明确允许的同 binding `started + ready batch` 崩溃组合,以及上文 activeQuestion/appliedAnswers 已消费证明外,binding 缺失/损坏、lifecycle 与 batch 不一致、同 revision 下 requestContextFingerprint 漂移、session 不是合法 successor,或 batch 已进入执行/等待状态时返回 `PLAN_NEEDS_RECONCILIATION`。此路径不自动删除、不补默认 binding、不重绑、不重试。 +5. 除第 1~2 项明确允许的同 binding `started + ready batch` 崩溃组合,以及上文 delivery 问题落盘/答案绑定的已消费证明外,binding 缺失/损坏、lifecycle 与 batch 不一致、同 revision 下 requestContextFingerprint 漂移、session 不是合法 successor,或 batch 已进入执行/等待状态时返回 `PLAN_NEEDS_RECONCILIATION`。此路径不自动删除、不补默认 binding、不重绑、不重试。 第 2 项的自动前滚必须与 session successor、batch supersede/cleanup 和 replacement request 的 started 写入都在项目锁内按幂等步骤恢复;任一断点重启后只能继续相同步骤。这样合法 steer 能确定性替换旧输出,而身份污染不会被“自动恢复”掩盖。 -`plan-decision-checkpoint` 使用同一 captured-context、v3 lifecycle、base/attempt 和 stale 身份规则,但以 response handoff 取代 v4 batch:`started + 同 binding handoff` 必须先补 completed。current session 精确等于 binding 且 handoff 通过 checkpoint validator 时,从 handoff 解析 checkpoint 并做同一 session CAS;session 已含相同 decisionCheckpointFingerprint 时只补 answered/observation。handoff 已 durable 但 validator 判定 checkpoint 协议无效时,保留该 raw handoff 与 completed lifecycle 作为真实响应历史,按同一 captured session 确定性进入 `repair-{K+1}`,原样复制 superseded 数组;恢复必须重放这一验证与 repair 转换,不得重发被拒绝的原 request,也不得把它的 ID 加入 superseded 数组。无 handoff 的 started 仅按上条 old-attempt-first 规则终结并重试。已验证 handoff durable、current session 已是仍含同 activeQuestion/answer identity 的合法 successor 且 appliedAnswers 未引用该 handoff 时,旧 completed lifecycle/handoff 保持成功历史但禁止应用到 successor;replacement request 使用 `decision-round-{N}-session-{currentSessionRevision}-repair-0`、新 base attempt 0,并按上段写入传递闭包数组。若最终 appliedAnswers 的 checkpointProviderRequestId 等于 replacement,且 `supersededCheckpointHandoffs[].providerRequestId` 逐项覆盖旧 binding 数组,则旧 handoff 已稳定收口为被替换历史,扫描 lifecycle 时不得再次应用、重发或报冲突;历史 handoff 文件已清理也不影响该结论。若 replacement 尚未被消费,则只恢复链上最新且唯一的 request。出现缺口、重复、分叉、binding IDs 与 session 摘要不等,或旧 request 被两个后继声明取代时进入 reconciliation。activeQuestion 或 answer identity 已变化、且没有上述 appliedAnswers 收口证明,同 session/context 出现不同已验证 handoff,或 handoff 与 lifecycle/request context 不一致时只进入 typed conflict/reconciliation。checkpoint 永远不进入 action batch,不可被 generic action recovery 执行。 +**(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,执行下列提交与投影,随后把顶层 run 投影为 `waiting-for-user-input` 同族状态并返回现有 `WaitingForUserInput` outcome;planning pending 的 `kind=gdd-approval` 区分审批等待,不新增第二个 queue outcome 或 run status。GDD create 成功不等于该 action 已 observed;只有 receipt 产生的确定性 terminal observation 才能完成原 action并让精确原 run 继续。 @@ -1146,7 +1149,7 @@ type PlanSubmitGddResult = { ## 13. 审批 pending、提交点与幂等 -> **2026-08-13:本节含依赖 `plan-decision-checkpoint` / `activeQuestion` 的 D10 时代内容,与 golden vector 强耦合,整体待改。处置与理由见第 5.2 节「D11 作废横幅」;凡与第 4.1 / 4.2 / 4.3 / 5.1 节冲突处以那四节为准。** +> **2026-08-13 按 D11 更正。** `gdd-approval` pending 建在 **Supervisor 根 run** 上(`source=project-supervisor-plan`),因为审批卡是给用户看的、而用户只跟 Supervisor 对话;提交 GDD 的策划子 run 在 `plan.submit_gdd` 返回后即终态,不等待审批。receipt、幂等域、`decisionFingerprint` / `receiptFingerprint` 与投影顺序**未变**。 ### 13.1 `gdd-approval` pending @@ -1170,7 +1173,7 @@ M1 新增独立 `.agent/planning/pending.json`,不升级、不迁移、不重 "approvalRequestId": "gdd-approval-" }, "runIdentity": { - "source": "project-supervisor-plan-chat", + "source": "project-supervisor-plan", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", "sessionId": "...", @@ -1254,7 +1257,7 @@ agent.db 必须新增专用 `append_plan_gdd_decision_if_missing`。以下是 he "actionFingerprint": "<64 位小写 hex>", "approvalRequestId": "gdd-approval-", "responseId": "gdd-response-", - "source": "project-supervisor-plan-chat", + "source": "project-supervisor-plan", "runProfile": "standard", "runProfileBindingFingerprint": "<64 位小写 hex>", "sessionId": "...", @@ -1303,7 +1306,7 @@ type PlanGddDecisionResult = { ## 14. 恢复与兼容矩阵 -> **2026-08-13:本节含依赖 `plan-decision-checkpoint` / `activeQuestion` 的 D10 时代内容,与 golden vector 强耦合,整体待改。处置与理由见第 5.2 节「D11 作废横幅」;凡与第 4.1 / 4.2 / 4.3 / 5.1 节冲突处以那四节为准。** +> **2026-08-13 按 D11 更正。** 表中原有 6 行围绕 `activeQuestion` 落盘时序与 `plan-decision-checkpoint` handoff 重放的场景已删除——这两样在 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;只有缺失或落后的投影允许自动修复。 @@ -1326,16 +1329,11 @@ type PlanGddDecisionResult = { | session primary 损坏但 previous 有效 | fail closed;不静默回退较旧 draft | | session 两份有效但分叉 | `PLAN_SESSION_RECOVERY_REQUIRED` | | session 缺失且存在 active run/未提交决定 | recovery required;不重置轮次或 gddId | -| answer-prepared sidecar + 相同 activeQuestion,尚无 checkpoint lifecycle | 必须先验证 v4 `ready/0/唯一 approved member`、standalone pending exact `auto/waiting-for-user-input/observation:null`、sidecar/action/provider identity 全等;通过后才从同一 session/question/answer identity 启动专用 checkpoint request。anchor 缺失/冲突则 reconciliation;CAS 前不增加轮次、不继续普通 tool-plan | -| v4 ready batch + current session 仍等于 binding,activeQuestion 尚未落 | user-input sidecar 只允许 absent 或 exact pending;缺失时确定性创建、存在时复用,再安装 activeQuestion。其它 sidecar 状态/hash/identity reconciliation;不得重新请求 Provider | -| v4 ready batch + activeQuestion 尚未落,current session 是 binding 的合法 successor | 仅在 standalone pending/card/answer-prepared 均不存在且 sidecar absent/exact pending 时,先补 lifecycle completed、把 batch 标为 superseded 并回读,再删除 exact pending sidecar、确认 absent、清理 batch,最后从 successor 新 base 请求;任一断点幂等前滚。其它漂移或 sidecar 状态 reconciliation | -| sole user-input 的 activeQuestion primary 已 durable,standalone pending 尚未落 | 仅接受 v4 `ready/nextActionIndex=0/一项 approved member`、user-input sidecar pending、standalone pending absent;幂等创建 exact `auto + waiting-for-user-input + observation:null` standalone pending 并补 runtime/task/event/card。补齐前不接收回答,不判 stale,不 supersede 或重新请求 Provider;任一状态/identity 冲突则 reconciliation | -| sole user-input 的 activeQuestion + exact standalone waiting pending 已 durable | v4 仍须 `ready/0/一项 approved member`;sidecar pending 时幂等补 runtime/task/event/card 后恢复同一等待,sidecar answer-prepared 时不再展示可提交卡片并转入 checkpoint 恢复。即使 current session 是 source binding 的合法后继,也不判 stale、不 supersede 或重新请求原提问 Provider | -| checkpoint lifecycle started、无 handoff | session/context/answer 未变时,已知 retryable failure 先写 failed,或证明旧 owner/lease/boot 已终止后写 interrupted;回读旧终态后才以同 base attempt N+1 重试。合法 session successor 先 interrupted,再原样复制前驱 superseded 数组,使用 `decision-round-{N}-session-{currentSessionRevision}-repair-0` 与新 base attempt 0;无法证明终止或其它漂移则 recovery/reconciliation | -| checkpoint success handoff 已落、current session 仍精确等于 binding | 幂等补 lifecycle completed,只重放 handoff 做同一 session CAS;不得重新请求 Agent | -| checkpoint raw response handoff 已落、validator 判定协议无效 | 幂等补 lifecycle completed 并保留 raw handoff;重放验证后确定性进入同 session 的 `repair-{K+1}`,继承 superseded 数组且不追加原 request ID;不得重发原 request | -| Provider 成功但 checkpoint raw handoff 被 storage 安全/容量/identity 门拒绝或 durable 提交失败 | 不保存不安全正文、不补 completed、不启动 repair/retry;写安全诊断并进入 `PLAN_NEEDS_RECONCILIATION`,保留 started 历史等待人工处置 | -| checkpoint success handoff 已落、current session 是保留相同 activeQuestion/answer 的合法 successor且未消费 handoff | 保留旧 completed lifecycle/handoff,但禁止应用到 successor;以 current session 的 `repair-0` 新 base attempt 0 重新请求,binding 的 superseded 数组复制旧数组并追加旧 request ID | +| 子 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`;不选择任一版本 | @@ -1499,10 +1497,11 @@ type PlanGddStateViewV1 = { | 'approved' | 'rejected' | 'recovery_required' - roundsUsed: number + clarificationRound: number + repairDepth: number accumulatedAgentMillis: number activeRunId: string | null - activeQuestion: { requestId: string; questionId: string; round: number } | null + awaitingAnswerFor: { delegationId: string; requestId: string; questionId: string; round: number } | null decisionStateCounts: { confirmed: number defaultPending: number @@ -1620,16 +1619,21 @@ Canvas 是条件外部能力,不是 M0-3 四个固定 owner 的通用前置: ## 21. 测试与故障注入矩阵 -> **2026-08-13:本节 `schema` / `session` / `Provider binding` 三行中涉及 `plan decision checkpoint`、`activeQuestion`、`checkpoint handoff`、`superseded` 数组的必测项,随 D10 作废,不再是 M1 的测试义务**(依据见第 5.1、23.1 节)。这些项与 golden vector 强耦合,统一在「schema 与 golden vector 收口」工作包中随第 8.3~8.6、9.1、12、13、14 节一并重写,前置是 M1 的 strict schema 实现(见第 23.6 节)。其余各行不受影响。 +> **2026-08-13 按 D11 收口。** 原横幅标记的 `plan decision checkpoint` / `activeQuestion` / `checkpoint handoff` 相关必测项已随 D10 删除;下方 D11 必测项合并进正表。 -> D11 新增的必测项(本节尚未展开,M1 实现时补齐):委派链上 `clarification_round` 推断与 3 轮上限;Supervisor 转述答案的 `questionsSha256`/`answersSha256` 绑定;plan 根 run 拒绝 steer;策划子 Agent 的 exact allowlist 广告与执行双门;`user.input_request` 在子 Agent 侧被执行层拒绝。 +| 委派链计数 | 沿 `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 + plan decision checkpoint + Provider session binding + hydrate view + 五个 durable/projection strict v1 struct;binding required superseded ID 数组与 session superseded handoff 摘要的 0/1/16/17 边界及逐项映射;unknown field/schema/enum;Agent 注入 Runtime 字段拒绝;ID/time/各类指纹形状;所有长度、数量、bytes 与 128 版本上限 | +| schema | submit input + Provider session binding + hydrate view + 五个 durable/projection strict v1 struct;binding required superseded ID 数组与 session superseded handoff 摘要的 0/1/16/17 边界及逐项映射;unknown field/schema/enum;Agent 注入 Runtime 字段拒绝;ID/time/各类指纹形状;所有长度、数量、bytes 与 128 版本上限 | | fingerprint | 本文 golden;中文/null/空数组/数组顺序;domain separation;任一 durable identity 变化;GDD/decision/receipt/session/pending/comment/request context;裸 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 → activeQuestion 的前置窗口;activeQuestion 前合法 successor 的 supersede/sidecar cleanup/replacement 断点;activeQuestion durable 后才展示;pre-wait 的 v4 ready/0/单 approved member + sidecar pending + standalone absent 与 waiting 的 standalone exact 状态;answer-prepared 必须重验 waiting anchor;其它状态 fail closed;activeQuestion + waiting successor 不判 stale;checkpoint handoff → session CAS 线性化;不同 request 合法复用 answerResponseId;prototypeValidationItems/appliedAnswers 同异 replay;session 已落而 sidecar/observation 落后的恢复 | +| 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/structured injection/实际工具目录与 binding 来自同 captured session;不适用 adapter 字段为 null;Provider 元数据不得注入;任一实际语义字段变化均改变 requestContextFingerprint/base ID;plan base ID/attempt vector;旧 started 必须先 failed/interrupted 再开下一 attempt;普通 tool-plan 的 started/ready/superseded;started + ready 先补 completed;GDD/activeQuestion/appliedAnswers 三类消费证明先于 stale;checkpoint same-session repair 原样继承数组、successor replacement 传递闭包、session 摘要在 handoff 清理后的 H1…Hn 稳定收口与分叉拒绝;checkpoint raw handoff 的超限/敏感键/绝对路径/容量/identity/durable write failure 只写安全诊断并 reconciliation、零自动 repair/retry;损坏 binding 只 reconciliation | | submit | durable request/context/session/batch/action binding;缺失 binding 时丢弃旧响应;sole-action batch;main-loop 专用 waiting 分支;同 submission + 同/异 payload;复用既有 Runtime ID/time;session CAS;决定元数据与 session 真相逐项一致;已有未决版本;并发版本分配;版本到顶;GDD 提交点前后强杀 | | approval | 同 response 同/异决定;不同 response 同版本;不同版本合法复用 responseId;两个窗口并发;旧卡决定新版本;approve/revise/reject comment;approve 后显式 continuation 提交并批准新版本;响应丢失重试 | @@ -1648,7 +1652,7 @@ Canvas 是条件外部能力,不是 M0-3 四个固定 owner 的通用前置: 3. receipt temp 写入前、发布前、线性化点后。 4. receipt 后,index/Markdown 前后;audit 前后;terminal observation 前后;session checkpoint 前后;command response 前。 5. command response 丢失、原 run continuation 前、observation durable 消费后 pending 清理前。 -6. user-input request sidecar 前后、activeQuestion session primary 发布前后、standalone waiting/pending/card 前后,以及 waiting durable 后;覆盖 sidecar absent/exact pending 与全部冲突状态,恢复必须保持原 question/request/action/provider identity。 +6. user-input request sidecar 前后、**delivery 问题落盘前后**、standalone waiting/pending/card 前后,以及 waiting durable 后;覆盖 sidecar absent/exact pending 与全部冲突状态,恢复必须保持原 question/request/action/provider identity。 7. 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 语义而不是假失败。 @@ -1676,7 +1680,7 @@ M0 文档 PR 本身最低验证:Markdown 结构与三张 Mermaid 图可解析 | 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 / decision checkpoint | `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 节) | +| 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_protocol/models.rs:5-21`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop.rs:2547-2626`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_queue.rs:347-408`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/action_projection.rs:3-90` | 参照 user-input special case,在普通 dispatch 前处理 submit,并复用现有 WaitingForUserInput outcome/queue 消费语义 | | JSON sidecar | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/json_sidecar.rs:44-104,122-250` | 现有 writer 可覆盖;不可变文件必须新增 no-replace helper | @@ -1793,7 +1797,9 @@ D9 之后,root 是未变的 Project Supervisor,它在 `standard` 下天然 ### 23.4 M0 完成状态(2026-08-12) -> **2026-08-12 修正:M0 代码部分完成,文档部分待修订。** D6 作废后 `M0A-1` 交付的设计基线失效,须以工作包 `M0A-3` 修订(见第 1.1 节)。`M0A-3` 收口前不得再声称「M0 全部完成」。三个代码工作包的完成状态不受影响,无需回退。 +> **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` 并已推送。 @@ -1844,16 +1850,20 @@ M0 完成不表示任何策划功能已上线。M1 的功能实现仍未开始 | 二 | `WP1` + `WP2`:澄清轮次与返工深度拆分及其回归 | **已完成**(见第 23.5 节) | | 三 | `project-planning` 的 agentCatalog 登记机制定稿 | **已完成**(机制冻结见第 3.1 节;代码落地属 M1) | | 四 | `M0A-3` 批二:拓扑与工具面部分 | **已完成**(2026-08-13),拆解见下 | -| 四之余 | schema 与 golden vector 收口 | **待开工,前置是 M1 的 strict schema 实现** | +| 四之余 | schema 与 golden vector 收口 | **已完成**(2026-08-13),拆解见下 | | 五 | M1 本体:策划闭环功能实现 | 未开始;合入门见下 | 批二在 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 行更正。 -*未完成*——第 5.2 节后半,以及第 8.3~8.6、9.1、12、13、14 节中依赖 `plan-decision-checkpoint` / `activeQuestion` 的内容。这些与 golden vector 强耦合:改 schema 就必须同步重算第 9.1 节的 typed 指纹,而**指纹必须由代码生成、不得手写**,M1 的 strict schema 尚未实现。此时逐段修补只会产生「schema 已改、vector 未换」的中间态,比暂时保留旧文更危险。已在第 5.2 节加作废横幅、并在上述五节各加一条指向它的短横幅,明确这些内容只作为 D10 时代的历史记录、不得作为实现依据,冲突时以第 4.1 / 4.2 / 4.3 / 5.1 节为准。 +*四之余(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 节线性化点不变量改写。 -第四步的范围纪律:批二是把这些小节从 D9/D10 旧拓扑改写到 D11 新拓扑,**不是**实现功能。第 9.1 节的 golden vector 必须由代码重新生成,不得手写。 +**第 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 新拓扑,**不是**实现功能。 第五步开工前必须先处置的事项,按性质分两类: @@ -1877,7 +1887,7 @@ M0 完成不表示任何策划功能已上线。M1 的功能实现仍未开始 ## 24. 最终不变量摘要 - 策划与构建是两个不同 profile/source 的 run,由用户动作连接,不由 Agent 自行升级。 -- 每轮决策解释先冻结在 activeQuestion,用户回答只在 session CAS durable 后计入 roundsUsed;同一回答永远不能产生两条解释。 +- **(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 有界恢复。