diff --git a/docs/README.md b/docs/README.md index e571dde87..a0c0c81df 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,12 @@ ## 当前专题 +### AI 游戏创作与 Agent Runtime + +- [AI 游戏创作智能体 App 实施计划](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md) +- [立项策划 Agent(Fast GDD)技术方案](<./technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md>) +- [AI 游戏创作 Agent Runtime V1.1](<./technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md>) + ### 图片编辑器与 Agent - [图片画布编辑器 MVP 接入方案](./technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md) diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index cd7fcc2f7..0659af6ae 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -1,5 +1,19 @@ # 决策记录 +## 2026-08-10 立项策划 Agent 使用 Fast GDD 版本审批作为完整构建的可选基线 + +- 背景:当前普通完整构建从简短需求直接进入 autonomous manifest,缺少用户在消耗完整构建成本前确认玩法方向、MVP 范围和原型验证项的正式环节;现有 `design-director` 是只读协调任务,`design-foundation` 又会自行补齐玩法定位,用户意图与实现之间没有可版本化、可审批、可恢复的信任根。 +- 决策:新增目标阶段“立项策划”。D1 每项目生成 2~4 条游戏支柱;D2 新项目默认进入立项策划并保留显式“直接开建”;D3 按关联方案冻结 source、composition、工具、schema、审批命令和 UI 名称;D4 M1 使用 `.agent/planning/` sidecar,不改 shared-contracts;D5 game-chat 只在 M3 可选只读消费批准 GDD;D6 使用 Project Supervisor 第三 persona 与 `standard` profile,不新增 agentCatalog 身份;D7 本期不实现知识图谱,只保留 v1 必须为空的预留位;D8 不提供引擎字段,平台事实由 Runtime 注入并强校验。 +- 阶段合同基线:durable source 为 `project-supervisor-plan-chat`,Rust 常量为 `AGENT_RUNTIME_SUPERVISOR_PLAN_CHAT_SOURCE`,Prompt composition/source kind 为 `supervisorPlanChat` / `SupervisorPlanChat`,原生工具为 `plan.submit_gdd`;strict submit/回答解释 checkpoint/provider binding 为 `plan-submit-gdd-input.v1` / `plan-decision-checkpoint.v1` / `plan-provider-session-binding.v1`。exact plan Provider lifecycle/action batch 目标写入 `game-creator-provider-request-lifecycle.v3` / `game-creator-provider-action-batch.v4`;普通 tool-plan 只要有 action,即使 sole action也强制 durable 写 v4 batch,user-input/submit 各有唯一 member,checkpoint/final-reply 无 action 才不建 batch。非 plan 继续写 v2/v3,旧 lifecycle v1/v2 与 batch v1/v2/v3 只按非 plan 语义双读,不能补 binding 恢复成 plan。独立 pending kind/schema 为 `gdd-approval` / `plan-gdd-approval-pending.v1`,审批与 hydrate 命令为 `decide_game_creator_plan_gdd` / `hydrate_game_creator_plan_gdd_state`,hydrate view 为 `plan-gdd-state-view.v1`;阶段名为“立项策划”,persona 名为“立项策划 Agent”,现有 design 组用户名称改为“设计实现组”。checkpoint handoff 的私有 schema/path/slot/ledger 排序不在本次非交付检查点选择实现方案,必须在 M1 对应代码合入前另行冻结。 +- 持久化与信任:`.agent/planning/gdd.v{N}.json` 和 `.agent/planning/approvals/v{N}.json` 使用 no-replace create-only 发布,磁盘 JSON 固定 compact UTF-8 且无结尾换行,分别以 GDD durable create 和 receipt durable create 作为提交点;receipt 是构建信任根,index、session 的已提交摘要、Markdown、pending、审计、event 和 observation 都不能反向覆盖不可变事实。Markdown 有有效 approved 时固定投影最高 approved,否则投影最新严格 submitted GDD 并显示推导状态,不能在 revise/reject 后残留未决文案。planning GDD/回答解释 checkpoint/审批 intent/receipt/session/pending/comment 使用 domain-separated typed serde `sha256-serde-json-v2:`;现役 `actionFingerprint`、`runProfileBindingFingerprint`、answer hash 与 handoff response fingerprint 继续使用裸 64 hex,不做全局迁移,两类值不得互相比较。 +- 对话 checkpoint:plan 专用 `user.input_request` 保持现役 questions-only sole action;Runtime 先创建或复用与 v4 batch identity 全等的 pending sidecar,再把完整问题、hash 与 request/action/provider identity 写入 session.activeQuestion,最后展示决策卡。activeQuestion 尚未落时,session 仍等于 batch binding 才安装同一问题;session 已是合法 successor 且尚无 standalone pending/card/answer 时,旧问题未被消费,必须先补 lifecycle completed、supersede 并回读旧 batch、删除 exact pending sidecar并确认 absent,再清理 batch和从 successor 请求新问题,不能把合法 steer 一律送入 reconciliation。activeQuestion durable 后只允许 v4 batch `ready/nextActionIndex=0/唯一 approved member`,standalone pending 从 absent 直接进入 exact `auto + waiting-for-user-input + observation:null`。若在 runtime/card 发布前崩溃,只能按相同身份补齐或按上述 successor 清理顺序前滚;其它 sidecar/member/cursor/pending 状态和无法证明的 session 漂移失败关闭。用户回答先停在 `answer-prepared`;启动 checkpoint 前还必须重验 exact v4/standalone waiting anchor。随后同一 run 发起只广告 plan 专用 strict `update_agent_plan` 的 `plan-decision-checkpoint` Provider turn;Agent 用 `plan-decision-checkpoint.v1` 形成 answerSummary 和需要时的微型原型项。success handoff durable 后,Runtime 以 session revision/fingerprint CAS 追加 decisionsSummary/prototypeValidationItems/appliedAnswers 并增加 roundsUsed;新 session primary durable 是该轮解释的线性化点,此后才发布原 input observation。handoff 与 current session binding 相等时只重放 handoff;若 current 已是仍保留同题/答案的合法 successor,旧 handoff 不能跨 context 应用,replacement binding 必须以 `supersededCheckpointProviderRequestIds` 传递闭包记录旧 request;最终 appliedAnswers 根据完整 handoff 生成并保存对应 `supersededCheckpointHandoffs` 最小摘要后,旧 handoff 才稳定收口为被替换历史并可清理。长期读取只信 sessionFingerprint 保护的摘要,不要求历史 handoff 文件存在;链缺口、分叉或 identity 不同失败关闭。answerResponseId 只在所属 requestId 域内幂等,不同 request 合法复用同一文本值;下一题/submit 使用新 session 的另一普通 tool-plan 请求。原始回答、选项说明或聊天正文单独都不能猜解释,同 ID 同 checkpoint 只补投影,不同 answer/checkpoint identity 失败关闭。 +- 审批与恢复:Runtime 在 GDD 提交前冻结 `approvalRequestId`,UI 为一次 GDD 审批决定生成并在传输重试中复用 `gdd-response-` responseId;它与现役 user-input answer transport 的同名 responseId 属于不同幂等域,不能跨域比较或恢复。approvalResponseId 只在所属 GDD ref/action 域内幂等,decision audit 使用 `(recordType,gddId,version,responseId)` 复合键,不同版本允许复用同一文本值。审批等待只写 `.agent/planning/pending.json`,不升级或重写 `game-creator-pending-action.v5`;该投影可由唯一待审 GDD 重建,receipt 后 observation 可确定性重建。最新版本已有任一 approve/revise/reject receipt 后才允许下一版本;revise/reject 由原 run 继续,approve 后必须由用户显式开始新 plan continuation,旧批准在新版本 approve 前继续有效。receipt 后固定修复 index/Markdown、`agent-runtime-plan-gdd-decided.v1` 专用幂等 decision audit、原 action terminal observation 和 session;原 run durable 消费 observation 后才清理 planning pending。提交点之后的投影失败仍返回成功 outcome,并以 `recoveryPending=true` 表示派生投影未齐,不回滚、覆盖或重编号不可变事实。 +- 安全不变量:plan run 必须通过 durable top-level `project-supervisor + standard + project-supervisor-plan-chat` exact binding,action tool 仅 `file.read`、`file.list`、`user.input_request`、`plan.submit_gdd`,MCP 为空且 `webSearchEnabled=false`;Prompt/tool-plan/checkpoint/action batch/batch recovery/repair/context/completion 都跳过 Supervisor collaboration 合同。submit 是 sole action,并通过 main-loop 专用审批等待分支;checkpoint 无 action batch。每个 Provider request 的 effective model、api kind、stream、当前 apiKind 实际生效的 official fallback/Anthropic strict/OpenAI Chat token budget field、输出 token、reasoning/verbosity、tool choice、messages、结构化注入、实际工具目录、requestContextFingerprint 与 durable session binding 必须来自同一 captured session;不适用于当前 apiKind 的 adapter 字段固定为 null,除明确排除的 timeout/retry/backoff/log/URL/header/秘密外,任一实际请求语义变化都产生新 context fingerprint 与 base ID。Provider request ID、handoff ID 和 superseded 控制元数据只进 binding/base identity,禁止进入 messages/tools/structured injection;同 session protocol repair 原样继承 superseded 数组。协议无效且已通过 handoff storage 安全/容量门的 Provider 成功响应仍先持久化 raw response handoff 并补 lifecycle completed;恢复只重放验证并进入同一 repair,不重发原 request,而该 raw handoff 未通过 checkpoint validator,不能追加到 superseded 数组。raw handoff 因超限、敏感键、绝对路径、容量、identity 或 durable write 失败而无法安全提交时,不保存正文、不补 completed、不自动 repair/retry,只写安全诊断并进入 reconciliation。同 session/context 的下一 attempt 也必须先把旧 attempt durable 闭合为已知 failure 的 failed,或在 owner/lease/boot 证明物理请求已终止后闭合为 interrupted;合法 successor 的 replacement 同样只能在 handler 已取得并丢弃旧 response,或证明旧调用终止并回读 interrupted 后启动。任何旧终态回读前都不得新建 started。started 后只有在不存在后续业务消费证明时,合法 session successor 才会 interrupt 旧请求、supersede 尚未执行的 ready batch,或为 checkpoint 使用 `decision-round-{N}-session-{revision}-repair-0` 与新 base attempt 0 自动前滚;同 submission GDD、精确 activeQuestion 和精确 appliedAnswers 是优先于 stale 的三类消费证明,`started + ready batch` 先补真实 completed,binding 损坏则只进入 reconciliation。Runtime 在 session revision 1 固定写入不可由 Provider 改写的 `initial-request`,后续决定和最多 3 个原型项逐项匹配 session 真相。plan 无 command、smoke、preview、canvas、任务图、委派或通用文件写能力;retry 只在旧 run 已终态且无 pending 时保留原 plan source/profile,不能降级为 background。`game/fast_gdd.md` 是 Runtime 内部投影,不推进代码 mutation revision。前端只经 hydrate command 从严格 GDD/receipt/session 推导 `not_started|draft|ready_for_approval|revision_requested|approved|rejected`;receipt 隐藏 stale pending,合法投影未齐只返回 `recoveryPending=true`。完整构建仍由用户动作启动,直接开建必须显式声明,不能因 ref 缺失静默降级。 +- 分期边界:本条与关联方案完成 M0A-1 非交付阶段设计检查,不表示 M0A、M1 入口门或 M0 全部完成;`M0A-*` / `M0B-*` 只作为 PR 工作包标签。M1 策划闭环、M2 approvedGddRef 构建绑定和 M3 试玩/game-chat 复用尚未实现。M0/M1 不改变当前 16-task topology 或现役 game-chat 单主结构;M0-3 尚未开始,需新增 dated 决策显式取代 2026-07-26 的旧 smoke 排他表述,再对齐 foundation 写后固定 smoke 与 preview-readiness 最终验收。M1 可以并行做详细设计或技术 spike,但 checkpoint handoff 私有持久化决策未冻结前对应代码不得合入,M0-3 合入前依赖 Fast GDD source/tool policy 的 M1 代码也不得合入主线。M0-4 尚未开始,需封闭动态美术 retry 的 `assets/**` 边界并修复单主前端投影;它不阻塞 M1/M2,只阻塞 M3-4 和“M0 全部完成”。 +- 影响范围:AI 游戏创作客户端、Project Supervisor、Agent Runtime、Prompt Bundle、本地项目 sidecar、项目开发工作台和后续完整构建准入。 +- 验证方式:M0 验证 tracked 技术方案、索引、注册表、golden 指纹、提交/恢复合同和决策记录自包含一致;M1~M3 分别按关联技术方案的阶段门禁执行,不能以文档合入冒充功能完成。 +- 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`。 + ## 2026-08-07 game-chat 使用单主路径按需补齐美术 - 背景:原 game-chat 把 Supervisor 的意图判断之后又硬接为 `design-director / code-director / art-* / code-prototype / preview-*` 固定图。`code-director` 代替程序主 Agent 判断素材缺口,会让“把现有美术资源应用到游戏中”被错误翻译成先生成美术,且美术回执无法天然回到同一个代码 Run 完成接入。 diff --git a/docs/project-memory/shared-memory/document-map.md b/docs/project-memory/shared-memory/document-map.md index cc98c88cb..8f6cb57f5 100644 --- a/docs/project-memory/shared-memory/document-map.md +++ b/docs/project-memory/shared-memory/document-map.md @@ -1,6 +1,6 @@ # 文档地图与阅读索引 -更新时间:`2026-07-16` +更新时间:`2026-08-10` ## 当前文档入口 @@ -14,7 +14,7 @@ | 创作入口、草稿架和玩法链路 | `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md` | | 创作流程统一阶段计划 | `docs/planning/【玩法创作】创作流程统一总计划-2026-05-30.md` | | 宿主壳、移动 App、桌面 App 与 AI H5 沙箱边界 | `docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`、`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md` | -| AI 游戏创作独立 App、Agent Runtime、Runner、浏览器验证与动态隔离子 Agent | `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` | +| AI 游戏创作独立 App、立项策划、Agent Runtime、Runner、浏览器验证与动态隔离子 Agent | `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`、`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` | | 本地启动、验证、部署、埋点和运营查询 | `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md` | | 微信小程序虚拟支付 | `docs/【技术方案】微信虚拟支付接入-2026-05-26.md` | | UI 像素资产与 9-slice 规范 | `UI_CODING_STANDARD.md` | @@ -44,6 +44,13 @@ 3. `docs/【项目基线】当前产品与工程约束-2026-05-15.md` 4. 相关前端组件、service、shared contract 和后端 module +AI 游戏创作独立 App / 立项策划 / Agent Runtime: + +1. `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` +2. 涉及立项策划、Fast GDD、审批或构建基线时,读取 `docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` +3. 涉及 Runtime 工具、持久化、恢复或 Profile 时,读取 `docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` +4. 涉及正式工作台 UI 时,读取 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md` + 生产部署 / 服务器 / Jenkins: 1. `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md` diff --git a/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md new file mode 100644 index 000000000..56e53a0cb --- /dev/null +++ b/docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md @@ -0,0 +1,1456 @@ +# 立项策划 Agent(Fast GDD)技术方案 + +- 日期:2026-08-10 +- 状态:M0A-1 非交付阶段设计检查通过;M0A-2 / M0-3 尚未开始,M1~M3 功能尚未实现 +- 适用范围:AI 游戏创作独立 App、Project Supervisor、Agent Runtime、本地项目策划 sidecar 与后续完整构建准入 +- 当前实现边界:本文件是后续详细设计与实现的仓库内阶段基线;合入本文只代表 M0A-1 设计检查点,不代表立项策划入口、审批 UI、Runtime 持久化或构建绑定已经可用 + +## 1. 背景与目标 + +当前普通完整构建会从简短需求直接进入 `autonomous-game-build`,用户在消耗完整构建成本前没有正式确认玩法方向、MVP 范围和原型验证项的环节。现有完整构建中的 `design-director` 是只读协调任务,`design-foundation` 又会自行补齐玩法定位;用户意图与后续实现之间缺少可版本化、可审批、可恢复的策划基线。 + +本方案新增“立项策划”阶段:用户给出一句需求后,由同一 Project Supervisor 顶层通道切换到独立 persona,在最多 3 轮决策卡内形成 Fast GDD;Runtime 校验并提交不可变版本,用户通过审批卡批准、修改或退回。只有不可变 GDD 与对应 approve receipt 同时有效时,后续完整构建才能取得 `approvedGddRef`。 + +目标: + +1. 一句需求经过不超过 3 轮关键澄清,形成包含一个完整可玩闭环的 Fast GDD。 +2. 策划阶段没有构建、委派、生成、预览或通用文件写能力。 +3. GDD 版本、用户决定和构建引用都有确定提交点、强身份绑定、幂等重放和崩溃恢复合同。 +4. 老项目与“直接开建”保持现行行为;是否策划不成为 game-chat 的强制前置条件。 +5. 后续 M1 无需任何仓库外材料即可实现 source、Prompt、工具、schema、审批与恢复。 + +非目标: + +- M0 不新增 Runtime source、Prompt composition、工具、命令、页面或项目数据,只冻结合同。 +- M0/M1 不修改现行 16 任务 DAG,不新增第 17 个任务。 +- 本期不实现知识图谱;只保留强类型可空槽,v1 必须为空且 UI 不渲染。 +- 不提供引擎选择字段。平台只有自包含 Web 运行事实,由 Runtime 注入并校验。 +- 不修改现役 game-chat 单主 `code-prototype` + 按需美术 child 结构。 +- 不修改 SpacetimeDB、HTTP API、OpenAPI 或 `shared-contracts` 中的正式作品数据合同。 +- M1 不实现 300 秒硬停;以 3 轮硬上限和 240 秒 Agent 活跃时间软提示收束。 + +## 2. 已锁定决定 D1~D8 + +| 编号 | 冻结结论 | +| --- | --- | +| D1 | 每个项目生成 2~4 条游戏支柱;可感知体验、角色成长、探索、构建变体只作为候选,不固定套用。 | +| D2 | 新项目目标态默认进入“立项策划”,同时保留“直接开建”;直接开建等价于当前无 GDD 基线的完整构建路径。 | +| D3 | 阶段、source、composition、工具、schema、审批命令和 UI 名称按第 3 节注册表冻结。 | +| D4 | M1 使用 `.agent/planning/` sidecar,不改 `shared-contracts`;是否把 `approvedGddRef` 提升进 manifest 留给 M2,不能在 M1 临时决定。 | +| D5 | game-chat 只在 M3 可选只读消费有效批准 GDD;没有批准 GDD 时继续走现役快车道。 | +| D6 | “立项策划 Agent”是 Project Supervisor 通道的第三 persona,以持久 source 区分,复用 `standard` profile,不注册新的 agentCatalog 身份。 | +| D7 | 本期不实现知识图谱;`basis`、知识 provider trait、composition 槽位可以预留,但 v1 数据必须为 `null`,空字段不渲染。 | +| D8 | GDD 不含引擎字段;平台事实固定由 Runtime 注入,Agent 不得向用户提问或修改。 | + +## 3. 合同名称注册表 + +| 类别 | 冻结值 | +| --- | --- | +| UI 阶段名 | `立项策划` | +| 英文称呼 | `plan phase` | +| Persona 名 | `立项策划 Agent` | +| durable source | `project-supervisor-plan-chat` | +| Rust source 常量 | `AGENT_RUNTIME_SUPERVISOR_PLAN_CHAT_SOURCE` | +| run profile | `standard` | +| Prompt composition | `supervisorPlanChat` | +| Prompt source kind | `SupervisorPlanChat` | +| 原生 action tool | `plan.submit_gdd` | +| pending kind | `gdd-approval` | +| 审批 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 变体) | +| 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 lifecycle schema | `game-creator-provider-request-lifecycle.v3` | +| plan Provider action batch schema | `game-creator-provider-action-batch.v4` | +| GDD schema | `plan-gdd.v1` | +| index schema | `plan-gdd-index.v1` | +| approval schema | `plan-gdd-approval.v1` | +| session schema | `plan-session.v1` | +| approval pending schema | `plan-gdd-approval-pending.v1` | +| decision audit schema | `agent-runtime-plan-gdd-decided.v1` | +| hydrate Tauri command | `hydrate_game_creator_plan_gdd_state` | +| hydrate read-model schema | `plan-gdd-state-view.v1` | +| GDD 状态 | `draft \| ready_for_approval \| revision_requested \| approved \| rejected \| superseded` | +| 单项决定状态 | `confirmed \| default_pending \| prototype_pending` | +| 回答来源 | `user_option \| user_freeform \| default` | +| 审计 recordType | `agent.runtime.plan.gdd_decided` | +| planning typed 指纹文本 | `sha256-serde-json-v2:<64 位小写十六进制>` | +| 现役 action/profile binding digest | `<64 位小写十六进制>`,无前缀 | +| 构建引用 | `approvedGddRef {gddId, version, fingerprint}` | +| 现有 design 组 UI 名 | `设计实现组`,替代原“策划 Agent”卡片名称 | +| 新组件名 | `GDD 审批卡` | +| 固定入口动作 | `直接开建`、`开始完整制作`、`批准并开建` | + +`approvalRequestId` 与 `responseId` 是两个不同的持久 ID:前者由 Runtime 在 GDD 提交前生成、进入不可变 GDD,一张审批卡终身不变;后者由 UI 在用户执行一次决定时生成,并在传输重试中复用。不得继续用含义不明的单个 `requestId` 同时承担两种职责。 + +本文同时沿用现役 user-input transport 的同名 `responseId`,必须按所属 DTO 区分:`user.input_request` answer/checkpoint 中的 responseId 是“本轮问题回答 ID”,遵守现役 user-input 形状;现役 UI 继续用 `app-user-input-*` generator 产生不超过 160 scalar 的值,后端兼容合同仍是 trim 后 1~160 scalar、无控制字符,不新增会拒绝历史/重试 ID 的前缀门禁。`decide_game_creator_plan_gdd`/approval receipt 中的 responseId 是“GDD 审批决定 ID”,固定为 `gdd-response-`。两者只在各自 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 工具面。 + +## 4. Runtime 拓扑与可信边界 + +```mermaid +flowchart TD + U["用户"] + subgraph SUP["Project Supervisor 顶层通道"] + PLAN["立项策划 Agent
source=project-supervisor-plan-chat
profile=standard
composition=supervisorPlanChat"] + BUILD["完整构建 Supervisor
source=project-supervisor-gui 或 project-supervisor-cli
profile=autonomous-game-build"] + CHAT["game-chat Supervisor
source=project-supervisor-game-chat
profile=autonomous-game-build"] + end + U --> PLAN + PLAN --> GDD["不可变 GDD + approve receipt"] + GDD -->|"用户动作:开始完整制作"| BUILD + U -.->|"直接开建"| BUILD + BUILD --> DAG["现行 16 任务 DAG"] + U --> CHAT + CHAT --> MAIN["code-prototype 单主 + 按需美术 child"] + GDD -.->|"M3 可选只读"| CHAT +``` + +### 4.1 source、profile 与 run 身份 + +策划 run 必须同时满足: + +- `agentId=project-supervisor`; +- 顶层 run,`parentAgentId`、`parentRunId` 和 delegation identity 均为空; +- `source=project-supervisor-plan-chat`; +- `runProfile=standard`; +- source、profile、session、run 与 profile binding fingerprint 已写入现有 durable run configuration,并在恢复、steer、工具执行和审批命令中逐项相等。 + +任一字段缺失、漂移或与当前 active plan run 不同都失败关闭。普通 `standard` Supervisor 不能借 persona 名称进入策划工具面,plan source 也不能切换到 `autonomous-game-build`。每个项目同一时刻最多一个非终态 plan run;首次开始策划时,Runtime 在项目锁内生成唯一 `gddId` 并 create-only 创建 session revision 1。第二窗口并发启动返回 typed `PLAN_ACTIVE_RUN_EXISTS`,不得创建第二条 lineage。 + +M1 不能把新 source 直接塞进一个被 autonomous 语义复用的总 matcher。source predicate 必须拆成三种语义:top-level Supervisor trusted matcher 接受 gui/cli/game-chat/plan;autonomous-build trusted matcher仍只接受现役 gui/cli/game-chat;plan binding matcher只接受 top-level `project-supervisor + standard + project-supervisor-plan-chat`。现有 run configuration、completion gate 和恢复调用点逐个改用正确 predicate,确保 `plan + autonomous-game-build` 永远非法。前端已有 `runProfile + source` 提交链只能作为请求;后端必须重新验证,不能信任页面选择。 + +所有 plan 例外必须共用单一 `is_exact_supervisor_plan_run_at(root, agentId, runId)`:只有 durable run-profile binding 已通过 project/fingerprint 校验,且 `agentId=project-supervisor`、`source=project-supervisor-plan-chat`、`profile=standard`、parent/delegation 均为空、`rootAgentId=agentId`、`rootRunId=runId`,并与 task/runtime 以及存在时的 planning pending、尚存 batch 的 source/profile/binding fingerprint 逐项相等时才返回 true。缺失必需 binding、损坏或漂移不得获得 plan 例外,不能只比较内存中的 `runtime.source`。 + +普通 standard retry 当前会改写为通用 background source。M1 必须在 generic standard fallback 前增加 exact plan root 分支,但不扩大现役 retry 的破坏面:只有 Provider 故障的旧 plan task 已由现有生命周期收束为终态、没有 provider/action/planning pending ledger,且原 task 与已验证 root binding 逐项相等、无 parent/delegation 时,retry 才以同一 gddId/session 创建新 run,继续使用 `project-supervisor-plan-chat + standard`,并在项目锁内写 session revision+1 successor;不得由 retry helper 主动终结 running/waiting run,不得新建 gddId,也不得降级为 `agent-background-task`。任一身份校验失败直接拒绝 retry。已经进入 `gdd-approval` 等待的 run 禁止走 generic retry,只能恢复并续跑 receipt 所绑定的精确原 run。 + +### 4.2 Prompt 分流 + +`supervisorPlanChat` 是独立 composition,不是 autonomous role overlay: + +1. Provider 请求的 system composition 按 durable root source 选择。 +2. tool-plan 请求和 final-reply 请求都必须选择 `supervisorPlanChat`,不能只有首轮生效、收尾又回到通用 `supervisorChat`。 +3. `SupervisorPlanChat` 是 Prompt Bundle 的编译期 source kind;不进入 agentCatalog,也不改变 seed manifest 的 Agent 列表。 +4. 现有 role overlay 仍只服务 autonomous 路径。plan standard run 不伪装成 game-chat overlay,也不注入知识图谱 overlay。 + +### 4.3 四个 action tool 与两项协议控制函数 + +plan source 对 Provider 可见且执行可通过的 action tool 恰好是: + +- `file.read` +- `file.list` +- `user.input_request` +- `plan.submit_gdd` + +`update_agent_plan` 与 `respond_to_user` 是 Runtime 协议控制函数,不计入“四个 action tool”,但仍受现有结构、轮次和终态门禁约束。plan source 的 MCP catalog 必须为空,Provider request 的 `webSearchEnabled` 固定为 false;广告层不得暴露其它 native action、内建搜索或 MCP,也不得只靠 Prompt 劝阻。执行 policy 必须按同一 durable source 再做 exact allowlist,伪造 tool call 一律拒绝。 + +明确禁止:通用写入、patch/delete、command、process、preview、canvas、asset generation、确认型副作用、`agent.delegate`、`agent.route_manifest`、isolated child、任务图调度和所有 MCP 工具。 + +项目级 collaboration policy 对所有 Supervisor 的现有预检不能应用到 exact plan run。六类入口冻结如下,非 plan Supervisor 行为保持不变: + +1. Provider request/context 不读取或渲染 collaboration policy,`collaborationPolicy` 固定为 not-applicable;MCP catalog 在构建请求前即为空,只渲染四 action tool 与两项 control function,不拼接 delegate、MCP、command 或 canvas 示例。 +2. tool-plan 不读取 collaboration policy/state、不做 preflight,`force_supervisor_initial_collaboration=false`;不进入 collaboration liveness/repair,不重新广告全量能力或注入 delegate repair。plan 输出非法协作 action 时由 exact allowlist 直接拒绝。 +3. action batch preparation 的 collaboration policy/state/contract 固定为空,跳过 collaboration/MCP orchestrator 逻辑;plan batch 若持久化了非空 `collaborationContract`,视为身份污染并进入 reconciliation。 +4. provider batch validate/write/recovery 对 exact plan 同样不解析、绑定或从 batch 恢复 collaboration snapshot;只有先验证 exact identity 且 `collaborationContract=null` 才能读取 plan batch。 +5. collaboration completion blocker 在读取项目 policy 前对已验证 exact plan 返回 not-applicable;身份验证错误不能吞掉为例外。plan 改由专用 GDD completion blocker检查提问/审批等待、receipt、observation、session 与 recovery 状态。 +6. prompt context、batch ledger 或 final-reply 任一调用点都不得另写宽松 source 字符串判断;全部调用第 4.1 节统一谓词,防止某一恢复入口重新强制 `agent.delegate`。 + +### 4.4 平台事实 + +Runtime 注入并强校验以下精确结构: + +```json +{ + "runtime": "self-contained-web", + "viewports": ["desktop", "mobile"], + "inputs": ["keyboard", "touch"], + "preview": "local-http" +} +``` + +数组顺序也是合同的一部分。Agent 不得修改、删减或向用户提问。`game/index.html`、远程依赖禁令和完整构建安全门仍由现役构建合同负责,不在 plan Prompt 中复制另一套运行规则。 + +## 5. 对话、决策卡与 Fast GDD + +### 5.1 对话循环 + +- 最多 3 轮主动追问,每轮只问 1 个主要决定。 +- 建议顺序:核心行为与本局目标 → 重玩动力 → 制作边界与 MVP。 +- 满足任一条件即出稿:用户明确说“直接出稿”;已经完成第 3 轮;剩余问题不影响首个可玩闭环;Runtime 注入 240 秒 Agent 活跃时间软提示。 +- `accumulatedAgentMillis` 只累计 Provider 活跃区间,不包含等待用户、等待审批、进程休眠或应用关闭时间。 +- 提问 Provider turn 只产生一条 strict `user.input_request`。Runtime 先把完整问题、请求/action/Provider identity 和问题哈希 checkpoint 到 session.activeQuestion,之后才允许决策卡对用户可见。 +- 用户回答先只进入现役 user-input sidecar 的 `answer-prepared`;Runtime 随后在同一 run 发起专用 `plan-decision-checkpoint` Provider turn,让 Agent 基于原问题和完整回答形成设计解释。checkpoint 成功响应 durable 后,Runtime 才以 session CAS 写入决定、原型验证项和 applied-answer identity。 +- 新 session primary 是“该轮 Agent 已解释用户回答”的线性化点。越过该点后才允许把 sidecar 标成 `answered`、发布原 `user.input_request` terminal observation 并结束该 action;页面重载、Runner 重启或同 run continuation 不能把一条回答计为两轮。 +- 下一题或 `plan.submit_gdd` 必须由新 session primary 上的第二个普通 tool-plan Provider 请求产生。不得让同一个 Provider response 一边绑定旧 session 解释回答,一边在尚未提交的新 session 上执行下一 action。 +- 用户明确输入优先于 Agent 默认;默认建议必须标为 `default_pending`,手感、节奏、镜头、可读性或重玩差异等需要验证的结论标为 `prototype_pending`。 +- session revision 1 由 Runtime 先写入固定 `initial-request` 决定:topic=`初始需求`、state=`confirmed`、answerSource=`user_freeform`、round=0、answerSummary 精确等于规范化后的 1~400 scalar 初始用户需求。Provider 不能改写或省略这条来源记录;超过上限的初始输入先要求用户收束,不能截断。 + +### 5.2 决策卡 + +每次 `user.input_request` 固定只含一题: + +- header:固定为 `第{N}轮·关键决定`,N 为 1~3,始终不超过现有 12 scalar 上限; +- question:以 `当前要决定:{主题}` 开头,再依次说明为什么现在问、推荐方案、好处、代价; +- 选项固定为 `接受推荐`、`暂按推荐`、`需要原型验证`; +- 自由填写由现有 Other 输入槽承载,placeholder 为 `改成:……`。 + +映射: + +| 用户行为 | decision state | answer source | +| --- | --- | --- | +| 接受推荐 | `confirmed` | `user_option` | +| 暂按推荐 | `default_pending` | `default` | +| 需要原型验证 | `prototype_pending` | `user_option` | +| 自由填写 | `confirmed` | `user_freeform` | + +选择“需要原型验证”必须新增一条与该 decision 使用相同 ID 的 30~90 分钟微型原型验证项,包含问题、最小原型、可观察信号和明确通过标准;不允许只写“试玩后再看”。 + +exact plan source 的 `user.input_request` 仍是四个 action tool 之一,不新增第五个 action tool。它继续使用现役 strict input,且 `questions` 必须恰好一题: + +```jsonc +{ + "questions": [ + { + "id": "route_replay", + "header": "第1轮·关键决定", + "question": "当前要决定:路线重玩……", + "options": [ + { "label": "接受推荐", "description": "……" }, + { "label": "暂按推荐", "description": "……" }, + { "label": "需要原型验证", "description": "……" } + ] + } + ] +} +``` + +三个 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 失败。 + +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。 + +回答提交沿用现役 `requestId + responseId + answers` transport。规范化答案精确等于三个固定 label 之一时按上表识别为 option;其它值一律是自由填写,不能由 UI 另传一个未持久化的“答案类型”布尔值。plan 回答额外限制为 1~400 scalar,不得截断。exact plan 先把 sidecar写到可恢复的 `answer-prepared`,不能先发布 terminal observation,也不能直接把用户原文或预设 option description 当作 Agent 解释写入 session。 + +`answer-prepared` 后,Runtime 从同一 current session primary 捕获 activeQuestion、完整答案、`requestId/responseId/answersSha256`、run/source/profile/Goal/steer identity,发起专用 Provider 请求: + +- `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 语义。 + +该请求唯一允许的函数参数是完整 `plan-decision-checkpoint.v1`: + +```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": "三名测试者中至少两名主动改变路线且能说出取舍" + } + } +} +``` + +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自行补写解释。 + +最终有效 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。 + +只有越过该线性化点,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 解释。 + +### 5.3 Fast GDD 固定内容 + +人类可读投影固定包含:决定状态汇总、游戏名称、游戏分类、美术风格、一句话描述、2~4 条游戏支柱、核心循环、目标用户、Runtime 平台事实、3~6 个最小 MVP 系统、独立创作者提示和审批请求。 + +MVP 明确排除多人、商城、服务器、开放世界、赛季、复杂社交、完整剧情和全量内容,除非未来 schema 与产品范围另行升级。GDD 只描述一个首版完整可玩闭环,不调度下游 Agent,也不生成美术或代码。 + +## 6. Prompt 权威稿 + +M1 的 `supervisorPlanChat` 必须以本节为语义基线;允许因 Prompt Bundle 结构拆成多个 section,但不得改变边界、轮次、选项、状态、平台事实或工具面。 + +```text +你是“立项策划 Agent”。用户通常只给一句游戏需求或简短玩法。你的职责是用短而尖锐的游戏设计对话,帮助用户确认最少量、最关键的决定,并提交一份可审批的 MVP Fast GDD。 + +【任务边界】 +- 你只负责玩法澄清、原型验证建议和最小 GDD。 +- 你不得委派或调度其他 Agent,不得写文件、生成图片、执行命令、启动预览或构建。 +- 只定义一个完整可玩闭环;MVP 不含多人、商城、服务器、开放世界、赛季、复杂社交、完整剧情或全量内容。 + +【平台事实:已确认,不可更改,不得向用户提问】 +- Runtime 会注入精确 platformFacts:自包含 Web、desktop/mobile 双视口、keyboard/touch 双输入、本地 HTTP 预览。 +- plan.submit_gdd 的 Provider input 不含 platformFacts;由 Runtime 写入最终 GDD。你不得另加、修改或向用户询问这些字段。 + +【目标】 +1. 用户最多回答 3 个关键问题后,提交一份最小闭环 GDD。 +2. 每轮回答进入同一 run;中断后从 Runtime 提供的最近持久状态继续。 +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。 +- 用户说“直接出稿”、第 3 轮已经完成、剩余问题不影响首个可玩闭环,或 Runtime 提示接近 240 秒 Agent 活跃预算时,立即整理并提交。 + +【低幻觉规则】 +- 用户明确提供或接受的内容标 confirmed;推荐但未确认的内容标 default_pending。 +- 需要靠手感、节奏、镜头、可读性或重玩行为证明的内容标 prototype_pending,并给出 30~90 分钟微型原型、观察信号和通过标准。 +- Runtime 注入的 initial-request 和既有 decisionsSummary 是来源事实;提交 GDD 时必须逐项保留,不能把默认建议改成 confirmed 或伪造回答来源/轮次。 +- 不得编造具体游戏的机制、数值、销量、团队规模、研究来源或用户已经确认的内容。 +- 默认建议只用于缩短对话,不能覆盖用户明确输入。 + +【默认建议】 +- 缺局长偏好时建议 10~20 分钟一局。 +- 缺美术方向时建议风格化、轮廓清楚、资产可复用,原型先控制素材范围。 +- 缺成长时建议 1 条成长线和 2~3 个选择;缺探索时建议 1 条主路线加 1 个有意义的岔路;缺构建时建议高风险输出与稳健防御两种方向。 +- 所有默认建议都标 default_pending。 + +【GDD 固定结构】 +- 决定状态;游戏名称;游戏分类(1 主类型 + 最多 1 融合类型);美术风格(视觉类型、3~5 个关键词、色彩氛围、MVP 美术边界);一句话描述;2~4 条游戏支柱;核心循环;目标用户;Runtime 平台事实;3~6 个最小 MVP 系统;先做/暂缓/验证/扩展条件;审批请求。 +- 游戏支柱候选可以参考可感知体验、角色成长、探索、构建变体,但必须按项目生成,不能固定套用。 +- 字段校验由 Runtime 执行;错误时按工具返回逐项修正,不得绕过。 + +【用户可读性】 +- 面向独立游戏创作者,用“玩家按什么、看见什么、得到什么、下一步做什么”描述。 +- 不写“提升体验”“增强沉浸感”“丰富内容”等不可执行结论。 + +【输出模式】 +- 澄清模式:只提交当前决策卡,不输出完整 GDD。 +- 成稿模式:调用 plan.submit_gdd,等待审批;不展示内部思考过程。 +- 收到 revise/reject observation 后,在同一 gddId 下修订并提交下一版本;收到 approve 后只做一句收尾确认。 +``` + +Runtime 还必须提供三类结构化注入,而不是让 Prompt 猜测:当前轮次/活跃毫秒提示、恢复摘要、审批 observation。注入只含项目相对引用和有界摘要,不含绝对路径、Provider 元数据或内部密钥。 + +## 7. 数据权威、路径与写者 + +| 路径或记录 | 语义 | 写者 | 原地覆盖 | +| --- | --- | --- | --- | +| `.agent/planning/gdd.v{N}.json` | 已提交 GDD 的不可变事实 | `plan.submit_gdd` Runtime handler | 禁止;create-only | +| `.agent/planning/approvals/v{N}.json` | 该版本唯一用户决定;approve 是构建信任根 | 审批 command | 禁止;create-only | +| `.agent/planning/index.json` | 不可变事实的连续索引与状态缓存 | Runtime projection/recovery | 允许原子重建 | +| `.agent/planning/session.json` | 待答问题、未提交已解释决定与恢复 checkpoint | plan Runtime | 允许带 revision/hash 链的原子 CAS | +| `game/fast_gdd.md` | 当前有效批准 GDD 或最新候选的人类可读投影 | Runtime renderer | 允许原子重渲染 | +| `.agent/planning/pending.json` | `gdd-approval` 交互与原 run observation 锚点;可重建投影 | plan Runtime pending ledger | 允许原子重建,消费后清理 | +| `agent.db` / runtime event / task state | 审计与 UI/Runner 投影 | Runtime | 按稳定身份幂等修复 | + +权威顺序固定为:不可变 GDD + 不可变 approval receipt 高于 session 中的已提交摘要,高于 index、Markdown、pending、审计、event 与 observation。session 是尚未提交的模型解释决定的唯一权威 checkpoint;现有 user-input response sidecar只证明原始回答已收到,不能自行重建 Agent 对回答的设计解释。 + +每个 schema 都绑定 `projectId` 与创建它的 durable plan identity。只允许安全枚举名称精确匹配 `gdd.v([1-9][0-9]{0,2}).json` 与 `approvals/v([1-9][0-9]{0,2}).json` 的普通文件;严格解析、项目身份和指纹全部通过后,它们本身就是事实。禁止从临时文件、Markdown、任意孤儿文件名、审计摘要或 Agent 自述推测事实。 + +`game/fast_gdd.md` 是 planning 内部投影,即使位于 `game/**` 也不推进 project mutation revision、不产生专业 Agent mutation ownership、不触发 verification gate。它必须走专用 renderer 和项目锁,不能伪装成通用 `file.write`。因此 plan source 不需要也不得获得 `game.static_smoke`。 + +### 7.1 Markdown 选版 + +1. 存在有效 approved 版本时,Markdown 始终投影当前最高有效 approved 版本。 +2. 尚无 approved 版本时,Markdown 投影最新一份严格有效的 submitted GDD;`ready_for_approval`、`revision_requested`、`rejected` 都保留该版本供用户追溯,并在头部逐字写明由 GDD/receipt 推导出的当前状态,不能因 receipt 已落而留下未决状态文案。 +3. 新候选的审批卡直接读取对应不可变 JSON,不要求覆盖仍有效的 approved Markdown。 +4. Markdown 缺失或头部 `gddId/version/fingerprint` 不匹配时只从权威 JSON 重渲染,绝不反向解析 Markdown 修复 JSON。 + +## 8. 严格 schema 与规范化 + +### 8.1 共同规则 + +- Rust 持久结构和 Tauri command input 都使用 `#[serde(rename_all = "camelCase", deny_unknown_fields)]`;unknown schema、unknown enum 和 unknown field 失败关闭。 +- 所有字段都显式序列化;`null` 和空数组不能通过 `skip_serializing_if` 消失。 +- 用户文本先把 CRLF/CR 变为 LF,再用 Rust `str::trim` 去掉首尾 Unicode whitespace;不做 Unicode NFC/NFKC 折叠,不改变内部空白。 +- 拒绝 NUL、DEL,以及除 LF/TAB 外的 C0 控制字符。长度按 Unicode scalar count;另执行 serialized UTF-8 byte 上限。不得静默截断。 +- 一般 opaque Runtime ID 满足 `[A-Za-z0-9][A-Za-z0-9._:-]{0,127}`;现役 actionId 另固定为 `action-[0-9a-f]{24}`。`gddId` 为 `gdd-` 加小写 RFC 4122 UUID;`approvalRequestId` 为 `gdd-approval-` 加小写 UUID;approval command/receipt 的 responseId 为 `gdd-response-` 加小写 UUID。user-input answerResponseId 是明确例外:继续按现役 trim 后 1~160 scalar、无控制字符合同读取,UI 继续生成 `app-user-input-*`,不套用 GDD 审批前缀或一般 opaque ID 的 128 字符上限。 +- 时间统一为 UTC、固定毫秒精度 `YYYY-MM-DDTHH:mm:ss.SSSZ`,由 Runtime 生成;输入时间不接受时区偏移或更高精度。 +- planning typed fingerprint(GDD、decision、receipt、session、pending、comment)完整匹配 `sha256-serde-json-v2:[0-9a-f]{64}`。现役 `actionFingerprint` 与 `runProfileBindingFingerprint` 保持已有 `[0-9a-f]{64}` 裸 digest,M1 不做全局格式迁移;两类字段不得互相比较。 +- submit input 与 GDD 最大 64 KiB,单个 plan decision checkpoint 4 KiB,Provider session binding 8 KiB,session 64 KiB,approval/pending 各 16 KiB,index 256 KiB,Markdown 128 KiB,hydrate view 512 KiB;限制按最终 UTF-8 bytes 计算。 +- 一个 lineage 最多 128 个版本;版本是 `u32` 且只能为 `1..=128`。到达上限返回 `PLAN_VERSION_LIMIT_REACHED`,不能绕回、删除或另建 lineage。 +- v1 所有 `basis` 必须为 `null`;非空值返回 `PLAN_UNSUPPORTED_KNOWLEDGE_BASIS`。 + +### 8.2 `plan-submit-gdd-input.v1` + +Provider 只能提交设计内容,不能提交或覆盖任何 Runtime 身份、版本、时间、平台事实、知识依据或指纹。`plan.submit_gdd` 的完整 strict input 固定为: + +```jsonc +{ + "schemaVersion": "plan-submit-gdd-input.v1", + "game": { + "title": "...", + "genre": { "primary": "...", "fusion": null }, + "artStyle": { + "visualType": "...", + "keywords": ["...", "...", "..."], + "moodAndColor": "...", + "mvpArtBoundary": "..." + }, + "oneLiner": "...", + "pillars": [ + { + "name": "...", + "playerFeel": "...", + "mechanism": "...", + "decisionState": "confirmed" + } + ], + "coreLoop": ["..."], + "targetUsers": { + "coreUsers": "...", + "preferences": "...", + "sessionLength": "...", + "referenceGames": [] + }, + "mvpSystems": [ + { + "system": "...", + "minimalFunction": "...", + "whyRequired": "...", + "verifyMethod": "...", + "decisionState": "confirmed" + } + ], + "outOfScope": ["..."], + "creatorTips": { + "doFirst": "...", + "deferForNow": "...", + "howToVerify": "...", + "expandWhen": "..." + } + }, + "decisions": [ + { + "id": "initial-request", + "topic": "初始需求", + "state": "confirmed", + "answerSource": "user_freeform", + "round": 0, + "answerSummary": "用户的规范化初始需求" + }, + { + "id": "d1", + "topic": "...", + "state": "prototype_pending", + "answerSource": "user_option", + "round": 1, + "answerSummary": "..." + } + ], + "prototypeValidationItems": [ + { + "id": "d1", + "question": "...", + "microPrototype": "...", + "observation": "...", + "passCriterion": "..." + } + ] +} +``` + +该 input 及所有嵌套类型都使用 `deny_unknown_fields`;`game.platformFacts`、任意 `basis`、`projectId/gddId/version/submissionId/approvalRequestId`、action/run/session identity、时间与任何 fingerprint 一旦出现在 Provider input 中即返回 `PLAN_INVALID_REQUEST`。Runtime 在发出本轮 Provider request 前把当前 `sessionRevision/sessionFingerprint` 绑定进内部执行上下文,在项目锁内验证该 CAS 后,才把 project、GDD、版本、durable action、source/profile、session/run、时间、固定 `platformFacts`、全部 `basis:null` 与 fingerprint 注入 `plan-gdd.v1`。字段数量和文本限制按第 8.3 节对应 durable 字段执行。 + +input 中必须逐项包含并精确等于 source session 的全部 `decisionsSummary` 和 `prototypeValidationItems`,不得改变决定的 id/topic/state/answerSource/round/answerSummary,也不得改变原型项正文或顺序;每个已提问决定因此具有可验证的 1~3 轮来源。额外 decision 只允许是未提问默认:`default_pending + default + round=0`,且不能为它伪造 prototype item。唯一允许的 `confirmed + user_freeform + round=0` 是 Runtime 创建的固定 `initial-request`,其 answerSummary 精确等于初始用户需求。直接出稿因此可以合法使用 `roundsUsed=0`,但仍至少提交该初始 decision。 + +### 8.3 `plan-gdd.v1` + +顶层字段顺序和覆盖范围固定为: + +```jsonc +{ + "schemaVersion": "plan-gdd.v1", + "projectId": "", + "gddId": "gdd-", + "version": 1, + "submissionId": "<原 plan.submit_gdd durable actionId>", + "approvalRequestId": "gdd-approval-", + "actionFingerprint": "<64 位小写 hex>", + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionId": "", + "sourceSessionRevision": 3, + "sourceSessionFingerprint": "sha256-serde-json-v2:", + "createdByRunId": "", + "createdAtUtc": "2026-08-10T00:00:00.000Z", + "game": { + "title": "...", + "genre": { "primary": "...", "fusion": null }, + "artStyle": { + "visualType": "...", + "keywords": ["...", "...", "..."], + "moodAndColor": "...", + "mvpArtBoundary": "..." + }, + "oneLiner": "...", + "pillars": [ + { + "name": "...", + "playerFeel": "...", + "mechanism": "...", + "decisionState": "confirmed", + "basis": null + } + ], + "coreLoop": ["..."], + "targetUsers": { + "coreUsers": "...", + "preferences": "...", + "sessionLength": "...", + "referenceGames": [] + }, + "platformFacts": { + "runtime": "self-contained-web", + "viewports": ["desktop", "mobile"], + "inputs": ["keyboard", "touch"], + "preview": "local-http" + }, + "mvpSystems": [ + { + "system": "...", + "minimalFunction": "...", + "whyRequired": "...", + "verifyMethod": "...", + "decisionState": "confirmed", + "basis": null + } + ], + "outOfScope": ["..."], + "creatorTips": { + "doFirst": "...", + "deferForNow": "...", + "howToVerify": "...", + "expandWhen": "..." + } + }, + "decisions": [ + { + "id": "initial-request", + "topic": "初始需求", + "state": "confirmed", + "answerSource": "user_freeform", + "round": 0, + "answerSummary": "用户的规范化初始需求", + "basis": null + }, + { + "id": "d1", + "topic": "...", + "state": "prototype_pending", + "answerSource": "user_option", + "round": 1, + "answerSummary": "...", + "basis": null + } + ], + "prototypeValidationItems": [ + { + "id": "d1", + "question": "...", + "microPrototype": "...", + "observation": "...", + "passCriterion": "..." + } + ], + "fingerprint": "sha256-serde-json-v2:" +} +``` + +字段限制: + +| 字段 | 限制 | +| --- | --- | +| title | 1~80 scalar | +| genre.primary / fusion | primary 1~40;fusion 为 `null` 或 1~40 | +| artStyle | visualType 1~80;keywords 3~5 个且去重,每项 1~32;其余各 1~400 | +| oneLiner | 45~90 scalar | +| pillars | 2~4 条;name 1~40,其余文本各 1~240;name 唯一 | +| coreLoop | 4~8 步,每步 1~120 | +| targetUsers | 三个主文本各 1~240;referenceGames 0~5 项,每项 1~80 | +| mvpSystems | 3~6 项;system 1~40 且唯一,其余文本各 1~240 | +| outOfScope | 1~12 项,每项 1~80,去重 | +| creatorTips | 四个字段各 1~400 | +| decisions | 1~32 条;id 匹配 `[a-z][a-z0-9-]{0,31}` 且唯一;topic 1~80;answerSummary 1~400;round 0~3。round 0 只允许初始用户需求或未提问默认决定 | +| prototypeValidationItems | 0~3 条;id 必须精确引用 decisions 中同 ID 的 `prototype_pending` 项且一一对应;question、microPrototype、observation、passCriterion 各 1~400 | + +`sourceSessionRevision` 必须大于 0,`sourceSessionFingerprint` 必须是 planning typed fingerprint;两者精确等于产生本次 Provider request 时绑定的 session primary。首次 create 时项目锁内要求它仍是 current primary 并做 CAS;同 submission replay 时先复用既有 GDD 的历史 source binding,不再要求后来已推进的 current session 与旧 revision 相等,只要求现有 session/receipt 未与同一 gddId/sessionId lineage 冲突,且绝不能拿新 primary 覆盖历史 binding。`fingerprint` 不参与自身计算;其余字段全部参与,包括时间、身份、`null`、空数组和数组顺序。GDD 不持久化可推导 status。 + +### 8.4 `plan-gdd-index.v1` + +```jsonc +{ + "schemaVersion": "plan-gdd-index.v1", + "projectId": "...", + "gddId": "gdd-...", + "entries": [ + { + "version": 1, + "submissionId": "...", + "approvalRequestId": "gdd-approval-...", + "actionFingerprint": "<64 位小写 hex>", + "fingerprint": "...", + "file": "gdd.v1.json", + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionId": "...", + "sourceSessionRevision": 3, + "sourceSessionFingerprint": "sha256-serde-json-v2:", + "createdByRunId": "...", + "createdAtUtc": "...", + "submittedAtUtc": "..." + } + ], + "statusCache": { + "latestVersion": 1, + "pendingVersion": 1, + "approvedVersion": null, + "versions": [{ "version": 1, "status": "ready_for_approval" }] + }, + "rebuiltAtUtc": "..." +} +``` + +entries 必须从 1 连续递增,文件名与 version 精确一致,且逐项等于对应 GDD 权威字段。v1 的 `submittedAtUtc` 固定等于 GDD `createdAtUtc`,因此可以确定性重建。`pendingVersion` 为 `null` 或唯一未决定版本;`approvedVersion` 为 `null` 或当前最高有效 approve 版本;`versions` 与 entries 等长、同序。`statusCache` 和 `rebuiltAtUtc` 全部可重建,不参与信任判断。index 损坏或缺失时直接从严格验证后的 GDD/receipt 重建,不以 `.previous` 为真相源。 + +### 8.5 `plan-gdd-approval.v1` + +```jsonc +{ + "schemaVersion": "plan-gdd-approval.v1", + "projectId": "...", + "gddId": "gdd-...", + "version": 1, + "fingerprint": "sha256-serde-json-v2:", + "pendingActionId": "<原 plan.submit_gdd actionId>", + "actionFingerprint": "<原 64 位小写 hex action fingerprint>", + "approvalRequestId": "gdd-approval-", + "responseId": "gdd-response-", + "decisionFingerprint": "sha256-serde-json-v2:", + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionId": "...", + "runId": "...", + "action": "approve", + "comment": null, + "decidedAtUtc": "2026-08-10T00:00:00.000Z", + "receiptFingerprint": "sha256-serde-json-v2:" +} +``` + +每个版本最多一个 receipt。approve 的 comment 可以为 `null` 或 1~1000 scalar;空白规范化后写为 `null`。revise/reject 的 comment 必须为 1~1000 scalar。`decisionFingerprint` 只用于同一用户意图的幂等判断;`receiptFingerprint` 覆盖 receipt 除自身外的全部字段,是读取信任根时的完整性校验。两者不能互相替代。 + +### 8.6 `plan-session.v1` + +```jsonc +{ + "schemaVersion": "plan-session.v1", + "projectId": "...", + "gddId": "gdd-...", + "sessionRevision": 1, + "previousFingerprint": null, + "sessionFingerprint": "sha256-serde-json-v2:", + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionId": "...", + "activeRunId": "...", + "lastRunId": "...", + "phase": "collecting", + "roundsUsed": 0, + "activeQuestion": null, + "accumulatedAgentMillis": 0, + "appliedSteerCursor": 0, + "decisionsSummary": [ + { + "id": "initial-request", + "topic": "初始需求", + "state": "confirmed", + "answerSource": "user_freeform", + "round": 0, + "answerSummary": "用户的规范化初始需求" + } + ], + "prototypeValidationItems": [], + "appliedAnswers": [], + "latestSubmittedRef": null, + "lastDecisionRef": null, + "updatedAtUtc": "..." +} +``` + +`phase` 取 `collecting | awaiting_user_input | awaiting_gdd_approval | revision_requested | approved | rejected | recovery_required`。`activeRunId` 为 opaque ID 或 `null`,只有非终态 plan run 时可非空;`lastRunId` 始终记录最近一次 plan run。`roundsUsed` 为 `0..=3`;`accumulatedAgentMillis` 为 `u64`,只单调增加。`appliedSteerCursor` 为现役 durable steer cursor,初始 0、只单调增加;每次接受会改变后续 Provider context 的 plan steer 时,必须先写 session successor,不能只改聊天消息。 + +`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` 时允许非空。 + +`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 重算。 + +`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。 + +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`。 + +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。 + +## 9. typed serde 指纹合同 + +M1 必须新增单一共享 helper;仓库当前没有可以直接满足本合同的通用实现,不能把现有裸 SHA-256 hex 或 map/string 拼接 helper 冒充本合同。 + +逻辑签名固定为: + +```rust +typed_serde_fingerprint(domain: &'static str, value: &T) + -> Result +``` + +算法: + +1. 先按第 8.1 节完成文本规范化和 strict struct 反序列化;禁止对原始 JSON 文本直接哈希。 +2. 各 canonical payload 必须是字段声明顺序冻结的 Rust struct。payload 内禁止 `HashMap`、`BTreeMap`、flatten 和自定义跳字段;数组使用 `Vec`,保持业务顺序,不排序。 +3. 构造字段顺序固定为 `domain`、`value` 的 `FingerprintEnvelope { domain, value }`。 +4. 使用 `serde_json::to_vec(&envelope)`;不 pretty print、不追加换行、不转义非 ASCII Unicode,不省略 `null` 或空数组。 +5. 对所得 UTF-8 bytes 计算 SHA-256,输出 `sha256-serde-json-v2:` 加 64 位小写 hex。 + +domain 固定为: + +| 对象 | domain | 排除字段 | +| --- | --- | --- | +| GDD | `genarrative.plan.gdd.v1` | 外层 `fingerprint` | +| 回答解释 checkpoint | `genarrative.plan.decision-checkpoint.v1` | 无;覆盖完整 `plan-decision-checkpoint.v1` | +| 用户决定 intent | `genarrative.plan.gdd-decision.v1` | 无;本身不含服务器时间或 receipt 字段 | +| approval receipt | `genarrative.plan.gdd-approval-receipt.v1` | `receiptFingerprint` | +| session | `genarrative.plan.session.v1` | `sessionFingerprint` | +| approval pending | `genarrative.plan.gdd-approval-pending.v1` | `pendingFingerprint` | +| decision comment | `genarrative.plan.gdd-comment.v1` | 无 | +| Provider 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。 + +用户决定 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。 + +### 9.1 GDD golden vector + +以下一行是完整 `FingerprintEnvelope` canonical value;外层存储字段 `fingerprint` 不在 value 中。数组顺序、中文、`null`、空数组、Runtime 注入的 session binding、initial request 和 prototype pass criterion 均为受保护字节。精确 UTF-8 bytes 无 BOM、无结尾换行,共 3707 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":"三名测试者中至少两名主动改变路线且能说出取舍"}]}} +``` + +预期输出: + +```text +sha256-serde-json-v2:d85c85dae3500ef90e5bc268e00dc4ef17fa039798837dd283310aa31f2c644d +``` + +M1 测试必须证明:改动任一中文字符、把 `fusion:null` 省略、交换关键词/平台数组顺序或改变任一 durable identity 都会得到不同 typed fingerprint。给原始 JSON 追加换行后 strict parse + canonical reserialize 的 typed fingerprint本身不变,但该文件不满足第 10.1 节 compact/no-newline storage bytes,必须以 non-canonical authority 拒绝,不能混淆两个门禁。 + +## 10. 文件发布与崩溃一致性 + +### 10.1 不可变 JSON 的 create-only 发布 + +现有可覆盖 JSON sidecar writer 不适用于 GDD 和 approval receipt。两个不可变文件的磁盘 bytes 固定为对完整 strict persisted struct 调用 `serde_json::to_vec` 的结果:字段使用 Rust 声明顺序、compact JSON、UTF-8、无 BOM、无结尾换行;typed fingerprint envelope 只用于计算指纹,不包在磁盘对象外。replay 比较的是 strict struct、重算 fingerprint 与这组 canonical storage bytes,pretty JSON 或语义等价但字节不同的文件不能被 writer 当作自己已提交的 canonical 文件。 + +M1 必须新增跨平台 `durable_create_json_no_replace`,固定流程如下: + +1. 持有项目级 write lock,重新验证 canonical target、父目录、普通文件边界、project identity 和当前事实集合。 +2. 在 target 同目录以随机且有界名称 `create_new` 创建私有 temp;拒绝 symlink/reparse point、目录、硬链接异常和路径逃逸。 +3. 写入完整 bytes,`sync_all`,通过仍持有的句柄回读并做 strict parse、typed fingerprint 与文件身份复核。 +4. 使用 OS 原生 no-replace 原子发布:Windows `MoveFileExW` 不带 replace 且带 write-through;Linux `renameat2(RENAME_NOREPLACE)`;macOS `renameatx_np(RENAME_EXCL)`。平台不支持时只允许同文件系统 hard-link no-replace fallback,随后验证 temp/target file identity;无法证明时失败关闭。 +5. 发布成功后重新打开 target,验证普通文件、字节、typed identity 与 fingerprint;删除 temp,并同步父目录。 +6. target 已存在时不得覆盖:先读取既有 strict struct。GDD replay 复用既有文件中的 `version/approvalRequestId/createdAtUtc` 与 Runtime 注入 identity,receipt replay 复用既有 `decidedAtUtc`,再从本次规范化业务 input 重建 canonical candidate;相同 logical ID 且完整 strict value、storage bytes 与 fingerprint 相同才视为 replay,否则返回 identity conflict。临时文件在完成比较后清理并再次同步父目录。 + +成功返回的耐久语义是:在本地文件系统正确实现 `sync_all`、no-replace publish 与父目录同步的前提下,承诺进程崩溃、操作系统崩溃和断电后 target 要么不存在、要么是完整有效文件,不出现半个 authoritative primary。父目录无法同步时不能返回 committed;只允许留下可清理 temp,不得把未确认 target 当成提交。 + +不可变 GDD/receipt 不创建 `.previous`。tmp 永远不是事实,恢复只能在持锁并确认没有活跃 writer 后清理。 + +### 10.2 index 与 session + +- index 是纯投影;使用原子 replace,但损坏、丢失或存在历史 backup 时一律从权威 GDD/receipt 重建,不依据 backup 猜新旧。 +- session 使用原子 replace + 单份 `.previous`,但读取必须验证 revision/hash 链。primary 与 previous 相同,或 primary 是 previous 的精确 `revision+1` successor 时 primary 胜出并清理 previous;primary 缺失且 previous 唯一有效时可在项目锁内提升并同步父目录;两者有效但不是同值/合法 successor 时 reconciliation;primary 存在但损坏时即使 previous 有效也失败关闭。 +- 这套规则只识别合法 successor,不会把一次正常的 Windows 安装中断留下的“新 primary + 旧 previous”误判为冲突。 + +## 11. 版本与状态推导 + +```mermaid +flowchart LR + S["plan-session.v1 draft"] -->|"plan.submit_gdd"| G["gdd.vN.json
create-only 提交点"] + G --> R["ready_for_approval"] + R -->|"receipt.action=revise"| RV["revision_requested"] + R -->|"receipt.action=reject"| RJ["rejected"] + R -->|"receipt.action=approve"| AP["approved"] + RV -->|"同一 gddId 提交 vN+1"| G2["gdd.vN+1.json"] + RJ -->|"同一 gddId 重新提交 vN+1"| G2 + AP -->|"后续 vN+1 被批准"| SS["superseded"] + G2 --> R2["ready_for_approval"] + R2 -->|"approve receipt"| AP2["新 approved"] +``` + +- `draft` 只存在于 session,不创建 GDD 文件。 +- GDD create-only 成功且尚无 receipt 时为 `ready_for_approval`。 +- receipt.action=`revise` 为 `revision_requested`;`reject` 为 `rejected`。 +- 有 approve receipt 的最高版本为 `approved`,更低的 approve 版本为 `superseded`。 +- revise/reject 新候选不撤销此前有效 approved;只有新版本 approve 才 supersede 旧批准。 +- status 只从 GDD/receipt 推导,index.statusCache、session、Markdown 和 UI 徽章不得反向决定状态。 + +## 12. GDD 提交合同 + +`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 中间。 + +产生 submit 的 Provider request 必须先冻结 durable source-session binding,不能只放在内存: + +```jsonc +{ + "schemaVersion": "plan-provider-session-binding.v1", + "projectId": "...", + "gddId": "gdd-...", + "agentId": "project-supervisor", + "taskId": "...", + "providerRequestId": "provider-request-<64 位小写 hex>", + "sessionId": "...", + "runId": "...", + "goalId": "...", + "goalRevision": 1, + "goalSnapshotFingerprint": "<现役 goal snapshot fingerprint>", + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionRevision": 3, + "sessionFingerprint": "sha256-serde-json-v2:", + "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 与恢复校验。 + +为遵守第 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。 + +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 补写旧请求。 + +同快照构建算法固定如下: + +1. Runtime 在项目锁内读取并严格验证 current session primary、run/profile binding、Goal/steer 与构造请求所需的全部 durable 输入,形成不可变 `CapturedPlanProviderContext`;不得先从旧 conversation/messages 构建 request,再用较新的 session revision 给它盖章。 +2. 只从该 captured context 构造 composition、messages、结构化注入与工具目录;构建后的 provider request object 不再可变,并据其实际字段计算 requestContextFingerprint。 +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。 + +外层 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,不能用默认值补齐。 + +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 项处于 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,不得要求文件仍存在。崩溃留下任一锚点时按相同步骤前滚。 + +只有不存在上述业务消费证明、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。 +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、不重绑、不重试。 + +第 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 执行。 + +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 继续。 + +1. 解析第 8.2 节 strict input;在项目锁内重读 project identity、active plan run、Provider request 所绑定的 session CAS、canonical GDD/receipt 与独立 planning pending。不信任 Provider payload 中不存在也不允许出现的版本、时间、平台事实或身份。 +2. 验证文本上限、轮次、决定状态和 prototype item 一一对应;Runtime 注入固定 platformFacts 和所有 `basis:null`,以当前 durable actionId/裸 action fingerprint 作为 submission identity。 +3. 若已有无 receipt 的 GDD,新的不同 submissionId 返回 `PLAN_PENDING_GDD_EXISTS`。最新 GDD 已有任一有效 approve/revise/reject receipt 后才允许分配下一版本:revise/reject 由精确原 run 的 observation continuation 继续;approve 后只允许用户显式发起第 14 节恢复矩阵所述新 plan continuation。旧 approved 在新版本提交和 revise/reject 期间仍有效,只有新版本 approve 才被 supersede。 +4. 版本取最后一个连续有效版本加一;版本 1~128,不允许缺号或扫描任意文件补号。 +5. 新提交由 Runtime 生成并冻结 `approvalRequestId/createdAtUtc`,填充全部 durable identity、source session binding 和时间,计算 GDD fingerprint,以第 10.1 节算法 create-only 发布 `gdd.v{N}.json`。同 submissionId replay 必须先找到并严格读取既有 GDD,复用其中 Runtime 生成的版本、request/time 与 identity 后再比较,不能用新时间制造假冲突。 +6. GDD target 完整发布并完成父目录同步是唯一提交点。提交点前失败不产生版本;提交点后任何投影失败都仍是已提交。 +7. 提交点后按固定顺序修复:重建 index/status → 按第 7.1 节渲染 Markdown → 创建/恢复独立 planning pending 与稳定 `approvalRequestId` → checkpoint session → task/state/event 与 waiting 投影。 +8. 同 submissionId + 同 canonical payload 返回同一 ref 并补投影;同 ID + 不同 payload 返回 `PLAN_SUBMISSION_IDENTITY_CONFLICT`,不得生成 vN+1。 + +等待审批由 planning pending 专用恢复路径负责,不复用通用确认 pending 的 action re-execution。main loop 恢复时若 GDD 已提交而 batch sidecar 缺失,仍从不可变 GDD和独立 pending 恢复同一 waiting action;若遗留 batch 存在,只能核对其 actionId/fingerprint/source/profile 与 GDD 相等后作为普通投影清理,不能让 submit 再次落入 generic dispatch。receipt 后,专用 continuation 把第 13.1 节 observation 幂等写回原 run/action:approve 回到 final-reply,只允许一句收尾;revise/reject 回到 tool-plan/成稿循环并继续提交同一 gddId 的下一版本。receipt、observation、session 或 recovery 状态未收口,以及 revise/reject 后尚无下一待审版本时,`plan_gdd_completion_blocker` 必须阻止最终回复。 + +结果 DTO: + +```ts +type PlanSubmitGddResult = { + outcome: 'submitted' | 'replayed' + gddRef: { gddId: string; version: number; fingerprint: string } + pendingActionId: string + approvalRequestId: string + recoveryPending: boolean +} +``` + +只有所有提交后投影都 durable 时 `recoveryPending=false`。已越过提交点但投影未齐时仍按首次提交或重放返回 `submitted/replayed`,并以 `recoveryPending=true` 表示未收口;页面不得重新提交新版本,只能用同 action identity 触发恢复。outcome 只表达权威事实是首次创建还是重放,投影恢复状态只由 boolean 表达。 + +## 13. 审批 pending、提交点与幂等 + +### 13.1 `gdd-approval` pending + +M1 新增独立 `.agent/planning/pending.json`,不升级、不迁移、不重写现役全局 `game-creator-pending-action.v5`,也不能把普通 `user.input_request` 或 confirmation pending 改名冒充审批。独立文件使用 `plan-gdd-approval-pending.v1` strict schema: + +```jsonc +{ + "schemaVersion": "plan-gdd-approval-pending.v1", + "kind": "gdd-approval", + "projectId": "...", + "agentId": "project-supervisor", + "gddRef": { + "gddId": "gdd-...", + "version": 1, + "fingerprint": "sha256-serde-json-v2:" + }, + "submission": { + "tool": "plan.submit_gdd", + "pendingActionId": "action-<24 位小写 hex>", + "actionFingerprint": "<64 位小写 hex>", + "approvalRequestId": "gdd-approval-" + }, + "runIdentity": { + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionId": "...", + "runId": "..." + }, + "status": "awaiting_decision", + "observation": null, + "pendingFingerprint": "sha256-serde-json-v2:" +} +``` + +所有嵌套对象同样 `deny_unknown_fields`。`agentId` 与 tool 是固定常量;`gddRef`、submission 和 runIdentity 的其它字段逐项来自不可变 GDD,其中 `pendingActionId=GDD.submissionId`、`runId=GDD.createdByRunId`。因此唯一无 receipt 的严格有效 GDD 足以确定性重建 `awaiting_decision` 文件,不需要也不允许猜现役 common pending 所需的 task、原 action input、loop/action index、nonce、project revision 或 verification gate。 + +`status` 只允许 `awaiting_decision | observed_approve | observed_revise | observed_reject`。无 receipt 时必须是 `awaiting_decision + observation:null`;receipt 存在时必须是与 receipt action 对应的 observed 状态和下述完整 observation。`pendingFingerprint` 使用第 9 节 helper,覆盖除自身外全部字段。文件是可重建投影而非审批真相;原 run durable 消费 observation 后直接清理,不持久化不可重建的 `consumed` 状态。若消费后、清理前崩溃,恢复会再次生成相同 observed 状态,依靠原 action terminal observation 幂等键安全重放后清理。 + +顶层 Runtime 状态继续复用 `waiting-for-user-input` 同族等待语义,不新增第二套 run status,但 action projection 必须显示 GDD approval waiting,不能当作通用自由输入。`approvalRequestId` 从不可变 GDD 读取,恢复不得换号。审批 observation 的稳定身份是 `(runId, pendingActionId)`,不是给现有 observation DTO 临时增加自由字符串 ID;observation body 从 receipt 确定性生成。 + +observation 使用现役四字段 strict shape `{tool,status,summary,detail}`,固定 `tool=plan.submit_gdd`、`status=ok`;approve summary/detail 分别为 `Fast GDD v{N} 已批准` / `用户已批准当前版本。`;revise 为 `Fast GDD v{N} 需要修改` / `用户修改意见:{normalizedComment}`;reject 为 `Fast GDD v{N} 已退回` / `用户退回原因:{normalizedComment}`。除规范化 comment 外不拼接自由错误文本,重放必须生成逐字相同 observation。 + +### 13.2 command 输入与线性化点 + +```ts +type DecideGameCreatorPlanGddInput = { + projectPath: string + gddId: string + version: number + fingerprint: string + pendingActionId: string + approvalRequestId: string + responseId: string + action: 'approve' | 'revise' | 'reject' + comment: string | null +} +``` + +`projectPath` 只是 Tauri transport 定位参数,不进入 receipt 或指纹;Runtime 从经过授权的项目根读取 `projectId`。UI 在一次用户决定开始时生成 responseId,busy、超时和网络重试都复用;用户改变 action/comment 后必须生成新 responseId。 + +项目锁内先验证项目与文件边界、GDD 文件名/projectId/gddId/version/重算 fingerprint、comment 规则并读取既有 receipt。若尚无 receipt,再要求当前唯一 active top-level exact plan run、独立 pending 的 kind/ID/request/source/profile/binding 与 GDD 逐项一致,随后计算 decisionFingerprint 并 create-only 发布 `approvals/v{N}.json`。若已有有效 receipt,不要求已经结束的原 run 仍是 current active:同 responseId 必须用既有 Runtime `decidedAtUtc` 重建 intent/receipt 做 replay 校验,不同 responseId 返回 `already-decided`;两者都按 receipt 的 durable run identity 补齐投影。无 receipt 且卡片或 pending 已过期才返回 `PLAN_STALE_APPROVAL`。 + +receipt 完整发布并同步父目录是用户决定的线性化点。此后不回滚、不覆盖、不删除,也不以“后续步骤失败”为由声称用户没有决定;改变决定只能提交并审批新版本。 + +### 13.3 receipt 后固定投影顺序 + +```mermaid +sequenceDiagram + participant UI as GDD 审批卡 + participant CMD as decide_game_creator_plan_gdd + participant LOCK as 项目 write lock + participant FACT as 不可变 GDD/receipt + participant PROJ as index/Markdown/audit/observation/session + participant RUN as 原 plan run + + UI->>CMD: approvalRequestId + responseId + ref + action + CMD->>LOCK: 获取锁并重读全部身份 + LOCK->>FACT: 校验 GDD,no-replace create receipt + FACT-->>LOCK: receipt durable(线性化点) + LOCK->>PROJ: 1 重建 index/status 与 Markdown + LOCK->>PROJ: 2 幂等追加 decision audit + LOCK->>PROJ: 3 写入 pending terminal observation 与 event/state + LOCK->>PROJ: 4 checkpoint session + LOCK-->>CMD: typed outcome + recoveryPending + CMD->>RUN: recoveryPending=false 时续跑精确原 run + RUN->>PROJ: durable 消费 observation 后清理 pending + CMD-->>UI: typed result +``` + +固定顺序是:权威投影 → audit → terminal observation → session → continuation 消费后清理 pending。pending 在 observation durable 前不能删除,因为它是原 action/run 的身份锚点。UI 发现有效 receipt 后立即隐藏 stale 审批卡,即使 pending ledger 尚待清理;恢复不得重新让用户决定。 + +agent.db 必须新增专用 `append_plan_gdd_decision_if_missing`。以下是 helper 入参的完整 strict payload;不允许额外字段或 comment 正文: + +```jsonc +{ + "recordType": "agent.runtime.plan.gdd_decided", + "auditSchemaVersion": "agent-runtime-plan-gdd-decided.v1", + "projectId": "...", + "agentId": "project-supervisor", + "gddId": "gdd-...", + "version": 1, + "gddFingerprint": "sha256-serde-json-v2:", + "pendingActionId": "action-<24 位小写 hex>", + "actionFingerprint": "<64 位小写 hex>", + "approvalRequestId": "gdd-approval-", + "responseId": "gdd-response-", + "source": "project-supervisor-plan-chat", + "runProfile": "standard", + "runProfileBindingFingerprint": "<64 位小写 hex>", + "sessionId": "...", + "runId": "...", + "action": "approve", + "decisionFingerprint": "sha256-serde-json-v2:", + "commentHash": "sha256-serde-json-v2:", + "commentLength": 0, + "receiptFingerprint": "sha256-serde-json-v2:", + "decidedAtUtc": "2026-08-10T00:00:00.000Z" +} +``` + +除固定 `agentId` 外,字段全部可从严格 GDD 与 receipt 确定性重建。`commentHash` 使用 domain `genarrative.plan.gdd-comment.v1`,canonical value 固定为字段顺序只有 `comment` 的 `{ "comment": normalizedCommentOrNull }`;comment 为 `null` 时也对显式 `null` 计算 typed hash,不使用空字符串或空 hash 代替。`commentLength` 是规范化后 comment 的 Unicode scalar count,`null` 固定为 0。 + +comment hash golden:`null` 的 69-byte envelope 是 `{"domain":"genarrative.plan.gdd-comment.v1","value":{"comment":null}}`,结果为 `sha256-serde-json-v2:3cae1a9b6efce6311511e88badea24f9be42814d0e9efb82e4396089999c0623`;`玩法更聚焦` 的 82-byte envelope 结果为 `sha256-serde-json-v2:a3f4cbbb5af197fc21623cbcea2b6f6b574f7a3acc5daa308fbda1013f83a19d`。 + +现役 agent.db serializer 在写 JSONL 时只额外加入通用 `schemaVersion=GAME_CREATOR_AGENT_DB_SCHEMA_VERSION` 与 `updatedAt`;stored validator 必须要求恰好这两个 envelope 字段,禁止其它额外字段。幂等比较先验证 envelope,再忽略 `updatedAt` 的写入时刻并对上述 helper payload 做完整相等比较;调用方不得传入或控制两个 envelope 字段。 + +幂等键为 `(recordType=agent.runtime.plan.gdd_decided, gddId, version, responseId)`;approvalResponseId 只在所属 GDD ref/action 域内幂等,不同版本允许合法复用同一文本值。same compound key/same 完整 strict payload replay,same compound key/different payload 返回 `PLAN_DECISION_IDENTITY_CONFLICT`。该 recordType 使用与 terminal observation 同级的专用保留额度,至少覆盖单 lineage 的 128 个版本,不能走 Ordinary append;尾部修复、容量上限和压缩后仍保留每个 receipt 恰一条逻辑记录。validator 必须区分 planning typed fingerprint 与裸 action/profile binding digest。 + +同一 `gddId/version` 内,同 responseId + 同 decisionFingerprint 返回原 receipt 并补投影;同 responseId + 不同 fingerprint 返回 `PLAN_DECISION_IDENTITY_CONFLICT`;不同 responseId 决定同一版本返回 `already-decided`,附既有 action/ref,不创建第二份 receipt。不同版本按上段复合键落入独立幂等域。 + +结果 DTO: + +```ts +type PlanGddDecisionResult = { + outcome: 'committed' | 'replayed' | 'already-decided' + requestedResponseId: string + decisionRef: { + gddId: string + version: number + gddFingerprint: string + approvalRequestId: string + responseId: string + action: 'approve' | 'revise' | 'reject' + decisionFingerprint: string + receiptFingerprint: string + } + approvedGddRef: { gddId: string; version: number; fingerprint: string } | null + recoveryPending: boolean +} +``` + +`requestedResponseId` 始终回显本次 command 入参;`decisionRef.responseId/action` 始终来自新建或既有 receipt。不同 responseId 决定已有 receipt 的版本返回成功 `already-decided`,同时返回请求 ID、receipt ID 与既有 action,不创建第二份 receipt;同 responseId、不同 intent 才返回 `PLAN_DECISION_IDENTITY_CONFLICT`。只有 receipt action 为 approve 才返回 approvedGddRef。`committed/replayed/already-decided` 均可与 `recoveryPending=true` 组合,表示 receipt 已确定但派生投影未齐;只有 `recoveryPending=false` 才允许“批准并开建”发出第二条独立开建命令。两项业务动作始终有两条 durable 记录。 + +## 14. 恢复与兼容矩阵 + +恢复顺序固定为:路径/普通文件/大小/schema → project/source/profile/run identity → GDD 连续性与 typed fingerprint → receipt 引用、decisionFingerprint 与 receiptFingerprint → 状态推导 → index/Markdown → pending → audit/observation/session。权威事实冲突时停止 mutation;只有缺失或落后的投影允许自动修复。 + +| 磁盘或运行状态 | 冻结行为 | +| --- | --- | +| 无 `.agent/planning/` | 老项目;直接开建零差异;首次策划在项目锁内懒创建 | +| valid v1 | 重算全部指纹、连续版本、项目/run identity 和 receipt 引用后读取 | +| unknown schema/field | `PLAN_UNSUPPORTED_SCHEMA`,只读失败;不覆盖、不降级 | +| GDD/receipt target 旁有 tmp | tmp 不是事实;持锁确认无活跃 writer 后清理 | +| GDD/receipt 有 `.previous` | 非法布局,进入 reconciliation;不可变 writer 永不创建 backup | +| GDD 存在、index 缺失/损坏/落后 | 从全部严格有效 GDD/receipt 重建 index/statusCache | +| index 引用缺失 GDD、版本缺号、同版本身份冲突 | `PLAN_NEEDS_RECONCILIATION`;禁止分配新版本 | +| receipt 缺 GDD,或 ref/project/run/fingerprint 不符 | 无效信任根;`PLAN_CORRUPT_AUTHORITY`,禁止开建 | +| GDD 无 receipt 且 pending 缺失 | 若全 lineage 恰好一个无 receipt 版本,按 GDD 内的 durable action identity 与 approvalRequestId 重建唯一 pending;不得生成新卡身份 | +| 两个无 receipt GDD | reconciliation;不猜哪一个待审 | +| stale pending 已有 receipt | UI 隐藏;补 audit/observation/session,精确原 run 消费后清理 | +| Markdown 缺失或头部不匹配 | 按第 7.1 节从权威 JSON 重渲染 | +| session primary + previous 为合法 successor 链 | primary 为准,清理 stale previous | +| session primary 缺失、previous 唯一有效 | 持锁提升、回读并同步父目录 | +| session primary 损坏但 previous 有效 | fail closed;不静默回退较旧 draft | +| session 两份有效但分叉 | `PLAN_SESSION_RECOVERY_REQUIRED` | +| session 缺失且存在 active run/未提交决定 | recovery required;不重置轮次或 gddId | +| 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 | +| appliedAnswers 指向最终 Hn,且 superseded handoff 摘要覆盖 H1…Hn-1 | 摘要 ID 序列必须等于 Hn binding 数组,并保留各祖先 session/response/checkpoint fingerprint;H1…Hn-1 均为稳定被替换历史,只补最终回答投影,不重新应用/请求旧 handoff。历史 handoff 文件可在 session durable 后清理;摘要缺口、乱序、分叉或 identity 不同则 reconciliation | +| session.appliedAnswers 已含相同 checkpoint,sidecar/observation 落后 | 不再请求 Agent 或写 decision;只补 answered/observation 与 same-run continuation | +| 同 request/response 的 answer hash、handoff response 或 decisionCheckpoint fingerprint 冲突 | `PLAN_ANSWER_IDENTITY_CONFLICT`;不选择任一版本 | +| 无三类业务消费证明;plan lifecycle 只有 started、无 batch,binding 指向 current session 的合法祖先 | 若无 GDD,仅在 handler 已取得并丢弃旧 response,或 owner/lease/boot 证明旧调用终止后追加 interrupted;回读终态后按 current session/context 的新 base attempt 0 前滚。否则保持恢复态,不并发 replacement | +| 无三类业务消费证明;plan lifecycle started + 同 binding ready batch | batch 证明 success handoff 已 durable;先幂等补 completed。若 binding 仍 current 则恢复 batch;若指向合法祖先则 supersede/回读/清理后新 base attempt 0 | +| 无三类业务消费证明;plan lifecycle completed + ready batch,binding 指向 current session 的合法祖先 | 若无 GDD,持锁把 batch 标记 superseded 并清理,再按新 base attempt 0 前滚;completed lifecycle 保留 | +| 无三类业务消费证明;plan lifecycle completed + batch 已清理,binding 指向 current session 的合法祖先 | 视为上一行崩溃恢复,继续创建新 base;同 session/context 下 completed 却无 batch 则 reconciliation | +| plan lifecycle/batch/handoff binding 缺失、损坏或互相冲突 | `PLAN_NEEDS_RECONCILIATION`;不删除、不补值、不重绑、不自动请求 | +| 无 active run、已有 durable GDD/receipt,用户显式“继续策划” | valid session 存在时写 revision+1 successor 并绑定新 activeRunId;session 确实不存在时才从事实创建相同 gddId 的最小 revision 1;不改变既有版本或 receipt | +| projectId/source/profile/binding/run 不符 | fail closed;不得跨项目复制信任或把普通/autonomous run hydrate 为 plan | +| receipt committed、command response 丢失 | 同 responseId 重试返回 replay;新窗口从 receipt 投影 `already-decided` | + +恢复“扫描”只枚举 canonical GDD/receipt 并逐个做完整验证,因而不是从任意孤儿猜意图。提交 GDD 自带 submission/action identity;receipt 自带 pending、request、response 和 run identity,pending 正常清理后仍能证明事实来源。 + +## 15. typed 错误合同 + +前端不得解析中文错误文案。M1 的 submit、approval、hydrate 和 build admission 返回统一错误对象: + +```ts +type PlanGddError = { + code: + | 'PLAN_INVALID_REQUEST' + | 'PLAN_PATH_UNSAFE' + | 'PLAN_PROJECT_ID_MISMATCH' + | 'PLAN_SOURCE_PROFILE_MISMATCH' + | 'PLAN_ACTIVE_RUN_EXISTS' + | 'PLAN_PENDING_GDD_EXISTS' + | 'PLAN_VERSION_LIMIT_REACHED' + | 'PLAN_SUBMISSION_IDENTITY_CONFLICT' + | 'PLAN_ANSWER_IDENTITY_CONFLICT' + | 'PLAN_DECISION_IDENTITY_CONFLICT' + | 'PLAN_STALE_APPROVAL' + | 'PLAN_UNSUPPORTED_SCHEMA' + | 'PLAN_UNSUPPORTED_KNOWLEDGE_BASIS' + | 'PLAN_CORRUPT_AUTHORITY' + | 'PLAN_SESSION_CAS_CONFLICT' + | 'PLAN_SESSION_RECOVERY_REQUIRED' + | 'PLAN_NEEDS_RECONCILIATION' + | 'PLAN_DURABILITY_FAILED' + message: string + retryable: boolean + recoveryAction: 'retry-same-id' | 'reload' | 'resume-original-run' | 'manual-reconcile' | 'none' +} +``` + +message 是有界、脱敏、用户可理解的中文;不得包含绝对路径、Provider payload、指纹全文、密钥或上游正文。提交点后的投影未齐使用成功 DTO 的 `recoveryPending=true`,不增加组合 outcome,也不伪装为“提交失败”。 + +## 16. M2 完整构建绑定 + +M1 只完成策划闭环。M2 才允许完整构建读取 approved GDD,并且必须显式区分两种启动意图: + +```ts +type PlanningBaselineInput = + | { + mode: 'approved-gdd' + approvedGddRef: { gddId: string; version: number; fingerprint: string } + } + | { + mode: 'direct-build' + approvedGddRef: null + } +``` + +不能把“缺少 ref”自动解释为直接开建;否则页面漏传、恢复丢字段或攻击者删字段会静默绕过策划。只有用户明确选择“直接开建”才能提交 `mode=direct-build`。 + +### 16.1 构建准入与 ref 贯穿 + +`mode=approved-gdd` 时,后端在项目锁内: + +1. 从 canonical GDD/receipt 重算 project identity、GDD fingerprint、receiptFingerprint、状态与当前 approved 版本;不信任 UI、index、Markdown、Agent 自述或 command 返回缓存。 +2. 核对输入 ref 精确等于当前有效 approved ref;旧 superseded ref、未审批候选或只有 Agent 文案均拒绝。 +3. 在同一个锁/CAS 边界内把 planning baseline 写入 root task record、durable run configuration binding 和 autonomous completion contract,再允许任务入队。当前在 completion contract 创建前释放项目锁的顺序必须调整或增加等价的可恢复提交标记,不能留下“task 已启动但基线未冻结”的窗口。 +4. Provider context bundle 持久携带 `planningBaseline`、权威相对路径与有界 GDD 摘要;恢复、retry、child 调度和 final completion 都核对相同 ref,不因后来批准新版本而换稿。 + +是否把 ref 写入 `.agent/manifest.json` 由 M2 结合迁移成本另行决策;但 task/run/completion identity 的贯穿是本方案已冻结的最低要求,不能省略。 + +`mode=direct-build` 不读取 planning sidecar,保持老项目行为。两种模式都继续使用现行 `project-supervisor-gui|cli + autonomous-game-build`,不把 plan source 带进 DAG。 + +### 16.2 现行 16 任务 DAG 的职责调整 + +- seed task ID、依赖和数量不变。 +- `design-director` 在 M2 变为确定性 GDD 准入门,不再启动 LLM。有 approved ref 时由 Runtime 物化 `.agent/spec.md` 并投影 completed;direct-build 时以“无策划基线”审计后确定性完成。 +- `design-foundation` 有 ref 时只把 approved GDD 具体化为实现规格、输入映射、HUD、实体、规则细节与 UI 原型,不得改玩法定位、支柱、目标用户或 MVP 范围;无 ref 时保持现行自主设计行为。 +- `game/game_design.md` 在有 ref 时写明 `Fast GDD v{N}` 与指纹前 12 位,完成门确定性核对;冲突时失败并要求新策划版本,不自行换玩法。 +- `art-director` 只读消费批准 GDD 的美术风格与 MVP 美术边界;详细视觉语言仍归美术链。 +- `quality-review` 检查核心循环、MVP 系统和支柱承诺是否偏离;`preview-playtest` 把 prototype validation items 转为观察清单;`publish-strategy` 可复用标题、一句话描述、目标用户和支柱。 +- 试玩发现只形成实现证据或下一版本建议,不回写、覆盖或“自动修正”已批准 GDD。 + +## 17. M3 game-chat 只读复用 + +game-chat 不强制先策划、不改变单主结构。项目存在有效 approved GDD 时,M3 可把 `title, oneLiner, coreLoop, mvpSystems 摘要, version, fingerprint` 作为只读上下文注入当前 `project-supervisor-game-chat` 根 run;没有时完全保持现状。 + +注入前仍从不可变 GDD/receipt 验证,不能读取 index 或 Markdown 当信任源。game-chat 的 `intentSummary` 语义、`audit-existing-first` 决策、唯一 `code-prototype` 主 Agent 与按需美术 child 不变;后续用户要求与 GDD 冲突时由 Supervisor 明示差异,不自动产生新批准版本。 + +## 18. 前端入口与交互合同 + +### 18.1 入口 + +- 新项目目标态默认提交 `standard + project-supervisor-plan-chat`;创建界面保留明确的“直接开建”。 +- 已有项目可从项目开发页进入“立项策划”或继续现行构建;一个项目同时最多显示一个 active plan run。 +- 策划阶段聊天输入属于当前 run:有活跃决策卡/审批卡时,输入回到该卡片对应 action;无 active run 时才可创建新的 plan continuation。 +- approved 后显示“开始完整制作”;“批准并开建”只是先审批、后开建的快捷交互,不合并后端命令或 durable 记录。 + +### 18.2 GDD 审批卡 + +审批卡直接从待审 `gdd.v{N}.json` 渲染:版本、短指纹、决定状态汇总、GDD 正文,以及“批准 vN”“修改”“退回重做”三项操作。revise/reject 要求多行原因;approve 原因可空。 + +卡片必须: + +- 持有 Runtime 提供的 approvalRequestId;一次点击生成 responseId,busy/超时重试复用。 +- command 进行中禁用重复点击;另一个窗口先决定后,当前卡刷新为 `already-decided`,不能覆盖。 +- `recoveryPending=true` 时显示可恢复状态,只允许重试同一 ID,不允许提交新版本或启动构建。 +- 待审版本的 receipt 已存在时隐藏对应 stale pending 卡;hydrate 只恢复精确 project/session/run/action identity。 +- approved 后只在所有必需投影恢复完成时启用完整构建按钮。 + +决策卡继续复用现有用户输入卡,不新建平行提问系统。现有完整构建 design 组用户名称改为“设计实现组”,与新阶段“立项策划”区分;内部 Agent ID 不改。 + +AI 游戏创作客户端壳继续遵守现行横屏工作台布局;GDD 中 desktop/mobile 是生成游戏产物的双视口合同,两者不是同一个 UI 尺寸要求。新增审批交互移动端优先、网页端可操作,不在面板内默认堆叠开发解释文案;独立详情应以弹层/独立面板打开,不能在当前卡下方无限追加。 + +### 18.3 权威 hydrate/read model + +M1 必须新增 `hydrate_game_creator_plan_gdd_state`,作为前端读取策划状态的唯一 command;input 使用 `deny_unknown_fields` 且只能是 `{projectPath: string}`。`projectPath` 只用于经过权限策略验证的 transport 定位,不回传、不进入任何业务 DTO 或 fingerprint。前端不能扫描 `.agent/planning/`、解析 Markdown、读取 index.statusCache 或自行拼接 receipt/session 状态。 + +返回值是以下完整、无额外字段的 `plan-gdd-state-view.v1`: + +```ts +type PlanGddStateViewV1 = { + schemaVersion: 'plan-gdd-state-view.v1' + projectId: string + gddId: string | null + state: + | 'not_started' + | 'draft' + | 'ready_for_approval' + | 'revision_requested' + | 'approved' + | 'rejected' + session: { + sessionId: string + sessionRevision: number + sessionFingerprint: string + phase: + | 'collecting' + | 'awaiting_user_input' + | 'awaiting_gdd_approval' + | 'revision_requested' + | 'approved' + | 'rejected' + | 'recovery_required' + roundsUsed: number + accumulatedAgentMillis: number + activeRunId: string | null + activeQuestion: { requestId: string; questionId: string; round: number } | null + decisionStateCounts: { + confirmed: number + defaultPending: number + prototypePending: number + } + } | null + versions: Array<{ + gddRef: { gddId: string; version: number; fingerprint: string } + status: + | 'ready_for_approval' + | 'revision_requested' + | 'approved' + | 'rejected' + | 'superseded' + approvalRequestId: string + createdAtUtc: string + decision: { + action: 'approve' | 'revise' | 'reject' + decidedAtUtc: string + } | null + }> + displayGdd: PlanGddV1 | null + pendingApproval: { + gddRef: { gddId: string; version: number; fingerprint: string } + pendingActionId: string + actionFingerprint: string + approvalRequestId: string + sessionId: string + runId: string + } | null + approvedGddRef: { gddId: string; version: number; fingerprint: string } | null + recoveryPending: boolean +} +``` + +`PlanGddV1` 精确等于第 8.3 节完整 strict GDD;页面不得反向提交或裁剪后冒充权威对象。无 planning 目录时成功返回 `not_started + gddId/session/displayGdd/pendingApproval/approvedGddRef=null + versions=[] + recoveryPending=false`,且不得为只读打开创建目录。存在版本时 versions 为 1~128 项并按 version 升序;approvedGddRef 始终是最高有效 approve 版本,即使更高版本后来 revise/reject 或当前正在形成新 draft 也不被撤销。 + +command 的权威读取与修复顺序固定为: + +1. 验证 canonical project root、manifest 与 projectId;路径不安全或项目身份不符直接返回 typed error。 +2. 在项目锁内只枚举第 7 节 canonical GDD/receipt 普通文件名,不把 tmp、`.previous`、index、Markdown、pending、audit、observation 或任意孤儿文件当事实。 +3. 对每个 GDD/receipt 做大小、strict schema/unknown field、canonical compact storage bytes、project/gdd/version/source/profile/run identity、typed fingerprint 和 receipt 引用验证;要求版本从 1 连续、同 projectId/gddId lineage、每版本至多一个 receipt、全 lineage 至多一个无 receipt 版本。 +4. 只从有效 GDD/receipt 推导 versions、approvedGddRef 和已提交版本状态。session 只决定尚未提交的 draft、active run 与待答问题,不得覆盖 GDD/receipt 结论。 +5. 以严格 session primary/previous 规则读取未提交解释状态;只有 receipt/GDD 已确定的 phase 前滚可以确定性修复 stale session,不能从聊天、Markdown、原始回答或版本摘要重造丢失的 draft decision。 +6. 最后才检查并按需重建 index、Markdown、planning pending、decision audit、terminal observation 和 session 已提交摘要。command 可以完成这些有限投影修复,但不得创建、覆盖或删除 GDD/receipt,不得启动/重试 Provider、创建 active run、增加轮次或替用户作决定。 + +顶层 state 按以下优先级确定,不能从 UI 徽标或 session 自述猜测: + +1. 存在唯一无 receipt GDD:`ready_for_approval`。 +2. 没有待审 GDD,且有效 session 正处于 `collecting | awaiting_user_input` 的 active draft:`draft`;此前 approvedGddRef 如有仍照常返回。 +3. 否则按最新 GDD 的 receipt:revise → `revision_requested`,reject → `rejected`,approve → `approved`。 +4. 没有 GDD、但存在有效 session:`draft`。 +5. planning 完全不存在:`not_started`。 + +`displayGdd` 依次选择唯一待审 GDD、最高有效 approved GDD、否则最新 revise/reject GDD;没有 GDD 时为 `null`。它的 ref 必须逐项等于 versions 中对应项。pendingApproval 只有在“恰好一个严格有效 GDD 无 receipt”时非空,六个 identity 字段都从该 GDD 确定性复制或推导;pending 文件缺失时可据此重建,不能生成新 approvalRequestId。 + +receipt 永远压过 stale pending:一旦该版本存在有效 receipt,pendingApproval 必须为 `null`,即使 pending 清理、audit、observation 或 session 前滚尚未完成;清理失败只令 `recoveryPending=true`,绝不重新显示审批卡。session 声称 awaiting approval 但 receipt 已存在时,按 receipt 返回 state 并确定性前滚 session;stale index/Markdown 不参与 state,能修就重建,暂未修齐同样只返回 `recoveryPending=true`。`recoveryPending` 可以与 draft/ready/revision/approved/rejected 任一已验证 state 组合,不是第二套权威状态。 + +错误语义固定如下: + +- 无 planning 目录是成功空 view,不是错误。 +- unknown/newer schema 或 unknown field 返回 `PLAN_UNSUPPORTED_SCHEMA`,不得由旧客户端覆盖。 +- GDD/receipt 非 canonical bytes、fingerprint 不符、引用损坏或不可变布局非法返回 `PLAN_CORRUPT_AUTHORITY`。 +- project/source/profile/run/session identity 不符返回对应 mismatch;无法更精确分类的权威身份冲突返回 `PLAN_CORRUPT_AUTHORITY`,不得降级为空状态。 +- active draft/run 的 session 缺失、损坏或分叉返回 `PLAN_SESSION_RECOVERY_REQUIRED`;不能凭已有 GDD 重置轮次。 +- 两个无 receipt GDD、版本断档、同版本身份冲突或合法性无法唯一证明返回 `PLAN_NEEDS_RECONCILIATION`,不得猜测待审版本。 +- 权威事实有效、只有派生投影暂未完全修复时返回成功 view + `recoveryPending=true`,不能把已经 committed 的 submit/decision 报成失败。 + +返回值不包含绝对路径、Prompt/system messages、Provider metadata、API 配置、审批 comment 正文或内部诊断;versions.decision 只暴露 action/time。前端在项目打开、页面 reload、App resume、submit 返回后、decision checkpoint 完成后和 approval decision 返回后调用 hydrate;已有 runtime polling 只负责运行态和现役 user-input 卡片 transport,不得作为 GDD authority。submit/decision 结果与随后 hydrate 不一致时,以 hydrate 对权威文件的重验为准并显示 typed 恢复状态,不在前端乐观改写审批事实。 + +## 19. 安全不变量与构建验证边界 + +1. 不放松 `autonomous-game-build` 对 `user.input_request` 的现行禁等待防线;多轮策划只发生在 top-level standard plan run。 +2. plan source 的 action 工具广告与执行双门都只有四项,MCP 为空,且跳过 Supervisor collaboration 强制委派。 +3. `.agent/planning/**` 与 `game/fast_gdd.md` 唯一写者是 Runtime;任何 Agent 通用写工具都不能触达。 +4. GDD、receipt 追加不可变;index、session、Markdown 和 UI 只作有限投影,不能反写权威事实。 +5. approve receipt 是构建信任根;Agent 文本、“已批准”状态缓存、Markdown 徽标或 UI 内存都无批准权。 +6. 所有 mutation 在项目锁内重新核对当前 root/project/run identity;跨项目复制、旧 run、错误 source/profile/binding 失败关闭。 +7. 构建始终由用户动作启动;plan tool、approval observation 和 final reply 都不能直接播种 DAG。 + +`game.static_smoke` 的目标能力与职责固定区分如下;正式任务 M0-3(PR 工作包标签 `M0A-2`)负责新增 dated 决策,明确取代 2026-07-26 旧决策中的排他性 smoke 表述,并把 tracked 文档、Prompt、policy 与测试统一到该裁决。在 M0-3 合入前,本表是待实施目标,不能声称旧冲突已经消失: + +| Runtime 路径 | Agent | 工具边界 | 职责 | +| --- | --- | --- | --- | +| 立项策划 | 立项策划 persona | command/smoke/preview 全部不可见且执行拒绝 | 无构建或验证职责 | +| 完整 16 任务 DAG | `design-foundation` | 只允许固定 `game.static_smoke` 验证本人当前 mutation revision;拒绝 preview/process/其它 command | 写后自检,不是最终验收 | +| 完整 16 任务 DAG | `preview-readiness` | 允许固定 `game.static_smoke` | 唯一正式最终静态验收任务 | +| game-chat 单主 | 根 `code-prototype` | source-bound 固定 smoke + desktop/mobile `preview.validate` | 对当前单主可玩 revision 负责 | +| game-chat 动态美术 child | `assets/**` 范围内交付;无根最终验收权 | 不得写 game/memory/.agent 或执行 command/preview | 只交付美术回执 | + +正式任务 M0-4 的 PR 工作包 `M0B-1` 还必须让 `agent-delegate` 与 `agent-delegate-retry` 共用严格动态美术 lineage predicate;当前 retry source 不能绕过 `assets/**`。该修复不属于本文档 PR 的功能实现,但在 `M0B-2` 收敛 game-chat 前端投影前必须完成。 + +## 20. rollout、停用与回滚 + +- M1 以 AppData/Runtime capability 开关启用,不把 feature flag 写进用户项目。 +- 开关关闭时不创建新 plan run、不接受新的 submit/decision mutation;已有 sidecar 只读保留,“直接开建”保持现状。 +- 重新开启后从同一 gddId、版本、receipt 和 session chain 恢复,不重置版本。 +- unknown/newer schema 只读失败,不由旧客户端降级覆盖。 +- 任意冲突只前滚修复;禁止自动删除 planning 目录、重编号 GDD、改指纹、覆盖回执或抹去用户 comment。 +- M0 文档回滚时,本方案、docs 索引与 decision log 必须同进同退,不能留下悬空引用。 +- M1 已开始后,如需修改 durable source、路径、schema、domain、enum 或 ID 语义,必须新增 breaking-change 决策和迁移方案,不能静默改名。 + +## 21. 测试与故障注入矩阵 + +| 层 | 必测合同 | +| --- | --- | +| 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 版本上限 | +| 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 落后的恢复 | +| 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 提交并批准新版本;响应丢失重试 | +| projection recovery | index/Markdown/audit/observation/session/独立 pending 每个断点恢复;现役 global pending v5 零迁移;恰一逻辑 audit/observation;无永久 stale 卡 | +| agent.db | 专用幂等 helper;same key conflict;日志达到普通容量、尾部截断与压缩后仍能补齐并保留决定记录 | +| source/security | durable exact identity;四 action tool 广告与执行;MCP 空且 webSearchEnabled=false;control functions 单列;tool-plan/batch/ledger/context/repair/completion 全部跳过 collaboration;plan retry 保留 source/profile;所有副作用工具拒绝 | +| Prompt | tool-plan、decision-checkpoint 和 final-reply 都用 supervisorPlanChat;3 轮/单题/固定选项;回答后设计解释与 prototype item;平台事实;恢复摘要;direct-out 条件 | +| frontend | 默认入口与直接开建;stable approvalRequestId/responseId;busy;stale card;hydrate strict input/view;无目录空态;receipt 隐藏 stale pending;corrupt authority typed error;project open/reload/resume/submit/decision 刷新;recovery pending;批准并开建两命令顺序 | +| M2 integration | explicit approved/direct mode;锁内重验 receipt;ref 贯穿 task/run/completion/context;无 ref 非回归;恢复不换稿 | +| M3 integration | 有批准 GDD 的只读注入;无 GDD 零差异;不改变 game-chat 单主 lineage | + +关键强杀点逐项覆盖: + +1. GDD temp 写入前、写入后、no-replace 发布前、发布后父目录同步前、提交点后。 +2. GDD 提交后、index/Markdown/pending/session 各投影之间。 +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。 +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 语义而不是假失败。 + +M0 文档 PR 本身最低验证:Markdown 结构与三张 Mermaid 图可解析;golden envelope bytes/hash 重算一致;相对链接存在;tracked diff 不包含仓库外依赖或本机路径;`npm run check:encoding` 与 `git diff --check` 通过。 + +## 22. 当前代码证据与 M1 接入点 + +以下行号基于 2026-08-10 当前基线;实施时必须打开源文件复核,不能只复制行号。 + +| 主题 | 当前证据 | M1/M2 要点 | +| --- | --- | --- | +| Supervisor trusted source | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver.rs:107-120` | 当前只有 gui/cli/game-chat;M1 新增 plan 常量与 matcher | +| autonomous matcher 消费者 | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/run_configuration.rs:112-116`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs:37-68,6848-6861,6982-7007,14396-14408` | 当前多处复用 trusted matcher;M1 必须拆出 autonomous-only matcher,不能因加入 plan 污染完成/恢复 | +| Run Profile | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_adapter.rs:48-75`、`apps/ai-game-creator-shell/src-tauri/src/main.rs:1243-1244` | 复用 `standard`,不新增 profile | +| Supervisor start 校验 | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs:114-134`、`apps/ai-game-creator-shell/src-tauri/src/commands.rs:497-529` | 增加 top-level plan source/profile 组合门 | +| durable run binding | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/run_configuration.rs:72-125,226-374` | source/profile/root/parent CAS 与恢复必须贯穿 | +| 前端提交链 | `apps/ai-game-creator-shell/src/features/agent-runtime/model.ts:844-899`、`apps/ai-game-creator-shell/src/App.tsx:5743-5800` | 已有 source/profile DTO;后端仍须重验 | +| Prompt Bundle | `apps/ai-game-creator-shell/src-tauri/prompts/runtime/manifest.json:30-74`、`apps/ai-game-creator-shell/src-tauri/build_support/runtime_prompt_bundle.rs:73-85,867-925` | 新增 supervisorPlanChat 与 SupervisorPlanChat | +| Prompt 请求/收尾 | `apps/ai-game-creator-shell/src-tauri/src/agent/prompt.rs:551-630`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs:123-178,208-241,314-318` | standard plan 的 tool-plan/final-reply 都按 source 分流;context 不读取/渲染 collaboration 或通用能力 | +| Provider request lifecycle / wire | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/models.rs:233-248`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_control.rs:316-343`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/real_e2e_checkpoint.rs:1073-1276`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/provider_retry.rs:1391-1451`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:897-922`、`server-rs/crates/platform-llm/src/lib.rs:55-75,167-180,2286-2349` | exact plan 从同一 captured session 构造 request/context binding;v3 lifecycle 与 v4 batch 同 binding;最终 model/request 字段及 adapter wire 配置进入 context fingerprint;旧 attempt 先终结再重试;stale 合法前滚和 identity 污染分流,replacement base/attempt 确定 | +| Provider 工具广告 | `apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs:279-314`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/tool_policy_snapshot.rs:19-128` | plan native 广告 exact allowlist,MCP 为空 | +| 执行 policy | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs:52-104,178-255`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/action_execution.rs:78-130` | source-aware 广告/执行双门;action_execution 只保留防御,submit 实际执行必须在普通 dispatch 前被专用分支截获 | +| submit sole-action 语义 | `apps/ai-game-creator-shell/src-tauri/src/agent_native_tools.rs:317-470`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs:540-594` | 在 action plan/batch identity 持久化前拒绝 mixed submit 或多次 submit | +| Supervisor collaboration | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_tool_plan.rs:540-594,852-914`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_action_batch.rs:342-455,560-606`、`apps/ai-game-creator-shell/src-tauri/src/collaboration.rs:82-124,846-868` | exact plan 跳过 policy/state/preflight、liveness/repair、合同注入与能力重广告 | +| 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,answer-prepared 后发起只含 strict update_agent_plan 的 checkpoint turn,success handoff 后 session CAS,非 plan 行为不变 | +| 现役 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 | +| 项目锁 | `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` | collaboration blocker 对 exact plan 为不适用;新增 plan GDD 专用完成门 | +| plan retry | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs:740-772,775-907` | generic standard fallback 前保留 exact plan source/profile/session lineage | +| planning hydrate command | `apps/ai-game-creator-shell/src-tauri/src/commands.rs:856-875`、`apps/ai-game-creator-shell/src-tauri/src/main.rs:2178-2180`、`apps/ai-game-creator-shell/src/App.tsx:1550,1650,2957,3364,3702` | 新增单一 hydrate command/注册与前端生命周期调用;不把 runtime polling 当 GDD authority | +| agent.db | `apps/ai-game-creator-shell/src-tauri/src/project/agent_db.rs:955-1034,1551-1605,1995-2088` | 普通 append 不满足 decision 幂等;新增专用保留入口 | +| 16 任务 DAG | `server-rs/crates/shared-contracts/src/game_creation_app.rs:263-425` | M0/M1 不改拓扑 | +| design-director 现状 | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs:70-85`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs:1480-1506` | mutation owner 清单不含 design-director,因此当前落入只读协调 Prompt;M2 才确定性化 | +| scheduler delivery | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/delivery.rs:171-185` | 下游不能依赖普通 durable delivery,必须读权威文件/ref | +| foundation smoke | `apps/ai-game-creator-shell/src-tauri/src/agent/prompt.rs:487-499`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/policy.rs:10-32,82-89` | 现行写后自检能力与旧文字有漂移,M0-3 / 工作包 M0A-2 显式裁决并对齐 | +| approvedGddRef | 当前仓库无匹配实现 | M2 从 command 到 task/run/completion/context 全链新增 | +| game-chat retry 边界 | `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs:7-39,120-134`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/lifecycle_control.rs:740-772,885-905` | M0-4 / 工作包 M0B-1 统一 delegate/retry assets-only lineage | + +## 23. 分期门禁与完成定义 + +`M0A-*`、`M0B-*` 只是便于拆 PR 的工作包标签,不是新增里程碑;正式编号始终只有 M0-1~M0-4。 + +### 23.1 M0-1 / M0-2(PR 工作包 `M0A-1`) + +- 本文、`docs/README.md`、document map 和 decision log 同步合入。 +- D1~D8、注册表、Prompt、submit/GDD/receipt/session 的业务语义、指纹、golden、create-only 算法、提交点、typed DTO/error、恢复矩阵和 M1 测试方向已形成阶段基线;checkpoint handoff 的私有 schema/path/ledger 排序作为 M1 详细设计与合入前置决策保留,不在当前非交付检查点内选择实现方案。 +- 三张 Mermaid 图分别覆盖运行拓扑、版本/审批状态、提交/恢复时序。 +- 合入只表示 M0A-1 非交付阶段设计检查通过,不表示 M0A、M1 入口门或 M0 全部完成,也不表示任何功能已上线。 + +M0A-1 是 M1 详细设计与技术 spike 的输入,不是功能交付。M1 可以与 M0-3 并行准备;但 checkpoint handoff 私有持久化决策未冻结前,对应 checkpoint 代码不得合入,M0-3 的 dated 决策、Prompt、source/tool policy 与测试未全部合入前,依赖 Fast GDD source/tool policy 的 M1 代码也不得合入主线。M0-4 不阻塞 M1/M2,仅阻塞 M3-4 和“M0 全部完成”;不得因为并行关系把工作包标签误报为阶段完成。 + +### 23.2 M0-3(PR 工作包 `M0A-2`) + +新增 2026-08-10 dated 决策,明确只取代 2026-07-26 条目中“`design-foundation` 不得调用固定 smoke / 只有 `preview-readiness` 可调用”的排他性表述;随后对齐 `design-foundation` 本人写后固定 smoke、`preview-readiness` 最终静态验收、plan source 零 smoke 的文档、Prompt、policy 与测试。M0-3 本轮尚未开始,不得用第 19 节目标表冒充已完成裁决。 + +### 23.3 M0-4(PR 工作包 `M0B-1` / `M0B-2`) + +`M0B-1` 先封闭 game-chat 动态美术 delegate/retry 的 `assets/**` 安全边界,`M0B-2` 再以 source-aware lineage 修复单主进度、最终回复、可玩 revision 与归档投影。M0-4 不阻塞 M1 策划闭环开工,但阻塞 M3 game-chat 接入和“M0 全部完成”。M0-1~M0-4 全部合入并通过各自门禁后,才能标记“M0 全部完成”。 + +## 24. 最终不变量摘要 + +- 策划与构建是两个不同 profile/source 的 run,由用户动作连接,不由 Agent 自行升级。 +- 每轮决策解释先冻结在 activeQuestion,用户回答只在 session CAS durable 后计入 roundsUsed;同一回答永远不能产生两条解释。 +- 每个 plan Provider request 的实际 messages、工具、结构化注入与 durable binding 来自同一 captured session;stale 输出不能重绑到新 session。 +- GDD durable create 是提交点;approval receipt durable create 是用户决定线性化点。 +- 不可变事实只增不改;其它内容都能从事实或 session checkpoint 有界恢复。 +- approvalRequestId 属于不可变 GDD,approval receipt 的 responseId 属于一次审批决定,两者都不可在重试中漂移;user-input answer/checkpoint 的同名 responseId 属于独立回答幂等域,不能跨域复用或恢复。 +- decisionFingerprint 解决意图幂等,receiptFingerprint 保护完整信任根。 +- plan source 无 command/smoke/preview/委派/MCP;内部 Markdown 投影不推进代码 revision。 +- 前端只通过 `hydrate_game_creator_plan_gdd_state` 读取策划权威状态;文件、Markdown 和 runtime polling 不能在页面侧合成批准事实。 +- 完整构建只认后端在项目锁内重算并冻结的 approvedGddRef;直接开建必须显式声明,不能由缺字段降级。 +- 任何无法证明 project/source/profile/session/run/action/ref 身份的恢复或 mutation 都失败关闭。