完善资源管理工作台与有界预览

将资源卡本体化并补齐分区缩放滚动与依赖聚类

增加全进程三槽预览调度范围取消与安全分块读取

保持布局 CAS 预览缓存回收和依赖图权威合同

补齐资源管理前端 Tauri AppSurface 回归并同步文档
This commit is contained in:
2026-08-11 18:04:11 +08:00
parent 3c457feb4c
commit ffb4cb5d62
32 changed files with 8777 additions and 1566 deletions
@@ -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,同时保留有界 tombstoneseen 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` 后,相邻卡片间的橙线线身只剩约 `614px`;菱形 / 分叉簇又把单节点层与多节点层顶部对齐,形成同组一侧很短、另一侧明显过长。
- 决策: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 夹取与零 CASSVG 回归覆盖单 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,卡片坐标和持久化布局不变。
+66 -2
View File
@@ -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 / 原生 scrollviewport 原生 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 零 SVGAppSurface 使用截断生产数据形状证明深度 `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`
## 依赖图未就绪时不能先初始化资源布局