# 决策记录 > 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。 > 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。 ## 记录格式 ```md ## YYYY-MM-DD 决策标题 - 背景:为什么需要这个决策 - 决策:最终决定是什么 - 影响范围:涉及哪些模块/文档/流程 - 验证方式:如何确认决策仍有效 - 关联文档:相关 PRD、技术文档、提交或 Issue ``` ## 2026-09-05 Planning V2 一次性写路径对齐 V1 的项目锁等待窗口 - 背景:V2 审批修改意见后立即 `continue_planning_session_v2`。审批、回合启动、策略落盘和 hydrate 原先无等待取锁,和 GUI 重灌或其它写操作撞车就返回 `项目正在被其他写操作占用`,前端再映射成总控失败。V1 已用完整/短窗口处理同一形状。 - 决策:V2 审批、回合启动、策略落盘、失败投影和 GDD 认领使用完整等待窗口;V2 hydrate 使用短窗口。不引入可重入项目锁,不放宽失效回收。 - 影响范围:`planning_policy_v2.rs`、`planning_session_v2.rs`、V2 hydrate 前端瞬时争用处理。 - 验证方式:Rust 定向测试覆盖短暂占用下的审批、修订续跑和 hydrate。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`docs/project-memory/shared-memory/pitfalls.md`。 ## 2026-09-05 本进程新建 Windows 私有对象不因继承 DACL 自动 UAC - 背景:#211 要求 sidecar 满足当前用户独占、禁止继承的 DACL。新建文件会先继承父目录 ACE,生产路径把这种短暂不合格送进 UAC;`project.lock` 还在独占句柄上 harden。含空格项目路径上提权 ArgumentList 被拆开,修复以 exit 1 失败。GDD 审批改意见因此弹权限,V1 锁创建不会。 - 决策:`harden_new_game_creator_private_path` 只在本进程收紧 owner/DACL,失败则删除刚创建的对象,不 UAC 接管。项目锁先写再释放句柄再 harden,并用内容回读防换绑;UAC 仍只用于允许范围内的已有外人本对象。提权 helper 的 ArgumentList 改为一条按 Windows 规则加引号的字符串。 - 影响范围:`config.rs` 的新建 harden 与提权命令行、`project/write_lock.rs` 的项目锁创建;不改变锁竞争、失效回收、Drop 删除,也不放宽 symlink / reparse / 外人本 fail-closed。 - 验证方式:Windows 定向测试覆盖 `Genarrative GameAgent\gameagent-*` 取锁与私有 DACL,以及带空格路径的 quoted ArgumentList。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`docs/project-memory/shared-memory/pitfalls.md`。 ## 2026-09-05 Planning V2 的 3 轮策略与 8 个问题门禁有意不对称 - 决策:模型提示最多提问 3 轮,并在达到 3 后要求出稿;Runtime `question_limit` 默认 8,对偏离模型策略的合法问题保留接收空间,达到 8 才拒绝新 question。前者是模型行为指令,后者是运行时接收边界,数值有意不同,不是缺陷或配置不一致。 - 评审口径:Session/UI 的 8 是容量,不是必须问满的配额;正常路径在 3 轮或更早出稿符合设计。不得仅因数值不同,把模型提示改为按 `question_limit` 出稿、把 3 改成可继续到 8 的软目标,或把 Runtime 门禁收紧为 3。 - 影响范围:仅补充文档解释,现有提示词、运行逻辑、校验和测试保持不变。 - 关联文档:[策划会话 Runtime V2 接入与旧链路退役方案 §1.3](../../technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md#13-问询策略与-runtime-门禁的不对称设计)。 ## 2026-09-04 Planning V2 将决定 ID 从 Provider 输入移回 Runtime - 背景:`plan_submit_gdd` 原先要求模型生成 `decisions[].id` 及原型验证项引用 ID。该字段既不是方案内容,又容易出现 `initial_request`、`initialRequest` 或错误层级,导致合法 GDD 在 Runtime 事后校验阶段失败。 - 决策:Provider-facing `plan_submit_gdd` schema 和入参删除决定/原型验证项 ID。Runtime 按决定数组顺序生成首项 `initial-request`、后续 `decision-{序号}`,并按 `prototype_pending` 决定顺序给原型验证项绑定同一 ID。最终持久化 `plan-gdd.v2` 仍保留 ID,供审批、引用和 fingerprint 使用。V2 尚未上线,不为旧 Provider 输入或历史 V2 artifact 增加兼容转换;不符合新契约的历史数据按现有失败策略处理。 - 影响范围:`planning_policy_v2.rs` 的工具 schema、Provider 入参解析、Runtime 产物构建与定向测试;Planning V2 技术方案。 - 验证方式:定向 Rust 测试确认 schema 不含 ID、无 ID 输入可生成 Runtime ID;并运行 `cargo fmt --check`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_policy_v2.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_session_v2.rs`。 ## 2026-09-04 Planning V2 持久化每次 Provider 尝试的诊断产物 - 背景:Provider 已成功返回但策略解析失败时,原 V2 只保留最终错误,无法核对实际请求、原始工具参数和单次重试结果。 - 决策:在 `.agent/planning-v2/debug//` 保存每次尝试的 request、response 和分类事件;诊断文件不进入会话上下文,不参与恢复、重试或 GDD 业务判断,写入失败不改变主流程结果。 - 影响范围:`planning_session_v2.rs` 的 Provider 调用外围和 Planning V2 技术方案持久化目录说明。 - 验证方式:通过 Provider 请求/响应产物可还原每次尝试及 `toolCalls.arguments`,并确认主流程仍按原有解析、重试和状态转换执行。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_session_v2.rs`。 ## 2026-09-04 Planning V2 把 `gdd.vN.json` 创建成功当作提交点 - 背景:V2 persist 先 create-only 写入不可变 GDD,再更新 index、Markdown、conversation 和 session。后续任一步失败会把 session 标成 `provider_failed`,但不回滚已创建文件;重试会重新生成 UUID/时间戳并撞上“已存在且内容不同”,hydrate 又只信 `current_artifact_version`,项目会卡死。 - 决策:`gdd.v{N}.json` 创建成功即提交点,禁止回滚不可变文件。persist / hydrate / 回合启动若发现 session 指针的下一个连续版本已在磁盘,必须读取既有 GDD 补投影,不得用新的 LLM 入参重建身份。session 指针写成功前的投影失败仍可返回 persist 错误,但恢复路径必须认领该版本。 - 影响范围:`planning_policy_v2.rs` persist/认领、`planning_session_v2.rs` hydrate 与回合启动;V2 技术方案。 - 验证方式:Rust 测试覆盖孤儿 GDD 重试认领、hydrate 认领、成功提交后仍分配下一版本;`cargo fmt --check`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`。 ## 2026-09-04 PlanningSessionRuntime V2 用协议工具输出问询和 GDD - 背景:原型已验证 `plan_ask_question` / `plan_submit_gdd` 两个协议工具、深层 schema、提示词只留策略、`tool_choice=auto` 可跑通;生产 V2 仍解析正文 `{kind,question|gdd}` JSON,并把形状骨架写在 system prompt 里。浅 schema + 正文 JSON 会误导模型把 GDD 写成普通文本;`tool_choice=required` 与 DeepSeek thinking 不能同时使用。 - 决策:V2 Provider 请求固定挂这两个协议工具,`tool_choice=auto`,`strict=false`。模型必须恰好调用其中一个;Runtime 解析 `toolCalls` 归一为 Question/Artifact,正文 JSON 视为非法。system prompt 只保留问询/出稿策略和当前问询进度,不再附 JSON 骨架或数量清单。入参不再要求模型回声 `schemaVersion`,落盘 GDD 仍由 Runtime 写入 `plan-gdd.v2`。既有结构门禁(含 `initial-request` 首项、`validate_plan_game` 数量/字数)不变,失败仍回灌一次。不把协议工具写入 `capabilities.tools`,不执行 MCP/Skill,不把 `tool_call`/`tool_result` 写入会话消息。 - 影响范围:`planning_session_v2.rs` 请求构造、重试文案与 Provider 结果投影;`planning_policy_v2.rs` 工具 schema、解析和入参 `schemaVersion`;V2 技术方案。 - 验证方式:Planning V2 定向 Rust 测试覆盖工具解析、缺 `schemaVersion` 的合法 GDD、正文 JSON 拒收、工具 schema 含嵌套 `game` 字段、提示词不再含骨架;`cargo fmt --check`、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`。 ## 2026-09-03 新建策划会话采用 PlanningSessionRuntime V2,旧 Supervisor 链路直接退役 - 背景:现有“做方案”依赖 `project-supervisor-plan` 根 Run、`project-planning` 子 Run、静态委派、delivery、Acceptance Graph 和审批前 evidence。新策划 Agent 只需要单 Agent 会话、问询、GDD 和审批;继续在旧 Runtime 上逐条放宽会保留身份/编排耦合。未来策划 Agent 可能支持无限多轮、MCP 和 Skill,需要避免把当前 8 题/GDD/no-tools 固化为 Runtime 根结构。 - 决策:新增独立 `PlanningSessionRuntime`,复用 Provider/流式、会话持久化、项目锁、原子写和基础错误恢复;当前启用 `mode=gdd`、最多展示 8 个有效问题、GDD 审批和用户修改。新建“做方案”会话不创建 Supervisor root、planning child、delegation 或 acceptance evidence。V2 使用独立 `.agent/planning-v2/` 与 V2 schema,继续输出 `game/fast_gdd.md`;不自动转换旧会话。 - 兼容性:Session 保存 `mode`、可空 `questionLimit`、`capabilities.tools/skills`;完整会话记录与 Provider 请求上下文分离;消息模型预留 tool/skill 事件类型但本期不执行 MCP/Skill。无限问询、上下文摘要、多产物和能力执行以后作为策略/能力层扩展,不重新引入 Supervisor 身份模型。 - 当前进度:P0 合同冻结、P1 会话内核和 P2 GDD/审批核心已落地;P3 正式入口/UI 接入已开始,P5 旧链路退役尚未开始。 - 退役:V2 切换时旧链路直接封存;所有未完成旧会话投影为 `legacy_retired` 失败,禁止继续问询、审批、恢复或 continuation。旧 GDD、approval、conversation 和 `.agent/planning` 文件只读保留;旧入口 caller 关闭,但不删除旧代码、旧测试或旧数据。 - 影响范围:AGC 做方案入口、Rust/Tauri planning session/Provider adapter、GDD/审批 V2、前端 planning lane、阶段任务与 BDD 验收;做游戏/做素材 DirectProject 不变。 - 验证方式:按 `docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md` 的 P0~P5 阶段验收执行;至少覆盖第 8 个问题、上限后 question 抑制、Provider 失败、非法输出、批准/修改/退回、重启恢复、旧会话切换强制失败、迟到 Provider 结果丢弃和当前空能力快照。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`。 ## 2026-09-03 PlanningSessionRuntime V2 统一 Agent 推断语义 - 背景:旧 V1 使用 `default_pending` / `answerSource=default` 表示未提问时由 Agent 按默认建议补齐的字段;该语义会让 V2 的 Agent 推断看起来像产品默认值,也会造成原型与生产字段不一致。 - 决策:V2 只使用 `confirmed`、`assumption_pending`、`prototype_pending` 三种决定状态;`assumption_pending` 的来源统一为 `agent_inferred`。`answerSource` 仍不是独立阻断项,缺失或不一致时按状态归一为 `user_freeform`、`user_option` 或 `agent_inferred`。V1 的 `default_pending` / `default` 校验和历史数据保持不动,不作为 V2 合同的一部分。 - 问询策略:V2 出稿前必须确认玩家核心行为、单局目标/核心循环、MVP 制作边界;其中任一仅由 Agent 推断时继续问一个关键问题。`questionLimit` 是 Runtime 对已展示问题数的硬上限,提示词中的“默认最多三轮”只是策略偏好,不要求与硬上限数值一致。 - 影响范围:V2 GDD 输入/产物、Provider system prompt、前端 V2 类型与决定状态展示;旧 Supervisor/V1 存储、校验和历史产物不变。 - 验证方式:V2 解析 `assumption_pending` 不报错并落盘为 `assumption_pending/agent_inferred`;`default_pending` 不作为 V2 合法状态;核心三项未确认时提示词要求继续问询;相关 Rust/TS 定向测试、类型和编码检查通过。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_policy_v2.rs`。 ## 2026-09-04 PlanningSessionRuntime V2 将既有输出阻断原因回灌给 Provider - 背景:原型已将会导致输出拒收的字段、类型、数量和长度契约写入提示词,并在校验失败重试时回灌具体原因;生产 V2 仍只有简要 GDD 形状提示,模型可能重复犯同一结构错误。 - 决策:生产 V2 只同步当前已经存在的 question/GDD 校验契约到 Provider system prompt,并在现有一次重试中明确列出本次阻断原因、要求逐项修复;不扩大校验范围、不新增门禁、不增加重试次数,也不把 `answerSource` 变成阻断条件。 - 影响范围:`planning_session_v2.rs` 的 Provider prompt 与现有非法输出重试提示;`planning_policy_v2.rs` 校验逻辑、问询上限和持久化契约不变。 - 验证方式:运行 Planning V2 定向 Rust 测试、`cargo fmt --check`、`git diff --check`,确认提示词构造和现有校验路径通过;不改变既有校验结果。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_session_v2.rs`。 ## 2026-09-04 PlanningSessionRuntime V2 提示词只保留形状和策略 - 背景:把逐字段长度、数量、类型清单写入 system prompt 后,提示词与 JSON 骨架、Rust 校验器三重叠,模型也难以消化长清单;精确数字已由校验失败的一次重试回灌。 - 决策:V2 system prompt 只保留问询/GDD 骨架、`game` 与 `decisions` / `prototypeValidationItems` 同级边界、状态枚举、首条 `initial-request` 约束,以及一行易错数量范围(options / keywords / pillars / coreLoop / mvpSystems / outOfScope / oneLiner)。不把逐字段长度、控制字符、label 去重等校验细则写入 prompt;校验范围、门禁和重试次数不变。 - 影响范围:`planning_session_v2.rs` 的 Provider system prompt;`planning_policy_v2.rs` 校验逻辑与非法输出重试路径不变。 - 验证方式:提示词含骨架与同级边界、不含逐字段长度清单;现有 Planning V2 定向测试、编码和 diff 检查通过。 - 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_session_v2.rs`。 ## 2026-09-03 AGC 登录 route event 使用 handler 已验证主体归属 - 背景:登录请求进入时尚未拥有 `AuthenticatedAccessToken`,通用 tracking middleware 无法从响应 extensions 归属登录成功用户;将 AGC marker 直接写入按用户/业务日幂等的 `daily_login` 又会受到不同来源登录顺序影响。 - 决策:密码登录和手机号登录 handler 在认证及 session 创建成功后,仅对合法 AGC marker 请求向响应 extensions 附加一次性 `TrackingLoginSubject`。tracking middleware 在现有 `ExternalApiPrincipal`、`AuthenticatedAccessToken` 之后使用该主体生成登录 route event 的 `user_id`、`owner_user_id` 和 User scope。`daily_login` 保持原有 event key、幂等键、业务日和 metadata 语义,不承担 AGC 来源归因。 - 安全边界:主体只来自后端认证服务返回的用户 ID;Header 仅决定是否进行 AGC 来源归因,不参与身份计算;不解析、不记录 access token、refresh token 或 Cookie。 - 影响范围:`api-server` 登录 handler、资产读取 handler、tracking middleware、Issue225 技术方案和定向集成测试;不修改 SpacetimeDB schema、migration、bindings、OpenAPI、后台页面或认证响应协议。 - 资产读取边界:`/api/assets/read-url` 和 `/api/assets/read-bytes` 继续支持匿名公开读取;有效 Bearer 复用同一次可选鉴权结果,在成功响应 extensions 中传递 `AuthenticatedAccessToken`,供 AGC route tracking 归属用户。External API Key/Admin 路由继续使用各自主体和审计链路。 - 验证方式:密码/手机号登录真实 `build_router` 链路分别验证 AGC route event 的 marker、真实用户归属和 outbox 落盘;资产读取主体保留、响应 extension 与匿名不附加单测;tracking identity 单测、`cargo check --locked -p api-server`、相关 `cargo test --locked -p api-server`、格式、编码和 diff 检查通过。 - 关联文档:`docs/technical/【后端架构】Issue225登录成功AGC用户归属修复方案-2026-09-03.md`、Issue #225、`server-rs/crates/api-server/src/tracking.rs`、`server-rs/crates/api-server/src/app.rs`、`server-rs/crates/api-server/src/assets.rs`。 ## 2026-09-03 server-rs workspace 保留独立 platform-agent 排除边界 - 背景:主站 #251 合并清理旧玩法表后,`server-rs/Cargo.toml` 的旧 crate 排除清单被收窄;`platform-agent` 仍位于 `server-rs/crates/` 下,但实际由 AGC 独立 Cargo workspace 通过路径依赖使用。若不显式排除,主 workspace 的 `cargo fmt --all` 会把它识别为“位于 workspace 内但不是 member”的非法包并直接失败。 - 决策:继续将 `crates/platform-agent` 放在 `server-rs` workspace 的 `exclude` 中。它不加入主 workspace,也不在其 manifest 中新增平行 `[workspace]`;AGC 的独立 Cargo manifest 继续负责该 crate 的构建边界。 - 影响范围:`server-rs/Cargo.toml` 与仓库 Rust 格式检查;不改变 `platform-agent` 源码、AGC 依赖关系或主站运行时。 - 验证方式:`npm run check:rustfmt` 通过;`cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check` 不再报告 `platform-agent` workspace 错误。 - 关联材料:Repository checks #5755、主站合并提交 `025f62729`、`server-rs/Cargo.toml`、AGC `apps/ai-game-creator-shell/src-tauri/Cargo.toml`。 ## 2026-09-03 Native shell 检查清单只维护现役 HostBridge 文件 - 背景:#251 退役并删除了 H5 个人中心 QR 扫码弹层及其测试,同时移除了不再有源码消费者的生命周期 / 网络 wrapper;`scripts/check-native-shells.mjs` 和根 Vitest include 仍保留旧路径,导致 Native shell tests 在最后的 H5 HostBridge 调用链扫描阶段失败。 - 决策:Native shell 静态检查、H5 HostBridge 定向测试和根 Vitest include 只列出现役文件;已删除的 QR 扫码组件/测试及生命周期、网络 wrapper 从清单移除,不恢复已退役实现,也不为历史路径增加兼容占位文件。 - 影响范围:`scripts/check-native-shells.mjs`、`vitest.config.ts` 和 Native shell 检查门禁;不改变移动壳现役 `QrScannerOverlay` 或 HostBridge 公共契约。 - 验证方式:运行 `npm run check:native-shells`,确认 H5 HostBridge 调用链静态扫描不再引用已删除文件;同时运行编码、格式和 diff 检查。 - 关联材料:Native shell tests #5758、主站合并提交 `025f62729`、已删除的 `PlatformProfileQrScannerModal` 文件。 --- ## 2026-09-02 Direct 过程卡按回合阶段状态驱动 - 背景:DirectProject 结果卡把工具活动词、中间文本和真实回复增量都当成“实时回复”,标题随最近一次事件跳动;上游常整包返回正文时还叠加合成打字机,用户看到的是行为名而非当前阶段。 - 决策:Direct 过程卡顶部标题只由 `GameCreatorDirectTurnUpdateStatus` 决定(accepted=需求已接收 / running=任务执行中 / streaming=回复生成中 / finalizing=结果整理中 / completed=回复已生成 / failed=处理失败),小字只展示当前正在执行的具体内容并统一加“正在”前缀;真实回复增量(AccumulatedText)才标记 streaming,计划、推理、工具输出与 Activity 一律 running。生成中的累计回复直接作为 assistant 消息气泡在会话列表中原位更新,不再拼进过程卡;进入 finalizing / completed 时保留完整累计回复直到正式消息接管,失败时清除未完成正文。移除合成打字机回放;工具说明/中间文本不再触发 streaming。计划/推理通知收敛为 `preparing` 活动并在界面显示“正在思考中”,原始推理/计划正文不进入 UI,思考期的心跳按 1.2s 限流。命令/文件/工具执行细节与回复流解耦,`stream=false` 时仍展示在过程卡;MCP 工具按用户语义显示(例如 `agc_write_file` 为“正在写入文件:<项目相对路径>”、图片/素材/搜索/试玩分别显示生成、导入、搜索、试玩等动作),未知工具只显示“正在调用工具”不暴露内部工具名;命令显示“正在执行命令:<命令>”,验证类命令显示“正在验证游戏:<命令>”。同一活动后续无正文的心跳不得用通用文案覆盖已展示的具体工作。展开/收起是同一 `project + clientTurnId` 内的持久状态,内容更新不重置,切换新回合才收起;展开详情的滚动条轨道和角落保持透明。 - 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs` 的 DirectProject observer、`apps/ai-game-creator-shell/src/App.tsx` 的事件投影、`ProjectSupervisorView` 过程卡渲染与对应 AppSurface 回归。 - 验证方式:Rust 单测证明只有开启流式时的 AccumulatedText 是 streaming、preparing 通知只产生 thinking 活动词且不携带原始推理文本、执行细节在 `stream=false` 时仍保留,并覆盖全部 AGC MCP 工具语义、未知工具不泄漏、绝对路径 / 上跳路径不展示;AppSurface 覆盖接受态、preparing 显示“正在思考中”、running 长文本展开、command-exec 与写文件心跳不覆盖具体工作、streaming 正文进入 assistant 气泡且过程卡只显示阶段、同一回合后续 running 不覆盖正文也不收起、失败后清除未完成正文、正式消息接管不重复;样式核对确认展开详情的滚动条轨道与角落透明;AGC typecheck、全量 appSurface、rustfmt、`npm run check:encoding`、`git diff --check` 通过。 - 关联文档:`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`、分支 `feat/agc-llm-router-official-chain`。 --- ## 2026-09-02 GDD 审批卡的后台 hydrate 不抢占已加载决定 - 背景:项目页首次加载和运行态刷新可能并发 hydrate。卡片已经显示后,短暂的 `hydrateBusy` 会让已打开的评论弹层提交按钮瞬时变灰,用户无法提交已输入的修改意见。 - 决策:`hydrateBusy` 只控制恢复区的重试按钮;已加载审批卡的决定按钮和评论弹层继续依据 `canDecide` 与 `decisionBusy` 门控。只有权威状态显式返回 `recoveryPending=true` 时才禁止决定,并保留弹层中的输入内容。 - 影响范围:AGC GDD 审批卡前端、Fast GDD 审批交互文档;不改变 hydrate command、审批 DTO 或后端状态机。 - 验证方式:运行评论弹层恢复竞态回归、完整 `appSurface.test.ts`,并执行类型、编码和 diff 检查。 - 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`、`apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx`。 ## 2026-09-02 旧玩法表采用两阶段退役清理 - 背景:旧创作模板的业务代码已退出现役编译链,但 SpacetimeDB 中的历史表仍需先完成数据清理;直接删除表定义会扩大 schema 迁移和客户端兼容风险。 - 决策:阶段一只在 `spacetime-module/src/migration.rs` 增加受 `database_migration_operator` 保护的 `clear_retired_database_tables` procedure。procedure 使用固定的 63 张旧玩法表清单,不接受动态表名;`dry_run=true` 只返回逐表行数统计,`dry_run=false` 在同一事务内逐表清空,任一失败整体回滚。阶段一不删除表定义、不修改 `legacy_schema/**`、migration 导入导出白名单或生成 bindings。 - 阶段边界:清理清单包含旧 gameplay、`custom_world`、Puzzle / Puzzle Clear、Bark Battle、Match3D、Jump Hop、Wooden Fish、Square Hole、Visual Novel 和 Big Fish 表;`runtime_setting`、`runtime_snapshot`、`user_browse_history`、`creation_entry_config` 等现役表明确排除。阶段二只有在备份、客户端兼容性和运行态确认完成后,才评估从 module 定义与 migration 白名单移除空表,并按 schema / bindings 流程发布;固定清单旁保留 TODO。 - 影响范围:SpacetimeDB migration procedure、`spacetime-client` 生成 bindings、后端数据契约和本决策记录;禁止新增 SQL `DROP TABLE`、`--delete-data=always` 或直接写系统表的实现。 - 验证方式:固定清单测试确认数量为 63 且不含现役表;本地数据库以已授权 operator 执行 dry-run,确认 63 张表均返回 0 行且未写入;apply 的单事务回滚由 procedure 实现,实际 apply 仅在另行授权的维护窗口执行。另运行 bindings 生成、SpacetimeDB schema / runtime 检查、编码和 diff 门禁。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`server-rs/crates/spacetime-module/src/migration.rs`。 - 补充落地:阶段一同时删除此前仅因退役而保留的旧玩法业务实现、未挂载 API handler/router/worker、旧客户端 facade/mapper、旧领域 crate、`retired/legacy-creation-templates/**` 归档及 `packages/shared/src/contracts/**` 中已无仓库内消费者的旧玩法公共契约;`legacy_schema/**`、生成表 bindings、现役 shared contracts 和持久化 schema 不在本次删除范围。 ## 2026-08-31 DirectProject 客户端扩展按独立 Skill/MCP 导入 - 背景:DirectProject 需要使用用户在 AGC 客户端导入的市面原生 Skill、MCP 和 Plugin 内容,但第三方内容不应直接安装到运行时 Codex,也不应要求用户转换为 AGC 自定义格式。 - 决策:客户端提供一个全局“扩展”入口,统一接受文件、目录、zip 和标准 Plugin;目录、zip、Plugin 只是导入来源,发现出的每个 Skill 和每个 MCP Server 分别成为独立扩展项,分别列表、重命名、启用、禁用和删除。已识别项导入后默认启用,下次 DirectProject Codex 启动时按原生 Skill root 和 MCP 配置注入。 - 命名:客户端列表名称与 Codex 运行时名称使用同一个原生标识,不维护 display/runtime 两套名称;重复或同名项保留为新的独立项并自动追加 `-2`、`-3`。Skill 重命名只修改客户端运行时副本中的有效名称,原始导入内容不修改。 - Plugin 边界:Plugin 只作为导入容器提取 Skill/MCP;当前 DirectProject 关闭的 hooks、apps、remote plugin 和完整 Plugin Runtime 不接入。单个可执行文件或脚本不提供手动指定为 MCP 入口的功能。 - 信任边界:不审核第三方 Skill 文案、脚本、二进制、MCP tool 或网络行为;导入阶段不执行内容。客户端只做标准结构识别、必要配置解析和 zip staging 路径边界处理,且不向第三方扩展注入 AGC 凭据或内部路径。 - 影响范围:AGC 客户端扩展设置 UI、客户端本地扩展存储、DirectProject Codex app-server 启动准备和 pool fingerprint;不新增 HTTP 服务、SpacetimeDB schema、公开 API 或独立 Plugin Runtime。 - 当前实现:客户端导入/list、Skill 临时 root 和 MCP 隔离配置注入均已落地。第三方 MCP 只从客户端已启用独立项生成本次隔离 `CODEX_HOME/config.toml`,每项固定非 required;配置错误或 app-server 启动状态失败只更新对应 `last_error`,内置 `agc_tools` 继续由客户端单独注入。客户端已启用 Skill/MCP 的名称、来源路径和内容指纹共同参与 DirectProject app-server pool identity。 - 验证方式:分三阶段验收:先验证导入拆分和完整列表,再验证 Skill 运行时发现和重命名,最后验证 MCP 配置合并、Plugin 提取和失败隔离;只增加对应的定向测试、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【技术方案】DirectProject客户端Skill与MCP扩展导入方案-2026-08-31.md`、`apps/ai-game-creator-shell/src/features/runtime-config/RuntimeConfigDialog.tsx`、`apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs`。 ## 2026-08-30 批准 GDD 直接进入做游戏链路 - 背景:立项策划 GDD 批准后需要给用户一个进入做游戏的自然出口,产品决策改为点击按钮后直接开始建造。 - 决策:批准态 GDD 交付行提供“做成游戏”按钮。点击后读取当前项目的权威 `game/fast_gdd.md`,直接创建自动游戏工作区、导入 `text/markdown` 参考附件,并以固定建造指令自动启动 Direct Codex;不再回首页等待用户二次提交。该动作不复制原项目的 `approvedGddRef`、planning sidecar 或 approval receipt。 - 影响范围:AGC 前端 GDD 交付行与现有自动建项/附件导入/Direct Codex 链路;移除首页 RichInputArea 的 GDD 一次性预填链路;不新增 HTTP API、SpacetimeDB schema、迁移、OpenAPI 或正式构建绑定。 - 验证方式:批准态按钮直接创建工作区、导入附件、携带固定首条指令进入项目工作台且重复点击不重复创建的 appSurface 回归;类型检查、编码检查和 `git diff --check` 通过。 - 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`。 --- ## 2026-08-31 Direct 回合把 Codex item 落成有界行为账本 - 背景:sidecar 已让模型看见本轮附件路径,但 native 读 / MCP / 写文件只存在于隔离 `CODEX_HOME` 的瞬时 stdout,回合结束即删。无法判断「没读附件」还是「读了仍走默认收集类」。 - 决策:GUI DirectProject 每个 `clientTurnId` 追加 `.agent/runtime/direct-codex/turns/.jsonl`,并在 `agent.db` 写一条 `direct.codex.turn` 摘要。记 sidecar 提供的路径与文件 hash、`item/completed` 的 Read/List/Search/MCP/写文件(不含 stdout、patch、MCP result),以及 `offeredRead` / `firstDesign`。审计 fail-open,不阻断做游戏。Home、CLI、Supervisor 收据模型不接。不灌附件正文,不强制读取,不为 GDD 开特例。 - 影响范围:`direct_codex_audit.rs`、Direct GUI command 边界、Codex collect 循环;前端 / jsonl 气泡 / sidecar 文案不变。 - 验证方式:Rust fixture 覆盖 turn_start hash、绝对路径相对化、stdout/diff 不落盘、art brief 保留、list/search 不算已读、firstDesign 顺序、256 条截断、写盘失败不 panic;sidecar 渲染与 Direct 活动词测试保持通过。 - 关联文档:`docs/technical/【技术方案】Direct回合行为审计账本-2026-08-31.md`、issue #212。 ## 2026-08-31 Direct 本轮附件只映射路径,不灌正文、不区别 GDD - 背景:issue #212。首页附件已经复制到 `assets/uploads/` 并登记,但 Direct 首轮只把用户原文发给 Codex,原文件名不是磁盘路径,模型会另起一套玩法。 - 决策:Home 与 Project 共用 `DirectCodexTurnAttachment`。有项目路径或导入状态时,只在发给 Codex 的 user prompt 末尾附有界 sidecar(原名 → 项目相对路径、类型、大小、状态);无路径且无状态时保持首页元数据文案。不灌正文、不强制读取、不按 GDD 开特例。做成游戏固定 prompt 不改,同一条 Direct 首轮附件链自动吃到 sidecar。jsonl 与工作台气泡仍只写用户原文。 - 影响范围:`direct_codex_attachments.rs`、Direct command 边界、首页建项 latch、工作台首轮 invoke;Supervisor / 做方案首轮忽略附件 sidecar。 - 验证方式:Rust 渲染测试(Home 逐字兼容、Project 映射、非法路径);home.suite 附件 Direct invoke 含 `localPath`;无附件不出现 `attachments` 键;做方案首轮仍走 Supervisor 且无 sidecar;后续手打消息不带 attachments。 - 关联文档:`docs/technical/【技术方案】DirectProject本轮附件路径映射-2026-08-31.md`、issue #212。 ## 2026-08-26 运行中自主扩图提案留在编排层 - 背景:`agent-runtime-orchestration` 已能构造和调度动态 DAG,但 LLM 在执行中发现缺少步骤时没有通用的安全扩图合同。 - 决策:新增严格 serde 的 `GraphProposal`(`TaskProposal` + `GraphEdge`)和 `GraphLimits`,由 `TaskGraph::apply_proposal` / `expand_with_proposal` 在内存中构造不可变候选图;新节点默认 `Pending`,边方向为前置 `from` → 依赖方 `to`。 - 安全与一致性:所有 Agent、端点、重复引用、环、节点/边/深度/扇出预算在候选返回前一次校验;边只能指向新节点,禁止给已运行任务原地追加依赖。任一失败保留旧图。成功后的 epoch、基图版本、proposal 幂等和持久化由宿主负责,crate 不调用 LLM/Provider/ToolHost/Runner,也不写 `.agent/runtime/**`。 - 验证:非游戏 conformance 覆盖有效扩图、ready/wave 重算、未知 Agent/端点、重复边、已有任务修改、环、预算、严格 JSON 和原子失败;关联文档为 `docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` V1.55。 ## 2026-08-26 通用多 Agent DAG 编排与执行内核分层 - 背景:`agent-runtime-core` 已承接 catalog、run/action 生命周期、lane、宿主 ToolHost、spawn/all-join 和 Provider 契约,但动态任务图的 ready 选择、依赖波次与返工下游闭包仍混在 `platform-agent::game_creation`,其它产品无法复用且非法环会被合并成伪 wave。 - 决策:新增纯 Rust `agent-runtime-orchestration`,依赖方向固定为 `agent-runtime-orchestration -> agent-runtime-core`。公共层只持有任务 ID、Agent ID、通用状态和依赖边,统一负责构图校验、ready、active/satisfied 波次、下游闭包和全量/返工选择;动态构图仍必须是 DAG,跨轮循环通过新的 pass / epoch 表达。 - 产品边界:16 个游戏任务、六组角色、产物/验收条件、Evaluator Markdown 和中文语义路由继续留在 `platform-agent`;AGC 组合根使用公共层校验任务图与 `AgentCatalog`。Runtime store、Runner、Provider、权限、ToolHost、委派 journal、isolated write scope 和 `.agent/runtime/**` 不迁移、不双写。 - 验证方式:非游戏 conformance 覆盖并行分支、汇合、repair closure、AgentCatalog 和非法图失败关闭;`platform-agent` 锁定种子 DAG 与现役波次/返工顺序,并验证环拒绝和 catalog 注入。根检查脚本必须执行新 crate 测试。 - 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md` V1.54。 ## 2026-08-27 `plan.submit_gdd` 拒绝无审批决定的 `user_revision` - 背景:结构校验允许 `round=0 + user_revision + confirmed`,提交闸原先只做结构、身份和 Session CAS。Provider 可在首次 collecting、澄清续跑或提交前质量返工里把未确认项标成用户审批修改,审批卡显示「已确认」。 - 决策:新版本 create 时,payload 含 `user_revision` 则当前 session 的 `lastDecisionRef.action` 必须是 `revise` 或 `reject`;否则 `PLAN_INVALID_REQUEST`。同 `submissionId` replay 不重判。不恢复 session 前缀逐项相等,不把 `user_revision` 与审批意见正文对齐,也不在这次处理 `round≥1` 的 `user_option` 伪造。 - 影响范围:`planning_submit.rs` 提交闸;Fast GDD 技术方案第 5.1 / 8.2 / 12 节。 - 验证方式:首次 collecting 带 invented-confirmation 必须拒绝且不落 GDD;reject continuation 再交 `user_revision` 的 v2 仍成功。 - 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`。 ## 2026-08-28 planning continuation 必须沿当前 delivery 游标推进 - 背景:`lastDecisionRef.action=revise/reject` 在用户修订后的质量返工中必须继续有效,但仅凭该历史指针无法证明当前 `agent.delegate` 选择的是本次 planning session 的当前分支。 - 决策:不新增用户修订授权字段,也不在 `plan.submit_gdd` 重复遍历 approval receipt/GDD lineage。已有 planning session 创建新 child 时,`repairOfDelegationId` 必须直接等于旧 session 的 `latestDelegationId`;不一致即在 Provider 启动前以 `PLAN_NEEDS_RECONCILIATION` 拒绝。合法用户修订及其后质量返工继续保留 `lastDecisionRef`,成功提交新的 GDD 后仍由 submit successor 清理该指针。 - 影响范围:`planning_coordinator.rs` continuation 投影门;Fast GDD 技术方案第 8.2 节和提交步骤;不改变静态委派通用返工合同或 `PlanSessionV1` schema。 - 验证方式:新增当前游标 continuation 正向/旧 delivery 负向回归;CI 继续验证首次伪造 `user_revision` 拒绝、用户修订后质量返工提交成功及现有澄清/返工 lineage。 - 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_coordinator.rs`。 ## 2026-08-27 退款 emergency spool 容量溢出保持可恢复 ## 2026-08-27 退款 emergency spool 容量溢出保持可恢复 - 背景:本机 emergency spool 仅作为 SpacetimeDB 完全不可达时的最后恢复路径,原有 `MAX_BYTES` 分支会直接返回 `Dropped`,导致扣费已经完成但没有可重放记录。 - 决策:达到普通 outbox `MAX_BYTES` 时,将退款记录写入同一持久目录的 `refund-overflow-*` 文件;该文件与普通 pending 文件一样由启动恢复和后台 worker 重放到 SpacetimeDB,且按 refund ledger id 保持幂等。溢出文件不计入普通阈值,但必须触发容量告警;底层磁盘写入失败仍进入关键退款人工补偿流程。 - 影响范围:api-server wallet refund emergency spool、资产失败退款日志、loadtest / 预览 Compose 持久卷、后端架构与开发运维文档。 - 验证方式:运行 api-server `wallet_refund_outbox` 定向测试,确认超限写入并保留 overflow 文件;运行 SpacetimeDB profile 测试、Compose 配置校验、编码和 diff 门禁。 ## 2026-08-27 短期认证状态进入共享 typed projection - 背景:短信验证码和微信 OAuth state 仍只存在 API 进程内 HashMap,多节点请求或 API 重启会直接丢失,无法满足无粘性会话的鉴权恢复要求。 - 决策:`AuthStoreProjectionView` 增加 `phone_codes` 与 `wechat_states` typed 字段,由 `auth_store_projection_meta` 以 JSON 投影持久化;启动恢复、CAS 同步和失败后的权威刷新都覆盖这两类短期状态。验证码哈希使用部署级稳定盐(当前复用 `GENARRATIVE_JWT_SECRET`),各 API 节点必须一致;发码前先刷新权威投影并用占位验证码记录做一次 projection CAS,只有占用成功才调用短信 provider,避免跨节点冷却竞态;认证 handler 在发码、消费验证码、创建/消费微信 state 后都要完成 projection sync,失败即返回服务错误;所有会读取或变更本机认证工作集的认证主链路(登录、刷新、`/me`、会话管理、密码、绑定和微信 state)在领域操作前先从正式投影做一次受 CAS 保护的只读刷新,受保护 Bearer 中间件也会在进入业务 handler 前执行同样的刷新,刷新失败时 fail closed,不能依赖粘性会话;同步遇到 CAS 冲突时,若本次尝试期间没有新的本地变更则恢复正式快照,若仍有待同步 revision 则由后续认证请求重试,避免节点永久卡在 pending。微信 OAuth state 设置有界活动数量,避免单个 JSON 投影无界膨胀。短期状态仍由 `module-auth` 内存工作集执行领域校验,但不再把本机 HashMap 当作持久化或跨节点真相。 - 影响范围:`module-auth` projection、`spacetime-module` auth schema/procedure、`spacetime-client` bindings/facade、api-server 手机号 / 微信 handler、认证架构与运维文档。 - 验证方式:运行 module-auth projection roundtrip(验证码可跨恢复校验、微信 state 可跨恢复消费)、SpacetimeDB schema/runtime/DDD 门禁、api-server 定向测试、编码和 diff 检查。 --- ## 2026-08-27 外部生成历史采用受控保留清理 - 背景:`external_generation_job`、`external_generation_job_summary` 与 `external_generation_job_event` 都是持久化表;摘要和 payload 边界收紧后,已确认的终态历史仍会继续占用 SpacetimeDB 常驻内存,且事件审计链会随任务数量增长。 - 决策:新增仅 migration operator 可调用的 `prune_external_generation_job_history_and_return`。默认按 `source_module=editor-canvas`、30 天保留期和 `job_id` 游标分批运行;只删除主任务与摘要状态一致、属于 completed / failed / cancelled、摘要已有 `notification_acknowledged_at` 且终态时间达到 cutoff 的任务。事件、摘要和主任务仍按同一事务顺序删除,但每次事务最多删除 256 条事件;事件未删完时保留任务与摘要并返回同一个 job cursor,维护脚本下一次继续,避免单个任务形成无界事务写集。默认 dry-run,必须固定 dry-run 返回的 cutoff 后再 apply;pending / running、未确认通知、摘要缺失或状态不一致的数据永不删除。其他 source module 必须显式指定并单独评估;资产对象和钱包流水不随任务历史删除;不新增自动定时器或 runtime 清理权限。 - 影响范围:`server-rs/crates/spacetime-module/src/external_generation.rs`、外部生成事件 job_id 单列索引、SpacetimeDB 生成 bindings、`scripts/spacetime-maintain-external-generation-jobs.mjs`、架构与生产运维文档。 - 验证方式:覆盖终态 / 活跃态 / 已确认与未确认摘要、状态或身份不一致、cutoff 边界测试;运行 SpacetimeDB module tests/check、bindings 生成、schema/encoding/diff 门禁,并在维护窗口先 dry-run 再 apply。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、PR #203。 ## 2026-08-27 SpacetimeDB 工具链统一升级到 2.8.3 - 背景:SpacetimeDB 2.8.0 引入 TypeScript submodule 与调度延迟观测,2.8.1 修复 v1 WebSocket 订阅移除死锁、TypeScript SDK `array` 读缓存别名和 Rust string 默认值支持,2.8.2 修复 table accessor 改名自动迁移,2.8.3 修复 scheduled function 从实际执行时间重排导致的长期漂移。仓库若继续锁定 2.7.0,会保留这些已知运行时与 SDK 问题。 - 决策:`server-rs/Cargo.toml` 的 `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 精确锁定 2.8.3;本地 CLI / standalone、Rust bindings、worker smoke 本地镜像、官方容器压测镜像和生产 provision 下载根同步对齐 `v2.8.3`,CLI / standalone commit 门禁为 `8e410d28...`。2.8.3 不再使用 2.7.0 的 hotfix3 特殊资产标签口径,但同版本 commit 校验继续保留。 - 影响范围:Rust workspace lockfile、SpacetimeDB bindings、本地 dev 版本门禁、容器 smoke / loadtest、server provision Jenkins 与项目 SpacetimeDB skills / 文档;现役 module 未使用 submodule,本次不修改 schema 或 migration。 - 验证方式:核对 CLI 版本和 commit,重新生成 Rust bindings,运行 `npm run check:spacetime-schema`、相关 Cargo check / tests、server provision 工具测试、dev 调度测试、encoding 和 diff 门禁。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 ## 2026-08-26 Fast GDD 修订后先取证再允许再次委派 - **现象**:GDD v1 经用户选择“修改”后,策划子 Agent 正确提交 v2,但 plan 根 Supervisor 的 `Delegated` 阶段仍同时广告 `agent.delegate` 与审批前置工具;模型可能在 Acceptance Graph 重新取证前重复创建修订 delivery,随后被 `PLAN_PROVIDER_USAGE_DEFERRED` 拦停。 - **决策**:plan 根阶段增加轻量的 `AwaitingAcceptanceEvidence` 状态。当前根最新 GDD 无 approval receipt/pending、session `latestSubmittedRef` 精确指向该提交、delivery 已由根认领且 Acceptance Graph 返回 `NeedsEvidence` 时,只广告 `file.read`、`agent.acceptance_update`、`agent.run_status`;只有用户真正对最新审批卡选择修改/退回后,才恢复 `agent.delegate`。 - **边界**:不放宽 Provider usage 门禁,不重构 delegation/repair lineage,不自动生成证据或审批 pending;审批 pending 仍只由既有 acceptance gate 在 `agent.acceptance_update` 成功后创建。 - **验证**:新增一条阶段工具面回归,并通过 15 条 M1C-2a acceptance gate 定向测试、plan root 原生工具目录测试、`cargo check --all-targets`、格式与 diff 检查。 - **锁边界修正(2026-08-27)**:阶段判定拆为 `plan_root_supervisor_stage_at_locked` 与负责取得一次项目锁的外层入口;Provider tool-plan builder 已持有项目锁时直接复用 locked 入口。Acceptance Evidence 判据和阶段工具面不变,禁止在持锁调用链中再次获取 `.agent/project.lock`。 - **回归验证**:planning submit 定向测试 68 passed、Provider request builder 定向测试 17 passed、Tauri `cargo check` 与 `cargo fmt --check` 通过。 ## 2026-08-24 AGC Direct 媒体能力只通过客户端语义工具开放 - 背景:资源页已经补齐视频、角色动画、音效和背景音乐的 create/derive 能力,但 Direct Codex 只能准备标准美术包,无法查询已登记源资源或表达新增媒体意图。直接开放 Tauri invoke 会把项目路径、revision、operation、幂等键、登录态和事务权力交给模型。 - 决策:只新增 `agc_list_registered_assets` 与 `agc_create_or_derive_resource` 两个语义工具。Codex 只能提交资源过滤条件或 kind/mode/localAssetId/prompt/name;客户端权威解析 manifest 来源,生成并恢复稳定 operation/idempotency,串行付费调用,执行权限、项目锁、画布/素材目录准备、下载校验和 manifest CAS。完全匹配的 pending 请求自动恢复,不创建替代付费请求。 - 输出边界:资源查询和生成结果只投影相对路径、稳定 Canvas/resource/asset/task 身份、序列帧身份、pending 状态及脱敏告警;不返回完整 manifest、prompt、model、provider route、绝对路径、URL、Token、Cookie 或 API Key。角色动画及视频/音频新请求统一携带同名画布与素材目录上下文;已有冻结请求不迁移、不重写。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`、`apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-client-projection/SKILL.md`。 ## 2026-08-23 External 去背景绑定权威静态来源与真实画布尺寸 - 背景:External v1 去背景曾把调用方 `assetKind` 原样带入持久化,并允许 `sourceImageSrc=A + targetLayerId=B` 覆盖不同资源;Python helper 又为所有画布完成请求固定生成 `1024×1024` 占位,导致非方形透明结果按占位尺寸拉伸。 - 决策:入队前从当前 owner 的项目资源或素材库解析来源权威语义类型,资产对象存储类型只参与非静态媒体门禁;显式项目资源 ID / 素材 ID 优先于 objectKey 回退,同一纯 objectKey 对应的候选权威元数据不一致时返回 `400` 并要求用 `sourceResourceId` 或业务 ID 消歧,禁止按列表首条决定类型。请求类型冲突或任一记录属于视频、音频、动画、图片序列时返回 `400`,队列只保存服务端解析出的静态语义类型。无 `canvasCompletion` 的原位替换优先比较双方 `assetObjectId`,任一缺失时回退 canonical `(bucket, objectKey)`,并要求默认类型一致;纯 objectKey 省略 `sourceResourceId` 时自动绑定目标图层资源并写入队列,由 Worker 复验同一绑定。helper 使用画布会话时必须取得真实源宽高或显式 `canvasWidth + canvasHeight`,不再猜测方形尺寸。 - 影响范围:External v1 去背景入队与 worker 复验、OpenAPI、Python helper、外部编辑器 skill 和相关契约测试;不修改 SpacetimeDB schema、BgFilter 协议或去背景输出尺寸语义。 - 验证方式:覆盖非静态类型与权威类型冲突、来源/目标不同对象拒绝及同对象通过、非方形 helper completion;运行 api-server 定向测试、helper self-test、OpenAPI 解析、编码与 diff 门禁。 ## 2026-08-20 UI Editor LLM 递归输出与参考图单文件限制 - 背景:结构识别、界面语义建议和多图合并直接把 LLM 工具 arguments 反序列化为递归树;结构识别与语义建议还在 async command 中同步读取并 base64 编码参考图。模型异常输出或过大图片可能造成不受控内存、栈和 async worker 占用。 - 决策:三个工具调用的 arguments 统一限制为 `1 MiB`,先解析通用 JSON 并迭代检查,再进入递归业务类型。结构识别按每棵树独立限制 `512` 个 LLM 节点 / `32` 层,不跨树求和且不计 Rust 页面根;语义建议限制 `4` 节点 / `4` 层;合并计划限制 `512` 节点 / `32` 层。超限整次拒绝,不截断或交付部分结果,日志不记录 arguments 正文。 - 输入边界:`merge_ui` 继续直接接收 `State`,不修改 Tauri/frontend IPC 参数;进入 Rust 后、发起 LLM 前按每棵源树独立限制 `512` 节点 / `32` 层,不跨树求和,并限制 `2 MiB` 序列化投影。UI 设计参考图只设单张 `5 MiB` 上限,不设批次合计或像素数上限;元数据检查、有限读取和 base64 编码进入 blocking worker,不新增命令超时。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 ## 2026-08-19 M1 审查后四项可靠性修复 - **审批 stale 收口**:`status` 或 `phase` 为 `needs-reconciliation` 的策划根不再满足审批所需的 active 身份;审批命令返回 `PLAN_STALE_APPROVAL`,并且不得创建 approval receipt。 - **rollout capability**:AppData 配置增加 `planning.capabilityEnabled`,默认 `true`。关闭后拒绝新的 plan 根 run、`plan.submit_gdd`、approval pending 和 decision mutation;hydrate 仅返回已有 sidecar 的只读视图,不执行恢复写入。 - **profile/source 边界**:durable run-profile binding 集中拒绝 `project-supervisor-plan + autonomous-game-build`;自主构建的 scheduler 与 completion consumer 使用不含 plan 的精确 source matcher,普通 plan `standard` 仍属于通用 trusted matcher。 - **边界**:不新增既有项目首次进入策划入口;不改做游戏、做素材路径。回归覆盖 capability、stale receipt、错项目 sidecar 写前失败、非法 binding 不落盘与新项目 ID 形状。 ## 2026-08-19 更正 M1D-2 首页入口映射 - **更正**:`bf2185fba` 把“做游戏”误接为 `standard + project-supervisor-plan`,并额外增加“直接开建”按钮;这与既有产品决定“做方案入口独立成链,不动做游戏路径”冲突。 - **当前入口合同**:仅首页“做方案”新建项目以 `standard + project-supervisor-plan` 进入立项策划;“做游戏”和“做素材”保持 `autonomous-game-build` 直接开建。目录提交与 Enter 自动创建使用同一映射。 - **边界**:项目页新建、打开既有项目和 Godot 导入不新增策划入口;已有 planning sidecar 或 active plan lineage 只恢复其原有链路。 - **回归**:覆盖做游戏目录提交、做方案目录提交、做方案 Enter 自动创建,以及既有做素材 Enter 自动创建,分别断言首个 Supervisor run 的 profile/source。 ## 2026-08-18 M1D 收口:design 组展示名改完 - **背景更正(先于结论)**:此前把这条的严重度建立在「新项目默认主路径上『立项策划』与『策划 Agent』同屏共存」上,**该说法未经验证且不成立**。用 appSurface harness 实测策划路径:`子 Agent 状态栏` 根本不渲染,`策划 Agent` 出现 0 次、`立项策划` 出现 2 次。结构上二者确在 `launcherView === 'project-development'` 分支的同一棵树里(`ProjectDevelopmentView` 的 dock + 作为 `supervisor` 传入的 `ProjectSupervisorView`),但未能把用例驱动到该分支,故不作为事实主张。若真会撞,也是**批准后进入完整制作**那一段(此时 `state=approved`,阶段进度卡守卫仍放行),比原描述窄得多。 - **仍然要改的理由**:与撞不撞名无关。`bf2185fba` 只把 `taskGroupLabels.design` 改成「设计实现组」,另两本同概念字典没动,于是同一个 design 组在开发者面板/文本汇总里叫「设计实现组」、在工作台状态栏里叫「策划 Agent」——**这个语义不一致是该提交引进来的**。第 18.2 节要求本就是「design 组用户名称改为设计实现组」,与新阶段区分只是其动机之一。改完比回退便宜(回退还需挑拣 `c6a08ef98` 里混着的按钮改名与 prettier 重排,并反改第 18.2 节)。 - **口径选择**:采最小方案,保持兄弟项的「X Agent」体系(美术 Agent / 程序 Agent / …),design 取「设计实现 Agent」。**不**把 `taskGroupLabels` 直接塞进 `groupConfigs`——两本字典命名体系不同(「X组」对「X Agent」,且音乐组/音频 Agent、运营组/发布 Agent 连词都不一样),直接替换会连带改掉另外五个分组名。消灭重复字典属视觉改版,单独立项。 - **改动**:`agentPresentation.ts` 的 `groupConfigs`、`view/project-development/index.tsx` 的 `summarizeAgent` 默认名与同文件分组头像字(`策`→`设`)、`model.ts` 中 `agentId.includes('design')` 的个体名兜底(`design-director` 走这里;`design-foundation` 的「玩法策划 Agent」单列在前,不变)。内部 agent id 与 `design` 分组键均未动。 - **测试**:原估「3 处断言」严重低估。实跑发现 `project-development.suite.ts` 有 16 处派生断言需跟改——「X 文本回执」由 `App.tsx` 的 `${candidate.label} 文本回执` 拼出、「历史成果 · X」由 `resourceProjectionModel.ts` 拼出、dock 的 article accessible name 亦然。替换时用后行否定守住 `玩法策划 Agent`(它含 `策划 Agent` 子串),替换前后该串恒为 6 处。`agentRuntimeModel.test.ts` 与 `projectResourceProjectionModel.test.ts` 里剩余 4 处是测试自造的输入 fixture、不由字典派生,保持不动。 - **验证**:`appSurface.test.ts` **383 passed / 0 failed**;`agentRuntimeModel` / `agentTraceSummary` / `projectResourceProjectionModel` / `projectResourceLiveUpdateModel` 合计 39 passed;`agc:typecheck`、ESLint `--max-warnings 0`、`check:encoding`、`git diff --check` 通过。 ## 2026-08-18 M1D 审查修复补充:锁错误脱敏与澄清轮次口径 - **锁错误回传绝对路径**:`acquire_project_write_lock` 的 Err 内嵌 `.agent/project.lock` 真实绝对路径,违反第 18.3 节「返回值不包含绝对路径……或内部诊断」。补 `redact_agent_runtime_project_paths` 的三处是前端审批卡真正会显示的那条链:`reconcile_plan_gdd_approval_projections_at`(hydrate 在取自己的锁之前调它)、hydrate 自己的锁、`decide_plan_gdd_at`(其错误与 hydrate 的错误渲染在同一个错误区)。planning 另有 14 个取锁点沿用未脱敏写法,属 M1B/M1C 既有模式,本次不扩面。脱敏不破坏 `项目正在被其他写操作占用:` 前缀,`project_gates.rs` / `provider_recovery.rs` 两处按前缀分类的判据不受影响。 - **澄清轮次差一格**:`clarificationRound` 与 `awaitingAnswerFor.round` 都由 `static_delegate_lineage_counters` 派生,该函数排除目标自身,是 0-indexed 的「已答轮数」;后端判上限用的是 `current_round + 1`。阶段进度原样渲染成「轮次 X/3」整体差一格,问最后一轮时显示「轮次 2/3」,字面暗示还剩一轮。**只改前端文案,不动 DTO 语义**:等待回答时显示「第 N+1 轮 / 共 3 轮」(此时 `latestDelegationId` 就是当前 delivery,+1 恰好等于后端校验用的轮次),其余状态退回「已完成 N/3 轮澄清」,不猜当前轮。 - **顺带**:`planning_hydrate.rs` 里 `reconcile` 的错误原本用同一 code 把 `to_string()` 当 detail 重包一层,而 `PlanningStorageError` 的 Display 已是 `"{code}: {detail}"`,渲染出 `CODE: CODE: detail`;code 与 detail 均无变化,改为直接 `?` 传播,并把「不重复拼 code」钉进回归。 - **测试陷阱(值得记)**:写「占住项目锁」的 fixture 时必须给锁 JSON 填**真实** `createdAt`。失效锁回收的年龄判定读的是该 JSON 字段而**不是**文件 mtime(`project_write_lock_age_seconds`),填 0 会让锁显得约 1.7e9 秒老、越过 600 秒阈值被当场回收删除,hydrate 反而成功。第一版 fixture 正是这样自证失败的。 - **验证**:Rust `planning_` 组 **155 passed / 0 failed**(原 154 + 本次 1 条);`appSurface.test.ts` **383 passed / 0 failed**(378 原有 + 5 条新增);三条新回归均经变异验证,逆转对应修复即变红。`cargo fmt --check`、`agc:typecheck`、ESLint `--max-warnings 0`、`check:encoding`、`git diff --check` 通过。 - **撤回一条此前的审查发现**:曾判定 hydrate 读 manifest 缺符号链接判定(因其走裸 `root.join` 而非 `resolve_local_project_path`)。复核后**不成立**:`read_manifest` 自身在 `metadata.file_type().is_symlink()` 处即拒(`manifest.rs`),防护在另一层;`.agent` 目录本身为符号链接的残差也无窗口,紧随其后的 `resolve_planning_path` 同样逐段判定。未据此改动代码。 - **仍未修**:① hydrate 在校验 GDD/session 的 projectId 与 manifest 一致之前已执行落盘投影修复,违反第 18.3 节固定顺序。**已裁决为不修**:它唯一有后果的前提是 projectId 变成每项目唯一,而该常量方案不属本工作包管辖;单独为一个不受控的假设改动权威读取路径不划算。触发后的实际后果也已复核为可忽略——命令仍正确返回 `PLAN_PROJECT_ID_MISMATCH`,写入的 pending/session/index 均为幂等或可重建投影,`session.previous.json` 是改名而非删除且只在 primary 缺失时发生。裁决与改动前提(含「不能把 `reconcile` 直接挪到 hydrate 取锁之后」这个非重入锁陷阱)已作为注释写在 `planning_hydrate.rs` 调用点旁,使提醒与会坏掉的代码同处,而不是只留在本文档里;② design 组展示名仍有 `agentPresentation.ts` 的 `groupConfigs` 与 `view/project-development/index.tsx` 的 `summarizeAgent` 两处硬编码「策划 Agent」,注意该两处与 `taskGroupLabels` **命名体系不同**(「X Agent」对「X组」),直接替换会连带改掉另外五个分组名,需先定命名口径。 - **新记一条既有问题(非 M1D 引入)**:`seedManifest.projectId` 是常量 `local-project-draft`,App 的 5 个 init/import 调用点全传它,因此**本机所有项目 projectId 相同**。第 18.3 节第 1 步依赖的「manifest 与 projectId 校验」因此分辨不出任意两个项目——把 A 项目的 `.agent/planning/**` 整体拷入 B 项目仍会通过。该门当前近乎恒真,须单独立项处置。 ## 2026-08-18 M1D 审查修复:GDD 审批决定失败路径与恢复期弹层门控 - **审查范围与基线**:对 `14c00017c..bf2185fba` 的 M1D-1/M1D-2 全量改动做规格对照审查(技术方案第 13、18 节)。审查完成后分支又前进了 `4624fd795`(文档同步)与 `c6a08ef98`(前端文案回归断言修复)两条,二者都不改 `src/**` 生产代码,审查结论不受影响。 - **修复一:决定失败也必须重灌权威状态**。`decidePlanGdd` 原来只在成功分支 hydrate,`catch` 只写错误后 rethrow。后端 `decide_plan_gdd_at` 有多条真实 `PLAN_STALE_APPROVAL` 分支(GDD 已不在当前 lineage、identity 不符、版本被更新版本取代、pending 丢失或不一致),命中后卡片停在已失效的 pending 身份上、三个决定按钮仍可点,且 `recoveryPending` 永不翻真导致「重试恢复」入口不渲染,卡内没有任何恢复路径。现在失败分支同样 hydrate,落实第 18.3 节「approval decision 返回后调用 hydrate」(该句不区分成功与失败)。**两句顺序已被回归钉死**:`hydratePlanGddState` 入口会 `setPlanGddError(null)`,必须先 hydrate 再写决定错误,写反会把这条错误擦掉。 - **修复二:responseId 复用键纳入 comment**。原键为 `approvalRequestId:action`,不含 comment,违反第 13.2 节「用户改变 action/comment 后必须生成新 responseId」。在第 14 节恢复矩阵承认的「receipt 已提交但 command response 丢失」构造下,用户改写修改意见后重提会带着旧 responseId,命中后端「同 responseId 的审批意图不一致」硬拒,改写后的原因永远落不了盘。现在键挂在 `approvalRequestId` 上并比对 `{action, comment}` 完整意图。**判据方向为宁可多换不可少换**:receipt 已存在时多换的最坏后果是 `replayed` 降级成 `already-decided`(两者都是 Ok,且 already-decided 正是第 18.2 节要求的刷新态),少换则是硬错误。 - **修复三:`recoveryPending` 必须挡住已经打开的评论弹层**。第 18.2 节要求恢复期只允许重试同一 ID、不允许提交决定;原实现只把 `canDecide` 接到三个触发按钮上,而弹层是打开之后才可能被后台 hydrate 翻掉决定资格的,其「提交决定」按钮只看 `busy || !comment.trim()`,仍可提交。现在 `submitComment` 与该按钮都判 `canDecide`,并在弹层内说明原因。**刻意不自动关弹层**,否则会丢掉用户已经写好的修改意见。 - **测试**:appSurface harness 新增 `hydrate_game_creator_plan_gdd_state` / `decide_game_creator_plan_gdd` 两个分发分支与 `createPlanGddStateView` fixture;未配置策划状态时 hydrate 与接入前一样抛出,既有用例行为不变。新增 `tests/appSurface/plan-gdd.suite.ts` 三条回归,并逐条做过变异验证——把对应修复单独逆转后三条各自以自己的断言变红(修复二的变异是**部分逆转**:保留新 Map 结构、只删掉 comment 比对,因此该用例钉住的是 comment 这一维本身而非那次重构)。 - **验证**:`appSurface.test.ts` **381 passed / 0 failed**(378 既有 + 3 新增);`agentTraceSummary` 与 `rememberCommand`(另两个 import `src/App` 的用例文件)13 passed;`agc:typecheck` 通过;6 个改动/新增文件 ESLint `--max-warnings 0` 通过;`check:encoding` 5409 文件通过。不改 Rust——三条全在前端,后端语义已经正确。 - **对既有记录的更正**:M1D-1 与 M1D-2 两条记录分别称「Shell TypeScript typecheck 仍被仓库既有依赖缺失阻断」「appSurface UI suite 受仓库现有缺失 Tauri plugin 依赖阻断,未把该基线失败归因于本包」,在原分支主工作树上都不成立:`agc:typecheck` 干净退出,appSurface 378 条全绿;两道门分别位于 CI 的 `check:native-shells`(且 typecheck 排在 cargo test 之前)与 Frontend tests 内,一直是活的。实际情况与记录相反——`bf2185fba` 改名 `taskGroupLabels.design` 后,appSurface 有 8 个用例文件的断言变红,随后由 `c6a08ef98` 修复;把该套件记为「基线阻断、不归因本包」正是让这条自带回归合入的原因。**隔离工作树的依赖缺失不能作为跳过门禁的依据,须回原工作树复跑后再下结论。** - **未修的审查发现(本次不并入,单列后续)**:① hydrate 在校验 GDD/session 的 projectId 与 manifest 一致之前,已执行 `reconcile_plan_gdd_approval_projections_at`、session previous 提升与 index 重建等落盘修复,违反第 18.3 节固定顺序,其中 `session.previous.json` 的提升+删除不可逆(触发需外部篡改 `.agent/`,App 自身流程造不出该分歧);② 项目写锁竞争时 `acquire_project_write_lock` 的错误原文内嵌项目绝对路径,被原样回传前端,违反第 18.3 节「返回值不包含绝对路径」,常态可达;③ design 组展示名只改了 `taskGroupLabels` 一本字典,`agentPresentation.ts` 的 `groupConfigs` 与 `view/project-development/index.tsx` 的 `summarizeAgent` 仍硬编码「策划 Agent」,与新阶段「立项策划」同屏共存,违反第 18.2 节;④ 阶段进度「轮次 X/3」直接透传 0-indexed 的 `clarificationRound` 未 +1(后端自己用的是 `current_round + 1`),最后一轮显示「轮次 2/3」,字面暗示还剩一轮。 ## 2026-08-18 M1E 隔离工作树:Fast GDD submit 有界拒绝与覆盖审计 - **范围与结论**:在 `codex/genarrative-isolated` 上按 M1E 只接受具备完整触发链的缺陷。确认 Provider 连续输出不合法 `plan.submit_gdd` 时,Runtime 原有「rejected observation → 同 child run 续跑」链没有次数上限,模型可反复请求 tool-plan 并累积历史 observation;这是可达的 prompt 膨胀与资源消耗链。 - **有界收束**:为 Runtime state 新增 durable `planSubmitGddRejectionCount`。仅本次 Provider input / 候选 GDD 触发的 `PLAN_INVALID_REQUEST`、`PLAN_SIZE_LIMIT` 计数;前四次维持既有 batch abort、observation 与 same-run continuation,第五次仍先完整落 rejected observation,再将该 planning child 终态失败并记录专用失败 audit,不发起第六次 Provider tool-plan。字段以 serde default 向后兼容,进程重启不会重置;新 child run 才从零开始,普通工具 observation 不计入。 - **自审修复**:`PLAN_SIZE_LIMIT` 也可能来自读取既有不可变 GDD/receipt,而非 Provider 输入;`PLAN_VERSION_LIMIT_REACHED` 则由既有 lineage 已达 128 版或其读取异常决定。若只按 error code 分类,会把 durable authority 异常误当可纠正模型输出,最多多跑五次。现将这些读取/版本边界转为 `PLAN_NEEDS_RECONCILIATION`;Provider input/候选 GDD 的大小限制仍可按上项重试,不扩大改变其它 authority 错误语义。 - **覆盖审计**:第 21 节要求的澄清三轮/回答绑定/continuation、submit→receipt 的 replay 与投影恢复、审批后修订、hydrate 空态与恢复均已有真实 Runtime/存储回归。未发现能低成本构成完整新断链的前端或跨层缺陷,因而未为拼接既有单测新增大而脆的 E2E。 - **自审收口**:复核第五次 rejected observation 已持久化、但终态失败写入前进程中断的窗口:原先恢复会再次进入 Provider loop,形成第六次请求。恢复入口现读取 durable counter,达到 5 时直接复用同一终态失败与 audit 收束,不依赖内存 continuation;该修复与原有计数边界完全同域。未发现其它具备完整触发链、且可在 M1E 或此前范围内明确修复的问题;不扩展至 M2 的 `approvedGddRef` 或完整构建绑定。 - **已验证**:新增“第五次终止”“第五次后恢复仍终止”“超限既有 GDD 转 reconciliation”及“lineage 版本上限不作 Provider feedback”四条回归;随后 Rust `planning_` 定向组 **157 passed / 0 failed**,`cargo check --offline --all-targets`、`cargo fmt --check`、`npm run check:encoding`(6608 files)和 `git diff --check` 均通过。仓库既有 Rust warnings 未在本包扩修。M1E 完成。 ## 2026-08-18 M1D-2 隔离工作树实现:入口分流与阶段进度 - **隔离范围**:在 `codex/genarrative-isolated`、基线 `5b11a0530` 上开工;只接入口分流、阶段进度和实际项目总控页面的现有审批卡挂载,不接 M2 `approvedGddRef` 构建绑定、完整构建按钮或 M1E 端到端故障注入。 - **入口合同(已被 2026-08-19 更正)**:本条原将游戏新项目默认接为 `standard + project-supervisor-plan`,现改为仅首页“做方案”进入该路径;做游戏/做素材保持既有直接构建。项目打开/项目页新建不注入 start mode,保持老项目行为。 - **页面接线**:`ProjectSupervisorView` 现在复用 M1D-1 的 hydrate、GDD 审批卡和阶段进度;阶段进度只消费 `plan-gdd-state-view.v1`,显示 `轮次 x/3`、当前版本与状态徽章。`project-planning` 显示为“立项策划 Agent”,设计组展示名改为“设计实现组”。 - **Runtime 路由**:项目总控聊天提交在规划入口或已读到 `project-supervisor-plan` 时继续使用 `standard + project-supervisor-plan`;规划 Run 尚未终态时不另起通用聊天 Run,要求先完成当前策划步骤。直接开建及非规划入口保留原提交 profile/source。 - **自审边界与验证**:只接受有完整触发链路的问题;本包不改变 Runtime source 门禁、审批命令、构建准入或老项目 direct-build 语义。改动文件 ESLint、Shell TypeScript 两套 typecheck、`agentRuntimeModel.test.ts`(29 passed)、编码检查与 `git diff --check` 通过;appSurface UI suite 受仓库现有缺失 Tauri plugin 依赖阻断,未把该基线失败归因于本包。自审未发现测试失败或修复边界明确的 M1D-2 之外缺陷;M2 approved-GDD 构建绑定与 M1E 跨层故障注入保留。 - **合入状态**:M1D-2 以 `bf2185fba`(`完成M1D-2入口分流与阶段进度`)fast-forward 合入 `feat/five_min_design`;自审中发现 `WorkspaceLauncher` 漏解构/传递新增的 `createHomeDraftDirectBuild`,会使 Shell typecheck 直接失败,已在同一提交修复。 ## 2026-08-18 M1D-1 隔离工作树实现:hydrate/read model 与 GDD 审批卡 - **隔离基线与范围**:在 `codex/genarrative-isolated`、基线 `14c00017c` 上开工;只实现 M1D-1 的前端 hydrate/read model 与 GDD 审批卡,不接 M1D-2 入口分流、完整构建按钮、构建准入或 M1E 下游链路。 - **Runtime hydrate**:新增 `hydrate_game_creator_plan_gdd_state` 与 `plan-gdd-state-view.v1`。command 通过严格 `deny_unknown_fields` 的 `{projectPath}` JSON 输入并经过项目权限校验;Rust 只读取 canonical GDD/receipt/session/pending/index authority,空 planning 项目返回 `not_started` 且不创建目录,页面不扫描 sidecar/Markdown/index。 - **审批卡链路**:工作台在项目打开、Runtime 状态推进、窗口恢复时 hydrate;待审 GDD 由 hydrate 提供,审批决定调用既有 `decide_game_creator_plan_gdd`,按 `(approvalRequestId, action)` 复用 `gdd-response-`,决定返回后再次 hydrate。卡片提供 approve/revise/reject,后两者必须填写原因;正文通过独立详情弹层展示,不在卡片下无限堆叠。 - **恢复与安全边界**:`recoveryPending` 时卡片只显示恢复重试,禁止决定;审批 pending 只有验收门已落下的 `gdd-approval` sidecar 才能成为可操作事实,不能把 `awaiting_gdd_approval` session 误当验收通过。未审批 GDD 缺失或错绑 session successor 时返回 `PLAN_SESSION_RECOVERY_REQUIRED`,pending/receipt 身份不一致 fail-closed;hydrate 响应使用序列号丢弃过期并发结果。 - **必要回归与验证**:保留一条必要 Rust 回归,验证已初始化但无 planning 目录 hydrate 返回完整空 view 且不创建存储;该测试通过。`cargo check --offline --all-targets --target-dir target-m1d1`、`cargo fmt --check`、`git diff --check` 通过。前端 TypeScript 复用原工作树依赖 junction 做检查,新增代码无类型错误;仓库现有缺少 `@tauri-apps/api/event`、`@tauri-apps/plugin-http`、`@tauri-apps/plugin-clipboard-manager`、`@tauri-apps/plugin-opener` 依赖的问题仍保留。 - **自审保留项与合入状态**:真实验收门等待期间 pending 尚未建立时不显示可操作审批卡;M1D-1 不新增完整跨层故障注入或构建链测试,留给 M1E。所有检查以完整触发链路为准,未发现需要扩大到 M1D-2/M1E 的问题。实现已由 `0052a80da` 合入 `feat/five_min_design`,其后 ESLint 修正为 `5b11a0530`。 ## 2026-08-18 `M1C-2c` 隔离工作树开工:A/B 决策卡语义与信封合同收口 - **隔离基线**:在 `codex/genarrative-isolated` 上从 `0199fb6e4` 开工;`M1C-2b` 已合回 `feat/five_min_design`,本包不回改其三轮上限、continuation 幂等、答案绑定或预算折叠。 - **本包范围**:把 planning 决策卡从“同一推荐的三种采纳程度”改为 A/B 平行方案 + 固定“需要原型验证”;Runtime 按 label 形状校验并确定性映射 `state` / `answerSource` / `answerSummary`,同步更新 planning role brief、final-reply 收束提示与定向回归。 - **冻结映射**:A、B → `confirmed / user_option`;固定第三项 → `prototype_pending / user_option`;自由填写 → `confirmed / user_freeform`;`default_pending / default / round=0` 只允许由未提问默认项产生。`answerSummary` 必须逐字等于用户选中的 label 或自由填写原文。 - **信封合同**:每张 planning 卡必须恰好三项;第一项 label 以 `A` + `·`/`:`/`-` 开头,第二项同形以 `B` 开头,第三项逐字为“需要原型验证”;形状不符 fail-closed,不建立 pending,不改变 session。 - **提示词纪律**:B 必须是真实、形状不同且说明代价的平行路线;第三项 description 要给出可执行的 30~90 分钟微型原型验证;平台事实和 MVP 已排除项不提问;改口保留用户原文并标注被哪一轮推翻。 - **纠正旧记录**:此前 M1C-2b 条目把“第四轮”写成正常进入 reconciliation;代码核查确认正常路径在 `agent.delegate` 边界硬拒并返回 failed observation,`planning_coordinator` 的超三轮 reconciliation 仅是损坏血缘的纵深防御,后续文档收口时一并更正。 - **当前进度**:Runtime 映射、role brief、Supervisor playbook/final-reply 提示、A/B/自由填写/非法信封回归已落地;`planning_clarification_*` **13 passed / 0 failed**,`project_planning` prompt 定向回归 **5 passed / 0 failed**,`planning_submit` 定向回归通过,prompt bundle、`cargo fmt --check`、离线 `cargo check --offline --all-targets --target-dir target-m1c2c`、`npm run check:encoding`(5406 files)及 `git diff --check` 均通过。实现已由提交 `6e4bd9703` 合回 `feat/five_min_design`。 ## 2026-08-17 M1C-2b 隔离工作树实现完成:策划澄清中转、链路派生与预算注入 - **当前基线**:`M1C-1`、`M1C-2a` 已合回 `feat/five_min_design`;本隔离分支开工后又以 merge commit `9f12d8467` 合入原分支截至 `a8215a599` 的全部已提交改动,包含 P4 的 Fast GDD 识别顺序修复与 P5 的委派栅栏 detail 等价性锁定。原工作树未提交的 `planning_approval.rs` 不属于该合并且未触碰。M1C-2a 的固定 Goal Contract、验收图与审批前置门作为既有前置,不在本包回改。 - **本包范围**:只把已发布的 `AGC_NEEDS_USER_INPUT_V1` 子 Agent → Supervisor 中转链接入 Fast GDD 的 planning session:回答以 `(requestId, answersSha256)` 原子绑定原 delivery 后,严格派生唯一 continuation identity、更新 `appliedAnswers` / session phase / active run 投影,并让 planning 子 Agent 的后续 Provider 请求获得当前澄清轮次与累计活跃时间的受控上下文。 - **编码前裁决**:现役 answer sidecar 只有题目、原始回答及 transport hash,不能提供 session schema 对 `decisionsSummary` / `prototypeValidationItems` 的必需字段;若等 continuation Provider 补字段,该 Provider 又会先被 `appliedAnswers.length != clarification_round` 拒绝。故冻结 Runtime 的确定性派生:从固定问题前缀提取 topic,按三个固定选项/自由填写映射 state、answerSource、answerSummary;“需要原型验证”同步生成固定四字段 30~90 分钟微型原型项。派生失败或已有同 ID 内容不一致均失败关闭,不请求 Provider 猜测。 - **开工验证计划**:先以现有澄清中转回归为基础,补 planning 专属的三轮边界、答案冲突、重复 wake/continuation 幂等、session 链与预算注入断言;随后运行对应 Rust 定向测试、格式/编码/diff 门禁。实现和验证结论在本条持续补充。 - **当前实现**:新增 planning coordinator,在首个 `project-planning` child 落 revision 1 initial-request session;`NeedsUserInput` delivery 投影为 `awaiting_user_input`;回答绑定且 continuation child durable 后,在同一项目写锁内严格派生 `appliedAnswers`、`decisionsSummary`、`prototypeValidationItems`、`collecting + activeRunId`。固定三选项及自由填写均按技术方案映射,问题必须是单题、`第N轮·关键决定`、固定三选项且 N 为 1~3;第四轮在建立 Supervisor pending 前失败关闭。 - **恢复与锁序**:本条只约束 **M1C-2b 新增的 planning 澄清写投影路径**,不把结论扩大到整个 Agent Runtime。Supervisor 直接回答先以只读候选判别是否为 planning 澄清,再按 `project write lock → execution lock` 重取并在双锁内重读 pending;`answer-prepared` 恢复先释放旧 execution lock,再按同一顺序重取,期间旧候选若已被并发回答、替换或清理,只按 obsolete candidate 让路,不把合法前滚误标为 reconciliation;planning parent-wake 先把父 run 持久化为 `waiting-for-delegate-receipts`,待主循环返回并释放 execution lane 后,再在 lane 外按 `project → execution` 创建唯一澄清 pending,lane 忙时只 deferred、重放不增加 session revision 或 action。恢复仍在 planning child 进入 Provider 路径前补 session 投影;已精确投影的 retry/provider handoff 只恢复冻结请求,不因 usage fold 的 `Deferred` 误进 reconciliation。**边界说明**:`main_loop.rs` 既有通用 completion blocker 仍存在 execution lane 内调用 project-lock wrapper 的路径,它不是 M1C-2b 新增逻辑,也不在本包重构范围;因此本包不得表述为“项目写锁始终先于全部 Session lane / execution lock”。 - **预算事实**:Provider 真实 future 的 completed / failed / interrupted 活跃区间以 requestId create-only fact 写入 Agent DB;同 ID 内容冲突失败关闭。下一次新的 planning request 在项目锁内、重建 request 前折叠合法 facts 到 `accumulatedAgentMillis`,因此冻结 binding 不会在 Provider 返回处漂移;项目锁、请求构造、用户/审批等待、retry backoff、handoff、工具执行与停机时间均不计入。fold 发现当前 run 仍有 ready / executing lifecycle 时返回 `Deferred`,不擅自改写 session。末次 `plan.submit_gdd` 的 usage fact 会被 v4 submit batch 暂时挡住;receipt 已完成 session 投影且精确消费 standalone/v4 anchors 后,同一项目锁内再 fold,确保直接 approve 而无下一次 planning request 时该区间也计入 session;任何 deferred/identity/I/O 异常只留 `recoveryPending`,不强写。 - **本轮已修的明确缺陷**:plan 回答读取曾把 opaque `taskId` 误与 Supervisor `agentId` 比较,会令第一轮 continuation 必然失败。现改为读取同一 parent run 的 Supervisor root task,并精确核对 taskId、agentId、sessionId、runId、source、requestId、questionsSha256 与 answersSha256;回答 sidecar 的共用 payload 校验保持完整,不降低普通 user-input 的身份校验。 - **终审修复**:完成至少一轮澄清后,审批 `revise/reject` 会保留 `appliedAnswers`,但新修订 delivery 的身份不再等于最后回答 continuation;旧纯 session 判据会把合法修订 successor 固定拒成 `PLAN_IDENTITY_CONFLICT`。现把独立 schema 校验收窄为“不得回退到已消费问题 delivery”,并在 session 新值、已有 primary/previous、发布后回读及普通读取边界读取真实 static-delivery 谱系:从 latest 回到最后回答 continuation 的**每一条边**都必须由父 delivery 的 `UserRevisionRequested` 状态授权,且 root/agent/session 身份一致、无 Unknown、缺节点或循环;质量返工边不得借路径中其它用户修订继续保留旧回答。正向回归同时覆盖 `revise/reject` 后 continuation、轮次/回答/决定保留与 Provider 注入;负向回归证明混入质量返工边时,即使重算合法 session fingerprint 仍失败关闭。 - **门禁中修复的测试缺陷**:并发整组首次复跑时,锁序测试把“回答线程开始”误当成“已得到调度”,180ms 内未观察到 project lock 竞争而失败;同用例精确复跑通过。测试只将调度观察窗口放宽到 2 秒,断言仍要求真实 project lock 竞争发生后才释放被占用的 execution lane,未改变生产锁序或放松结果判据。 - **纠正旧观察**:第四轮澄清委派在 `agent.delegate` 工具边界就按持久化血缘轮次上限硬拒,返回普通 failed observation,正常路径不会进入 reconciliation;`planning_coordinator` 的 `clarification_round > 3` 分支仅是损坏/篡改血缘的纵深防御。Supervisor 可据失败 observation 改走 `plan.submit_gdd`,不需要在 M1C-2b 另加自动 submit 状态机。 - **最终门禁证据**:`planning_clarification_*` **11 passed / 0 failed**(原 9 条之外新增真实 main-loop 释放 execution lane 后 parent-wake 回归,以及已回答后 `revise/reject` 修订回归);`tests::collaboration::static_deliveries::*` **44 passed**;planning storage **13 passed**(含重新计算 fingerprint 的混合质量返工谱系负例);`planning_submit` **53 passed**;`planning_provider_usage` **4 passed**;真实末次 submit usage receipt 回归 **1 passed**;`barrier_detail_*` **3 passed**。`cargo fmt --check`、`cargo check --offline --all-targets --target-dir target-m1c2b`、`npm run check:encoding`(7810 files)及整个工作树 `git diff --check` 均通过。**M1C-2b 本包实现及门禁已完成,并已快进合回 `feat/five_min_design`;审批 UI、hydrate、构建准入和下游完整构建仍后置。** ## 2026-08-17 立项策划决策卡改为 A/B 平行方案:`default_pending` 收回为未提问默认项唯一来源,改口不改合同,立包 `M1C-2c` - **裁决**:决策卡三选项从「接受推荐 / 暂按推荐 / 需要原型验证」(同一条推荐的三种采纳程度)改为「方案 A(推荐)/ 方案 B(真实可行、形状不同的平行备选)/ 固定『需要原型验证』」,仍恒定三项,第 3 项的 description 须给出这一题可执行的验证方式。A、B 均记 `confirmed / user_option`;`需要原型验证` 仍记 `prototype_pending` 并要求同 ID 微型原型项;自由填写仍记 `confirmed / user_freeform`。`default_pending` 不再由任何选项产生,只表示**未提问、由子 Agent 按默认建议填写**的字段(`answerSource=default, round=0`),与 `plan.submit_gdd` 校验「前缀之后只允许追加 `default_pending` 默认决定」完全一致——三个决定状态各只有一个来源。状态映射按 label(A/B 前缀 + 第 3 项固定文案)而非位置,Runtime 校验信封形状;`answerSummary` 直接落所选 label,台账自描述。 - **依据**:以 DeepSeek v4-flash 做的本地原型多轮实测(原型不入库、只用于开发调试):选 1 与选 2 产出的 GDD 一字不差,差别只是一个不进任何机制的标签,却消耗一轮问询名额(上限 3);台账只落「接受推荐」三个字,用户下一轮改口推翻上一轮已确认决定时,Supervisor 转述与子 Agent 出稿只能靠改写文本消化。改为 A/B 后两轮冒烟:B 均为带独立代价说明的真实岔路;模型一次擅自把第 3 项换成自定义方案 C,被信封形状校验拒回并自行改正。(首版曾允许第 3 项按问题性质省略,产品拍板改回恒定三项。) - **改口(已确认决定被后续自由填写推翻)**:M1 内不改合同——台账前缀不可变,两条 `confirmed` 并存;Supervisor 转述时必须在被推翻的那条后注明「已被第 N 轮回答推翻,以后者为准」且用户答案原文逐字保留(不得改写、拆分或搬轮次),子 Agent 按后者出稿并在新决定 topic 中写明推翻关系。实测三次改口 Supervisor 均能消化,但一次靠改写用户原文(转述保真审计报「内容缺失」),因此该规则必须写进 Supervisor prompt。`supersedes` 字段进 `plan-gdd.v1` 列为 M2 候选。 - **提问纪律补一条**:平台事实已定的事(含移动/桌面优先级)与 MVP 规则已排除的事(多人/联机/商城/服务器)不作为问题;A/B 格式会诱使模型问"天然二选一"但无价值的问题,实测一轮 3 张卡 2 张如此。 - **与 `M1C-2b` 的关系**:拟定本条时 `M1C-2b` 尚在隔离工作树,其合入门禁(3 轮上限、continuation 重放幂等、答案绑定冲突被拒)与选项文案无关,故按当时的 §5.2 映射表实现,并已于同日快进合回(见上一条)——它把「单题、`第N轮·关键决定`、固定三选项」的校验与「三个固定选项/自由填写 → state、answerSource、answerSummary」的确定性派生冻结在 planning coordinator 里。映射翻转、信封形状校验与 prompt 文案由新立的 **`M1C-2c`**(依赖 `M1C-2b`,现在即可开工)承接。**`M1D-1` 前端决策卡直接按新语义实现**(label 动态渲染、默认焦点 A、Other 槽不变),避免做两遍。§5.2 正文、§5.1 prompt 段与 §23.6「仍冻结:固定选项」一句由 `M1C-2c` 一并改写;本轮只在 §5.2 顶部加了指向注、新增 §23.9 与 §23.8 表 `M1C-2c` 行。 - **顺带核对项(归 `M1E`)**:`plan.submit_gdd` 连续校验失败必须有次数上限。本地实测无界时模型对大载荷序列化出错后连续 40 余次重试、每次重放全部历史、单次 prompt 涨到 15 万 token;240/300 秒预算注入兜不住「硬超时后仍连续校验失败」。若 §12 retry 状态机没有该上限,补一个(原型取 5 次)。 - **不改的部分**:`user.input_request` strict input 2~3 项区间、Other 槽 placeholder、`prototype_pending` 同 ID 验证项规则、第 12 节提交校验、§23.7 用户修订不计 `repair_depth`(`M1C-0`/`M1C-1` 已落地)均不变;生产代码本轮零改动。 - **关联**:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` 第 5.2 节指向注、第 23.8 节 `M1C-2c` / `M1D-1` 行、第 23.9 节。 ## 2026-08-15 M1C-2a 隔离工作树实现:固定 Goal Contract 与审批前置门 - **本轮范围**:只实现 Supervisor 根 run 的 Goal Contract / Acceptance Graph 与 Fast GDD 审批前置门;不接 `M1C-2b` 澄清中转、审批 UI、构建准入或下游完整构建。当前变更仍在隔离 worktree,尚未合回原分支。 - **固定合同**:`project-supervisor-plan / standard` 的首轮与格式修复请求都必须且只能调用一次 `agent.goal_contract`。按项目变化的字段只有 `outcome / nonNegotiables / forbiddenAssumptions / openQuestions`;`preferences` 固定空数组,`acceptanceNodes` 固定为唯一 `fast-gdd-serves-intent` 节点,required evidence 固定 `tool:file.read`。其它 source 保持动态合同,即使使用同名 criterionId 也不套 Fast GDD 特例。 - **证据合同**:Fast GDD evidence 只接受当前 Supervisor 根 run 自己读取 `game/fast_gdd.md` 的成功 action receipt。receipt 保存规范化路径、完整内容 SHA-256 与行覆盖摘要;多页必须从 `startLine=1` 无缺口、无重叠覆盖到 EOF,全部页同 hash/同总行数,`agent.acceptance_update.evidence` 必须列出所有分页 actionId。普通 Graph 读取只验 durable receipt 形状与完整覆盖;当前 Markdown hash 只在审批前且无 exact pending/receipt 时复核,审批后的状态投影不会让旧 Graph 损坏。 - **取证与返工三态**:planning delivery 先由同一根 run 的 `agent.run_status` durable 认领。Graph 缺失、not-observed、project revision/hash 过期或证据不完整属于 `NeedsEvidence`,下一步是 `file.read`,不得提前返工;只有当前完整证据支撑的显式 failed 属 `RepairRequired`,才返回原 delivery 的 `repairOfDelegationId`;passed 才幂等创建 `gdd-approval` pending。`run_status` 在持有项目锁时调用 locked gate,并在 Ready 数为零时仍重放,封住旧 action 已认领但 gate 尚未落盘的崩溃窗口;GDD create 到 child/delivery 完成前保持惰性,不抢断 M1B-2 恢复。 - **恢复与完成门**:acceptance update、delivery claim、Runner recovery、completion 与 finalization 都消费同一 gate。恢复不重新执行 Provider 或 `file.read` 动作,只复核 durable Graph、动作回执与当前 Markdown hash;missing pending 按原 approvalRequestId/identity 补建,exact pending 与 receipt 优先于 Graph/hash 复核,冲突则失败关闭。当前根没有自己的 GDD 时,上一根遗留 Markdown/Graph 不能绕过完成门。 ## 2026-08-15 M1C-1 隔离工作树收口:审批核心与专用完成门已落地,生产前置门保持后置 - **本轮落地**:在 `planning_storage.rs` 增加 `plan-gdd-approval.v1` receipt、`plan-gdd-approval-pending.v1` projection、comment/decision/receipt/pending 的 typed fingerprint 与 strict canonical 校验;审批 observation 固定校验 tool/status、版本摘要、detail 前缀和规范化 comment。`planning_approval.rs` 增加 receipt create-only、三动作幂等(`committed / replayed / already-decided`)、版本/指纹竞态防护、receipt 后 index/Markdown/audit/terminal observation/session 投影与恢复,以及只读的 plan 根专用 completion blocker;`commands.rs` 暴露 `decide_game_creator_plan_gdd`。 - **恢复边界**:generic `plan.submit_gdd` 的 v5 standalone pending 与 v4 batch 仍是独立恢复锚点。receipt 投影只在 exact planning-submit batch(v4、单 action、cursor 已到 1、completed、observation 与 receipt 逐字相等)时清理残留;pending 已缺失但 terminal observation 存在时仍校验/清理该 batch,形状不 exact 则保持 `recoveryPending`,不猜测删除。无 receipt 的 GDD 不由本包自行重建审批 pending。 - **明确后置**:技术方案第 13.0 节要求的 acceptance-gate 取证成功后才创建生产 `gdd-approval` pending;当前创建 helper 仅由定向测试调用,真实 submit/recovery caller 留给 `M1C-2a` 的验收前置门接线。审批 UI、验收图接线和构建准入仍未完成,不能把当前隔离 WIP 宣称为完整产品交付。 - **验证**:专用 target `target-m1c1-current` 下 `cargo test --all-targets planning_submit --no-fail-fast`(37 passed,含 completion blocker 正向/阻塞/作用域/identity 回归)、`cargo test --all-targets planning_storage --no-fail-fast`(11 passed)、`cargo check --all-targets` 通过;`npm run check:encoding`(7848 files)和 `git diff --check` 通过。编译仍有仓库既有 warnings,不作为本包缺陷。 - **关联**:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` 第 13、14、23.6、23.8 节;本条只记录隔离工作树状态,合回原分支前仍需按既定合并流程复核。 ## 2026-08-15 CI 五条失败归并为三个根因:修位置,不修症状 栈溢出修复后的全量跑出 5 条失败(`2051 passed / 5 failed`,**零栈溢出**,栈修复站住)。逐条定位后归并为 3 个根因,**全部先于本轮工作**:与栈修复、`M1C-0b`、以及刚合入的 master 都无关,master(`9f5c84ee7`)自身全绿,`deae1e08c`(栈修复前、`M1C-0b` 前)已全挂。三者形状相同——**新增的检查被放到了链路更靠前的位置,改变的是作用域而非严格程度**,详见 pitfalls 同日条。 - **归属**:`tool_plan_handoff_rejects_out_of_order_entries` + 两条 `tests::goal`(`provider_action_batch_goal_resume_never_rewinds_newer_steer_cursor`、`agent_goal_paused_edit_replans_old_confirmation_in_same_run`)→ `M1B-2`(`27c3eb847`)的 `validate_next_entry` 判据上提;`response_stream::structured_plan_finalization_without_readable_runtime_state_needs_reconciliation` → 同一提交新增的 `missing_plan_submit_anchor_candidate_at` 强读;`immutable_writer_rejects_symlink_and_hardlink_targets` → `M1B-1`(`f453c2ca2`)起就没绿过的错误码分类,Windows 上编译都不参与故一直不可见。 - **证据不是推测**:失败用例遗留的临时项目目录里,`.agent/agent.db` 记着 `failureKind=tool-plan-integrity`、`errorChars=55`、`errorSha256=0750f609…`,与 `M1B-2` 新增那句「`tool-plan 成功响应交接 entry 的 Provider/session binding 链身份冲突`」的 SHA-256 与字符数逐位相同;`requestSlot` 由 `loop-1-repair-0` 走到 `loop-2-repair-0`,直接指认是跨 loop 那一跳被误判。 - **裁决一:撤回上提,而不是放宽判据本身。** `same_tool_plan_repair_chain` 含 steer cursor、goal revision/快照与 planning session binding 这些本轮量,进入新 loop 本就意味着它们前进;这条判据只在同 loop 的 repair 之间成立。**撤回不留缺口**:跨 loop 的 durable 身份由 `same_durable_tool_plan_run` 守,binding 漂移由 `provider_retry.rs` 的 `..._drift_fields`(含 `planningSessionBinding`)在**每个请求**层面守,`ledger.rs` 的 `is_later_repair_identity` 一直就把这条判据限定在同 loop——上提是模块内唯一的例外。**未采纳**「保留跨 loop 检查但只比 durable 子集」:`gdd_id` 在策划子 run 内会从 `None` 变 `Some`、`goal_id` 亦非绝对不变,凭空发明一条无测试支撑的新不变量,对一个管 Provider 计费与身份的安全屏障不划算。上提本身也**没有任何测试**(`M1B-2` 在该文件只机械补了一行 `planning_session_binding: None`)。 - **裁决二:探测器不得对自己的前置条件 fail-fast。** `missing_plan_submit_anchor_candidate_at` 是机会性修复,不是门。state 读不出来就不可能匹配它要找的形状,改为 `Ok(None)`;不可读 state 的处置权归下游 `resume_game_creator_agent_finalization_at`(从 task record 重建并 fail-closed 到 `needs-reconciliation`)。**不算掩盖**:紧随其后的那一步照样会读同一个文件并留下 `reason=runtime-state-missing` 的记录。**未采纳**「把探测器挪到 finalization 之后」——那会让 `Recovered`/`Blocked` 分支的 `continue` 直接跳过探测,改动的是语义而不是位置。 - **裁决三:链接一律按不可信路径分类,且模块内统一。** 新增 `resolve_planning_path`:先用模块自己的 `planning_metadata_is_link_or_reparse` 逐组件判链接/重解析点(比通用解析器的 `is_symlink()` 多覆盖 Windows reparse point),命中返回 `PLAN_UNTRUSTED_PATH`,其余仍交通用解析器并保持 `PLAN_INVALID_PATH`。模块内 13 处解析全部改走它。**未采纳**「改测试去迁就现状」——同模块 `ensure_planning_parent`、`verify_regular_planning_file` 都把链接判为 `PLAN_UNTRUSTED_PATH`,测试写的才是既定语义。已确认无生产代码或其它测试对这两个码做分支(全仓库仅这两处断言)。 - **回归钉边界,不只钉拒绝**:新增 `tool_plan_handoff_accepts_new_loop_after_steer_and_goal_revision_advance`——跨 loop 且 steer cursor / goal revision 已前进必须被**接受**。原有用例只钉「什么该拒」,所以判据作用域被放大时无人报警。 - **验证**:4 条可在 Windows 复现的用例全绿(含新增回归);Linux-only 那条在 WSL 上跑通。同轮曾出现 3 条 `response_stream` 超时失败,空载单独复跑 3 passed / 0 failed,且三者走 `final-reply` 路径、不经过 `validate_next_entry`,与本次改动无因果——判为负载抖动。 - **未做**:不追查这 5 条各自的完整历史绿/红轨迹(引入点已锁定到单个提交,继续二分无增量);不动 `M1B-2` 的功能面。 - 关联:`apps/ai-game-creator-shell/src-tauri/src/tool_plan_handoff/{identity_order_validation.rs,tests.rs}`、`.../agent/runtime_driver/recovery_scan.rs`、`.../agent/runtime_protocol/planning_storage.rs`;pitfalls.md 同日「把校验往链路前面挪」条。 ## 2026-08-15 恢复重启栈溢出:主循环专用 worker 从「枚举入口」改为不变量 CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 `tokio-rt-worker` 栈溢出并 SIGABRT。属于本文件 pitfalls「Runtime 后台执行不能让大型 async frame 共用默认 worker 栈」的同一失败类,但暴露出该条目的判据形式本身有缺陷。**Windows 上可直接复现,不必去 Linux/WSL**。 - **是本分支引进的,但根因在 master。** 同机同用例、默认栈 A/B:master(`9f5c84ee7`)通过,HEAD(`deae1e08c`)溢出。二分 `RUST_MIN_STACK` 量化:started 变体 master 需 1536–1792 KiB、HEAD 需 2048–2176 KiB(默认 2048,**超出不到 128 KiB**);队列 drain(`drain_next_*`)master 需 1280–1536 KiB、HEAD 需 1792–1856 KiB。`M1B-2` 往主循环与恢复扫描加分支消耗 256–576 KiB,而 master 本就只剩 256–512 KiB 余量——**边界一直是缺的,只是以前刚好没超**。 - **不是递归**:16 MiB 下 2.14s 通过,逐级下探到 2176 KiB 仍通过,符合 pitfalls 记的「大型 async poll frame」而非业务递归。 - **根因:恢复重启是第四个入口,从未被纳入专用 worker。** `recovery_scan.rs` 手写 `tauri::async_runtime::spawn` 直接跑 `drain_game_creator_agent_background_tasks`——正是仓库明确规定必须上 16 MiB 的那个 future。pitfalls 原文按入口枚举三个(普通后台任务、静态委派子任务、manifest ready-task 首次执行),**漏掉一个入口不会产生任何信号**,故判据改写为不变量:所有会进入 Agent 主循环的 future 必须在 `agent-runtime-worker-*` 专用线程上轮询。 - **一并收拢队列 drain。** `spawn_next_..._with_lock` 与 started 变体只差一个 poll 帧(实测 256–384 KiB),却长期分两种栈待遇。HEAD 上它只剩 192–256 KiB 余量,**小于本分支单个工作包的消耗量**,即下一个同量级工作包必然顶穿;且它有 8 个生产调用点,崩在哪条取决于当时路径,比恢复路径更难定位。 - **处置**:统一常量 `AGENT_RUNTIME_BACKGROUND_WORKER_STACK_BYTES`;`spawn_next_..._with_lock` 改用专用线程,**签名保持 `-> ()`、8 个调用点不动**——该入口是 best-effort 幂等语义(拿不到锁即返回、后续 wake 重试),建线程失败只需记录并随闭包释放锁,无需像 started 入口那样交还锁、也就不需要握手;`recovery_scan.rs` 两处手写 spawn 收敛为 helper 调用。恢复重启改走 `spawn_started_..._with_lock` 顺带补上首轮轮询握手——原写法把执行锁 move 进一个无人保证会被轮询的 future,运行时关停时 run 会永远停在 running 且无主。 - **回归改为钉不变量,不钉余量。** drain 入口在 `#[cfg(test)]` 下记录 `std::thread::current().name()`,用例断言其全部以 `agent-runtime-worker-` 开头。**变异验证**:把 `recovery_scan.rs` 改回手写 spawn 并把 `RUST_MIN_STACK` 抬到 16 MiB(因而不会溢出),用例仍以 `["tokio-rt-worker", "tokio-rt-worker"]` 失败——证明该断言独立于栈余量。只断言「默认栈下没崩」的用例在这次崩溃前全部是绿的。 - **验收标准也随之改变**:不是「默认栈下通过」,而是「主循环栈依赖消失」。修复后两条用例在 `RUST_MIN_STACK=1024 KiB`(半个默认栈)下通过;修复前分别需要 2048+ 与 1792+。 - **未做**:不抬 `RUST_MIN_STACK`(pitfalls 明令禁止,CI 有意不配置该变量)、不调大 tauri 全局运行时 worker 栈、不去削 `M1B-2` 的帧——余量不是修复。 - **需回流 master**:master 同样存在「恢复重启走默认栈」与「`drain_next_*` 余量偏低」,只是尚未触发;与 `manifest.rs` 的 Windows 构建修复同理。 - 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/{task_queue.rs,recovery_scan.rs}`;pitfalls.md 同名条目已按不变量重写。 ## 2026-08-15 M1C-1 开工前三条裁决:barrier 独立计数、正向校验的上限、前向兼容粒度另拆 `M1C-0b` 对本文件 2026-08-14「`M1C-0` 合入复核」留下的三条前置逐条裁决,全部读实代码后定稿。**其中第三条订正了 08-14 自己的表述**:原文写的「二选一:给枚举加单条容错,或接受回滚锁死」是伪二选一——「单条跳过并告警」这个选项对 delivery 不安全,已作废。 - **裁决一:补 barrier,且必须是独立的第六个计数 `user_revision_pending_count`。** 不补的后果是 `M1C-1` 写入方一落地,Supervisor 就能在用户修订尚未派出时收束用户任务(收束门 `static_delegate_completion_blocker_at_locked`)。不复用现有两个计数的理由是硬的:`repair_required_count` 带 `repair_of_delegation_id.is_none()`,只算原始委派——因 depth≤1,返工的返工本就不该阻塞;而**用户第 2 次修订的父节点自身就是 repair 节点**,并进去会让第 2 次及以后的修订全部不阻塞,恰好在最需要处失效。`user_input_required_count` 的语义是「等用户回答」,与「用户已发话、等 Supervisor 派发」不同,`detail()` 文案会说反。判据形状照 `user_input_required_count`(`ClaimedByParent` + 该 status + 无活跃 child,**不带** `repair_of.is_none()`)——它正是仓库里「轮次无上限」概念的既有先例。**连带**:除 `is_clear()` 与 `detail()` 外,另有四处调用点逐字段读 barrier 而不走 `is_clear()`(`autonomous_policy.rs` 两处、`runtime_tools/delivery.rs` 两处),加字段不会自动传播,每处单独裁决。这是 `M1C-0` 那个失败模式的同构版本,载体从 enum 变体换成 struct 字段,编译器同样沉默。 - **裁决二:正向一致性锁死在「只能从 `EvidenceReady` 改写而来」,不得更严。** 复用 `EvidenceReady` 的三条客观证据约束(终态 `completed`、无缺失产物、`verification_required` 时 `verified_revision` 存在);产物覆盖校验与「不得携带 `user_input_questions`」已对所有状态生效,不需改。**重点是实现形状**:把 `validate_static_delegate_structured_result` 里按 `contract_status` 的 if/else-if 链改成穷尽 `match`、不留 `_`——同一失败形状在本仓库已出现两次,靠纪律没拦住。**反向风险**:该函数不是入口过滤器,`read_static_delegate_delivery_at` → `validate_static_delegate_delivery_record` 让它每次读取都跑;过严等于把写入方的一个 bug 变成「该 delivery 永久读不出来」,直接触发裁决三的锁死,故不得再加 `EvidenceReady` 自己都没有的条款(例如要求 `error` 为 `None`)。**另订正 08-14 前置二可能引起的误读**:它不是既有漏洞——专业 Agent 自证「用户要求修订」今天不可达,唯一派生点 `build_static_delegate_structured_result_at` 只能产出三个旧变体,claim 回执的 `structuredResult` 从 delivery 拷贝,均为 Runtime 侧;本条约束的是 `M1C-1` 引入的**第二个写入方**(审批命令)。 - **裁决三:「单条跳过并告警」作废,走第三条路,并另开 `M1C-0b`。** 单条跳过不安全的原因很具体:`list_static_delegate_deliveries_at` 喂的是 barrier 的**计数**,跳过一条损坏的 `Dispatched` 记录会让 `waiting_count` 少 1、barrier 变 clear,Supervisor 在仍有未完成委派时收束——把可用性故障换成了正确性故障,比锁死更糟。对照组说明危险面很精确:谱系重放不受影响,缺节点走「上游缺失」出口返回 `(u32::MAX, u32::MAX)` 哨兵,本就 fail closed。**第三条路是把「解析失败」拆两类**:损坏(截断/非 UTF-8/超限)状态未知,维持整体锁死不动;前向不兼容(结构良好、只有一个 enum 值不认识)是已知的未知,解析进显式 `Unknown` 但最大化阻塞(进 barrier、返工门无条件拒、谱系按最保守的「其它」分类,只会拒不会放)。不违反 `M1C-0` 条里「不得静默降级为 `NeedsRepair`」——那条禁的是把记录当正常记录继续走流程,`Unknown` 是显式隔离。半径从「整个项目静态委派面不可用(做游戏链路一起挂)」降到「只冻结携带该状态的那条 lineage」。 - **两条比 08-14 记录更糟的事实。** 锁死半径是**整个项目目录**——`list_static_delegate_deliveries_at(root)` 先读全目录再按 parent run 过滤,任何一条无关 run 的损坏记录毒化所有 run 的 barrier;`.json.previous` 备份救不了——`read_agent_runtime_json_sidecar_with_max_bytes` 只在 primary **NotFound** 时才回退,损坏但存在的 primary 不回退。 - **实现坑。** `#[serde(other)]` 用不了:它只允许在 internally/adjacently tagged 枚举上,而 `StaticDelegateContractStatus` 是序列化成纯字符串的 unit-variant 枚举;需自定义 `Deserialize`,且 `Serialize` 必须原样回写原始字符串,否则旧版本任何一次读-改-写都会把未知值抹掉。 - **拆包与门禁。** ①② 进 `M1C-1`(它们约束的正是 `M1C-1` 新增的写入方);③ 拆 `M1C-0b`,与 `M1C-0` 同形状——无写入方、纯读路径、对既有记录零行为变化可证;塞进审批闭环的 diff 就是重犯第 23.8 节「拆包纪律」自己写的错。`M1C-1` 门禁因此为二选一:`M1C-0b` 先落,或直接上线并在 release note 明写「用过策划审批后回滚旧版本会让该项目静态委派面不可用」;**建议前者**,多机/版本不一致不需要用户主动回滚就会发生。`WP1` 的 depth≤1 结论仍未被推翻。 - 关联文档:`docs/technical/【技术方案】立项策划Agent(Fast GDD)-2026-08-10.md` 第 23.7 节「落地约束」裁决一/二/三与「回归必须覆盖」、第 23.8 节 `M1C-0b` / `M1C-1` 行与「拆包纪律」。 ## 2026-08-15 M1C-0b:静态委派 durable status 前向兼容实现完成 - **落地**:`StaticDelegateContractStatus` 采用手写 serde。四个已知 durable 值保持原有字符串;结构良好的未知字符串解析为 `Unknown(raw)`,并在再次序列化及读-改-写时原样保留 raw;非字符串输入仍拒绝。该包只改读路径,不新增状态写入方。 - **门禁**:`Unknown` 进入 completion barrier 的独立计数与 waiting blocker,不能让 Supervisor 在未知状态未处理时收束;返工入口遇到 `Unknown` 无条件拒绝,即使 lineage depth 为 0;lineage 将其按“其它”质量返工分支保守计数(`depth + 1`、`round = 0`),只会拒绝、不会放宽额度。 - **损坏边界**:截断、非法 JSON、非 UTF-8 或超过 128 KiB 的 sidecar 仍维持整目录 fail closed,不改成单条跳过或告警,以免 barrier 少计数而错误放行。 - **范围与依赖**:不包含 `M1C-1` 的审批写入、`gdd-approval` pending、receipt、UI 或构建准入;不 bump durable schema 版本。为覆盖所有既有读路径,补了 planning Provider、自治 liveness 与终态扫描的 fail-closed 消费门,但没有新增或改变 `M1B-*` 功能依赖;该包仍可在 `M1C-0` 基础上独立验证。既有三种状态及历史记录行为保持不变。 - **结论**:`WP1` 的 `repair_depth≤1` 结论未被推翻;Unknown 只增加前向不兼容时的保守阻塞,不会放宽普通做游戏链路的返工深度门。 - **验收门禁与关联**:定向回归须覆盖未知 raw round-trip、barrier/waiting、无条件返工拒绝、保守 lineage 分类,以及损坏 sidecar 整体锁死;详见技术方案第 23.7 节裁决三与第 23.8 节 `M1C-0b` 门禁。 ## 2026-08-15 完美像素编码前按整数倍 nearest 放大到接近源图 - 背景:2026-08-10 起成功产物直接落逻辑网格 PNG,画布按资源实际宽高显示,结果会明显小于源图。用户要求保持逻辑图宽高比,并把产物放大到接近原图;禁止再走非整数 nearest 拉回精确源尺寸(会让逻辑块宽窄不一)。 - 决策:`style="pixelArt"` 与手动 `POST /api/editor/images/pixel-art-snaps` 仍共用 `snap_pixel_art_with_grid_policy`。检测、切线、采样、Alpha、strict 拒兜底不变。`resample` 之后、`encode_png` 之前,用单一整数 N 做 nearest 放大:`N*` 为 `(C·W + R·H) / (C² + R²)`,在 `floor` / `ceil`(小于 1 当 1)中取距离平方更小者,并列取较小 N;超单边 `10000` 或总像素 `8294400` 则降 N,最低 `N=1`。只持久化这一张 PNG。手动算法指纹升为 `perfect-pixel-v3`。 - 不做:改 walker、透明补边、裁切、横纵不同倍率、Lanczos / bilinear、另存逻辑图、前端框缩放、失败路径、新测试。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】画板角色形象生成入口设计-2026-06-15.md`、`docs/【编辑器】画板图标素材生成入口设计-2026-06-15.md`、`docs/【编辑器】图片画布结构化持久化与迁移回滚方案-2026-07-19.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 - 背景:真实 `gpt-5.6-sol / max` 验收中,`art-director` 失败后已形成 `ready + needs-repair` delivery,但认领、合同读取、claim observation 和完成 blocker 均硬编码为 Supervisor-only;实际直属父 Run `code-prototype` 无法消费回执,随后又发起 29 次 Provider 请求。 - 验证方式:覆盖合法认领与合同精确读取、错误 Agent/Run/delegation 拒绝、delivery 身份篡改阻断、唯一安全默认返工、普通失败零后续 Provider lifecycle,以及新的真实 Provider 空项目轮次。顺带收紧 `validate_executable_inline_javascript_syntax`:正文游离 `<` 不再吞掉后续 `