完善资源管理工作台与有界预览
将资源卡本体化并补齐分区缩放滚动与依赖聚类 增加全进程三槽预览调度范围取消与安全分块读取 保持布局 CAS 预览缓存回收和依赖图权威合同 补齐资源管理前端 Tauri AppSurface 回归并同步文档
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# AI 游戏创作项目开发工作台 PRD
|
||||
|
||||
更新时间:`2026-08-10`(现有 Godot 项目入口与项目根绑定)
|
||||
更新时间:`2026-08-10`(资源卡本体化、分区缩放、同类型依赖聚类与详情优先收口;现有 Godot 项目入口与项目根绑定)
|
||||
|
||||
## 1. 产品定位
|
||||
|
||||
@@ -50,6 +50,22 @@
|
||||
- 资源分区由资源投影固定,前端交互不能改变分类。
|
||||
- 资源卡手动拖动、拖动持久化、拖动性能与冲突后的重新拖动提示全部暂缓,不作为当前产品合同或验收条件。
|
||||
|
||||
#### 3.3.1 资源管理三个串行阶段
|
||||
|
||||
1. 阶段一“资源卡本体化”:`dependency / type` 两种布局共用同一个资源卡组件和预览调度器。图片与安全 SVG 直接显示主体并保留透明棋盘底;视频显示首个可解码画面并可在卡内播放;音频使用清晰的音频视觉和独立播放键;文档显示安全纯文本摘要或稳定类型占位;项目版本不伪造媒体,只显示稳定版本视觉和必要的父子 / 绑定状态。中央详情以元数据、Rust 权威依赖关系和版本信息为主,不重复放大图片、SVG 或视频;文档正文和按用户意图读取的音频控件位于元数据之后。
|
||||
2. 阶段二“分区独立缩放”(已完成)同时包含互不替代的分区可视高度和内容倍率。文档、项目版本、美术资源、音乐音效资源的标题栏分别保留高度减小 / 恢复 / 增大,并新增 `50%..200%` 内容缩放减小 / 百分比复位 / 增大;触摸板捏合、`Ctrl/Cmd + wheel` 与按钮使用同一倍率模型,普通双指滚动仍只滚动。高度与倍率都以 `projectId + layoutMode + category` 隔离并只保留在当前会话内存;倍率以分区 viewport 中的手势中心为锚点,逻辑 `x / y`、`180×128` 本体尺寸和媒体比例不变。最小高度固定容纳标题操作与至少一排卡片,最大高度以中央资源画布当前可用高度为上限。分区保持正常文档流,后续分区由外层资源画布滚动访问,单区内容在缩放后仍只在该区内部滚动。两类表现态都不修改 manifest、`game-creator-resource-layout.v1`、布局 CAS 或资源身份,不重建媒体控件、不抢走焦点,也不恢复卡片拖动。
|
||||
3. 阶段三“依赖布局聚类”(已完成):仅在 `dependency` 模式中,按固定资源分区对当前 Rust 只读图返回的同类型精确引用边和同类型聚合任务流做弱连通分组;任务流以流节点连接其成员,绝不展开成资源笛卡尔积,但只作为布局邻近提示,不绘制虚线、不进入画布关系说明。`dependencyDepth` 是唯一横向层级权威;每个相关簇先按“最小深度 -> 最小稳定资源 ID”排序,再在同层进行固定两轮由左至右、由右至左的中位数扫描,平局回退稳定资源 ID;同 SCC/环成员保持连续,环后资源继续按 Rust 深度推进。孤立资源是稳定单例组,紧凑排在相关簇之后,簇之间保持固定留白。跨分类 read model 关系不改变 Rust 业务真相,但不得进入前端布局邻接、关系说明或 SVG;搜索只隐藏卡片和橙色精确引用边,不重算组或压缩坐标。历史 `manuallyPlaced=true` 坐标继续原样保留并参与自动坐标避让。聚类是可派生表现,不新增 manifest 字段、sidecar schema、SpacetimeDB 表或第二套依赖真相。
|
||||
|
||||
#### 3.3.2 本体化资源卡交互与性能合同
|
||||
|
||||
- 卡片默认可视区域不再显示文件名、资源名称、来源、路径、任务和媒体类型等详细文本。这些字段继续进入搜索索引和中央详情;卡片“打开详情”入口的可访问名称必须包含稳定可辨识的资源名与类别。
|
||||
- 卡片外层是非交互容器;“打开详情”与“播放 / 暂停”必须是可分别键盘聚焦的同级按钮,禁止在 `<button>` 中嵌套 `<button>`。播放键不选择资源、不打开详情。
|
||||
- 同一时间最多播放一个卡片音频或视频。打开详情、切换布局模式、切换项目、进入运行视图、搜索 / 筛选隐藏当前资源、当前资源被删除或资源身份 / 路径变化时,必须暂停旧媒体并收口待播放意图;卡片卸载还必须执行防御性暂停。同资源 ID 的无关 manifest 更新不得重建控件或抢走焦点。
|
||||
- 图片、视频首帧和项目文档只在卡片进入资源画布可见区及小幅预取边界后读取;音频只在用户点击播放后读取。前端逻辑调度器必须使用有界并发、同身份去重、有界 LRU 淘汰,不允许按全部投影同步启动读取。`play > detail > visible` 是固定调度优先级;可见性预取不得占满全部队列容量或静默丢弃播放 / 详情请求,主动请求进入硬上限队列时必须替换最低优先级的排队预取并优先执行。终态预览缓存同时受 `48` 项和 `64 MiB` 总载荷双重上限约束,媒体按解码后的 Blob 字节、文档按 UTF-8 字节计入;Tauri 返回的 data URL 只允许作为 IPC 临时载体,进入 React 状态前必须转换为可撤销的 Blob URL。LRU 淘汰、资源删除、项目 / mode 切换和卸载都必须 `revokeObjectURL`,不能让 base64 字符串或失效 Blob 长期占用 WebView。
|
||||
- 卡片读取继续调用 `read_local_project_image_preview`、`read_local_project_media_preview` 和 `read_local_project_text_preview`;项目边界、manifest / 任务登记、`file.read` 策略、普通文件 / 链接、签名、尺寸和读取漂移门禁不变。每个 Hook 挂载及每次 `projectPath + projectId + mode` 变化必须生成不复用的 `scopeId`,每个实际读取生成唯一 `requestId`;每个队列任务和结果继续绑定完整的 `projectPath + projectId + mode + resourceId + category + path + mediaType`。前端 scope epoch 只隔离逻辑队列、缓存和异步回调;三个读取命令在 Tauri 进程中共用唯一的全局 `3` 槽物理读取管理器,不能因项目、mode、Hook 或窗口不同获得额外物理槽。scope 切换或卸载必须调用窄职责 `cancel_local_project_resource_preview_scope`,使等待 permit 和分块读取中的旧任务协作取消;旧 epoch 的 `then / catch / finally` 仍不得写入、释放或扣减新 scope 的前端状态,`A → B → A` 也不得复用第一轮 A 的 scope、请求或迟到结果。
|
||||
- 原生读取必须在命令入口、权限 / 登记复核后、打开文件后、每个固定上限读取块之间、签名 / 图片结构校验前、漂移复核前和 base64 编码前检查取消。取消的排队任务不得打开文件,取消的在途任务不得生成 data URL 或 Blob URL;全局 permit 只能在对应原生任务结束后释放。重复 `requestId` 失败关闭,取消未知或已完成 scope 幂等成功;成功、失败和取消均必须清理活动 request / scope registry。为防止近期 request ID 重放和已取消 scope 复活,原生管理器继续保留有界 tombstone:seen request 最多 `8192` 项;非活动 cancelled scope 的保留预算为 `1024` 项,仍有请求的已取消 scope 必须临时钉住,最后一个请求结束后立即重新淘汰到预算内。明确取消只静默收口旧 scope,当前 scope 的真实 transient / permanent 失败语义不变。
|
||||
- 安全读取失败、图片解码失败或视频无可解码画面时,卡片展示稳定类型占位,不挂载破图,不降级为项目外 URL 或裸路径读取。滚动可见性不得自动重试已失败预取;读取漂移、文件替换和通用暂时失败标记为 transient,用户再次打开详情或点击播放时可以显式重试。超尺寸、损坏、类型不支持和不安全 SVG 等永久失败继续缓存且不得用“关闭后重试”误导用户。
|
||||
|
||||
### 3.4 数值微调
|
||||
|
||||
- 数值修改立即写入当前项目的编辑态配置。
|
||||
@@ -131,11 +147,11 @@ idle -> focused(document|art|audio|version) -> idle
|
||||
```
|
||||
|
||||
- 文档:合法 Agent 文本回执直接使用对话投影内容;项目文件只允许读取当前 manifest 已登记资产或已完成任务产物中的 Markdown、文本、JSON、YAML、TOML,必须经过 `file.read` auto 权限、相对路径、项目边界、普通文件、符号链接 / 硬链接、读取漂移、2 MiB、UTF-8 与扩展名白名单校验。正文使用不执行 HTML、不加载远程图片、不产生可点击外链的安全 Markdown 渲染,并在中央画布内独立滚动;读取失败显示错误空态。
|
||||
- 美术:PNG、JPEG、WEBP 继续使用图片魔数与像素边界预览;GIF、SVG、AVIF、BMP、MP4、WebM、MOV 通过新增受控媒体读取链路按文件签名校验后在中央画布放大聚焦。SVG 额外拒绝脚本、事件处理器、外部资源引用和实体声明;视频使用内置播放控件。读取失败显示错误空态。
|
||||
- 美术:PNG、JPEG、WEBP、GIF、SVG、AVIF、BMP、MP4、WebM、MOV 只在资源卡本体中按既有受控读取、文件签名与解码门禁展示;中央详情不重复加载或放大图片 / 视频本体。SVG 继续拒绝脚本、事件处理器、外部资源引用和实体声明。
|
||||
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源替换仍留给后续切片。
|
||||
- mentor 最新决定:资源聚焦不提供工具栏,也不提供工具侧边栏。
|
||||
- 点击资源后,中央主视窗从 `resources.list` 切换为 `resources.focused.document / art / audio / version`,左侧平台导航、右侧 Supervisor 对话和底部 Agent 状态栏保持原位;聚焦容器只包含标题、资源主体、必要元数据与右上角收起按钮,不使用页面级浮层或可拖动标题栏。
|
||||
- 点击资源后,中央主视窗从 `resources.list` 切换为 `resources.focused.document / art / audio / version`,左侧平台导航、右侧 Supervisor 对话和底部 Agent 状态栏保持原位。聚焦容器以路径、类型、来源任务、Rust 权威依赖层级、同类型上下游、同类型任务流和版本字段为首屏主体;美术图片与视频不在详情页重复展示大尺寸本体,避免遮挡信息。文档正文与按意图加载的音频控制位于元数据之后;不使用页面级浮层或可拖动标题栏。
|
||||
- 焦点转换以稳定资源 ID 为准。只有从资源列表进入详情或从一个资源 ID 切换到另一个 ID 时聚焦详情 region;同一资源 ID 因 manifest 更新而重新投影时,不得抢走详情内音频 / 视频控件、文档链接或收起按钮的当前焦点。
|
||||
- 显式收起或按 Escape 后恢复进入前的搜索条件、dependency / type 布局模式、资源画布滚动位置和选中资源,并优先把键盘焦点还给原触发资源卡;这些只属于当前前端会话,不写入布局 sidecar。若资源已经被后台删除,必须清理 stale focused / selected ID、关闭详情并把焦点落到“搜索项目资源”,不得落到 `body`。项目切换和进入运行视图必须取消旧项目的焦点恢复意图。
|
||||
- 阶段四只新增上述受控读取与媒体展示;阶段六在同一聚焦容器内补齐正式版本只读展示和引用高亮,但不新增资源聚焦工具栏 / 工具侧边栏,不新增美术编辑、音频编辑 / 替换、资源重新生成、版本替换或运行模块。飞书原需求中“编辑并生成新资源”的条件项仍暂缓,不能只打开画板却缺少回写、`referenceResourceIds` 血缘登记、新资源自动选中与邻近布局的完整闭环。
|
||||
@@ -168,7 +184,7 @@ P0 中 `approvalMode` 只能有效写入 `strict`;其它值只能作为不可
|
||||
|
||||
### 5.2 资源画布布局(P1)
|
||||
|
||||
实现状态(2026-08-03):dependency / type 双模式通过项目内 CAS sidecar 独立持久化;dependency 模式由 Tauri Rust 只读构建关系拓扑与确定性依赖深度、前端 SVG 派生几何,图结构和线段均不写入布局 sidecar。依赖图加载完成前设布局初始化屏障,避免以临时 `dependencyDepth=0` 生成并持久化错误坐标。当前用户入口只允许自动布局与资源卡点击;资源卡手动拖动已按 mentor 决定暂缓。历史 sidecar 坐标继续只读恢复,底层布局读写与 CAS 合同保留,但当前没有用户手动布局入口。资源替换、缩放 / 平移等其余 P1 能力仍按本文非目标保持未实现。
|
||||
实现状态(2026-08-10):dependency / type 双模式通过项目内 CAS sidecar 独立持久化;dependency 模式由 Tauri Rust 只读构建关系拓扑与确定性依赖深度、前端 SVG 派生几何,图结构和线段均不写入布局 sidecar。依赖图加载完成前设布局初始化屏障,避免以临时 `dependencyDepth=0` 生成并持久化错误坐标。当前用户入口只允许自动布局与资源卡点击;资源卡手动拖动已按 mentor 决定暂缓。历史 sidecar 坐标继续只读恢复,底层布局读写与 CAS 合同保留,但当前没有用户手动布局入口。阶段二的分区可视高度与内容倍率都是和 sidecar 完全分离的当前会话表现态;内容倍率不改变持久逻辑坐标或资源身份。整个资源画布的自由平移、资源替换等其余 P1 能力仍按本文非目标保持未实现。
|
||||
|
||||
```ts
|
||||
type ProjectResourceCanvasLayout = {
|
||||
@@ -251,10 +267,11 @@ type UpdateProjectResourceCanvasLayoutResult =
|
||||
|
||||
- 资源卡是可点击按钮,只负责选择资源并让中央主视窗进入当前唯一资源聚焦状态;不得绑定卡片级 `pointerdown / pointermove / pointerup / pointercancel` 拖动处理器。
|
||||
- 指针移动不得修改卡片 `x / y`、不得产生拖动预览、不得更新依赖线几何,也不得提交手动布局 CAS。卡片 title、cursor、`touch-action` 和 class 不得暗示可拖动。
|
||||
- dependency 默认布局按 `dependencyDepth` 形成横向层级,同层资源纵向寻找第一个不重叠位置;type 默认布局固定按“资源子类型 -> 媒体类型 -> 名称 -> 资源 ID”稳定排序,在分区内从左到右、从上到下寻找第一个空位。布局模型的 `subtype` 必填:manifest 资产使用 `asset.kind`,任务产物、导入附件与 Agent 文本成果分别使用稳定的 `task-artifact`、`attachment`、`agent-result`,不得以缺失值或显示文案兜底;资源协调签名必须包含 subtype。卡片尺寸与间距由单一前端布局模型常量维护。
|
||||
- type 模式资源集合变化时保留全部仍存在的坐标,只为新 ID 计算默认位置,并删除已确认失效的旧 ID。dependency 模式只永久保留 `manuallyPlaced=true` 的用户坐标;`manuallyPlaced=false` 属于可派生自动位置,在 Rust 关系图首次就绪或可信 producer / dependency depth 变化后按最终拓扑确定性重算。自动重算不得移动手动坐标,协调结果与持久布局逐项一致时不得产生 CAS 写入。
|
||||
- dependency 默认布局按 Rust 返回的 `dependencyDepth` 形成横向层级;同层仅在所属固定分区内按精确引用、聚合 task-flow 的稳定邻接、连通簇和固定两轮中位数扫描确定纵向次序,随后寻找第一个不重叠位置。task-flow 只作为聚合超边参与分组和排序,不能生成资源两两边。dependency 自动布局使用专用 `48px` 列间走线区和 `40px` 行间走线区;相关簇以本簇最大层行数为高度,资源较少的层在该高度中居中,避免菱形 / 分叉关系一侧极短、另一侧过长。type 默认布局继续使用原有 `16px` 行列间距,并固定按“资源子类型 -> 媒体类型 -> 名称 -> 资源 ID”稳定排序。布局模型的 `subtype` 必填:manifest 资产使用 `asset.kind`,任务产物、导入附件与 Agent 文本成果分别使用稳定的 `task-artifact`、`attachment`、`agent-result`,不得以缺失值或显示文案兜底;资源协调签名必须包含 subtype。卡片尺寸、间距和 `0..=1_000_000` 坐标上限由前后端同名合同维护。超深依赖仍保留原始 `dependencyDepth` 业务真相,但显示坐标在上限列确定性饱和并纵向避让;自动布局在 IPC 前必须保证全部 `x / y` 合法,不能向 Tauri 永久重放必然失败的坐标。
|
||||
- type 模式资源集合变化时保留全部仍存在的坐标,只为新 ID 计算默认位置,并删除已确认失效的旧 ID。dependency 模式只永久保留 `manuallyPlaced=true` 的历史坐标;`manuallyPlaced=false` 属于可派生自动位置,在 Rust 关系图首次就绪、`dependencyDepth` 或资源拓扑身份签名(精确引用端点和聚合 task-flow 成员)变化后按最终拓扑确定性重算。签名以稳定资源 ID 的规范端点 / 成员序列生成固定大小摘要,不使用显示名称或浏览器测量值;自动重算不得移动手动坐标,协调结果与持久布局逐项一致时不得产生 CAS 写入。
|
||||
- 搜索或筛选只隐藏卡片,不删除、压缩或重排其坐标;清空搜索后恢复原位置。
|
||||
- 窗口尺寸变化只改变可视范围和分区滚动边界,不回写、裁切或缩放持久坐标。当前客户端继续以 `1280×800` 横屏合同验收。
|
||||
- 窗口尺寸变化只改变可视范围和分区滚动边界,不回写或裁切持久坐标。当前客户端继续以 `1280×800` 横屏合同验收。
|
||||
- 分区高度和内容倍率都不属于逻辑布局几何。资源卡始终使用原 `x / y` 与 `180×128` 逻辑尺寸;内容 plane 只在显示层按 `50%..200%` 变换,并用同比例 frame 形成真实滚动范围。标题栏后的分区 viewport 使用内部滚动暴露缩放后的卡片,整个分区继续参与外层正常文档流。触摸板捏合、`Ctrl/Cmd + wheel` 与缩放按钮共享倍率状态,普通 wheel 不缩放;`Ctrl/Cmd + wheel` 必须由可取消的原生 `{ passive: false }` 监听处理并真实取消 WebView 默认缩放。搜索、项目或 mode 切换均不得把倍率写回布局。分区内部滚动按 `projectId + mode + category` 隔离,外层资源画布按 `projectId + mode` 隔离,二者在详情开关、模式与项目切换后分别恢复。
|
||||
- 打开项目、切换 mode 或当前 mode 首次出现新资源时执行“读取 -> 协调 -> 必要时 CAS 写入”;dependency 模式必须先等待与当前 `projectPath + projectId + resource inputs` 匹配的 Rust 图进入 `ready` 或 `failed` 终态,等待期间不得创建 fallback、读取 sidecar、协调资源或入队保存。`failed` 只允许以空图降级初始化一次。项目或 mode 已切换后返回的旧异步结果必须丢弃。
|
||||
- 同一 `projectPath + projectId + mode` 的首次读取与资源集合协调必须分开:资源集合变化不得取消已经发出的读取或保存。当前 scope 内资源自动协调写入使用单写者 FIFO,任一时刻最多一个 CAS 在途,后一笔必须使用前一笔成功返回的 revision。切换项目或 mode 后,旧 scope 的在途请求不能阻塞新 scope 队列;前端放弃旧请求槽位并丢弃其迟到响应,后端继续依靠 `expectedProjectId + expectedRevision + 系统锁` 仲裁已发出的请求。
|
||||
- 自动协调 CAS 冲突时直接载入返回的最新布局;仍需协调时可以基于权威 revision 最多追加 `2` 次重试,持续跨窗口写入时不得无限自旋。当前提示只说明“布局已在其他窗口更新”,不得要求用户重新拖动。
|
||||
@@ -271,17 +288,18 @@ type UpdateProjectResourceCanvasLayoutResult =
|
||||
- 阶段五实现状态(2026-08-03):dependency 自动排列同时消费任务 DAG 与精确资源引用。Rust 把可信 producer 的任务深度作为资源深度下限,再对 `asset-reference` 图做迭代式 SCC 压缩与确定性层级传播;被引用资源位于引用资源之前,同一引用环共享稳定深度,环后资源继续递增,没有引用关系的资源保持默认不重叠位置。布局深度通过独立 `dependencyDepths` 返回,不能把 producer assignment 冒充全部资源的布局结果。
|
||||
- `producerMappingTruncated=true` 只关闭依赖有界 Agent DB 审计的 `producerAssignments`、`taskFlows` 和 `cyclicTaskIds`。Rust 返回的 `dependencyDepths` 仍是 manifest / 精确引用 read model 的权威结果,前端必须过滤未知资源、负数、非整数和非安全整数后继续消费;不得因 producer 截断清空全部深度,也不得在前端重算替代深度。`referenceEdges`、connection index 中的 reference 关系、`cyclicResourceIds` 和 unresolved reference 继续保持可信。
|
||||
- 图层只在 dependency 模式挂载;type 模式不得渲染 SVG、连线或 marker。切换 mode、切换项目或卸载工作台时必须销毁旧图层,并清理尺寸观察和窗口事件监听。
|
||||
- 输入固定为当前资源投影的全部卡片身份 / 坐标与 Tauri Rust 返回的 `ProjectResourceGraph` 只读 DTO;Rust 负责资源过滤、去重、迭代式环检测、SCC 压缩后的确定性依赖深度、任务流聚合和一跳连接索引,前端只负责 DTO 防御归一化、浏览器几何与原生 SVG path / marker。SVG 叠加在资源卡底层并设置 `pointer-events: none`,不得引入 D3、React Flow 等图表库,也不得阻断卡片点击。
|
||||
- `asset-reference` 表示精确资源引用,使用明亮橙色实线与连续贝塞尔曲线。`GameCreationAppAssetManifestEntry.source.referenceResourceIds` 中的外部资源 ID 必须先唯一匹配另一项资产的 `source.resourceId`,再映射为当前资源卡 ID;缺失、重复或已删除的目标均不得渲染幽灵连线。
|
||||
- `task-flow` 表示同一资源类型内的任务产物流转,使用灰色圆头虚线;文档、项目版本、美术、音频之间不得绘制跨分区虚线。任务依赖按 `sourceTaskId -> targetTaskId + section` 分区聚合为一条主线,两端只保留同分区资源并绘制平滑曲线分支,不得出现直角折线;禁止对上下游资源生成笛卡尔积连线。任务主线与分支可以使用不同线宽和透明度表达聚合层级,但不能改变端点或方向语义。
|
||||
- 输入固定为当前资源投影的全部卡片身份 / 坐标与 Tauri Rust 返回的 `ProjectResourceGraph` 只读 DTO;Rust 负责资源过滤、去重、迭代式环检测、SCC 压缩后的确定性依赖深度、任务流聚合和一跳连接索引,前端只负责 DTO 防御归一化、逻辑 viewport 裁剪与原生 SVG path / marker。每个固定分区在自己的 `.game-resource-plane` 内持有一个 SVG,SVG 与卡片直接消费同一逻辑坐标、卡片尺寸、CSS scale 和 viewport scroll;端点主路径不得再通过 `getBoundingClientRect` 拼接全局屏幕坐标。SVG 位于资源卡底层并设置 `pointer-events: none`,不得引入 D3、React Flow 等图表库,也不得阻断卡片点击。
|
||||
- `asset-reference` 表示精确资源引用;只有两端属于同一固定资源分类时才进入前端布局邻接、无障碍关系说明和 SVG,使用明亮橙色实线与实心箭头。跨分类引用仍可存在于 Rust read model,但资源管理画布不得绘线、不得以它形成布局簇或边界偏置。`GameCreationAppAssetManifestEntry.source.referenceResourceIds` 中的外部资源 ID 必须先唯一匹配另一项资产的 `source.resourceId`,再映射为当前资源卡 ID;缺失、重复或已删除的目标均不得渲染幽灵连线。
|
||||
- `task-flow` 表示同一资源类型内的任务产物流转,但资源画布不再绘制灰色虚线或任务流 marker,也不把它加入 `aria-describedby` 关系说明。它只按 `sourceTaskId -> targetTaskId + section` 作为聚合超边参与 dependency 连通簇和中位数排序,禁止对上下游资源生成笛卡尔积邻接;详情仍可展示 Rust read model 已证明的任务信息。
|
||||
- 画布资产 producer 只能来自 `agent.runtime.canvas.asset_generate` 的 `assetId -> agentId` 审计且 `agentId` 必须存在于当前 manifest;External Editor `source.taskId` 属于平台生成任务命名空间,禁止当作 manifest task ID。证据缺失、冲突或有界审计读取未覆盖时不生成对应 task flow,不猜测归属。
|
||||
- 图模型必须对资源引用图和完整任务依赖图做迭代式环检测,不得用无界递归遍历;参与环的可见边保留渲染并标记 cyclic,环本身不能造成重复生成或死循环。
|
||||
- 资源自引用的起点与终点为同一张卡片时,必须绘制在卡片外侧的可见闭环并保留箭头,不得让路径穿过卡片后被底层 SVG 层级遮挡。
|
||||
- 搜索只允许为当前可见端点生成几何;任一精确引用端点隐藏时该线隐藏,聚合任务流只保留仍可见的两端分支,任一侧没有可见资源时整条任务流隐藏。
|
||||
- 资源点击只进入中央聚焦并保留当前选中卡片,不改变依赖卡片或连线的颜色、线宽与透明度;关系线始终直接展示,不提供点击后的上下游高亮或无关线弱化。
|
||||
- 搜索只允许为当前可见端点生成几何;任一精确引用端点隐藏时该橙线隐藏。task-flow 不产生显示几何,搜索也不因此重排布局。
|
||||
- 同类型精确引用按稳定边 ID 和对端次序为同一卡片同侧的多条边分配独立端口;横向层级可用时优先左右连接,同列或横向间隙不足时才上下连接。同轴端点直接用直线,需转向时使用正交线段与最大 `10px` 的小圆角,不使用大范围贝塞尔控制柄。端口顺序不使用显示名称、随机数或浏览器枚举顺序,相同输入必须产生相同路径。资源点击只进入中央聚焦并保留当前选中卡片,不改变依赖卡片或连线的颜色、线宽与透明度;关系线始终直接展示,不提供点击后的上下游高亮或无关线弱化。
|
||||
- 资源卡 Pointer Move 不改变基础 positions 或 SVG 几何。连线只随布局读取、资源自动协调、搜索、项目切换或 section origin 变化而更新。
|
||||
- `ResizeObserver` 在单个图层生命周期只允许构造一次。dependency section 额外提供至少 `64px` 右侧视觉 gutter,确保最右侧自环和箭头可完整滚动显示,但不得修改卡片坐标或布局 sidecar。
|
||||
- 阶段五不改变手动位置边界:已有 `manuallyPlaced=true` 坐标原样保留,资源引用新增或变化只允许重新派生 `manuallyPlaced=false` 的自动坐标;任务流继续按任务对与资源分区聚合,禁止为了计算深度或绘线生成资源笛卡尔积。
|
||||
- 每个已挂载 dependency 分区最多构造一个 `ResizeObserver`,四区总数最多四个;observer 只维护该分区的逻辑 viewport,不测量或重建卡片屏幕端点。dependency section 额外提供至少 `64px` 右侧视觉 gutter,确保最右侧自环和箭头可完整滚动显示,但不得修改卡片坐标或布局 sidecar。
|
||||
- 分区高度、分区内部滚动和窗口 resize 在各分区内通过单一 `requestAnimationFrame` 调度器合并逻辑 viewport 更新,不得在每个 scroll event 中同步重算。浏览器以分区 viewport 原生裁剪同属该 plane 的 SVG 和卡片,因此线段不能穿过标题栏或泄漏到其它分区;一端可见时绘制入向 / 出向边界继续线,两端都离屏时不渲染。项目 / mode 切换或卸载时必须清理对应 observer、scroll 与 window resize 监听。
|
||||
- 阶段五不改变手动位置边界:已有 `manuallyPlaced=true` 坐标原样保留,资源引用新增或变化只允许重新派生 `manuallyPlaced=false` 的自动坐标;任务流继续按任务对与资源分区聚合,禁止为了布局分组生成资源笛卡尔积,且不进入 SVG。
|
||||
|
||||
### 5.3 资源类型与替换兼容性(P1)
|
||||
|
||||
@@ -439,15 +457,17 @@ type ProjectAgentMudPointAttribution = {
|
||||
|
||||
### 7.3 P1 资源依赖关系图验收
|
||||
|
||||
1. dependency 模式显示对画布背景至少 `3:1` 对比度的橙色实线资源引用,并只在同一资源类型分区内显示灰色虚线任务流;跨类型不显示虚线,type 模式没有图层或连线。
|
||||
1. dependency 模式只显示同一资源类型分区内的精确引用,使用对画布背景至少 `3:1` 对比度的橙色实线箭头;聚合任务流只参与布局,不绘制灰色虚线或 marker。type 模式没有图层或连线。
|
||||
2. 精确引用只接受唯一有效的外部资源 ID 映射,删除或不存在的资源不产生幽灵连线。
|
||||
3. 多资源任务依赖按资源类型分区后,各分区只形成一条聚合主线与 `O(S+T)` 条端点分支,不产生 `S×T` 连线或跨分区虚线。
|
||||
3. 多资源任务依赖按资源类型分区后只形成布局超边,以 `O(S+T)` 成员关系参与聚类,不产生 `S×T` 邻接、SVG 主线、端点分支或跨分区虚线。
|
||||
4. 资源引用环和无资源产物参与的任务环都可被有限遍历识别,界面不死循环。
|
||||
5. 搜索触发端点过滤;资源点击不改变上下游卡片或任何连线的视觉状态,资源卡指针移动不更新线段,点击与中央聚焦行为不回归。
|
||||
6. 切换布局模式或项目后旧 SVG、ResizeObserver 与窗口监听全部清理;图层从不写入 layout sidecar、manifest 或其它持久化。
|
||||
7. 4096 资源链式 fixture 继续验证拓扑、聚合复杂度和自动布局性能;拖动局部更新与真实 Chromium 拖动帧预算暂缓,不作为当前验收条件。最右侧自环与箭头仍需完整显示。
|
||||
8. Rust 图读取延迟时,dependency sidecar 在图进入 `ready / failed` 前没有读取或写入;首次布局直接使用 Rust 返回的最终 producer 与 dependency depth。Agent DB 有界读取截断时 producer、task flow 与 `cyclicTaskIds` 失败关闭,精确 manifest 引用及 Rust 返回的合法 `dependencyDepths` 继续到达布局层。重新打开包含深度 `0 / 1 / 2` 自动坐标的旧布局时不得降成全 `0` 或持久化扁平布局;手动位置逐项不变,自动位置按最终拓扑协调且相同结果不增加 revision。
|
||||
9. 依赖 SVG 作为装饰层不可聚焦并对辅助技术隐藏;画布通过关联的视觉隐藏文本逐条说明当前可见资源引用和任务流,搜索过滤或模式切换后文本与可见关系同步变化。
|
||||
9. 依赖 SVG 作为装饰层不可聚焦并对辅助技术隐藏;画布通过关联的视觉隐藏文本逐条说明当前可见的橙色精确引用,搜索过滤或模式切换后文本与可见关系同步变化。task-flow 不进入该关系说明。
|
||||
10. 精确引用使用橙色实线和可见箭头;dependency 自动卡片之间保留横向 `48px`、纵向 `40px` 走线区,相关簇的窄层按本簇最大层高度居中。不同横向层级且卡片间有空间时使用左右端口优先的横线,同列、同深度或横向空间不足时才使用上下端口纵向降级。同行 / 同列直接连线,需要转向时只使用正交线段与最大 `10px` 小圆角。同一卡片同侧的多条精确引用按稳定边 ID 与对端位置分配不同端口;自引用继续在卡片右侧外绕,箭头端点与卡片保留固定显示间隙。
|
||||
11. 每个分区 SVG 必须位于拥有相应资源卡的 `.game-resource-plane` 内,以同一逻辑坐标随原生滚动和 CSS scale 同步移动;滚动 / 缩放不得通过异步 DOM 屏幕测量重新绑定端点。分区 viewport 原生裁剪本区 SVG,不使用四区 viewport 并集裁剪;一端离屏时只显示对应方向、带同语义箭头的边界继续线,不保留指向已裁剪卡片的悬空主路径,两端离屏时隐藏。搜索隐藏精确引用任一端点时整边隐藏。
|
||||
|
||||
### 7.4 P1 正式项目版本阶段六验收
|
||||
|
||||
@@ -458,18 +478,28 @@ type ProjectAgentMudPointAttribution = {
|
||||
5. 任意现有 manifest 写入只能保留磁盘版本前缀并追加新记录;存储边界以跨进程专用锁串行覆盖旧状态读取、前缀校验、安装和回读,修改、删除、重排或并发旧快照覆盖已有版本时写入失败。
|
||||
6. 版本选择和高亮不写 manifest、布局 sidecar 或 project revision;dependency / type 两种布局都可显示绑定高亮,既有依赖关系 SVG 语义不变。
|
||||
|
||||
### 7.5 阶段七完整验收
|
||||
### 7.5 资源分区独立高度验收
|
||||
|
||||
1. 四个分区均可独立缩小、放大和恢复默认高度;任一分区变化时其它分区高度、资源卡尺寸和媒体比例不变。到达上下限时对应按钮同时具备正确的 disabled 视觉与辅助技术语义。
|
||||
2. 高度、倍率和分区内部滚动按 `projectId + dependency|type + document|version|art|audio` 隔离,外层资源画布位置按 `projectId + dependency|type` 隔离;切换模式、切换项目和打开 / 关闭资源详情后,分别恢复当前会话中的内外滚动位置,且键盘焦点不被无关重置。
|
||||
3. 最小高度可操作标题控制并完整容纳至少一排卡片;最大高度不超过中央资源画布当前可用高度。窗口变小后超界尺寸被永久夹取到新上限,后续放大窗口不自动恢复旧超界值。
|
||||
4. 分区放大只在正常文档流中下推后续分区,不使用浮层、绝对定位、负 margin 或 z-index 覆盖。分区内容超出时内部可滚动,外层画布仍可滚动访问其它分区,`1280×800` 无页面级溢出。
|
||||
5. dependency 线在高度、分区内部滚动、外层滚动和窗口 resize 后仍与可见卡片端点对齐;单端卡片被分区 viewport 裁掉时只保留边界继续线,不保留悬空主路径,两端都被裁掉时隐藏,线段不穿过标题栏。滚动与 resize 在每个分区内使用单一 `requestAnimationFrame` 合帧,每个分区 plane 最多创建一个 `ResizeObserver` 并在卸载时清理全部监听。
|
||||
6. 高度操作不调用 `update_local_project_resource_canvas_layout`,不改 manifest、不更新 sidecar revision;阶段一的图片 / 视频 / 音频 / 文档 / 版本卡片、单媒体播放与中央详情回归全部通过。
|
||||
|
||||
### 7.6 阶段七完整验收
|
||||
|
||||
1. 对照飞书需求、当前 PRD、技术方案、代码、测试与阶段提交复核阶段零至阶段六;美术编辑生成新资源继续按本 PRD 已确认的闭环条件暂缓,不作为遗漏或伪完成。
|
||||
2. AppSurface 同时覆盖文档、图片、SVG、音频和视频聚焦;视频必须使用原生 `controls` 且 `preload="metadata"`,读取策略失败时中央主视窗显示安全空态,右侧对话和底部 Agent 状态栏继续存在。
|
||||
3. `1280×800` 应用内浏览器实测 `window`、document 与 body 均无页面级横向或纵向溢出。浏览器开发页受真实登录门禁保护,不为验收绕过认证或伪造 Tauri;工作台内部结构由 AppSurface 集成测试与资源布局 CSS 合同测试复核。
|
||||
4. 根目录全量 Vitest、前后端 typecheck / lint / build、Rust workspace test / check、SpacetimeDB schema、原生壳、内容 / 编码、生产运维与部署门禁全部通过后,阶段七才允许提交。
|
||||
5. 本地 `.env`、`.env.local`、密钥、缓存、日志和构建产物不进入阶段七提交;提交前再次执行编码检查和 `git diff --check`。
|
||||
2. AppSurface 同时覆盖文档、图片、SVG、音频和视频资源卡与中央聚焦:图片、SVG 和视频主体只在卡内展示,中央详情展示元数据与依赖信息且不重复挂载 `img / video`;文档正文与按意图读取的原生音频 `controls` 位于详情元数据之后。读取策略失败时卡片或详情显示与错误类型一致的安全空态,右侧对话和底部 Agent 状态栏继续存在。
|
||||
3. 预览回归必须同时覆盖前端意图调度与原生物理读取:连续项目 / mode / Hook / 窗口切换和 `A → B → A` 下,全进程真实读取峰值始终 `<= 3`;旧 scope 的等待与在途任务可取消,新 scope 在旧任务最近取消点后取得 permit;取消任务不进入 base64 / Blob 阶段,不写当前状态,活动 request / scope registry 最终清零。seen request tombstone 最多保留 `8192` 项;非活动 cancelled scope tombstone 的预算为 `1024` 项,仍活动的取消 scope 可为防复活临时超出预算,但必须在对应 scope 结束后立即收敛。不得把这两类防重放 / 防复活 tombstone 误判为活动 registry 泄漏。文档、图片、安全 SVG、视频与音频原有登记、权限、路径、签名、尺寸、链接和漂移门禁必须全部回归。
|
||||
4. `1280×800` 必须在当前 HEAD 的真实、已认证应用 WebView / Chromium 中测量 `window`、documentElement 与 body 的 client / scroll 宽高,证明页面级横向和纵向均无溢出,并确认右侧对话、输入区和底部 Agent Dock 的边界全部位于视口内。浏览器开发页受真实登录门禁保护,不为验收绕过认证或伪造 Tauri;AppSurface 与资源布局 CSS 合同测试只作为内部结构回归,不能替代本条真实测量。验证记录必须包含当前 commit、视口、测量值、操作步骤和结果,确保可重复复核。
|
||||
5. 根目录全量 Vitest、前后端 typecheck / lint / build、Rust workspace test / check、SpacetimeDB schema、原生壳、内容 / 编码、生产运维与部署门禁全部通过后,阶段七才允许提交。
|
||||
6. 本地 `.env`、`.env.local`、密钥、缓存、日志和构建产物不进入阶段七提交;提交前再次执行编码检查和 `git diff --check`。
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
- 当前收口不实现资源卡手动拖动,也不实现资源聚焦工具栏、资源聚焦工具侧边栏、美术编辑、音频编辑 / 替换、资源重新生成、资源替换、下一迭代版本创建入口、运行版本切换、版本回滚、运行模块扩展、测试切片、运行态消费版本、数值参数或泥点归因。正式版本记录已经成为 manifest 业务真相,但当前只读取、校验和展示已有记录。
|
||||
- 本切片不持久化资源聚焦状态、画布缩放 / 平移、搜索条件、筛选条件或当前 mode;聚焦退出时的列表上下文恢复只限当前前端会话,这些状态如需跨重启保存必须另行扩展合同,不能塞入 `game-creator-resource-layout.v1`。
|
||||
- 本切片不持久化资源聚焦状态、分区高度、分区内部滚动、画布缩放 / 平移、搜索条件、筛选条件或当前 mode;聚焦退出时的列表上下文恢复只限当前前端会话,这些状态如需跨重启保存必须另行扩展合同,不能塞入 `game-creator-resource-layout.v1`。
|
||||
- 不修改 SpacetimeDB schema。
|
||||
- 不开放普通用户 Agent.md/Skill。
|
||||
- 不自动确认 Agent 动作,不自动触发可能扣费的生成。
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
# 资源管理评审缺陷修复与阶段七收口实施计划
|
||||
|
||||
日期:`2026-08-11`
|
||||
|
||||
状态:`T0–T5 已完成;T6 进行中;T7 未开始`
|
||||
|
||||
适用分支:`codex/resource-management-final`
|
||||
|
||||
当前实现基线:隔离分支 `codex/resource-management-clean` 的三提交树(基于 `origin/master@3c457feb4c82d123e9bd311ae69aab0192f94e3c`,与源分支本地权威快照逐字节一致)
|
||||
|
||||
已融合主线:`origin/master@3c457feb4c82d123e9bd311ae69aab0192f94e3c`
|
||||
|
||||
权威产品合同:[`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`](../../prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)
|
||||
|
||||
长期决策入口:[`docs/project-memory/shared-memory/decision-log.md`](../shared-memory/decision-log.md)
|
||||
|
||||
> 本文是本轮资源管理收口期间的临时执行合同。后续 Agent 每次继续本任务时,必须先重新核对当前 Git 状态、最新 `origin/master`、权威 PRD 和本文状态,再从第一个未完成阶段继续;不得把历史测试结果当成当前 HEAD 的证据。本文只在 T7 的删除门禁满足后删除。
|
||||
|
||||
## 一、计划用途与更新规则
|
||||
|
||||
1. 本文负责把当前评审阻塞项、实现边界、测试矩阵、真实验收、Git 历史整理和最终删除条件收在同一处,避免后续会话只修其中一条后提前宣称完成。
|
||||
2. 每个阶段只有在对应实现、测试和证据都完成后,才能把状态从“未开始”改成“已完成”;命令被中断、受环境限制或只通过定向测试时,必须写成“阻塞”或“部分通过”。
|
||||
3. 如果代码、最新主线或权威 PRD 与本文冲突,以代码和最新权威文档为准;先局部修订本文和权威文档,再继续编码。
|
||||
4. 不把本文写入 `docs/README.md` 或长期共享记忆索引。它是阶段性计划,不是新的产品合同。
|
||||
5. `.env`、`.env.local`、密钥、认证状态、日志、缓存、测试数据库和构建产物始终视为用户或本机所有,不得修改、暂存或提交。
|
||||
6. 不执行隐式 `stash`、硬重置、覆盖式 checkout 或未获授权的强制推送。需要改写远端 PR 分支历史时,必须先取得用户对 `--force-with-lease` 的明确授权。
|
||||
|
||||
## 二、T0 审计结论
|
||||
|
||||
### 2.1 当前 Git 状态
|
||||
|
||||
- T0 首次审计时 `HEAD` 与上游均为 `d29fc1e0fb62d0714646fe3b7346db1847664693`,主线为 `origin/master@755bf808587a7cb663c458160e1f102a4a676ff8`。
|
||||
- T1 于 `2026-08-11` 结束原未完成 merge,形成本地提交 `ac3c028f5`;随后把上一阶段 Native HTTP 候选调整和本轮临时计划保存为本地 checkpoint `bbca9471c`,最后以 `42f1bd359` 融合 `origin/master@aa5c857060af4fe2644b21e0679a727490995935`。
|
||||
- `42f1bd359` 形成时已融合 `aa5c857060af4fe2644b21e0679a727490995935`。T5 执行期间重新 fetch 后,`origin/master` 又前进到 `3c457feb4c82d123e9bd311ae69aab0192f94e3c`;先以 `373d03eb3` 保护本轮资源修复现场,再由 `b2be9de2f` 无冲突融合新增 PR #155。该主线提交修改完美像素逻辑分辨率、External v1 文档契约和图片画布生成链路,不直接修改资源预览实现;两份共享记忆文档已并列保留双方新增内容。最新主线现为 `HEAD` 祖先,无 `MERGE_HEAD`、无未解决冲突。
|
||||
- 本地快照提交后,任务侧没有未提交文件;未提交区域只保留:
|
||||
- 用户所有的 `.env` 与 `.env.local`;永久排除。
|
||||
- `server-rs/crates/api-server/server-rs/.data/tracking-outbox/` 生成数据;默认从读取、修改、暂存和提交中明确排除,只有取得用户明确授权后才能核对目标并定向清理,且不能扩大删除范围。
|
||||
- 按用户要求,上一阶段 Native 候选改动不在 T1 继续复测或阻塞本轮资源预览修复;其最终去留与完整 Native 门禁统一留到 T4/T5。
|
||||
|
||||
### 2.2 当前两条 P1 根因
|
||||
|
||||
#### P1-A:旧 scope 的 Tauri 读取没有取消,实际资源占用不再全局有界
|
||||
|
||||
当前 `useProjectResourceCardPreviews.ts` 在项目或布局 mode 切换时:
|
||||
|
||||
- 推进 `scopeEpochRef`;
|
||||
- 清空当前队列、pending identity 和终态缓存;
|
||||
- 把 `activeReadsRef` 直接换成新 epoch 的 `{ count: 0 }`;
|
||||
- 立即允许新 scope 再发起 3 个读取。
|
||||
|
||||
但是已经发出的 `read_local_project_image_preview`、`read_local_project_media_preview` 和 `read_local_project_text_preview` 没有 request ID、scope ID 或取消令牌。现有测试把 A scope 的 3 个 Promise 保持挂起后切到 B,并明确期待总 `invoke` 数立即变成 6。这只证明迟到回调不会写错新状态,没有证明底层读取、base64 临时字符串和 WebView 瞬时内存仍然有界。
|
||||
|
||||
Tauri 当前读取路径又会整文件读入内存:图片最多 `8 MiB`,文档最多 `2 MiB`,媒体最多 `32 MiB`;媒体随后还会生成 base64 data URL。连续 dependency/type、项目或 A→B→A 切换可以不断叠加旧物理读取,绕过 `64 MiB` 终态缓存预算。
|
||||
|
||||
#### P1-B:缺少绑定当前 HEAD 的真实 `1280×800` 验收证据
|
||||
|
||||
PRD §7.6.3 已明确要求真实、已认证应用 WebView / Chromium 记录:
|
||||
|
||||
- 当前 commit;
|
||||
- 真实视口;
|
||||
- `window`、`documentElement` 与 `body` 的 client / scroll 宽高;
|
||||
- 中央主视窗、右侧对话、输入区和底部 Agent Dock 的关键边界;
|
||||
- 操作步骤与结果。
|
||||
|
||||
评审截图记录当前 PR 普通评论为空,既有 CI run 也没有对应 artifact。AppSurface、JSDOM 和 CSS 正则测试只能作为结构回归,不能替代这条人工门禁;实施 T6 前还必须在 Gitea 重新核对最新外部状态。该门禁对应 PRD §7.6 第 4 项。
|
||||
|
||||
### 2.3 其它评审阻塞项
|
||||
|
||||
- Native shell 曾出现 `1800 passed / 1 failed`,失败用例为 `game_chat_pure_continue_inherits_failed_root_semantics_and_manifest_progress`,表现为项目锁占用。必须先得到当前融合树上的可复现结果、第一错误和根因,再确认当前未暂存的 HTTP 插件 feature 调整是否真的相关。
|
||||
- 当前 PR 分支包含不符合仓库提交规范的英文 merge 标题、空正文或仅冲突清单正文。最终远端历史不能继续保留这些提交。
|
||||
- 当前 HEAD 没有有效审批。最新主线、全部 required CI、真实 `1280×800` 证据和提交历史整理完成后,必须重新请求评审并取得有效批准。
|
||||
|
||||
## 三、目标、非目标与不变量
|
||||
|
||||
### 3.1 本轮目标
|
||||
|
||||
1. 让资源卡预览读取在前端逻辑队列和 Tauri 物理读取两层都保持有界。
|
||||
2. 项目或 mode 切换时取消旧 scope 的排队/在途读取,使新 scope 不被旧 scope 长期饿死,也不通过重置前端计数制造物理超额并发。
|
||||
3. 保持 `play > detail > visible`、队列硬上限、同身份去重、48 项/64 MiB LRU、Blob URL 回收、transient/permanent 分类与 A→B→A 隔离。
|
||||
4. 融合最新主线并解决当前 merge,不回退主线新增的现有 Godot 项目入口和其它现役能力。
|
||||
5. 修复或证伪 Native shell 单测失败,跑完当前工作流要求的全部门禁。
|
||||
6. 在最终 PR HEAD 上完成真实、已认证 `1280×800` 测量并记录可复核证据。
|
||||
7. 用中文标题和逐行中文正文整理 PR 分支历史,重新取得 required CI 与有效评审。
|
||||
|
||||
### 3.2 明确非目标
|
||||
|
||||
- 不恢复资源卡拖动,不新增手动拖动持久化。
|
||||
- 不修改 manifest、项目版本结构、Rust 资源依赖图、SpacetimeDB schema、资源布局 sidecar schema 或布局 CAS。
|
||||
- 不改变 `dependencyDepth` 的 Rust 权威地位,不在前端重新推导业务真相。
|
||||
- 不放宽项目根、manifest/任务登记、`file.read` 策略、普通文件/链接、签名、尺寸、读取漂移或安全 SVG 门禁。
|
||||
- 不新增平行资源页面、第二套预览系统或通用任务调度框架。
|
||||
- 不把真实登录信息、项目绝对路径、Cookie 或本机验收资料提交到仓库。
|
||||
|
||||
### 3.3 必须保持的现有合同
|
||||
|
||||
| 合同 | 必须保持的结果 |
|
||||
| --- | --- |
|
||||
| 前端优先级 | `play > detail > visible`;主动请求可替换最低优先级排队预取 |
|
||||
| 前端逻辑队列 | 总上限 `96`;可见性预取保留主动请求槽位 |
|
||||
| 终态缓存 | 最多 `48` 项且最多 `64 MiB`;媒体按 Blob 字节、文档按 UTF-8 字节计入 |
|
||||
| IPC 载荷 | data URL 只作临时传输;进入 React 状态前转 Blob URL |
|
||||
| 回收 | 淘汰、资源删除、项目/mode 切换、身份变化和卸载均撤销 Blob URL |
|
||||
| 身份 | `projectPath + projectId + mode + resourceId + category + path + mediaType` 全量绑定 |
|
||||
| 异步隔离 | 旧 epoch 的 then/catch/finally 不写入、不释放也不扣减新 scope 状态 |
|
||||
| 媒体行为 | 搜索隐藏、切换视图/项目/mode、详情打开或资源删除时暂停旧媒体 |
|
||||
| 安全读取 | 原有三条 Tauri command 名称与全部安全门禁不变 |
|
||||
|
||||
## 四、冻结的修复方案
|
||||
|
||||
### 4.1 两层调度边界
|
||||
|
||||
前端继续负责“用户意图与展示状态”,Tauri 新增唯一的进程级物理读取管理器负责“真实并发与取消”:
|
||||
|
||||
```text
|
||||
资源卡可见/详情/播放意图
|
||||
-> 前端优先级队列(96,play > detail > visible)
|
||||
-> Tauri requestId + scopeId
|
||||
-> 原生全局读取管理器(所有窗口/项目/mode 合计最多 3 个 permit)
|
||||
-> 权限/登记/路径/签名/漂移门禁
|
||||
-> 有界分块读取与取消检查
|
||||
-> data URL 临时回传
|
||||
-> Blob URL + 48 项/64 MiB 终态 LRU
|
||||
```
|
||||
|
||||
前端的 3 槽只用于当前 Hook 的逻辑泵;原生 3 槽才是跨 scope、跨 Hook、跨窗口的物理上限。任何旧 scope 都不能因为前端重置计数而获得额外物理读取槽。
|
||||
|
||||
### 4.2 scope 与 request 身份
|
||||
|
||||
- 每次 Hook 挂载和每次 `projectPath + projectId + mode` 变化都生成新的不透明 `scopeId`;A→B→A 的第三个 scope 不复用第一个 A 的 ID。
|
||||
- 每个实际 IPC 读取生成唯一 `requestId`,并继续携带完整资源身份。
|
||||
- 三个现有读取命令保留命令名,只增加内部调度所需的 `scopeId/requestId` 参数;不得把三个安全读取入口合并成新的宽泛文件读取命令。
|
||||
- 新增窄职责 `cancel_local_project_resource_preview_scope` 命令,只接受不透明 scope ID,不接受路径,也不读取文件。
|
||||
- 重复 request ID 失败关闭;取消未知或已经结束的 scope 幂等成功,避免卸载与切换竞态制造用户错误。
|
||||
|
||||
### 4.3 原生取消与全局并发
|
||||
|
||||
- 在 Tauri app state 中注册唯一 `ProjectResourcePreviewReadManager`,全进程共享 `3` 个 permit。
|
||||
- 等待 permit 的任务必须同时等待 scope 取消;旧 scope 一旦取消,尚未取得 permit 的任务立即退出,不进入文件读取。
|
||||
- 已取得 permit 的任务使用共享取消令牌,并在以下边界检查取消:命令入口、权限/manifest 登记复核后、打开文件后、每个固定上限读取块之间、媒体签名/图片结构检查前、漂移复核前和 base64 编码前。
|
||||
- 物理 permit 只能由对应原生任务在结束或确认取消后释放;前端 scope 切换不得直接释放原生 permit。
|
||||
- 读取使用固定小块并继续受现有 `2/8/32 MiB` 文件上限约束。取消后的字节缓冲和临时 base64 不进入返回值;取消不得生成 Blob URL。
|
||||
- 原生取消返回稳定的内部取消类别。当前 scope 的真实失败仍按 transient/permanent 规则展示;旧 scope 或明确取消结果静默收口,不能显示成当前资源失败。
|
||||
- 原生管理器必须在成功、失败、取消、panic/join error 和窗口卸载路径清理活动 request/scope 注册,不能形成永久活动 registry 泄漏;seen request tombstone 最多保留 `8192` 项;非活动 cancelled scope tombstone 的预算为 `1024` 项,仍有请求的已取消 scope 必须临时钉住,最后一个请求结束后立即重新收敛。
|
||||
|
||||
### 4.4 新 scope 不被旧 scope 饿死
|
||||
|
||||
- scope 切换时,前端先推进 epoch、停止媒体、清理队列/缓存并向原生管理器发送旧 scope 取消,再泵入新 scope 请求;取消命令不阻塞 React 提交。
|
||||
- 新请求可以立即进入原生等待队列;旧排队任务会因取消退出,旧在途任务在最近的分块取消点释放 permit,新 scope 随即取得槽位。
|
||||
- 不采用“每个 scope 自己拥有 3 个物理槽”的方案,也不采用仅靠忽略 Promise 结果的伪取消。
|
||||
- 如果实施中发现某个平台普通文件读取无法在合理边界内执行协作取消,必须暂停并补充平台级可中断方案;不得退回无限叠加旧读取。
|
||||
|
||||
### 4.5 文档先行修正
|
||||
|
||||
编码前先局部修正以下权威文档:
|
||||
|
||||
- PRD §3.3.2:把“每个 scope 获得独立并发计数”改为“每个 scope 拥有独立逻辑状态/epoch,但物理读取使用进程级全局 3 槽并可取消旧 scope”。
|
||||
- PRD §7.6:加入跨 scope 物理并发、取消确认、连续切换和无旧 base64/Blob 残留验收。
|
||||
- `decision-log.md` 当前资源管理决策:区分逻辑 epoch 与物理全局调度,纠正会允许 6 个未完成读取并存的旧表述。
|
||||
- `pitfalls.md`:新增“读预览与 CAS 写入不同,不能只丢弃旧 Promise 后重置物理槽”的长期排障结论。
|
||||
- AI 游戏创作 App 技术方案中与资源预览并发/验收重复的段落同步到同一口径。
|
||||
|
||||
## 五、预计改动范围
|
||||
|
||||
| 文件 | 预计职责 |
|
||||
| --- | --- |
|
||||
| `apps/ai-game-creator-shell/src/view/project-development/useProjectResourceCardPreviews.ts` | 生成 scope/request ID、发送取消、保留优先级队列与 epoch 防线、静默处理取消 |
|
||||
| `apps/ai-game-creator-shell/src/view/project-development/resourceCardPreviewModel.ts` | 仅在需要时补充窄的调度常量/类型;不承载原生业务规则 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/resource_preview_scheduler.rs` | 新增进程级 3 槽、活动 scope/request registry、等待取消与清理,以及 `8192 / 1024` 两类有界 tombstone;不读取任意路径 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/resource_inspect.rs` | 文档/媒体分块读取与取消检查,保留稳定读取和安全 SVG 门禁 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/image_inspect.rs` | 图片预览读取接入同一取消探针,保留尺寸/签名/漂移门禁 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/commands.rs` | 三个读取命令接入 manager,并增加窄 scope 取消命令 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/main.rs` | 注册 app state 与取消 command |
|
||||
| `apps/ai-game-creator-shell/tests/useProjectResourceCardPreviews.test.ts` | 改写错误的“6 个挂起读取即成功”用例并补连续切换/取消/ABA 回归 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/tests/project_tools.rs` 及 scheduler 单测 | 覆盖全局并发、排队取消、分块取消、活动 registry 清理、两类 tombstone 有界淘汰和安全门禁回归 |
|
||||
| AppSurface/相关前端测试 | 证明卡片、详情、播放、搜索隐藏与资源切换没有回归 |
|
||||
| PRD、技术方案、decision log、pitfalls | 同步最终合同与已验证经验 |
|
||||
|
||||
如实际实现需要修改 manifest、资源图、布局 sidecar、SpacetimeDB、外部 API 或媒体安全策略,立即停止并重新评审;这些不属于本计划的默认授权范围。
|
||||
|
||||
## 六、阶段执行顺序
|
||||
|
||||
### T1:收口当前 merge 与工作区所有权
|
||||
|
||||
1. 再次获取并核对最新 `origin/master`;若主线已越过当前融合基线 `aa5c857060af4fe2644b21e0679a727490995935`,更新本文基线后重新审计差异。
|
||||
2. 检查 staged merge diff,确认所有冲突解析同时保留资源管理合同和主线 Godot 项目入口。
|
||||
3. 单独审计未暂存的 Native shell 候选修复;只有复现结果支持因果时才纳入。
|
||||
4. 永久排除 `.env`、`.env.local` 与 tracking-outbox;未取得用户明确授权时不得读取、删除、暂存或提交 tracking-outbox,获授权后也只能核对明确目标并定向清理,不触碰其它目录。
|
||||
5. 以中文标题和逐行中文正文结束本地融合,保留一个可回退的本地集成锚点;暂不推送。
|
||||
|
||||
退出条件:merge 已结束、最新主线是祖先、无未解决冲突、用户配置未进入 index、`git diff --check` 和 staged diff 审计通过。
|
||||
|
||||
### T2:先修正文档合同
|
||||
|
||||
1. 按 §4.5 更新 PRD、技术方案、decision log 和 pitfalls。
|
||||
2. 明确取消类别、全局 3 槽、scope/request 身份、分块取消点、活动 registry 清理、seen request `8192` 硬上限、cancelled scope `1024` 非活动预算与活动钉住后收敛语义,以及现有安全门禁。
|
||||
3. 确认文档没有把 AppSurface/CSS 测试写成 `1280×800` 实测替代品。
|
||||
|
||||
退出条件:文档已经具体到可以直接指导 TypeScript/Rust 字段、状态和测试,不再保留“独立 scope 物理 3 槽”的歧义。
|
||||
|
||||
### T3:实现可取消的全局预览读取
|
||||
|
||||
1. 先实现并单测原生 scheduler、取消令牌和清理守卫。
|
||||
2. 把图片、文档、媒体读取改成可检查取消的有界分块读取;保持所有安全门禁和大小上限。
|
||||
3. 将三个 Tauri command 接入原生 manager,注册取消 command 与 app state。
|
||||
4. 前端生成唯一 scope/request ID,在切换和卸载时取消旧 scope;保留当前优先级、LRU、Blob 回收与 epoch 防线。
|
||||
5. 对取消和旧 scope 结果执行静默收口;当前 scope 的真实 transient/permanent 错误行为不变。
|
||||
|
||||
退出条件:任意时刻原生实际读取不超过 3;旧 scope 取消后新 scope 能取得槽位;取消任务不编码 data URL、不创建 Blob URL、不污染新 scope。
|
||||
|
||||
### T4:补齐本轮资源预览回归
|
||||
|
||||
> 2026-08-11 用户补充执行顺序:上一阶段 Native shell 候选故障暂不复测、不阻塞本轮资源预览缺陷修复;先完成并定向验证本轮改动,Native 单项与全量门禁统一保留到 T5 最终验证。
|
||||
|
||||
#### 前端/跨层必测矩阵
|
||||
|
||||
- `play > detail > visible` 和 96 队列硬上限不回归。
|
||||
- 48 项/64 MiB 双重 LRU、连续媒体浏览和 Blob URL 恰好一次回收不回归。
|
||||
- A→B 时旧 scope 被取消,不能再把 6 个未取消读取并存当成成功标准。
|
||||
- A→B→A、项目切换、dependency/type 快速连续切换至少覆盖多轮;旧 then/catch/finally、取消 ACK 和活动 request registry 清理均不能改写新 scope;两类 tombstone 继续按 `8192 / 1024` 上限有界保留和淘汰。
|
||||
- 3 个旧读取挂起时切换 scope:原生活动数峰值始终 `<= 3`,B 在旧读取确认取消后启动,旧任务不进入 base64/Blob 阶段。
|
||||
- 取消发生在等待 permit、读取首块后、读取末块前、签名/结构检查前和 base64 前时都可安全退出。
|
||||
- 文档/图片/安全 SVG/视频/音频的登记、策略、类型、大小、链接、漂移和替换门禁全部回归。
|
||||
- 搜索隐藏立即暂停、详情与播放意图、资源身份变化、卸载和 AppSurface 展示不回归。
|
||||
|
||||
退出条件:本轮新增/改写的前端、scheduler、安全读取与 AppSurface 回归全部通过;旧 scope、取消 ACK 和异常/崩溃清理不污染新 scope;Native shell 仍明确留在 T5,不能把“暂缓”写成“已通过”。
|
||||
|
||||
### T5:全量本地门禁
|
||||
|
||||
进入全量命令前,先单独运行旧 Native shell 失败用例:
|
||||
|
||||
```bash
|
||||
cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml game_chat_pure_continue_inherits_failed_root_semantics_and_manifest_progress -- --test-threads=1 --nocapture
|
||||
```
|
||||
|
||||
若失败,记录第一错误、锁路径、持有者生命周期和最小复现,不先假设是 flaky;同时核对当前 `tauri-plugin-http` feature 调整是否存在直接因果,无因果则从本分支排除该候选改动。定向用例连续通过后再运行完整 Tauri suite 与 `npm run check:native-shells`,不能用单测通过替代完整门禁。
|
||||
|
||||
按从窄到宽顺序执行并记录 commit、命令、通过数和失败原因:
|
||||
|
||||
```bash
|
||||
npm run test -- apps/ai-game-creator-shell/tests/useProjectResourceCardPreviews.test.ts apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts
|
||||
npm run ai-game-creator-shell:typecheck
|
||||
cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --test-threads=1
|
||||
npm run ai-game-creator-shell:check
|
||||
npm run lint
|
||||
npm run test
|
||||
npm run build
|
||||
npm run check:content
|
||||
npm run check:server-rs-ddd
|
||||
npm run bgfilter-worker:smoke-test
|
||||
npm run check:production-health-patrol
|
||||
npm run check:production-api-release
|
||||
npm run check:production-api-deploy
|
||||
cargo test --locked --workspace --no-fail-fast --manifest-path server-rs/Cargo.toml
|
||||
cargo check --locked -p api-server --all-targets --manifest-path server-rs/Cargo.toml
|
||||
cargo check --locked -p spacetime-module --manifest-path server-rs/Cargo.toml
|
||||
npm run check:native-shells
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
```
|
||||
|
||||
若环境导致 `listen EPERM`、网络依赖下载或真实认证阻塞,必须把它与源码失败分开记录,并在允许的环境重跑对应 required gate;不得把“被中断/被沙箱阻塞”写成通过。
|
||||
|
||||
按用户补充要求,最终生产实现树完成上述全量 native 门禁后,若后续只修改测试夹具的确定性而不改变生产实现,可以只运行直接受影响的定向回归、格式与静态检查;T6 推送后的 required CI 仍必须在最终干净提交上全量通过。该窄例外不能用于替代生产代码、依赖图或运行时合同变化后的完整门禁。
|
||||
|
||||
退出条件:PRD §7.6 第 5 项对应的本地门禁全部通过,或仅剩必须由 Gitea runner/真实登录环境完成且已明确记录的外部门禁。
|
||||
|
||||
### T6:整理提交历史、推送并完成真实验收
|
||||
|
||||
#### 干净历史
|
||||
|
||||
1. 基于最终最新 `origin/master` 在隔离 worktree 构造干净分支,把最终资源管理树按逻辑提交移植过去;不使用破坏当前用户工作区的 reset/checkout。
|
||||
2. 最终 PR 历史不得包含 `d29fc1e0f`、`647820825`、`189e5bb59` 等不合规提交。
|
||||
3. 每个提交使用中文标题,标题后逐行写明修改内容。例如:
|
||||
|
||||
```text
|
||||
修复资源预览跨范围并发泄漏
|
||||
|
||||
增加原生全局有界预览读取与取消协议
|
||||
保持资源安全读取和终态缓存回收合同
|
||||
补齐跨范围取消与内存边界回归测试
|
||||
```
|
||||
|
||||
资源管理实现与合同、Runtime 测试夹具竞态、Native HTTP 的 OS 自动代理隔离必须分别成组;第三组已由同一源码树上的 A/B 门禁证明必要,不能再混入资源管理提交或省略因果记录。
|
||||
|
||||
- 第一组:资源管理实现、前端/Tauri/AppSurface 测试、PRD/技术方案、临时计划,以及 `decision-log.md` 的资源管理 hunk。
|
||||
- 第二组:`main_loop_tests.rs`、`autonomous_completion_contract_tests.rs` 与 `canvas_generation.rs` 的 Runtime / HTTP 测试夹具确定性修复。
|
||||
- 第三组:`Cargo.toml`、`Cargo.lock`、`check-native-shells.mjs` 与 `decision-log.md` 的 HTTP 边界 hunk。
|
||||
|
||||
`decision-log.md` 横跨第一、第三组,必须按 hunk 暂存;当前本地 checkpoint 不进入最终第四组,全部折叠进上述逻辑提交。
|
||||
|
||||
4. 推送前逐文件暂存,只包含任务代码/文档;`.env`、`.env.local`、tracking-outbox、日志、缓存和构建产物必须从暂存和提交中明确排除。tracking-outbox 只有取得用户明确授权后才允许定向清理。
|
||||
5. 对同一 PR 分支的历史替换只使用带精确旧 OID 的 `--force-with-lease=<ref>:<oid>`,且执行前必须获得用户明确授权并重新用 `git ls-remote` 核对远端 lease;lease 漂移立即停止,禁止退化为 `--force`,也禁止先普通推送当前不合规历史。
|
||||
|
||||
#### Gitea required CI
|
||||
|
||||
最终远端 commit 必须同时通过:
|
||||
|
||||
- `Project CI / Repository checks (pull_request)`
|
||||
- `Project CI / Frontend tests (pull_request)`
|
||||
- `Project CI / Backend tests (pull_request)`
|
||||
- `Project CI / Native shell tests (pull_request)`
|
||||
|
||||
任一 job 失败时,先记录失败 job、第一错误、根因和计划,再修复并重跑;不得只重跑到偶然绿色或跳过 required context。
|
||||
|
||||
#### 真实 `1280×800` 验收
|
||||
|
||||
只在已推送、CI 对应的当前 HEAD 上执行:
|
||||
|
||||
1. 使用真实已认证应用 WebView 或 Chromium,正常登录并打开真实本地项目工作台;不绕过认证,不伪造 Tauri,不把 Cookie/Token 写入证据。
|
||||
2. 把内容视口设为精确 `1280×800`,确认 `window.innerWidth === 1280`、`window.innerHeight === 800`。
|
||||
3. 至少记录以下值:
|
||||
- `window.innerWidth/innerHeight/devicePixelRatio`;
|
||||
- `document.documentElement.clientWidth/clientHeight/scrollWidth/scrollHeight`;
|
||||
- `document.body.clientWidth/clientHeight/scrollWidth/scrollHeight`;
|
||||
- `.game-project-workbench`、`.game-workbench-stage`、`.game-workbench-chat`、`.project-supervisor-composer`、`.game-agent-dock` 的 `getBoundingClientRect()`;
|
||||
- 每个关键矩形是否满足 `left/top >= 0` 且 `right <= innerWidth`、`bottom <= innerHeight`。
|
||||
4. 操作 dependency/type 切换、四分区内外滚动、分区高度/倍率、搜索过滤、文档详情、音频播放、图片/SVG/视频卡和退出详情;确认右侧对话、输入区与底部 Dock 全程在视口内,页面级横向/纵向均无溢出。
|
||||
5. 在 PR 普通评论记录最终 commit、平台/WebView 或 Chromium 版本、视口、全部测量值、关键操作、结果和必要截图。截图只作辅助,数值记录才是门禁证据。
|
||||
|
||||
退出条件:四个 required CI 全绿,真实测量评论已绑定当前 PR HEAD,且重新请求评审后取得有效批准。
|
||||
|
||||
### T7:删除本文并对最终 HEAD 再验收
|
||||
|
||||
只有以下条件全部满足时才删除本文:
|
||||
|
||||
- [ ] P1-A 的全局有界取消实现和全部回归已完成。
|
||||
- [ ] Native shell 阻塞已解决,全部本地门禁已通过。
|
||||
- [ ] 最新主线已经成为 PR 分支祖先且无未解决冲突。
|
||||
- [ ] 不合规历史已经从远端 PR 分支移除。
|
||||
- [ ] 四个 required CI 在删除前的代码完成 commit 上全部通过。
|
||||
- [ ] 删除前的代码完成 commit 已有真实 `1280×800` 证据和有效批准。
|
||||
- [ ] 没有剩余评审线程、未记录风险或待实现功能被伪称完成。
|
||||
|
||||
删除本文后使用单独中文提交,例如:
|
||||
|
||||
```text
|
||||
删除已完成的资源管理临时计划
|
||||
|
||||
移除已全部执行完成的阶段性计划文档
|
||||
保留权威 PRD 技术方案与长期决策记录
|
||||
```
|
||||
|
||||
由于删除会产生新的最终 HEAD,删除提交推送后必须再次:
|
||||
|
||||
1. 跑四个 required CI;
|
||||
2. 在同一真实环境复核并记录最终 commit 的 `1280×800` 数值(可以引用相同步骤,但必须写出新 commit 和实际结果);
|
||||
3. 确认 Gitea 没有因新提交撤销审批;如审批失效,重新请求并取得批准;
|
||||
4. 确认仓库最终树中不存在本文,且 `.env`、`.env.local`、tracking-outbox、日志、缓存和构建产物均未提交;tracking-outbox 仍保持排除,除非用户另行明确授权定向清理。
|
||||
|
||||
最终退出条件:计划文档已删除,删除后的最终 HEAD 通过全部 required CI、真实测量和有效审批,PR 无未解决阻塞项。
|
||||
|
||||
## 七、阶段状态表
|
||||
|
||||
| 阶段 | 状态 | 完成证据 |
|
||||
| --- | --- | --- |
|
||||
| T0 审计并冻结计划 | 已完成 | 2026-08-11 核对 Git、PRD、前端 Hook、Tauri 读取链路、现有测试与评审截图 |
|
||||
| T1 收口 merge 与工作区所有权 | 已完成 | `b2be9de2f` 已无冲突融合 `origin/master@3c457feb4c82d123e9bd311ae69aab0192f94e3c`,最新主线为 `HEAD` 祖先;`.env`、`.env.local` 与 tracking-outbox 始终排除 |
|
||||
| T2 修正文档合同 | 已完成 | PRD、技术方案、decision log 与 pitfalls 已明确逻辑 scope、进程级全局 3 槽、scope/request 身份、分块取消点、活动 registry 清理、`8192 / 1024` tombstone 有界淘汰和安全读取不变量;编码检查与 `git diff --check` 通过 |
|
||||
| T3 实现全局有界取消 | 已完成 | 新增全进程共享 3 permit scheduler、活动 scope/request registry、seen request `8192` 项硬上限,以及 cancelled scope `1024` 项非活动预算与活动钉住后收敛;permit 和 registration guard 由真实 blocking 任务持有,外层 future abort 不提前释放物理槽或切断取消;三条既有读取命令接入 `spawn_blocking` 和 64 KiB 分块取消,Hook 与 Supervisor 成果图片入口均使用唯一 scope/request 并在切换或卸载时取消;跨窗口 ID fallback 也使用随机熵避免同毫秒碰撞 |
|
||||
| T4 本轮资源预览回归 | 已完成 | Hook `10/10`、scheduler `8/8`、resource inspect `4/4`、AppSurface `368/368`、Supervisor 成果图片定向 `1/1`、图片 base64 取消与自动读取策略定向用例通过;新增 outer future abort、cancel 可达性、tombstone 收敛与精确取消错误反例。三条 main-loop 竞态和 pure-continue 用例定向通过,回执夹具同时等待父子 execution lane 收口;shell typecheck、ESLint、`cargo fmt --check`、编码检查、`git diff --check` 通过 |
|
||||
| T5 全量本地门禁 | 已完成 | 最新主线融合后,`npm run lint` 全部子门禁通过;`npm run test` 为 `206 files / 2812 passed / 8 skipped / 0 failed`;build、content、server-rs DDD 通过;server-rs workspace 中 api-server `894 passed / 6 ignored`、spacetime-module `230 passed / 1 ignored`、platform-llm `131 passed`、shared-contracts `72 passed`,两个 cargo check 通过;BgFilter worker `4/4`、production health patrol、release 与 deploy 四项 Frontend CI 门禁通过。A 组为显式关闭插件默认 feature 的 `eee777651` 最终生产实现树,完整 `npm run check:native-shells` 通过:Tauri 主测试 `1775 passed / 14 ignored`,integration `2/2 + 14/14`,agent-run smoke、两套 release build 与最终扫描通过。B 组只把 `tauri-plugin-http` 恢复为默认 feature 并运行同一命令:前端 `644/644`、Runtime Core `19/19`、platform-llm `134 passed / 1 ignored`、shared-contracts `11/11` 后,该次 Tauri 结果为 `1760 passed / 15 failed / 14 ignored`;首错把预期 transport/outcome-unknown 改写成代理 `502` 且出现 `Go-http-client/1.1`,其它失败不在本文逐项宣称根因。恢复 A 后 `Cargo.toml` 与 `Cargo.lock` 均重新等同 `eee777651`;Cargo feature 复核证明默认插件会把 `reqwest/system-proxy` 合并到全部原生客户端,故该独立边界保留并在 T6 单独成组。定向复验另发现非阻塞 listener 接受的请求 stream 可在 macOS 继承非阻塞状态,测试 helper 已在读取前显式恢复 blocking 并继续保留 10 秒超时;按用户允许的测试夹具窄验证边界,响应丢失与精确重放两条直接回归随后各 `1/1` 通过。tracking-outbox 最后一次已记录为 `512` 个并明确排除;最终 feature 图无 `system-proxy`,编码、Rustfmt、Node 语法与 `git diff --check` 通过 |
|
||||
| T6 干净历史、远端 CI、真实验收与批准 | 进行中 | 已确认 `origin/master@3c457feb4c82d123e9bd311ae69aab0192f94e3c` 未漂移;本地权威快照已提交,隔离分支 `codex/resource-management-clean` 已构造“资源管理实现与合同 / Runtime 与 HTTP 测试夹具竞态 / Native HTTP 系统代理隔离”三组中文提交,并通过 tree hash、逐字节 diff、无 merge、洁净工作树及 `.env*` / tracking-outbox 排除核对。远端覆盖推送尚未获用户授权,禁止执行;required CI、真实 `1280×800` 与有效审批尚未执行 |
|
||||
| T7 删除计划并复验最终 HEAD | 未开始 | 待补 |
|
||||
|
||||
## 八、最终完成定义
|
||||
|
||||
- 资源预览物理读取跨 scope、跨项目、跨 mode 和跨窗口始终全局 `<= 3`,旧 scope 可取消且不再累积 base64/Blob 瞬时内存。
|
||||
- 当前优先级、队列、LRU、Blob 回收、媒体暂停、失败重试、ABA epoch 和全部安全读取门禁无回归。
|
||||
- 最新主线已融合,现有 Godot 项目入口及其它主线能力无回归。
|
||||
- Native shell 单项和全量门禁均通过,四个 Gitea required job 均为最终 HEAD 的绿色结果。
|
||||
- 最终 HEAD 的真实、已认证 `1280×800` 数值证据可在 PR 评论复核,AppSurface/CSS 测试没有冒充该证据。
|
||||
- PR 历史中的提交标题和正文符合中文规范,远端分支改写经过授权且使用 `--force-with-lease`。
|
||||
- `.env`、`.env.local`、密钥、认证资料、tracking-outbox、日志、缓存和构建产物没有进入提交。
|
||||
- 本临时计划已按 T7 删除;最终 PR 仍保留同步后的权威 PRD、技术方案、decision log 和 pitfalls。
|
||||
@@ -1,5 +1,49 @@
|
||||
# 决策记录
|
||||
|
||||
## 2026-08-10 资源管理评审阻塞项按第二轮正式合同修复
|
||||
|
||||
- 背景:资源管理第一轮实现后,人工验证继续暴露 WebView 默认缩放、预览队列饥饿、过滤后媒体残留播放、外层滚动串 scope、超深依赖坐标越过 Rust 上限和暂时错误无法重试等问题。部分 PRD / 技术方案仍描述第一轮的中央媒体预览、单全局 Overlay 和统一 section scope,已经与第二轮代码及验收结论冲突。
|
||||
- 决策:以当前代码、2026-08-10 最新资源管理决定和第二轮人工验证为正式合同。`Ctrl/Cmd + wheel` 使用原生 `{ passive: false }` 委托监听;预览固定 `play > detail > visible` 且总队列上限 `96`,主动请求可替换最低优先级预取;终态缓存同时限制为 `48` 项与 `64 MiB`,IPC data URL 立即转成可撤销 Blob URL;项目 / mode 使用单调 scope epoch 隔离逻辑队列、缓存与异步回调,但物理读取由 Tauri 进程级唯一 `3` 槽管理器统一限制。每次挂载或 scope 切换生成不复用的 `scopeId`,每次 IPC 读取生成唯一 `requestId`;切换和卸载通过窄取消命令中止旧 scope 的排队 / 分块读取,取消任务不得进入 base64 / Blob 阶段。成功、失败和取消后清理活动 request / scope registry,同时保留有界 tombstone:seen request 上限 `8192`;非活动 cancelled scope 预算 `1024`,活动取消 scope 为防复活可临时钉住,结束后立即重新收敛。搜索隐藏立即暂停媒体;外层滚动按 `projectId + mode`、内层按 `projectId + mode + section` 隔离;前后端坐标共同限制为 `0..=1_000_000`,超深 dependency 只饱和显示列并保留原始深度;失败预览区分 transient / permanent,仅用户意图重试 transient。
|
||||
- 展示边界:图片、安全 SVG 和视频主体只在资源卡内展示;中央详情以元数据、同类型依赖和版本信息为主,文档正文与按意图读取的音频控件保留。每个分区拥有自己的 SVG plane;单端离屏显示边界继续线,两端离屏隐藏。`1280×800` 需要已认证 WebView / Chromium 的当前 HEAD 真实测量,CSS 正则测试不能冒充实测证据。
|
||||
- 兼容与边界:不修改 manifest、Rust 资源图、SpacetimeDB、sidecar schema、布局 CAS 或媒体安全读取门禁;不恢复资源卡拖动。过期文档在同一次变更内同步修正,避免实现通过测试但评审继续依据旧合同。
|
||||
- 验证方式:新增原生 wheel 取消、队列优先级 / 硬上限、Blob / 总字节 LRU、连续媒体浏览、挂起旧 scope / ABA epoch、原生全局活动峰值 `<= 3`、等待与分块取消、取消前无 base64 / Blob、活动 registry 清零及 `8192 / 1024` tombstone 有界淘汰、transient 重试、过滤后暂停、外层滚动 scope、坐标边界及 Rust 安全读取回归;继续运行 AppSurface、纯布局模型、Tauri 定向测试、编码检查和 `git diff --check`。真实 `1280×800` 是自动测试外的跨文件人工门禁,必须由当前 HEAD 的已认证 WebView / Chromium 实测并在 PR 评论中记录;记录缺失时不得声称阶段七完成。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-10 任务流退出画布连线且 dependency 层间距为精确引用留走线区
|
||||
|
||||
- 背景:人工验收确认灰色聚合 task-flow 虚线与橙色精确引用叠加后增加噪音,但用户在画布上只需要判断资源间的真实精确引用。原 dependency 自动布局沿用 type 模式 `16px` 行列间距,目标箭头预留 `10px` 后,相邻卡片间的橙线线身只剩约 `6–14px`;菱形 / 分叉簇又把单节点层与多节点层顶部对齐,形成同组一侧很短、另一侧明显过长。
|
||||
- 决策:Rust read model、详情数据和布局拓扑继续保留合法 task-flow;前端仍用流节点的 `O(S+T)` 成员关系参与同类型连通簇和中位数排序,但 `ResourceDependencyOverlay`、SVG marker、CSS 虚线和画布 `aria-describedby` 关系说明不再渲染或宣告 task-flow。画布关系层只显示同类型 `asset-reference` 的橙色实线箭头。
|
||||
- 间距与对齐:dependency 自动布局使用独立的 `48px` 列间距和 `40px` 行间距,type 模式继续使用 `16px`。每个相关簇以最大层行数确定高度;同簇资源较少的层增加稳定的半差偏移,在最大层高度中居中。Rust `dependencyDepth` 仍唯一决定横向层级,稳定 ID 仍决定平局,不压缩深度、不截断真实端点;已有历史手动坐标原样保留并继续优先占位。
|
||||
- 边界:本次只调整前端可派生自动坐标与显示关系集合,不修改 manifest、Rust resource graph、SpacetimeDB、sidecar schema、CAS、scope epoch、单写者 FIFO 或搜索不重排合同,不恢复资源卡拖动。自动 dependency 坐标会通过现有协调流程按新间距重派生;type 坐标不变。
|
||||
- 验证方式:布局纯模型覆盖 dependency / type 间距隔离、菱形窄层居中、手动坐标避让、确定性和 4096 项性能;Overlay / AppSurface 覆盖 task-flow 零 path / marker / 关系说明、橙色箭头最小可辨认走线区、跨分区、自环、滚动 / 缩放绑定和完整前端回归。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-10 依赖 SVG 改为分区 plane 所有并使用逻辑坐标路由
|
||||
|
||||
- 背景:全局 SVG 与四个独立 section plane 是不同滚动 / transform 坐标系。旧实现通过 `getBoundingClientRect + requestAnimationFrame + React state` 把卡片屏幕 rect 反投影到全局图层;触摸板缩放或连续滚动时,浏览器先在合成层移动卡片,异步测量后的线才追赶,因此会出现线与卡片分离、同一关系忽隐忽现。四 viewport 的并集 clipPath 还允许某区线段泄漏到另一分区。大贝塞尔控制柄进一步放大了视觉绕行。
|
||||
- 决策:固定四区各自把 `ResourceDependencyOverlay` 挂在拥有卡片的 `.game-resource-plane` 内。SVG 与卡片直接消费同一 sidecar 逻辑坐标、`180×128` 卡片常量、CSS scale 和 viewport scroll;浏览器原生 transform / overflow 负责机械同步和分区裁剪,主路径不再依赖 DOM 屏幕坐标测量。每区只用一个 `ResizeObserver` 与 RAF 维护逻辑 viewport,四区合计最多四个;一端离屏时对称输出 incoming / outgoing 边界继续线,两端离屏时隐藏。
|
||||
- 路由:精确引用在同行 / 同列时使用直线,需要转向时使用正交线段与最大 `10px` 二次曲线小圆角;仅自引用保留卡片外侧贝塞尔闭环。同侧稳定端口和同类型过滤不变。该条最初保留的灰色聚合 task-flow 虚线已被上方“任务流退出画布连线”决定替代,task-flow 只保留布局超边语义。
|
||||
- 边界:只改变前端派生 SVG 的所有权、显示裁剪和路径形状;Rust resource graph、`dependencyDepths`、producer 截断降级、manifest、SpacetimeDB、dependency / type sidecar、CAS、scope epoch、单写者 FIFO、搜索不重排和历史手动坐标均不变。没有恢复卡片拖动。
|
||||
- 验证方式:SVG 定向回归覆盖直线 / 小圆角、橙色 marker、自环、同侧端口、task-flow 零渲染、缩放同 plane、incoming / outgoing、两端离屏、四区互不串线及单区滚动隔离;ProjectDevelopment / AppSurface 继续覆盖模式和项目销毁、媒体卡、分区缩放与页面骨架。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-10 资源画布只展示同类型关系且详情以信息为主
|
||||
|
||||
- 背景:人工验收发现跨分类精确引用在四个独立 viewport 中只能显示成断裂长线;同一美术卡多入 / 多出时,多条精确引用与聚合任务流复用卡片中心端口,线段重叠;点击美术卡后大图占满中央视窗,路径、来源和依赖信息被挤到首屏之外。
|
||||
- 决策:Rust 资源图、`dependencyDepths`、manifest 引用和 producer 降级语义保持不变;前端布局拓扑、无障碍关系说明与 SVG 只消费两端同属 `document / version / art / audio` 之一的关系。跨分类 reference edge 与跨分类-only task-flow 不形成布局簇、边界偏置或连线。相同分区内,同一卡片同侧的精确边按对端坐标与稳定边 ID 有界分配独立端口;横向层级仍优先左右连接,同列或空间不足才上下连接。源端可见而目标离屏时保留关系 DOM,但画面只显示边界短继续箭头。该条中的 task-flow 显示口径已由上方最新决定替代为仅参与布局。
|
||||
- 详情合同:点击资源卡后,元数据、Rust 权威依赖层级、同类型精确上下游、同类型任务流和版本信息位于内容首部;美术图片 / 视频不在详情重复读取或放大,仍在列表卡本体中展示。文档正文和按播放意图读取的音频控制位于元数据之后。
|
||||
- 兼容与复杂度:不修改 Rust read model、manifest、SpacetimeDB 或 sidecar schema;历史手动坐标、type 布局、搜索不重排、分区高度 / 倍率及单写者 FIFO 不变。过滤与端口分配为线性收集加稳定排序,task-flow 继续按超边成员处理,不构造资源笛卡尔积。
|
||||
- 验证方式:覆盖跨分类精确引用不渲染 / 不聚类、跨分类-only task-flow 不聚类、全部 task-flow 不绘线、同侧多边端口确定性、离屏长线收敛、详情无大图 / 视频且元数据与依赖字段可见,并继续运行 4096 布局、媒体卡、分区缩放与 AppSurface 回归。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-10 分区内容缩放与持续关系线替代阶段二旧显示口径
|
||||
|
||||
- 背景:阶段二最初把“缩放”只解释为分区可视高度,并让 SVG 只为完整位于 viewport 的卡片建立端点。人工验收发现触摸板无法缩放内容、精确引用箭头在裁剪边界不稳定、纵向长线容易被截断,以及同一关系随分区滚动时有时无。
|
||||
- 决策:保留已有分区高度,同时新增互不替代的 `50%..200%` 内容倍率;按钮、触摸板 `Ctrl/Cmd + wheel` 和 WebKit 捏合共用确定性 clamp,普通双指仍滚动。高度与倍率都按 `projectId + mode + section` 留在会话态,不进入 sidecar。SVG 与卡片共用分区 plane 和 CSS scale;合法关系不再因端点没有“完整可见”而卸载,一端离屏时在可见区边界显示同语义继续箭头。精确引用恢复橙色实线实心箭头,不同层优先左右端口横连,只有同列或横向间距不足时才上下端口纵连;自环继续在卡片外侧。
|
||||
- 替代关系:本条替代下方 2026-08-07 阶段二中“缩放只等于可视高度”和“只为完整可见卡片建立端点”的显示口径;其中曾采用的全局 SVG、四 viewport 联合 clip 和单全局 Observer 又由上方“依赖 SVG 改为分区 plane 所有”决定替代。高度模型、四分区、会话隔离、内外滚动和无布局 CAS 等其它决定继续有效。阶段一媒体卡、阶段三确定性聚类、Rust `dependencyDepths`、producer 截断降级、历史手动坐标与 type sidecar 均不变。
|
||||
- 验证方式:纯倍率模型覆盖按钮 / wheel 边界;AppSurface 覆盖项目、mode、section 隔离、普通 wheel、Ctrl wheel、WebKit gesture 与零布局写入;SVG 覆盖同 plane 倍率、橙色 marker、直线 / 小圆角横纵路由、自环间隙、双向边界继续线、分区原生裁剪、每区单 Observer、task-flow 零渲染和 4096 精确关系有界输出。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-07 game-chat 使用单主路径按需补齐美术
|
||||
|
||||
- 背景:原 game-chat 把 Supervisor 的意图判断之后又硬接为 `design-director / code-director / art-* / code-prototype / preview-*` 固定图。`code-director` 代替程序主 Agent 判断素材缺口,会让“把现有美术资源应用到游戏中”被错误翻译成先生成美术,且美术回执无法天然回到同一个代码 Run 完成接入。
|
||||
@@ -8,6 +52,30 @@
|
||||
- 验证方式:覆盖路由后只启动 `code-prototype`、既有素材零美术委派、精确缺口才允许单个对应 child、child 的 `game/**`、memory 和 manifest 写入拒绝而 `assets/**` 写入允许、回执恢复同一主 Run,以及主 Agent 的接入、静态 smoke 与双视口试玩;追加真实 scheduler 对旧 canonical task 的同 Run 恢复、旧固定美术 child 及其历史 isolated 后代对 Canvas/memory/manifest/project scope 零 mutation、伪只读 `agent.run_status` 阻断、嵌套美术 child 硬截止对账回执;另跑完整 DAG 非回归。
|
||||
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-07 资源管理阶段三以确定性分层聚类收口
|
||||
|
||||
- 背景:阶段一已经统一 `180×128` 资源卡本体与媒体播放,阶段二已经把四个固定资源分区改为独立可视高度和内部滚动;原 dependency 自动布局仍只按 Rust `dependencyDepth` 横向分层、同层按名称排布,容易让已有合法关系形成长线和交叉。
|
||||
- 决策:`dependencyDepths` 继续是唯一横向层级真相,前端只消费现有 Rust resource graph 的合法 `asset-reference` 与未截断 producer mapping 下的聚合 `task-flow`。每个固定分区迭代建立弱连通分量,task-flow 以流节点连接成员而非展开资源笛卡尔积;相关簇按最小深度、最小稳定资源 ID 排列,孤立资源统一紧凑置后。簇内固定执行两轮左右中位数扫描,精确引用使用上下游相邻层 rank,task-flow 使用另一端成员的中位 rank,平局回退稳定资源 ID。原“跨分区边界偏好”已由 2026-08-10 同类型关系决定替代,跨分类关系不再进入前端布局。搜索仍只过滤可见卡片 / SVG,不参与布局;环继续依赖 Rust SCC 深度,成员可连续排列、环后资源继续前进。
|
||||
- 兼容与复杂度:只重派生 `manuallyPlaced=false` 的 dependency 坐标,历史手动位置原样保留且先占用避让;type 坐标不改。资源协调签名加入由稳定 ID 的规范端点 / task-flow 成员序列导出的固定大小摘要,因此邻接改变即使深度不变也会写入同一 sidecar FIFO / CAS,不增 schema、manifest、SpacetimeDB 或第二存储。遍历为迭代式 `O(V + E)`,排序扫描轮数固定,流成员不构成全量配对。
|
||||
- 三阶段状态:阶段一资源卡本体与单媒体播放、阶段二分区可视高度 / 内外滚动和阶段三依赖聚类均已实施;它们共用既有卡片尺寸、SVG 几何、sidecar、scope epoch 与单写者 FIFO,均不恢复卡片拖动。
|
||||
- 验证方式:布局模型与 Hook 覆盖同区关系簇、独立簇与孤立项、多入多出 / 聚合流、环 / 自环、跨区、不变输入确定性、手动坐标避让、type 不变、邻接签名更新与 4096 项性能;依赖图 / SVG、ProjectDevelopment / AppSurface 和阶段一 / 二回归继续作为整体验收。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-07 资源管理阶段二以分区可视高度完成独立缩放
|
||||
|
||||
- 背景:阶段一已把 `180×128` 资源卡本体、预览调度与单媒体播放统一到 dependency / type 两种布局。“分区缩放”旧文档一度定义为卡片 `50%..200%`,会破坏本体卡尺寸、媒体比例与依赖端点。
|
||||
- 决策:阶段二固定为调整四个资源分区的可视高度,而非任何卡片、媒体、文字或浏览器缩放。会话内存 Map 以 `projectId + mode + section` 隔离状态,不写 localStorage / sessionStorage、manifest 或 `game-creator-resource-layout.v1`,也不发布局 CAS。上限为中央资源画布的当前可用高度,窗口变小时对当前项目两种 mode 的既有值同步且永久夹取;不在放大窗口时回弹旧超界值。
|
||||
- 布局与图层:分区是正常文档流 grid item,标题与内部 viewport 分离;仅 viewport 内滚动超出内容,外层画布仍在分区之间滚动。本条原先采用的单全局 SVG、单 Observer、完整可见端点和四 viewport 合集裁线已被 2026-08-10 的分区 plane 自有 SVG 决定替代;分区正常文档流和内外滚动合同继续有效。
|
||||
- 验证:纯高度模型回归锁定分区键隔离、最小一排卡片与中央画布上限;AppSurface 覆盖四区独立、两 mode / 两项目隔离、详情与内外滚动恢复、resize 夹取与零 CAS;SVG 回归覆盖单 observer、RAF 合帧、viewport 裁线与卸载清理。
|
||||
|
||||
## 2026-08-07 资源管理三项改造串行落地且先完成资源卡本体化
|
||||
|
||||
- 背景:固定四区资源投影、dependency / type 布局、依赖 SVG、中央详情和受控文档 / 媒体读取已落地,但列表卡片仍只显示图标、名称、来源与路径,不利于直接辨认资源主体。后续还需要分区缩放和依赖布局聚类,三项不能各自发明尺寸、几何或资源真相。
|
||||
- 决策:按“本体卡 -> 分区缩放 -> 依赖聚类”串行实施。阶段一让两种布局共用同一个卡片与画布级预览 Controller,用现有受控 Tauri 命令按可见性有界读取图片、视频首帧和文档摘要,音频只在用户播放后读取;卡上详细文本后置到中央详情,详情与播放使用合法同级按钮。同时播放上限为一,项目 / mode / 详情 / 运行视图 / 资源身份变化时统一收口媒体状态。
|
||||
- 后续约束:阶段二只调整分区 viewport 高度,保持卡片 rect 和逻辑坐标不变;SVG 对每个 viewport 做可见端点与裁线。阶段三的依赖聚类只从当前 Rust 只读图按分区派生稳定弱连通组,搜索不参与坐标派生。两者都不修改 manifest、SpacetimeDB 或 `game-creator-resource-layout.v1`,不恢复资源卡拖动。
|
||||
- 验证方式:AppSurface 覆盖两种布局的本体卡、图片 / 视频 / 音频 / 文档 / 版本、合法点击与键盘语义、单媒体播放、可见性懒加载、去重 / 并发 / LRU、迟到丢弃、失败占位、隐藏字段搜索和详情信息保留;布局纯模型与 SVG 回归继续锁定同一卡片尺寸。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 2026-08-03 资源管理阶段七以完整 CI 与可重复界面合同收口
|
||||
|
||||
- 背景:飞书资源管理需求的阶段零至阶段六已经分别完成资源卡禁拖、固定资源投影、中央聚焦、安全文档 / 媒体预览、依赖深度与正式版本只读模型;最后需要统一复核需求边界并用当前主分支完整门禁排除集成回归。
|
||||
@@ -154,7 +222,7 @@
|
||||
> 状态:其中资源卡 Pointer Move 拖动预览与局部更新验收已由 2026-08-03 mentor 最新决定暂缓;只读拓扑、SVG 派生展示、搜索与选择高亮合同继续生效。
|
||||
|
||||
- 背景:资源画布已有 dependency / type 双模式坐标与本地 CAS sidecar,但 dependency 模式尚未把当前 manifest 中可证明的资源引用和任务流转可视化;关系图不能反向污染布局持久化或建立第二套资源真相。
|
||||
- 决策:dependency 模式由 Tauri Rust 只读命令从当前 manifest、资源卡身份和有界 `.agent/agent.db` 审计构建稳定 `ProjectResourceGraph` read model,前端只归一化 DTO、测量卡片坐标并用原生 SVG 渲染。资产 `source.referenceResourceIds` 只在唯一匹配另一资产 `source.resourceId` 后形成橙色实线;任务依赖按任务对聚合为灰色虚线主线与两端分支,禁止资源笛卡尔积。Rust 以迭代式强连通分量分析识别资源环和完整任务 DAG 环,并返回资源局部连接索引。
|
||||
- 决策:dependency 模式由 Tauri Rust 只读命令从当前 manifest、资源卡身份和有界 `.agent/agent.db` 审计构建稳定 `ProjectResourceGraph` read model,前端只归一化 DTO、测量卡片坐标并用原生 SVG 渲染。资产 `source.referenceResourceIds` 只在唯一匹配另一资产 `source.resourceId` 后形成橙色实线;任务依赖按任务对聚合,禁止资源笛卡尔积。本条最初把聚合任务流绘制为灰色虚线主线与分支,该显示口径已由 2026-08-10 最新决定替代为只参与布局、不进入 SVG。Rust 以迭代式强连通分量分析识别资源环和完整任务 DAG 环,并返回资源局部连接索引。
|
||||
- 任务身份:External Editor 响应中的 `source.taskId` 是平台生成任务 ID,不等于本地 manifest task ID,禁止据此分配 producer。画布资产只接受 `agent.runtime.canvas.asset_generate` 审计中经当前 manifest task 校验的 `assetId -> agentId`;证据缺失、冲突或已超出有界读取窗口时不生成对应 task flow。任务产物与 Agent 回执继续使用自身已有的 manifest task 身份。
|
||||
- 生命周期与边界:Pointer Move 先用 `requestAnimationFrame` 合帧;基础 positions 与拖动预览分离,SVG 静态拓扑保持复用,每帧只按局部索引更新拖动资源关联的 reference edge 和 task flow。`ResizeObserver` 在单个图层生命周期只创建一次。type 模式不挂载图层;切换 mode、项目或卸载工作台时销毁 SVG、Observer 和窗口监听。SVG 统一 `pointer-events: none`;path、marker、图结构和 section 原点从不持久化。
|
||||
- 数据边界:本切片只新增 Tauri Rust 只读 read model,不修改 layout sidecar、`resourceCanvasLayoutModel.ts`、manifest、api-server、SpacetimeDB schema 或生成绑定,也不引入第三方图表库。dependency section 只在显示层额外预留 `64px` 右侧视觉 gutter,卡片坐标和持久化布局不变。
|
||||
|
||||
@@ -14,6 +14,30 @@
|
||||
- 关联:相关文件、文档、提交或 Issue
|
||||
```
|
||||
|
||||
## 资源管理第二轮修复后不能继续用第一轮文档和弱测试作为验收合同
|
||||
|
||||
- 现象:代码已经改成分区内 SVG plane 和卡内媒体,文档仍要求单全局 Overlay 或中央大图;CSS 正则和浅层 AppSurface 测试保持绿色,但真实 Tauri WebView 仍会默认缩放、主动预览请求饥饿、过滤后媒体继续播放或超深布局反复提交非法坐标。
|
||||
- 原因:第一轮编码时同步编写的 PRD / 技术方案被后续代码修复绕过,第二轮只改实现和局部测试,没有把新验证结论回写正式合同。React 合成 wheel 事件、单例滚动 ref、只按数量限制的 base64 缓存、跨 scope 共用的活动读取计数、无优先级有界队列和前后端不同坐标边界又分别跨越浏览器、会话状态与 IPC 边界,浅层文本断言无法证明运行时行为。
|
||||
- 处理:每轮验证后按“当前代码 + 最新决策 + 真实运行证据”同步修订 PRD、技术方案、决策记录和回归测试。原生可取消事件要直接断言 `defaultPrevented`;队列验证主动请求替换预取;预览 data URL 只作临时传输并转为可撤销 Blob URL,LRU 同时限制项目数和总字节;项目 / mode 切换推进 epoch,旧 `finally` 不得扣减新 scope;媒体状态同时核对可见集合;滚动分别按内外 scope 保存;坐标合同由共享 TS 与 Rust 同边界维护;暂时 / 永久错误在模型中显式分类。
|
||||
- 验证:运行定向 hook / AppSurface / 纯布局 / Rust 边界测试,再执行类型检查、编码检查和 `git diff --check`。涉及窗口几何和 WebView 行为时,还必须在当前 HEAD 的已认证 `1280×800` WebView / Chromium 中记录 `window`、document、body 和关键容器测量;CSS 正则只保留为静态防回退门。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/src/view/project-development/useProjectResourceCardPreviews.ts`、`apps/ai-game-creator-shell/src/view/project-development/resourceCanvasLayoutModel.ts`、`apps/ai-game-creator-shell/src-tauri/src/project/resource_layout.rs`、`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`。
|
||||
|
||||
## 预览读取不能像放弃旧 CAS 回调一样直接重置并发槽
|
||||
|
||||
- 现象:A 项目的 3 个预览 IPC 仍挂起时切到 B,前端把活动计数归零并立即再发 3 个;界面不会被 A 的迟到结果污染,但原生同时保留 6 个读取。连续项目 / mode / Hook / 窗口切换会继续叠加整文件缓冲、base64 临时字符串和 WebView IPC 载荷,绕过 `64 MiB` 终态缓存预算。
|
||||
- 原因:布局 CAS 已经发出后只能依靠 revision / 系统锁仲裁,前端放弃回调是正确语义;只读预览却有明确的物理内存和文件读取成本,可以协作取消。把 scope epoch 的逻辑隔离误当成底层取消,又让每个 scope 自行拥有 3 个物理槽,实际并发就不再全局有界。
|
||||
- 处理:前端继续用 epoch、优先级队列、LRU 和完整资源身份隔离展示状态,但每次挂载 / scope 变化生成不复用的 `scopeId`,每次 IPC 生成唯一 `requestId`。三个安全读取命令共用 Tauri 进程级 3 permit 管理器;切换和卸载调用窄 scope 取消命令,等待 permit 与固定块读取都检查取消。permit 和 request/scope 清理 guard 必须移入真正的 `spawn_blocking` 读取闭包;WebView 卸载或调用方 abort 只会丢弃外层等待,不能让仍在运行的 blocking 读取提前释放物理槽或从取消 registry 消失。取消任务在 base64 前退出,所有终态清理活动 request / scope registry。seen request tombstone 最多保留 `8192` 项;非活动 cancelled scope tombstone 的预算为 `1024` 项,活动取消 scope 为防复活必须临时钉住并在结束后重新收敛。不得为了追求整个 registry 字面清零而删除防重放 / 防复活记录,也不得让已经结束的 scope 长期占用预算外记录。不得放宽原有项目边界、登记、权限、链接、签名、大小、漂移或安全 SVG 门禁。
|
||||
- 验证:先让旧 scope 占满 3 个 permit,再连续执行 A → B → A、mode、Hook 和多窗口切换;断言原生活动峰值始终 `<= 3`,旧等待任务不打开文件,旧在途任务在最近检查点释放,新 scope 随后启动,取消任务不编码 data URL / 不创建 Blob URL,最终活动 request / scope registry 为零。阻塞读取进入后主动 abort 外层 future,必须证明旧 blocking 任务仍占 permit、仍可按 scope 取消且新请求不能提前启动。另分别证明 seen request `8192` 项的硬上限、cancelled scope 超预算时不淘汰活动记录,以及任一活动 scope 结束后非活动 tombstone 立即收敛到 `1024` 项预算。前端再断言旧 `then / catch / finally` 和取消 ACK 均不写新 scope;内部取消类别只允许精确匹配,不能因真实错误正文恰好包含该标识而静默吞错。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/useProjectResourceCardPreviews.ts`、`apps/ai-game-creator-shell/src-tauri/src/resource_preview_scheduler.rs`、`apps/ai-game-creator-shell/src-tauri/src/resource_inspect.rs`、`apps/ai-game-creator-shell/src-tauri/src/image_inspect.rs`。
|
||||
|
||||
## 依赖线与资源卡不在同一 transform 层时会在滚动和缩放中分离
|
||||
|
||||
- 现象:静止时依赖线似乎对齐,触摸板缩放或连续滚动后线段会追赶、漂移或忽隐忽现;某一资源分区的长线还可能出现在相邻分区。
|
||||
- 原因:资源卡位于各自可滚动、可缩放的 section plane,全局 SVG 却是外层兄弟节点;通过 `getBoundingClientRect`、RAF 和 React state 重建屏幕端点无法与浏览器合成层 transform 原子同步。把四个 viewport 做成一个 clipPath 并集也不具备“每条线属于哪个分区”的所有权语义。
|
||||
- 处理:让每个固定分区在自己的 `.game-resource-plane` 中拥有独立 SVG,卡片与路径都直接使用布局逻辑坐标并共享父级 CSS scale / 原生 scroll;viewport 原生 overflow 负责本区裁剪。DOM 测量只换算本区逻辑 viewport,用于完整路径、incoming / outgoing 继续线和两端离屏隐藏,不参与端点身份或主路径坐标。每区 observer 和 RAF 各至多一个,卸载时清理。
|
||||
- 验证:同时挂载至少两个分区和各自同类型关系,断言每条边只存在于对应分区 SVG;只滚动其中一分区,另一分区的逻辑 viewport 与 path 不变。另覆盖缩放后 SVG / 卡片仍在同一 plane、双向离屏继续线、两端离屏隐藏、marker、自环以及 mode / 项目切换清理。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/ResourceDependencyOverlay.tsx`、`apps/ai-game-creator-shell/tests/ResourceDependencyOverlay.test.ts`、`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`。
|
||||
|
||||
## 派生 Debug 会让完整配置经应用状态递归进入日志
|
||||
|
||||
- 现象:配置和状态当前没有直接日志调用,但新增一行 `debug!(?state, ...)` 或 `format!("{config:?}")` 就能把 JWT、后台口令、支付私钥、OSS / provider key 与 SpacetimeDB token 一次性写入日志及 OTel 留存面。
|
||||
@@ -71,6 +95,14 @@
|
||||
- 验证:用真实 scheduler 恢复确定性 v1 主 Run;把旧 scheduler 美术 child 置为 running,断言 `canvas.asset_generate`、`memory.write`、`task.create`、`task.update` 和 `agent.run_status` 均被拒绝;再持久化其历史 `game/**` writeScope isolated 后代,断言恢复执行写操作仍失败且项目未变。对当前合法美术 child 同样验证 memory/manifest 零写入,再让它带在途外部生成命中硬截止,断言状态进入 `needs-reconciliation` 且 pending/batch/外部生成账本原样保留。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs`、`runtime_driver/game_chat_fast_path.rs`、`runtime_driver/main_loop.rs`、`runtime_tools/file_ops.rs`。
|
||||
|
||||
## 委派幂等与 child 写入测试不能和真实后台 worker 抢状态
|
||||
|
||||
- 现象:测试刚建立 static delivery,自动 parent-wake 就抢先恢复并终结父 Run,使随后同 action 重放被“当前 durable task 仍为 running”拒绝;受限美术 child 测试也可能在后台 worker 抢先终态后,让本应允许的 `assets/**` 写入误报 verification failure。
|
||||
- 原因:测试 fixture 同时手工推进 journal/manifest,又允许真实后台 future 执行同一父子 Run;单测运行时序决定谁最后写入。若为让测试通过而把 active durable task 门禁整体移动到 existing-child 分支之后,该分支仍可能补建 delivery 或投影 Ready,反而允许终态/过期父 Run 发生修复性写入。
|
||||
- 处理:保留生产门禁顺序,父 Run 终态后的迟到 delivery 继续只允许 suppressed。需要断言回执、claim 与同 action 重放时,测试持有父 Agent execution lane,断言结束后释放再执行 wake;直接测试 child 写工具时持有目标 Agent lane,再把 child 持久推进到 `running`。所有 lane 均由 RAII 释放,不能依赖后台 future 的调度时机。
|
||||
- 验证:覆盖父 Run active 时 claimed delivery 的同 action 重放返回 existing、不同 action 的重复缺口仍被拒绝、父终态后的新委派/迟到 delivery 继续失败关闭或 suppressed、合法运行中 child 只可写 `assets/**`。macOS 直接拼接 `std::env::temp_dir()` 的仓库安全测试还应先规范化临时根,避免 `/var -> /private/var` 被误当成项目内符号链接。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/delegation.rs`、`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/main_loop_tests.rs`、`apps/ai-game-creator-shell/src-tauri/src/repository_context.rs`。
|
||||
|
||||
## 执行锁移交给未确认启动的异步 future 会制造永久 queued
|
||||
|
||||
- 现象:父 Supervisor 与 Runner 一直显示运行中、heartbeat 正常,专业 Agent 已有 `background_task.queued` 和 `autonomous_ready_task.scheduled`,对应执行锁也被 Runner 持有,但该 child 永远没有 running journal、`turn.started` 或后续 Runtime event;其它同批 Agent 可能已经完成。
|
||||
@@ -111,6 +143,30 @@
|
||||
- 验证:单测覆盖 CAS 边界(满了返回失败且计数不越界、上限为 0 时任何进入都失败),并由独立用例覆盖 guard 离开作用域后的计数归还。预算耗尽路径只断言 `504`,不得通过另一个测试也会修改的进程级 static before/after 来推断“未入队”,也不得用串行锁或 `--test-threads=1` 掩盖隔离问题。
|
||||
- 关联:`server-rs/crates/api-server/src/editor_project.rs`(`try_enter_bounded_queue`、`EditorPixelArtSnapQueueGuard`)。
|
||||
|
||||
## dependency 不能复用 type 的紧凑间距或让窄层始终顶部对齐
|
||||
|
||||
- 现象:相邻卡片间的橙色引用只剩一个箭头,看起来像长度异常;同一个菱形 / 分叉关系中,上半组线很短而下半组线绕很远。
|
||||
- 原因:type 模式的 `16px` 紧凑行列间距不足以同时容纳 marker 安全距离和可辨认线身;分层布局若只按每层 index 从簇顶向下排,单节点层无法与多节点层的垂直中心对齐。
|
||||
- 处理:dependency 自动坐标使用独立 `48px` 列间距和 `40px` 行间距,type 继续使用 `16px`。相关簇记录最大层行数,每层起始 y 增加 `(maxRows - layerRows) * dependencySlotHeight / 2` 的确定性偏移;平局仍用稳定资源 ID,手动坐标仍原样占位。不能通过裁短长线、偏移真实端点或压缩 Rust depth 伪造一致长度。
|
||||
- 验证:纯模型锁定 type / dependency 间距隔离、四节点菱形的首尾单节点层居中、同层稳定顺序、历史手动坐标和 4096 项性能;Overlay 使用相邻 dependency 槽位证明箭头前保留可辨认线身。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/resourceCanvasLayoutModel.ts`、`ResourceDependencyOverlay.tsx`、`resourceCanvasLayoutModel.test.ts`。
|
||||
|
||||
## 依赖聚类不能把聚合 task-flow 展开为资源两两边
|
||||
|
||||
- 现象:为了让 task-flow 的两端资源靠近,若对每个 source × target 构造边,资源多的任务流会迅速放大内存、排序工作和虚假关系;同一图输入还可能随着成员枚举顺序出现不稳定排列。
|
||||
- 原因:task-flow 的业务语义是任务对的聚合流,不是资源间的完整笛卡尔依赖;dependency depth 也已经由 Rust SCC read model 权威计算,前端不能用布局边重建业务方向。
|
||||
- 处理:布局分组把每条 flow 作为一个临时流节点,仅与其 source / target 成员相连;中位数扫描读取流另一端成员现有 rank 的中位值。遍历保持迭代式,扫描轮数固定,所有初始序和最终平局都以稳定资源 ID 收口。用于自动重派生的拓扑签名先按固定分类过滤并规范化稳定 ID,再生成固定大小摘要,不能把显示名、卡片大小或浏览器几何加入签名。跨分类 reference 与跨分类-only task-flow 不进入前端布局;所有 task-flow 都不进入 SVG 或画布关系说明。`producerMappingTruncated` 时继续只消费现有同类型精确引用,不重建 task-flow。
|
||||
- 验证:纯模型覆盖多入、多出、聚合 task-flow、环、4096 链和重复输入坐标一致;Hook 覆盖仅改邻接、深度不变仍重派生自动坐标。不得把搜索后的可见集传入聚类。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/resourceCanvasLayoutModel.ts`、`useProjectResourceCanvasLayout.ts`、`resourceDependencyGraphModel.ts`。
|
||||
|
||||
## 四分区 SVG 不能把跨类型业务关系当成可绘制几何
|
||||
|
||||
- 现象:跨分类资源位于彼此独立滚动和裁剪的 viewport;若仍绘制一条全局 SVG 路径,只会在两个分区中留下没有完整上下文的断线,滚动时还会看似随机出现或消失。同一卡片多边若都锚在中心点,也会让合法的同类型线叠成一束。
|
||||
- 原因:Rust read model 的业务关系范围大于资源管理画布的展示合同;四分区视图没有跨标题栏的合法连线走廊。几何层直接遍历全部 reference edge 等于把业务真相误当成全部可视关系;单中心端口又忽略了边的稳定身份与对端顺序。
|
||||
- 处理:保留 Rust 图与权威深度;布局拓扑按资源分类过滤 reference 和 task-flow 超边切片,关系说明与 SVG 则只消费同类型精确引用。相同分区内按对端坐标、稳定边 ID 为同侧精确边分配有界端口。不要通过改变端点、隐藏同类型合法精确边或生成资源笛卡尔积来换取整洁。
|
||||
- 验证:同时覆盖跨分类精确引用与跨分类-only flow 不聚类 / 不绘制、全部 task-flow 零 SVG / 零画布关系说明、同侧多边端口不重合且重复输入路径一致、同类环 / 自环和 4096 项回归。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`ResourceDependencyOverlay.tsx`、`resourceCanvasLayoutModel.ts`。
|
||||
|
||||
## Linux 生产脚本门禁不能假设本地也是 GNU userland
|
||||
|
||||
- 现象:macOS 本地运行维护页、生产 API 部署和 Rust 产物门禁时,依次出现 `mv: illegal option -- T`、`mapfile: command not found`、`/usr/bin/cp` / `/usr/bin/chmod` 不存在,以及 `.rlib` 明明含有 `.o` 却报告“没有可扫描成员”;安全修复计划还会把 `/var/folders` 到 `/private/var/folders` 的系统别名误判为用户符号链接。
|
||||
@@ -119,12 +175,20 @@
|
||||
- 验证:运行 `npm run check:maintenance-page`、`npm run check:production-api-deploy`、`npm run check:server-rs-ddd`、`npm run test -- scripts/spacetime-repair-editor-canvas-resources.test.ts`,并在 Linux CI 保留同一生产脚本语义。
|
||||
- 关联:`scripts/deploy/maintenance-on.sh`、`scripts/check-maintenance-page.mjs`、`scripts/check-production-api-deploy.mjs`、`scripts/deploy/production-api-deploy.sh`、`scripts/check-module-runtime-artifact.mjs`、`scripts/spacetime-repair-editor-canvas-resources.mjs`。
|
||||
|
||||
## 分区内部滚动不能只重测分区原点
|
||||
|
||||
- 现象:若滚动时只重测分区原点,线会停在旧位置或穿过标题栏;若进一步把“卡片完整位于 viewport”当作关系挂载条件,同一合法关系会在卡片刚触边时突然消失、滚回又出现,箭头也可能恰好落在 clip 外而只剩一截线。
|
||||
- 原因:全局 SVG 与 section plane 不共享 transform / scroll,`getBoundingClientRect + RAF + state` 重建屏幕端点只能异步追赶浏览器合成层;卡片可见性又是显示裁剪状态,不是关系身份。SVG marker 贴卡或贴裁剪边界时还可能只剩主 path。
|
||||
- 处理:每个 section plane 自己持有 SVG,让路径和卡片直接使用同一逻辑坐标与父级 scale / scroll;每区只以一个 Observer 和 RAF 维护逻辑 viewport。精确引用源端或目标端单独离屏时分别绘制 outgoing / incoming 边界继续线,两端离屏才隐藏;目标锚点预留固定箭头间隙,marker 使用 `userSpaceOnUse` 且允许 overflow;自环整体外移避免箭头压卡。搜索隐藏端点仍属于业务可见性过滤,不能与 viewport 裁剪混用。
|
||||
- 验证:覆盖同帧多次 scroll 只调度一次 RAF、部分离屏时 outgoing / incoming 正确切换、两端离屏隐藏、缩放后路径与卡片仍处于同一 plane、箭头可见、分区互不串线,以及每区单 observer 与卸载清理。搜索隐藏任一精确端点时整条橙线隐藏;task-flow 始终不渲染。
|
||||
- 关联:`apps/ai-game-creator-shell/src/view/project-development/ResourceDependencyOverlay.tsx`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`。
|
||||
|
||||
## External Editor taskId 不能当作本地 manifest taskId
|
||||
|
||||
- 现象:画布资产之间已有橙色精确引用线,但依赖任务之间没有灰色 task flow;测试用 `design-foundation` 之类字符串时正常,真实生成返回 `task-1` 后失败。
|
||||
- 现象:Rust read model、资源详情或 dependency 聚类中缺少本应存在的 task-flow;测试用 `design-foundation` 之类字符串时正常,真实生成返回 `task-1` 后失败。画布不显示灰色 task-flow 是当前产品决定,不能再用是否出现虚线判断 producer 映射是否正确。
|
||||
- 原因:`GameCreationAppAssetSource.taskId` 保存的是 External Editor 生成任务身份,命名空间与本地 `.agent/manifest.json` 的 Agent/task 身份不同;前端用 `taskById.get(source.taskId)` 会让真实画布资产全部失去 producer。
|
||||
- 处理:资源依赖图的 Tauri Rust read model 从有界 `.agent/agent.db` 读取 `agent.runtime.canvas.asset_generate`,以 `assetId -> agentId` 映射 producer,并要求 `agentId` 存在于当前 manifest。记录缺失、多个不同有效 Agent 冲突或读取已截断时失败关闭 producer assignment、task flow 与对应 `cyclicTaskIds`,不回退 `source.taskId`。精确 `asset-reference` 仍只依赖 manifest 中外部 resourceId 的唯一匹配;Rust 独立返回的 `dependencyDepths` 继续作为 manifest / reference read model 权威结果,前端只过滤未知资源、负数、非整数和非安全整数,不得因 producer 截断把它整体清空。
|
||||
- 验证:Rust fixture 把 `source.taskId` 固定为 `task-1 / task-2`,只有审计提供 `art-director / design-foundation` 后才生成 task flow;移除或截断审计后橙色引用保留、灰色任务流消失,合法深度仍为 `asset:spec=0 / asset:ui=1`。AppSurface 使用截断生产数据形状证明深度 `0 / 1 / 2` 真实到达卡片布局,并且不会把已有自动坐标持久化成扁平布局。
|
||||
- 验证:Rust fixture 把 `source.taskId` 固定为 `task-1 / task-2`,只有审计提供 `art-director / design-foundation` 后才生成 task-flow read model;移除或截断审计后该 flow 消失但橙色引用保留,合法深度仍为 `asset:spec=0 / asset:ui=1`。前端始终断言 task-flow 零 SVG;AppSurface 使用截断生产数据形状证明深度 `0 / 1 / 2` 真实到达卡片布局,并且不会把已有自动坐标持久化成扁平布局。
|
||||
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/resource_dependency_graph.rs`、`apps/ai-game-creator-shell/src/view/project-development/resourceDependencyGraphModel.ts`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。
|
||||
|
||||
## 依赖图未就绪时不能先初始化资源布局
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user