# 决策记录 > 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。 > 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。 ## 记录格式 ```md ## YYYY-MM-DD 决策标题 - 背景:为什么需要这个决策 - 决策:最终决定是什么 - 影响范围:涉及哪些模块/文档/流程 - 验证方式:如何确认决策仍有效 - 关联文档:相关 PRD、技术文档、提交或 Issue ``` ## 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-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`:正文游离 `<` 不再吞掉后续 `