暂缓资源卡拖动并统一资源管理合同
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Failing after 2m18s
Project CI / Native shell tests (pull_request) Successful in 11m37s

移除资源卡 Pointer 拖动入口并保留点击详情
改写交互与布局测试,覆盖指针不改坐标且不提交 CAS
同步工作台 PRD、技术方案和项目决策记录
This commit is contained in:
2026-08-03 15:35:54 +08:00
parent c24e3010f8
commit 7339bb5da1
8 changed files with 130 additions and 709 deletions
@@ -154,7 +154,7 @@ V1.17 计划快照随 `game-creator-runtime-context-bundle.v3` 持久化,v2
2026-07-19 起,当前父 run 下的专业 Agent 进入 `failed` 后,正式工作台必须提供“在当前项目重试”恢复入口,不得要求用户新建项目。重试必须精确核对原 `agentId + runId + parentRunId`,复用原 task、active Session 和父 run 归属,同时生成新的专业 Agent runId;新 run 继承已持久化的上下文和父子绑定,不覆写旧失败 run 的审计事实,也不得把 UI 重试解释为底层 transport 根因已修复。`agent.resume` 默认 `confirm` 不变:自动 retry command 继续执行 auto gate;正式失败卡按钮自身是本次明确确认,使用 deny-only 的 confirmed retry command。按钮必须原卡即时显示“正在提交重试”、受理或安全错误;若 Supervisor 已为同一 delegation 准备合同 repair,则该按钮优先确认既有 repair,避免重复派发。
completed 专业 Agent 的用户可见成果不能仅依赖项目文件。当委派合同为 `expectedArtifacts=[]` 或未产生显式文件时,工作台必须读取该 Agent 持久对话的最后一条 assistant,作为明确标注的“专业 Agent 文本回执”提供查看,并投影到“资源管理 → 文档”。该投影是持久回执的可见视图,必须保留来源 Agent 与 run 身份,不冒充 manifest asset、项目目录中的实际文件或可下载交付物;内部 Agent ID 不进入用户资源名。美术只交付计划且 manifest 没有图片时必须显示“仅完成计划,尚未生成或登记图片”,不能把文本回执称为美术图片产物。策划 `design-foundation` 与美术 `art-asset-plan` 从 2026-07-20 起属于图片产物型 canonical task:前者必须生成并登记 `assets/ui-prototype.png` 横屏界面原型图,后者必须生成并登记 `assets/art-spritesheet.png` 首版核心美术素材;只有文本计划或空 `expectedArtifacts` 不构成完成。策划图还必须由 `design-foundation` 的 `image.inspect` 对当前图片 SHA 形成 `ui-prototype.v2` 结构化视觉验收:信息 HUD、主要可玩区域、当前玩法的关键实体、主要操作、失败/重开流程、移动端布局意图、实现清晰度与原创主题八项全部通过且问题列表为空。验收不得预设塔防或任何固定玩法;`ui-prototype.v1` 仅供历史动作回执安全读取,不能作为新建或恢复 run 的 completion authority。Runtime finalization 只接受同 run、当前 SHA 的 v2 证据。视觉调用失败、响应不可解析、检查未通过或图片被替换后 SHA 不一致都继续阻塞。未通过的已登记图片在工作台只称“候选界面图(待视觉验收)”,不得冒充正式 UI 原型。图片生成未配置、待确认或失败时保持阻塞/失败,不得投影为 completed。资源卡允许同分类内做当前会话拖拽重排;详情使用受 viewport 与底部 dock 约束的独立可拖浮层,长正文由唯一外层滚动容器承载并用 Markdown 安全渲染。PDF 方案外的顶部项目标题条移除,dock 下方不得保留空白;底部专业 Agent 为只读状态卡,不得因点击保留多个详情浮层。
completed 专业 Agent 的用户可见成果不能仅依赖项目文件。当委派合同为 `expectedArtifacts=[]` 或未产生显式文件时,工作台必须读取该 Agent 持久对话的最后一条 assistant,作为明确标注的“专业 Agent 文本回执”提供查看,并投影到“资源管理 → 文档”。该投影是持久回执的可见视图,必须保留来源 Agent 与 run 身份,不冒充 manifest asset、项目目录中的实际文件或可下载交付物;内部 Agent ID 不进入用户资源名。美术只交付计划且 manifest 没有图片时必须显示“仅完成计划,尚未生成或登记图片”,不能把文本回执称为美术图片产物。策划 `design-foundation` 与美术 `art-asset-plan` 从 2026-07-20 起属于图片产物型 canonical task:前者必须生成并登记 `assets/ui-prototype.png` 横屏界面原型图,后者必须生成并登记 `assets/art-spritesheet.png` 首版核心美术素材;只有文本计划或空 `expectedArtifacts` 不构成完成。策划图还必须由 `design-foundation` 的 `image.inspect` 对当前图片 SHA 形成 `ui-prototype.v2` 结构化视觉验收:信息 HUD、主要可玩区域、当前玩法的关键实体、主要操作、失败/重开流程、移动端布局意图、实现清晰度与原创主题八项全部通过且问题列表为空。验收不得预设塔防或任何固定玩法;`ui-prototype.v1` 仅供历史动作回执安全读取,不能作为新建或恢复 run 的 completion authority。Runtime finalization 只接受同 run、当前 SHA 的 v2 证据。视觉调用失败、响应不可解析、检查未通过或图片被替换后 SHA 不一致都继续阻塞。未通过的已登记图片在工作台只称“候选界面图(待视觉验收)”,不得冒充正式 UI 原型。图片生成未配置、待确认或失败时保持阻塞/失败,不得投影为 completed。资源卡当前只允许点击打开详情,不提供拖动重排;详情继续使用受 viewport 与底部 dock 约束的独立可拖浮层,长正文由唯一外层滚动容器承载并用 Markdown 安全渲染。PDF 方案外的顶部项目标题条移除,dock 下方不得保留空白;底部专业 Agent 为只读状态卡,不得因点击保留多个详情浮层。
V1.17 同时把 finalization journal 升级为 v2 并绑定最终完整计划快照:assistant 已落盘而 Runtime state 丢失时,从 v2 journal 恢复原结构化计划后补齐终态;assistant 尚未落盘且 state 丢失时失败关闭。外层 `failed / budget-exhausted` 只保留最后可信计划,不把未完成步骤机械改成失败。thinking summary、legacy plan event 和 tool-plan repair 公共审计只保留哈希、字符数或计数,必要的模型输出与错误上下文仅留在有界私有 repair 请求中。
@@ -360,7 +360,7 @@ game-project/
- 页面骨架固定为左侧现有全局导航、中间主视窗、右侧陶泥儿对话和底部子 Agent 状态栏;不新建第二套客户端或平行项目页。
- 中间主视窗提供 `资源管理 / 运行` 切换。`code-prototype` 任务完成前运行入口保持视觉不可用,但仍可点击查看“当前无可运行版本”,不能使用会阻断说明交互的原生 `disabled` 或 `aria-disabled`;完成后才允许进入运行表现层。切回资源管理只修改前端展示态,不伪造后端预览暂停结果。
- 资源管理从当前 `GameCreationAppManifest` 派生项目文档、项目版本和 `assets`,并把首页已导入附件作为当前项目上传资源展示。资源按文档、版本、美术、动作、音乐音效分区;`按依赖 / 按类型` 使用各自前端排列,dependency 模式额外绘制当前 manifest 与资源投影可证明的依赖关系。排列与图层都不写回 manifest,不能推断或伪造缺失依赖。
- 资源卡支持选择聚焦、文档展开 / 收起、搜索和类型筛选的界面交互。2026-07-28 起,原一维会话拖拽已替换为两套二维坐标与本地 CAS sidecar;2026-07-31 起,dependency 模式增加不持久化的原生 SVG 关系图层。画板编辑和同类型版本资源替换仍不得在缺少各自正式写回契约时保存为业务事实。
- 资源卡支持点击详情、搜索和类型筛选。2026-07-28 起完成两套二维坐标与本地 CAS sidecar;2026-07-31 起 dependency 模式增加不持久化的原生 SVG 关系图层。2026-08-03 mentor 决定暂缓资源卡拖动,当前卡片不挂载 Pointer Down / Move / Up / Cancel 拖动入口,只允许自动布局和点击详情。资源聚焦不提供工具栏或工具侧边栏;本阶段不继续实现文档读取、美术编辑、音频能力、项目版本或运行模块。
- 运行表现层首版直接嵌入当前项目的 loopback 游戏画面,并展示上一项 / 暂停继续 / 下一项切片控制、素材信息和数值微调面板。`preview.start` 启动本地 server 后把真实 URL 回写工作台,`preview.open` 只激活客户端内运行视图,不再调用系统浏览器;切片、参数调整和自然语言新增调节项首版仍只保留本地 UI 草稿,不修改代码或 manifest。
- 右侧继续复用现有 Project Supervisor 会话、Runtime 澄清和确认链路;输入区展示 `严格审批 / 风险审批 / 无需审批` 独立面板。P0 只有严格审批可选;风险审批和无需审批保持视觉不可用但允许点击查看原因,不替代 Runtime 的逐动作权限、确认、sandbox 或 reconciliation 门禁。风险 Rank 算法记录在 `docs/project-memory/todos/【待解决】AI游戏创作高风险审批Rank-2026-07-20.md`,前端不得自行计算。
- 底部状态栏默认展示策划、美术、程序 3 组,并允许在同一栏展开数值、音频、发布组;状态来自 manifest 与当前 Supervisor run 的 Runtime,悬停显示当前任务与进度。累计泥点必须等待后端计费归因投影;Agent.md 编辑和自定义 Skill 在来源审核、版本、权限、sandbox 与回滚合同完备前不向普通用户开放。
@@ -375,18 +375,20 @@ game-project/
2026-07-28 起,资源画布布局持久化以工作台 PRD §5.2 和 §7.2 为唯一编码合同,实施边界如下:
2026-08-03 需求收口:资源卡手动拖动暂缓。历史 sidecar 坐标继续只读恢复,自动布局仍可为资源集合协调提交 CAS;sidecar schema、布局读取、FIFO、跨窗口系统锁和 Rust CAS 基础设施全部保留,但当前没有用户手动布局入口。
- dependency 与 type 分别保存到 `.agent/workbench/resource-layouts/dependency.json` 和 `type.json`,schema 固定为 `game-creator-resource-layout.v1`。布局是本地工作台 UI sidecar,不进入 manifest、游戏项目 mutation revision、Runtime verification、Agent 产物、资产或云端事实。
- `x / y` 使用 section 内容坐标,`updatedAt` 使用 Unix 毫秒;文件缺失只合成 revision `0` 空布局且不产生只读副作用。每个 mode 按 `projectId + mode + expectedRevision` 做 CAS,成功 revision 加一,冲突返回最新完整布局且不写文件。revision 虽在 Rust 中使用 `u64`,但跨 JSON / Tauri / TypeScript 的合法域固定为 `0..=Number.MAX_SAFE_INTEGER`;共享 DTO 序列化与反序列化、Tauri 输入和前端 IPC 响应均执行同一边界校验。
- Tauri 命令固定为 `read_local_project_resource_canvas_layout` 与 `update_local_project_resource_canvas_layout`。写命令先只读确认有效 manifest,再通过持久 `.layout.lock` 入口获取句柄级跨窗口系统锁,并在锁内复核 projectId、重新读取当前 sidecar。Unix 使用 `flock`,Windows 使用不共享文件句柄;释放只通过句柄 Drop / 进程退出完成,不使用 mtime stale 回收,也不删除锁文件。其余写入继续复用安全路径、链接检查、容量上限、恢复副本与原子替换能力;不能只依赖 React 状态或进程内锁。
- 前端从当前项目开发大组件中拆出纯布局模型与持久 Hook。默认布局、碰撞检查、资源增删协调和 section 边界由纯模型负责;读取、异步身份、CAS、错误回滚和冲突载入由 Hook 负责。Hook 以 `projectPath + projectId + mode` epoch 隔离异步结果,资源变化不取消首读或在途保存;单窗口写入经同一 FIFO 串行提交,每笔都使用最近一次成功 / 冲突响应的权威 revision。视图使用 Pointer Events 做二维拖动,保存中仍允许继续拖动并排队,普通点击、搜索、筛选和唯一资源详情浮层语义保持不变。
- 前端从当前项目开发大组件中拆出纯布局模型与持久 Hook。默认布局、碰撞检查、资源增删协调和 section 边界由纯模型负责;读取、异步身份、CAS、错误回滚和冲突载入由 Hook 负责。Hook 以 `projectPath + projectId + mode` epoch 隔离异步结果,资源变化不取消首读或在途保存;自动协调写入经同一 FIFO 串行提交,每笔都使用最近一次成功 / 冲突响应的权威 revision。资源卡是普通可点击按钮,不绑定卡片级 Pointer 拖动处理器;指针移动不修改 CSS 坐标、不调用 SVG preview、不提交手动布局 CAS。
- 新资源只在第一次进入某个 mode 时计算默认不重叠位置;全部现存坐标保持不变。搜索、筛选、窗口 resize 和 mode 切换不得重排或回写已有坐标,窄视图通过 section 画布范围与滚动访问,不裁切持久坐标。
- type 默认布局固定按 `subtype -> mediaType -> label -> id` 排序。manifest 资产的 subtype 使用 `asset.kind`,任务产物、导入附件和 Agent 文本成果使用稳定的来源 fallback;subtype 必须进入资源协调签名,不能因 MIME 相同而退化成按名称混排。
- 普通保存失败恢复最近可信持久布局;CAS 冲突载入对方最新布局并要求用户重新拖动,同时清除基于旧快照排队的全部手动意图,不自动重放旧坐标。即使冲突发生在允许自动重试的资源协调请求上,只要本次冲突清除了排队手动意图,重新拖动提示就必须绑定当前 scope 保留,不得被后续资源协调成功、失败或通用提示定时器静默清除;新的手动布局成功保存或 scope 切换后才解除。资源自动协调可基于冲突布局最多追加两次重试,持续跨窗口竞争时停止自旋并保留当前会话协调结果。损坏、未知 schema、身份冲突、超限与链接文件失败关闭,不能用空布局覆盖原文件。
- 自动协调保存失败时保留当前会话布局;CAS 冲突载入对方最新布局,需要继续协调时最多追加两次重试,持续跨窗口竞争时停止自旋。用户提示只说明“布局已在其他窗口更新”,不要求重新拖动。损坏、未知 schema、身份冲突、超限与链接文件失败关闭,不能用空布局覆盖原文件。
- 本布局持久化切片不包含资源关系线、资源替换、详情浮层位置、缩放 / 平移、搜索 / 筛选条件、当前 mode,也不修改 `api-server` 或 SpacetimeDB。资源关系线已在后续独立的纯前端切片接入,不改变本段 sidecar 合同;其余 P1 能力继续独立实施。
实施顺序固定为:先同步 TypeScript / Rust DTO 与序列化测试,再实现 Tauri sidecar 读写和 CAS,随后接入前端纯模型、持久 Hook 与二维拖动,最后完成 Rust 安全测试、React 交互测试、跨重启 / 双窗口验收和文档状态回写。任何一步不得用 `localStorage`、manifest 字段或只在当前 React 会话有效的状态冒充项目持久化。
历史实施顺序已完成 TypeScript / Rust DTO、Tauri sidecar/CAS、前端纯模型与持久 Hook。二维手动拖动接线现已暂缓;重新开放前必须先更新 PRD 与验收合同。任何后续步骤不得用 `localStorage`、manifest 字段或只在当前 React 会话有效的状态冒充项目持久化。
2026-07-30 前端并发与性能加固状态:首读、资源更新和拖动保存已拆成 scope epoch + scope 内单写者 FIFO;定向 Hook 测试覆盖首读期间资源变化、在途手动 CAS 后资源协调、冲突清除排队拖动、旧 mode 迟到读取 / 写入、新 scope 不等待旧 scope 卡死请求、持续冲突有界停止,以及在途 CAS 后同资源连续拖动折叠为最后坐标。旧 scope 请求已经发出后不做不安全取消,但会释放前端活动槽并由 epoch 丢弃迟到响应;同 scope 的在途请求仍保持唯一。默认布局用 section 分组与二维占用索引替代逐 slot 全量扫描,dependency 使用按列单调游标,type 使用单调 slot 游标;`4096` 项双模式性能回归纳入前端测试,避免恢复到接近 `O(N³)` 的主线程阻塞实现。
2026-07-30 前端并发与性能加固形成的 scope epoch、scope 内单写者 FIFO、手动意图折叠和冲突恢复实现继续保留为底层技术资产;当前用户入口只消费布局读取与资源自动协调。默认布局仍使用 section 分组与二维占用索引,dependency 使用按列单调游标,type 使用单调 slot 游标;`4096` 项双模式自动布局性能回归继续防止恢复到接近 `O(N³)` 的主线程阻塞实现。手动拖动帧预算、局部 preview 性能和重新拖动提示不再是当前验收条件。
2026-07-30 Rust 并发与零副作用加固状态:资源布局锁已由 `create_new + mtime stale 删除` 改为持久锁文件上的 Unix `flock` / Windows 独占句柄,活锁即使 mtime 很旧也不能被另一个写入者回收,释放后仍复用同一文件实例。更新命令携带只用于校验的 `expectedProjectId`,在任何目录创建前先读取 manifest 并拒绝旧项目窗口,锁内再次核对 projectId;不存在根、非项目根、损坏 manifest 和路径重建后的旧窗口均不产生 `.agent/workbench`。revision 在共享 serde、Tauri 命令和前端 IPC 三层限制到 `Number.MAX_SAFE_INTEGER`,达到上限时保持原文件并失败关闭,不能让 Rust `u64` 值在 JavaScript 中失真后击穿 CAS。
@@ -402,12 +404,12 @@ game-project/
- `ResourceDependencyOverlay.tsx` 使用原生 SVG path/marker,绝对定位在 `.game-resource-canvas-content` 底层并统一 `pointer-events: none`。橙色实线表示 `asset-reference`,灰色圆头虚线表示 `task-flow`;两类连线统一使用连续贝塞尔曲线,任务主线略强于两端分支,箭头使用不随高亮线宽缩放的稳定用户空间尺寸,避免直角折线、突兀拐弯和箭头跳变。不引入 D3、React Flow 或其它图表依赖。
- `asset-reference` 的 source / target 是同一资源时使用卡片右侧外绕贝塞尔闭环,两个锚点分开且 marker 保留在返回锚点;路径不穿过卡片。dependency section 在现有 extent 外额外增加 `64px` 右侧视觉 gutter,确保最右卡片的闭环和箭头可滚动显示;不改卡片坐标、`resourceCanvasLayoutModel.ts` 或 sidecar。
- 图层用 SVG 自身节点定位所属画布容器,测量各 section plane 相对原点;`ResizeObserver` 在图层挂载时只创建一次,与 window resize 一起负责重新测量并在卸载时清理。type 模式不挂载图层且释放 graph state;项目身份作为 key,切换 mode、项目或工作台卸载都会销毁旧 SVG。
- 基础 positions 保持稳定,拖动预览单独传入。Pointer Move 通过 `requestAnimationFrame` 合帧;静态 task/reference React SVG 子树不依赖 drag preview,每帧只按局部连接索引重新计算当前资源关联的 edge / flow 并更新对应 path `d`。搜索、选择、项目切换、真实 positions 或 section origin 变化才允许重新协调静态图层;结束后仍只通过原布局 Hook 保存卡片坐标,SVG 几何从不持久化。
- Rust、前端 DTO/SVG 和工作台 AppSurface 回归覆盖生产 `task-1` 数据形状、真实 producer、证据缺失、精确引用、去重、无效 ID、完整任务环、4096 链式拓扑、聚合复杂度、搜索过滤、选择高亮、局部拖动更新、稳定 Observer、type 模式卸载与项目切换销毁。真实 Chromium 性能目标为拖动 p95 `<16.7ms`、不出现 `>50ms` long task;若实测仍超过预算,再增加 viewport + overscan 边裁剪,不在首轮预先引入额外复杂度。
- 基础 positions 保持稳定;卡片 Pointer Move 不进入 SVG preview,只有搜索、选择、项目切换、真实 positions 或 section origin 变化才重新协调图层。SVG 几何从不持久化。
- Rust、前端 DTO/SVG 和工作台 AppSurface 回归覆盖生产 `task-1` 数据形状、真实 producer、证据缺失、精确引用、去重、无效 ID、完整任务环、4096 链式拓扑、聚合复杂度、搜索过滤、选择高亮、稳定 Observer、type 模式卸载与项目切换销毁。局部拖动更新与真实 Chromium 拖动性能目标暂缓。
2026-08-03 评审加固:Rust read model 在现有 producer assignment 上同时返回经完整任务图 SCC 压缩计算的确定性 `dependencyDepth`,前端不再递归推导正式依赖层级。dependency 模式以 scope 化 `idle / loading / ready / failed` 状态阻断布局 Hook;图终态前不创建 fallback、不读取或写入 sidecar,图失败只以空图初始化一次。读取已有 dependency 布局时保留全部 `manuallyPlaced=true` 坐标,把 `manuallyPlaced=false` 作为可派生自动位置按最终深度重新协调;结果未变化时不写入。
拖动热路径不再把 preview 写入工作台父组件 state。卡片使用稳定回调与 `React.memo`,动画帧直接更新拖动卡片 CSS 变量和 section 临时 extent;SVG 图层暴露命令式 preview 句柄,复用 Rust 局部连接索引和已缓存 path 节点,只更新受影响 reference / task-flow 几何。ResizeObserver 仍为单图层单实例。4096 张真实卡片测试必须证明非拖动卡片零重渲染、静态 SVG 子树不重建,Chromium p95 继续以 `<16.7ms` 为门槛。实时 DOM 几何不得通过 Tauri IPC 往返 Rust。
历史命令式 drag preview 句柄与局部连接索引可以保留,但项目工作台不再向资源卡传入该入口。拖动热路径、4096 张真实卡片拖动重渲染和 Chromium p95 门槛统一暂缓;当前回归只要求 Pointer Move 不改变卡片坐标、SVG path 或布局 revision。`ResizeObserver` 仍保持单图层单实例,任何实时 DOM 几何都不得通过 Tauri IPC 往返 Rust。
## 分阶段实施
@@ -421,7 +423,7 @@ game-project/
- 用户能创建本地 Web 游戏项目。
- 用户进入项目开发页后能看到资源管理主视窗、陶泥儿对话栏和底部策划 / 美术 / 程序 Agent 状态栏;`1280×800` 最小横屏窗口和更大桌面窗口均不得出现页面级横向 / 纵向溢出,对话输入与底部 Agent 状态栏始终位于视口内。
- 资源管理可在按依赖 / 按类型之间切换、搜索资源、展开文档和聚焦资源;dependency 模式展示可验证的资源引用和聚合任务流,搜索、选择与拖动同步更新线段;所有展示数据来自当前 manifest、当前资源投影或当前项目导入附件。
- 资源管理可在按依赖 / 按类型之间切换、搜索资源并点击打开当前资源详情;dependency 模式展示可验证的资源引用和聚合任务流,搜索过滤端点、选择高亮直接上下游。资源卡不可拖动,Pointer Move 不更新坐标、线段或手动布局;所有展示数据来自当前 manifest、当前资源投影或当前项目导入附件。
- 首个 `code-prototype` 任务未完成时运行入口不可进入并给出可感知提示;完成后可进入运行表现层,真实预览直接加载到客户端内受限运行容器。
- 审批档位通过独立弹出面板切换,默认严格审批;界面选择不得绕过 Runtime 现有确认门禁。
- 聊天输入 `/plan` 可在普通聊天消息里查看下一轮分工计划,不读取任务文件、不启动 run、不修改项目,也不新增普通用户计划面板。